コード例 #1
0
    public void ProfileData_AddMarkerName_AddsMarkerAndContainsName()
    {
        var data        = new ProfileData();
        var markerNames = new List <string>()
        {
            "Marker01",
            "Marker02",
            "Marker03",
            "Marker04"
        };

        var markerList = new List <ProfileMarker>();

        for (int i = 0; i < 10; ++i)
        {
            var marker = new ProfileMarker()
            {
                msMarkerTotal = 0.5f,
                depth         = i
            };

            int expectedIndex = i % markerNames.Count;
            data.AddMarkerName(markerNames[expectedIndex], marker);
            markerList.Add(marker);

            Assert.IsTrue(expectedIndex == marker.nameIndex, "Index mismatch at: " + i + " , " + marker.nameIndex);;
        }

        for (int i = 0; i < markerList.Count; ++i)
        {
            var curName = data.GetMarkerName(markerList[i]);
            Assert.IsTrue(markerNames.Contains(curName));
        }
    }
コード例 #2
0
        internal ExecutionState SwitchContext(ExecutionContext context, uint instructionPointer)
        {
            if (context == null)
            {
                throw new VMException(this, "SwitchContext failed, context can't be null");
            }

            using (var m = new ProfileMarker("SwitchContext"))
            {
                var tempContext = PreviousContext;
                PreviousContext = CurrentContext;
                SetCurrentContext(context);
                PushFrame(context, instructionPointer, DefaultRegisterCount);

                _activeAddresses.Push(context.Address);

                var result = context.Execute(this.CurrentFrame, this.Stack);

                PreviousContext = tempContext;

                var temp = _activeAddresses.Pop();
                if (temp != context.Address)
                {
                    throw new VMException(this, "VM implementation bug detected: address stack");
                }

                return(result);
            }
        }
コード例 #3
0
        public BigInteger MintToken(string symbol, Address from, Address target, byte[] rom, byte[] ram)
        {
            var Runtime = this;

            Runtime.Expect(Runtime.TokenExists(symbol), "invalid token");
            IToken token;

            using (var m = new ProfileMarker("Runtime.GetToken"))
                token = Runtime.GetToken(symbol);
            Runtime.Expect(!token.IsFungible(), "token must be non-fungible");
            // TODO should not be necessary, verified by trigger
            //Runtime.Expect(IsWitness(target), "invalid witness");

            Runtime.Expect(Runtime.IsRootChain(), "can only mint nft in root chain");

            Runtime.Expect(rom.Length <= TokenContent.MaxROMSize, "ROM size exceeds maximum allowed");
            Runtime.Expect(ram.Length <= TokenContent.MaxRAMSize, "RAM size exceeds maximum allowed");

            BigInteger tokenID;

            using (var m = new ProfileMarker("Nexus.CreateNFT"))
                tokenID = Nexus.CreateNFT(this, symbol, Runtime.Chain.Name, target, rom, ram);
            Runtime.Expect(tokenID > 0, "invalid tokenID");

            using (var m = new ProfileMarker("Nexus.MintToken"))
                Nexus.MintToken(this, token, from, target, Chain.Name, tokenID, rom, ram);

            return(tokenID);
        }
コード例 #4
0
        public void AllowGas(Address from, Address target, BigInteger price, BigInteger limit)
        {
            if (Runtime.IsReadOnlyMode())
            {
                return;
            }

            if (_lastInflationDate == 0)
            {
                _lastInflationDate = Runtime.Time;
            }

            Runtime.Expect(from.IsUser, "must be a user address");
            Runtime.Expect(Runtime.PreviousContext.Name == VirtualMachine.EntryContextName, "must be entry context");
            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");
            Runtime.Expect(target.IsSystem, "destination must be system address");

            Runtime.Expect(price > 0, "price must be positive amount");
            Runtime.Expect(limit > 0, "limit must be positive amount");

            if (target.IsNull)
            {
                target = Runtime.Chain.Address;
            }

            var maxAmount = price * limit;

            using (var m = new ProfileMarker("_allowanceMap"))
            {
                var allowance = _allowanceMap.ContainsKey(from) ? _allowanceMap.Get <Address, BigInteger>(from) : 0;
                Runtime.Expect(allowance == 0, "unexpected pending allowance");

                allowance += maxAmount;
                _allowanceMap.Set(from, allowance);
                _allowanceTargets.Set(from, target);
            }

            BigInteger balance;

            using (var m = new ProfileMarker("Runtime.GetBalance"))
            {
                balance = Runtime.GetBalance(DomainSettings.FuelTokenSymbol, from);
            }

            if (maxAmount > balance)
            {
                var diff      = maxAmount - balance;
                var fuelToken = Runtime.GetToken(DomainSettings.FuelTokenSymbol);
                throw new BalanceException(fuelToken, from, diff);
            }

            Runtime.Expect(balance >= maxAmount, $"not enough {DomainSettings.FuelTokenSymbol} {balance} in address {from} {maxAmount}");

            using (var m = new ProfileMarker("Runtime.TransferTokens"))
                Runtime.TransferTokens(DomainSettings.FuelTokenSymbol, from, this.Address, maxAmount);
            using (var m = new ProfileMarker("Runtime.Notify"))
                Runtime.Notify(EventKind.GasEscrow, from, new GasEventData(target, price, limit));
        }
コード例 #5
0
 internal ExecutionState SwitchContext(ExecutionContext context, uint instructionPointer)
 {
     using (var m = new ProfileMarker("SwitchContext"))
     {
         this.CurrentContext = context;
         PushFrame(context, instructionPointer, DefaultRegisterCount);
         return(context.Execute(this.CurrentFrame, this.Stack));
     }
 }
コード例 #6
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);
        }
コード例 #7
0
        public override ExecutionState ExecuteInterop(string method)
        {
            BigInteger gasCost;

            // construtor
            if (method.EndsWith("()"))
            {
                gasCost = 10;
            }
            else
            {
                int dotPos = method.IndexOf('.');
                Expect(dotPos > 0, "extcall is missing namespace");

                var methodNamespace = method.Substring(0, dotPos);
                switch (methodNamespace)
                {
                case "Runtime":
                    gasCost = 50;
                    break;

                case "Nexus":
                    gasCost = 1000;
                    break;

                case "Organization":
                    gasCost = 200;
                    break;

                default:
                    Expect(false, "invalid extcall namespace: " + methodNamespace);
                    gasCost = 0;
                    break;
                }
            }

            if (gasCost > 0)
            {
                ConsumeGas(gasCost);
            }

            if (handlers.ContainsKey(method))
            {
                using (var m = new ProfileMarker(method))
                    return(handlers[method](this));
            }

            return(ExecutionState.Fault);
        }
コード例 #8
0
    public void ProfileData_AddThreadName_AddsThreadAndContainsName()
    {
        var data        = new ProfileData();
        var threadNames = new List <string>()
        {
            "Thread01",
            "Thread02",
            "Thread03",
            "Thread04"
        };

        var threadDict = new Dictionary <string, ProfileThread>();

        for (int i = 0; i < 10; ++i)
        {
            int           expectedIndex = i % threadNames.Count;
            string        threadName    = threadNames[expectedIndex];
            ProfileThread thread;

            if (!threadDict.TryGetValue(threadName, out thread))
            {
                thread = new ProfileThread();
                threadDict.Add(threadName, thread);
            }

            var marker = new ProfileMarker()
            {
                msMarkerTotal = 0.5f,
                depth         = i
            };

            thread.Add(marker);
            data.AddThreadName(threadName, thread);
            Assert.IsTrue(expectedIndex == thread.threadIndex, "Index mismatch at: " + i + " , " + thread.threadIndex);;
        }

        foreach (var curThread in threadDict)
        {
            var curName = data.GetThreadName(curThread.Value);
            Assert.IsTrue(threadNames.Contains(curName));
        }
    }
コード例 #9
0
        public void MintTokens(string symbol, Address from, Address target, BigInteger amount)
        {
            var Runtime = this;

            // TODO should not be necessary, verified by trigger
            //Runtime.Expect(IsWitness(from), "invalid witness");

            Runtime.Expect(amount > 0, "amount must be positive and greater than zero");

            Runtime.Expect(Runtime.TokenExists(symbol), "invalid token");
            IToken token;

            using (var m = new ProfileMarker("Runtime.GetToken"))
                token = Runtime.GetToken(symbol);
            Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");
            Runtime.Expect(!token.Flags.HasFlag(TokenFlags.Fiat), "token can't be fiat");

            using (var m = new ProfileMarker("Nexus.MintTokens"))
                Nexus.MintTokens(this, token, from, target, Chain.Name, amount);
        }
コード例 #10
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);
        }
コード例 #11
0
        private ExecutionState InternalCall(NativeContract contract, ContractMethod method, ExecutionFrame frame, Stack <VMObject> stack)
        {
            var args = new object[method.parameters.Length];

            for (int i = 0; i < args.Length; i++)
            {
                var arg = stack.Pop();
                args[i] = arg.Data;
            }

            object result;

            using (var m = new ProfileMarker(method.name))
                result = contract.CallInternalMethod((RuntimeVM)frame.VM, method.name, args);

            if (method.returnType != VMType.None)
            {
                var obj = VMObject.FromObject(result);
                stack.Push(obj);
            }

            return(ExecutionState.Running);
        }
コード例 #12
0
        public void AllowGas(Address from, Address target, BigInteger price, BigInteger limit)
        {
            if (Runtime.IsReadOnlyMode())
            {
                return;
            }

            Runtime.Expect(from.IsUser, "must be a user address");
            Runtime.Expect(Runtime.IsWitness(from), "invalid witness");
            Runtime.Expect(target.IsSystem, "destination must be system address");

            Runtime.Expect(price > 0, "price must be positive amount");
            Runtime.Expect(limit > 0, "limit must be positive amount");

            var maxAmount = price * limit;

            using (var m = new ProfileMarker("_allowanceMap"))
            {
                var allowance = _allowanceMap.ContainsKey(from) ? _allowanceMap.Get <Address, BigInteger>(from) : 0;
                Runtime.Expect(allowance == 0, "unexpected pending allowance");

                allowance += maxAmount;
                _allowanceMap.Set(from, allowance);
                _allowanceTargets.Set(from, target);
            }

            BigInteger balance;

            using (var m = new ProfileMarker("Runtime.GetBalance"))
                balance = Runtime.GetBalance(DomainSettings.FuelTokenSymbol, from);
            Runtime.Expect(balance >= maxAmount, "not enough gas in address");

            using (var m = new ProfileMarker("Runtime.TransferTokens"))
                Runtime.TransferTokens(DomainSettings.FuelTokenSymbol, from, this.Address, maxAmount);
            using (var m = new ProfileMarker("Runtime.Notify"))
                Runtime.Notify(EventKind.GasEscrow, from, new GasEventData(target, price, limit));
        }
コード例 #13
0
        public bool IsWitness(Address address)
        {
            if (address.IsInterop)
            {
                return(false);
            }

            if (address == Address.Null)
            {
                return(false);
            }

            if (address == this.Chain.Address /*|| address == this.Address*/)
            {
                return(false);
            }

            if (address == this.EntryAddress)
            {
                return(true);
            }

            using (var m = new ProfileMarker("validatedWitnesses.Contains"))
                if (validatedWitnesses.Contains(address))
                {
                    return(true);
                }

            if (address.IsSystem)
            {
                var org = Nexus.GetOrganizationByAddress(RootStorage, address);
                if (org != null)
                {
                    this.ConsumeGas(10000);
                    var result = org.IsWitness(Transaction);

                    if (result)
                    {
                        validatedWitnesses.Add(address);
                    }

                    return(result);
                }
                else
                {
                    return(address == CurrentContext.Address);
                }
            }

            if (this.Transaction == null)
            {
                return(false);
            }

            bool accountResult;

            if (address.IsUser && Nexus.HasGenesis && OptimizedHasAddressScript(RootStorage, address))
            {
                using (var m = new ProfileMarker("InvokeTriggerOnAccount"))
                    accountResult = InvokeTriggerOnAccount(address, AccountTrigger.OnWitness, address);
            }
            else
            {
                using (var m = new ProfileMarker("Transaction.IsSignedBy"))
                    accountResult = this.Transaction.IsSignedBy(address);
            }

            if (accountResult)
            {
                validatedWitnesses.Add(address);
            }

            return(accountResult);
        }
コード例 #14
0
        private void MintBlock(List <Transaction> transactions)
        {
            if (Mempool.ValidatorAddress == null)
            {
                Mempool.Logger.Error($"Validator keys not set for mempool");
                return;
            }

            var lastBlockHash = Chain.GetLastBlockHash();
            var lastBlock     = Chain.GetBlockByHash(lastBlockHash);
            var isFirstBlock  = lastBlock == null;

            var protocol = Nexus.GetProtocolVersion(Nexus.RootStorage);

            var minFee = Mempool.MinimumFee;

            Mempool.Logger.Message($"Minting new block with {transactions.Count} potential transactions");

            while (transactions.Count > 0)
            {
                var block = new Block(isFirstBlock ? 1 : (lastBlock.Height + 1)
                                      , Chain.Address
                                      , Timestamp.Now
                                      , transactions.Select(x => x.Hash)
                                      , isFirstBlock ? Hash.Null : lastBlock.Hash
                                      , protocol
                                      , Mempool.ValidatorAddress
                                      , Mempool.Payload);

                try
                {
                    using (var m = new ProfileMarker("Chain.ProcessBlock"))
                    {
                        Chain.ProcessBlock(block, transactions, minFee);
                    }
                }
                catch (InvalidTransactionException e)
                {
                    using (var m = new ProfileMarker("InvalidTransactionException"))
                    {
                        int index = -1;

                        for (int i = 0; i < transactions.Count; i++)
                        {
                            if (transactions[i].Hash == e.Hash)
                            {
                                index = i;
                                break;
                            }
                        }

                        lock (_pending)
                        {
                            if (index >= 0)
                            {
                                transactions.RemoveAt(index);
                            }

                            _pending.Remove(e.Hash);
                            Mempool.RegisterRejectionReason(e.Hash, e.Message);
                        }

                        Mempool.OnTransactionFailed?.Invoke(e.Hash);
                        continue;
                    }
                }

                try
                {
                    StorageChangeSetContext changeSet;
                    using (var m = new ProfileMarker("block.process"))
                    {
                        changeSet = Chain.ProcessBlock(block, transactions, minFee);
                    }

                    using (var m = new ProfileMarker("block.Sign"))
                    {
                        block.Sign(Mempool.ValidatorKeys);
                    }

                    using (var m = new ProfileMarker("Chain.AddBlock"))
                        Chain.AddBlock(block, transactions, minFee, changeSet);
                }
                catch (Exception e)
                {
                    Mempool.Logger.Error(e.ToString());
                }

                lock (_pending)
                {
                    _pending.Clear();
                }

                using (var m = new ProfileMarker("Mempool.OnTransactionCommitted"))
                    foreach (var tx in transactions)
                    {
                        Mempool.OnTransactionCommitted?.Invoke(tx.Hash);
                    }

                return;
            }
        }
