public object InvokeContract(string contractName, string methodName, params object[] args)
        {
            var contract = FindContract <SmartContract>(contractName);

            Throw.IfNull(contract, nameof(contract));

            var script    = ScriptUtils.BeginScript().CallContract(contractName, methodName, args).EndScript();
            var changeSet = new StorageChangeSetContext(this.Storage);
            var vm        = new RuntimeVM(script, this, this.LastBlock, null, changeSet, true);

            contract.SetRuntimeData(vm);

            var state = vm.Execute();

            if (state != ExecutionState.Halt)
            {
                throw new ChainException($"Invocation of method '{methodName}' of contract '{contractName}' failed with state: " + state);
            }

            if (vm.Stack.Count == 0)
            {
                throw new ChainException($"No result, vm stack is empty");
            }

            var result = vm.Stack.Pop();

            return(result.ToObject());
        }
Exemple #2
0
        private static VMObject ExecuteScript(Chain chain, byte[] script, ContractInterface abi, string methodName, params object[] args)
        {
            var method = abi.FindMethod(methodName);

            if (method == null)
            {
                throw new Exception("ABI is missing: " + method.name);
            }

            var changeSet = new StorageChangeSetContext(chain.Storage);
            var oracle    = chain.Nexus.GetOracleReader();
            var vm        = new RuntimeVM(-1, script, (uint)method.offset, chain, Address.Null, Timestamp.Now, null, changeSet, oracle, ChainTask.Null, true);

            //var vm = new GasMachine(script, (uint)method.offset);

            // TODO maybe this needs to be in inverted order?
            foreach (var arg in args)
            {
                vm.Stack.Push(VMObject.FromObject(arg));
            }

            var result = vm.Execute();

            if (result == ExecutionState.Halt)
            {
                return(vm.Stack.Pop());
            }

            throw new Exception("Script execution failed for: " + method.name);
        }
Exemple #3
0
        internal bool Execute(Chain chain, Block block, StorageChangeSetContext changeSet, Action <Hash, Event> onNotify, out byte[] result)
        {
            result = null;

            var runtime = new RuntimeVM(this.Script, chain, block, this, changeSet, false);

            var state = runtime.Execute();

            if (state != ExecutionState.Halt)
            {
                return(false);
            }

            var cost = runtime.UsedGas;

            // fee distribution TODO
//            if (chain.NativeTokenAddress != null && cost > 0)
            {
                //chain.TransferToken(this.PublicKey, chain.DistributionPubKey, cost);
            }

            foreach (var evt in runtime.Events)
            {
                onNotify(this.Hash, evt);
            }

            if (runtime.Stack.Count > 0)
            {
                var obj = runtime.Stack.Pop();
                result = Serialization.Serialize(obj);
            }

            return(true);
        }
Exemple #4
0
        internal bool Execute(Chain chain, Block block, StorageChangeSetContext changeSet, Action <Hash, Event> onNotify, OracleReaderDelegate oracleReader, out byte[] result)
        {
            result = null;

            var runtime = new RuntimeVM(this.Script, chain, block, this, changeSet, false);

            runtime.ThrowOnFault = true;
            runtime.OracleReader = oracleReader;

            var state = runtime.Execute();

            if (state != ExecutionState.Halt)
            {
                return(false);
            }

            var cost = runtime.UsedGas;

            foreach (var evt in runtime.Events)
            {
                onNotify(this.Hash, evt);
            }

            if (runtime.Stack.Count > 0)
            {
                var obj = runtime.Stack.Pop();
                result = Serialization.Serialize(obj);
            }

            return(true);
        }
Exemple #5
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);
        }
