/// <summary>
        /// executes the script until it finishes, fails or hits a breakpoint
        /// </summary>
        public DebuggerState Run()
        {
            do
            {
                lastState = Step();
            } while (lastState.state == DebuggerState.State.Running);

            return(lastState);
        }
        public void Reset(byte[] inputScript, ABI ABI, string methodName)
        {
            if (ContractByteCode == null || ContractByteCode.Length == 0)
            {
                throw new Exception("Contract bytecode is not set yet!");
            }

            if (lastState.state == DebuggerState.State.Reset)
            {
                return;
            }

            if (currentTransaction == null)
            {
                //throw new Exception("Transaction not set");
                currentTransaction = new API.Transaction(this.blockchain.currentBlock);
            }

            usedGas         = 0;
            usedOpcodeCount = 0;

            currentTransaction.emulator = this;
            engine = new ExecutionEngine(currentTransaction, null, interop);
            engine.LoadScript(ContractByteCode);
            engine.LoadScript(inputScript);

            this.currentMethod = methodName;

            /*foreach (var output in currentTransaction.outputs)
             * {
             *  if (output.hash == this.currentHash)
             *  {
             *      output.hash = engine.CurrentContext.ScriptHash;
             *  }
             * }*/

            foreach (var pos in _breakpoints)
            {
                engine.AddBreakPoint((uint)pos);
            }

            //engine.Reset();

            lastState          = new DebuggerState(DebuggerState.State.Reset, 0);
            currentTransaction = null;

            _variables.Clear();
            this._ABI = ABI;
        }
        /// <summary>
        /// executes a single instruction in the current script, and returns the last script offset
        /// </summary>
        public DebuggerState Step()
        {
            if (lastState.state == DebuggerState.State.Finished || lastState.state == DebuggerState.State.Invalid)
            {
                return(lastState);
            }

            ExecuteSingleStep();

            try
            {
                lastOffset = engine.CurrentContext.InstructionPointer;

                var     opcode = engine.lastOpcode;
                decimal opCost;

                if (opcode <= OpCode.PUSH16)
                {
                    opCost = 0;
                }
                else
                {
                    switch (opcode)
                    {
                    case OpCode.SYSCALL:
                    {
                        var callInfo = interop.FindCall(engine.lastSysCall);
                        opCost = (callInfo != null) ? callInfo.gasCost : 0;

                        if (engine.lastSysCall.EndsWith("Storage.Put"))
                        {
                            opCost *= (Storage.lastStorageLength / 1024.0m);
                            if (opCost < 1)
                            {
                                opCost = 1;
                            }
                        }
                        break;
                    }

                    case OpCode.CHECKMULTISIG:
                    case OpCode.CHECKSIG: opCost = 0.1m; break;

                    case OpCode.APPCALL:
                    case OpCode.TAILCALL:
                    case OpCode.SHA256:
                    case OpCode.SHA1: opCost = 0.01m; break;

                    case OpCode.HASH256:
                    case OpCode.HASH160: opCost = 0.02m; break;

                    case OpCode.NOP: opCost = 0; break;

                    default: opCost = 0.001m; break;
                    }
                }

                usedGas += opCost;
                usedOpcodeCount++;

                OnStep?.Invoke(new EmulatorStepInfo()
                {
                    byteCode = engine.CurrentContext.Script, offset = engine.CurrentContext.InstructionPointer, opcode = opcode, gasCost = opCost, sysCall = opcode == OpCode.SYSCALL? engine.lastSysCall : null
                });
            }
            catch
            {
                // failed to get instruction pointer
            }

            if (engine.State.HasFlag(VMState.FAULT))
            {
                lastState = new DebuggerState(DebuggerState.State.Exception, lastOffset);
                return(lastState);
            }

            if (engine.State.HasFlag(VMState.BREAK))
            {
                lastState    = new DebuggerState(DebuggerState.State.Break, lastOffset);
                engine.State = VMState.NONE;
                return(lastState);
            }

            if (engine.State.HasFlag(VMState.HALT))
            {
                lastState = new DebuggerState(DebuggerState.State.Finished, lastOffset);
                return(lastState);
            }

            lastState = new DebuggerState(DebuggerState.State.Running, lastOffset);
            return(lastState);
        }
        public void Reset(DataNode inputs, ABI ABI)
        {
            if (contractByteCode == null || contractByteCode.Length == 0)
            {
                throw new Exception("Contract bytecode is not set yet!");
            }

            if (lastState.state == DebuggerState.State.Reset)
            {
                return;
            }

            if (currentTransaction == null)
            {
                //throw new Exception("Transaction not set");
                currentTransaction = new Transaction(this.blockchain.currentBlock);
            }

            usedGas         = 0;
            usedOpcodeCount = 0;

            currentTransaction.emulator = this;
            engine = new ExecutionEngine(currentTransaction, Crypto.Default, null, interop);
            engine.LoadScript(contractByteCode);

            foreach (var output in currentTransaction.outputs)
            {
                if (output.hash == this.currentHash)
                {
                    output.hash = new UInt160(engine.CurrentContext.ScriptHash);
                }
            }

            foreach (var pos in _breakpoints)
            {
                engine.AddBreakPoint((uint)pos);
            }

            using (ScriptBuilder sb = new ScriptBuilder())
            {
                var items = new Stack <object>();

                if (inputs != null)
                {
                    foreach (var item in inputs.Children)
                    {
                        var obj = Emulator.ConvertArgument(item);
                        items.Push(obj);
                    }
                }

                while (items.Count > 0)
                {
                    var item = items.Pop();
                    EmitObject(sb, item);
                }

                var loaderScript = sb.ToArray();
                //System.IO.File.WriteAllBytes("loader.avm", loaderScript);
                engine.LoadScript(loaderScript);
            }

            //engine.Reset();

            lastState          = new DebuggerState(DebuggerState.State.Reset, 0);
            currentTransaction = null;

            _variables.Clear();
            this._ABI = ABI;
        }