rpc: use raw stack items in invoke* RPC

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2020-07-31 15:26:28 +03:00
parent 7b4acd5a7f
commit 9cba25616d
6 changed files with 103 additions and 80 deletions

View file

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

View file

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

View file

@ -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)},
}
},
},

View file

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

View file

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

View file

@ -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())
},
},
{