示例#1
0
        /// <inheritdoc/>
        public async Task <BlockExecutionResult> ExecuteBlock(IBlock block)
        {
            if (_isMining)
            {
                _logger?.Trace($"Prevent block {block.BlockHashToHex} from entering block execution," +
                               "for this node is doing mining.");
                return(BlockExecutionResult.Mining);
            }

            _current = block.BlockHashToHex;

            var result = Prepare(block);

            if (result.IsFailed())
            {
                _current = null;
                return(result);
            }

            var stopwatch = new Stopwatch();

            stopwatch.Start();

            _executing = true;
            if (_terminated)
            {
                return(BlockExecutionResult.Terminated);
            }

            var txnRes   = new List <TransactionResult>();
            var readyTxs = new List <Transaction>();
            var cts      = new CancellationTokenSource();

            var res = BlockExecutionResult.Fatal;

            try
            {
                // get txn from pool
                var tuple = CollectTransactions(block);
                result   = tuple.Item1;
                readyTxs = tuple.Item2;
                if (result.IsFailed() || readyTxs.Count == 0)
                {
                    _logger?.Warn($"Collect transaction from block failed: {result}, block height: {block.Header.Index}, " +
                                  $"block hash: {block.BlockHashToHex}.");
                    res = result;
                    return(res);
                }

                double distanceToTimeSlot = 0;
                if (_isLimitExecutionTime)
                {
                    distanceToTimeSlot = await _dpoSInfoProvider.GetDistanceToTimeSlotEnd();

                    cts.CancelAfter(TimeSpan.FromMilliseconds(distanceToTimeSlot * NodeConfig.Instance.RatioSynchronize));
                }

                var trs = await _txHub.GetReceiptsForAsync(readyTxs, cts);

                if (cts.IsCancellationRequested)
                {
                    return(BlockExecutionResult.ExecutionCancelled);
                }

                foreach (var tr in trs)
                {
                    if (!tr.IsExecutable)
                    {
                        throw new InvalidBlockException($"Transaction is not executable, transaction: {tr}, " +
                                                        $"block height: {block.Header.Index}, block hash: {block.BlockHashToHex}, SignatureSt:{tr.SignatureSt},RefBlockSt:{tr.RefBlockSt},Status:{tr.Status}");
                    }
                }

                txnRes = await ExecuteTransactions(readyTxs, block.Header.ChainId, block.Header.GetDisambiguationHash(), cts);

                if (cts.IsCancellationRequested)
                {
                    _logger?.Trace($"Execution Cancelled and rollback: block hash: {block.BlockHashToHex}, execution time: {distanceToTimeSlot * NodeConfig.Instance.RatioSynchronize} ms.");
                    Rollback(block, txnRes).ConfigureAwait(false);
                    return(BlockExecutionResult.ExecutionCancelled);
                }

                txnRes = SortToOriginalOrder(txnRes, readyTxs);

                var blockChain = _chainService.GetBlockChain(Hash.LoadHex(ChainConfig.Instance.ChainId));
                if (await blockChain.GetBlockByHashAsync(block.GetHash()) != null)
                {
                    res = BlockExecutionResult.AlreadyAppended;
                    return(res);
                }

                result = UpdateWorldState(block, txnRes);
                if (result.IsFailed())
                {
                    res = result;
                    return(res);
                }

                await UpdateCrossChainInfo(block, txnRes);

                // BlockExecuting -> BlockAppending
                // ExecutingLoop -> BlockAppending
                MessageHub.Instance.Publish(StateEvent.StateUpdated);

                await AppendBlock(block);

                InsertTxs(txnRes, block);

                await _txHub.OnNewBlock((Block)block);

                res = BlockExecutionResult.Success;
                return(res);
            }
            catch (Exception e)
            {
                _logger?.Error(e, $"Exception while execute block {block.BlockHashToHex}.");
                // TODO, no wait may need improve
                Rollback(block, txnRes).ConfigureAwait(false);

                return(res);
            }
            finally
            {
                _current   = null;
                _executing = false;
                cts.Dispose();
                if (_prepareTerminated)
                {
                    _terminated = true;
                    MessageHub.Instance.Publish(new TerminatedModule(TerminatedModuleEnum.BlockExecutor));
                }

                stopwatch.Stop();
                if (res.CanExecuteAgain())
                {
                    _logger?.Warn($"Block {block.BlockHashToHex} can execute again.");
                }

                _logger?.Info($"Executed block {block.BlockHashToHex} with result {res}, {block.Body.Transactions.Count} txns, " +
                              $"duration {stopwatch.ElapsedMilliseconds} ms.");
            }
        }