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


            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();

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