コード例 #15
0
        public StorageChangeSetContext ProcessBlock(Block block, IEnumerable <Transaction> transactions, BigInteger minimumFee, out Transaction inflationTx, PhantasmaKeys signingKeys)
        {
            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 {block.Timestamp} should be greater than {lastBlock.Timestamp}");
                }
            }

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

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

            var diff = transactions.Count() - inputHashes.Count;

            if (diff > 0)
            {
                var temp = new HashSet <Hash>();
                foreach (var tx in transactions)
                {
                    if (temp.Contains(tx.Hash))
                    {
                        throw new DuplicatedTransactionException(tx.Hash, $"transaction {tx.Hash} appears more than once in the block being minted");
                    }
                    else
                    if (txBlockMap.ContainsKey <Hash>(tx.Hash))
                    {
                        var previousBlockHash = txBlockMap.Get <Hash, Hash>(tx.Hash);
                        throw new DuplicatedTransactionException(tx.Hash, $"transaction {tx.Hash} already added to previous block {previousBlockHash}");
                    }
                    else
                    {
                        temp.Add(tx.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))
                {
#if DEBUG
                    tx.IsValid(this);
#endif
                    throw new InvalidTransactionException(tx.Hash, $"invalid transaction with hash {tx.Hash}");
                }
            }

            var oracle = Nexus.GetOracleReader();

            block.CleanUp();

            var changeSet = ProcessTransactions(block, transactions, oracle, minimumFee, out inflationTx, signingKeys);

            Address expectedValidator;
            using (var m = new ProfileMarker("GetValidator"))
                expectedValidator = Nexus.HasGenesis ? GetValidator(Nexus.RootStorage, block.Timestamp) : Nexus.GetGenesisAddress(Nexus.RootStorage);

            var migrationFound  = false;
            var migratedAddress = Address.Null;
            foreach (var hash in outputHashes)
            {
                if (migrationFound)
                {
                    break;
                }

                var events = block.GetEventsForTransaction(hash);
                foreach (var evt in events)
                {
                    if (evt.Kind == EventKind.AddressMigration && evt.Contract == "validator")
                    {
                        var oldAddress = evt.GetContent <Address>();
                        if (oldAddress == expectedValidator)
                        {
                            migratedAddress = evt.Address;
                            migrationFound  = true;
                            break;
                        }
                    }
                }
            }

            if (block.Validator != expectedValidator && !expectedValidator.IsNull)
            {
                if (migrationFound && migratedAddress == block.Validator)
                {
                    expectedValidator = migratedAddress;
                }
                else
                {
                    throw new BlockGenerationException($"unexpected validator {block.Validator}, expected {expectedValidator}");
                }
            }

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

            return(changeSet);
        }
コード例 #16
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);
                }
            }
        }
コード例 #17
0
        public override ExecutionState Execute(ExecutionFrame frame, Stack <VMObject> stack)
        {
            if (this.Contract.ABI == null)
            {
                throw new VMException(frame.VM, $"VM nativecall failed: ABI is missing for contract '{this.Contract.Name}'");
            }

            if (stack.Count <= 0)
            {
                throw new VMException(frame.VM, $"VM nativecall failed: method name not present in the VM stack {frame.Context.Name}");
            }

            var stackObj   = stack.Pop();
            var methodName = stackObj.AsString();

            var runtime = (RuntimeVM)frame.VM;

            if (methodName.Equals(SmartContract.ConstructorName, StringComparison.OrdinalIgnoreCase) && runtime.HasGenesis)
            {
                BigInteger usedQuota;

                if (Nexus.IsNativeContract(Contract.Name))
                {
                    usedQuota = 1024; // does not matter what number, just than its greater than 0
                }
                else
                {
                    usedQuota = runtime.CallNativeContext(NativeContractKind.Storage, nameof(StorageContract.GetUsedDataQuota), this.Contract.Address).AsNumber();
                }

                if (usedQuota > 0)
                {
                    throw new VMException(frame.VM, $"VM nativecall failed: constructor can only be called once");
                }
            }

            var method = this.Contract.ABI.FindMethod(methodName);

            if (method == null)
            {
                throw new VMException(frame.VM, $"VM nativecall failed: contract '{this.Contract.Name}' does not have method '{methodName}' in its ABI");
            }

            if (stack.Count < method.parameters.Length)
            {
                throw new VMException(frame.VM, $"VM nativecall failed: calling method {methodName} with {stack.Count} arguments instead of {method.parameters.Length}");
            }

            ExecutionState result;

            BigInteger gasCost = 10;

            result = runtime.ConsumeGas(gasCost);
            if (result != ExecutionState.Running)
            {
                return(result);
            }

            var native = Contract as NativeContract;

            if (native != null)
            {
                using (var m = new ProfileMarker("InternalCall"))
                {
                    try
                    {
                        native.SetRuntime(runtime);
                        native.LoadFromStorage(runtime.Storage);

                        result = InternalCall(native, method, frame, stack);
                        native.SaveChangesToStorage();
                    }
                    catch (ArgumentException ex)
                    {
                        throw new VMException(frame.VM, $"VM nativecall failed: calling method {methodName} with arguments of wrong type, " + ex.ToString());
                    }
                }
            }
            else
            {
                var custom = Contract as CustomContract;

                if (custom != null)
                {
                    if (method.offset < 0)
                    {
                        throw new VMException(frame.VM, $"VM context call failed: abi contains invalid offset for {method.name}");
                    }

#if SUSHI_MODE
                    var debugPath = @"C:\Code\Poltergeist\Builds\Windows\debug.asm";
                    var disasm    = new VM.Disassembler(custom.Script);
                    var asm       = string.Join("\n", disasm.Instructions.Select(x => x.ToString()));
                    System.IO.File.WriteAllText(debugPath, asm);
#endif

                    var context = new ScriptContext(Contract.Name, custom.Script, (uint)method.offset);
                    result = context.Execute(frame, stack);
                }
                else
                {
                    throw new VMException(frame.VM, $"VM context call failed: unknown contract instance class {Contract.GetType().Name}");
                }
            }


            // we terminate here execution, since it will be restarted in next context
            if (result == ExecutionState.Running)
            {
                result = ExecutionState.Halt;
            }

            return(result);
        }
