/// <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."); } }