Example #1
0
        /// <summary>
        /// Parses one Bitcoin block.
        /// </summary>
        /// <param name="blockchainFileName">
        /// The name of the blockchain file that contains the block being parsed.
        /// </param>
        /// <param name="binaryReader">
        /// Provides access to a Bitcoin blockchain file.
        /// </param>
        private ParserBlock ParseBlockchainFile(string blockchainFileName, BinaryReader binaryReader)
        {
            // There are some rare situations where a block is preceded by a section containing zero bytes.
            if (binaryReader.SkipZeroBytes() == false)
            {
                // We reached the end of the file. There is no block to be parsed.
                return(null);
            }

            UInt32 blockId = binaryReader.ReadUInt32();

            if (blockId != this.blockMagicId)
            {
                throw new InvalidBlockchainContentException(string.Format(CultureInfo.InvariantCulture, "Invalid block Id: {0:X}. Expected: {1:X}", blockId, this.blockMagicId));
            }

            int blockLength = (int)binaryReader.ReadUInt32();

            byte[] blockBuffer = binaryReader.ReadBytes(blockLength);

            using (BlockMemoryStreamReader blockMemoryStreamReader = new BlockMemoryStreamReader(blockBuffer))
            {
                return(BlockchainParser.InternalParseBlockchainFile(blockBuffer,
                                                                    blockchainFileName, blockMemoryStreamReader));
            }
        }
        /// <summary>
        /// Parses a Bitcoin block header.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The block header information.
        /// </returns>
        /// <exception cref="InvalidBlockchainContentException">
        /// Thrown if the block version is unknown.
        /// </exception>
        private static BlockHeader ParseBlockHeader(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            BlockHeader blockHeader = new BlockHeader();

            int positionInBaseStreamAtBlockHeaderStart = (int)blockMemoryStreamReader.BaseStream.Position;

            blockHeader.BlockVersion = blockMemoryStreamReader.ReadUInt32();

            //// TODO: We need to understand better what is different in V2 and V3.

            if (blockHeader.BlockVersion != 1 &&
                blockHeader.BlockVersion != 2 &&
                blockHeader.BlockVersion != 3 &&
                blockHeader.BlockVersion != 0x20000007 &&
                blockHeader.BlockVersion != 0x30000000 &&
                blockHeader.BlockVersion != 4 &&
                blockHeader.BlockVersion != 0x20000000 &&
                blockHeader.BlockVersion != 0x20000001 &&
                blockHeader.BlockVersion != 0x30000001 &&
                blockHeader.BlockVersion != 0x08000004 &&
                blockHeader.BlockVersion != 0x20000002 &&
                blockHeader.BlockVersion != 0x30000007 &&
                blockHeader.BlockVersion != 0x20000004)
            {
                throw new UnknownBlockVersionException(string.Format(CultureInfo.InvariantCulture, "Unknown block version: {0} ({0:X}).", blockHeader.BlockVersion));
            }

            blockHeader.PreviousBlockHash = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());
            blockHeader.MerkleRootHash    = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());

            blockHeader.BlockTimestampUnix = blockMemoryStreamReader.ReadUInt32();
            blockHeader.BlockTimestamp     = new DateTime(1970, 1, 1).AddSeconds(blockHeader.BlockTimestampUnix);

            blockHeader.BlockTargetDifficulty = blockMemoryStreamReader.ReadUInt32();
            blockHeader.BlockNonce            = blockMemoryStreamReader.ReadUInt32();

            int positionInBaseStreamAfterBlockHeaderEnd = (int)blockMemoryStreamReader.BaseStream.Position;

            using (SHA256Managed sha256 = new SHA256Managed())
            {
                //// We need to calculate the double SHA256 hash of this transaction.
                //// We need to access the buffer that contains the transaction that we jut read through.
                //// Here we take advantage of the fact that the entire block was loaded as an in-memory buffer.
                //// The base stream of blockMemoryStreamReader is that in-memory buffer.

                byte[] baseBuffer            = blockMemoryStreamReader.GetBuffer();
                int    blockHeaderBufferSize = positionInBaseStreamAfterBlockHeaderEnd - positionInBaseStreamAtBlockHeaderStart;

                if (blockHeaderBufferSize != ExpectedBlockHeaderBufferSize)
                {
                    // We have a problem. The block header should be 80 bytes in size.
                    throw new InvalidBlockchainContentException(string.Format(CultureInfo.InvariantCulture, "Block header buffer size has an invalid length: {0}. Expected: {1}.", blockHeaderBufferSize, ExpectedBlockHeaderBufferSize));
                }

                byte[] hash1 = sha256.ComputeHash(baseBuffer, positionInBaseStreamAtBlockHeaderStart, blockHeaderBufferSize);
                blockHeader.BlockHash = new ByteArray(sha256.ComputeHash(hash1).ReverseByteArray());
            }

            return(blockHeader);
        }
        /// <summary>
        /// Parses a Bitcoin transaction output.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction output that was parsed.
        /// </returns>
        private static TransactionOutput ParseTransactionOutput(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            TransactionOutput transactionOutput = new TransactionOutput();

            transactionOutput.OutputValueSatoshi = blockMemoryStreamReader.ReadUInt64();
            int scriptLength = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            transactionOutput.OutputScript = new ByteArray(blockMemoryStreamReader.ReadBytes(scriptLength));

            return(transactionOutput);
        }
        /// <summary>
        /// Parses one Bitcoin block except for a few fields before the actual block header.
        /// </summary>
        /// <param name="blockchainFileName">
        /// The name of the blockchain file that contains the block being parsed.
        /// </param>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        private static Block InternalParseBlockchainFile(string blockchainFileName, BlockMemoryStreamReader blockMemoryStreamReader)
        {
            BlockHeader blockHeader = BlockchainParser.ParseBlockHeader(blockMemoryStreamReader);

            Block block = new Block(blockchainFileName, blockHeader);

            int blockTransactionCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            for (int transactionIndex = 0; transactionIndex < blockTransactionCount; transactionIndex++)
            {
                Transaction transaction = BlockchainParser.ParseTransaction(blockMemoryStreamReader);
                block.AddTransaction(transaction);
            }

            return(block);
        }
        private static Witness ParseWitness(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            Witness witness = new Witness();

            int witnessStackCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            witness.WitnessStack = new List <ByteArray>();

            for (int witnessStackIndex = 0; witnessStackIndex < witnessStackCount; witnessStackIndex++)
            {
                int witnessSize = (int)blockMemoryStreamReader.ReadVariableLengthInteger();
                witness.WitnessStack.Add(new ByteArray(blockMemoryStreamReader.ReadBytes(witnessSize)));
            }

            return(witness);
        }
        /// <summary>
        /// Parses a Bitcoin transaction input.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction input that was parsed.
        /// </returns>
        private static TransactionInput ParseTransactionInput(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            TransactionInput transactionInput = new TransactionInput();

            transactionInput.SourceTransactionHash        = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());
            transactionInput.SourceTransactionOutputIndex = blockMemoryStreamReader.ReadUInt32();

            int scriptLength = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            // Ignore the script portion.
            transactionInput.InputScript = new ByteArray(blockMemoryStreamReader.ReadBytes(scriptLength));

            // Ignore the sequence number.
            blockMemoryStreamReader.SkipBytes(4);

            return(transactionInput);
        }
        /// <summary>
        /// Parses a Bitcoin transaction.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction that was parsed.
        /// </returns>
        private static Transaction ParseTransaction(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            Transaction transaction = new Transaction();

            int positionInBaseStreamAtTransactionStart = (int)blockMemoryStreamReader.BaseStream.Position;

            transaction.TransactionVersion = blockMemoryStreamReader.ReadUInt32();

            int inputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            for (int inputIndex = 0; inputIndex < inputsCount; inputIndex++)
            {
                TransactionInput transactionInput = BlockchainParser.ParseTransactionInput(blockMemoryStreamReader);
                transaction.AddInput(transactionInput);
            }

            int outputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            for (int outputIndex = 0; outputIndex < outputsCount; outputIndex++)
            {
                TransactionOutput transactionOutput = BlockchainParser.ParseTransactionOutput(blockMemoryStreamReader);
                transaction.AddOutput(transactionOutput);
            }

            // TODO: Need to find out more details about the semantic of TransactionLockTime.
            transaction.TransactionLockTime = blockMemoryStreamReader.ReadUInt32();

            int positionInBaseStreamAfterTransactionEnd = (int)blockMemoryStreamReader.BaseStream.Position;

            using (SHA256Managed sha256 = new SHA256Managed())
            {
                //// We need to calculate the double SHA256 hash of this transaction.
                //// We need to access the buffer that contains the transaction that we jut read through.
                //// Here we take advantage of the fact that the entire block was loaded as an in-memory buffer.
                //// The base stream of blockMemoryStreamReader is that in-memory buffer.

                byte[] baseBuffer            = blockMemoryStreamReader.GetBuffer();
                int    transactionBufferSize = positionInBaseStreamAfterTransactionEnd - positionInBaseStreamAtTransactionStart;

                byte[] hash1 = sha256.ComputeHash(baseBuffer, positionInBaseStreamAtTransactionStart, transactionBufferSize);
                transaction.TransactionHash = new ByteArray(sha256.ComputeHash(hash1).ReverseByteArray());
            }

            return(transaction);
        }
