/// <summary>
        /// Initializes a new instance of the <see cref="Block"/> class.
        /// </summary>
        /// <param name="blockchainFileName">
        /// The name of the Bitcoin blockchain file that contains this block.
        /// </param>
        /// <param name="blockHeader">
        /// The block header.
        /// </param>
        public Block(string blockchainFileName, BlockHeader blockHeader)
        {
            this.BlockchainFileName = blockchainFileName;
            this.BlockHeader = blockHeader;

            this.transactions = new List<Transaction>();
            this.Transactions = new ReadOnlyCollection<Transaction>(this.transactions);
        }
        public Block GenerateBlock(
            ByteArray blockHash,
            ByteArray transactionHash,
            InputInfo[] inputs,
            OutputInfo[] outputs)
        {
            BlockHeader blockHeader = new BlockHeader()
            {
                BlockHash = blockHash,
                BlockNonce = 0,
                BlockTargetDifficulty = 0,
                BlockTimestamp = this.GetCurrentBlockTimeStamp(),
                BlockTimestampUnix = 0,
                BlockVersion = 1,
                MerkleRootHash = ByteArray.Empty,
                PreviousBlockHash = this.previousBlockHash,
            };

            this.blockTimeStamp = this.blockTimeStamp.AddDays(1);

            string expectedFileName = string.Format(CultureInfo.InvariantCulture, "blk{0:00000}.dat", this.GetCurentBlockchainFileIndex());
            Block block = new Block(expectedFileName, blockHeader);

            Transaction transaction = new Transaction()
            {
                TransactionHash = transactionHash,
                TransactionLockTime = 0,
                TransactionVersion = 1,
            };

            foreach (InputInfo inputInfo in inputs)
            {
                TransactionInput transactionInput = new TransactionInput()
                {
                    InputScript = ByteArray.Empty,
                    SourceTransactionHash = inputInfo.SourceTransactionHash,
                    SourceTransactionOutputIndex = inputInfo.SourceTransactionOutputIndex,
                };
                transaction.AddInput(transactionInput);
            }

            foreach (OutputInfo outputInfo in outputs)
            {
                TransactionOutput transactionOutput = new TransactionOutput()
                {
                    OutputScript = ByteArray.Empty,
                    OutputValueSatoshi = (ulong)(outputInfo.OutputValueSatoshi * DatabaseGenerator.BtcToSatoshi),
                };

                transaction.AddOutput(transactionOutput);
            }

            block.AddTransaction(transaction);

            this.previousBlockHash = block.BlockHeader.BlockHash;

            return block;
        }
        /// <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;
        }