예제 #1
0
        // TODO - the way this operates is specific to the block validation pipeline, this should be more apparent
        public async Task ApplyChangesAsync()
        {
            CheckWriteTransaction();
            if (changesApplied)
            {
                throw new InvalidOperationException();
            }

            unspentTxes.WorkQueue.Complete();
            unspentTxOutputs.WorkQueue.Complete();
            await utxoApplier.Completion;
            await utxoApplier2.Completion;

            parentCursor.ChainTip           = chainTip;
            parentCursor.UnspentOutputCount = unspentOutputCount;
            parentCursor.UnspentTxCount     = unspentTxCount;
            parentCursor.TotalTxCount       = totalTxCount;
            parentCursor.TotalInputCount    = totalInputCount;
            parentCursor.TotalOutputCount   = totalOutputCount;

            if (headers.Updated.Count > 0)
            {
                throw new InvalidOperationException();
            }
            foreach (var chainedHeader in headers.Added)
            {
                if (!parentCursor.TryAddHeader(chainedHeader.Value))
                {
                    throw new InvalidOperationException();
                }
            }
            foreach (var blockHash in headers.Deleted)
            {
                if (!parentCursor.TryRemoveHeader(blockHash))
                {
                    throw new InvalidOperationException();
                }
            }

            if (blockSpentTxes.Updated.Count > 0)
            {
                throw new InvalidOperationException();
            }
            foreach (var spentTxes in blockSpentTxes.Added)
            {
                if (!parentCursor.TryAddBlockSpentTxes(spentTxes.Key, spentTxes.Value))
                {
                    throw new InvalidOperationException();
                }
            }
            foreach (var blockHeight in blockSpentTxes.Deleted)
            {
                if (!parentCursor.TryRemoveBlockSpentTxes(blockHeight))
                {
                    throw new InvalidOperationException();
                }
            }

            if (blockUnmintedTxes.Updated.Count > 0)
            {
                throw new InvalidOperationException();
            }
            foreach (var unmintedTxes in blockUnmintedTxes.Added)
            {
                if (!parentCursor.TryAddBlockUnmintedTxes(unmintedTxes.Key, unmintedTxes.Value))
                {
                    throw new InvalidOperationException();
                }
            }
            foreach (var blockHeight in blockUnmintedTxes.Deleted)
            {
                if (!parentCursor.TryRemoveBlockUnmintedTxes(blockHeight))
                {
                    throw new InvalidOperationException();
                }
            }

            changesApplied = true;
        }
예제 #2
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));
        }
예제 #3
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);
        }