rpc: use raw stack items in invoke*
RPC
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
7b4acd5a7f
commit
9cba25616d
6 changed files with 103 additions and 80 deletions
|
@ -6,12 +6,12 @@ import (
|
||||||
|
|
||||||
"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/encoding/address"
|
"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/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/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/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"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)
|
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
|
index := len(st) - 1 // top stack element is last in the array
|
||||||
var decimals int64
|
bi, err := st[index].TryInteger()
|
||||||
switch typ := st[index].Type; typ {
|
if err != nil {
|
||||||
case smartcontract.IntegerType:
|
return 0, err
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
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
|
index := len(st) - 1 // top stack element is last in the array
|
||||||
var s string
|
bs, err := st[index].TryBytes()
|
||||||
switch typ := st[index].Type; typ {
|
if err != nil {
|
||||||
case smartcontract.StringType:
|
return "", err
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
return s, nil
|
return string(bs), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"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/stackitem"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,27 +52,26 @@ func (c *Client) GetBlockedAccounts() (native.BlockedAccounts, error) {
|
||||||
return topBlockedAccountsFromStack(result.Stack)
|
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
|
index := len(st) - 1 // top stack element is last in the array
|
||||||
var (
|
var (
|
||||||
ba native.BlockedAccounts
|
ba native.BlockedAccounts
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
switch typ := st[index].Type; typ {
|
items, ok := st[index].Value().([]stackitem.Item)
|
||||||
case smartcontract.ArrayType:
|
if !ok {
|
||||||
data, ok := st[index].Value.([]smartcontract.Parameter)
|
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 {
|
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))
|
ba[i], err = util.Uint160DecodeBytesLE(val)
|
||||||
for i, account := range data {
|
if err != nil {
|
||||||
ba[i], err = util.Uint160DecodeBytesLE(account.Value.([]byte))
|
return nil, err
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid stack item type: %s", typ)
|
|
||||||
}
|
}
|
||||||
return ba, nil
|
return ba, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"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/util"
|
"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/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -648,12 +649,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
State: "HALT",
|
State: "HALT",
|
||||||
GasConsumed: 31100000,
|
GasConsumed: 31100000,
|
||||||
Script: "1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf",
|
Script: "1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf",
|
||||||
Stack: []smartcontract.Parameter{
|
Stack: []stackitem.Item{stackitem.NewByteArray(bytes)},
|
||||||
{
|
|
||||||
Type: smartcontract.ByteArrayType,
|
|
||||||
Value: bytes,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -680,12 +676,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
State: "HALT",
|
State: "HALT",
|
||||||
GasConsumed: 16100000,
|
GasConsumed: 16100000,
|
||||||
Script: "00046e616d656724058e5e1b6008847cd662728549088a9ee82191",
|
Script: "00046e616d656724058e5e1b6008847cd662728549088a9ee82191",
|
||||||
Stack: []smartcontract.Parameter{
|
Stack: []stackitem.Item{stackitem.NewByteArray(bytes)},
|
||||||
{
|
|
||||||
Type: smartcontract.ByteArrayType,
|
|
||||||
Value: bytes,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,76 @@
|
||||||
package result
|
package result
|
||||||
|
|
||||||
import (
|
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
|
// Invoke represents code invocation result and is used by several RPC calls
|
||||||
// that invoke functions, scripts and generic bytecode.
|
// that invoke functions, scripts and generic bytecode.
|
||||||
type Invoke struct {
|
type Invoke struct {
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
GasConsumed int64 `json:"gasconsumed,string"`
|
GasConsumed int64 `json:"gasconsumed,string"`
|
||||||
Script string `json:"script"`
|
Script string `json:"script"`
|
||||||
Stack []smartcontract.Parameter `json:"stack"`
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"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/crypto/keys"
|
"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/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/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network"
|
"github.com/nspcc-dev/neo-go/pkg/network"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc"
|
"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}
|
d := decimals{Hash: h}
|
||||||
switch item := res.Stack[len(res.Stack)-1]; item.Type {
|
bi, err := res.Stack[len(res.Stack)-1].TryInteger()
|
||||||
case smartcontract.IntegerType:
|
if err != nil {
|
||||||
d.Value = item.Value.(int64)
|
return decimals{}, err
|
||||||
case smartcontract.ByteArrayType:
|
|
||||||
d.Value = bigint.FromBytes(item.Value.([]byte)).Int64()
|
|
||||||
default:
|
|
||||||
return d, errors.New("invalid result: not an integer")
|
|
||||||
}
|
}
|
||||||
|
d.Value = bi.Int64()
|
||||||
if d.Value < 0 {
|
if d.Value < 0 {
|
||||||
return d, errors.New("incorrect result: negative result")
|
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(),
|
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().ToArray(),
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -531,7 +531,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "HALT", res.State)
|
assert.Equal(t, "HALT", res.State)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
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)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "HALT", res.State)
|
assert.Equal(t, "HALT", res.State)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
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)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "HALT", res.State)
|
assert.Equal(t, "HALT", res.State)
|
||||||
require.Equal(t, 1, len(res.Stack))
|
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)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "HALT", res.State)
|
assert.Equal(t, "HALT", res.State)
|
||||||
assert.Equal(t, 1, len(res.Stack))
|
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())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue