private static TransformBlock<LoadedTx, LoadedTx> InitMerkleValidator(ChainedHeader chainedHeader, MerkleStream merkleStream, CancellationToken cancelToken) { return new TransformBlock<LoadedTx, LoadedTx>( loadedTx => { try { merkleStream.AddNode(new MerkleTreeNode(loadedTx.TxIndex, 0, loadedTx.Transaction.Hash, false)); } //TODO catch (InvalidOperationException) { throw CreateMerkleRootException(chainedHeader); } return loadedTx; }, new ExecutionDataflowBlockOptions { CancellationToken = cancelToken }); }
public static async Task ValidateBlockAsync(ICoreStorage coreStorage, IBlockchainRules rules, ChainedHeader chainedHeader, ISourceBlock<LoadedTx> loadedTxes, CancellationToken cancelToken = default(CancellationToken)) { // validate merkle root var merkleStream = new MerkleStream(); var merkleValidator = InitMerkleValidator(chainedHeader, merkleStream, cancelToken); // begin feeding the merkle validator loadedTxes.LinkTo(merkleValidator, new DataflowLinkOptions { PropagateCompletion = true }); // validate transactions var txValidator = InitTxValidator(rules, chainedHeader, cancelToken); // begin feeding the tx validator merkleValidator.LinkTo(txValidator, new DataflowLinkOptions { PropagateCompletion = true }); // validate scripts var scriptValidator = InitScriptValidator(rules, chainedHeader, cancelToken); // begin feeding the script validator txValidator.LinkTo(scriptValidator, new DataflowLinkOptions { PropagateCompletion = true }); await merkleValidator.Completion; await txValidator.Completion; await scriptValidator.Completion; if (!rules.BypassPrevTxLoading) { try { merkleStream.FinishPairing(); } //TODO catch (InvalidOperationException) { throw CreateMerkleRootException(chainedHeader); } if (merkleStream.RootNode.Hash != chainedHeader.MerkleRoot) throw CreateMerkleRootException(chainedHeader); } }
private ISourceBlock <T> InitMerkleValidator <T>(ChainedHeader chainedHeader, ISourceBlock <T> blockTxes, CancellationToken cancelToken) where T : BlockTx { var merkleStream = new MerkleStream <BlockTxNode>(); var txHashes = new HashSet <UInt256>(); var cve_2012_2459 = false; var merkleValidator = new TransformManyBlock <T, T>( blockTx => { if (cve_2012_2459) { return(new T[0]); } if (txHashes.Add(blockTx.Hash)) { try { merkleStream.AddNode(blockTx); return(new[] { blockTx }); } //TODO catch (InvalidOperationException) { throw CreateMerkleRootException(chainedHeader); } } else { // TODO this needs proper testing, and needs to be made sure this is a safe way to handle the attack // TODO the block should be unmutated before being shared onto the network // CVE-2012-2459 // - if a tx has been repeated, this may be a merkle tree malleability attack against the block // - finish the merkle stream early and verify if the root still matches the block header // // - if the root matches, this is a CVE-2012-2459 attack // - proceed to validate the block normally by ignoring the remaining duplicate transactions // // - if the root does not match // - the block truly did contain duplicate transactions and is invalid merkleStream.FinishPairing(); if (merkleStream.RootNode.Hash == chainedHeader.MerkleRoot) { cve_2012_2459 = true; } else { throw CreateMerkleRootException(chainedHeader); } //TODO throw exception anyway for the sake of the pull tester //return new DecodedBlockTx[0]; //TODO remove the attacked version of the block coreStorage.TryRemoveChainedHeader(chainedHeader.Hash); coreStorage.TryRemoveBlockTransactions(chainedHeader.Hash); //TODO fail the block as missing, not invalid throw new MissingDataException(chainedHeader.Hash); } }, new ExecutionDataflowBlockOptions { CancellationToken = cancelToken }); blockTxes.LinkTo(merkleValidator, new DataflowLinkOptions { PropagateCompletion = true }); return(OnCompleteBlock.Create(merkleValidator, () => { try { merkleStream.FinishPairing(); } //TODO catch (InvalidOperationException) { throw CreateMerkleRootException(chainedHeader); } if (merkleStream.RootNode.Hash != chainedHeader.MerkleRoot) { throw CreateMerkleRootException(chainedHeader); } }, cancelToken)); }