Exemple #6
0
        public void TestDBChangeSetStorageMapClear()
        {
            var storage   = new KeyStoreStorage(CreateKeyStoreAdapterTest("test2"));
            var changeSet = new StorageChangeSetContext(storage);

            var testMapKey  = Encoding.UTF8.GetBytes($".test._valueMap");
            var testMapKey2 = Encoding.UTF8.GetBytes($".test2._valueMap");

            var testMap  = new StorageMap(testMapKey, changeSet);
            var testMap2 = new StorageMap(testMapKey2, changeSet);

            testMap.Set("test1", "Value1");
            testMap.Set("test2", "Value2");
            testMap.Set("test3", "Value3");
            testMap.Set("test4", "Value4");
            Assert.IsTrue(testMap.Count() == 4);

            testMap2.Set <BigInteger, string>(new BigInteger(1), "Value21");
            testMap2.Set <BigInteger, string>(new BigInteger(2), "Value22");
            testMap2.Set <BigInteger, string>(new BigInteger(3), "Value23");
            testMap2.Set <BigInteger, string>(new BigInteger(4), "Value24");
            Assert.IsTrue(testMap2.Count() == 4);

            changeSet.Execute();
            var count = 0;

            testMap.Visit <string, string>((key, value) => {
                count++;
            });

            testMap2.Visit <BigInteger, string>((key, value) => {
                count++;
            });

            Console.WriteLine($"visit: {count} count: {(int)testMap.Count() + testMap2.Count()}");
            Assert.AreEqual(count, (int)testMap.Count() + testMap2.Count());

            testMap.Clear();
            testMap2.Clear();

            Assert.IsTrue(testMap.Count() == 0);
            Assert.IsTrue(testMap2.Count() == 0);

            Assert.IsNull(testMap.Get <string, string>("test1"));
            Assert.IsNull(testMap.Get <string, string>("test2"));
            Assert.IsNull(testMap.Get <string, string>("test3"));
            Assert.IsNull(testMap.Get <string, string>("test4"));

            Assert.IsNull(testMap2.Get <BigInteger, string>(new BigInteger(1)));
            Assert.IsNull(testMap2.Get <BigInteger, string>(new BigInteger(2)));
            Assert.IsNull(testMap2.Get <BigInteger, string>(new BigInteger(3)));
            Assert.IsNull(testMap2.Get <BigInteger, string>(new BigInteger(4)));
        }
Exemple #7
0
        public void TestDBChangeSetStorageMap22()
        {
            var storage    = (StorageContext) new KeyStoreStorage(CreateKeyStoreAdapterTest("test2"));
            var changeSet  = new StorageChangeSetContext(storage);
            var testMapKey = Encoding.UTF8.GetBytes($".test._valueMap");
            var testMap    = new StorageMap(testMapKey, changeSet);

            testMap.Set(1, 1);
            Assert.IsTrue(testMap.Count() == 1);
            testMap.Clear();
            Assert.IsTrue(testMap.Count() == 0);
        }
Exemple #8
0
        private RuntimeVM ExecuteScript(string[] scriptString)
        {
            var script = BuildScript(scriptString);

            var keys  = KeyPair.Generate();
            var nexus = new Nexus("vmnet", keys.Address, new ConsoleLogger());
            var tx    = new Transaction(nexus.Name, nexus.RootChain.Name, script, 0, 0);

            var changeSet = new StorageChangeSetContext(new MemoryStorageContext());

            var vm = new RuntimeVM(tx.Script, nexus.RootChain, null, tx, changeSet, true);

            vm.Execute();

            return(vm);
        }
Exemple #9
0
        public RuntimeVM(byte[] script, Chain chain, Timestamp time, Transaction transaction, StorageChangeSetContext changeSet, OracleReader oracle, bool readOnlyMode, bool delayPayment = false) : base(script)
        {
            Core.Throw.IfNull(chain, nameof(chain));
            Core.Throw.IfNull(changeSet, nameof(changeSet));

            // NOTE: block and transaction can be null, required for Chain.InvokeContract
            //Throw.IfNull(block, nameof(block));
            //Throw.IfNull(transaction, nameof(transaction));

            this.MinimumFee   = 1;
            this.GasPrice     = 0;
            this.UsedGas      = 0;
            this.PaidGas      = 0;
            this.GasTarget    = chain.Address;
            this.MaxGas       = 10000; // a minimum amount required for allowing calls to Gas contract etc
            this.DelayPayment = delayPayment;

            this.Time         = time;
            this.Chain        = chain;
            this.Transaction  = transaction;
            this.Oracle       = oracle;
            this.changeSet    = changeSet;
            this.readOnlyMode = readOnlyMode;

            this.isBlockOperation = false;
            this.randomized       = false;

            this.FeeTargetAddress = Address.Null;

            if (this.Chain != null && !Chain.IsRoot)
            {
                var parentName = chain.Nexus.GetParentChainByName(chain.Name);
                this.ParentChain = chain.Nexus.GetChainByName(parentName);
            }
            else
            {
                this.ParentChain = null;
            }

            ExtCalls.RegisterWithRuntime(this);
        }
Exemple #10
0
        public VMObject InvokeScript(byte[] script)
        {
            var changeSet = new StorageChangeSetContext(this.Storage);
            var vm        = new RuntimeVM(script, this, this.LastBlock, null, changeSet, true);

            var state = vm.Execute();

            if (state != ExecutionState.Halt)
            {
                return(null);
            }

            if (vm.Stack.Count == 0)
            {
                throw new ChainException($"No result, vm stack is empty");
            }

            var result = vm.Stack.Pop();

            return(result);
        }
Exemple #11
0
        public VMObject InvokeScript(StorageContext storage, byte[] script, Timestamp time)
        {
            var oracle    = Nexus.CreateOracleReader();
            var changeSet = new StorageChangeSetContext(storage);
            var vm        = new RuntimeVM(script, this, time, null, changeSet, oracle, true);

            var state = vm.Execute();

            if (state != ExecutionState.Halt)
            {
                return(null);
            }

            if (vm.Stack.Count == 0)
            {
                throw new ChainException($"No result, vm stack is empty");
            }

            var result = vm.Stack.Pop();

            return(result);
        }
Exemple #12
0
        public RuntimeVM(byte[] script, Chain chain, Block block, Transaction transaction, StorageChangeSetContext changeSet, bool readOnlyMode) : base(script)
        {
            Throw.IfNull(chain, nameof(chain));
            Throw.IfNull(changeSet, nameof(changeSet));

            // NOTE: block and transaction can be null, required for Chain.InvokeContract
            //Throw.IfNull(block, nameof(block));
            //Throw.IfNull(transaction, nameof(transaction));

            this.GasPrice = 0;
            this.UsedGas  = 0;
            this.PaidGas  = 0;
            this.MaxGas   = 100; // a minimum amount required for allowing calls to Gas contract etc

            this.Chain        = chain;
            this.Block        = block;
            this.Transaction  = transaction;
            this.ChangeSet    = changeSet;
            this.readOnlyMode = readOnlyMode;

            Chain.RegisterInterop(this);
        }
        public IAPIResult InvokeRawScript([APIParameter("Address or name of chain", "root")] string chainInput, [APIParameter("Serialized script bytes, in hexadecimal format", "0000000000")] string scriptData)
        {
            var chain = FindChainByInput(chainInput);

            if (chain == null)
            {
                return(new ErrorResult {
                    error = "invalid chain"
                });
            }

            byte[] script;
            try
            {
                script = Base16.Decode(scriptData);
            }
            catch
            {
                return(new ErrorResult {
                    error = "Failed to decode script"
                });
            }

            if (script.Length == 0)
            {
                return(new ErrorResult {
                    error = "Invalid transaction script"
                });
            }

            var changeSet = new StorageChangeSetContext(chain.Storage);
            var vm        = new RuntimeVM(script, chain, null, null, changeSet, true);

            var state = vm.Execute();

            if (state != ExecutionState.Halt)
            {
                return(new ErrorResult {
                    error = $"Execution failed, state:{state}"
                });
            }

            string encodedResult;

            if (vm.Stack.Count == 0)
            {
                encodedResult = "";
            }
            else
            {
                var temp        = vm.Stack.Pop();
                var result      = temp.ToObject();
                var resultBytes = Serialization.Serialize(result);
                encodedResult = Base16.Encode(resultBytes);
            }

            var evts = vm.Events.Select(evt => new EventResult()
            {
                address = evt.Address.Text, kind = evt.Kind.ToString(), data = Base16.Encode(evt.Data)
            });

            return(new ScriptResult {
                result = encodedResult, events = evts.ToArray()
            });
        }
Exemple #14
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);
                }
            }
        }
Exemple #15
0
        public void AddBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee, StorageChangeSetContext changeSet)
        {
            if (!block.IsSigned)
            {
                throw new BlockGenerationException($"block must be signed");
            }

            var unsignedBytes = block.ToByteArray(false);

            if (!block.Signature.Verify(unsignedBytes, block.Validator))
            {
                throw new BlockGenerationException($"block signature does not match validator {block.Validator.Text}");
            }

            var hashList            = new StorageList(BlockHeightListTag, this.Storage);
            var expectedBlockHeight = hashList.Count() + 1;

            if (expectedBlockHeight != block.Height)
            {
                throw new ChainException("unexpected block height");
            }

            // from here on, the block is accepted
            using (var m = new ProfileMarker("changeSet.Execute"))
                changeSet.Execute();

            hashList.Add <Hash>(block.Hash);

            using (var m = new ProfileMarker("Compress"))
            {
                var blockMap   = new StorageMap(BlockHashMapTag, this.Storage);
                var blockBytes = block.ToByteArray(true);
                blockBytes = CompressionUtils.Compress(blockBytes);
                blockMap.Set <Hash, byte[]>(block.Hash, blockBytes);

                var txMap      = new StorageMap(TransactionHashMapTag, this.Storage);
                var txBlockMap = new StorageMap(TxBlockHashMapTag, this.Storage);
                foreach (Transaction tx in transactions)
                {
                    var txBytes = tx.ToByteArray(true);
                    txBytes = CompressionUtils.Compress(txBytes);
                    txMap.Set <Hash, byte[]>(tx.Hash, txBytes);
                    txBlockMap.Set <Hash, Hash>(tx.Hash, block.Hash);
                }
            }

            using (var m = new ProfileMarker("AddressBlockHashMapTag"))
                foreach (var transaction in transactions)
                {
                    var addresses = new HashSet <Address>();
                    var events    = block.GetEventsForTransaction(transaction.Hash);

                    foreach (var evt in events)
                    {
                        if (evt.Address.IsSystem)
                        {
                            continue;
                        }

                        addresses.Add(evt.Address);
                    }

                    var addressTxMap = new StorageMap(AddressBlockHashMapTag, this.Storage);
                    foreach (var address in addresses)
                    {
                        var addressList = addressTxMap.Get <Address, StorageList>(address);
                        addressList.Add <Hash>(transaction.Hash);
                    }
                }

            using (var m = new ProfileMarker("Nexus.PluginTriggerBlock"))
                Nexus.PluginTriggerBlock(this, block);
        }
Exemple #16
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)
        {
            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;
            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()))
                        if (allowModify)
                        {
                            onNotify(transaction.Hash, evt);
                        }
                }
            }

            if (runtime.Stack.Count > 0)
            {
                result = runtime.Stack.Pop();
            }

            return(true);
        }
Exemple #17
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);
        }
Exemple #18
0
        public void CloseBlock(Block block, StorageChangeSetContext storage)
        {
            var rootStorage = this.IsRoot ? storage : Nexus.RootStorage;

            if (block.Height > 1)
            {
                var prevBlock = GetBlockByHash(block.PreviousHash);

                if (prevBlock.Validator != block.Validator)
                {
                    block.Notify(new Event(EventKind.ValidatorSwitch, block.Validator, "block", Serialization.Serialize(prevBlock)));
                }
            }

            var balance        = new BalanceSheet(DomainSettings.FuelTokenSymbol);
            var blockAddress   = Address.FromHash("block");
            var totalAvailable = balance.Get(storage, blockAddress);

            var targets = new List <Address>();

            if (Nexus.HasGenesis)
            {
                var validators = Nexus.GetValidators();

                var totalValidators = Nexus.GetPrimaryValidatorCount();

                for (int i = 0; i < totalValidators; i++)
                {
                    var validator = validators[i];
                    if (validator.type != ValidatorType.Primary)
                    {
                        continue;
                    }

                    targets.Add(validator.address);
                }
            }
            else
            if (totalAvailable > 0)
            {
                targets.Add(Nexus.GetGenesisAddress(rootStorage));
            }

            if (targets.Count > 0)
            {
                if (!balance.Subtract(storage, blockAddress, totalAvailable))
                {
                    throw new BlockGenerationException("could not subtract balance from block address");
                }

                var amountPerValidator = totalAvailable / targets.Count;
                var leftOvers          = totalAvailable - (amountPerValidator * targets.Count);

                foreach (var address in targets)
                {
                    BigInteger amount = amountPerValidator;

                    if (address == block.Validator)
                    {
                        amount += leftOvers;
                    }

                    // TODO this should use triggers when available...
                    if (!balance.Add(storage, address, amount))
                    {
                        throw new BlockGenerationException($"could not add balance to {address}");
                    }

                    var eventData = Serialization.Serialize(new TokenEventData(DomainSettings.FuelTokenSymbol, amount, this.Name));
                    block.Notify(new Event(EventKind.TokenClaim, address, "block", eventData));
                }
            }
        }
Exemple #19
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);
                }
            }
        }
Exemple #20
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>());
        }
Exemple #21
0
        public StorageChangeSetContext ValidateBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee)
        {
            if (!block.Validator.IsUser)
            {
                throw new BlockGenerationException($"block validator must be user address");
            }

            Block lastBlock;

            using (var m = new ProfileMarker("GetLastBlock"))
            {
                var lastBlockHash = GetLastBlockHash();
                lastBlock = GetBlockByHash(lastBlockHash);
            }

            if (lastBlock != null)
            {
                if (lastBlock.Height != block.Height - 1)
                {
                    throw new BlockGenerationException($"height of block should be {lastBlock.Height + 1}");
                }

                if (block.PreviousHash != lastBlock.Hash)
                {
                    throw new BlockGenerationException($"previous hash should be {lastBlock.PreviousHash}");
                }

                if (block.Timestamp < lastBlock.Timestamp)
                {
                    throw new BlockGenerationException($"timestamp of block should be greater than {lastBlock.Timestamp}");
                }
            }

            var inputHashes = new HashSet <Hash>(transactions.Select(x => x.Hash));

            foreach (var hash in block.TransactionHashes)
            {
                if (!inputHashes.Contains(hash))
                {
                    throw new BlockGenerationException($"missing in inputs transaction with hash {hash}");
                }
            }

            var outputHashes = new HashSet <Hash>(block.TransactionHashes);

            foreach (var tx in transactions)
            {
                if (!outputHashes.Contains(tx.Hash))
                {
                    throw new BlockGenerationException($"missing in outputs transaction with hash {tx.Hash}");
                }
            }

            // TODO avoid fetching this every time
            var expectedProtocol = Nexus.GetGovernanceValue(Nexus.RootStorage, Nexus.NexusProtocolVersionTag);

            if (block.Protocol != expectedProtocol)
            {
                throw new BlockGenerationException($"invalid protocol number {block.Protocol}, expected protocol {expectedProtocol}");
            }

            foreach (var tx in transactions)
            {
                if (!tx.IsValid(this))
                {
                    throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}");
                }
            }

            var changeSet = new StorageChangeSetContext(this.Storage);

            var oracle = Nexus.CreateOracleReader();

            block.CleanUp();

            Address expectedValidator;

            using (var m = new ProfileMarker("GetValidator"))
                expectedValidator = Nexus.HasGenesis ? GetValidator(Nexus.RootStorage, block.Timestamp) : Nexus.GetGenesisAddress(Nexus.RootStorage);
            if (block.Validator != expectedValidator)
            {
                throw new BlockGenerationException($"unexpected validator {block.Validator}, expected {expectedValidator}");
            }

            int txIndex = 0;

            foreach (var tx in transactions)
            {
                byte[] result;
                try
                {
                    using (var m = new ProfileMarker("ExecuteTransaction"))
                    {
                        if (ExecuteTransaction(txIndex, tx, block.Timestamp, changeSet, block.Notify, oracle, minimumFee, out result))
                        {
                            if (result != null)
                            {
                                block.SetResultForHash(tx.Hash, result);
                            }
                        }
                        else
                        {
                            throw new InvalidTransactionException(tx.Hash, "script execution failed");
                        }
                    }
                }
                catch (Exception e)
                {
                    if (e.InnerException != null)
                    {
                        e = e.InnerException;
                    }

                    if (tx == null)
                    {
                        throw new BlockGenerationException(e.Message);
                    }

                    throw new InvalidTransactionException(tx.Hash, e.Message);
                }

                txIndex++;
            }

            using (var m = new ProfileMarker("CloseBlock"))
            {
                CloseBlock(block, changeSet);
            }

            if (oracle.Entries.Any())
            {
                block.MergeOracle(oracle);
            }

            return(changeSet);
        }
Exemple #22
0
        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);
        }
Exemple #23
0
        public void AddBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee)
        {
            /*if (CurrentEpoch != null && CurrentEpoch.IsSlashed(Timestamp.Now))
             * {
             *  return false;
             * }*/

            var lastBlockHash = GetLastBlockHash();
            var lastBlock     = GetBlockByHash(lastBlockHash);

            if (lastBlock != null)
            {
                if (lastBlock.Height != block.Height - 1)
                {
                    throw new BlockGenerationException($"height of block should be {lastBlock.Height + 1}");
                }

                if (block.PreviousHash != lastBlock.Hash)
                {
                    throw new BlockGenerationException($"previous hash should be {lastBlock.PreviousHash}");
                }
            }

            var inputHashes = new HashSet <Hash>(transactions.Select(x => x.Hash));

            foreach (var hash in block.TransactionHashes)
            {
                if (!inputHashes.Contains(hash))
                {
                    throw new BlockGenerationException($"missing in inputs transaction with hash {hash}");
                }
            }

            var outputHashes = new HashSet <Hash>(block.TransactionHashes);

            foreach (var tx in transactions)
            {
                if (!outputHashes.Contains(tx.Hash))
                {
                    throw new BlockGenerationException($"missing in outputs transaction with hash {tx.Hash}");
                }
            }

            // TODO avoid fetching this every time
            var expectedProtocol = Nexus.GetGovernanceValue(Nexus.RootStorage, Nexus.NexusProtocolVersionTag);

            if (block.Protocol != expectedProtocol)
            {
                throw new BlockGenerationException($"invalid protocol number {block.Protocol}, expected protocol {expectedProtocol}");
            }

            foreach (var tx in transactions)
            {
                if (!tx.IsValid(this))
                {
                    throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}");
                }
            }

            var changeSet = new StorageChangeSetContext(this.Storage);

            var oracle = Nexus.CreateOracleReader();

            foreach (var tx in transactions)
            {
                byte[] result;
                try
                {
                    if (ExecuteTransaction(tx, block.Timestamp, changeSet, block.Notify, oracle, minimumFee, out result))
                    {
                        if (result != null)
                        {
                            block.SetResultForHash(tx.Hash, result);
                        }
                    }
                    else
                    {
                        throw new InvalidTransactionException(tx.Hash, $"execution failed");
                    }
                }
                catch (Exception e)
                {
                    if (e.InnerException != null)
                    {
                        e = e.InnerException;
                    }
                    throw new InvalidTransactionException(tx.Hash, e.Message);
                }
            }

            block.MergeOracle(oracle);

            var hashList            = new StorageList(BlockHeightListTag, this.Storage);
            var expectedBlockHeight = hashList.Count() + 1;

            if (expectedBlockHeight != block.Height)
            {
                throw new ChainException("unexpected block height");
            }

            // from here on, the block is accepted
            changeSet.Execute();

            hashList.Add <Hash>(block.Hash);

            var blockMap = new StorageMap(BlockHashMapTag, this.Storage);

            blockMap.Set <Hash, Block>(block.Hash, block);

            var txMap      = new StorageMap(TransactionHashMapTag, this.Storage);
            var txBlockMap = new StorageMap(TxBlockHashMapTag, this.Storage);

            foreach (Transaction tx in transactions)
            {
                txMap.Set <Hash, Transaction>(tx.Hash, tx);
                txBlockMap.Set <Hash, Hash>(tx.Hash, block.Hash);
            }

            var blockValidator = GetValidatorForBlock(block);

            if (blockValidator.IsNull)
            {
                throw new BlockGenerationException("no validator for this block");
            }

            Nexus.PluginTriggerBlock(this, block);
        }
        public void AddBlock(Block block, IEnumerable <Transaction> transactions, OracleReaderDelegate oracleReader)
        {
            /*if (CurrentEpoch != null && CurrentEpoch.IsSlashed(Timestamp.Now))
             * {
             *  return false;
             * }*/

            if (LastBlock != null)
            {
                if (LastBlock.Height != block.Height - 1)
                {
                    throw new BlockGenerationException($"height of block should be {LastBlock.Height + 1}");
                }

                if (block.PreviousHash != LastBlock.Hash)
                {
                    throw new BlockGenerationException($"previous hash should be {LastBlock.PreviousHash}");
                }
            }

            var inputHashes = new HashSet <Hash>(transactions.Select(x => x.Hash));

            foreach (var hash in block.TransactionHashes)
            {
                if (!inputHashes.Contains(hash))
                {
                    throw new BlockGenerationException($"missing in inputs transaction with hash {hash}");
                }
            }

            var outputHashes = new HashSet <Hash>(block.TransactionHashes);

            foreach (var tx in transactions)
            {
                if (!outputHashes.Contains(tx.Hash))
                {
                    throw new BlockGenerationException($"missing in outputs transaction with hash {tx.Hash}");
                }
            }

            foreach (var tx in transactions)
            {
                if (!tx.IsValid(this))
                {
                    throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}");
                }
            }

            var changeSet = new StorageChangeSetContext(this.Storage);

            foreach (var tx in transactions)
            {
                byte[] result;
                if (tx.Execute(this, block, changeSet, block.Notify, oracleReader, out result))
                {
                    if (result != null)
                    {
                        block.SetResultForHash(tx.Hash, result);
                    }
                }
                else
                {
                    throw new InvalidTransactionException(tx.Hash, $"transaction execution failed with hash {tx.Hash}");
                }
            }

            // from here on, the block is accepted
            _blockHeightMap[block.Height] = block;
            _blocks[block.Hash]           = block;
            _blockChangeSets[block.Hash]  = changeSet;

            changeSet.Execute();

            if (CurrentEpoch == null)
            {
                GenerateEpoch();
            }

            CurrentEpoch.AddBlockHash(block.Hash);
            CurrentEpoch.UpdateHash();

            LastBlock = block;

            foreach (Transaction tx in transactions)
            {
                _transactions[tx.Hash]        = tx;
                _transactionBlockMap[tx.Hash] = block.Hash;
            }

            Nexus.PluginTriggerBlock(this, block);
        }
Exemple #25
0
        public void AddBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee)
        {
            /*if (CurrentEpoch != null && CurrentEpoch.IsSlashed(Timestamp.Now))
             * {
             *  return false;
             * }*/

            if (LastBlock != null)
            {
                if (LastBlock.Height != block.Height - 1)
                {
                    throw new BlockGenerationException($"height of block should be {LastBlock.Height + 1}");
                }

                if (block.PreviousHash != LastBlock.Hash)
                {
                    throw new BlockGenerationException($"previous hash should be {LastBlock.PreviousHash}");
                }
            }

            var inputHashes = new HashSet <Hash>(transactions.Select(x => x.Hash));

            foreach (var hash in block.TransactionHashes)
            {
                if (!inputHashes.Contains(hash))
                {
                    throw new BlockGenerationException($"missing in inputs transaction with hash {hash}");
                }
            }

            var outputHashes = new HashSet <Hash>(block.TransactionHashes);

            foreach (var tx in transactions)
            {
                if (!outputHashes.Contains(tx.Hash))
                {
                    throw new BlockGenerationException($"missing in outputs transaction with hash {tx.Hash}");
                }
            }

            foreach (var tx in transactions)
            {
                if (!tx.IsValid(this))
                {
                    throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}");
                }
            }

            var changeSet = new StorageChangeSetContext(this.Storage);

            var oracle = Nexus.CreateOracleReader();

            foreach (var tx in transactions)
            {
                byte[] result;
                try
                {
                    if (tx.Execute(this, block.Timestamp, changeSet, block.Notify, oracle, minimumFee, out result))
                    {
                        if (result != null)
                        {
                            block.SetResultForHash(tx.Hash, result);
                        }
                    }
                    else
                    {
                        throw new InvalidTransactionException(tx.Hash, $"execution failed");
                    }
                }
                catch (Exception e)
                {
                    if (e.InnerException != null)
                    {
                        e = e.InnerException;
                    }
                    throw new InvalidTransactionException(tx.Hash, e.Message);
                }
            }

            block.MergeOracle(oracle);

            // from here on, the block is accepted
            _blockHeightMap[block.Height] = block.Hash;
            _blocks[block.Hash]           = block;
            _blockChangeSets[block.Hash]  = changeSet;

            changeSet.Execute();

            Dictionary <string, BigInteger> synchMap = null;

            foreach (Transaction tx in transactions)
            {
                _transactions[tx.Hash]        = tx;
                _transactionBlockMap[tx.Hash] = block.Hash;

                var evts = block.GetEventsForTransaction(tx.Hash);
                foreach (var evt in evts)
                {
                    if (evt.Kind == EventKind.TokenMint || evt.Kind == EventKind.TokenBurn || evt.Kind == EventKind.TokenReceive || evt.Kind == EventKind.TokenSend)
                    {
                        var eventData = evt.GetContent <TokenEventData>();
                        var token     = Nexus.GetTokenInfo(eventData.symbol);

                        if (!token.IsFungible)
                        {
                            // TODO support this
                            continue;
                        }

                        if (token.IsCapped)
                        {
                            BigInteger balance;

                            if (synchMap == null)
                            {
                                synchMap = new Dictionary <string, BigInteger>();
                                balance  = 0;
                            }
                            else
                            {
                                balance = synchMap.ContainsKey(eventData.symbol) ? synchMap[eventData.symbol] : 0;
                            }

                            if (evt.Kind == EventKind.TokenBurn || evt.Kind == EventKind.TokenSend)
                            {
                                balance -= eventData.value;
                            }
                            else
                            {
                                balance += eventData.value;
                            }

                            synchMap[eventData.symbol] = balance;
                        }
                    }
                }
            }

            if (synchMap != null)
            {
                SynchronizeSupplies(synchMap);
            }

            var blockValidator = GetValidatorForBlock(block);

            if (blockValidator.IsNull)
            {
                throw new BlockGenerationException("no validator for this block");
            }

            Nexus.PluginTriggerBlock(this, block);
        }
