コード例 #1
0
ファイル: Block.cs プロジェクト: wymoon2690/PhantasmaChain
 internal void MergeOracle(OracleReader oracle)
 {
     if (oracle.Entries.Any())
     {
         _oracleData = oracle.Entries.ToList();
         _dirty      = true;
     }
 }
コード例 #2
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);
        }
コード例 #3
0
        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>());
        }
コード例 #4
0
        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);
        }
コード例 #5
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);
        }
コード例 #6
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);
                }
            }
        }
コード例 #7
0
ファイル: Chain.cs プロジェクト: yjsyyyjszf/PhantasmaChain
        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);
        }
コード例 #8
0
        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);
                }
            }
        }