/// <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 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 != 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(blockchainFileName, blockMemoryStreamReader)); } }
private static JoinSplit ParseJoinSplit(BlockMemoryStreamReader blockMemoryStreamReader) { JoinSplit joinSplit = new JoinSplit(); joinSplit.AmountIn = blockMemoryStreamReader.ReadUInt64(); joinSplit.AmountOut = blockMemoryStreamReader.ReadUInt64(); joinSplit.Anchor = new ByteArray(blockMemoryStreamReader.ReadBytes(32)); for (int i = 0; i < joinSplit.Nullifiers.Length; ++i) { joinSplit.Nullifiers[i] = new ByteArray(blockMemoryStreamReader.ReadBytes(32)); } for (int i = 0; i < joinSplit.Commitments.Length; ++i) { joinSplit.Commitments[i] = new ByteArray(blockMemoryStreamReader.ReadBytes(32)); } joinSplit.EphemeralKey = new ByteArray(blockMemoryStreamReader.ReadBytes(32)); joinSplit.RandomSeed = new ByteArray(blockMemoryStreamReader.ReadBytes(32)); for (int i = 0; i < joinSplit.MACs.Length; ++i) { joinSplit.MACs[i] = new ByteArray(blockMemoryStreamReader.ReadBytes(32)); } joinSplit.Proof = BlockchainParser.ParseZCProof(blockMemoryStreamReader); for (int i = 0; i < joinSplit.Ciphertexts.Length; ++i) { joinSplit.Ciphertexts[i] = new ByteArray(blockMemoryStreamReader.ReadBytes(ZcashConstants.ZC_NOTECIPHERTEXT_SIZE)); } return(joinSplit); }
/// <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 ZCProof ParseZCProof(BlockMemoryStreamReader blockMemoryStreamReader) { ZCProof proof = new ZCProof { G_A = BlockchainParser.ParseCompressedG1Element(blockMemoryStreamReader), G_A_Prime = BlockchainParser.ParseCompressedG1Element(blockMemoryStreamReader), G_B = BlockchainParser.ParseCompressedG2Element(blockMemoryStreamReader), G_B_Prime = BlockchainParser.ParseCompressedG1Element(blockMemoryStreamReader), G_C = BlockchainParser.ParseCompressedG1Element(blockMemoryStreamReader), G_C_Prime = BlockchainParser.ParseCompressedG1Element(blockMemoryStreamReader), G_K = BlockchainParser.ParseCompressedG1Element(blockMemoryStreamReader), G_H = BlockchainParser.ParseCompressedG1Element(blockMemoryStreamReader) }; return(proof); }
/// <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(); if (transaction.TransactionVersion >= 2) { int joinSplitCount = (int)blockMemoryStreamReader.ReadVariableLengthInteger(); for (int jsIndex = 0; jsIndex < joinSplitCount; ++jsIndex) { JoinSplit joinSplit = BlockchainParser.ParseJoinSplit(blockMemoryStreamReader); transaction.AddJoinSplit(joinSplit); } if (joinSplitCount > 0) { transaction.JoinSplitPublicKey = new ByteArray(blockMemoryStreamReader.ReadBytes(32)); transaction.JoinSplitSignature = new ByteArray(blockMemoryStreamReader.ReadBytes(64)); } } 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); }