Exemple #26
0
        public RuntimeVM(byte[] script, Chain chain, Block block, Transaction transaction, StorageChangeSetContext changeSet, bool readOnlyMode, bool delayPayment = false) : base(script)
        {
            Throw.IfNull(chain, nameof(chain));
            Throw.IfNull(changeSet, nameof(changeSet));

            // NOTE: block and transaction can be null, required for Chain.InvokeContract
            //Throw.IfNull(block, nameof(block));
            //Throw.IfNull(transaction, nameof(transaction));

            this.GasPrice     = 0;
            this.UsedGas      = 0;
            this.PaidGas      = 0;
            this.MaxGas       = 10000; // a minimum amount required for allowing calls to Gas contract etc
            this.DelayPayment = delayPayment;

            this.Chain        = chain;
            this.Block        = block;
            this.Transaction  = transaction;
            this.ChangeSet    = changeSet;
            this.readOnlyMode = readOnlyMode;

            this.FeeTargetAddress = Address.Null;

            if (this.Chain != null && !Chain.IsRoot)
            {
                var parentName = chain.Nexus.GetParentChainByName(chain.Name);
                this.ParentChain = chain.Nexus.FindChainByName(parentName);
            }
            else
            {
                this.ParentChain = null;
            }

            Chain.RegisterInterop(this);
        }