Example #8
0
        /// <summary>
        /// Initializes a new instance of the <see cref="BlockchainParser" /> class.
        /// </summary>
        /// <param name="blockchainPath">
        /// The path to the folder containing the blockchain files.
        /// </param>
        /// <param name="firstBlockchainFileName">
        /// The name of the first blockchain file that should be processed from the series of blockchain files.
        /// In the list of blockchain files ordered by name, any blockchain file that appears prior
        /// to the file specified by this parameter will be ignored.
        /// If null then all file from the series of blockchain files will be processed.
        /// </param>
        /// </param name="nextNblockDataFiles">
        /// process the next N blockdata files. the default value is int.MaxValue.
        /// </param>
        /// <exception cref="InvalidBlockchainFilesException">
        /// Thrown when the list of Bitcoin blockchain files is found to be invalid.
        /// The blockchain folder must contain files named with the pattern "blkxxxxx.dat",
        //ader.BlockVersion != 0x30000007 &&
        //    //    blockHeader.BlockVersion != 0x20000004)
        //    //{
        //    //    throw new UnknownBlockVersionException(string.Format(CultureInfo.InvariantCulture, "Unknown block version: {0} ({0:X}).", blockHeader.BlockVersion));
        //    //}

        //    blockHeader.PreviousBlockHash = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());
        //    blockHeader.MerkleRootHash = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());

        //    blockHeader.BlockTimestampUnix = blockMemoryStreamReader.ReadUInt32();
        //    blockHeader.BlockTimestamp = new DateTime(1970, 1, 1).AddSeconds(blockHeader.BlockTimestampUnix);

        //    blockHeader.BlockTargetDifficulty = blockMemoryStreamReader.ReadUInt32();
        //    blockHeader.BlockNonce = blockMemoryStreamReader.ReadUInt32();

        //    int positionInBaseStreamAfterBlockHeaderEnd = (int)blockMemoryStreamReader.BaseStream.Position;

        //    using (SHA256Managed sha256 = new SHA256Managed())
        //    {
        //        //// We need to calculate the double SHA256 hash of this transaction.
        //        //// We need to access the buffer that contains the transaction that we jut read through.
        //        //// Here we take advantage of the fact that the entire block was loaded as an in-memory buffer.
        //        //// The base stream of blockMemoryStreamReader is that in-memory buffer.

        //        byte[] baseBuffer = blockMemoryStreamReader.GetBuffer();
        //        int blockHeaderBufferSize = positionInBaseStreamAfterBlockHeaderEnd - positionInBaseStreamAtBlockHeaderStart;

        //        if (blockHeaderBufferSize != ExpectedBlockHeaderBufferSize)
        //        {
        //            // We have a problem. The block header should be 80 bytes in size.
        //            throw new InvalidBlockchainContentException(string.Format(CultureInfo.InvariantCulture, "Block header buffer size has an invalid length: {0}. Expected: {1}.", blockHeaderBufferSize, ExpectedBlockHeaderBufferSize));
        //        }

        //        byte[] hash1 = sha256.ComputeHash(baseBuffer, positionInBaseStreamAtBlockHeaderStart, blockHeaderBufferSize);
        //        blockHeader.BlockHash = new ByteArray(sha256.ComputeHash(hash1).ReverseByteArray());
        //    }

        //    return blockHeader;
        //}

        /// <summary>
        /// Parses a Bitcoin transaction input.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction input that was parsed.
        /// </returns>
        //private static TransactionInput ParseTransactionInput(BlockMemoryStreamReader blockMemoryStreamReader)
        //{
        //    TransactionInput transactionInput = new TransactionInput();

        //    transactionInput.SourceTransactionHash = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());
        //    transactionInput.SourceTransactionOutputIndex = blockMemoryStreamReader.ReadUInt32();

        //    int scriptLength = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

        //    // Ignore the script portion.
        //    transactionInput.InputScript = new ByteArray(blockMemoryStreamReader.ReadBytes(scriptLength));

        //    // Ignore the sequence number.
        //    blockMemoryStreamReader.SkipBytes(4);

        //    return transactionInput;
        //}

        /// <summary>
        /// Parses a Bitcoin transaction output.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction output that was parsed.
        /// </returns>
        //private static TransactionOutput ParseTransactionOutput(BlockMemoryStreamReader blockMemoryStreamReader)
        //{
        //    TransactionOutput transactionOutput = new TransactionOutput();

        //    transactionOutput.OutputValueSatoshi = blockMemoryStreamReader.ReadUInt64();
        //    int scriptLength = (int)blockMemoryStreamReader.ReadVariableLengthInteger();
        //    transactionOutput.OutputScript = new ByteArray(blockMemoryStreamReader.ReadBytes(scriptLength));

        //    return transactionOutput;
        //}

        /// <summary>
        /// Parses a Bitcoin transaction.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction that was parsed.
        /// </returns>
        //private static Transaction ParseTransaction(BlockMemoryStreamReader blockMemoryStreamReader)
        //{
        //    Transaction transaction = new Transaction();

        //    int positionInBaseStreamAtTransactionStart = (int)blockMemoryStreamReader.BaseStream.Position;

        //    transaction.TransactionVersion = blockMemoryStreamReader.ReadUInt32();

        //    int inputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

        //    for (int inputIndex = 0; inputIndex < inputsCount; inputIndex++)
        //    {
        //        TransactionInput transactionInput = BlockchainParser.ParseTransactionInput(blockMemoryStreamReader);
        //        transaction.AddInput(transactionInput);
        //    }

        //    int outputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

        //    for (int outputIndex = 0; outputIndex < outputsCount; outputIndex++)
        //    {
        //        TransactionOutput transactionOutput = BlockchainParser.ParseTransactionOutput(blockMemoryStreamReader);
        //        transaction.AddOutput(transactionOutput);
        //    }

        //    // TODO: Need to find out more details about the semantic of TransactionLockTime.
        //    transaction.TransactionLockTime = blockMemoryStreamReader.ReadUInt32();

        //    int positionInBaseStreamAfterTransactionEnd = (int)blockMemoryStreamReader.BaseStream.Position;

        //    using (SHA256Managed sha256 = new SHA256Managed())
        //    {
        //        //// We need to calculate the double SHA256 hash of this transaction.
        //        //// We need to access the buffer that contains the transaction that we jut read through.
        //        //// Here we take advantage of the fact that the entire block was loaded as an in-memory buffer.
        //        //// The base stream of blockMemoryStreamReader is that in-memory buffer.

        //        byte[] baseBuffer = blockMemoryStreamReader.GetBuffer();
        //        int transactionBufferSize = positionInBaseStreamAfterTransactionEnd - positionInBaseStreamAtTransactionStart;

        //        byte[] hash1 = sha256.ComputeHash(baseBuffer, positionInBaseStreamAtTransactionStart, transactionBufferSize);
        //        transaction.TransactionHash = new ByteArray(sha256.ComputeHash(hash1).ReverseByteArray());
        //    }

        //    return transaction;
        //}

        /// <summary>
        /// Parses one Bitcoin block except for a few fields before the actual block header.
        /// </summary>
        /// <param name="blockchainFileName">
        /// The name of the blockchain file that contains the block being parsed.
        /// </param>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        private static ParserBlock InternalParseBlockchainFile(byte[] blockBuffer,
                                                               string blockchainFileName, BlockMemoryStreamReader blockMemoryStreamReader)
        {
            Block       nblk  = ParserBlock.Load(blockBuffer, NBitcoin.Network.Main);
            ParserBlock block = new ParserBlock(nblk, blockBuffer.Length, blockchainFileName);

            //BlockHeader blockHeader = BlockchainParser.ParseBlockHeader(blockMemoryStreamReader);
            //Block block = new Block(blockBuffer, blockchainFileName, blockHeader);
            //if (block.BlockHeader.BlockVersion < 0x20000002)
            //{

            //int blockTransactionCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();
            //    for (int transactionIndex = 0; transactionIndex < blockTransactionCount; transactionIndex++)
            //    {
            //        Transaction transaction = BlockchainParser.ParseTransaction(blockMemoryStreamReader);
            //        block.AddTransaction(transaction);
            //    }
            //}
            //else
            //{
            //NBitcoin.Block nblock = NBitcoin.Block.Load(blockBuffer, NBitcoin.Network.Main);
            //foreach(NBitcoin.Transaction ntx in nblock.Transactions)
            //{
            //    //block.AddTransaction()
            //}
            //}
            //NBitcoin.Block nblock = NBitcoin.Block.Load(blockBuffer, NBitcoin.Network.Main);
            return(block);
        }
        /// <summary>
        /// Parses a Bitcoin transaction.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction that was parsed.
        /// </returns>
        private static Transaction ParseTransaction(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            Transaction transaction = new Transaction();

            int positionInBaseStreamAtTransactionStart = (int)blockMemoryStreamReader.BaseStream.Position;

            transaction.TransactionVersion = blockMemoryStreamReader.ReadUInt32();

            int inputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();


            bool isSegWit = false;

            if (inputsCount == 0)
            {
                byte flag = blockMemoryStreamReader.ReadByte();
                if (flag != 0x01)
                {
                    throw new InvalidBlockchainContentException(string.Format(CultureInfo.InvariantCulture,
                                                                              "Unknown transaction serialization. No input transactions, but SegWit flag was {0} instead of 1.", flag));
                }
                inputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();
                isSegWit    = true;
            }


            for (int inputIndex = 0; inputIndex < inputsCount; inputIndex++)
            {
                TransactionInput transactionInput = BlockchainParser.ParseTransactionInput(blockMemoryStreamReader);
                transaction.AddInput(transactionInput);
            }

            int outputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            for (int outputIndex = 0; outputIndex < outputsCount; outputIndex++)
            {
                TransactionOutput transactionOutput = BlockchainParser.ParseTransactionOutput(blockMemoryStreamReader);
                transaction.AddOutput(transactionOutput);
            }


            int positionInBaseStreamAfterTxOuts = (int)blockMemoryStreamReader.BaseStream.Position;

            if (isSegWit)
            {
                for (int inputIndex = 0; inputIndex < inputsCount; inputIndex++)
                {
                    Witness witness = BlockchainParser.ParseWitness(blockMemoryStreamReader);
                    transaction.AddWitness(witness);
                }
            }

            // TODO: Need to find out more details about the semantic of TransactionLockTime.
            transaction.TransactionLockTime = blockMemoryStreamReader.ReadUInt32();

            int positionInBaseStreamAfterTransactionEnd = (int)blockMemoryStreamReader.BaseStream.Position;

            using (SHA256Managed sha256 = new SHA256Managed())
            {
                //// We need to calculate the double SHA256 hash of this transaction.
                //// We need to access the buffer that contains the transaction that we jut read through.
                //// Here we take advantage of the fact that the entire block was loaded as an in-memory buffer.
                //// The base stream of blockMemoryStreamReader is that in-memory buffer.

                //byte[] baseBuffer = blockMemoryStreamReader.GetBuffer();
                //int transactionBufferSize = positionInBaseStreamAfterTransactionEnd - positionInBaseStreamAtTransactionStart;
                byte[] baseBuffer = blockMemoryStreamReader.GetBuffer(), hash1 = null;

                if (isSegWit)
                {
                    using (SHA256Managed innerSHA256 = new SHA256Managed())
                    {
                        //// SegWit transactions are still identified by their txid, which is double SHA256 of the old
                        //// serialization format (i.e. no marker, flag, or witness). So, we need to calculate the txid by
                        //// recreating the old format as the input to the hash algorithm.

                        // First, the version number
                        innerSHA256.TransformBlock(baseBuffer, positionInBaseStreamAtTransactionStart, 4, baseBuffer, positionInBaseStreamAtTransactionStart);

                        // Skip the marker and flag (each one byte), then read in txins and txouts (starting with txin count)
                        int txStart = positionInBaseStreamAtTransactionStart + 6;
                        int txSize  = positionInBaseStreamAfterTxOuts - txStart;
                        innerSHA256.TransformBlock(baseBuffer, txStart, txSize, baseBuffer, txStart);

                        ///// After the transactions comes the segregated witness data, which is not included in the txid.
                        ///// The only thing left to add to calcualte the txid is nLockTime located in the last 4 bytes
                        int lockTimeStart = positionInBaseStreamAfterTransactionEnd - 4;
                        innerSHA256.TransformFinalBlock(baseBuffer, lockTimeStart, 4);
                        hash1 = innerSHA256.Hash;
                    }
                }
                else
                {
                    int transactionBufferSize = positionInBaseStreamAfterTransactionEnd - positionInBaseStreamAtTransactionStart;
                    hash1 = sha256.ComputeHash(baseBuffer, positionInBaseStreamAtTransactionStart, transactionBufferSize);
                }



                // byte[] hash1 = sha256.ComputeHash(baseBuffer, positionInBaseStreamAtTransactionStart, transactionBufferSize);
                transaction.TransactionHash = new ByteArray(sha256.ComputeHash(hash1).ReverseByteArray());
            }

            return(transaction);
        }
        /// <summary>
        /// Parses a Bitcoin transaction output.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction output that was parsed.
        /// </returns>
        private static TransactionOutput ParseTransactionOutput(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            TransactionOutput transactionOutput = new TransactionOutput();

            transactionOutput.OutputValueSatoshi = blockMemoryStreamReader.ReadUInt64();
            int scriptLength = (int)blockMemoryStreamReader.ReadVariableLengthInteger();
            transactionOutput.OutputScript = new ByteArray(blockMemoryStreamReader.ReadBytes(scriptLength));

            return transactionOutput;
        }
        /// <summary>
        /// Parses a Bitcoin transaction output.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction output that was parsed.
        /// </returns>
        private static TransactionOutput ParseTransactionOutput(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            TransactionOutput transactionOutput = new TransactionOutput();

            transactionOutput.OutputValueSatoshi = blockMemoryStreamReader.ReadUInt64();
            int scriptLength = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            byte[] OutputScriptBytes = blockMemoryStreamReader.ReadBytes(scriptLength);
            transactionOutput.OutputScript = new ByteArray(OutputScriptBytes);
            //this is new add to parse address, if can't parse the address, set the address = '0'
            byte[] outputAddress;
            if (scriptLength > 2)
            {
                using (BlockMemoryStreamReader outputScriptReader = new BlockMemoryStreamReader(OutputScriptBytes))
                {
                    byte first = outputScriptReader.ReadByte();
                    if (first == 0x6a)//RETURN: can't parse the address
                    {
                        outputAddress = System.Text.Encoding.ASCII.GetBytes("0");
                    }
                    else if (first == 0x00 && (OutputScriptBytes[1] == 0x14 || OutputScriptBytes[1] == 0x20))//witness addr
                    {
                        int    hash_len  = outputScriptReader.ReadByte();
                        byte[] addr_hash = outputScriptReader.ReadBytes(hash_len);
                        outputAddress = Bench32.SegwitAddrEncode("bc", 0, addr_hash, addr_hash.Length);
                    }
                    else if (first == 0x76 && OutputScriptBytes[1] == 0xa9)//normal bitcoin addr,base58check encode and prefix = 0
                    {
                        outputScriptReader.ReadByte();
                        int    hash_len  = outputScriptReader.ReadByte();
                        byte[] addr_hash = outputScriptReader.ReadBytes(hash_len);
                        outputAddress = Base58.EncodeBase58Check(addr_hash, addr_hash.Length, 0);
                    }
                    else if (first == 0xa9 && (OutputScriptBytes[1] == 0x14 || OutputScriptBytes[1] == 0x20))//base58check encode and prefix = 5
                    {
                        int    hash_len  = outputScriptReader.ReadByte();
                        byte[] addr_hash = outputScriptReader.ReadBytes(hash_len);
                        outputAddress = Base58.EncodeBase58Check(addr_hash, addr_hash.Length, 1);
                    }
                    else if (first == 0x41)//非压缩公钥
                    {
                        byte[] bitcoin_addr = outputScriptReader.ReadBytes(65);
                        byte[] addr_hash    = Hash160.hash160(bitcoin_addr);
                        outputAddress = Base58.EncodeBase58Check(addr_hash, addr_hash.Length, 0);
                    }
                    else
                    {
                        /*
                         * string str = string.Empty;
                         * foreach (byte item in OutputScriptBytes)
                         * {
                         *  str += string.Format("{0:x2}", item);
                         * }
                         * Console.WriteLine(str);
                         */
                        outputAddress = System.Text.Encoding.ASCII.GetBytes("0");
                    }
                }
            }
            else
            {
                /*
                 * Console.WriteLine(scriptLength);
                 * string str = string.Empty;
                 * foreach (byte item in OutputScriptBytes)
                 * {
                 *  str += string.Format("{0:x2}", item);
                 * }
                 * Console.WriteLine(str);
                 */
                outputAddress = System.Text.Encoding.ASCII.GetBytes("0");
            }
            transactionOutput.OutputAddress = new ByteArray(outputAddress);
            return(transactionOutput);
        }
        /// <summary>
        /// Parses a Bitcoin transaction input.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction input that was parsed.
        /// </returns>
        private static TransactionInput ParseTransactionInput(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            TransactionInput transactionInput = new TransactionInput();

            transactionInput.SourceTransactionHash = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());
            transactionInput.SourceTransactionOutputIndex = blockMemoryStreamReader.ReadUInt32();

            int scriptLength = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            // Ignore the script portion.
            transactionInput.InputScript = new ByteArray(blockMemoryStreamReader.ReadBytes(scriptLength));

            // Ignore the sequence number.
            blockMemoryStreamReader.SkipBytes(4);

            return transactionInput;
        }
        /// <summary>
        /// Parses a Bitcoin transaction.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The Bitcoin transaction that was parsed.
        /// </returns>
        private static Transaction ParseTransaction(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            Transaction transaction = new Transaction();

            int positionInBaseStreamAtTransactionStart = (int)blockMemoryStreamReader.BaseStream.Position;

            transaction.TransactionVersion = blockMemoryStreamReader.ReadUInt32();

            int inputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            for (int inputIndex = 0; inputIndex < inputsCount; inputIndex++)
            {
                TransactionInput transactionInput = BlockchainParser.ParseTransactionInput(blockMemoryStreamReader);
                transaction.AddInput(transactionInput);
            }

            int outputsCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            for (int outputIndex = 0; outputIndex < outputsCount; outputIndex++)
            {
                TransactionOutput transactionOutput = BlockchainParser.ParseTransactionOutput(blockMemoryStreamReader);
                transaction.AddOutput(transactionOutput);
            }

            // TODO: Need to find out more details about the semantic of TransactionLockTime.
            transaction.TransactionLockTime = blockMemoryStreamReader.ReadUInt32();

            int positionInBaseStreamAfterTransactionEnd = (int)blockMemoryStreamReader.BaseStream.Position;

            using (SHA256Managed sha256 = new SHA256Managed())
            {
                //// We need to calculate the double SHA256 hash of this transaction.
                //// We need to access the buffer that contains the transaction that we jut read through.
                //// Here we take advantage of the fact that the entire block was loaded as an in-memory buffer.
                //// The base stream of blockMemoryStreamReader is that in-memory buffer.

                byte[] baseBuffer = blockMemoryStreamReader.GetBuffer();
                int transactionBufferSize = positionInBaseStreamAfterTransactionEnd - positionInBaseStreamAtTransactionStart;

                byte[] hash1 = sha256.ComputeHash(baseBuffer, positionInBaseStreamAtTransactionStart, transactionBufferSize);
                transaction.TransactionHash = new ByteArray(sha256.ComputeHash(hash1).ReverseByteArray());
            }

            return transaction;
        }
        /// <summary>
        /// Parses a Bitcoin block header.
        /// </summary>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        /// <returns>
        /// The block header information.
        /// </returns>
        /// <exception cref="InvalidBlockchainContentException">
        /// Thrown if the block version is unknown.
        /// </exception>
        private static BlockHeader ParseBlockHeader(BlockMemoryStreamReader blockMemoryStreamReader)
        {
            BlockHeader blockHeader = new BlockHeader();

            int positionInBaseStreamAtBlockHeaderStart = (int)blockMemoryStreamReader.BaseStream.Position;

            blockHeader.BlockVersion = blockMemoryStreamReader.ReadUInt32();

            // TODO: We need to understand better what is different in V2 and V3.
            if (blockHeader.BlockVersion != 1 && blockHeader.BlockVersion != 2 && blockHeader.BlockVersion != 3)
            {
                throw new UnknownBlockVersionException(string.Format(CultureInfo.InvariantCulture, "Unknown block version: {0}.", blockHeader.BlockVersion));
            }

            blockHeader.PreviousBlockHash = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());
            blockHeader.MerkleRootHash = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray());

            blockHeader.BlockTimestampUnix = blockMemoryStreamReader.ReadUInt32();
            blockHeader.BlockTimestamp = new DateTime(1970, 1, 1).AddSeconds(blockHeader.BlockTimestampUnix);

            blockHeader.BlockTargetDifficulty = blockMemoryStreamReader.ReadUInt32();
            blockHeader.BlockNonce = blockMemoryStreamReader.ReadUInt32();

            int positionInBaseStreamAfterBlockHeaderEnd = (int)blockMemoryStreamReader.BaseStream.Position;

            using (SHA256Managed sha256 = new SHA256Managed())
            {
                //// We need to calculate the double SHA256 hash of this transaction.
                //// We need to access the buffer that contains the transaction that we jut read through.
                //// Here we take advantage of the fact that the entire block was loaded as an in-memory buffer.
                //// The base stream of blockMemoryStreamReader is that in-memory buffer.

                byte[] baseBuffer = blockMemoryStreamReader.GetBuffer();
                int blockHeaderBufferSize = positionInBaseStreamAfterBlockHeaderEnd - positionInBaseStreamAtBlockHeaderStart;

                if (blockHeaderBufferSize != ExpectedBlockHeaderBufferSize)
                {
                    // We have a problem. The block header should be 80 bytes in size.
                    throw new InvalidBlockchainContentException(string.Format(CultureInfo.InvariantCulture, "Block header buffer size has an invalid length: {0}. Expected: {1}.", blockHeaderBufferSize, ExpectedBlockHeaderBufferSize));
                }

                byte[] hash1 = sha256.ComputeHash(baseBuffer, positionInBaseStreamAtBlockHeaderStart, blockHeaderBufferSize);
                blockHeader.BlockHash = new ByteArray(sha256.ComputeHash(hash1).ReverseByteArray());
            }

            return blockHeader;
        }
        /// <summary>
        /// Parses one Bitcoin block.
        /// </summary>
        /// <param name="blockchainFileName">
        /// The name of the blockchain file that contains the block being parsed.
        /// </param>
        /// <param name="binaryReader">
        /// Provides access to a Bitcoin blockchain file.
        /// </param>
        private static Block ParseBlockchainFile(string blockchainFileName, BinaryReader binaryReader)
        {
            // There are some rare situations where a block is preceded by a section containing zero bytes.
            if (binaryReader.SkipZeroBytes() == false)
            {
                // We reached the end of the file. There is no block to be parsed.
                return null;
            }

            UInt32 blockId = binaryReader.ReadUInt32();
            if (blockId != BlockMagicId)
            {
                throw new InvalidBlockchainContentException(string.Format(CultureInfo.InvariantCulture, "Invalid block Id: {0:X}. Expected: {1:X}", blockId, BlockMagicId));
            }

            int blockLength = (int)binaryReader.ReadUInt32();
            byte[] blockBuffer = binaryReader.ReadBytes(blockLength);

            using (BlockMemoryStreamReader blockMemoryStreamReader = new BlockMemoryStreamReader(blockBuffer))
            {
                return BlockchainParser.InternalParseBlockchainFile(blockchainFileName, blockMemoryStreamReader);
            }
        }
        /// <summary>
        /// Parses one Bitcoin block except for a few fields before the actual block header.
        /// </summary>
        /// <param name="blockchainFileName">
        /// The name of the blockchain file that contains the block being parsed.
        /// </param>
        /// <param name="blockMemoryStreamReader">
        /// Provides access to a section of the Bitcoin blockchain file.
        /// </param>
        private static Block InternalParseBlockchainFile(string blockchainFileName, BlockMemoryStreamReader blockMemoryStreamReader)
        {
            BlockHeader blockHeader = BlockchainParser.ParseBlockHeader(blockMemoryStreamReader);

            Block block = new Block(blockchainFileName, blockHeader);

            int blockTransactionCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger();

            for (int transactionIndex = 0; transactionIndex < blockTransactionCount; transactionIndex++)
            {
                Transaction transaction = BlockchainParser.ParseTransaction(blockMemoryStreamReader);
                block.AddTransaction(transaction);
            }

            return block;
        }