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="ProtocolException"/> /// <exception cref="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, commandBytes.Length); for (var i = 0; i < commandBytes.Length; i++) { // Emulate ASCII by replacing extended characters with question marks. if (commandBytes[i] >= 0x80) { commandBytes[i] = 0x3F; } } var command = Encoding.UTF8.GetString(commandBytes, 0, commandBytes.Length); 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 { return(MakeMessage(command, payloadBytes)); } catch (Exception e) { throw new ProtocolException("Error deserializing message " + Utils.BytesToHexString(payloadBytes) + Environment.NewLine + e.Message, e); } }