internal void MergeOracle(OracleReader oracle) { if (oracle.Entries.Any()) { _oracleData = oracle.Entries.ToList(); _dirty = true; } }
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); }
private IEnumerable <Transaction> ProcessPendingTasks(Block block, OracleReader oracle, BigInteger minimumFee, StorageChangeSetContext changeSet, bool allowModify) { var taskList = new StorageList(TaskListTag, changeSet); var taskCount = taskList.Count(); List <Transaction> transactions = null; int i = 0; while (i < taskCount) { var taskID = taskList.Get <BigInteger>(i); var task = GetTask(changeSet, taskID); Transaction tx; var taskResult = ProcessPendingTask(block, oracle, minimumFee, changeSet, allowModify, task, out tx); if (taskResult == TaskResult.Running) { i++; } else { taskList.RemoveAt <BigInteger>(i); } if (tx != null) { if (transactions == null) { transactions = new List <Transaction>(); } transactions.Add(tx); } } if (transactions != null) { return(transactions); } return(Enumerable.Empty <Transaction>()); }
private bool ExecuteTransaction(Transaction transaction, Timestamp time, StorageChangeSetContext changeSet, Action <Hash, Event> onNotify, OracleReader oracle, BigInteger minimumFee, out byte[] result) { result = null; var runtime = new RuntimeVM(transaction.Script, this, time, transaction, changeSet, oracle, false); runtime.MinimumFee = minimumFee; runtime.ThrowOnFault = true; var state = runtime.Execute(); if (state != ExecutionState.Halt) { return(false); } var cost = runtime.UsedGas; foreach (var evt in runtime.Events) { onNotify(transaction.Hash, evt); } if (runtime.Stack.Count > 0) { var obj = runtime.Stack.Pop(); result = Serialization.Serialize(obj); } 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); } } }
private bool ExecuteTransaction(int index, Transaction transaction, Timestamp time, StorageChangeSetContext changeSet, Action <Hash, Event> onNotify, OracleReader oracle, BigInteger minimumFee, out byte[] result) { result = null; RuntimeVM runtime; using (var m = new ProfileMarker("new RuntimeVM")) runtime = new RuntimeVM(index, transaction.Script, this, time, transaction, changeSet, oracle, false); runtime.MinimumFee = minimumFee; runtime.ThrowOnFault = true; 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())) onNotify(transaction.Hash, evt); } } if (runtime.Stack.Count > 0) { var obj = runtime.Stack.Pop(); result = Serialization.Serialize(obj); } return(true); }
private TaskResult ProcessPendingTask(Block block, OracleReader oracle, BigInteger minimumFee, StorageChangeSetContext changeSet, bool allowModify, ChainTask task, out Transaction transaction) { transaction = null; BigInteger currentRun = GetTaskTimeFromBlock(task.Mode, block); var taskKey = GetTaskKey(task.ID, "task_run"); if (task.Mode != TaskFrequencyMode.Always) { bool isFirstRun = !changeSet.Has(taskKey); if (isFirstRun) { var taskBlockHash = GetBlockHashAtHeight(task.Height); var taskBlock = GetBlockByHash(taskBlockHash); BigInteger firstRun = GetTaskTimeFromBlock(task.Mode, taskBlock) + task.Delay; if (currentRun < firstRun) { return(TaskResult.Skipped); // skip execution for now } } else { BigInteger lastRun = isFirstRun ? changeSet.Get <BigInteger>(taskKey) : 0; var diff = currentRun - lastRun; if (diff < task.Frequency) { return(TaskResult.Skipped); // skip execution for now } } } else { currentRun = 0; } 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)) { var resultBytes = Serialization.Serialize(vmResult); block.SetResultForHash(transaction.Hash, resultBytes); // update last_run value in storage if (currentRun > 0) { changeSet.Put <BigInteger>(taskKey, currentRun); } var shouldStop = vmResult.AsBool(); return(shouldStop ? TaskResult.Halted : TaskResult.Running); } else { transaction = null; return(TaskResult.Crashed); } } }