make State a set as in reference C# implementation (#123)
* make State a set as in reference C# implementation * fix issues
This commit is contained in:
parent
001a0e601e
commit
57cb289bcd
4 changed files with 161 additions and 20 deletions
|
@ -1,6 +1,9 @@
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import "github.com/CityOfZion/neo-go/pkg/core/transaction"
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
|
)
|
||||||
|
|
||||||
type InvokeScriptResponse struct {
|
type InvokeScriptResponse struct {
|
||||||
responseHeader
|
responseHeader
|
||||||
|
@ -11,9 +14,9 @@ type InvokeScriptResponse struct {
|
||||||
// InvokeResult represents the outcome of a script that is
|
// InvokeResult represents the outcome of a script that is
|
||||||
// executed by the NEO VM.
|
// executed by the NEO VM.
|
||||||
type InvokeResult struct {
|
type InvokeResult struct {
|
||||||
State string `json:"state"`
|
State vm.State `json:"state"`
|
||||||
GasConsumed string `json:"gas_consumed"`
|
GasConsumed string `json:"gas_consumed"`
|
||||||
Script string `json:"script"`
|
Script string `json:"script"`
|
||||||
Stack []StackParam
|
Stack []StackParam
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,75 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
// State of the VM.
|
// State of the VM.
|
||||||
type State uint
|
type State uint8
|
||||||
|
|
||||||
// Available States.
|
// Available States.
|
||||||
const (
|
const (
|
||||||
noneState State = iota
|
noneState State = 0
|
||||||
haltState
|
haltState State = 1 << iota
|
||||||
faultState
|
faultState
|
||||||
breakState
|
breakState
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (s State) HasFlag(f State) bool {
|
||||||
|
return s&f != 0
|
||||||
|
}
|
||||||
|
|
||||||
func (s State) String() string {
|
func (s State) String() string {
|
||||||
switch s {
|
if s == noneState {
|
||||||
case haltState:
|
|
||||||
return "HALT"
|
|
||||||
case faultState:
|
|
||||||
return "FAULT"
|
|
||||||
case breakState:
|
|
||||||
return "BREAK"
|
|
||||||
default:
|
|
||||||
return "NONE"
|
return "NONE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss := make([]string, 0, 3)
|
||||||
|
if s.HasFlag(haltState) {
|
||||||
|
ss = append(ss, "HALT")
|
||||||
|
}
|
||||||
|
if s.HasFlag(faultState) {
|
||||||
|
ss = append(ss, "FAULT")
|
||||||
|
}
|
||||||
|
if s.HasFlag(breakState) {
|
||||||
|
ss = append(ss, "BREAK")
|
||||||
|
}
|
||||||
|
return strings.Join(ss, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func StateFromString(s string) (st State, err error) {
|
||||||
|
if s = strings.TrimSpace(s); s == "NONE" {
|
||||||
|
return noneState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ss := strings.Split(s, ",")
|
||||||
|
for _, state := range ss {
|
||||||
|
switch state = strings.TrimSpace(state); state {
|
||||||
|
case "HALT":
|
||||||
|
st |= haltState
|
||||||
|
case "FAULT":
|
||||||
|
st |= faultState
|
||||||
|
case "BREAK":
|
||||||
|
st |= breakState
|
||||||
|
default:
|
||||||
|
return 0, errors.New("unknown state")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) MarshalJSON() (data []byte, err error) {
|
||||||
|
return []byte(`"` + s.String() + `"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
l := len(data)
|
||||||
|
if l < 2 || data[0] != '"' || data[l-1] != '"' {
|
||||||
|
return errors.New("wrong format")
|
||||||
|
}
|
||||||
|
|
||||||
|
*s, err = StateFromString(string(data[1 : l-1]))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
88
pkg/vm/state_test.go
Normal file
88
pkg/vm/state_test.go
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStateFromString(t *testing.T) {
|
||||||
|
var (
|
||||||
|
s State
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
s, err = StateFromString("HALT")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, haltState, s)
|
||||||
|
|
||||||
|
s, err = StateFromString("BREAK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, breakState, s)
|
||||||
|
|
||||||
|
s, err = StateFromString("FAULT")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, faultState, s)
|
||||||
|
|
||||||
|
s, err = StateFromString("NONE")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, noneState, s)
|
||||||
|
|
||||||
|
s, err = StateFromString("HALT, BREAK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, haltState|breakState, s)
|
||||||
|
|
||||||
|
s, err = StateFromString("FAULT, BREAK")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, faultState|breakState, s)
|
||||||
|
|
||||||
|
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.False(t, haltState.HasFlag(breakState))
|
||||||
|
assert.False(t, noneState.HasFlag(haltState))
|
||||||
|
assert.False(t, (faultState | breakState).HasFlag(haltState))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestState_MarshalJSON(t *testing.T) {
|
||||||
|
var (
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
data, err = json.Marshal(haltState | breakState)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, data, []byte(`"HALT, BREAK"`))
|
||||||
|
|
||||||
|
data, err = json.Marshal(faultState)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, data, []byte(`"FAULT"`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestState_UnmarshalJSON(t *testing.T) {
|
||||||
|
var (
|
||||||
|
s State
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(`"HALT, BREAK"`), &s)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, haltState|breakState, s)
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(`"FAULT, BREAK"`), &s)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, faultState|breakState, s)
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(`"NONE"`), &s)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, noneState, s)
|
||||||
|
}
|
10
pkg/vm/vm.go
10
pkg/vm/vm.go
|
@ -194,21 +194,21 @@ func (v *VM) Run() {
|
||||||
|
|
||||||
v.state = noneState
|
v.state = noneState
|
||||||
for {
|
for {
|
||||||
switch v.state {
|
switch {
|
||||||
case haltState:
|
case v.state.HasFlag(haltState):
|
||||||
if !v.mute {
|
if !v.mute {
|
||||||
fmt.Println(v.Stack("estack"))
|
fmt.Println(v.Stack("estack"))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case breakState:
|
case v.state.HasFlag(breakState):
|
||||||
ctx := v.Context()
|
ctx := v.Context()
|
||||||
i, op := ctx.CurrInstr()
|
i, op := ctx.CurrInstr()
|
||||||
fmt.Printf("at breakpoint %d (%s)\n", i, op.String())
|
fmt.Printf("at breakpoint %d (%s)\n", i, op.String())
|
||||||
return
|
return
|
||||||
case faultState:
|
case v.state.HasFlag(faultState):
|
||||||
fmt.Println("FAULT")
|
fmt.Println("FAULT")
|
||||||
return
|
return
|
||||||
case noneState:
|
case v.state == noneState:
|
||||||
v.Step()
|
v.Step()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue