/// <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 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);
        }
        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 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 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 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 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;
        }