Example #1
0
        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);
        }
Example #2
0
        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);
        }
Example #3
0
        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);
                }
            }
        }