コード例 #18
0
ファイル: Chain.cs プロジェクト: yjsyyyjszf/PhantasmaChain
        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);
        }
コード例 #19
0
        public void Step(ref ExecutionFrame frame, Stack <VMObject> stack)
        {
            try
            {
                opcode = (Opcode)Read8();

                frame.VM.ValidateOpcode(opcode);

                switch (opcode)
                {
                case Opcode.NOP:
                {
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.MOVE:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    frame.Registers[dst] = frame.Registers[src];
                    frame.Registers[src] = new VMObject();
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.COPY:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    frame.Registers[dst].Copy(frame.Registers[src]);
                    break;
                }

                // args: byte dst_reg, byte type, var length, var data_bytes
                case Opcode.LOAD:
                {
                    var dst  = Read8();
                    var type = (VMType)Read8();
                    var len  = (int)ReadVar(0xFFFF);

                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var bytes = ReadBytes(len);
                    frame.Registers[dst].SetValue(bytes, type);

                    break;
                }

                // args: byte src_reg, dst_reg, byte type
                case Opcode.CAST:
                {
                    var src  = Read8();
                    var dst  = Read8();
                    var type = (VMType)Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var val = frame.Registers[src];
                    val = VMObject.CastTo(val, type);

                    frame.Registers[dst] = val;
                    break;
                }

                // args: byte src_reg
                case Opcode.PUSH:
                {
                    var src = Read8();
                    Expect(src < frame.Registers.Length, "invalid src register");

                    var val = frame.Registers[src];

                    var temp = new VMObject();
                    temp.Copy(val);
                    stack.Push(temp);
                    break;
                }

                // args: byte dest_reg
                case Opcode.POP:
                {
                    var dst = Read8();

                    Expect(stack.Count > 0, "stack is empty");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    frame.Registers[dst] = stack.Pop();
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.SWAP:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var temp = frame.Registers[src];
                    frame.Registers[src] = frame.Registers[dst];
                    frame.Registers[dst] = temp;

                    break;
                }

                // args: ushort offset, byte regCount
                case Opcode.CALL:
                {
                    var count = Read8();
                    var ofs   = Read16();

                    Expect(ofs < this.Script.Length, "invalid jump offset");
                    Expect(count >= 1, "at least 1 register required");
                    Expect(count <= VirtualMachine.MaxRegisterCount, "invalid register allocs");

                    frame.VM.PushFrame(this, InstructionPointer, count);
                    frame = frame.VM.CurrentFrame;

                    InstructionPointer = ofs;
                    break;
                }

                // args: byte srcReg
                case Opcode.EXTCALL:
                    using (var m = new ProfileMarker("EXTCALL"))
                    {
                        var src = Read8();
                        Expect(src < frame.Registers.Length, "invalid src register");

                        var method = frame.Registers[src].AsString();

                        var state = frame.VM.ExecuteInterop(method);
                        if (state != ExecutionState.Running)
                        {
                            throw new VMException(frame.VM, "VM extcall failed: " + method);
                        }

                        break;
                    }

                // args: ushort offset, byte src_reg
                // NOTE: JMP only has offset arg, not the rest
                case Opcode.JMP:
                case Opcode.JMPIF:
                case Opcode.JMPNOT:
                {
                    bool shouldJump;

                    if (opcode == Opcode.JMP)
                    {
                        shouldJump = true;
                    }
                    else
                    {
                        var src = Read8();
                        Expect(src < frame.Registers.Length, "invalid src register");

                        shouldJump = frame.Registers[src].AsBool();

                        if (opcode == Opcode.JMPNOT)
                        {
                            shouldJump = !shouldJump;
                        }
                    }

                    var newPos = (short)Read16();

                    Expect(newPos >= 0, "jump offset can't be negative value");
                    Expect(newPos < this.Script.Length, "trying to jump outside of script bounds");

                    if (shouldJump)
                    {
                        InstructionPointer = (uint)newPos;
                    }

                    break;
                }

                // args: var length, var bytes
                case Opcode.THROW:
                {
                    var src = Read8();

                    Expect(src < frame.Registers.Length, "invalid exception register");

                    var exception        = frame.Registers[src];
                    var exceptionMessage = exception.AsString();

                    throw new VMException(frame.VM, exceptionMessage);
                }

                // args: none
                case Opcode.RET:
                {
                    if (frame.VM.frames.Count > 1)
                    {
                        var temp = frame.VM.PeekFrame();

                        if (temp.Context.Name == this.Name)
                        {
                            InstructionPointer = frame.VM.PopFrame();
                            frame = frame.VM.CurrentFrame;
                        }
                        else
                        {
                            SetState(ExecutionState.Halt);
                        }
                    }
                    else
                    {
                        SetState(ExecutionState.Halt);
                    }
                    return;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.CAT:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length, "invalid srcA register");
                    Expect(srcB < frame.Registers.Length, "invalid srcB register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var A = frame.Registers[srcA];
                    var B = frame.Registers[srcB];

                    if (!A.IsEmpty)
                    {
                        if (B.IsEmpty)
                        {
                            frame.Registers[dst].Copy(A);
                        }
                        else
                        {
                            if (A.Type != B.Type)
                            {
                                throw new VMException(frame.VM, "Invalid cast during concat opcode");
                            }

                            var bytesA = A.AsByteArray();
                            var bytesB = B.AsByteArray();

                            var result = new byte[bytesA.Length + bytesB.Length];
                            Array.Copy(bytesA, result, bytesA.Length);
                            Array.Copy(bytesB, 0, result, bytesA.Length, bytesB.Length);

                            VMType type = A.Type;
                            frame.Registers[dst].SetValue(result, type);
                        }
                    }
                    else
                    {
                        if (B.IsEmpty)
                        {
                            frame.Registers[dst] = new VMObject();
                        }
                        else
                        {
                            frame.Registers[dst].Copy(B);
                        }
                    }

                    break;
                }

                case Opcode.SUBSTR:
                {
                    throw new NotImplementedException();
                }

                // args: byte src_reg, byte dest_reg, var length
                case Opcode.LEFT:
                {
                    var src = Read8();
                    var dst = Read8();
                    var len = (int)ReadVar(0xFFFF);

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var src_array = frame.Registers[src].AsByteArray();
                    Expect(len <= src_array.Length, "invalid length");

                    var result = new byte[len];

                    Array.Copy(src_array, result, len);

                    frame.Registers[dst].SetValue(result, VMType.Bytes);
                    break;
                }

                // args: byte src_reg, byte dest_reg, byte length
                case Opcode.RIGHT:
                {
                    var src = Read8();
                    var dst = Read8();
                    var len = (int)ReadVar(0xFFFF);

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var src_array = frame.Registers[src].AsByteArray();
                    Expect(len <= src_array.Length, "invalid length register");

                    var ofs = src_array.Length - len;

                    var result = new byte[len];
                    Array.Copy(src_array, ofs, result, 0, len);

                    frame.Registers[dst].SetValue(result, VMType.Bytes);
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.SIZE:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    int size;

                    var src_val = frame.Registers[src];

                    switch (src_val.Type)
                    {
                    case VMType.String:
                        size = src_val.AsString().Length;
                        break;

                    case VMType.Timestamp:
                    case VMType.Number:
                    case VMType.Enum:
                    case VMType.Bool:
                        size = 1;
                        break;

                    case VMType.None:
                        size = 0;
                        break;

                    default:
                        var src_array = src_val.AsByteArray();
                        size = src_array.Length;
                        break;
                    }

                    frame.Registers[dst].SetValue(size);
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.COUNT:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var val = frame.Registers[src];
                    int count;

                    switch (val.Type)
                    {
                    case VMType.Struct:
                    {
                        var children = val.GetChildren();
                        count = children.Count;
                        break;
                    }

                    default: count = 1; break;
                    }

                    frame.Registers[dst].SetValue(count);
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.NOT:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var val = frame.Registers[src].AsBool();

                    frame.Registers[dst].SetValue(!val);
                    break;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.AND:
                case Opcode.OR:
                case Opcode.XOR:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length, "invalid srcA register");
                    Expect(srcB < frame.Registers.Length, "invalid srcB register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var valA = frame.Registers[srcA];
                    var valB = frame.Registers[srcB];

                    switch (valA.Type)
                    {
                    case VMType.Bool:
                    {
                        Expect(valB.Type == VMType.Bool, $"expected {valA.Type} for logical op");

                        var a = valA.AsBool();
                        var b = valB.AsBool();

                        bool result;
                        switch (opcode)
                        {
                        case Opcode.AND: result = (a && b); break;

                        case Opcode.OR: result = (a || b); break;

                        case Opcode.XOR: result = (a ^ b); break;

                        default:
                        {
                            SetState(ExecutionState.Fault);
                            return;
                        }
                        }

                        frame.Registers[dst].SetValue(result);
                        break;
                    }

                    case VMType.Enum:
                    {
                        Expect(valB.Type == VMType.Enum, $"expected {valA.Type} for flag op");

                        var numA = valA.AsNumber();
                        var numB = valB.AsNumber();

                        Expect(numA.GetBitLength() <= 32, "too many bits");
                        Expect(numB.GetBitLength() <= 32, "too many bits");

                        var a = (uint)numA;
                        var b = (uint)numB;

                        if (opcode != Opcode.AND)
                        {
                            SetState(ExecutionState.Fault);
                        }

                        bool result = (a & b) != 0;

                        frame.Registers[dst].SetValue(result);
                        break;
                    }

                    case VMType.Number:
                    {
                        Expect(valB.Type == VMType.Number, $"expected {valA.Type} for logical op");

                        var numA = valA.AsNumber();
                        var numB = valB.AsNumber();

                        Expect(numA.GetBitLength() <= 64, "too many bits");
                        Expect(numB.GetBitLength() <= 64, "too many bits");

                        var a = (long)numA;
                        var b = (long)numB;

                        BigInteger result;
                        switch (opcode)
                        {
                        case Opcode.AND: result = (a & b); break;

                        case Opcode.OR: result = (a | b); break;

                        case Opcode.XOR: result = (a ^ b); break;

                        default:
                        {
                            SetState(ExecutionState.Fault);
                            return;
                        }
                        }

                        frame.Registers[dst].SetValue(result);
                        break;
                    }

                    default:
                        throw new VMException(frame.VM, "logical op unsupported for type " + valA.Type);
                    }

                    break;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.EQUAL:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length, "invalid srcA register");
                    Expect(srcB < frame.Registers.Length, "invalid srcB register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var a = frame.Registers[srcA];
                    var b = frame.Registers[srcB];

                    var result = a.Equals(b);
                    frame.Registers[dst].SetValue(result);

                    break;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.LT:
                case Opcode.GT:
                case Opcode.LTE:
                case Opcode.GTE:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length, "invalid srcA register");
                    Expect(srcB < frame.Registers.Length, "invalid srcB register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var a = frame.Registers[srcA].AsNumber();
                    var b = frame.Registers[srcB].AsNumber();

                    bool result;
                    switch (opcode)
                    {
                    case Opcode.LT: result = (a < b); break;

                    case Opcode.GT: result = (a > b); break;

                    case Opcode.LTE: result = (a <= b); break;

                    case Opcode.GTE: result = (a >= b); break;

                    default:
                    {
                        SetState(ExecutionState.Fault);
                        return;
                    }
                    }

                    frame.Registers[dst].SetValue(result);
                    break;
                }

                // args: byte reg
                case Opcode.INC:
                {
                    var dst = Read8();
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var val = frame.Registers[dst].AsNumber();
                    frame.Registers[dst].SetValue(val + 1);

                    break;
                }

                // args: byte reg
                case Opcode.DEC:
                {
                    var dst = Read8();
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var val = frame.Registers[dst].AsNumber();
                    frame.Registers[dst].SetValue(val - 1);

                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.SIGN:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var val = frame.Registers[src].AsNumber();

                    if (val == 0)
                    {
                        frame.Registers[dst].SetValue(BigInteger.Zero);
                    }
                    else
                    {
                        frame.Registers[dst].SetValue(val < 0 ? -1 : 1);
                    }

                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.NEGATE:
                {
                    var src = Read8();
                    var dst = Read8();
                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var val = frame.Registers[src].AsNumber();
                    frame.Registers[dst].SetValue(-val);

                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.ABS:
                {
                    var src = Read8();
                    var dst = Read8();
                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    var val = frame.Registers[src].AsNumber();
                    frame.Registers[dst].SetValue(val < 0 ? -val : val);

                    break;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.ADD:
                case Opcode.SUB:
                case Opcode.MUL:
                case Opcode.DIV:
                case Opcode.MOD:
                case Opcode.SHR:
                case Opcode.SHL:
                case Opcode.MIN:
                case Opcode.MAX:
                case Opcode.POW:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length, "invalid srcA register");
                    Expect(srcB < frame.Registers.Length, "invalid srcB register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    if (opcode == Opcode.ADD && frame.Registers[srcA].Type == VMType.String)
                    {
                        Expect(frame.Registers[srcB].Type == VMType.String, "invalid string as right operand");

                        var a = frame.Registers[srcA].AsString();
                        var b = frame.Registers[srcB].AsString();

                        var result = a + b;
                        frame.Registers[dst].SetValue(result);
                    }
                    else
                    {
                        var a = frame.Registers[srcA].AsNumber();
                        var b = frame.Registers[srcB].AsNumber();

                        BigInteger result;

                        switch (opcode)
                        {
                        case Opcode.ADD: result = a + b; break;

                        case Opcode.SUB: result = a - b; break;

                        case Opcode.MUL: result = a * b; break;

                        case Opcode.DIV: result = a / b; break;

                        case Opcode.MOD: result = a % b; break;

                        case Opcode.SHR: result = a >> (int)b; break;

                        case Opcode.SHL: result = a << (int)b; break;

                        case Opcode.MIN: result = a < b ? a : b; break;

                        case Opcode.MAX: result = a > b ? a : b; break;

                        case Opcode.POW: result = BigInteger.Pow(a, b); break;

                        default:
                        {
                            SetState(ExecutionState.Fault);
                            return;
                        }
                        }

                        frame.Registers[dst].SetValue(result);
                    }

                    break;
                }

                // args: byte src_reg, byte dest_reg, byte key
                case Opcode.PUT:
                {
                    var src    = Read8();
                    var dst    = Read8();
                    var keyReg = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");
                    Expect(keyReg < frame.Registers.Length, "invalid key register");

                    var key = frame.Registers[keyReg];
                    Throw.If(key.Type == VMType.None, "invalid key type");

                    var value = frame.Registers[src];

                    frame.Registers[dst].SetKey(key, value);

                    break;
                }

                // args: byte src_reg, byte dest_reg, byte key
                case Opcode.GET:
                {
                    var src    = Read8();
                    var dst    = Read8();
                    var keyReg = Read8();

                    Expect(src < frame.Registers.Length, "invalid src register");
                    Expect(dst < frame.Registers.Length, "invalid dst register");
                    Expect(keyReg < frame.Registers.Length, "invalid key register");

                    var key = frame.Registers[keyReg];
                    Throw.If(key.Type == VMType.None, "invalid key type");

                    var val = frame.Registers[src].GetKey(key);

                    frame.Registers[dst] = val;

                    break;
                }

                // args: byte dest_reg
                case Opcode.CLEAR:
                {
                    var dst = Read8();

                    Expect(dst < frame.Registers.Length, "invalid dst register");

                    frame.Registers[dst] = new VMObject();

                    break;
                }

                // args: byte dest_reg, var key
                case Opcode.CTX:
                    using (var m = new ProfileMarker("CTX"))
                    {
                        var src = Read8();
                        var dst = Read8();

                        Expect(src < frame.Registers.Length, "invalid src register");
                        Expect(dst < frame.Registers.Length, "invalid dst register");

                        var contextName = frame.Registers[src].AsString();

                        ExecutionContext context = frame.VM.FindContext(contextName);

                        if (context == null)
                        {
                            throw new VMException(frame.VM, $"VM ctx instruction failed: could not find context with name '{contextName}'");
                        }

                        frame.Registers[dst].SetValue(context);

                        break;
                    }

                // args: byte src_reg
                case Opcode.SWITCH:
                    using (var m = new ProfileMarker("SWITCH"))
                    {
                        var src = Read8();
                        Expect(src < frame.Registers.Length, "invalid src register");

                        var context = frame.Registers[src].AsInterop <ExecutionContext>();

                        _state = frame.VM.SwitchContext(context, InstructionPointer);

                        if (_state == ExecutionState.Halt)
                        {
                            _state = ExecutionState.Running;
                            frame.VM.PopFrame();
                        }
                        else
                        {
                            throw new VMException(frame.VM, $"VM switch instruction failed: execution state did not halt");
                        }

                        break;
                    }

                // args: byte src_reg dst_reg
                case Opcode.UNPACK:
                    using (var m = new ProfileMarker("SWITCH"))
                    {
                        var src = Read8();
                        var dst = Read8();

                        Expect(src < frame.Registers.Length, "invalid src register");
                        Expect(dst < frame.Registers.Length, "invalid dst register");

                        var bytes = frame.Registers[src].AsByteArray();
                        frame.Registers[dst] = VMObject.FromBytes(bytes);
                        break;
                    }

                case Opcode.DEBUG:
                {
                    break;         // put here a breakpoint for debugging
                }

                default:
                {
                    throw new VMException(frame.VM, $"Unknown VM opcode: {(int)opcode}");
                }
                }
            }
            catch (Exception ex)
            {
                ex = ex.ExpandInnerExceptions();

                Trace.WriteLine(ex.ToString());
                SetState(ExecutionState.Fault);

                if (!(ex is VMException))
                {
                    ex = new VMException(frame.VM, ex.Message);
                }

                throw ex;
            }
        }
コード例 #20
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);
        }
