示例#1
0
        public static async Task ValidateBlockAsync(ICoreStorage coreStorage, ICoreRules rules, Chain newChain, ISourceBlock <ValidatableTx> validatableTxes, CancellationToken cancelToken = default(CancellationToken))
        {
            // tally transactions
            object finalTally = null;
            var    txTallier  = new TransformBlock <ValidatableTx, ValidatableTx>(
                validatableTx =>
            {
                var runningTally = finalTally;
                rules.TallyTransaction(newChain, validatableTx, ref runningTally);
                finalTally = runningTally;

                return(validatableTx);
            });

            validatableTxes.LinkTo(txTallier, new DataflowLinkOptions {
                PropagateCompletion = true
            });

            // validate transactions
            var txValidator = InitTxValidator(rules, newChain, cancelToken);

            // begin feeding the tx validator
            txTallier.LinkTo(txValidator, new DataflowLinkOptions {
                PropagateCompletion = true
            });

            // validate scripts
            var scriptValidator = InitScriptValidator(rules, newChain, cancelToken);

            // begin feeding the script validator
            txValidator.LinkTo(scriptValidator, new DataflowLinkOptions {
                PropagateCompletion = true
            });

            //TODO
            await PipelineCompletion.Create(
                new Task[] { },
                new IDataflowBlock[] { validatableTxes, txTallier, txValidator, scriptValidator });

            // validate overall block
            rules.PostValidateBlock(newChain, finalTally);
        }
