From 9cba25616defa24f51443f62cea454773f0b8380 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 31 Jul 2020 15:26:28 +0300 Subject: [PATCH] rpc: use raw stack items in `invoke*` RPC Signed-off-by: Evgenii Stratonikov --- pkg/rpc/client/nep5.go | 48 +++++---------------- pkg/rpc/client/policy.go | 26 +++++------ pkg/rpc/client/rpc_test.go | 15 ++----- pkg/rpc/response/result/invoke.go | 72 ++++++++++++++++++++++++++++--- pkg/rpc/server/server.go | 14 +++--- pkg/rpc/server/server_test.go | 8 ++-- 6 files changed, 103 insertions(+), 80 deletions(-) diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 150b44d4f..5f1618d9e 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -6,12 +6,12 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" - "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "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/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/wallet" ) @@ -192,46 +192,20 @@ func (c *Client) MultiTransferNEP5(acc *wallet.Account, token util.Uint160, gas return c.SendRawTransaction(tx) } -func topIntFromStack(st []smartcontract.Parameter) (int64, error) { +func topIntFromStack(st []stackitem.Item) (int64, error) { index := len(st) - 1 // top stack element is last in the array - var decimals int64 - switch typ := st[index].Type; typ { - case smartcontract.IntegerType: - var ok bool - decimals, ok = st[index].Value.(int64) - if !ok { - return 0, errors.New("invalid Integer item") - } - case smartcontract.ByteArrayType: - data, ok := st[index].Value.([]byte) - if !ok { - return 0, errors.New("invalid ByteArray item") - } - decimals = bigint.FromBytes(data).Int64() - default: - return 0, fmt.Errorf("invalid stack item type: %s", typ) + bi, err := st[index].TryInteger() + if err != nil { + return 0, err } - return decimals, nil + return bi.Int64(), nil } -func topStringFromStack(st []smartcontract.Parameter) (string, error) { +func topStringFromStack(st []stackitem.Item) (string, error) { index := len(st) - 1 // top stack element is last in the array - var s string - switch typ := st[index].Type; typ { - case smartcontract.StringType: - var ok bool - s, ok = st[index].Value.(string) - if !ok { - return "", errors.New("invalid String item") - } - case smartcontract.ByteArrayType: - data, ok := st[index].Value.([]byte) - if !ok { - return "", errors.New("invalid ByteArray item") - } - s = string(data) - default: - return "", fmt.Errorf("invalid stack item type: %s", typ) + bs, err := st[index].TryBytes() + if err != nil { + return "", err } - return s, nil + return string(bs), nil } diff --git a/pkg/rpc/client/policy.go b/pkg/rpc/client/policy.go index 4f9b15103..558491df5 100644 --- a/pkg/rpc/client/policy.go +++ b/pkg/rpc/client/policy.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/pkg/errors" ) @@ -51,27 +52,26 @@ func (c *Client) GetBlockedAccounts() (native.BlockedAccounts, error) { return topBlockedAccountsFromStack(result.Stack) } -func topBlockedAccountsFromStack(st []smartcontract.Parameter) (native.BlockedAccounts, error) { +func topBlockedAccountsFromStack(st []stackitem.Item) (native.BlockedAccounts, error) { index := len(st) - 1 // top stack element is last in the array var ( ba native.BlockedAccounts err error ) - switch typ := st[index].Type; typ { - case smartcontract.ArrayType: - data, ok := st[index].Value.([]smartcontract.Parameter) + items, ok := st[index].Value().([]stackitem.Item) + if !ok { + return nil, fmt.Errorf("invalid stack item type: %s", st[index].Type()) + } + ba = make(native.BlockedAccounts, len(items)) + for i, account := range items { + val, ok := account.Value().([]byte) if !ok { - return nil, errors.New("invalid Array item") + return nil, fmt.Errorf("invalid array element: %s", account.Type()) } - ba = make(native.BlockedAccounts, len(data)) - for i, account := range data { - ba[i], err = util.Uint160DecodeBytesLE(account.Value.([]byte)) - if err != nil { - return nil, err - } + ba[i], err = util.Uint160DecodeBytesLE(val) + if err != nil { + return nil, err } - default: - return nil, fmt.Errorf("invalid stack item type: %s", typ) } return ba, nil } diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 00e17031f..07b683fd7 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -27,6 +27,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -648,12 +649,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ State: "HALT", GasConsumed: 31100000, Script: "1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf", - Stack: []smartcontract.Parameter{ - { - Type: smartcontract.ByteArrayType, - Value: bytes, - }, - }, + Stack: []stackitem.Item{stackitem.NewByteArray(bytes)}, } }, }, @@ -680,12 +676,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ State: "HALT", GasConsumed: 16100000, Script: "00046e616d656724058e5e1b6008847cd662728549088a9ee82191", - Stack: []smartcontract.Parameter{ - { - Type: smartcontract.ByteArrayType, - Value: bytes, - }, - }, + Stack: []stackitem.Item{stackitem.NewByteArray(bytes)}, } }, }, diff --git a/pkg/rpc/response/result/invoke.go b/pkg/rpc/response/result/invoke.go index 16c877275..bab39a104 100644 --- a/pkg/rpc/response/result/invoke.go +++ b/pkg/rpc/response/result/invoke.go @@ -1,14 +1,76 @@ package result import ( - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "encoding/json" + + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // Invoke represents code invocation result and is used by several RPC calls // that invoke functions, scripts and generic bytecode. type Invoke struct { - State string `json:"state"` - GasConsumed int64 `json:"gasconsumed,string"` - Script string `json:"script"` - Stack []smartcontract.Parameter `json:"stack"` + State string `json:"state"` + GasConsumed int64 `json:"gasconsumed,string"` + Script string `json:"script"` + Stack []stackitem.Item `json:"stack"` +} + +type invokeAux struct { + State string `json:"state"` + GasConsumed int64 `json:"gasconsumed,string"` + Script string `json:"script"` + Stack json.RawMessage `json:"stack"` +} + +// MarshalJSON implements json.Marshaler. +func (r Invoke) MarshalJSON() ([]byte, error) { + var st json.RawMessage + arr := make([]json.RawMessage, len(r.Stack)) + for i := range arr { + data, err := stackitem.ToJSONWithTypes(r.Stack[i]) + if err != nil { + st = []byte("error: recursive reference") + break + } + arr[i] = data + } + + var err error + if st == nil { + st, err = json.Marshal(arr) + if err != nil { + return nil, err + } + } + return json.Marshal(&invokeAux{ + GasConsumed: r.GasConsumed, + Script: r.Script, + State: r.State, + Stack: st, + }) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (r *Invoke) UnmarshalJSON(data []byte) error { + aux := new(invokeAux) + if err := json.Unmarshal(data, aux); err != nil { + return err + } + var arr []json.RawMessage + if err := json.Unmarshal(aux.Stack, &arr); err == nil { + st := make([]stackitem.Item, len(arr)) + for i := range arr { + st[i], err = stackitem.FromJSONWithTypes(arr[i]) + if err != nil { + break + } + } + if err == nil { + r.Stack = st + } + } + r.GasConsumed = aux.GasConsumed + r.Script = aux.Script + r.State = aux.State + return nil } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 5e661ac71..47873fdc7 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -22,7 +22,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" - "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/network" "github.com/nspcc-dev/neo-go/pkg/rpc" @@ -623,14 +622,11 @@ func (s *Server) getDecimals(contractID int32, cache map[int32]decimals) (decima } d := decimals{Hash: h} - switch item := res.Stack[len(res.Stack)-1]; item.Type { - case smartcontract.IntegerType: - d.Value = item.Value.(int64) - case smartcontract.ByteArrayType: - d.Value = bigint.FromBytes(item.Value.([]byte)).Int64() - default: - return d, errors.New("invalid result: not an integer") + bi, err := res.Stack[len(res.Stack)-1].TryInteger() + if err != nil { + return decimals{}, err } + d.Value = bi.Int64() if d.Value < 0 { return d, errors.New("incorrect result: negative result") } @@ -896,7 +892,7 @@ func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *resu State: vm.State().String(), GasConsumed: vm.GasConsumed(), Script: hex.EncodeToString(script), - Stack: vm.Estack().ToContractParameters(), + Stack: vm.Estack().ToArray(), } return result } diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index a9a0d37bc..acc15d1f4 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -531,7 +531,7 @@ var rpcTestCases = map[string][]rpcTestCase{ require.True(t, ok) assert.Equal(t, "HALT", res.State) require.Equal(t, 1, len(res.Stack)) - require.Equal(t, int64(3), res.Stack[0].Value) + require.Equal(t, big.NewInt(3), res.Stack[0].Value()) }, }, { @@ -543,7 +543,7 @@ var rpcTestCases = map[string][]rpcTestCase{ require.True(t, ok) assert.Equal(t, "HALT", res.State) require.Equal(t, 1, len(res.Stack)) - require.Equal(t, int64(2), res.Stack[0].Value) + require.Equal(t, big.NewInt(2), res.Stack[0].Value()) }, }, { @@ -555,7 +555,7 @@ var rpcTestCases = map[string][]rpcTestCase{ require.True(t, ok) assert.Equal(t, "HALT", res.State) require.Equal(t, 1, len(res.Stack)) - require.Equal(t, int64(1), res.Stack[0].Value) + require.Equal(t, big.NewInt(1), res.Stack[0].Value()) }, }, { @@ -567,7 +567,7 @@ var rpcTestCases = map[string][]rpcTestCase{ require.True(t, ok) assert.Equal(t, "HALT", res.State) assert.Equal(t, 1, len(res.Stack)) - assert.Equal(t, int64(1), res.Stack[0].Value) + assert.Equal(t, big.NewInt(1), res.Stack[0].Value()) }, }, {