/// <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> /// Replaces the top item in the stack with a hash160 of it /// </summary> private void ProcessOpHash160() { var buf = _stack.Pop(); var hash = Utils.Sha256Hash160(buf); _stack.Push(hash); _log.DebugFormat("HASH160: output is {0}", Utils.BytesToHexString(hash)); }
public override string ToString() { var b = new StringBuilder(); b.Append("pub:").Append(Utils.BytesToHexString(_pub)); b.Append(" priv:").Append(Utils.BytesToHexString(_priv.ToByteArray())); return(b.ToString()); }
/// <summary> /// Returns a human readable debug string. /// </summary> public override string ToString() { if (IsCoinBase) { return("TxIn: COINBASE"); } return("TxIn from tx " + Outpoint + " (pubkey: " + Utils.BytesToHexString(ScriptSig.PubKey) + ") script:" + ScriptSig); }
internal void LogStack() { var stack = _stack.ToList(); for (var i = 0; i < stack.Count; i++) { _log.DebugFormat("Stack[{0}]: {1}", i, Utils.BytesToHexString(stack[i])); } }
/// <exception cref="BitCoinSharp.ScriptException" /> private void ProcessOpEqualVerify() { _log.Debug("EQUALVERIFY"); var a = _stack.Pop(); var b = _stack.Pop(); if (!a.SequenceEqual(b)) { throw new ScriptException("EQUALVERIFY failed: " + Utils.BytesToHexString(a) + " vs " + Utils.BytesToHexString(b)); } }
public override string ToString() { var b = new StringBuilder(); b.Append("getblocks: "); foreach (var hash in _locator) { b.Append(Utils.BytesToHexString(hash)); b.Append(" "); } return(b.ToString()); }
/// <summary> /// Returns the program opcodes as a string, for example "[1234] DUP HAHS160" /// </summary> public override string ToString() { var buf = new StringBuilder(); foreach (var chunk in _chunks) { if (chunk.Length == 1) { string opName; var opcode = chunk[0]; switch (opcode) { case OpDup: opName = "DUP"; break; case OpHash160: opName = "HASH160"; break; case OpCheckSig: opName = "CHECKSIG"; break; case OpEqualVerify: opName = "EQUALVERIFY"; break; default: opName = "?(" + opcode + ")"; break; } buf.Append(opName); buf.Append(" "); } else { // Data chunk buf.Append("["); buf.Append(chunk.Length); buf.Append("]"); buf.Append(Utils.BytesToHexString(chunk)); buf.Append(" "); } } return(buf.ToString()); }
/// <exception cref="System.IO.IOException" /> private void BlockChainDownload(byte[] toHash) { // This may run in ANY thread. // The block chain download process is a bit complicated. Basically, we start with zero or more blocks in a // chain that we have from a previous session. We want to catch up to the head of the chain BUT we don't know // where that chain is up to or even if the top block we have is even still in the chain - we // might have got ourselves onto a fork that was later resolved by the network. // // To solve this, we send the peer a block locator which is just a list of block hashes. It contains the // blocks we know about, but not all of them, just enough of them so the peer can figure out if we did end up // on a fork and if so, what the earliest still valid block we know about is likely to be. // // Once it has decided which blocks we need, it will send us an inv with up to 500 block messages. We may // have some of them already if we already have a block chain and just need to catch up. Once we request the // last block, if there are still more to come it sends us an "inv" containing only the hash of the head // block. // // That causes us to download the head block but then we find (in processBlock) that we can't connect // it to the chain yet because we don't have the intermediate blocks. So we rerun this function building a // new block locator describing where we're up to. // // The getblocks with the new locator gets us another inv with another bunch of blocks. We download them once // again. This time when the peer sends us an inv with the head block, we already have it so we won't download // it again - but we recognize this case as special and call back into blockChainDownload to continue the // process. // // So this is a complicated process but it has the advantage that we can download a chain of enormous length // in a relatively stateless manner and with constant/bounded memory usage. _log.InfoFormat("blockChainDownload({0})", Utils.BytesToHexString(toHash)); // TODO: Block locators should be abstracted out rather than special cased here. var blockLocator = new LinkedList <byte[]>(); // We don't do the exponential thinning here, so if we get onto a fork of the chain we will end up // re-downloading the whole thing again. blockLocator.AddLast(_params.GenesisBlock.Hash); var topBlock = _blockChain.ChainHead.Header; if (!topBlock.Equals(_params.GenesisBlock)) { blockLocator.AddFirst(topBlock.Hash); } var message = new GetBlocksMessage(_params, blockLocator.ToList(), toHash); _conn.WriteMessage(message); }
/// <exception cref="BitCoinSharp.VerificationException" /> private void CheckMerkleHash() { var tree = BuildMerkleTree(); var calculatedRoot = tree[tree.Count - 1]; if (!calculatedRoot.SequenceEqual(_merkleRoot)) { _log.Error("Merkle tree did not verify: "); foreach (var b in tree) { _log.Error(Utils.BytesToHexString(b)); } throw new VerificationException("Merkle hashes do not match: " + Utils.BytesToHexString(calculatedRoot) + " vs " + Utils.BytesToHexString(_merkleRoot)); } }
/// <summary> /// Runs the script with the given Transaction as the "context". Some operations like CHECKSIG /// require a transaction to operate on (eg to hash). The context transaction is typically /// the transaction having its inputs verified, ie the one where the scriptSig comes from. /// </summary> /// <exception cref="BitCoinSharp.ScriptException" /> public bool Run(Transaction context) { foreach (var chunk in _chunks) { if (chunk.Length == 1) { var opcode = chunk[0]; switch (opcode) { case OpDup: ProcessOpDup(); break; case OpHash160: ProcessOpHash160(); break; case OpEqualVerify: ProcessOpEqualVerify(); break; case OpCheckSig: ProcessOpCheckSig(context); break; default: _log.DebugFormat("Unknown/unimplemented opcode: {0}", opcode); break; } } else { // Data block, push it onto the stack. _log.DebugFormat("Push {0}", Utils.BytesToHexString(chunk)); _stack.Push(chunk); } } var result = _stack.Pop(); if (result.Length != 1) { throw new ScriptException("Script left junk at the top of the stack: " + Utils.BytesToHexString(result)); } return(result[0] == 1); }
/// <summary> /// Returns a multi-line string containing a description of the contents of the block. Use for debugging purposes /// only. /// </summary> public override string ToString() { var s = new StringBuilder(); s.AppendFormat("v{0} block:", _version).AppendLine(); s.AppendFormat(" previous block: {0}", Utils.BytesToHexString(_prevBlockHash)).AppendLine(); s.AppendFormat(" merkle root: {0}", Utils.BytesToHexString(MerkleRoot)).AppendLine(); s.AppendFormat(" time: [{0}] {1}", _time, new DateTime(_time * 1000)).AppendLine(); s.AppendFormat(" difficulty target (nBits): {0}", _difficultyTarget).AppendLine(); s.AppendFormat(" nonce: {0}", _nonce).AppendLine(); if (Transactions != null && Transactions.Count > 0) { s.AppendFormat(" with {0} transaction(s):", Transactions.Count).AppendLine(); foreach (var tx in Transactions) { s.Append(tx.ToString()); } } return(s.ToString()); }
/// <exception cref="ProtocolException"/> internal Message(NetworkParameters @params, byte[] msg, int offset, uint protocolVersion = NetworkParameters.ProtocolVersion) { ProtocolVersion = protocolVersion; Params = @params; Bytes = msg; Cursor = Offset = offset; Parse(); #if SELF_CHECK // Useful to ensure serialize/deserialize are consistent with each other. if (GetType() != typeof(VersionMessage)) { var msgbytes = new byte[Cursor - offset]; Array.Copy(msg, offset, msgbytes, 0, Cursor - offset); var reserialized = BitcoinSerialize(); if (!reserialized.SequenceEqual(msgbytes)) { throw new Exception("Serialization is wrong: " + Environment.NewLine + Utils.BytesToHexString(reserialized) + " vs " + Environment.NewLine + Utils.BytesToHexString(msgbytes)); } } #endif Bytes = null; }
/// <summary> /// Once a transaction has some inputs and outputs added, the signatures in the inputs can be calculated. The /// signature is over the transaction itself, to prove the redeemer actually created that transaction, /// so we have to do this step last. /// </summary> /// <remarks> /// This method is similar to SignatureHash in script.cpp /// </remarks> /// <param name="hashType">This should always be set to SigHash.ALL currently. Other types are unused. </param> /// <param name="wallet">A wallet is required to fetch the keys needed for signing.</param> /// <exception cref="ScriptException"/> public void SignInputs(SigHash hashType, Wallet wallet) { Debug.Assert(_inputs.Count > 0); Debug.Assert(_outputs.Count > 0); // I don't currently have an easy way to test other modes work, as the official client does not use them. Debug.Assert(hashType == SigHash.All); // The transaction is signed with the input scripts empty except for the input we are signing. In the case // where addInput has been used to set up a new transaction, they are already all empty. The input being signed // has to have the connected OUTPUT program in it when the hash is calculated! // // Note that each input may be claiming an output sent to a different key. So we have to look at the outputs // to figure out which key to sign with. var signatures = new byte[_inputs.Count][]; var signingKeys = new EcKey[_inputs.Count]; for (var i = 0; i < _inputs.Count; i++) { var input = _inputs[i]; Debug.Assert(input.ScriptBytes.Length == 0, "Attempting to sign a non-fresh transaction"); // Set the input to the script of its output. input.ScriptBytes = input.Outpoint.ConnectedPubKeyScript; // Find the signing key we'll need to use. var connectedPubKeyHash = input.Outpoint.ConnectedPubKeyHash; var key = wallet.FindKeyFromPubHash(connectedPubKeyHash); // This assert should never fire. If it does, it means the wallet is inconsistent. Debug.Assert(key != null, "Transaction exists in wallet that we cannot redeem: " + Utils.BytesToHexString(connectedPubKeyHash)); // Keep the key around for the script creation step below. signingKeys[i] = key; // The anyoneCanPay feature isn't used at the moment. const bool anyoneCanPay = false; var hash = HashTransactionForSignature(hashType, anyoneCanPay); // Set the script to empty again for the next input. input.ScriptBytes = TransactionInput.EmptyArray; // Now sign for the output so we can redeem it. We use the keypair to sign the hash, // and then put the resulting signature in the script along with the public key (below). using (var bos = new MemoryStream()) { bos.Write(key.Sign(hash)); bos.Write((byte)(((int)hashType + 1) | (anyoneCanPay ? 0x80 : 0))); signatures[i] = bos.ToArray(); } } // Now we have calculated each signature, go through and create the scripts. Reminder: the script consists of // a signature (over a hash of the transaction) and the complete public key needed to sign for the connected // output. for (var i = 0; i < _inputs.Count; i++) { var input = _inputs[i]; Debug.Assert(input.ScriptBytes.Length == 0); var key = signingKeys[i]; input.ScriptBytes = Script.CreateInputScript(signatures[i], key.PubKey); } // Every input is now complete. }
public override string ToString() { return(Utils.BytesToHexString(_bytes)); }
public override string ToString() { return("Unknown message [" + _name + "]: " + (Bytes == null ? "" : Utils.BytesToHexString(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); } }
/// <exception cref="System.IO.IOException" /> /// <exception cref="BitCoinSharp.BlockStoreException" /> private void Load(FileInfo file) { _log.InfoFormat("Reading block store from {0}", file); using (var input = file.OpenRead()) { // Read a version byte. var version = input.Read(); if (version == -1) { // No such file or the file was empty. throw new FileNotFoundException(file.Name + " does not exist or is empty"); } if (version != 1) { throw new BlockStoreException("Bad version number: " + version); } // Chain head pointer is the first thing in the file. var chainHeadHash = new byte[32]; input.Read(chainHeadHash); _chainHead = new Sha256Hash(chainHeadHash); _log.InfoFormat("Read chain head from disk: {0}", _chainHead); var now = Environment.TickCount; // Rest of file is raw block headers. var headerBytes = new byte[Block.HeaderSize]; try { while (true) { // Read a block from disk. if (input.Read(headerBytes) < 80) { // End of file. break; } // Parse it. var b = new Block(_params, headerBytes); // Look up the previous block it connects to. var prev = Get(b.PrevBlockHash); StoredBlock s; if (prev == null) { // First block in the stored chain has to be treated specially. if (b.Equals(_params.GenesisBlock)) { s = new StoredBlock(_params.GenesisBlock.CloneAsHeader(), _params.GenesisBlock.GetWork(), 0); } else { throw new BlockStoreException("Could not connect " + Utils.BytesToHexString(b.Hash) + " to " + Utils.BytesToHexString(b.PrevBlockHash)); } } else { // Don't try to verify the genesis block to avoid upsetting the unit tests. b.Verify(); // Calculate its height and total chain work. s = prev.Build(b); } // Save in memory. _blockMap[new Sha256Hash(b.Hash)] = s; } } catch (ProtocolException e) { // Corrupted file. throw new BlockStoreException(e); } catch (VerificationException e) { // Should not be able to happen unless the file contains bad blocks. throw new BlockStoreException(e); } var elapsed = Environment.TickCount - now; _log.InfoFormat("Block chain read complete in {0}ms", elapsed); } }