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/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: return bi.Int64(), nil
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
} }
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: return string(bs), nil
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
} }

View file

@ -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:
data, ok := st[index].Value.([]smartcontract.Parameter)
if !ok { if !ok {
return nil, errors.New("invalid Array item") return nil, fmt.Errorf("invalid stack item type: %s", st[index].Type())
} }
ba = make(native.BlockedAccounts, len(data)) ba = make(native.BlockedAccounts, len(items))
for i, account := range data { for i, account := range items {
ba[i], err = util.Uint160DecodeBytesLE(account.Value.([]byte)) val, ok := account.Value().([]byte)
if !ok {
return nil, fmt.Errorf("invalid array element: %s", account.Type())
}
ba[i], err = util.Uint160DecodeBytesLE(val)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
default:
return nil, fmt.Errorf("invalid stack item type: %s", typ)
}
return ba, nil 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/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,
},
},
} }
}, },
}, },

View file

@ -1,7 +1,9 @@
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
@ -10,5 +12,65 @@ 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
} }

View file

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

View file

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