private bool ExecuteTransaction(int index, Transaction transaction, byte[] script, Address validator, Timestamp time, StorageChangeSetContext changeSet , Action <Hash, Event> onNotify, OracleReader oracle, ITask task, BigInteger minimumFee, out VMObject result, bool allowModify = true) { if (!transaction.HasSignatures) { throw new ChainException("Cannot execute unsigned transaction"); } result = null; uint offset = 0; RuntimeVM runtime; using (var m = new ProfileMarker("new RuntimeVM")) { runtime = new RuntimeVM(index, script, offset, this, validator, time, transaction, changeSet, oracle, task, false); } runtime.MinimumFee = minimumFee; ExecutionState state; using (var m = new ProfileMarker("runtime.Execute")) state = runtime.Execute(); if (state != ExecutionState.Halt) { return(false); } //var cost = runtime.UsedGas; using (var m = new ProfileMarker("runtime.Events")) { foreach (var evt in runtime.Events) { using (var m2 = new ProfileMarker(evt.ToString())) if (allowModify) { onNotify(transaction.Hash, evt); } } } if (runtime.Stack.Count > 0) { result = runtime.Stack.Pop(); } // merge transaction oracle data oracle.MergeTxData(); return(true); }
public StorageChangeSetContext ProcessTransactions(Block block, IEnumerable <Transaction> transactions , OracleReader oracle, BigInteger minimumFee, bool allowModify = true) { if (allowModify) { block.CleanUp(); } var changeSet = new StorageChangeSetContext(this.Storage); transactions = ProcessPendingTasks(block, oracle, minimumFee, changeSet, allowModify).Concat(transactions); int txIndex = 0; foreach (var tx in transactions) { VMObject vmResult; try { using (var m = new ProfileMarker("ExecuteTransaction")) { if (ExecuteTransaction(txIndex, tx, tx.Script, block.Validator, block.Timestamp, changeSet, block.Notify, oracle, ChainTask.Null, minimumFee, out vmResult, allowModify)) { // merge transaction oracle data oracle.MergeTxData(); if (vmResult != null) { if (allowModify) { var resultBytes = Serialization.Serialize(vmResult); block.SetResultForHash(tx.Hash, resultBytes); } } } else { throw new InvalidTransactionException(tx.Hash, "script execution failed"); } } } catch (Exception e) { e = e.ExpandInnerExceptions(); if (tx == null) { throw new BlockGenerationException(e.Message); } throw new InvalidTransactionException(tx.Hash, e.Message); } txIndex++; } if (this.IsRoot) { var inflationReady = NativeContract.LoadFieldFromStorage <bool>(changeSet, NativeContractKind.Gas, nameof(GasContract._inflationReady)); if (inflationReady) { var script = new ScriptBuilder() .AllowGas(block.Validator, Address.Null, minimumFee, 999999) .CallContract(NativeContractKind.Gas, nameof(GasContract.ApplyInflation), block.Validator) .SpendGas(block.Validator) .EndScript(); var transaction = new Transaction(this.Nexus.Name, this.Name, script, block.Timestamp.Value + 1, "SYSTEM"); VMObject vmResult; if (!ExecuteTransaction(-1, transaction, transaction.Script, block.Validator, block.Timestamp, changeSet, block.Notify, oracle, ChainTask.Null, minimumFee, out vmResult, allowModify)) { throw new ChainException("failed to execute inflation transaction"); } transactions = transactions.Concat(new Transaction[] { transaction }); } } if (block.Protocol > DomainSettings.LatestKnownProtocol) { throw new BlockGenerationException($"unexpected protocol number {block.Protocol}, maybe software update required?"); } // Only check protocol version if block is created on this node, no need to check if it's a non validator node. if (allowModify) { var expectedProtocol = Nexus.GetGovernanceValue(Nexus.RootStorage, Nexus.NexusProtocolVersionTag); if (block.Protocol != expectedProtocol) { throw new BlockGenerationException($"invalid protocol number {block.Protocol}, expected protocol {expectedProtocol}"); } using (var m = new ProfileMarker("CloseBlock")) { CloseBlock(block, changeSet); } } return(changeSet); }
private TaskResult ProcessPendingTask(Block block, OracleReader oracle, BigInteger minimumFee, StorageChangeSetContext changeSet, bool allowModify, ChainTask task, out Transaction transaction) { transaction = null; if (task.Mode != TaskFrequencyMode.Always) { var taskKey = GetTaskKey(task.ID, "task_run"); BigInteger lastRun = changeSet.Has(taskKey) ? changeSet.Get <BigInteger>(taskKey) : 0; switch (task.Mode) { case TaskFrequencyMode.Blocks: { var diff = block.Height - lastRun; if (diff < task.Frequency) { return(TaskResult.Skipped); // skip execution for now } break; } case TaskFrequencyMode.Time: { var diff = block.Timestamp.Value - lastRun; if (diff < task.Frequency) { return(TaskResult.Skipped); // skip execution for now } break; } } } using (var m = new ProfileMarker("ExecuteTask")) { var taskScript = new ScriptBuilder() .AllowGas(task.Owner, Address.Null, minimumFee, task.GasLimit) .CallContract(task.ContextName, task.Method) .SpendGas(task.Owner) .EndScript(); transaction = new Transaction(this.Nexus.Name, this.Name, taskScript, block.Timestamp.Value + 1, "TASK"); VMObject vmResult; if (ExecuteTransaction(-1, transaction, transaction.Script, block.Validator, block.Timestamp, changeSet, block.Notify, oracle, task, minimumFee, out vmResult, allowModify)) { // merge transaction oracle data oracle.MergeTxData(); var resultBytes = Serialization.Serialize(vmResult); block.SetResultForHash(transaction.Hash, resultBytes); var shouldStop = vmResult.AsBool(); return(shouldStop ? TaskResult.Halted : TaskResult.Running); } else { transaction = null; return(TaskResult.Crashed); } } }