/// <exception cref="BitCoinSharp.ProtocolException" /> protected override void Parse() { _version = ReadUint32(); // First come the inputs. var numInputs = ReadVarInt(); _inputs = new List <TransactionInput>((int)numInputs); for (var i = 0UL; i < numInputs; i++) { var input = new TransactionInput(Params, this, Bytes, Cursor); _inputs.Add(input); Cursor += input.MessageSize; } // Now the outputs var numOutputs = ReadVarInt(); _outputs = new List <TransactionOutput>((int)numOutputs); for (var i = 0UL; i < numOutputs; i++) { var output = new TransactionOutput(Params, this, Bytes, Cursor); _outputs.Add(output); Cursor += output.MessageSize; } _lockTime = ReadUint32(); // Store a hash, it may come in useful later (want to avoid re-serialization costs). _hash = new Sha256Hash(Utils.ReverseBytes(Utils.DoubleDigest(Bytes, Offset, Cursor - Offset))); }
/// <exception cref="BitCoinSharp.ProtocolException" /> protected override void Parse() { _version = ReadUint32(); _prevBlockHash = ReadHash(); _merkleRoot = ReadHash(); _time = ReadUint32(); _difficultyTarget = ReadUint32(); _nonce = ReadUint32(); _hash = Utils.ReverseBytes(Utils.DoubleDigest(Bytes, 0, Cursor)); if (Cursor == Bytes.Length) { // This message is just a header, it has no transactions. return; } var numTransactions = (int)ReadVarInt(); Transactions = new List <Transaction>(numTransactions); for (var i = 0; i < numTransactions; i++) { var tx = new Transaction(Params, Bytes, Cursor); Transactions.Add(tx); Cursor += tx.MessageSize; } }
/// <summary> /// Writes message to to the output stream. /// </summary> /// <exception cref="System.IO.IOException" /> public void Serialize(Message message, Stream @out) { string name; if (!_names.TryGetValue(message.GetType(), out name)) { throw new Exception("BitcoinSerializer doesn't currently know how to serialize " + message.GetType()); } var header = new byte[4 + _commandLen + 4 + (_usesChecksumming ? 4 : 0)]; Utils.Uint32ToByteArrayBe(_params.PacketMagic, header, 0); // The header array is initialized to zero so we don't have to worry about // NULL terminating the string here. for (var i = 0; i < name.Length && i < _commandLen; i++) { header[4 + i] = (byte)name[i]; } var payload = message.BitcoinSerialize(); Utils.Uint32ToByteArrayLe((uint)payload.Length, header, 4 + _commandLen); if (_usesChecksumming) { var hash = Utils.DoubleDigest(payload); Array.Copy(hash, 0, header, 4 + _commandLen + 4, 4); } @out.Write(header); @out.Write(payload); _log.DebugFormat("Sending {0} message: {1}", name, Utils.BytesToHexString(header) + Utils.BytesToHexString(payload)); }
/// <summary> /// Calculates the block hash by serializing the block and hashing the resulting bytes. /// </summary> private byte[] CalculateHash() { using (var bos = new MemoryStream()) { WriteHeader(bos); return(Utils.ReverseBytes(Utils.DoubleDigest(bos.ToArray()))); } }
public override string ToString() { // A stringified address is: // 1 byte version + 20 bytes hash + 4 bytes check code (itself a truncated hash) var addressBytes = new byte[1 + 20 + 4]; addressBytes[0] = (byte)Version; Array.Copy(Bytes, 0, addressBytes, 1, 20); var check = Utils.DoubleDigest(addressBytes, 0, 21); Array.Copy(check, 0, addressBytes, 21, 4); return(Base58.Encode(addressBytes)); }
public override string ToString() { // A stringified buffer is: // 1 byte version + data bytes hash + 4 bytes check code (itself a truncated hash) var addressBytes = new byte[1 + Bytes.Length + 4]; addressBytes[0] = (byte)Version; Array.Copy(Bytes, 0, addressBytes, 1, Bytes.Length); var check = Utils.DoubleDigest(addressBytes, 0, Bytes.Length + 1); Array.Copy(check, 0, addressBytes, Bytes.Length + 1, 4); return(Base58.Encode(addressBytes)); }
private byte[] HashTransactionForSignature(SigHash type, bool anyoneCanPay) { using (var bos = new MemoryStream()) { BitcoinSerializeToStream(bos); // We also have to write a hash type. var hashType = (uint)type + 1; if (anyoneCanPay) { hashType |= 0x80; } Utils.Uint32ToByteStreamLe(hashType, bos); // Note that this is NOT reversed to ensure it will be signed correctly. If it were to be printed out // however then we would expect that it is IS reversed. return(Utils.DoubleDigest(bos.ToArray())); } }
/// <summary> /// Uses the checksum in the last 4 bytes of the decoded data to verify the rest are correct. The checksum is /// removed from the returned data. /// </summary> /// <exception cref="AddressFormatException">If the input is not base 58 or the checksum does not validate.</exception> public static byte[] DecodeChecked(string input) { var tmp = Decode(input); if (tmp.Length < 4) { throw new AddressFormatException("Input too short"); } var checksum = new byte[4]; Array.Copy(tmp, tmp.Length - 4, checksum, 0, 4); var bytes = new byte[tmp.Length - 4]; Array.Copy(tmp, 0, bytes, 0, tmp.Length - 4); tmp = Utils.DoubleDigest(bytes); var hash = new byte[4]; Array.Copy(tmp, 0, hash, 0, 4); if (!hash.SequenceEqual(checksum)) { throw new AddressFormatException("Checksum does not validate"); } return(bytes); }
/// <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); } }