private async Task ExecuteInlineTransactions(int depth, Timestamp currentBlockTime, ITransactionContext txContext, TieredStateCache internalStateCache, IChainContext internalChainContext, CancellationToken cancellationToken) { var trace = txContext.Trace; if (txContext.Trace.IsSuccessful() && txContext.Trace.InlineTransactions.Count > 0) { internalStateCache.Update(txContext.Trace.GetFlattenedWrites() .Select(x => new KeyValuePair <string, byte[]>(x.Key, x.Value.ToByteArray()))); foreach (var inlineTx in txContext.Trace.InlineTransactions) { var inlineTrace = await ExecuteOneAsync(depth + 1, internalChainContext, inlineTx, currentBlockTime, cancellationToken, txContext.Origin); trace.InlineTraces.Add(inlineTrace); if (!inlineTrace.IsSuccessful()) { Logger.LogError($"Method name: {inlineTx.MethodName}, {inlineTrace.Error}"); // Already failed, no need to execute remaining inline transactions break; } internalStateCache.Update(inlineTrace.GetFlattenedWrites() .Select(x => new KeyValuePair <string, byte[]>(x.Key, x.Value.ToByteArray()))); } } }
private async Task <bool> ExecutePluginOnPostTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; foreach (var plugin in _postPlugins) { var transactions = await plugin.GetPostTransactionsAsync(executive.Descriptors, txContext); foreach (var postTx in transactions) { var postTrace = await ExecuteOneAsync(0, internalChainContext, postTx, currentBlockTime, cancellationToken, isCancellable : false); trace.PostTransactions.Add(postTx); trace.PostTraces.Add(postTrace); if (!postTrace.IsSuccessful()) { trace.ExecutionStatus = ExecutionStatus.Postfailed; postTrace.SurfaceUpError(); trace.Error += postTrace.Error; return(false); } internalStateCache.Update(postTrace.GetFlattenedWrites() .Select(x => new KeyValuePair <string, byte[]>(x.Key, x.Value.ToByteArray()))); } } return(true); }
private async Task ExecuteInlineTransactions(int depth, Timestamp currentBlockTime, ITransactionContext txContext, TieredStateCache internalStateCache, IChainContext internalChainContext, CancellationToken cancellationToken) { var trace = txContext.Trace; internalStateCache.Update(txContext.Trace.GetStateSets()); foreach (var inlineTx in txContext.Trace.InlineTransactions) { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = depth + 1, ChainContext = internalChainContext, Transaction = inlineTx, CurrentBlockTime = currentBlockTime, Origin = txContext.Origin }; var inlineTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken); if (inlineTrace == null) { break; } trace.InlineTraces.Add(inlineTrace); if (!inlineTrace.IsSuccessful()) { Logger.LogWarning($"Method name: {inlineTx.MethodName}, {inlineTrace.Error}"); // Already failed, no need to execute remaining inline transactions break; } internalStateCache.Update(inlineTrace.GetStateSets()); } }
private async Task <bool> ExecutePluginOnPreTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; foreach (var plugin in _prePlugins) { var transactions = await plugin.GetPreTransactionsAsync(executive.Descriptors, txContext); foreach (var preTx in transactions) { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = 0, ChainContext = internalChainContext, Transaction = preTx, CurrentBlockTime = currentBlockTime }; var preTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken); if (preTrace == null) { return(false); } trace.PreTransactions.Add(preTx); trace.PreTraces.Add(preTrace); if (preTx.MethodName == "ChargeTransactionFees") { var txFee = new TransactionFee(); txFee.MergeFrom(preTrace.ReturnValue); trace.TransactionFee = txFee; } if (!preTrace.IsSuccessful()) { return(false); } var stateSets = preTrace.GetStateSets().ToList(); internalStateCache.Update(stateSets); var parentStateCache = txContext.StateCache as TieredStateCache; parentStateCache?.Update(stateSets); if (trace.TransactionFee == null || !trace.TransactionFee.IsFailedToCharge) { continue; } preTrace.ExecutionStatus = ExecutionStatus.Executed; return(false); } } return(true); }
private async Task <bool> ExecutePluginOnPostTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; if (!trace.IsSuccessful()) { // If failed to execute this tx, at least we need to commit pre traces. internalStateCache = new TieredStateCache(txContext.StateCache); foreach (var preTrace in txContext.Trace.PreTraces) { var stateSets = preTrace.GetStateSets(); internalStateCache.Update(stateSets); } internalChainContext.StateCache = internalStateCache; } foreach (var plugin in _postPlugins) { var transactions = await plugin.GetPostTransactionsAsync(executive.Descriptors, txContext); foreach (var postTx in transactions) { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = 0, ChainContext = internalChainContext, Transaction = postTx, CurrentBlockTime = currentBlockTime, OriginTransactionId = txContext.OriginTransactionId }; var postTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken); if (postTrace == null) { return(false); } trace.PostTransactions.Add(postTx); trace.PostTraces.Add(postTrace); if (!postTrace.IsSuccessful()) { return(false); } internalStateCache.Update(postTrace.GetStateSets()); } } return(true); }
private async Task <bool> ExecutePluginOnPreTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; foreach (var plugin in _prePlugins) { var transactions = await plugin.GetPreTransactionsAsync(executive.Descriptors, txContext); foreach (var preTx in transactions) { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = 0, //TODO: this 0 means it is possible that pre/post txs could have own pre/post txs ChainContext = internalChainContext, Transaction = preTx, CurrentBlockTime = currentBlockTime, OriginTransactionId = txContext.OriginTransactionId }; var preTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken); if (preTrace == null) { return(false); } trace.PreTransactions.Add(preTx); trace.PreTraces.Add(preTrace); if (!preTrace.IsSuccessful()) { return(false); } var stateSets = preTrace.GetStateSets().ToList(); internalStateCache.Update(stateSets); var parentStateCache = txContext.StateCache as TieredStateCache; parentStateCache?.Update(stateSets); if (!plugin.IsStopExecuting(preTrace.ReturnValue, out var error)) { continue; } // If pre-tx fails, still commit the changes, but return false to notice outside to stop the execution. preTrace.Error = error; preTrace.ExecutionStatus = ExecutionStatus.Executed; return(false); } } return(true); }
public async Task <List <ExecutionReturnSet> > ExecuteAsync(BlockHeader blockHeader, List <Transaction> transactions, CancellationToken cancellationToken, bool throwException) { var groupStateCache = new TieredStateCache(); var groupChainContext = new ChainContextWithTieredStateCache(blockHeader.PreviousBlockHash, blockHeader.Height - 1, groupStateCache); var returnSets = new List <ExecutionReturnSet>(); foreach (var transaction in transactions) { if (cancellationToken.IsCancellationRequested) { break; } var trace = await ExecuteOneAsync(0, groupChainContext, transaction, blockHeader.Time.ToDateTime(), cancellationToken); if (!trace.IsSuccessful()) { if (throwException) { Logger.LogError(trace.StdErr); } trace.SurfaceUpError(); } else { groupStateCache.Update(trace.GetFlattenedWrite() .Select(x => new KeyValuePair <string, byte[]>(x.Key, x.Value.ToByteArray()))); } if (trace.StdErr != string.Empty) { Logger.LogError(trace.StdErr); } var result = GetTransactionResult(trace, blockHeader.Height); if (result != null) { await _transactionResultService.AddTransactionResultAsync(result, blockHeader); } var returnSet = GetReturnSet(trace, result); returnSets.Add(returnSet); } return(returnSets); }
private async Task ExecuteInlineTransactions(int depth, Timestamp currentBlockTime, ITransactionContext txContext, TieredStateCache internalStateCache, IChainContext internalChainContext, CancellationToken cancellationToken) { var trace = txContext.Trace; internalStateCache.Update(txContext.Trace.GetStateSets()); foreach (var inlineTx in txContext.Trace.InlineTransactions) { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = depth + 1, ChainContext = internalChainContext, Transaction = inlineTx, CurrentBlockTime = currentBlockTime, Origin = txContext.Origin }; // Only system contract can send TransferFrom tx as inline tx. if (!_inlineTransactionValidationService.Validate(inlineTx)) { break; } var inlineTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken); if (inlineTrace == null) { break; } trace.InlineTraces.Add(inlineTrace); if (!inlineTrace.IsSuccessful()) { // Already failed, no need to execute remaining inline transactions break; } internalStateCache.Update(inlineTrace.GetStateSets()); } }
private static bool TryUpdateStateCache(TransactionTrace trace, TieredStateCache groupStateCache) { if (trace == null) { return(false); } if (!trace.IsSuccessful()) { var transactionExecutingStateSets = new List <TransactionExecutingStateSet>(); AddToTransactionStateSets(transactionExecutingStateSets, trace.PreTraces); AddToTransactionStateSets(transactionExecutingStateSets, trace.PostTraces); groupStateCache.Update(transactionExecutingStateSets); trace.SurfaceUpError(); } else { groupStateCache.Update(trace.GetStateSets()); } return(true); }
private async Task <TransactionTrace> ExecuteOneAsync(int depth, IChainContext chainContext, Transaction transaction, Timestamp currentBlockTime, CancellationToken cancellationToken, Address origin = null, bool isCancellable = true) { if (isCancellable && cancellationToken.IsCancellationRequested) { return(new TransactionTrace { TransactionId = transaction.GetHash(), ExecutionStatus = ExecutionStatus.Canceled, Error = "Execution cancelled" }); } if (transaction.To == null || transaction.From == null) { throw new Exception($"error tx: {transaction}"); } var trace = new TransactionTrace { TransactionId = transaction.GetHash() }; var txContext = new TransactionContext { PreviousBlockHash = chainContext.BlockHash, CurrentBlockTime = currentBlockTime, Transaction = transaction, BlockHeight = chainContext.BlockHeight + 1, Trace = trace, CallDepth = depth, StateCache = chainContext.StateCache, Origin = origin != null ? origin : transaction.From }; var internalStateCache = new TieredStateCache(chainContext.StateCache); var internalChainContext = new ChainContextWithTieredStateCache(chainContext, internalStateCache); IExecutive executive; try { executive = await _smartContractExecutiveService.GetExecutiveAsync( internalChainContext, transaction.To); } catch (SmartContractFindRegistrationException e) { txContext.Trace.ExecutionStatus = ExecutionStatus.ContractError; txContext.Trace.Error += "Invalid contract address.\n"; return(trace); } try { #region PreTransaction if (depth == 0) { if (!await ExecutePluginOnPreTransactionStageAsync(executive, txContext, currentBlockTime, internalChainContext, internalStateCache, cancellationToken)) { return(trace); } } #endregion await executive.ApplyAsync(txContext); await ExecuteInlineTransactions(depth, currentBlockTime, txContext, internalStateCache, internalChainContext, cancellationToken); #region PostTransaction if (depth == 0) { if (!await ExecutePluginOnPostTransactionStageAsync(executive, txContext, currentBlockTime, internalChainContext, internalStateCache, cancellationToken)) { return(trace); } } #endregion } catch (Exception ex) { Logger.LogError($"Tx execution failed: {txContext}"); txContext.Trace.ExecutionStatus = ExecutionStatus.ContractError; txContext.Trace.Error += ex + "\n"; throw; } finally { await _smartContractExecutiveService.PutExecutiveAsync(transaction.To, executive); await LocalEventBus.PublishAsync(new TransactionExecutedEventData { TransactionTrace = trace }); } return(trace); }
private async Task <TransactionTrace> ExecuteOneAsync(SingleTransactionExecutingDto singleTxExecutingDto, CancellationToken cancellationToken) { if (singleTxExecutingDto.IsCancellable) { cancellationToken.ThrowIfCancellationRequested(); } var txContext = CreateTransactionContext(singleTxExecutingDto, out var trace); var internalStateCache = new TieredStateCache(singleTxExecutingDto.ChainContext.StateCache); var internalChainContext = new ChainContextWithTieredStateCache(singleTxExecutingDto.ChainContext, internalStateCache); IExecutive executive; try { executive = await _smartContractExecutiveService.GetExecutiveAsync( internalChainContext, singleTxExecutingDto.Transaction.To); } catch (SmartContractFindRegistrationException) { txContext.Trace.ExecutionStatus = ExecutionStatus.ContractError; txContext.Trace.Error += "Invalid contract address.\n"; return(trace); } try { #region PreTransaction if (singleTxExecutingDto.Depth == 0) { if (!await ExecutePluginOnPreTransactionStageAsync(executive, txContext, singleTxExecutingDto.CurrentBlockTime, internalChainContext, internalStateCache, cancellationToken)) { trace.ExecutionStatus = ExecutionStatus.Prefailed; return(trace); } } #endregion await executive.ApplyAsync(txContext); if (txContext.Trace.IsSuccessful()) { await ExecuteInlineTransactions(singleTxExecutingDto.Depth, singleTxExecutingDto.CurrentBlockTime, txContext, internalStateCache, internalChainContext, cancellationToken); } #region PostTransaction if (singleTxExecutingDto.Depth == 0) { if (!await ExecutePluginOnPostTransactionStageAsync(executive, txContext, singleTxExecutingDto.CurrentBlockTime, internalChainContext, internalStateCache, cancellationToken)) { trace.ExecutionStatus = ExecutionStatus.Postfailed; return(trace); } } #endregion } catch (Exception ex) { Logger.LogError(ex, "Transaction execution failed."); txContext.Trace.ExecutionStatus = ExecutionStatus.ContractError; txContext.Trace.Error += ex + "\n"; throw; } finally { await _smartContractExecutiveService.PutExecutiveAsync(singleTxExecutingDto.ChainContext, singleTxExecutingDto.Transaction.To, executive); #if DEBUG await LocalEventBus.PublishAsync(new TransactionExecutedEventData { TransactionTrace = trace }); #endif } return(trace); }
private async Task <TransactionTrace> ExecuteOneAsync(int depth, IChainContext chainContext, Transaction transaction, DateTime currentBlockTime, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new TransactionTrace() { TransactionId = transaction.GetHash(), ExecutionStatus = ExecutionStatus.Canceled }); } if (transaction.To == null || transaction.From == null) { throw new Exception($"error tx: {transaction}"); } var trace = new TransactionTrace { TransactionId = transaction.GetHash() }; var txCtxt = new TransactionContext { PreviousBlockHash = chainContext.BlockHash, CurrentBlockTime = currentBlockTime, Transaction = transaction, BlockHeight = chainContext.BlockHeight + 1, Trace = trace, CallDepth = depth, StateCache = chainContext.StateCache }; var internalStateCache = new TieredStateCache(chainContext.StateCache); var internalChainContext = new ChainContextWithTieredStateCache(chainContext, internalStateCache); var executive = await _smartContractExecutiveService.GetExecutiveAsync( internalChainContext, transaction.To); try { #region PreTransaction if (depth == 0) { foreach (var plugin in _plugins) { var transactions = await plugin.GetPreTransactionsAsync(executive.Descriptors, txCtxt); foreach (var preTx in transactions) { var preTrace = await ExecuteOneAsync(0, internalChainContext, preTx, currentBlockTime, cancellationToken); trace.PreTransactions.Add(preTx); trace.PreTraces.Add(preTrace); if (!preTrace.IsSuccessful()) { trace.ExecutionStatus = ExecutionStatus.Prefailed; return(trace); } internalStateCache.Update(preTrace.GetFlattenedWrite() .Select(x => new KeyValuePair <string, byte[]>(x.Key, x.Value.ToByteArray()))); } } } #endregion await executive.ApplyAsync(txCtxt); if (txCtxt.Trace.IsSuccessful() && txCtxt.Trace.InlineTransactions.Count > 0) { internalStateCache.Update(txCtxt.Trace.GetFlattenedWrite() .Select(x => new KeyValuePair <string, byte[]>(x.Key, x.Value.ToByteArray()))); foreach (var inlineTx in txCtxt.Trace.InlineTransactions) { var inlineTrace = await ExecuteOneAsync(depth + 1, internalChainContext, inlineTx, currentBlockTime, cancellationToken); trace.InlineTraces.Add(inlineTrace); if (!inlineTrace.IsSuccessful()) { // Fail already, no need to execute remaining inline transactions break; } internalStateCache.Update(inlineTrace.GetFlattenedWrite() .Select(x => new KeyValuePair <string, byte[]>(x.Key, x.Value.ToByteArray()))); } } } catch (Exception ex) { txCtxt.Trace.ExecutionStatus = ExecutionStatus.ContractError; txCtxt.Trace.StdErr += ex + "\n"; throw; } finally { await _smartContractExecutiveService.PutExecutiveAsync(transaction.To, executive); } return(trace); }
private async Task <bool> ExecutePluginOnPostTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; if (!trace.IsSuccessful()) { internalStateCache = new TieredStateCache(txContext.StateCache); foreach (var preTrace in txContext.Trace.PreTraces) { var stateSets = preTrace.GetStateSets(); internalStateCache.Update(stateSets); } internalChainContext.StateCache = internalStateCache; } foreach (var plugin in _postPlugins) { var transactions = await plugin.GetPostTransactionsAsync(executive.Descriptors, txContext); foreach (var postTx in transactions) { TransactionTrace postTrace; try { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = 0, ChainContext = internalChainContext, Transaction = postTx, CurrentBlockTime = currentBlockTime, IsCancellable = false }; postTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken).WithCancellation(cancellationToken); } catch (OperationCanceledException) { Logger.LogTrace("execute transaction timeout"); return(false); } if (postTrace == null) { return(false); } trace.PostTransactions.Add(postTx); trace.PostTraces.Add(postTrace); if (!postTrace.IsSuccessful()) { trace.ExecutionStatus = ExecutionStatus.Postfailed; postTrace.SurfaceUpError(); trace.Error += postTrace.Error; return(false); } internalStateCache.Update(postTrace.GetStateSets()); } } return(true); }
private async Task <bool> ExecutePluginOnPreTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; foreach (var plugin in _prePlugins) { var transactions = await plugin.GetPreTransactionsAsync(executive.Descriptors, txContext); foreach (var preTx in transactions) { TransactionTrace preTrace; try { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = 0, ChainContext = internalChainContext, Transaction = preTx, CurrentBlockTime = currentBlockTime }; preTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken).WithCancellation(cancellationToken); } catch (OperationCanceledException) { Logger.LogTrace("execute transaction timeout"); return(false); } if (preTrace == null) { return(false); } trace.PreTransactions.Add(preTx); trace.PreTraces.Add(preTrace); if (preTx.MethodName == "ChargeTransactionFees") { var txFee = new TransactionFee(); txFee.MergeFrom(preTrace.ReturnValue); trace.TransactionFee = txFee; } if (!preTrace.IsSuccessful()) { trace.ExecutionStatus = IsTransactionCanceled(preTrace) ? ExecutionStatus.Canceled : ExecutionStatus.Prefailed; preTrace.SurfaceUpError(); trace.Error += preTrace.Error; return(false); } var stateSets = preTrace.GetStateSets().ToList(); internalStateCache.Update(stateSets); var parentStateCache = txContext.StateCache as TieredStateCache; parentStateCache?.Update(stateSets); } } return(true); }
private async Task <bool> ExecutePluginOnPostTransactionStageAsync(IExecutive executive, ITransactionContext txContext, Timestamp currentBlockTime, IChainContext internalChainContext, TieredStateCache internalStateCache, CancellationToken cancellationToken) { var trace = txContext.Trace; if (!trace.IsSuccessful()) { internalStateCache = new TieredStateCache(txContext.StateCache); foreach (var preTrace in txContext.Trace.PreTraces) { var stateSets = preTrace.GetStateSets(); internalStateCache.Update(stateSets); } internalChainContext.StateCache = internalStateCache; } foreach (var plugin in _postPlugins) { var transactions = await plugin.GetPostTransactionsAsync(executive.Descriptors, txContext); foreach (var postTx in transactions) { var singleTxExecutingDto = new SingleTransactionExecutingDto { Depth = 0, ChainContext = internalChainContext, Transaction = postTx, CurrentBlockTime = currentBlockTime }; var postTrace = await ExecuteOneAsync(singleTxExecutingDto, cancellationToken); if (postTrace == null) { return(false); } trace.PostTransactions.Add(postTx); trace.PostTraces.Add(postTrace); if (postTx.MethodName == "ChargeResourceToken") { var consumedResourceTokens = new ConsumedResourceTokens(); consumedResourceTokens.MergeFrom(postTrace.ReturnValue); trace.ConsumedResourceTokens = consumedResourceTokens; } if (!postTrace.IsSuccessful()) { return(false); } internalStateCache.Update(postTrace.GetStateSets()); } } return(true); }