示例#1
0
        internal uint ReadUint32()
        {
            var u = Utils.ReadUint32(Bytes, Cursor);

            Cursor += 4;
            return(u);
        }
        /// <summary>
        /// Reads a message from the given InputStream and returns it.
        /// </summary>
        /// <exception cref="BitCoinSharp.ProtocolException" />
        /// <exception cref="System.IO.IOException" />
        public Message Deserialize(Stream @in)
        {
            // A BitCoin protocol message has the following format.
            //
            //   - 4 byte magic number: 0xfabfb5da for the testnet or
            //                          0xf9beb4d9 for production
            //   - 12 byte command in ASCII
            //   - 4 byte payload size
            //   - 4 byte checksum
            //   - Payload data
            //
            // The checksum is the first 4 bytes of a SHA256 hash of the message payload. It isn't
            // present for all messages, notably, the first one on a connection.
            //
            // Satoshi's implementation ignores garbage before the magic header bytes. We have to do the same because
            // sometimes it sends us stuff that isn't part of any message.
            SeekPastMagicBytes(@in);
            // Now read in the header.
            var header     = new byte[_commandLen + 4 + (_usesChecksumming ? 4 : 0)];
            var readCursor = 0;

            while (readCursor < header.Length)
            {
                var bytesRead = @in.Read(header, readCursor, header.Length - readCursor);
                if (bytesRead == -1)
                {
                    // There's no more data to read.
                    throw new IOException("Socket is disconnected");
                }
                readCursor += bytesRead;
            }

            var cursor = 0;

            // The command is a NULL terminated string, unless the command fills all twelve bytes
            // in which case the termination is implicit.
            var mark = cursor;

            for (; header[cursor] != 0 && cursor - mark < _commandLen; cursor++)
            {
            }
            var commandBytes = new byte[cursor - mark];

            Array.Copy(header, mark, commandBytes, 0, cursor - mark);
            var command = Encoding.UTF8.GetString(commandBytes);

            cursor = mark + _commandLen;

            var size = Utils.ReadUint32(header, cursor);

            cursor += 4;

            if (size > Message.MaxSize)
            {
                throw new ProtocolException("Message size too large: " + size);
            }

            // Old clients don't send the checksum.
            var checksum = new byte[4];

            if (_usesChecksumming)
            {
                // Note that the size read above includes the checksum bytes.
                Array.Copy(header, cursor, checksum, 0, 4);
            }

            // Now try to read the whole message.
            readCursor = 0;
            var payloadBytes = new byte[size];

            while (readCursor < payloadBytes.Length - 1)
            {
                var bytesRead = @in.Read(payloadBytes, readCursor, (int)(size - readCursor));
                if (bytesRead == -1)
                {
                    throw new IOException("Socket is disconnected");
                }
                readCursor += bytesRead;
            }

            // Verify the checksum.
            if (_usesChecksumming)
            {
                var hash = Utils.DoubleDigest(payloadBytes);
                if (checksum[0] != hash[0] || checksum[1] != hash[1] ||
                    checksum[2] != hash[2] || checksum[3] != hash[3])
                {
                    throw new ProtocolException("Checksum failed to verify, actual " +
                                                Utils.BytesToHexString(hash) +
                                                " vs " + Utils.BytesToHexString(checksum));
                }
            }

            if (_log.IsDebugEnabled)
            {
                _log.DebugFormat("Received {0} byte '{1}' message: {2}",
                                 size,
                                 command,
                                 Utils.BytesToHexString(payloadBytes)
                                 );
            }

            try
            {
                Func <NetworkParameters, byte[], Message> c;
                if (!_messageConstructors.TryGetValue(command, out c))
                {
                    throw new ProtocolException("No support for deserializing message with name " + command);
                }
                return(c.Invoke(_params, payloadBytes));
            }
            catch (Exception e)
            {
                throw new ProtocolException("Error deserializing message " + Utils.BytesToHexString(payloadBytes) + Environment.NewLine + e.Message, e);
            }
        }