diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 58c5ecac0..96a06ea54 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -378,7 +378,7 @@ func (bc *Blockchain) notificationDispatcher() { for ch := range executionFeed { ch <- aer } - if aer.VMState == "HALT" { + if aer.VMState == vm.HaltState { for i := range aer.Events { for ch := range notificationFeed { ch <- &aer.Events[i] diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index adbe56f2f..480d692f3 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -13,6 +13,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/stretchr/testify/assert" @@ -316,7 +317,7 @@ func TestSubscriptions(t *testing.T) { exec := <-executionCh require.Equal(t, b.Hash(), exec.TxHash) - require.Equal(t, exec.VMState, "HALT") + require.Equal(t, exec.VMState, vm.HaltState) // 3 burn events for every tx and 1 mint for primary node require.True(t, len(notificationCh) >= 4) @@ -331,7 +332,7 @@ func TestSubscriptions(t *testing.T) { require.Equal(t, txExpected, tx) exec := <-executionCh require.Equal(t, tx.Hash(), exec.TxHash) - if exec.VMState == "HALT" { + if exec.VMState == vm.HaltState { notif := <-notificationCh require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash) } diff --git a/pkg/core/native_contract_test.go b/pkg/core/native_contract_test.go index c155abab6..b2a9623d6 100644 --- a/pkg/core/native_contract_test.go +++ b/pkg/core/native_contract_test.go @@ -10,6 +10,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" @@ -122,14 +123,14 @@ func TestNativeContract_Invoke(t *testing.T) { res, err := chain.GetAppExecResult(tx.Hash()) require.NoError(t, err) - require.Equal(t, "HALT", res.VMState) + require.Equal(t, vm.HaltState, res.VMState) require.Equal(t, 1, len(res.Stack)) require.Equal(t, smartcontract.IntegerType, res.Stack[0].Type) require.EqualValues(t, 42, res.Stack[0].Value) res, err = chain.GetAppExecResult(tx2.Hash()) require.NoError(t, err) - require.Equal(t, "FAULT", res.VMState) + require.Equal(t, vm.FaultState, res.VMState) require.NoError(t, chain.persist()) select { diff --git a/pkg/core/native_policy_test.go b/pkg/core/native_policy_test.go index 903af3925..b0fd12964 100644 --- a/pkg/core/native_policy_test.go +++ b/pkg/core/native_policy_test.go @@ -12,6 +12,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/stretchr/testify/require" ) @@ -253,7 +254,7 @@ func invokeNativePolicyMethod(chain *Blockchain, method string, args ...interfac } func checkResult(t *testing.T, result *state.AppExecResult, expected smartcontract.Parameter) { - require.Equal(t, "HALT", result.VMState) + require.Equal(t, vm.HaltState, result.VMState) require.Equal(t, 1, len(result.Stack)) require.Equal(t, expected.Type, result.Stack[0].Type) require.EqualValues(t, expected.Value, result.Stack[0].Value) diff --git a/pkg/core/state/notification_event.go b/pkg/core/state/notification_event.go index 4849de6c4..1818f4c7b 100644 --- a/pkg/core/state/notification_event.go +++ b/pkg/core/state/notification_event.go @@ -7,6 +7,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -23,7 +24,7 @@ type NotificationEvent struct { type AppExecResult struct { TxHash util.Uint256 Trigger trigger.Type - VMState string + VMState vm.State GasConsumed int64 Stack []smartcontract.Parameter Events []NotificationEvent @@ -56,7 +57,7 @@ func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) { func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) { w.WriteBytes(aer.TxHash[:]) w.WriteB(byte(aer.Trigger)) - w.WriteString(aer.VMState) + w.WriteB(byte(aer.VMState)) w.WriteU64LE(uint64(aer.GasConsumed)) w.WriteArray(aer.Stack) w.WriteArray(aer.Events) @@ -66,7 +67,7 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) { func (aer *AppExecResult) DecodeBinary(r *io.BinReader) { r.ReadBytes(aer.TxHash[:]) aer.Trigger = trigger.Type(r.ReadB()) - aer.VMState = r.ReadString() + aer.VMState = vm.State(r.ReadB()) aer.GasConsumed = int64(r.ReadU64LE()) r.ReadArray(&aer.Stack) r.ReadArray(&aer.Events) diff --git a/pkg/core/state/notification_event_test.go b/pkg/core/state/notification_event_test.go index 2b81c9bc6..5242b8544 100644 --- a/pkg/core/state/notification_event_test.go +++ b/pkg/core/state/notification_event_test.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/internal/random" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -23,7 +24,7 @@ func TestEncodeDecodeAppExecResult(t *testing.T) { appExecResult := &AppExecResult{ TxHash: random.Uint256(), Trigger: 1, - VMState: "Hault", + VMState: vm.HaltState, GasConsumed: 10, Stack: []smartcontract.Parameter{}, Events: []NotificationEvent{}, diff --git a/pkg/rpc/response/result/application_log.go b/pkg/rpc/response/result/application_log.go index afefddf8a..8d93e005b 100644 --- a/pkg/rpc/response/result/application_log.go +++ b/pkg/rpc/response/result/application_log.go @@ -49,7 +49,7 @@ func NewApplicationLog(appExecRes *state.AppExecResult) ApplicationLog { return ApplicationLog{ TxHash: appExecRes.TxHash, Trigger: appExecRes.Trigger.String(), - VMState: appExecRes.VMState, + VMState: appExecRes.VMState.String(), GasConsumed: appExecRes.GasConsumed, Stack: appExecRes.Stack, Events: events, diff --git a/pkg/rpc/response/result/raw_mempool.go b/pkg/rpc/response/result/raw_mempool.go new file mode 100644 index 000000000..d0a3ed518 --- /dev/null +++ b/pkg/rpc/response/result/raw_mempool.go @@ -0,0 +1,10 @@ +package result + +import "github.com/nspcc-dev/neo-go/pkg/util" + +// RawMempool represents a result of getrawmempool RPC call. +type RawMempool struct { + Height uint32 `json:"height"` + Verified []util.Uint256 `json:"verified"` + Unverified []util.Uint256 `json:"unverified"` +} diff --git a/pkg/rpc/response/result/tx_raw_output.go b/pkg/rpc/response/result/tx_raw_output.go index 484158f74..518e8ef9b 100644 --- a/pkg/rpc/response/result/tx_raw_output.go +++ b/pkg/rpc/response/result/tx_raw_output.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/blockchainer" + "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/util" ) @@ -22,10 +23,11 @@ type TransactionMetadata struct { Blockhash util.Uint256 `json:"blockhash,omitempty"` Confirmations int `json:"confirmations,omitempty"` Timestamp uint64 `json:"blocktime,omitempty"` + VMState string `json:"vmstate"` } // NewTransactionOutputRaw returns a new ransactionOutputRaw object. -func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, chain blockchainer.Blockchainer) TransactionOutputRaw { +func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, appExecResult *state.AppExecResult, chain blockchainer.Blockchainer) TransactionOutputRaw { // confirmations formula confirmations := int(chain.BlockHeight() - header.Base.Index + 1) return TransactionOutputRaw{ @@ -34,6 +36,7 @@ func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, Blockhash: header.Hash(), Confirmations: confirmations, Timestamp: header.Timestamp, + VMState: appExecResult.VMState.String(), }, } } @@ -44,6 +47,7 @@ func (t TransactionOutputRaw) MarshalJSON() ([]byte, error) { Blockhash: t.Blockhash, Confirmations: t.Confirmations, Timestamp: t.Timestamp, + VMState: t.VMState, }) if err != nil { return nil, err @@ -75,6 +79,7 @@ func (t *TransactionOutputRaw) UnmarshalJSON(data []byte) error { t.Blockhash = output.Blockhash t.Confirmations = output.Confirmations t.Timestamp = output.Timestamp + t.VMState = output.VMState return json.Unmarshal(data, &t.Transaction) } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index bac1dfeb8..9d17c71c5 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -463,13 +463,20 @@ func (s *Server) getPeers(_ request.Params) (interface{}, *response.Error) { return peers, nil } -func (s *Server) getRawMempool(_ request.Params) (interface{}, *response.Error) { +func (s *Server) getRawMempool(reqParams request.Params) (interface{}, *response.Error) { + verbose := reqParams.Value(0).GetBoolean() mp := s.chain.GetMemPool() hashList := make([]util.Uint256, 0) for _, item := range mp.GetVerifiedTransactions() { hashList = append(hashList, item.Hash()) } - return hashList, nil + if !verbose { + return hashList, nil + } + return result.RawMempool{ + Height: s.chain.BlockHeight(), + Verified: hashList, + }, nil } func (s *Server) validateAddress(reqParams request.Params) (interface{}, *response.Error) { @@ -685,10 +692,13 @@ func (s *Server) getrawtransaction(reqParams request.Params) (interface{}, *resp _header := s.chain.GetHeaderHash(int(height)) header, err := s.chain.GetHeader(_header) if err != nil { - resultsErr = response.NewInvalidParamsError(err.Error(), err) - } else { - results = result.NewTransactionOutputRaw(tx, header, s.chain) + return nil, response.NewInvalidParamsError(err.Error(), err) } + st, err := s.chain.GetAppExecResult(txHash) + if err != nil { + return nil, response.NewRPCError("Unknown transaction", err.Error(), err) + } + results = result.NewTransactionOutputRaw(tx, header, st, s.chain) } else { results = hex.EncodeToString(tx.Bytes()) } @@ -873,7 +883,7 @@ func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *resu vm.LoadScriptWithFlags(script, smartcontract.All) _ = vm.Run() result := &result.Invoke{ - State: vm.State(), + State: vm.State().String(), GasConsumed: vm.GasConsumed(), Script: hex.EncodeToString(script), Stack: vm.Estack().ToContractParameters(), diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index e97328362..78633cc99 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -162,14 +162,14 @@ func testFile(t *testing.T, filename string) { t.Run(ut.Tests[i].Name, func(t *testing.T) { prog := []byte(test.Script) vm := load(prog) - vm.state = breakState + vm.state = BreakState vm.RegisterInteropGetter(getTestingInterop) for i := range test.Steps { execStep(t, vm, test.Steps[i]) result := test.Steps[i].Result require.Equal(t, result.State, vm.state) - if result.State == faultState { // do not compare stacks on fault + if result.State == FaultState { // do not compare stacks on fault continue } diff --git a/pkg/vm/state.go b/pkg/vm/state.go index e90dad708..5fe6ee00f 100644 --- a/pkg/vm/state.go +++ b/pkg/vm/state.go @@ -11,10 +11,14 @@ type State uint8 // Available States. const ( - noneState State = 0 - haltState State = 1 << iota - faultState - breakState + // NoneState represents NONE VM state. + NoneState State = 0 + // HaltState represents HALT VM state. + HaltState State = 1 << iota + // FaultState represents FAULT VM state. + FaultState + // BreakState represents BREAK VM state. + BreakState ) // HasFlag checks for State flag presence. @@ -24,18 +28,18 @@ func (s State) HasFlag(f State) bool { // String implements the stringer interface. func (s State) String() string { - if s == noneState { + if s == NoneState { return "NONE" } ss := make([]string, 0, 3) - if s.HasFlag(haltState) { + if s.HasFlag(HaltState) { ss = append(ss, "HALT") } - if s.HasFlag(faultState) { + if s.HasFlag(FaultState) { ss = append(ss, "FAULT") } - if s.HasFlag(breakState) { + if s.HasFlag(BreakState) { ss = append(ss, "BREAK") } return strings.Join(ss, ", ") @@ -44,18 +48,18 @@ func (s State) String() string { // StateFromString converts string into the VM State. func StateFromString(s string) (st State, err error) { if s = strings.TrimSpace(s); s == "NONE" { - return noneState, nil + return NoneState, nil } ss := strings.Split(s, ",") for _, state := range ss { switch state = strings.TrimSpace(state); state { case "HALT": - st |= haltState + st |= HaltState case "FAULT": - st |= faultState + st |= FaultState case "BREAK": - st |= breakState + st |= BreakState default: return 0, errors.New("unknown state") } diff --git a/pkg/vm/state_test.go b/pkg/vm/state_test.go index 0b4dc64f7..be31dc5ee 100644 --- a/pkg/vm/state_test.go +++ b/pkg/vm/state_test.go @@ -15,42 +15,42 @@ func TestStateFromString(t *testing.T) { s, err = StateFromString("HALT") assert.NoError(t, err) - assert.Equal(t, haltState, s) + assert.Equal(t, HaltState, s) s, err = StateFromString("BREAK") assert.NoError(t, err) - assert.Equal(t, breakState, s) + assert.Equal(t, BreakState, s) s, err = StateFromString("FAULT") assert.NoError(t, err) - assert.Equal(t, faultState, s) + assert.Equal(t, FaultState, s) s, err = StateFromString("NONE") assert.NoError(t, err) - assert.Equal(t, noneState, s) + assert.Equal(t, NoneState, s) s, err = StateFromString("HALT, BREAK") assert.NoError(t, err) - assert.Equal(t, haltState|breakState, s) + assert.Equal(t, HaltState|BreakState, s) s, err = StateFromString("FAULT, BREAK") assert.NoError(t, err) - assert.Equal(t, faultState|breakState, s) + assert.Equal(t, FaultState|BreakState, s) _, err = StateFromString("HALT, KEK") assert.Error(t, err) } func TestState_HasFlag(t *testing.T) { - assert.True(t, haltState.HasFlag(haltState)) - assert.True(t, breakState.HasFlag(breakState)) - assert.True(t, faultState.HasFlag(faultState)) - assert.True(t, (haltState | breakState).HasFlag(haltState)) - assert.True(t, (haltState | breakState).HasFlag(breakState)) + assert.True(t, HaltState.HasFlag(HaltState)) + assert.True(t, BreakState.HasFlag(BreakState)) + assert.True(t, FaultState.HasFlag(FaultState)) + assert.True(t, (HaltState | BreakState).HasFlag(HaltState)) + assert.True(t, (HaltState | BreakState).HasFlag(BreakState)) - assert.False(t, haltState.HasFlag(breakState)) - assert.False(t, noneState.HasFlag(haltState)) - assert.False(t, (faultState | breakState).HasFlag(haltState)) + assert.False(t, HaltState.HasFlag(BreakState)) + assert.False(t, NoneState.HasFlag(HaltState)) + assert.False(t, (FaultState | BreakState).HasFlag(HaltState)) } func TestState_MarshalJSON(t *testing.T) { @@ -59,11 +59,11 @@ func TestState_MarshalJSON(t *testing.T) { err error ) - data, err = json.Marshal(haltState | breakState) + data, err = json.Marshal(HaltState | BreakState) assert.NoError(t, err) assert.Equal(t, data, []byte(`"HALT, BREAK"`)) - data, err = json.Marshal(faultState) + data, err = json.Marshal(FaultState) assert.NoError(t, err) assert.Equal(t, data, []byte(`"FAULT"`)) } @@ -76,13 +76,13 @@ func TestState_UnmarshalJSON(t *testing.T) { err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s) assert.NoError(t, err) - assert.Equal(t, haltState|breakState, s) + assert.Equal(t, HaltState|BreakState, s) err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s) assert.NoError(t, err) - assert.Equal(t, faultState|breakState, s) + assert.Equal(t, FaultState|BreakState, s) err = json.Unmarshal([]byte(`"NONE"`), &s) assert.NoError(t, err) - assert.Equal(t, noneState, s) + assert.Equal(t, NoneState, s) } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index d9e294731..0ad2a65f6 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -93,7 +93,7 @@ func New() *VM { func NewWithTrigger(t trigger.Type) *VM { vm := &VM{ getInterop: make([]InteropGetterFunc, 0, 3), // 3 functions is typical for our default usage. - state: haltState, + state: HaltState, istack: NewStack("invocation"), refs: newRefCounter(), keys: make(map[string]*keys.PublicKey), @@ -264,7 +264,7 @@ func (v *VM) Load(prog []byte) { // Clear all stacks and state, it could be a reload. v.istack.Clear() v.estack.Clear() - v.state = noneState + v.state = NoneState v.gasConsumed = 0 v.LoadScript(prog) } @@ -330,9 +330,9 @@ func (v *VM) Stack(n string) string { return string(b) } -// State returns string representation of the state for the VM. -func (v *VM) State() string { - return v.state.String() +// State returns the state for the VM. +func (v *VM) State() State { + return v.state } // Ready returns true if the VM ready to execute the loaded program. @@ -344,37 +344,37 @@ func (v *VM) Ready() bool { // Run starts the execution of the loaded program. func (v *VM) Run() error { if !v.Ready() { - v.state = faultState + v.state = FaultState return errors.New("no program loaded") } - if v.state.HasFlag(faultState) { + if v.state.HasFlag(FaultState) { // VM already ran something and failed, in general its state is // undefined in this case so we can't run anything. return errors.New("VM has failed") } - // haltState (the default) or breakState are safe to continue. - v.state = noneState + // HaltState (the default) or BreakState are safe to continue. + v.state = NoneState for { // check for breakpoint before executing the next instruction ctx := v.Context() if ctx != nil && ctx.atBreakPoint() { - v.state = breakState + v.state = BreakState } switch { - case v.state.HasFlag(faultState): + case v.state.HasFlag(FaultState): // Should be caught and reported already by the v.Step(), // but we're checking here anyway just in case. return errors.New("VM has failed") - case v.state.HasFlag(haltState), v.state.HasFlag(breakState): + case v.state.HasFlag(HaltState), v.state.HasFlag(BreakState): // Normal exit from this loop. return nil - case v.state == noneState: + case v.state == NoneState: if err := v.Step(); err != nil { return err } default: - v.state = faultState + v.state = FaultState return errors.New("unknown state") } } @@ -385,7 +385,7 @@ func (v *VM) Step() error { ctx := v.Context() op, param, err := ctx.Next() if err != nil { - v.state = faultState + v.state = FaultState return newError(ctx.ip, op, err) } return v.execute(ctx, op, param) @@ -397,7 +397,7 @@ func (v *VM) StepInto() error { ctx := v.Context() if ctx == nil { - v.state = haltState + v.state = HaltState } if v.HasStopped() { @@ -407,7 +407,7 @@ func (v *VM) StepInto() error { if ctx != nil && ctx.prog != nil { op, param, err := ctx.Next() if err != nil { - v.state = faultState + v.state = FaultState return newError(ctx.ip, op, err) } vErr := v.execute(ctx, op, param) @@ -418,7 +418,7 @@ func (v *VM) StepInto() error { cctx := v.Context() if cctx != nil && cctx.atBreakPoint() { - v.state = breakState + v.state = BreakState } return nil } @@ -426,14 +426,14 @@ func (v *VM) StepInto() error { // StepOut takes the debugger to the line where the current function was called. func (v *VM) StepOut() error { var err error - if v.state == breakState { - v.state = noneState + if v.state == BreakState { + v.state = NoneState } else { - v.state = breakState + v.state = BreakState } expSize := v.istack.len - for v.state == noneState && v.istack.len >= expSize { + for v.state == NoneState && v.istack.len >= expSize { err = v.StepInto() } return err @@ -447,22 +447,22 @@ func (v *VM) StepOver() error { return err } - if v.state == breakState { - v.state = noneState + if v.state == BreakState { + v.state = NoneState } else { - v.state = breakState + v.state = BreakState } expSize := v.istack.len for { err = v.StepInto() - if !(v.state == noneState && v.istack.len > expSize) { + if !(v.state == NoneState && v.istack.len > expSize) { break } } - if v.state == noneState { - v.state = breakState + if v.state == NoneState { + v.state = BreakState } return err @@ -471,22 +471,22 @@ func (v *VM) StepOver() error { // HasFailed returns whether VM is in the failed state now. Usually used to // check status after Run. func (v *VM) HasFailed() bool { - return v.state.HasFlag(faultState) + return v.state.HasFlag(FaultState) } // HasStopped returns whether VM is in Halt or Failed state. func (v *VM) HasStopped() bool { - return v.state.HasFlag(haltState) || v.state.HasFlag(faultState) + return v.state.HasFlag(HaltState) || v.state.HasFlag(FaultState) } // HasHalted returns whether VM is in Halt state. func (v *VM) HasHalted() bool { - return v.state.HasFlag(haltState) + return v.state.HasFlag(HaltState) } // AtBreakpoint returns whether VM is at breakpoint. func (v *VM) AtBreakpoint() bool { - return v.state.HasFlag(breakState) + return v.state.HasFlag(BreakState) } // GetInteropID converts instruction parameter to an interop ID. @@ -512,10 +512,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro // each panic at a central point, putting the VM in a fault state and setting error. defer func() { if errRecover := recover(); errRecover != nil { - v.state = faultState + v.state = FaultState err = newError(ctx.ip, op, errRecover) } else if v.refs.size > MaxStackSize { - v.state = faultState + v.state = FaultState err = newError(ctx.ip, op, "stack is too big") } }() @@ -1268,7 +1268,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro v.unloadContext(oldCtx) if v.istack.Len() == 0 { - v.state = haltState + v.state = HaltState break }