Merge pull request #1236 from nspcc-dev/rpc/getrawmempool
rpc: update `getrawmempool` and `getrawtransaction` RPC-calls
This commit is contained in:
commit
b187dfe3ce
14 changed files with 120 additions and 86 deletions
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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{},
|
||||||
|
|
|
@ -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,
|
||||||
|
|
10
pkg/rpc/response/result/raw_mempool.go
Normal file
10
pkg/rpc/response/result/raw_mempool.go
Normal 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"`
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
70
pkg/vm/vm.go
70
pkg/vm/vm.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue