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. /// </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)); } }
/// <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)); transactionOutput.Address = AddressParser.GetAddressFromOutputScript(transactionOutput.OutputScript.ToArray()); return(transactionOutput); }
/// <summary> /// Parses a Zcash 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(); if (blockHeader.BlockVersion != 4) { 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.ReservedHash = 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 = new ByteArray(blockMemoryStreamReader.ReadBytes(32).ReverseByteArray()); int equihashStart = (int)blockMemoryStreamReader.BaseStream.Position; int solutionSize = (int)blockMemoryStreamReader.ReadVariableLengthInteger(); blockHeader.Solution = new ByteArray(blockMemoryStreamReader.ReadBytes(solutionSize)); int equihashSize = (int)blockMemoryStreamReader.BaseStream.Position - equihashStart; 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 + equihashSize) { // 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 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); }
private static CompressedElement ParseCompressedG2Element(BlockMemoryStreamReader blockMemoryStreamReader) { CompressedElement element = new CompressedElement(); byte leadingByte = blockMemoryStreamReader.ReadByte(); if ((leadingByte & (~1)) != ZcashConstants.G2_PREFIX_MASK) { throw new InvalidBlockchainContentException("Unexpected lead byte for G2 point"); } element.YIndicator = (leadingByte & 1) == 1; element.X = new ByteArray(blockMemoryStreamReader.ReadBytes(64)); return(element); }
/// <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(); 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); }