示例#2
0
        public async Task AddBlockAsync(ChainedHeader chainedHeader, IEnumerable <BlockTx> blockTxes, CancellationToken cancelToken = default(CancellationToken))
        {
            var stopwatch  = Stopwatch.StartNew();
            var txCount    = 0;
            var inputCount = 0;

            var newChain = chain.Value.ToBuilder().AddBlock(chainedHeader).ToImmutable();

            // pre-validate block before doing any work
            rules.PreValidateBlock(newChain);

            using (var chainState = ToImmutable())
                using (var handle = storageManager.OpenDeferredChainStateCursor(chainState))
                {
                    var chainStateCursor = handle.Item;

                    //TODO note - the utxo updates could be applied either while validation is ongoing,
                    //TODO        or after it is complete, begin transaction here to apply immediately
                    chainStateCursor.BeginTransaction();

                    // verify storage chain tip matches this chain state builder's chain tip
                    CheckChainTip(chainStateCursor);

                    // begin reading and decoding block txes into the buffer
                    var blockTxesBuffer = new BufferBlock <BlockTx>();
                    var sendBlockTxes   = blockTxesBuffer.SendAndCompleteAsync(blockTxes, cancelToken);

                    var decodedTxes = new TransformBlock <BlockTx, DecodedBlockTx>(blockTx => blockTx.Decode(),
                                                                                   new ExecutionDataflowBlockOptions {
                        CancellationToken = cancelToken
                    });

                    // feed block txes through the merkle validator
                    var merkleBlockTxes = InitMerkleValidator(chainedHeader, blockTxesBuffer, cancelToken);
                    merkleBlockTxes.LinkTo(decodedTxes, new DataflowLinkOptions {
                        PropagateCompletion = true
                    });

                    // track tx/input stats
                    var countBlockTxes = new TransformBlock <DecodedBlockTx, DecodedBlockTx>(
                        blockTx =>
                    {
                        txCount++;
                        inputCount += blockTx.Transaction.Inputs.Length;
                        if (!blockTx.IsCoinbase)
                        {
                            stats.txRateMeasure.Tick();
                            stats.inputRateMeasure.Tick(blockTx.Transaction.Inputs.Length);
                        }

                        return(blockTx);
                    }, new ExecutionDataflowBlockOptions {
                        CancellationToken = cancelToken
                    });
                    decodedTxes.LinkTo(countBlockTxes, new DataflowLinkOptions {
                        PropagateCompletion = true
                    });

                    // warm-up utxo entries for block txes
                    var warmedBlockTxes = UtxoLookAhead.LookAhead(countBlockTxes, chainStateCursor, cancelToken);

                    // begin calculating the utxo updates
                    var validatableTxes = utxoBuilder.CalculateUtxo(chainStateCursor, newChain, warmedBlockTxes, cancelToken);

                    // begin validating the block
                    var blockValidator = BlockValidator.ValidateBlockAsync(coreStorage, rules, newChain, validatableTxes, cancelToken);

                    // prepare to finish applying chain state changes once utxo calculation has completed
                    var applyChainState =
                        validatableTxes.Completion.ContinueWith(_ =>
                    {
                        if (validatableTxes.Completion.Status == TaskStatus.RanToCompletion)
                        {
                            // finish applying the utxo changes, do not yet commit
                            chainStateCursor.ChainTip = chainedHeader;
                            chainStateCursor.TryAddHeader(chainedHeader);

                            return(chainStateCursor.ApplyChangesAsync());
                        }
                        else
                        {
                            return(Task.CompletedTask);
                        }
                    }).Unwrap();

                    var timingTasks = new List <Task>();

                    // time block txes read
                    timingTasks.Add(
                        blockTxesBuffer.Completion.ContinueWith(_ =>
                    {
                        lock (stopwatch)
                            stats.txesReadDurationMeasure.Tick(stopwatch.Elapsed);
                    }));

                    // time block txes decode
                    timingTasks.Add(
                        decodedTxes.Completion.ContinueWith(_ =>
                    {
                        lock (stopwatch)
                            stats.txesDecodeDurationMeasure.Tick(stopwatch.Elapsed);
                    }));

                    // time utxo look-ahead
                    timingTasks.Add(
                        warmedBlockTxes.Completion.ContinueWith(_ =>
                    {
                        lock (stopwatch)
                            stats.lookAheadDurationMeasure.Tick(stopwatch.Elapsed);
                    }));

                    // time utxo calculation
                    timingTasks.Add(
                        validatableTxes.Completion.ContinueWith(_ =>
                    {
                        lock (stopwatch)
                            stats.calculateUtxoDurationMeasure.Tick(stopwatch.Elapsed);
                    }));

                    // time utxo application
                    timingTasks.Add(
                        applyChainState.ContinueWith(_ =>
                    {
                        lock (stopwatch)
                            stats.applyUtxoDurationMeasure.Tick(stopwatch.Elapsed);
                    }));

                    // time block validation
                    timingTasks.Add(
                        blockValidator.ContinueWith(_ =>
                    {
                        lock (stopwatch)
                            stats.validateDurationMeasure.Tick(stopwatch.Elapsed);
                    }));

                    // wait for all tasks to complete, any exceptions that ocurred will be thrown
                    var pipelineCompletion = PipelineCompletion.Create(
                        new[]
                    {
                        sendBlockTxes, applyChainState, blockValidator
                    }
                        .Concat(timingTasks).ToArray(),
                        new IDataflowBlock[]
                    {
                        blockTxesBuffer, merkleBlockTxes, countBlockTxes, warmedBlockTxes, validatableTxes
                    }
                        .Concat(chainStateCursor.DataFlowBlocks).ToArray());
                    await pipelineCompletion;

                    var totalTxCount       = chainStateCursor.TotalTxCount;
                    var totalInputCount    = chainStateCursor.TotalInputCount;
                    var unspentTxCount     = chainStateCursor.UnspentTxCount;
                    var unspentOutputCount = chainStateCursor.UnspentOutputCount;

                    // only commit the utxo changes once block validation has completed
                    await commitLock.WaitAsync();

                    try
                    {
                        await chainStateCursor.CommitTransactionAsync();

                        chain = new Lazy <Chain>(() => newChain).Force();
                    }
                    finally
                    {
                        commitLock.Release();
                    }

                    stats.commitUtxoDurationMeasure.Tick(stopwatch.Elapsed);

                    // update total count stats
                    stats.Height             = chain.Value.Height;
                    stats.TotalTxCount       = totalTxCount;
                    stats.TotalInputCount    = totalInputCount;
                    stats.UnspentTxCount     = unspentTxCount;
                    stats.UnspentOutputCount = unspentOutputCount;
                }

            stats.blockRateMeasure.Tick();
            stats.txesPerBlockMeasure.Tick(txCount);
            stats.inputsPerBlockMeasure.Tick(inputCount);

            stats.addBlockDurationMeasure.Tick(stopwatch.Elapsed);

            LogBlockchainProgress();
        }