Merge pull request #1047 from nspcc-dev/neo3/rpc/invoke
rpc: update invoke* RPC-calls
This commit is contained in:
commit
fe31c7ed2d
15 changed files with 464 additions and 187 deletions
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/go-yaml/yaml"
|
||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||
|
@ -68,6 +69,9 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
|||
func Main(op string, args []interface{}) {
|
||||
runtime.Notify("Hello world!")
|
||||
}`
|
||||
// cosignersSeparator is a special value which is used to distinguish
|
||||
// parameters and cosigners for invoke* commands
|
||||
cosignersSeparator = "--"
|
||||
)
|
||||
|
||||
// NewCommands returns 'contract' command.
|
||||
|
@ -132,31 +136,14 @@ func NewCommands() []cli.Command {
|
|||
gasFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "invoke",
|
||||
Usage: "invoke deployed contract on the blockchain",
|
||||
UsageText: "neo-go contract invoke -e endpoint -w wallet [-a address] [-g gas] scripthash [arguments...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given arguments.
|
||||
See testinvoke documentation for the details about parameters. It differs
|
||||
from testinvoke in that this command sends an invocation transaction to
|
||||
the network.
|
||||
`,
|
||||
Action: invoke,
|
||||
Flags: []cli.Flag{
|
||||
endpointFlag,
|
||||
walletFlag,
|
||||
addressFlag,
|
||||
gasFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "invokefunction",
|
||||
Usage: "invoke deployed contract on the blockchain",
|
||||
UsageText: "neo-go contract invokefunction -e endpoint -w wallet [-a address] [-g gas] scripthash [method] [arguments...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given method and
|
||||
and arguments. See testinvokefunction documentation for the details about
|
||||
parameters. It differs from testinvokefunction in that this command sends an
|
||||
invocation transaction to the network.
|
||||
UsageText: "neo-go contract invokefunction -e endpoint -w wallet [-a address] [-g gas] scripthash [method] [arguments...] [--] [cosigners...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given method,
|
||||
arguments and cosigners. See testinvokefunction documentation for the details
|
||||
about parameters. It differs from testinvokefunction in that this command
|
||||
sends an invocation transaction to the network.
|
||||
`,
|
||||
Action: invokeFunction,
|
||||
Flags: []cli.Flag{
|
||||
|
@ -166,36 +153,17 @@ func NewCommands() []cli.Command {
|
|||
gasFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testinvoke",
|
||||
Usage: "invoke deployed contract on the blockchain (test mode)",
|
||||
UsageText: "neo-go contract testinvoke -e endpoint scripthash [arguments...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given arguments.
|
||||
It's very similar to the tesinvokefunction command, but differs in the way
|
||||
arguments are being passed. This invoker does not accept method parameter
|
||||
and it passes all given parameters as plain values to the contract, not
|
||||
wrapping them them into array like testinvokefunction does. For arguments
|
||||
syntax please refer to the testinvokefunction command help.
|
||||
|
||||
Most of the time (if your contract follows the standard convention of
|
||||
method with array of values parameters) you want to use testinvokefunction
|
||||
command instead of testinvoke.
|
||||
`,
|
||||
Action: testInvoke,
|
||||
Flags: []cli.Flag{
|
||||
endpointFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testinvokefunction",
|
||||
Usage: "invoke deployed contract on the blockchain (test mode)",
|
||||
UsageText: "neo-go contract testinvokefunction -e endpoint scripthash [method] [arguments...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given method and
|
||||
arguments. If no method is given "" is passed to the script, if no arguments
|
||||
are given, an empty array is passed. All of the given arguments are
|
||||
encapsulated into array before invoking the script. The script thus should
|
||||
follow the regular convention of smart contract arguments (method string and
|
||||
an array of other arguments).
|
||||
UsageText: "neo-go contract testinvokefunction -e endpoint scripthash [method] [arguments...] [--] [cosigners...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given method,
|
||||
arguments and cosigners. If no method is given "" is passed to the script, if
|
||||
no arguments are given, an empty array is passed, if no cosigners are given,
|
||||
no array will be passed. All of the given arguments are encapsulated into
|
||||
array before invoking the script. The script thus should follow the regular
|
||||
convention of smart contract arguments (method string and an array of other
|
||||
arguments).
|
||||
|
||||
Arguments always do have regular Neo smart contract parameter types, either
|
||||
specified explicitly or being inferred from the value. To specify the type
|
||||
|
@ -251,6 +219,32 @@ func NewCommands() []cli.Command {
|
|||
* 'string\:string' is a string with a value of 'string:string'
|
||||
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
|
||||
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
|
||||
|
||||
Cosigners represent a set of Uint160 hashes with witness scopes and are used
|
||||
to verify hashes in System.Runtime.CheckWitness syscall. To specify cosigners
|
||||
use cosigner[:scope] syntax where
|
||||
* 'cosigner' is hex-encoded 160 bit (20 byte) LE value of cosigner's address,
|
||||
which could have '0x' prefix.
|
||||
* 'scope' is a comma-separated set of cosigner's scopes, which could be:
|
||||
- 'Global' - allows this witness in all contexts. This cannot be combined
|
||||
with other flags.
|
||||
- 'CalledByEntry' - means that this condition must hold: EntryScriptHash
|
||||
== CallingScriptHash. The witness/permission/signature
|
||||
given on first invocation will automatically expire if
|
||||
entering deeper internal invokes. This can be default
|
||||
safe choice for native NEO/GAS.
|
||||
- 'CustomContracts' - define valid custom contract hashes for witness check.
|
||||
- 'CustomGroups' - define custom pubkey for group members.
|
||||
|
||||
If no scopes were specified, 'Global' used as default. If no cosigners were
|
||||
specified, no array will be passed. Note that scopes are properly handled by
|
||||
neo-go RPC server only. C# implementation does not support scopes capability.
|
||||
|
||||
Examples:
|
||||
* '0000000009070e030d0f0e020d0c06050e030c02'
|
||||
* '0x0000000009070e030d0f0e020d0c06050e030c02'
|
||||
* '0x0000000009070e030d0f0e020d0c06050e030c02:Global'
|
||||
* '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,CustomGroups'
|
||||
`,
|
||||
Action: testInvokeFunction,
|
||||
Flags: []cli.Flag{
|
||||
|
@ -258,8 +252,12 @@ func NewCommands() []cli.Command {
|
|||
},
|
||||
},
|
||||
{
|
||||
Name: "testinvokescript",
|
||||
Usage: "Invoke compiled AVM code on the blockchain (test mode, not creating a transaction for it)",
|
||||
Name: "testinvokescript",
|
||||
Usage: "Invoke compiled AVM code on the blockchain (test mode, not creating a transaction for it)",
|
||||
UsageText: "neo-go contract testinvokescript -e endpoint -i input.avm [cosigners...]",
|
||||
Description: `Executes given compiled AVM instructions with the given set of
|
||||
cosigners. See testinvokefunction documentation for the details about parameters.
|
||||
`,
|
||||
Action: testInvokeScript,
|
||||
Flags: []cli.Flag{
|
||||
endpointFlag,
|
||||
|
@ -388,31 +386,25 @@ func contractCompile(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func testInvoke(ctx *cli.Context) error {
|
||||
return invokeInternal(ctx, false, false)
|
||||
}
|
||||
|
||||
func testInvokeFunction(ctx *cli.Context) error {
|
||||
return invokeInternal(ctx, true, false)
|
||||
}
|
||||
|
||||
func invoke(ctx *cli.Context) error {
|
||||
return invokeInternal(ctx, false, true)
|
||||
return invokeInternal(ctx, false)
|
||||
}
|
||||
|
||||
func invokeFunction(ctx *cli.Context) error {
|
||||
return invokeInternal(ctx, true, true)
|
||||
return invokeInternal(ctx, true)
|
||||
}
|
||||
|
||||
func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
||||
func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
||||
var (
|
||||
err error
|
||||
gas util.Fixed8
|
||||
operation string
|
||||
params = make([]smartcontract.Parameter, 0)
|
||||
paramsStart = 1
|
||||
resp *result.Invoke
|
||||
acc *wallet.Account
|
||||
err error
|
||||
gas util.Fixed8
|
||||
operation string
|
||||
params = make([]smartcontract.Parameter, 0)
|
||||
paramsStart = 1
|
||||
cosigners []transaction.Cosigner
|
||||
cosignersStart = 0
|
||||
resp *result.Invoke
|
||||
acc *wallet.Account
|
||||
)
|
||||
|
||||
endpoint := ctx.String("endpoint")
|
||||
|
@ -425,15 +417,19 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
|||
return cli.NewExitError(errNoScriptHash, 1)
|
||||
}
|
||||
script := args[0]
|
||||
if withMethod {
|
||||
if len(args) <= 1 {
|
||||
return cli.NewExitError(errNoMethod, 1)
|
||||
}
|
||||
operation = args[1]
|
||||
paramsStart++
|
||||
|
||||
if len(args) <= 1 {
|
||||
return cli.NewExitError(errNoMethod, 1)
|
||||
}
|
||||
operation = args[1]
|
||||
paramsStart++
|
||||
|
||||
if len(args) > paramsStart {
|
||||
for k, s := range args[paramsStart:] {
|
||||
if s == cosignersSeparator {
|
||||
cosignersStart = paramsStart + k + 1
|
||||
break
|
||||
}
|
||||
param, err := smartcontract.NewParameterFromString(s)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to parse argument #%d: %v", k+paramsStart+1, err), 1)
|
||||
|
@ -442,6 +438,16 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
if len(args) >= cosignersStart && cosignersStart > 0 {
|
||||
for i, c := range args[cosignersStart:] {
|
||||
cosigner, err := parseCosigner(c)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to parse cosigner #%d: %v", i+cosignersStart+1, err), 1)
|
||||
}
|
||||
cosigners = append(cosigners, cosigner)
|
||||
}
|
||||
}
|
||||
|
||||
if signAndPush {
|
||||
gas = flags.Fixed8FromContext(ctx, "gas")
|
||||
acc, err = getAccFromContext(ctx)
|
||||
|
@ -454,11 +460,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
|||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
if withMethod {
|
||||
resp, err = c.InvokeFunction(script, operation, params)
|
||||
} else {
|
||||
resp, err = c.Invoke(script, params)
|
||||
}
|
||||
resp, err = c.InvokeFunction(script, operation, params, cosigners)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -502,13 +504,25 @@ func testInvokeScript(ctx *cli.Context) error {
|
|||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
args := ctx.Args()
|
||||
var cosigners []transaction.Cosigner
|
||||
if args.Present() {
|
||||
for i, c := range args[:] {
|
||||
cosigner, err := parseCosigner(c)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to parse cosigner #%d: %v", i+1, err), 1)
|
||||
}
|
||||
cosigners = append(cosigners, cosigner)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := client.New(context.TODO(), endpoint, client.Options{})
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
scriptHex := hex.EncodeToString(b)
|
||||
resp, err := c.InvokeScript(scriptHex)
|
||||
resp, err := c.InvokeScript(scriptHex, cosigners)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -677,3 +691,26 @@ func parseContractConfig(confFile string) (ProjectConfig, error) {
|
|||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func parseCosigner(c string) (transaction.Cosigner, error) {
|
||||
var (
|
||||
err error
|
||||
res = transaction.Cosigner{}
|
||||
)
|
||||
data := strings.SplitN(strings.ToLower(c), ":", 2)
|
||||
s := data[0]
|
||||
if len(s) == 2*util.Uint160Size+2 && s[0:2] == "0x" {
|
||||
s = s[2:]
|
||||
}
|
||||
res.Account, err = util.Uint160DecodeStringLE(s)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if len(data) > 1 {
|
||||
res.Scopes, err = transaction.ScopesFromString(data[1])
|
||||
if err != nil {
|
||||
return transaction.Cosigner{}, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -1246,8 +1246,8 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
|
|||
}
|
||||
|
||||
// GetTestVM returns a VM and a Store setup for a test run of some sort of code.
|
||||
func (bc *Blockchain) GetTestVM() *vm.VM {
|
||||
systemInterop := bc.newInteropContext(trigger.Application, bc.dao, nil, nil)
|
||||
func (bc *Blockchain) GetTestVM(tx *transaction.Transaction) *vm.VM {
|
||||
systemInterop := bc.newInteropContext(trigger.Application, bc.dao, nil, tx)
|
||||
vm := SpawnVM(systemInterop)
|
||||
vm.SetPriceGetter(getPrice)
|
||||
return vm
|
||||
|
|
|
@ -41,7 +41,7 @@ type Blockchainer interface {
|
|||
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
|
||||
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
|
||||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||
GetTestVM() *vm.VM
|
||||
GetTestVM(tx *transaction.Transaction) *vm.VM
|
||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
mempool.Feer // fee interface
|
||||
PoolTx(*transaction.Transaction) error
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WitnessScope represents set of witness flags for Transaction cosigner.
|
||||
type WitnessScope byte
|
||||
|
||||
|
@ -17,3 +22,34 @@ const (
|
|||
// CustomGroups define custom pubkey for group members.
|
||||
CustomGroups WitnessScope = 0x20
|
||||
)
|
||||
|
||||
// ScopesFromString converts string of comma-separated scopes to a set of scopes
|
||||
// (case doesn't matter). String can combine several scopes, e.g. be any of:
|
||||
// 'Global', 'CalledByEntry,CustomGroups' etc. In case of an empty string an
|
||||
// error will be returned.
|
||||
func ScopesFromString(s string) (WitnessScope, error) {
|
||||
var result WitnessScope
|
||||
s = strings.ToLower(s)
|
||||
scopes := strings.Split(s, ",")
|
||||
dict := map[string]WitnessScope{
|
||||
"global": Global,
|
||||
"calledbyentry": CalledByEntry,
|
||||
"customcontracts": CustomContracts,
|
||||
"customgroups": CustomGroups,
|
||||
}
|
||||
var isGlobal bool
|
||||
for _, scopeStr := range scopes {
|
||||
scope, ok := dict[scopeStr]
|
||||
if !ok {
|
||||
return result, fmt.Errorf("invalid witness scope: %v", scopeStr)
|
||||
}
|
||||
if isGlobal && !(scope == Global) {
|
||||
return result, fmt.Errorf("Global scope can not be combined with other scopes")
|
||||
}
|
||||
result |= scope
|
||||
if scope == Global {
|
||||
isGlobal = true
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
45
pkg/core/transaction/witness_scope_test.go
Normal file
45
pkg/core/transaction/witness_scope_test.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestScopesFromString(t *testing.T) {
|
||||
s, err := ScopesFromString("")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = ScopesFromString("123")
|
||||
require.Error(t, err)
|
||||
|
||||
s, err = ScopesFromString("Global")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, Global, s)
|
||||
|
||||
s, err = ScopesFromString("CalledByEntry")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, CalledByEntry, s)
|
||||
|
||||
s, err = ScopesFromString("CustomContracts")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, CustomContracts, s)
|
||||
|
||||
s, err = ScopesFromString("CustomGroups")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, CustomGroups, s)
|
||||
|
||||
s, err = ScopesFromString("Calledbyentry,customgroups")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, CalledByEntry|CustomGroups, s)
|
||||
|
||||
_, err = ScopesFromString("global,customgroups")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = ScopesFromString("calledbyentry,global,customgroups")
|
||||
require.Error(t, err)
|
||||
|
||||
s, err = ScopesFromString("Calledbyentry,customgroups,Customgroups")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, CalledByEntry|CustomGroups, s)
|
||||
}
|
|
@ -97,7 +97,7 @@ func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]
|
|||
func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetTestVM() *vm.VM {
|
||||
func (chain testChain) GetTestVM(tx *transaction.Transaction) *vm.VM {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) {
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
// NEP5Decimals invokes `decimals` NEP5 method on a specified contract.
|
||||
func (c *Client) NEP5Decimals(tokenHash util.Uint160) (int64, error) {
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "decimals", []smartcontract.Parameter{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "decimals", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -30,7 +30,7 @@ func (c *Client) NEP5Decimals(tokenHash util.Uint160) (int64, error) {
|
|||
|
||||
// NEP5Name invokes `name` NEP5 method on a specified contract.
|
||||
func (c *Client) NEP5Name(tokenHash util.Uint160) (string, error) {
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "name", []smartcontract.Parameter{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "name", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -42,7 +42,7 @@ func (c *Client) NEP5Name(tokenHash util.Uint160) (string, error) {
|
|||
|
||||
// NEP5Symbol invokes `symbol` NEP5 method on a specified contract.
|
||||
func (c *Client) NEP5Symbol(tokenHash util.Uint160) (string, error) {
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "symbol", []smartcontract.Parameter{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "symbol", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -54,7 +54,7 @@ func (c *Client) NEP5Symbol(tokenHash util.Uint160) (string, error) {
|
|||
|
||||
// NEP5TotalSupply invokes `totalSupply` NEP5 method on a specified contract.
|
||||
func (c *Client) NEP5TotalSupply(tokenHash util.Uint160) (int64, error) {
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "totalSupply", []smartcontract.Parameter{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "totalSupply", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -66,7 +66,7 @@ func (c *Client) NEP5TotalSupply(tokenHash util.Uint160) (int64, error) {
|
|||
|
||||
// NEP5BalanceOf invokes `balanceOf` NEP5 method on a specified contract.
|
||||
func (c *Client) NEP5BalanceOf(tokenHash util.Uint160) (int64, error) {
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "balanceOf", []smartcontract.Parameter{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "balanceOf", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -120,7 +120,12 @@ func (c *Client) CreateNEP5TransferTx(acc *wallet.Account, to util.Uint160, toke
|
|||
},
|
||||
}
|
||||
|
||||
result, err := c.InvokeScript(hex.EncodeToString(script))
|
||||
result, err := c.InvokeScript(hex.EncodeToString(script), []transaction.Cosigner{
|
||||
{
|
||||
Account: from,
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't add system fee to transaction: %v", err)
|
||||
}
|
||||
|
|
|
@ -339,11 +339,16 @@ func (c *Client) GetVersion() (*result.Version, error) {
|
|||
|
||||
// InvokeScript returns the result of the given script after running it true the VM.
|
||||
// NOTE: This is a test invoke and will not affect the blockchain.
|
||||
func (c *Client) InvokeScript(script string) (*result.Invoke, error) {
|
||||
func (c *Client) InvokeScript(script string, cosigners []transaction.Cosigner) (*result.Invoke, error) {
|
||||
var (
|
||||
params = request.NewRawParams(script)
|
||||
params request.RawParams
|
||||
resp = &result.Invoke{}
|
||||
)
|
||||
if cosigners != nil {
|
||||
params = request.NewRawParams(script, cosigners)
|
||||
} else {
|
||||
params = request.NewRawParams(script)
|
||||
}
|
||||
if err := c.performRequest("invokescript", params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -353,25 +358,17 @@ func (c *Client) InvokeScript(script string) (*result.Invoke, error) {
|
|||
// InvokeFunction returns the results after calling the smart contract scripthash
|
||||
// with the given operation and parameters.
|
||||
// NOTE: this is test invoke and will not affect the blockchain.
|
||||
func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter) (*result.Invoke, error) {
|
||||
func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter, cosigners []transaction.Cosigner) (*result.Invoke, error) {
|
||||
var (
|
||||
p = request.NewRawParams(script, operation, params)
|
||||
p request.RawParams
|
||||
resp = &result.Invoke{}
|
||||
)
|
||||
if err := c.performRequest("invokefunction", p, resp); err != nil {
|
||||
return nil, err
|
||||
if cosigners != nil {
|
||||
p = request.NewRawParams(script, operation, params, cosigners)
|
||||
} else {
|
||||
p = request.NewRawParams(script, operation, params)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Invoke returns the results after calling the smart contract scripthash
|
||||
// with the given parameters.
|
||||
func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*result.Invoke, error) {
|
||||
var (
|
||||
p = request.NewRawParams(script, params)
|
||||
resp = &result.Invoke{}
|
||||
)
|
||||
if err := c.performRequest("invoke", p, resp); err != nil {
|
||||
if err := c.performRequest("invokefunction", p, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
|
|
|
@ -563,7 +563,9 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
Type: smartcontract.Hash160Type,
|
||||
Value: hash,
|
||||
},
|
||||
})
|
||||
}, []transaction.Cosigner{{
|
||||
Account: util.Uint160{1, 2, 3},
|
||||
}})
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf","state":"HALT","gas_consumed":"0.311","stack":[{"type":"ByteArray","value":"JivsCEQy"}],"tx":"d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
|
@ -589,7 +591,9 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191")
|
||||
return c.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191", []transaction.Cosigner{{
|
||||
Account: util.Uint160{1, 2, 3},
|
||||
}})
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"00046e616d656724058e5e1b6008847cd662728549088a9ee82191","state":"HALT","gas_consumed":"0.161","stack":[{"type":"ByteArray","value":"TkVQNSBHQVM="}],"tx":"d1011b00046e616d656724058e5e1b6008847cd662728549088a9ee82191000000000000000000000000"}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
|
@ -874,13 +878,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
{
|
||||
name: "invokefunction_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeFunction("", "", []smartcontract.Parameter{})
|
||||
return c.InvokeFunction("", "", []smartcontract.Parameter{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invokescript_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeScript("")
|
||||
return c.InvokeScript("", nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1050,13 +1054,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
{
|
||||
name: "invokefunction_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeFunction("", "", []smartcontract.Parameter{})
|
||||
return c.InvokeFunction("", "", []smartcontract.Parameter{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invokescript_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeScript("")
|
||||
return c.InvokeScript("", nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"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/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -65,6 +66,7 @@ const (
|
|||
TxFilterT
|
||||
NotificationFilterT
|
||||
ExecutionFilterT
|
||||
Cosigner
|
||||
)
|
||||
|
||||
func (p Param) String() string {
|
||||
|
@ -154,6 +156,47 @@ func (p Param) GetBytesHex() ([]byte, error) {
|
|||
return hex.DecodeString(s)
|
||||
}
|
||||
|
||||
// GetCosigner returns transaction.Cosigner value of the parameter.
|
||||
func (p Param) GetCosigner() (transaction.Cosigner, error) {
|
||||
c, ok := p.Value.(transaction.Cosigner)
|
||||
if !ok {
|
||||
return transaction.Cosigner{}, errors.New("not a cosigner")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// GetCosigners returns a slice of transaction.Cosigner with global scope from
|
||||
// array of Uint160 or array of serialized transaction.Cosigner stored in the
|
||||
// parameter.
|
||||
func (p Param) GetCosigners() ([]transaction.Cosigner, error) {
|
||||
hashes, err := p.GetArray()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cosigners := make([]transaction.Cosigner, len(hashes))
|
||||
// try to extract hashes first
|
||||
for i, h := range hashes {
|
||||
var u util.Uint160
|
||||
u, err = h.GetUint160FromHex()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
cosigners[i] = transaction.Cosigner{
|
||||
Account: u,
|
||||
Scopes: transaction.Global,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
for i, h := range hashes {
|
||||
cosigners[i], err = h.GetCosigner()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return cosigners, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (p *Param) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
|
@ -167,6 +210,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
|||
{TxFilterT, &TxFilter{}},
|
||||
{NotificationFilterT, &NotificationFilter{}},
|
||||
{ExecutionFilterT, &ExecutionFilter{}},
|
||||
{Cosigner, &transaction.Cosigner{}},
|
||||
{ArrayT, &[]Param{}},
|
||||
}
|
||||
|
||||
|
@ -196,6 +240,8 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
|||
} else {
|
||||
continue
|
||||
}
|
||||
case *transaction.Cosigner:
|
||||
p.Value = *val
|
||||
case *[]Param:
|
||||
p.Value = *val
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"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/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -19,9 +20,13 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
|||
{"cosigner": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
|
||||
{"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554", "cosigner": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
|
||||
{"contract": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
|
||||
{"state": "HALT"}]`
|
||||
{"state": "HALT"},
|
||||
{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0},
|
||||
[{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}]]`
|
||||
contr, err := util.Uint160DecodeStringLE("f84d6a337fbc3d3a201d41da99e86b479e7a2554")
|
||||
require.NoError(t, err)
|
||||
accountHash, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569")
|
||||
require.NoError(t, err)
|
||||
expected := Params{
|
||||
{
|
||||
Type: StringT,
|
||||
|
@ -83,6 +88,25 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
|||
Type: ExecutionFilterT,
|
||||
Value: ExecutionFilter{State: "HALT"},
|
||||
},
|
||||
{
|
||||
Type: Cosigner,
|
||||
Value: transaction.Cosigner{
|
||||
Account: accountHash,
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: ArrayT,
|
||||
Value: []Param{
|
||||
{
|
||||
Type: Cosigner,
|
||||
Value: transaction.Cosigner{
|
||||
Account: accountHash,
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var ps Params
|
||||
|
@ -214,3 +238,67 @@ func TestParamGetBytesHex(t *testing.T) {
|
|||
_, err = p.GetBytesHex()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestParamGetCosigner(t *testing.T) {
|
||||
c := transaction.Cosigner{
|
||||
Account: util.Uint160{1, 2, 3, 4},
|
||||
Scopes: transaction.Global,
|
||||
}
|
||||
p := Param{Type: Cosigner, Value: c}
|
||||
actual, err := p.GetCosigner()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c, actual)
|
||||
|
||||
p = Param{Type: Cosigner, Value: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`}
|
||||
_, err = p.GetCosigner()
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestParamGetCosigners(t *testing.T) {
|
||||
u1 := util.Uint160{1, 2, 3, 4}
|
||||
u2 := util.Uint160{5, 6, 7, 8}
|
||||
t.Run("from hashes", func(t *testing.T) {
|
||||
p := Param{ArrayT, []Param{
|
||||
{Type: StringT, Value: u1.StringLE()},
|
||||
{Type: StringT, Value: u2.StringLE()},
|
||||
}}
|
||||
actual, err := p.GetCosigners()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(actual))
|
||||
require.True(t, u1.Equals(actual[0].Account))
|
||||
require.True(t, u2.Equals(actual[1].Account))
|
||||
})
|
||||
|
||||
t.Run("from cosigners", func(t *testing.T) {
|
||||
c1 := transaction.Cosigner{
|
||||
Account: u1,
|
||||
Scopes: transaction.Global,
|
||||
}
|
||||
c2 := transaction.Cosigner{
|
||||
Account: u2,
|
||||
Scopes: transaction.CustomContracts,
|
||||
AllowedContracts: []util.Uint160{
|
||||
{1, 2, 3},
|
||||
{4, 5, 6},
|
||||
},
|
||||
}
|
||||
p := Param{ArrayT, []Param{
|
||||
{Type: Cosigner, Value: c1},
|
||||
{Type: Cosigner, Value: c2},
|
||||
}}
|
||||
actual, err := p.GetCosigners()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(actual))
|
||||
require.Equal(t, c1, actual[0])
|
||||
require.Equal(t, c2, actual[1])
|
||||
})
|
||||
|
||||
t.Run("bad format", func(t *testing.T) {
|
||||
p := Param{ArrayT, []Param{
|
||||
{Type: StringT, Value: u1.StringLE()},
|
||||
{Type: StringT, Value: "bla"},
|
||||
}}
|
||||
_, err := p.GetCosigners()
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -98,7 +98,6 @@ var rpcHandlers = map[string]func(*Server, request.Params) (interface{}, *respon
|
|||
"getunclaimedgas": (*Server).getUnclaimedGas,
|
||||
"getvalidators": (*Server).getValidators,
|
||||
"getversion": (*Server).getVersion,
|
||||
"invoke": (*Server).invoke,
|
||||
"invokefunction": (*Server).invokeFunction,
|
||||
"invokescript": (*Server).invokescript,
|
||||
"sendrawtransaction": (*Server).sendrawtransaction,
|
||||
|
@ -619,7 +618,7 @@ func (s *Server) getDecimals(h util.Uint160, cache map[util.Uint160]int64) (int6
|
|||
if err != nil {
|
||||
return 0, response.NewInternalServerError("Can't create script", err)
|
||||
}
|
||||
res := s.runScriptInVM(script)
|
||||
res := s.runScriptInVM(script, nil)
|
||||
if res == nil || res.State != "HALT" || len(res.Stack) == 0 {
|
||||
return 0, response.NewInternalServerError("execution error", errors.New("no result"))
|
||||
}
|
||||
|
@ -854,32 +853,7 @@ func (s *Server) getValidators(_ request.Params) (interface{}, *response.Error)
|
|||
return res, nil
|
||||
}
|
||||
|
||||
// invoke implements the `invoke` RPC call.
|
||||
func (s *Server) invoke(reqParams request.Params) (interface{}, *response.Error) {
|
||||
scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
|
||||
if !ok {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
scriptHash, err := scriptHashHex.GetUint160FromHex()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
sliceP, ok := reqParams.ValueWithType(1, request.ArrayT)
|
||||
if !ok {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
slice, err := sliceP.GetArray()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
script, err := request.CreateInvocationScript(scriptHash, slice)
|
||||
if err != nil {
|
||||
return nil, response.NewInternalServerError("can't create invocation script", err)
|
||||
}
|
||||
return s.runScriptInVM(script), nil
|
||||
}
|
||||
|
||||
// invokescript implements the `invokescript` RPC call.
|
||||
// invokeFunction implements the `invokeFunction` RPC call.
|
||||
func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *response.Error) {
|
||||
scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
|
||||
if !ok {
|
||||
|
@ -889,11 +863,21 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons
|
|||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:])
|
||||
tx := &transaction.Transaction{}
|
||||
checkWitnessHashesIndex := len(reqParams)
|
||||
if checkWitnessHashesIndex > 3 {
|
||||
cosigners, err := reqParams[3].GetCosigners()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
tx.Cosigners = cosigners
|
||||
checkWitnessHashesIndex--
|
||||
}
|
||||
script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:checkWitnessHashesIndex])
|
||||
if err != nil {
|
||||
return nil, response.NewInternalServerError("can't create invocation script", err)
|
||||
}
|
||||
return s.runScriptInVM(script), nil
|
||||
return s.runScriptInVM(script, tx), nil
|
||||
}
|
||||
|
||||
// invokescript implements the `invokescript` RPC call.
|
||||
|
@ -907,13 +891,21 @@ func (s *Server) invokescript(reqParams request.Params) (interface{}, *response.
|
|||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
return s.runScriptInVM(script), nil
|
||||
tx := &transaction.Transaction{}
|
||||
if len(reqParams) > 1 {
|
||||
cosigners, err := reqParams[1].GetCosigners()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
tx.Cosigners = cosigners
|
||||
}
|
||||
return s.runScriptInVM(script, tx), nil
|
||||
}
|
||||
|
||||
// runScriptInVM runs given script in a new test VM and returns the invocation
|
||||
// result.
|
||||
func (s *Server) runScriptInVM(script []byte) *result.Invoke {
|
||||
vm := s.chain.GetTestVM()
|
||||
func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *result.Invoke {
|
||||
vm := s.chain.GetTestVM(tx)
|
||||
vm.SetGasLimit(s.config.MaxGasInvoke)
|
||||
vm.LoadScriptWithFlags(script, smartcontract.All)
|
||||
_ = vm.Run()
|
||||
|
|
|
@ -572,45 +572,6 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
},
|
||||
},
|
||||
},
|
||||
"invoke": {
|
||||
{
|
||||
name: "positive",
|
||||
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", [{"type": "String", "value": "qwerty"}]]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "0c067177657274790c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", res.Script)
|
||||
assert.NotEqual(t, "", res.State)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "not a string",
|
||||
params: `[42, []]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "not a scripthash",
|
||||
params: `["qwerty", []]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "not an array",
|
||||
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", 42]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "bad params",
|
||||
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", [{"type": "Integer", "value": "qwerty"}]]`,
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
"invokefunction": {
|
||||
{
|
||||
name: "positive",
|
||||
|
@ -658,6 +619,55 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, good witness",
|
||||
// script is hex-encoded `test_verify.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix
|
||||
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
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)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, bad witness of second hash",
|
||||
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
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)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, no good hashes",
|
||||
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340"]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
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)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, bad hashes witness",
|
||||
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
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)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
|
|
BIN
pkg/rpc/server/testdata/test_verify.avm
vendored
Executable file
BIN
pkg/rpc/server/testdata/test_verify.avm
vendored
Executable file
Binary file not shown.
17
pkg/rpc/server/testdata/test_verify.go
vendored
Normal file
17
pkg/rpc/server/testdata/test_verify.go
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package testdata
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
|
||||
// This contract is used to test `invokescript` and `invokefunction` RPC-calls
|
||||
func Main() int {
|
||||
// h1 and h2 are just random uint160 hashes
|
||||
h1 := []byte{1, 12, 3, 14, 5, 6, 12, 13, 2, 14, 15, 13, 3, 14, 7, 9, 0, 0, 0, 0}
|
||||
if !runtime.CheckWitness(h1) {
|
||||
return 1
|
||||
}
|
||||
h2 := []byte{13, 15, 3, 2, 9, 0, 2, 1, 3, 7, 3, 4, 5, 2, 1, 0, 14, 6, 12, 9}
|
||||
if !runtime.CheckWitness(h2) {
|
||||
return 2
|
||||
}
|
||||
return 3
|
||||
}
|
Loading…
Reference in a new issue