Merge pull request #1236 from nspcc-dev/rpc/getrawmempool

rpc: update `getrawmempool` and `getrawtransaction` RPC-calls
This commit is contained in:
fyrchik 2020-07-29 10:18:51 +03:00 committed by GitHub
commit b187dfe3ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 120 additions and 86 deletions

View file

@ -378,7 +378,7 @@ func (bc *Blockchain) notificationDispatcher() {
for ch := range executionFeed { for ch := range executionFeed {
ch <- aer ch <- aer
} }
if aer.VMState == "HALT" { if aer.VMState == vm.HaltState {
for i := range aer.Events { for i := range aer.Events {
for ch := range notificationFeed { for ch := range notificationFeed {
ch <- &aer.Events[i] ch <- &aer.Events[i]

View file

@ -13,6 +13,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -316,7 +317,7 @@ func TestSubscriptions(t *testing.T) {
exec := <-executionCh exec := <-executionCh
require.Equal(t, b.Hash(), exec.TxHash) 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 // 3 burn events for every tx and 1 mint for primary node
require.True(t, len(notificationCh) >= 4) require.True(t, len(notificationCh) >= 4)
@ -331,7 +332,7 @@ func TestSubscriptions(t *testing.T) {
require.Equal(t, txExpected, tx) require.Equal(t, txExpected, tx)
exec := <-executionCh exec := <-executionCh
require.Equal(t, tx.Hash(), exec.TxHash) require.Equal(t, tx.Hash(), exec.TxHash)
if exec.VMState == "HALT" { if exec.VMState == vm.HaltState {
notif := <-notificationCh notif := <-notificationCh
require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash) require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash)
} }

View file

@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -122,14 +123,14 @@ func TestNativeContract_Invoke(t *testing.T) {
res, err := chain.GetAppExecResult(tx.Hash()) res, err := chain.GetAppExecResult(tx.Hash())
require.NoError(t, err) 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, 1, len(res.Stack))
require.Equal(t, smartcontract.IntegerType, res.Stack[0].Type) require.Equal(t, smartcontract.IntegerType, res.Stack[0].Type)
require.EqualValues(t, 42, res.Stack[0].Value) require.EqualValues(t, 42, res.Stack[0].Value)
res, err = chain.GetAppExecResult(tx2.Hash()) res, err = chain.GetAppExecResult(tx2.Hash())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "FAULT", res.VMState) require.Equal(t, vm.FaultState, res.VMState)
require.NoError(t, chain.persist()) require.NoError(t, chain.persist())
select { select {

View file

@ -12,6 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "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/emit"
"github.com/stretchr/testify/require" "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) { 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, 1, len(result.Stack))
require.Equal(t, expected.Type, result.Stack[0].Type) require.Equal(t, expected.Type, result.Stack[0].Type)
require.EqualValues(t, expected.Value, result.Stack[0].Value) require.EqualValues(t, expected.Value, result.Stack[0].Value)

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "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" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
@ -23,7 +24,7 @@ type NotificationEvent struct {
type AppExecResult struct { type AppExecResult struct {
TxHash util.Uint256 TxHash util.Uint256
Trigger trigger.Type Trigger trigger.Type
VMState string VMState vm.State
GasConsumed int64 GasConsumed int64
Stack []smartcontract.Parameter Stack []smartcontract.Parameter
Events []NotificationEvent Events []NotificationEvent
@ -56,7 +57,7 @@ func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) { func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(aer.TxHash[:]) w.WriteBytes(aer.TxHash[:])
w.WriteB(byte(aer.Trigger)) w.WriteB(byte(aer.Trigger))
w.WriteString(aer.VMState) w.WriteB(byte(aer.VMState))
w.WriteU64LE(uint64(aer.GasConsumed)) w.WriteU64LE(uint64(aer.GasConsumed))
w.WriteArray(aer.Stack) w.WriteArray(aer.Stack)
w.WriteArray(aer.Events) w.WriteArray(aer.Events)
@ -66,7 +67,7 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) {
func (aer *AppExecResult) DecodeBinary(r *io.BinReader) { func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
r.ReadBytes(aer.TxHash[:]) r.ReadBytes(aer.TxHash[:])
aer.Trigger = trigger.Type(r.ReadB()) aer.Trigger = trigger.Type(r.ReadB())
aer.VMState = r.ReadString() aer.VMState = vm.State(r.ReadB())
aer.GasConsumed = int64(r.ReadU64LE()) aer.GasConsumed = int64(r.ReadU64LE())
r.ReadArray(&aer.Stack) r.ReadArray(&aer.Stack)
r.ReadArray(&aer.Events) r.ReadArray(&aer.Events)

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/internal/random" "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/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
@ -23,7 +24,7 @@ func TestEncodeDecodeAppExecResult(t *testing.T) {
appExecResult := &AppExecResult{ appExecResult := &AppExecResult{
TxHash: random.Uint256(), TxHash: random.Uint256(),
Trigger: 1, Trigger: 1,
VMState: "Hault", VMState: vm.HaltState,
GasConsumed: 10, GasConsumed: 10,
Stack: []smartcontract.Parameter{}, Stack: []smartcontract.Parameter{},
Events: []NotificationEvent{}, Events: []NotificationEvent{},

View file

@ -49,7 +49,7 @@ func NewApplicationLog(appExecRes *state.AppExecResult) ApplicationLog {
return ApplicationLog{ return ApplicationLog{
TxHash: appExecRes.TxHash, TxHash: appExecRes.TxHash,
Trigger: appExecRes.Trigger.String(), Trigger: appExecRes.Trigger.String(),
VMState: appExecRes.VMState, VMState: appExecRes.VMState.String(),
GasConsumed: appExecRes.GasConsumed, GasConsumed: appExecRes.GasConsumed,
Stack: appExecRes.Stack, Stack: appExecRes.Stack,
Events: events, Events: events,

View file

@ -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"`
}

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/block" "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/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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
) )
@ -22,10 +23,11 @@ type TransactionMetadata struct {
Blockhash util.Uint256 `json:"blockhash,omitempty"` Blockhash util.Uint256 `json:"blockhash,omitempty"`
Confirmations int `json:"confirmations,omitempty"` Confirmations int `json:"confirmations,omitempty"`
Timestamp uint64 `json:"blocktime,omitempty"` Timestamp uint64 `json:"blocktime,omitempty"`
VMState string `json:"vmstate"`
} }
// NewTransactionOutputRaw returns a new ransactionOutputRaw object. // 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 formula
confirmations := int(chain.BlockHeight() - header.Base.Index + 1) confirmations := int(chain.BlockHeight() - header.Base.Index + 1)
return TransactionOutputRaw{ return TransactionOutputRaw{
@ -34,6 +36,7 @@ func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header,
Blockhash: header.Hash(), Blockhash: header.Hash(),
Confirmations: confirmations, Confirmations: confirmations,
Timestamp: header.Timestamp, Timestamp: header.Timestamp,
VMState: appExecResult.VMState.String(),
}, },
} }
} }
@ -44,6 +47,7 @@ func (t TransactionOutputRaw) MarshalJSON() ([]byte, error) {
Blockhash: t.Blockhash, Blockhash: t.Blockhash,
Confirmations: t.Confirmations, Confirmations: t.Confirmations,
Timestamp: t.Timestamp, Timestamp: t.Timestamp,
VMState: t.VMState,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -75,6 +79,7 @@ func (t *TransactionOutputRaw) UnmarshalJSON(data []byte) error {
t.Blockhash = output.Blockhash t.Blockhash = output.Blockhash
t.Confirmations = output.Confirmations t.Confirmations = output.Confirmations
t.Timestamp = output.Timestamp t.Timestamp = output.Timestamp
t.VMState = output.VMState
return json.Unmarshal(data, &t.Transaction) return json.Unmarshal(data, &t.Transaction)
} }

View file

@ -463,13 +463,20 @@ func (s *Server) getPeers(_ request.Params) (interface{}, *response.Error) {
return peers, nil 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() mp := s.chain.GetMemPool()
hashList := make([]util.Uint256, 0) hashList := make([]util.Uint256, 0)
for _, item := range mp.GetVerifiedTransactions() { for _, item := range mp.GetVerifiedTransactions() {
hashList = append(hashList, item.Hash()) 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) { 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 := s.chain.GetHeaderHash(int(height))
header, err := s.chain.GetHeader(_header) header, err := s.chain.GetHeader(_header)
if err != nil { if err != nil {
resultsErr = response.NewInvalidParamsError(err.Error(), err) return nil, response.NewInvalidParamsError(err.Error(), err)
} else {
results = result.NewTransactionOutputRaw(tx, header, s.chain)
} }
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 { } else {
results = hex.EncodeToString(tx.Bytes()) 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.LoadScriptWithFlags(script, smartcontract.All)
_ = vm.Run() _ = vm.Run()
result := &result.Invoke{ result := &result.Invoke{
State: vm.State(), State: vm.State().String(),
GasConsumed: vm.GasConsumed(), GasConsumed: vm.GasConsumed(),
Script: hex.EncodeToString(script), Script: hex.EncodeToString(script),
Stack: vm.Estack().ToContractParameters(), Stack: vm.Estack().ToContractParameters(),

View file

@ -162,14 +162,14 @@ func testFile(t *testing.T, filename string) {
t.Run(ut.Tests[i].Name, func(t *testing.T) { t.Run(ut.Tests[i].Name, func(t *testing.T) {
prog := []byte(test.Script) prog := []byte(test.Script)
vm := load(prog) vm := load(prog)
vm.state = breakState vm.state = BreakState
vm.RegisterInteropGetter(getTestingInterop) vm.RegisterInteropGetter(getTestingInterop)
for i := range test.Steps { for i := range test.Steps {
execStep(t, vm, test.Steps[i]) execStep(t, vm, test.Steps[i])
result := test.Steps[i].Result result := test.Steps[i].Result
require.Equal(t, result.State, vm.state) 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 continue
} }

View file

@ -11,10 +11,14 @@ type State uint8
// Available States. // Available States.
const ( const (
noneState State = 0 // NoneState represents NONE VM state.
haltState State = 1 << iota NoneState State = 0
faultState // HaltState represents HALT VM state.
breakState HaltState State = 1 << iota
// FaultState represents FAULT VM state.
FaultState
// BreakState represents BREAK VM state.
BreakState
) )
// HasFlag checks for State flag presence. // HasFlag checks for State flag presence.
@ -24,18 +28,18 @@ func (s State) HasFlag(f State) bool {
// String implements the stringer interface. // String implements the stringer interface.
func (s State) String() string { func (s State) String() string {
if s == noneState { if s == NoneState {
return "NONE" return "NONE"
} }
ss := make([]string, 0, 3) ss := make([]string, 0, 3)
if s.HasFlag(haltState) { if s.HasFlag(HaltState) {
ss = append(ss, "HALT") ss = append(ss, "HALT")
} }
if s.HasFlag(faultState) { if s.HasFlag(FaultState) {
ss = append(ss, "FAULT") ss = append(ss, "FAULT")
} }
if s.HasFlag(breakState) { if s.HasFlag(BreakState) {
ss = append(ss, "BREAK") ss = append(ss, "BREAK")
} }
return strings.Join(ss, ", ") return strings.Join(ss, ", ")
@ -44,18 +48,18 @@ func (s State) String() string {
// StateFromString converts string into the VM State. // StateFromString converts string into the VM State.
func StateFromString(s string) (st State, err error) { func StateFromString(s string) (st State, err error) {
if s = strings.TrimSpace(s); s == "NONE" { if s = strings.TrimSpace(s); s == "NONE" {
return noneState, nil return NoneState, nil
} }
ss := strings.Split(s, ",") ss := strings.Split(s, ",")
for _, state := range ss { for _, state := range ss {
switch state = strings.TrimSpace(state); state { switch state = strings.TrimSpace(state); state {
case "HALT": case "HALT":
st |= haltState st |= HaltState
case "FAULT": case "FAULT":
st |= faultState st |= FaultState
case "BREAK": case "BREAK":
st |= breakState st |= BreakState
default: default:
return 0, errors.New("unknown state") return 0, errors.New("unknown state")
} }

View file

@ -15,42 +15,42 @@ func TestStateFromString(t *testing.T) {
s, err = StateFromString("HALT") s, err = StateFromString("HALT")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, haltState, s) assert.Equal(t, HaltState, s)
s, err = StateFromString("BREAK") s, err = StateFromString("BREAK")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, breakState, s) assert.Equal(t, BreakState, s)
s, err = StateFromString("FAULT") s, err = StateFromString("FAULT")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, faultState, s) assert.Equal(t, FaultState, s)
s, err = StateFromString("NONE") s, err = StateFromString("NONE")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, noneState, s) assert.Equal(t, NoneState, s)
s, err = StateFromString("HALT, BREAK") s, err = StateFromString("HALT, BREAK")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, haltState|breakState, s) assert.Equal(t, HaltState|BreakState, s)
s, err = StateFromString("FAULT, BREAK") s, err = StateFromString("FAULT, BREAK")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, faultState|breakState, s) assert.Equal(t, FaultState|BreakState, s)
_, err = StateFromString("HALT, KEK") _, err = StateFromString("HALT, KEK")
assert.Error(t, err) assert.Error(t, err)
} }
func TestState_HasFlag(t *testing.T) { func TestState_HasFlag(t *testing.T) {
assert.True(t, haltState.HasFlag(haltState)) assert.True(t, HaltState.HasFlag(HaltState))
assert.True(t, breakState.HasFlag(breakState)) assert.True(t, BreakState.HasFlag(BreakState))
assert.True(t, faultState.HasFlag(faultState)) assert.True(t, FaultState.HasFlag(FaultState))
assert.True(t, (haltState | breakState).HasFlag(haltState)) assert.True(t, (HaltState | BreakState).HasFlag(HaltState))
assert.True(t, (haltState | breakState).HasFlag(breakState)) assert.True(t, (HaltState | BreakState).HasFlag(BreakState))
assert.False(t, haltState.HasFlag(breakState)) assert.False(t, HaltState.HasFlag(BreakState))
assert.False(t, noneState.HasFlag(haltState)) assert.False(t, NoneState.HasFlag(HaltState))
assert.False(t, (faultState | breakState).HasFlag(haltState)) assert.False(t, (FaultState | BreakState).HasFlag(HaltState))
} }
func TestState_MarshalJSON(t *testing.T) { func TestState_MarshalJSON(t *testing.T) {
@ -59,11 +59,11 @@ func TestState_MarshalJSON(t *testing.T) {
err error err error
) )
data, err = json.Marshal(haltState | breakState) data, err = json.Marshal(HaltState | BreakState)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, data, []byte(`"HALT, BREAK"`)) assert.Equal(t, data, []byte(`"HALT, BREAK"`))
data, err = json.Marshal(faultState) data, err = json.Marshal(FaultState)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, data, []byte(`"FAULT"`)) assert.Equal(t, data, []byte(`"FAULT"`))
} }
@ -76,13 +76,13 @@ func TestState_UnmarshalJSON(t *testing.T) {
err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s) err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, haltState|breakState, s) assert.Equal(t, HaltState|BreakState, s)
err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s) err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, faultState|breakState, s) assert.Equal(t, FaultState|BreakState, s)
err = json.Unmarshal([]byte(`"NONE"`), &s) err = json.Unmarshal([]byte(`"NONE"`), &s)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, noneState, s) assert.Equal(t, NoneState, s)
} }

View file

@ -93,7 +93,7 @@ func New() *VM {
func NewWithTrigger(t trigger.Type) *VM { func NewWithTrigger(t trigger.Type) *VM {
vm := &VM{ vm := &VM{
getInterop: make([]InteropGetterFunc, 0, 3), // 3 functions is typical for our default usage. getInterop: make([]InteropGetterFunc, 0, 3), // 3 functions is typical for our default usage.
state: haltState, state: HaltState,
istack: NewStack("invocation"), istack: NewStack("invocation"),
refs: newRefCounter(), refs: newRefCounter(),
keys: make(map[string]*keys.PublicKey), 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. // Clear all stacks and state, it could be a reload.
v.istack.Clear() v.istack.Clear()
v.estack.Clear() v.estack.Clear()
v.state = noneState v.state = NoneState
v.gasConsumed = 0 v.gasConsumed = 0
v.LoadScript(prog) v.LoadScript(prog)
} }
@ -330,9 +330,9 @@ func (v *VM) Stack(n string) string {
return string(b) return string(b)
} }
// State returns string representation of the state for the VM. // State returns the state for the VM.
func (v *VM) State() string { func (v *VM) State() State {
return v.state.String() return v.state
} }
// Ready returns true if the VM ready to execute the loaded program. // 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. // Run starts the execution of the loaded program.
func (v *VM) Run() error { func (v *VM) Run() error {
if !v.Ready() { if !v.Ready() {
v.state = faultState v.state = FaultState
return errors.New("no program loaded") 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 // VM already ran something and failed, in general its state is
// undefined in this case so we can't run anything. // undefined in this case so we can't run anything.
return errors.New("VM has failed") return errors.New("VM has failed")
} }
// haltState (the default) or breakState are safe to continue. // HaltState (the default) or BreakState are safe to continue.
v.state = noneState v.state = NoneState
for { for {
// check for breakpoint before executing the next instruction // check for breakpoint before executing the next instruction
ctx := v.Context() ctx := v.Context()
if ctx != nil && ctx.atBreakPoint() { if ctx != nil && ctx.atBreakPoint() {
v.state = breakState v.state = BreakState
} }
switch { switch {
case v.state.HasFlag(faultState): case v.state.HasFlag(FaultState):
// Should be caught and reported already by the v.Step(), // Should be caught and reported already by the v.Step(),
// but we're checking here anyway just in case. // but we're checking here anyway just in case.
return errors.New("VM has failed") 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. // Normal exit from this loop.
return nil return nil
case v.state == noneState: case v.state == NoneState:
if err := v.Step(); err != nil { if err := v.Step(); err != nil {
return err return err
} }
default: default:
v.state = faultState v.state = FaultState
return errors.New("unknown state") return errors.New("unknown state")
} }
} }
@ -385,7 +385,7 @@ func (v *VM) Step() error {
ctx := v.Context() ctx := v.Context()
op, param, err := ctx.Next() op, param, err := ctx.Next()
if err != nil { if err != nil {
v.state = faultState v.state = FaultState
return newError(ctx.ip, op, err) return newError(ctx.ip, op, err)
} }
return v.execute(ctx, op, param) return v.execute(ctx, op, param)
@ -397,7 +397,7 @@ func (v *VM) StepInto() error {
ctx := v.Context() ctx := v.Context()
if ctx == nil { if ctx == nil {
v.state = haltState v.state = HaltState
} }
if v.HasStopped() { if v.HasStopped() {
@ -407,7 +407,7 @@ func (v *VM) StepInto() error {
if ctx != nil && ctx.prog != nil { if ctx != nil && ctx.prog != nil {
op, param, err := ctx.Next() op, param, err := ctx.Next()
if err != nil { if err != nil {
v.state = faultState v.state = FaultState
return newError(ctx.ip, op, err) return newError(ctx.ip, op, err)
} }
vErr := v.execute(ctx, op, param) vErr := v.execute(ctx, op, param)
@ -418,7 +418,7 @@ func (v *VM) StepInto() error {
cctx := v.Context() cctx := v.Context()
if cctx != nil && cctx.atBreakPoint() { if cctx != nil && cctx.atBreakPoint() {
v.state = breakState v.state = BreakState
} }
return nil return nil
} }
@ -426,14 +426,14 @@ func (v *VM) StepInto() error {
// StepOut takes the debugger to the line where the current function was called. // StepOut takes the debugger to the line where the current function was called.
func (v *VM) StepOut() error { func (v *VM) StepOut() error {
var err error var err error
if v.state == breakState { if v.state == BreakState {
v.state = noneState v.state = NoneState
} else { } else {
v.state = breakState v.state = BreakState
} }
expSize := v.istack.len expSize := v.istack.len
for v.state == noneState && v.istack.len >= expSize { for v.state == NoneState && v.istack.len >= expSize {
err = v.StepInto() err = v.StepInto()
} }
return err return err
@ -447,22 +447,22 @@ func (v *VM) StepOver() error {
return err return err
} }
if v.state == breakState { if v.state == BreakState {
v.state = noneState v.state = NoneState
} else { } else {
v.state = breakState v.state = BreakState
} }
expSize := v.istack.len expSize := v.istack.len
for { for {
err = v.StepInto() err = v.StepInto()
if !(v.state == noneState && v.istack.len > expSize) { if !(v.state == NoneState && v.istack.len > expSize) {
break break
} }
} }
if v.state == noneState { if v.state == NoneState {
v.state = breakState v.state = BreakState
} }
return err return err
@ -471,22 +471,22 @@ func (v *VM) StepOver() error {
// HasFailed returns whether VM is in the failed state now. Usually used to // HasFailed returns whether VM is in the failed state now. Usually used to
// check status after Run. // check status after Run.
func (v *VM) HasFailed() bool { 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. // HasStopped returns whether VM is in Halt or Failed state.
func (v *VM) HasStopped() bool { 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. // HasHalted returns whether VM is in Halt state.
func (v *VM) HasHalted() bool { func (v *VM) HasHalted() bool {
return v.state.HasFlag(haltState) return v.state.HasFlag(HaltState)
} }
// AtBreakpoint returns whether VM is at breakpoint. // AtBreakpoint returns whether VM is at breakpoint.
func (v *VM) AtBreakpoint() bool { func (v *VM) AtBreakpoint() bool {
return v.state.HasFlag(breakState) return v.state.HasFlag(BreakState)
} }
// GetInteropID converts instruction parameter to an interop ID. // 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. // each panic at a central point, putting the VM in a fault state and setting error.
defer func() { defer func() {
if errRecover := recover(); errRecover != nil { if errRecover := recover(); errRecover != nil {
v.state = faultState v.state = FaultState
err = newError(ctx.ip, op, errRecover) err = newError(ctx.ip, op, errRecover)
} else if v.refs.size > MaxStackSize { } else if v.refs.size > MaxStackSize {
v.state = faultState v.state = FaultState
err = newError(ctx.ip, op, "stack is too big") 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) v.unloadContext(oldCtx)
if v.istack.Len() == 0 { if v.istack.Len() == 0 {
v.state = haltState v.state = HaltState
break break
} }