Exemplo n.º 1
0
        public ISourceBlock <ValidatableTx> CalculateUtxo(IChainStateCursor chainStateCursor, Chain chain, ISourceBlock <DecodedBlockTx> blockTxes, CancellationToken cancelToken = default(CancellationToken))
        {
            var chainedHeader  = chain.LastBlock;
            var blockSpentTxes = new BlockSpentTxesBuilder();

            var utxoCalculator = new TransformBlock <DecodedBlockTx, ValidatableTx>(
                blockTx =>
            {
                var tx      = blockTx.Transaction;
                var txIndex = blockTx.Index;

                var prevTxOutputs = ImmutableArray.CreateBuilder <PrevTxOutput>(!blockTx.IsCoinbase ? tx.Inputs.Length : 0);

                //TODO apply real coinbase rule
                // https://github.com/bitcoin/bitcoin/blob/481d89979457d69da07edd99fba451fd42a47f5c/src/core.h#L219
                if (!blockTx.IsCoinbase)
                {
                    // spend each of the transaction's inputs in the utxo
                    for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++)
                    {
                        var input        = tx.Inputs[inputIndex];
                        var prevTxOutput = this.Spend(chainStateCursor, txIndex, tx, inputIndex, input, chainedHeader, blockSpentTxes);

                        prevTxOutputs.Add(prevTxOutput);
                    }
                }

                // there exist two duplicate coinbases in the blockchain, which the design assumes to be impossible
                // ignore the first occurrences of these duplicates so that they do not need to later be deleted from the utxo, an unsupported operation
                // no other duplicates will occur again, it is now disallowed
                var isDupeCoinbase = IsDupeCoinbase(chainedHeader, tx);

                // add transaction's outputs to utxo, except for the genesis block and the duplicate coinbases
                if (chainedHeader.Height > 0 && !isDupeCoinbase)
                {
                    // mint the transaction's outputs in the utxo
                    this.Mint(chainStateCursor, tx, txIndex, chainedHeader);

                    // increase unspent output count
                    chainStateCursor.UnspentOutputCount += tx.Outputs.Length;

                    // increment unspent tx count
                    chainStateCursor.UnspentTxCount++;

                    chainStateCursor.TotalTxCount++;
                    chainStateCursor.TotalInputCount  += tx.Inputs.Length;
                    chainStateCursor.TotalOutputCount += tx.Outputs.Length;
                }

                return(new ValidatableTx(blockTx, chainedHeader, prevTxOutputs.MoveToImmutable()));
            },
                new ExecutionDataflowBlockOptions {
                CancellationToken = cancelToken
            });

            blockTxes.LinkTo(utxoCalculator, new DataflowLinkOptions {
                PropagateCompletion = true
            });

            return(OnCompleteBlock.Create(utxoCalculator, () =>
            {
                if (!chainStateCursor.TryAddBlockSpentTxes(chainedHeader.Height, blockSpentTxes.ToImmutable()))
                {
                    throw new ValidationException(chainedHeader.Hash);
                }
            }, cancelToken));
        }
Exemplo n.º 2
0
        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));
        }