コード例 #21
0
        public StorageChangeSetContext ProcessBlock(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 {block.Timestamp} 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}");
                }
            }

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

            var oracle = Nexus.GetOracleReader();

            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 && !expectedValidator.IsNull)
            {
                throw new BlockGenerationException($"unexpected validator {block.Validator}, expected {expectedValidator}");
            }

            var changeSet = ProcessTransactions(block, transactions, oracle, minimumFee);

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

            return(changeSet);
        }
コード例 #22
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);
        }
コード例 #23
0
        public override ExecutionState Execute(ExecutionFrame frame, Stack <VMObject> stack)
        {
            if (this.Contract.ABI == null)
            {
                throw new VMException(frame.VM, $"VM nativecall failed: ABI is missing for contract '{this.Contract.Name}'");
            }

            if (stack.Count <= 0)
            {
                throw new VMException(frame.VM, $"VM nativecall failed: method name not present in the VM stack");
            }

            var stackObj   = stack.Pop();
            var methodName = stackObj.AsString();
            var method     = this.Contract.ABI.FindMethod(methodName);

            if (method == null)
            {
                throw new VMException(frame.VM, $"VM nativecall failed: contract '{this.Contract.Name}' does not have method '{methodName}' in its ABI");
            }

            if (stack.Count < method.parameters.Length)
            {
                throw new VMException(frame.VM, $"VM nativecall failed: calling method {methodName} with {stack.Count} arguments instead of {method.parameters.Length}");
            }

            var runtime = (RuntimeVM)frame.VM;

            using (var m = new ProfileMarker("InternalCall"))
                if (this.Contract.HasInternalMethod(methodName))
                {
                    ExecutionState result;
                    try
                    {
                        BigInteger gasCost = 10;
                        result = runtime.ConsumeGas(gasCost);
                        if (result == ExecutionState.Running)
                        {
                            Contract.LoadRuntimeData(runtime);
                            result = InternalCall(method, frame, stack);
                            Contract.UnloadRuntimeData();
                        }
                    }
                    catch (ArgumentException ex)
                    {
                        throw new VMException(frame.VM, $"VM nativecall failed: calling method {methodName} with arguments of wrong type, " + ex.ToString());
                    }

                    // we terminate here execution, since it will be restarted in next context
                    if (result == ExecutionState.Running)
                    {
                        result = ExecutionState.Halt;
                    }

                    return(result);
                }


            if (!(this.Contract is CustomContract customContract))
            {
                throw new VMException(frame.VM, $"VM nativecall failed: contract '{this.Contract.Name}' is not a valid custom contract");
            }

            stack.Push(stackObj);

            var context = new ScriptContext(customContract.Name, customContract.Script);

            return(context.Execute(frame, stack));
        }
