コード例 #1
0
        /// <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));
        }
コード例 #2
0
ファイル: Script.cs プロジェクト: nikserdev/BitcoinSharp
        /// <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));
        }
コード例 #3
0
        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());
        }
コード例 #4
0
 /// <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);
 }
コード例 #5
0
ファイル: Script.cs プロジェクト: nikserdev/BitcoinSharp
        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]));
            }
        }
コード例 #6
0
ファイル: Script.cs プロジェクト: nikserdev/BitcoinSharp
        /// <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));
            }
        }
コード例 #7
0
        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());
        }
コード例 #8
0
ファイル: Script.cs プロジェクト: nikserdev/BitcoinSharp
        /// <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());
        }
コード例 #9
0
ファイル: Peer.cs プロジェクト: nikserdev/BitcoinSharp
        /// <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);
        }
コード例 #10
0
ファイル: Block.cs プロジェクト: nikserdev/BitcoinSharp
        /// <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));
            }
        }
コード例 #11
0
ファイル: Script.cs プロジェクト: nikserdev/BitcoinSharp
        /// <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);
        }
コード例 #12
0
ファイル: Block.cs プロジェクト: nikserdev/BitcoinSharp
        /// <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());
        }
コード例 #13
0
ファイル: Message.cs プロジェクト: dotnext24/bitcoinsharp
        /// <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;
        }
コード例 #14
0
ファイル: Transaction.cs プロジェクト: mavrol/bitcoinsharpev
        /// <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.
        }
コード例 #15
0
 public override string ToString()
 {
     return(Utils.BytesToHexString(_bytes));
 }
コード例 #16
0
 public override string ToString()
 {
     return("Unknown message [" + _name + "]: " + (Bytes == null ? "" : Utils.BytesToHexString(Bytes)));
 }
コード例 #17
0
        /// <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);
            }
        }
コード例 #18
0
 /// <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);
     }
 }