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