コード例 #24
0
        public void Step(ExecutionFrame frame, Stack <VMObject> stack)
        {
            try
            {
                var opcode = (Opcode)Read8();

                frame.VM.ValidateOpcode(opcode);

                switch (opcode)
                {
                case Opcode.NOP:
                {
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.MOVE:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    frame.Registers[dst] = frame.Registers[src];
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.COPY:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    frame.Registers[dst].Copy(frame.Registers[src]);
                    break;
                }

                // args: byte dst_reg, byte type, var length, var data_bytes
                case Opcode.LOAD:
                {
                    var dst  = Read8();
                    var type = (VMType)Read8();
                    var len  = (int)ReadVar(0xFFFF);

                    Expect(dst < frame.Registers.Length);

                    var bytes = ReadBytes(len);
                    frame.Registers[dst].SetValue(bytes, type);

                    break;
                }

                // args: byte src_reg, dst_reg, byte type
                case Opcode.CAST:
                {
                    var src  = Read8();
                    var dst  = Read8();
                    var type = (VMType)Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var val = frame.Registers[src];
                    val = VMObject.CastTo(val, type);

                    frame.Registers[dst] = val;
                    break;
                }

                // args: byte src_reg
                case Opcode.PUSH:
                {
                    var src = Read8();
                    Expect(src < frame.Registers.Length);

                    var val = frame.Registers[src];

                    var temp = new VMObject();
                    temp.Copy(val);
                    stack.Push(temp);
                    break;
                }

                // args: byte dest_reg
                case Opcode.POP:
                {
                    var dst = Read8();

                    Expect(stack.Count > 0);
                    Expect(dst < frame.Registers.Length);

                    frame.Registers[dst] = stack.Pop();
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.SWAP:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var temp = frame.Registers[src];
                    frame.Registers[src] = frame.Registers[dst];
                    frame.Registers[dst] = temp;

                    break;
                }

                // args: ushort offset, byte regCount
                case Opcode.CALL:
                {
                    var count = Read8();
                    var ofs   = Read16();

                    Expect(ofs < this.Script.Length);
                    Expect(count >= 1);
                    Expect(count <= VirtualMachine.MaxRegisterCount);

                    frame.VM.PushFrame(this, InstructionPointer, count);

                    InstructionPointer = ofs;
                    break;
                }

                // args: byte srcReg
                case Opcode.EXTCALL:
                    using (var m = new ProfileMarker("EXTCALL"))
                    {
                        var src = Read8();
                        Expect(src < frame.Registers.Length);

                        var method = frame.Registers[src].AsString();

                        var state = frame.VM.ExecuteInterop(method);
                        if (state != ExecutionState.Running)
                        {
                            throw new VMException(frame.VM, "VM extcall failed: " + method);
                        }

                        break;
                    }

                // args: ushort offset, byte src_reg
                // NOTE: JMP only has offset arg, not the rest
                case Opcode.JMP:
                case Opcode.JMPIF:
                case Opcode.JMPNOT:
                {
                    bool shouldJump;

                    if (opcode == Opcode.JMP)
                    {
                        shouldJump = true;
                    }
                    else
                    {
                        var src = Read8();
                        Expect(src < frame.Registers.Length);

                        shouldJump = frame.Registers[src].AsBool();

                        if (opcode == Opcode.JMPNOT)
                        {
                            shouldJump = !shouldJump;
                        }
                    }

                    var newPos = (short)Read16();

                    Expect(newPos >= 0);
                    Expect(newPos < this.Script.Length);

                    if (shouldJump)
                    {
                        InstructionPointer = (uint)newPos;
                    }

                    break;
                }

                // args: var length, var bytes
                case Opcode.THROW:
                {
                    var len = (int)ReadVar(1024);

                    if (len > 0)
                    {
                        var bytes = ReadBytes(len);
                    }

                    SetState(ExecutionState.Fault);
                    return;
                }

                // args: none
                case Opcode.RET:
                {
                    if (frame.VM.frames.Count > 1)
                    {
                        var temp = frame.VM.PeekFrame();

                        if (temp.Context == this)
                        {
                            InstructionPointer = frame.VM.PopFrame();
                            //Expect(InstructionPointer == this.Script.Length);
                        }
                        else
                        {
                            SetState(ExecutionState.Halt);
                        }
                    }
                    else
                    {
                        SetState(ExecutionState.Halt);
                    }
                    return;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.CAT:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length);
                    Expect(srcB < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var A = frame.Registers[srcA];
                    var B = frame.Registers[srcB];

                    if (!A.IsEmpty)
                    {
                        if (B.IsEmpty)
                        {
                            frame.Registers[dst].Copy(A);
                        }
                        else
                        {
                            var bytesA = A.AsByteArray();
                            var bytesB = B.AsByteArray();

                            var result = new byte[bytesA.Length + bytesB.Length];
                            Array.Copy(bytesA, result, bytesA.Length);
                            Array.Copy(bytesB, 0, result, bytesA.Length, bytesB.Length);

                            Expect(A.Type == B.Type);

                            VMType type = A.Type;
                            frame.Registers[dst].SetValue(result, type);
                        }
                    }
                    else
                    {
                        if (B.IsEmpty)
                        {
                            frame.Registers[dst] = new VMObject();
                        }
                        else
                        {
                            frame.Registers[dst].Copy(B);
                        }
                    }

                    break;
                }

                case Opcode.SUBSTR:
                {
                    throw new NotImplementedException();
                }

                // args: byte src_reg, byte dest_reg, var length
                case Opcode.LEFT:
                {
                    var src = Read8();
                    var dst = Read8();
                    var len = (int)ReadVar(0xFFFF);

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var src_array = frame.Registers[src].AsByteArray();
                    Expect(len <= src_array.Length);

                    var result = new byte[len];

                    Array.Copy(src_array, result, len);

                    frame.Registers[dst].SetValue(result, VMType.Bytes);
                    break;
                }

                // args: byte src_reg, byte dest_reg, byte length
                case Opcode.RIGHT:
                {
                    var src = Read8();
                    var dst = Read8();
                    var len = (int)ReadVar(0xFFFF);

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var src_array = frame.Registers[src].AsByteArray();
                    Expect(len <= src_array.Length);

                    var ofs = src_array.Length - len;

                    var result = new byte[len];
                    Array.Copy(src_array, ofs, result, 0, len);

                    frame.Registers[dst].SetValue(result, VMType.Bytes);
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.SIZE:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var src_array = frame.Registers[src].AsByteArray();
                    frame.Registers[dst].SetValue(src_array.Length);
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.COUNT:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var val = frame.Registers[src];
                    int count;

                    switch (val.Type)
                    {
                    case VMType.Struct:
                    {
                        var children = val.GetChildren();
                        count = children.Count;
                        break;
                    }

                    default: count = 1; break;
                    }

                    frame.Registers[dst].SetValue(count);
                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.NOT:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var val = frame.Registers[src].AsBool();

                    frame.Registers[dst].SetValue(!val);
                    break;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.AND:
                case Opcode.OR:
                case Opcode.XOR:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length);
                    Expect(srcB < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var a = frame.Registers[srcA].AsBool();
                    var b = frame.Registers[srcB].AsBool();

                    bool result;
                    switch (opcode)
                    {
                    case Opcode.AND: result = (a && b); break;

                    case Opcode.OR: result = (a || b); break;

                    case Opcode.XOR: result = (a ^ b); break;

                    default:
                    {
                        SetState(ExecutionState.Fault);
                        return;
                    }
                    }

                    frame.Registers[dst].SetValue(result);
                    break;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.EQUAL:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length);
                    Expect(srcB < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var a = frame.Registers[srcA];
                    var b = frame.Registers[srcB];

                    var result = a.Equals(b);
                    frame.Registers[dst].SetValue(result);

                    break;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.LT:
                case Opcode.GT:
                case Opcode.LTE:
                case Opcode.GTE:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length);
                    Expect(srcB < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var a = frame.Registers[srcA].AsNumber();
                    var b = frame.Registers[srcB].AsNumber();

                    bool result;
                    switch (opcode)
                    {
                    case Opcode.LT: result = (a < b); break;

                    case Opcode.GT: result = (a > b); break;

                    case Opcode.LTE: result = (a <= b); break;

                    case Opcode.GTE: result = (a >= b); break;

                    default:
                    {
                        SetState(ExecutionState.Fault);
                        return;
                    }
                    }

                    frame.Registers[dst].SetValue(result);
                    break;
                }

                // args: byte reg
                case Opcode.INC:
                {
                    var dst = Read8();
                    Expect(dst < frame.Registers.Length);

                    var val = frame.Registers[dst].AsNumber();
                    frame.Registers[dst].SetValue(val + 1);

                    break;
                }

                // args: byte reg
                case Opcode.DEC:
                {
                    var dst = Read8();
                    Expect(dst < frame.Registers.Length);

                    var val = frame.Registers[dst].AsNumber();
                    frame.Registers[dst].SetValue(val - 1);

                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.SIGN:
                {
                    var src = Read8();
                    var dst = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var val = frame.Registers[src].AsNumber();

                    if (val == 0)
                    {
                        frame.Registers[dst].SetValue(BigInteger.Zero);
                    }
                    else
                    {
                        frame.Registers[dst].SetValue(val < 0 ? -1 : 1);
                    }

                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.NEGATE:
                {
                    var src = Read8();
                    var dst = Read8();
                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var val = frame.Registers[src].AsNumber();
                    frame.Registers[dst].SetValue(-val);

                    break;
                }

                // args: byte src_reg, byte dest_reg
                case Opcode.ABS:
                {
                    var src = Read8();
                    var dst = Read8();
                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var val = frame.Registers[src].AsNumber();
                    frame.Registers[dst].SetValue(val < 0 ? -val : val);

                    break;
                }

                // args: byte src_a_reg, byte src_b_reg, byte dest_reg
                case Opcode.ADD:
                case Opcode.SUB:
                case Opcode.MUL:
                case Opcode.DIV:
                case Opcode.MOD:
                case Opcode.SHR:
                case Opcode.SHL:
                case Opcode.MIN:
                case Opcode.MAX:
                {
                    var srcA = Read8();
                    var srcB = Read8();
                    var dst  = Read8();

                    Expect(srcA < frame.Registers.Length);
                    Expect(srcB < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);

                    var a = frame.Registers[srcA].AsNumber();
                    var b = frame.Registers[srcB].AsNumber();

                    BigInteger result;

                    switch (opcode)
                    {
                    case Opcode.ADD: result = a + b; break;

                    case Opcode.SUB: result = a - b; break;

                    case Opcode.MUL: result = a * b; break;

                    case Opcode.DIV: result = a / b; break;

                    case Opcode.MOD: result = a % b; break;

                    case Opcode.SHR: result = a >> (int)b; break;

                    case Opcode.SHL: result = a << (int)b; break;

                    case Opcode.MIN: result = a < b ? a : b; break;

                    case Opcode.MAX: result = a > b ? a : b; break;

                    default:
                    {
                        SetState(ExecutionState.Fault);
                        return;
                    }
                    }

                    frame.Registers[dst].SetValue(result);
                    break;
                }

                // args: byte src_reg, byte dest_reg, byte key
                case Opcode.PUT:
                {
                    var src    = Read8();
                    var dst    = Read8();
                    var keyReg = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);
                    Expect(keyReg < frame.Registers.Length);

                    var key   = frame.Registers[keyReg];
                    var value = frame.Registers[src];

                    frame.Registers[dst].SetKey(key, value);

                    break;
                }

                // args: byte src_reg, byte dest_reg, byte key
                case Opcode.GET:
                {
                    var src    = Read8();
                    var dst    = Read8();
                    var keyReg = Read8();

                    Expect(src < frame.Registers.Length);
                    Expect(dst < frame.Registers.Length);
                    Expect(keyReg < frame.Registers.Length);

                    var key = frame.Registers[keyReg];
                    var val = frame.Registers[src].GetKey(key);

                    frame.Registers[dst] = val;

                    break;
                }

                // args: byte dest_reg
                case Opcode.THIS:
                {
                    var dst = Read8();
                    Expect(dst < frame.Registers.Length);

                    frame.Registers[dst].SetValue(this);

                    break;
                }

                // args: byte dest_reg, var key
                case Opcode.CTX:
                    using (var m = new ProfileMarker("CTX"))
                    {
                        var src = Read8();
                        var dst = Read8();

                        Expect(src < frame.Registers.Length);
                        Expect(dst < frame.Registers.Length);

                        var contextName = frame.Registers[src].AsString();

                        ExecutionContext context = frame.VM.FindContext(contextName);

                        if (context == null)
                        {
                            throw new VMException(frame.VM, $"VM ctx instruction failed: could not find context with name '{contextName}'");
                        }

                        frame.Registers[dst].SetValue(context);

                        break;
                    }

                // args: byte src_reg
                case Opcode.SWITCH:
                    using (var m = new ProfileMarker("SWITCH"))
                    {
                        var src = Read8();
                        Expect(src < frame.Registers.Length);

                        var context = frame.Registers[src].AsInterop <ExecutionContext>();

                        _state = frame.VM.SwitchContext(context, InstructionPointer);

                        if (_state == ExecutionState.Halt)
                        {
                            _state = ExecutionState.Running;
                            frame.VM.PopFrame();
                        }
                        else
                        {
                            throw new VMException(frame.VM, $"VM switch instruction failed: execution state did not halt");
                        }

                        break;
                    }

                default:
                {
                    throw new VMException(frame.VM, $"Unknown VM opcode: {(int)opcode}");
                }
                }
            }
            catch (Exception ex)
            {
                if (ex is TargetInvocationException)
                {
                    ex = ((TargetInvocationException)ex).InnerException;
                }

                Trace.WriteLine(ex.ToString());
                SetState(ExecutionState.Fault);

                if (!(ex is VMException))
                {
                    ex = new VMException(frame.VM, ex.Message);
                }

                if (frame.VM.ThrowOnFault) // enable this when debugging difficult stuff in the VM, should not be activated for production code
                {
                    throw ex;
                }
            }
        }
コード例 #25
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);
                }
            }
        }