Merge pull request #1489 from nspcc-dev/rpc/invoke_with_base64

rpc, cli: use base64 script encoding for Invoke* calls; add Magic to `getversion` RPC call
This commit is contained in:
Roman Khimov 2020-10-19 16:17:06 +03:00 committed by GitHub
commit 8be7cd4bff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 254 additions and 118 deletions

View file

@ -17,7 +17,7 @@ func TestRegisterCandidate(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep5", "multitransfer", e.Run(t, "neo-go", "wallet", "nep5", "multitransfer",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"neo:"+validatorPriv.Address()+":10", "neo:"+validatorPriv.Address()+":10",
@ -26,7 +26,7 @@ func TestRegisterCandidate(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "candidate", "register", e.Run(t, "neo-go", "wallet", "candidate", "register",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--address", validatorPriv.Address()) "--address", validatorPriv.Address())
e.checkTxPersisted(t) e.checkTxPersisted(t)
@ -40,7 +40,7 @@ func TestRegisterCandidate(t *testing.T) {
t.Run("Vote", func(t *testing.T) { t.Run("Vote", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "candidate", "vote", e.Run(t, "neo-go", "wallet", "candidate", "vote",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--address", validatorPriv.Address(), "--address", validatorPriv.Address(),
"--candidate", hex.EncodeToString(validatorPriv.PublicKey().Bytes())) "--candidate", hex.EncodeToString(validatorPriv.PublicKey().Bytes()))
@ -55,7 +55,7 @@ func TestRegisterCandidate(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "candidate", "unregister", e.Run(t, "neo-go", "wallet", "candidate", "unregister",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--address", validatorPriv.Address()) "--address", validatorPriv.Address())
e.checkTxPersisted(t) e.checkTxPersisted(t)

View file

@ -40,7 +40,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "deploy", e.Run(t, "neo-go", "contract", "deploy",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr, "--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName) "--in", nefName, "--manifest", manifestName)
@ -53,7 +53,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "testinvokefunction", e.Run(t, "neo-go", "contract", "testinvokefunction",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
h.StringLE(), "getValue") h.StringLE(), "getValue")
res := new(result.Invoke) res := new(result.Invoke)
@ -84,7 +84,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "invokefunction", e.Run(t, "neo-go", "contract", "invokefunction",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr, "--wallet", validatorWallet, "--address", validatorAddr,
h.StringLE(), "update", h.StringLE(), "update",
"bytes:"+hex.EncodeToString(realNef.Script), "bytes:"+hex.EncodeToString(realNef.Script),
@ -94,7 +94,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "testinvokefunction", e.Run(t, "neo-go", "contract", "testinvokefunction",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
hash.Hash160(realNef.Script).StringLE(), "getValue") hash.Hash160(realNef.Script).StringLE(), "getValue")
res := new(result.Invoke) res := new(result.Invoke)

View file

@ -53,7 +53,7 @@ func TestSignMultisigTx(t *testing.T) {
// Transfer funds to the multisig. // Transfer funds to the multisig.
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep5", "multitransfer", e.Run(t, "neo-go", "wallet", "nep5", "multitransfer",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"neo:"+multisigAddr+":4", "neo:"+multisigAddr+":4",
@ -68,14 +68,14 @@ func TestSignMultisigTx(t *testing.T) {
defer os.Remove(txPath) defer os.Remove(txPath)
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "nep5", "transfer", e.Run(t, "neo-go", "wallet", "nep5", "transfer",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet1Path, "--from", multisigAddr, "--wallet", wallet1Path, "--from", multisigAddr,
"--to", priv.Address(), "--token", "neo", "--amount", "1", "--to", priv.Address(), "--token", "neo", "--amount", "1",
"--out", txPath) "--out", txPath)
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "multisig", "sign", e.Run(t, "neo-go", "wallet", "multisig", "sign",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet2Path, "--address", multisigAddr, "--wallet", wallet2Path, "--address", multisigAddr,
"--in", txPath, "--out", txPath) "--in", txPath, "--out", txPath)
e.checkTxPersisted(t) e.checkTxPersisted(t)
@ -88,7 +88,7 @@ func TestSignMultisigTx(t *testing.T) {
t.Run("via invokefunction", func(t *testing.T) { t.Run("via invokefunction", func(t *testing.T) {
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "contract", "invokefunction", e.Run(t, "neo-go", "contract", "invokefunction",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet1Path, "--address", multisigAddr, "--wallet", wallet1Path, "--address", multisigAddr,
"--out", txPath, "--out", txPath,
client.NeoContractHash.StringLE(), "transfer", client.NeoContractHash.StringLE(), "transfer",
@ -99,7 +99,7 @@ func TestSignMultisigTx(t *testing.T) {
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "multisig", "sign", e.Run(t, "neo-go", "wallet", "multisig", "sign",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet2Path, "--address", multisigAddr, "--wallet", wallet2Path, "--address", multisigAddr,
"--in", txPath, "--out", txPath) "--in", txPath, "--out", txPath)
e.checkTxPersisted(t) e.checkTxPersisted(t)

View file

@ -109,7 +109,6 @@ func TestNEP5Transfer(t *testing.T) {
defer e.Close(t) defer e.Close(t)
args := []string{ args := []string{
"neo-go", "wallet", "nep5", "transfer", "neo-go", "wallet", "nep5", "transfer",
"--unittest",
"--rpc-endpoint", "http://" + e.RPC.Addr, "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
@ -141,7 +140,7 @@ func TestNEP5MultiTransfer(t *testing.T) {
defer e.Close(t) defer e.Close(t)
args := []string{ args := []string{
"neo-go", "wallet", "nep5", "multitransfer", "neo-go", "wallet", "nep5", "multitransfer",
"--unittest", "--rpc-endpoint", "http://" + e.RPC.Addr, "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"neo:" + privs[0].Address() + ":42", "neo:" + privs[0].Address() + ":42",

View file

@ -34,10 +34,6 @@ var RPC = []cli.Flag{
Name: "timeout, s", Name: "timeout, s",
Usage: "Timeout for the operation (10 seconds by default)", Usage: "Timeout for the operation (10 seconds by default)",
}, },
cli.BoolFlag{Name: "privnet, p"},
cli.BoolFlag{Name: "mainnet, m"},
cli.BoolFlag{Name: "testnet, t"},
cli.BoolFlag{Name: "unittest", Hidden: true},
} }
var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'") var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'")
@ -73,7 +69,11 @@ func GetRPCClient(gctx context.Context, ctx *cli.Context) (*client.Client, cli.E
if len(endpoint) == 0 { if len(endpoint) == 0 {
return nil, cli.NewExitError(errNoEndpoint, 1) return nil, cli.NewExitError(errNoEndpoint, 1)
} }
c, err := client.New(gctx, endpoint, client.Options{Network: GetNetwork(ctx)}) c, err := client.New(gctx, endpoint, client.Options{})
if err != nil {
return nil, cli.NewExitError(err, 1)
}
err = c.Init()
if err != nil { if err != nil {
return nil, cli.NewExitError(err, 1) return nil, cli.NewExitError(err, 1)
} }

View file

@ -2,10 +2,13 @@ package options
import ( import (
"flag" "flag"
"net/http"
"net/http/httptest"
"testing" "testing"
"time" "time"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -56,6 +59,17 @@ func TestGetTimeoutContext(t *testing.T) {
} }
func TestGetRPCClient(t *testing.T) { func TestGetRPCClient(t *testing.T) {
// need test server for proper client.Init() handling
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
response := `{"id":1,"jsonrpc":"2.0","result":{"magic":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`
_, err := w.Write([]byte(response))
if err != nil {
t.Fatalf("Error writing response: %s", err.Error())
}
}))
defer srv.Close()
t.Run("no endpoint", func(t *testing.T) { t.Run("no endpoint", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError) set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil) ctx := cli.NewContext(cli.NewApp(), set, nil)
@ -66,7 +80,7 @@ func TestGetRPCClient(t *testing.T) {
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError) set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String(RPCEndpointFlag, "http://localhost:50333", "") set.String(RPCEndpointFlag, srv.URL, "")
ctx := cli.NewContext(cli.NewApp(), set, nil) ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := GetTimeoutContext(ctx) gctx, _ := GetTimeoutContext(ctx)
_, ec := GetRPCClient(gctx, ctx) _, ec := GetRPCClient(gctx, ctx)

View file

@ -487,6 +487,9 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
if err != nil { if err != nil {
return err return err
} }
if err = c.Init(); err != nil {
return err
}
resp, err = c.InvokeFunction(script, operation, params, cosigners) resp, err = c.InvokeFunction(script, operation, params, cosigners)
if err != nil { if err != nil {
@ -500,11 +503,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
fmt.Fprintln(ctx.App.Writer, errText+". Sending transaction...") fmt.Fprintln(ctx.App.Writer, errText+". Sending transaction...")
} }
if out := ctx.String("out"); out != "" { if out := ctx.String("out"); out != "" {
script, err := hex.DecodeString(resp.Script) tx, err := c.CreateTxFromScript(resp.Script, acc, resp.GasConsumed, int64(gas), cosigners...)
if err != nil {
return cli.NewExitError(fmt.Errorf("bad script returned from the RPC node: %w", err), 1)
}
tx, err := c.CreateTxFromScript(script, acc, resp.GasConsumed, int64(gas), cosigners...)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1) return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
} }
@ -518,11 +517,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
if len(resp.Script) == 0 { if len(resp.Script) == 0 {
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1) return cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
} }
script, err := hex.DecodeString(resp.Script) txHash, err := c.SignAndPushInvocationTx(resp.Script, acc, resp.GasConsumed, gas, cosigners)
if err != nil {
return cli.NewExitError(fmt.Errorf("bad script returned from the RPC node: %w", err), 1)
}
txHash, err := c.SignAndPushInvocationTx(script, acc, resp.GasConsumed, gas, cosigners)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1) return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
} }

View file

@ -176,7 +176,7 @@ func TestClaimGas(t *testing.T) {
balanceBefore := e.Chain.GetUtilityTokenBalance(validatorHash) balanceBefore := e.Chain.GetUtilityTokenBalance(validatorHash)
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "claim", e.Run(t, "neo-go", "wallet", "claim",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--address", validatorAddr) "--address", validatorAddr)
tx, end := e.checkTxPersisted(t) tx, end := e.checkTxPersisted(t)
@ -195,7 +195,7 @@ func TestImportDeployed(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "deploy", e.Run(t, "neo-go", "contract", "deploy",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr, "--wallet", validatorWallet, "--address", validatorAddr,
"--in", "testdata/verify.nef", "--manifest", "testdata/verify.manifest.json") "--in", "testdata/verify.nef", "--manifest", "testdata/verify.manifest.json")
@ -217,7 +217,7 @@ func TestImportDeployed(t *testing.T) {
e.In.WriteString("acc\rpass\rpass\r") e.In.WriteString("acc\rpass\rpass\r")
e.Run(t, "neo-go", "wallet", "import-deployed", e.Run(t, "neo-go", "wallet", "import-deployed",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath, "--wif", priv.WIF(), "--wallet", walletPath, "--wif", priv.WIF(),
"--contract", h.StringLE()) "--contract", h.StringLE())
@ -232,7 +232,7 @@ func TestImportDeployed(t *testing.T) {
t.Run("Sign", func(t *testing.T) { t.Run("Sign", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep5", "multitransfer", e.Run(t, "neo-go", "wallet", "nep5", "multitransfer",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--from", validatorAddr, "--wallet", validatorWallet, "--from", validatorAddr,
"neo:"+contractAddr+":10", "neo:"+contractAddr+":10",
"gas:"+contractAddr+":10") "gas:"+contractAddr+":10")
@ -243,7 +243,7 @@ func TestImportDeployed(t *testing.T) {
e.In.WriteString("pass\r") e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "nep5", "transfer", e.Run(t, "neo-go", "wallet", "nep5", "transfer",
"--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath, "--from", contractAddr, "--wallet", walletPath, "--from", contractAddr,
"--to", privTo.Address(), "--token", "neo", "--amount", "1") "--to", privTo.Address(), "--token", "neo", "--amount", "1")
e.checkTxPersisted(t) e.checkTxPersisted(t)

View file

@ -29,6 +29,8 @@ const (
type Client struct { type Client struct {
cli *http.Client cli *http.Client
endpoint *url.URL endpoint *url.URL
network netmode.Magic
initDone bool
ctx context.Context ctx context.Context
opts Options opts Options
requestF func(*request.Raw) (*response.Raw, error) requestF func(*request.Raw) (*response.Raw, error)
@ -46,7 +48,6 @@ type Options struct {
CACert string CACert string
DialTimeout time.Duration DialTimeout time.Duration
RequestTimeout time.Duration RequestTimeout time.Duration
Network netmode.Magic
} }
// cache stores cache values for the RPC client methods // cache stores cache values for the RPC client methods
@ -61,7 +62,8 @@ type calculateValidUntilBlockCache struct {
expiresAt uint32 expiresAt uint32
} }
// New returns a new Client ready to use. // New returns a new Client ready to use. You should call Init method to
// initialize network magic the client is operating on.
func New(ctx context.Context, endpoint string, opts Options) (*Client, error) { func New(ctx context.Context, endpoint string, opts Options) (*Client, error) {
url, err := url.Parse(endpoint) url, err := url.Parse(endpoint)
if err != nil { if err != nil {
@ -99,6 +101,19 @@ func New(ctx context.Context, endpoint string, opts Options) (*Client, error) {
return cl, nil return cl, nil
} }
// Init sets magic of the network client connected to. This method should be called
// before any transaction-, header- or block-related requests in order to deserialize
// responses properly.
func (c *Client) Init() error {
version, err := c.GetVersion()
if err != nil {
return fmt.Errorf("failed to get network magic: %w", err)
}
c.network = version.Magic
c.initDone = true
return nil
}
func (c *Client) performRequest(method string, p request.RawParams, v interface{}) error { func (c *Client) performRequest(method string, p request.RawParams, v interface{}) error {
var r = request.Raw{ var r = request.Raw{
JSONRPC: request.JSONRPCVersion, JSONRPC: request.JSONRPCVersion,

View file

@ -5,14 +5,13 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"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/rpc/client" "github.com/nspcc-dev/neo-go/pkg/rpc/client"
) )
func Example() { func Example() {
endpoint := "http://seed5.bridgeprotocol.io:10332" endpoint := "http://seed5.bridgeprotocol.io:10332"
opts := client.Options{Network: netmode.MainNet} opts := client.Options{}
c, err := client.New(context.TODO(), endpoint, opts) c, err := client.New(context.TODO(), endpoint, opts)
if err != nil { if err != nil {

View file

@ -152,7 +152,8 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, gas int64, recip
} }
// CreateTxFromScript creates transaction and properly sets cosigners and NetworkFee. // CreateTxFromScript creates transaction and properly sets cosigners and NetworkFee.
// If sysFee <= 0, it is determined via result of `invokescript` RPC. // If sysFee <= 0, it is determined via result of `invokescript` RPC. You should
// initialize network magic with Init before calling CreateTxFromScript.
func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee, netFee int64, func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee, netFee int64,
cosigners ...transaction.Signer) (*transaction.Transaction, error) { cosigners ...transaction.Signer) (*transaction.Transaction, error) {
from, err := address.StringToUint160(acc.Address) from, err := address.StringToUint160(acc.Address)
@ -172,7 +173,10 @@ func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee,
sysFee = result.GasConsumed sysFee = result.GasConsumed
} }
tx := transaction.New(c.opts.Network, script, sysFee) if !c.initDone {
return nil, errNetworkNotInitialized
}
tx := transaction.New(c.GetNetwork(), script, sysFee)
tx.Signers = signers tx.Signers = signers
tx.ValidUntilBlock, err = c.CalculateValidUntilBlock() tx.ValidUntilBlock, err = c.CalculateValidUntilBlock()

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/fee"
@ -20,6 +21,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
) )
var errNetworkNotInitialized = errors.New("RPC client network is not initialized")
// GetApplicationLog returns the contract log based on the specified txid. // GetApplicationLog returns the contract log based on the specified txid.
func (c *Client) GetApplicationLog(hash util.Uint256) (*state.AppExecResult, error) { func (c *Client) GetApplicationLog(hash util.Uint256) (*state.AppExecResult, error) {
var ( var (
@ -50,12 +53,14 @@ func (c *Client) GetBlockCount() (uint32, error) {
return resp, nil return resp, nil
} }
// GetBlockByIndex returns a block by its height. // GetBlockByIndex returns a block by its height. You should initialize network magic
// with Init before calling GetBlockByIndex.
func (c *Client) GetBlockByIndex(index uint32) (*block.Block, error) { func (c *Client) GetBlockByIndex(index uint32) (*block.Block, error) {
return c.getBlock(request.NewRawParams(index)) return c.getBlock(request.NewRawParams(index))
} }
// GetBlockByHash returns a block by its hash. // GetBlockByHash returns a block by its hash. You should initialize network magic
// with Init before calling GetBlockByHash.
func (c *Client) GetBlockByHash(hash util.Uint256) (*block.Block, error) { func (c *Client) GetBlockByHash(hash util.Uint256) (*block.Block, error) {
return c.getBlock(request.NewRawParams(hash.StringLE())) return c.getBlock(request.NewRawParams(hash.StringLE()))
} }
@ -66,6 +71,9 @@ func (c *Client) getBlock(params request.RawParams) (*block.Block, error) {
err error err error
b *block.Block b *block.Block
) )
if !c.initDone {
return nil, errNetworkNotInitialized
}
if err = c.performRequest("getblock", params, &resp); err != nil { if err = c.performRequest("getblock", params, &resp); err != nil {
return nil, err return nil, err
} }
@ -74,7 +82,7 @@ func (c *Client) getBlock(params request.RawParams) (*block.Block, error) {
return nil, err return nil, err
} }
r := io.NewBinReaderFromBuf(blockBytes) r := io.NewBinReaderFromBuf(blockBytes)
b = block.New(c.opts.Network) b = block.New(c.GetNetwork())
b.DecodeBinary(r) b.DecodeBinary(r)
if r.Err != nil { if r.Err != nil {
return nil, r.Err return nil, r.Err
@ -83,14 +91,14 @@ func (c *Client) getBlock(params request.RawParams) (*block.Block, error) {
} }
// GetBlockByIndexVerbose returns a block wrapper with additional metadata by // GetBlockByIndexVerbose returns a block wrapper with additional metadata by
// its height. // its height. You should initialize network magic with Init before calling GetBlockByIndexVerbose.
// NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively. // NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively.
func (c *Client) GetBlockByIndexVerbose(index uint32) (*result.Block, error) { func (c *Client) GetBlockByIndexVerbose(index uint32) (*result.Block, error) {
return c.getBlockVerbose(request.NewRawParams(index, 1)) return c.getBlockVerbose(request.NewRawParams(index, 1))
} }
// GetBlockByHashVerbose returns a block wrapper with additional metadata by // GetBlockByHashVerbose returns a block wrapper with additional metadata by
// its hash. // its hash. You should initialize network magic with Init before calling GetBlockByHashVerbose.
func (c *Client) GetBlockByHashVerbose(hash util.Uint256) (*result.Block, error) { func (c *Client) GetBlockByHashVerbose(hash util.Uint256) (*result.Block, error) {
return c.getBlockVerbose(request.NewRawParams(hash.StringLE(), 1)) return c.getBlockVerbose(request.NewRawParams(hash.StringLE(), 1))
} }
@ -100,7 +108,10 @@ func (c *Client) getBlockVerbose(params request.RawParams) (*result.Block, error
resp = &result.Block{} resp = &result.Block{}
err error err error
) )
resp.Network = c.opts.Network if !c.initDone {
return nil, errNetworkNotInitialized
}
resp.Network = c.GetNetwork()
if err = c.performRequest("getblock", params, resp); err != nil { if err = c.performRequest("getblock", params, resp); err != nil {
return nil, err return nil, err
} }
@ -120,13 +131,17 @@ func (c *Client) GetBlockHash(index uint32) (util.Uint256, error) {
} }
// GetBlockHeader returns the corresponding block header information from serialized hex string // GetBlockHeader returns the corresponding block header information from serialized hex string
// according to the specified script hash. // according to the specified script hash. You should initialize network magic
// // with Init before calling GetBlockHeader.
func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) { func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) {
var ( var (
params = request.NewRawParams(hash.StringLE()) params = request.NewRawParams(hash.StringLE())
resp string resp string
h *block.Header h *block.Header
) )
if !c.initDone {
return nil, errNetworkNotInitialized
}
if err := c.performRequest("getblockheader", params, &resp); err != nil { if err := c.performRequest("getblockheader", params, &resp); err != nil {
return nil, err return nil, err
} }
@ -136,7 +151,7 @@ func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) {
} }
r := io.NewBinReaderFromBuf(headerBytes) r := io.NewBinReaderFromBuf(headerBytes)
h = new(block.Header) h = new(block.Header)
h.Network = c.opts.Network h.Network = c.GetNetwork()
h.DecodeBinary(r) h.DecodeBinary(r)
if r.Err != nil { if r.Err != nil {
return nil, r.Err return nil, r.Err
@ -271,13 +286,17 @@ func (c *Client) GetRawMemPool() ([]util.Uint256, error) {
return *resp, nil return *resp, nil
} }
// GetRawTransaction returns a transaction by hash. // GetRawTransaction returns a transaction by hash. You should initialize network magic
// with Init before calling GetRawTransaction.
func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction, error) { func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction, error) {
var ( var (
params = request.NewRawParams(hash.StringLE()) params = request.NewRawParams(hash.StringLE())
resp string resp string
err error err error
) )
if !c.initDone {
return nil, errNetworkNotInitialized
}
if err = c.performRequest("getrawtransaction", params, &resp); err != nil { if err = c.performRequest("getrawtransaction", params, &resp); err != nil {
return nil, err return nil, err
} }
@ -285,7 +304,7 @@ func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction,
if err != nil { if err != nil {
return nil, err return nil, err
} }
tx, err := transaction.NewTransactionFromBytes(c.opts.Network, txBytes) tx, err := transaction.NewTransactionFromBytes(c.GetNetwork(), txBytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -293,7 +312,8 @@ func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction,
} }
// GetRawTransactionVerbose returns a transaction wrapper with additional // GetRawTransactionVerbose returns a transaction wrapper with additional
// metadata by transaction's hash. // metadata by transaction's hash. You should initialize network magic
// with Init before calling GetRawTransactionVerbose.
// NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively. // NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively.
func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.TransactionOutputRaw, error) { func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.TransactionOutputRaw, error) {
var ( var (
@ -301,7 +321,10 @@ func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.Transactio
resp = &result.TransactionOutputRaw{} resp = &result.TransactionOutputRaw{}
err error err error
) )
resp.Network = c.opts.Network if !c.initDone {
return nil, errNetworkNotInitialized
}
resp.Network = c.GetNetwork()
if err = c.performRequest("getrawtransaction", params, resp); err != nil { if err = c.performRequest("getrawtransaction", params, resp); err != nil {
return nil, err return nil, err
} }
@ -381,7 +404,7 @@ func (c *Client) GetVersion() (*result.Version, error) {
// InvokeScript returns the result of the given script after running it true the VM. // 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. // NOTE: This is a test invoke and will not affect the blockchain.
func (c *Client) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) { func (c *Client) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
var p = request.NewRawParams(hex.EncodeToString(script)) var p = request.NewRawParams(script)
return c.invokeSomething("invokescript", p, signers) return c.invokeSomething("invokescript", p, signers)
} }
@ -569,3 +592,8 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs
tx.NetworkFee += int64(size)*fee + extraFee tx.NetworkFee += int64(size)*fee + extraFee
return nil return nil
} }
// GetNetwork returns the network magic of the RPC node client connected to.
func (c *Client) GetNetwork() netmode.Magic {
return c.network
}

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"math/big" "math/big"
"net/http" "net/http"
@ -347,7 +348,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
return c.GetFeePerByte() return c.GetFeePerByte()
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c0d676574466565506572427974650c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"1000"}],"tx":null}}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"1000"}],"tx":null}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
return int64(1000) return int64(1000)
}, },
@ -359,7 +360,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
return c.GetMaxTransactionsPerBlock() return c.GetMaxTransactionsPerBlock()
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c1a6765744d61785472616e73616374696f6e73506572426c6f636b0c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"512"}],"tx":null}}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMGmdldE1heFRyYW5zYWN0aW9uc1BlckJsb2NrDBSaYaRu7Je4kwbXzoHxW0YgkdAJMkFifVtS","stack":[{"type":"Integer","value":"512"}],"tx":null}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
return int64(512) return int64(512)
}, },
@ -371,7 +372,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
return c.GetMaxBlockSize() return c.GetMaxBlockSize()
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c0f6765744d6178426c6f636b53697a650c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"262144"}],"tx":null}}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMD2dldE1heEJsb2NrU2l6ZQwUmmGkbuyXuJMG186B8VtGIJHQCTJBYn1bUg==","stack":[{"type":"Integer","value":"262144"}],"tx":null}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
return int64(262144) return int64(262144)
}, },
@ -383,7 +384,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
return c.GetBlockedAccounts() return c.GetBlockedAccounts()
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c12676574426c6f636b65644163636f756e74730c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Array","value":[]}],"tx":null}}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMEmdldEJsb2NrZWRBY2NvdW50cwwUmmGkbuyXuJMG186B8VtGIJHQCTJBYn1bUg==","stack":[{"type":"Array","value":[]}],"tx":null}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
return native.BlockedAccounts{} return native.BlockedAccounts{}
}, },
@ -631,9 +632,10 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
return c.GetVersion() return c.GetVersion()
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"magic":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
return &result.Version{ return &result.Version{
Magic: netmode.UnitTestNet,
TCPPort: uint16(20332), TCPPort: uint16(20332),
WSPort: uint16(20342), WSPort: uint16(20342),
Nonce: 2153672787, Nonce: 2153672787,
@ -663,7 +665,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
Account: util.Uint160{1, 2, 3}, Account: util.Uint160{1, 2, 3},
}}) }})
}, },
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"01e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b5238","state":"HALT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"000800000080969800000000000204130000000000b004000001aa8acf859d4fe402b34e673f2156821796a488eb01005701e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b523801420c40d83408774d4bd8b19ae870561d06f2744eb7678f58d1b90cf2f50e98ae83f60b0824e2feeadef6de6418a4cfc43bbc1f916c33ec594cbe662a9d924786e17a14290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78"}}`, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"FCaufGyYYexBhGjB8P3Ep/KWPriRUcEJYmFsYW5jZU9mZ74557Vi9gy/4q68o3Wi5e4oc3yv","state":"HALT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"000800000080969800000000000204130000000000b004000001aa8acf859d4fe402b34e673f2156821796a488eb01005701e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b523801420c40d83408774d4bd8b19ae870561d06f2744eb7678f58d1b90cf2f50e98ae83f60b0824e2feeadef6de6418a4cfc43bbc1f916c33ec594cbe662a9d924786e17a14290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78"}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
return &result.Invoke{} return &result.Invoke{}
}, },
@ -674,9 +676,13 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
if err != nil { if err != nil {
panic(err) panic(err)
} }
script, err := base64.StdEncoding.DecodeString("FCaufGyYYexBhGjB8P3Ep/KWPriRUcEJYmFsYW5jZU9mZ74557Vi9gy/4q68o3Wi5e4oc3yv")
if err != nil {
panic(err)
}
assert.Equal(t, "HALT", res.State) assert.Equal(t, "HALT", res.State)
assert.Equal(t, int64(31100000), res.GasConsumed) assert.Equal(t, int64(31100000), res.GasConsumed)
assert.Equal(t, "01e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b5238", res.Script) assert.Equal(t, script, res.Script)
assert.Equal(t, []stackitem.Item{stackitem.NewByteArray(bytes)}, res.Stack) assert.Equal(t, []stackitem.Item{stackitem.NewByteArray(bytes)}, res.Stack)
assert.NotNil(t, res.Transaction) assert.NotNil(t, res.Transaction)
}, },
@ -701,7 +707,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
Account: util.Uint160{1, 2, 3}, Account: util.Uint160{1, 2, 3},
}}) }})
}, },
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf","state":"FAULT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"000800000080969800000000000204130000000000b004000001aa8acf859d4fe402b34e673f2156821796a488eb01005701e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b523801420c40d83408774d4bd8b19ae870561d06f2744eb7678f58d1b90cf2f50e98ae83f60b0824e2feeadef6de6418a4cfc43bbc1f916c33ec594cbe662a9d924786e17a14290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78","exception":"gas limit exceeded"}}`, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"FCaufGyYYexBhGjB8P3Ep/KWPriRUcEJYmFsYW5jZU9mZ74557Vi9gy/4q68o3Wi5e4oc3yv","state":"FAULT","gasconsumed":"31100000","stack":[{"type":"ByteString","value":"JivsCEQy"}],"tx":"000800000080969800000000000204130000000000b004000001aa8acf859d4fe402b34e673f2156821796a488eb01005701e8030c14aa8acf859d4fe402b34e673f2156821796a488eb0c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb013c00c087472616e736665720c14e79eb66d3c134a4a776ee807d2e5b846dda4fdb041627d5b523801420c40d83408774d4bd8b19ae870561d06f2744eb7678f58d1b90cf2f50e98ae83f60b0824e2feeadef6de6418a4cfc43bbc1f916c33ec594cbe662a9d924786e17a14290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78","exception":"gas limit exceeded"}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
return &result.Invoke{} return &result.Invoke{}
}, },
@ -712,9 +718,13 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
if err != nil { if err != nil {
panic(err) panic(err)
} }
script, err := base64.StdEncoding.DecodeString("FCaufGyYYexBhGjB8P3Ep/KWPriRUcEJYmFsYW5jZU9mZ74557Vi9gy/4q68o3Wi5e4oc3yv")
if err != nil {
panic(err)
}
assert.Equal(t, "FAULT", res.State) assert.Equal(t, "FAULT", res.State)
assert.Equal(t, int64(31100000), res.GasConsumed) assert.Equal(t, int64(31100000), res.GasConsumed)
assert.Equal(t, "1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf", res.Script) assert.Equal(t, script, res.Script)
assert.Equal(t, []stackitem.Item{stackitem.NewByteArray(bytes)}, res.Stack) assert.Equal(t, []stackitem.Item{stackitem.NewByteArray(bytes)}, res.Stack)
assert.NotNil(t, res.Transaction) assert.NotNil(t, res.Transaction)
}, },
@ -724,7 +734,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
{ {
name: "positive", name: "positive",
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
script, err := hex.DecodeString("00046e616d656724058e5e1b6008847cd662728549088a9ee82191") script, err := base64.StdEncoding.DecodeString("AARuYW1lZyQFjl4bYAiEfNZicoVJCIqe6CGR")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -732,16 +742,20 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
Account: util.Uint160{1, 2, 3}, Account: util.Uint160{1, 2, 3},
}}) }})
}, },
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"00046e616d656724058e5e1b6008847cd662728549088a9ee82191","state":"HALT","gasconsumed":"16100000","stack":[{"type":"ByteString","value":"TkVQNSBHQVM="}],"tx":null}}`, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"AARuYW1lZyQFjl4bYAiEfNZicoVJCIqe6CGR","state":"HALT","gasconsumed":"16100000","stack":[{"type":"ByteString","value":"TkVQNSBHQVM="}],"tx":null}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
bytes, err := hex.DecodeString("4e45503520474153") bytes, err := hex.DecodeString("4e45503520474153")
if err != nil { if err != nil {
panic(err) panic(err)
} }
script, err := base64.StdEncoding.DecodeString("AARuYW1lZyQFjl4bYAiEfNZicoVJCIqe6CGR")
if err != nil {
panic(err)
}
return &result.Invoke{ return &result.Invoke{
State: "HALT", State: "HALT",
GasConsumed: 16100000, GasConsumed: 16100000,
Script: "00046e616d656724058e5e1b6008847cd662728549088a9ee82191", Script: script,
Stack: []stackitem.Item{stackitem.NewByteArray(bytes)}, Stack: []stackitem.Item{stackitem.NewByteArray(bytes)},
} }
}, },
@ -1234,12 +1248,6 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
return c.GetNextBlockValidators() return c.GetNextBlockValidators()
}, },
}, },
{
name: "getversion_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetVersion()
},
},
{ {
name: "invokefunction_unmarshalling_error", name: "invokefunction_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
@ -1280,13 +1288,17 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
func TestRPCClients(t *testing.T) { func TestRPCClients(t *testing.T) {
t.Run("Client", func(t *testing.T) { t.Run("Client", func(t *testing.T) {
testRPCClient(t, func(ctx context.Context, endpoint string, opts Options) (*Client, error) { testRPCClient(t, func(ctx context.Context, endpoint string, opts Options) (*Client, error) {
return New(ctx, endpoint, opts) c, err := New(ctx, endpoint, opts)
require.NoError(t, err)
require.NoError(t, c.Init())
return c, nil
}) })
}) })
t.Run("WSClient", func(t *testing.T) { t.Run("WSClient", func(t *testing.T) {
testRPCClient(t, func(ctx context.Context, endpoint string, opts Options) (*Client, error) { testRPCClient(t, func(ctx context.Context, endpoint string, opts Options) (*Client, error) {
wsc, err := NewWS(ctx, httpURLtoWS(endpoint), opts) wsc, err := NewWS(ctx, httpURLtoWS(endpoint), opts)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wsc.Init())
return &wsc.Client, nil return &wsc.Client, nil
}) })
}) })
@ -1301,7 +1313,7 @@ func testRPCClient(t *testing.T, newClient func(context.Context, string, Options
defer srv.Close() defer srv.Close()
endpoint := srv.URL endpoint := srv.URL
opts := Options{Network: netmode.UnitTestNet} opts := Options{}
c, err := newClient(context.TODO(), endpoint, opts) c, err := newClient(context.TODO(), endpoint, opts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -1325,7 +1337,7 @@ func testRPCClient(t *testing.T, newClient func(context.Context, string, Options
defer srv.Close() defer srv.Close()
endpoint := srv.URL endpoint := srv.URL
opts := Options{Network: netmode.UnitTestNet} opts := Options{}
c, err := newClient(context.TODO(), endpoint, opts) c, err := newClient(context.TODO(), endpoint, opts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -1352,12 +1364,24 @@ func initTestServer(t *testing.T, resp string) *httptest.Server {
require.NoError(t, err) require.NoError(t, err)
for { for {
ws.SetReadDeadline(time.Now().Add(2 * time.Second)) ws.SetReadDeadline(time.Now().Add(2 * time.Second))
_, _, err = ws.ReadMessage() _, p, err := ws.ReadMessage()
if err != nil { if err != nil {
break break
} }
r := request.NewIn()
err = json.Unmarshal(p, r)
if err != nil {
t.Fatalf("Cannot decode request body: %s", req.Body)
}
var response string
switch r.Method {
case "getversion":
response = `{"id":1,"jsonrpc":"2.0","result":{"magic":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`
default:
response = resp
}
ws.SetWriteDeadline(time.Now().Add(2 * time.Second)) ws.SetWriteDeadline(time.Now().Add(2 * time.Second))
err = ws.WriteMessage(1, []byte(resp)) err = ws.WriteMessage(1, []byte(response))
if err != nil { if err != nil {
break break
} }
@ -1365,16 +1389,27 @@ func initTestServer(t *testing.T, resp string) *httptest.Server {
ws.Close() ws.Close()
return return
} }
requestHandler(t, w, resp) r := request.NewIn()
err := r.DecodeData(req.Body)
if err != nil {
t.Fatalf("Cannot decode request body: %s", req.Body)
}
requestHandler(t, r.Method, w, resp)
})) }))
return srv return srv
} }
func requestHandler(t *testing.T, w http.ResponseWriter, resp string) { func requestHandler(t *testing.T, method string, w http.ResponseWriter, resp string) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
_, err := w.Write([]byte(resp)) var response string
switch method {
case "getversion":
response = `{"id":1,"jsonrpc":"2.0","result":{"magic":42,"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`
default:
response = resp
}
_, err := w.Write([]byte(response))
if err != nil { if err != nil {
t.Fatalf("Error writing response: %s", err.Error()) t.Fatalf("Error writing response: %s", err.Error())
} }
@ -1399,10 +1434,8 @@ func TestCalculateValidUntilBlock(t *testing.T) {
case "getnextblockvalidators": case "getnextblockvalidators":
getValidatorsCalled++ getValidatorsCalled++
response = `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}` response = `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}`
default:
t.Fatalf("Bad request method: %s", r.Method)
} }
requestHandler(t, w, response) requestHandler(t, r.Method, w, response)
})) }))
defer srv.Close() defer srv.Close()
@ -1412,6 +1445,7 @@ func TestCalculateValidUntilBlock(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
require.NoError(t, c.Init())
validUntilBlock, err := c.CalculateValidUntilBlock() validUntilBlock, err := c.CalculateValidUntilBlock()
assert.NoError(t, err) assert.NoError(t, err)
@ -1426,3 +1460,32 @@ func TestCalculateValidUntilBlock(t *testing.T) {
assert.Equal(t, 2, getBlockCountCalled) assert.Equal(t, 2, getBlockCountCalled)
assert.Equal(t, 1, getValidatorsCalled) assert.Equal(t, 1, getValidatorsCalled)
} }
func TestGetNetwork(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// request handler already have `getversion` response wrapper
requestHandler(t, "getversion", w, "")
}))
defer srv.Close()
endpoint := srv.URL
opts := Options{}
t.Run("bad", func(t *testing.T) {
c, err := New(context.TODO(), endpoint, opts)
if err != nil {
t.Fatal(err)
}
// network was not initialised
require.Equal(t, netmode.Magic(0), c.GetNetwork())
require.Equal(t, false, c.initDone)
})
t.Run("good", func(t *testing.T) {
c, err := New(context.TODO(), endpoint, opts)
if err != nil {
t.Fatal(err)
}
require.NoError(t, c.Init())
require.Equal(t, netmode.UnitTestNet, c.GetNetwork())
})
}

View file

@ -69,6 +69,8 @@ const (
// NewWS returns a new WSClient ready to use (with established websocket // NewWS returns a new WSClient ready to use (with established websocket
// connection). You need to use websocket URL for it like `ws://1.2.3.4/ws`. // connection). You need to use websocket URL for it like `ws://1.2.3.4/ws`.
// You should call Init method to initialize network magic the client is
// operating on.
func NewWS(ctx context.Context, endpoint string, opts Options) (*WSClient, error) { func NewWS(ctx context.Context, endpoint string, opts Options) (*WSClient, error) {
cl, err := New(ctx, endpoint, opts) cl, err := New(ctx, endpoint, opts)
if err != nil { if err != nil {
@ -137,9 +139,9 @@ readloop:
var val interface{} var val interface{}
switch event { switch event {
case response.BlockEventID: case response.BlockEventID:
val = block.New(c.opts.Network) val = block.New(c.GetNetwork())
case response.TransactionEventID: case response.TransactionEventID:
val = &transaction.Transaction{Network: c.opts.Network} val = &transaction.Transaction{Network: c.GetNetwork()}
case response.NotificationEventID: case response.NotificationEventID:
val = new(state.NotificationEvent) val = new(state.NotificationEvent)
case response.ExecutionEventID: case response.ExecutionEventID:

View file

@ -18,7 +18,7 @@ import (
func TestWSClientClose(t *testing.T) { func TestWSClientClose(t *testing.T) {
srv := initTestServer(t, "") srv := initTestServer(t, "")
defer srv.Close() defer srv.Close()
wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{Network: netmode.UnitTestNet}) wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{})
require.NoError(t, err) require.NoError(t, err)
wsc.Close() wsc.Close()
} }
@ -43,8 +43,9 @@ func TestWSClientSubscription(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`) srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`)
defer srv.Close() defer srv.Close()
wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{Network: netmode.UnitTestNet}) wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wsc.Init())
id, err := f(wsc) id, err := f(wsc)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "55aaff00", id) require.Equal(t, "55aaff00", id)
@ -56,8 +57,9 @@ func TestWSClientSubscription(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "error":{"code":-32602,"message":"Invalid Params"}}`) srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "error":{"code":-32602,"message":"Invalid Params"}}`)
defer srv.Close() defer srv.Close()
wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{Network: netmode.UnitTestNet}) wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wsc.Init())
_, err = f(wsc) _, err = f(wsc)
require.Error(t, err) require.Error(t, err)
}) })
@ -105,8 +107,9 @@ func TestWSClientUnsubscription(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
srv := initTestServer(t, rc.response) srv := initTestServer(t, rc.response)
defer srv.Close() defer srv.Close()
wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{Network: netmode.UnitTestNet}) wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wsc.Init())
rc.code(t, wsc) rc.code(t, wsc)
}) })
} }
@ -139,8 +142,9 @@ func TestWSClientEvents(t *testing.T) {
} }
})) }))
wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{Network: netmode.UnitTestNet}) wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{})
require.NoError(t, err) require.NoError(t, err)
wsc.network = netmode.UnitTestNet
for range events { for range events {
select { select {
case _, ok = <-wsc.Notifications: case _, ok = <-wsc.Notifications:
@ -162,8 +166,9 @@ func TestWSExecutionVMStateCheck(t *testing.T) {
// Will answer successfully if request slips through. // Will answer successfully if request slips through.
srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`) srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`)
defer srv.Close() defer srv.Close()
wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{Network: netmode.UnitTestNet}) wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, wsc.Init())
filter := "NONE" filter := "NONE"
_, err = wsc.SubscribeForTransactionExecutions(&filter) _, err = wsc.SubscribeForTransactionExecutions(&filter)
require.Error(t, err) require.Error(t, err)
@ -326,8 +331,9 @@ func TestWSFilteredSubscriptions(t *testing.T) {
} }
})) }))
defer srv.Close() defer srv.Close()
wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{Network: netmode.UnitTestNet}) wsc, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{})
require.NoError(t, err) require.NoError(t, err)
wsc.network = netmode.UnitTestNet
c.clientCode(t, wsc) c.clientCode(t, wsc)
wsc.Close() wsc.Close()
}) })
@ -339,11 +345,12 @@ func TestNewWS(t *testing.T) {
defer srv.Close() defer srv.Close()
t.Run("good", func(t *testing.T) { t.Run("good", func(t *testing.T) {
_, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{Network: netmode.UnitTestNet}) c, err := NewWS(context.TODO(), httpURLtoWS(srv.URL), Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init())
}) })
t.Run("bad URL", func(t *testing.T) { t.Run("bad URL", func(t *testing.T) {
_, err := NewWS(context.TODO(), strings.Trim(srv.URL, "http://"), Options{Network: netmode.UnitTestNet}) _, err := NewWS(context.TODO(), strings.Trim(srv.URL, "http://"), Options{})
require.Error(t, err) require.Error(t, err)
}) })
} }

View file

@ -18,7 +18,7 @@ import (
type Invoke struct { type Invoke struct {
State string State string
GasConsumed int64 GasConsumed int64
Script string Script []byte
Stack []stackitem.Item Stack []stackitem.Item
FaultException string FaultException string
// Transaction represents transaction bytes. Use GetTransaction method to decode it. // Transaction represents transaction bytes. Use GetTransaction method to decode it.
@ -28,7 +28,7 @@ type Invoke struct {
type invokeAux struct { type invokeAux struct {
State string `json:"state"` State string `json:"state"`
GasConsumed int64 `json:"gasconsumed,string"` GasConsumed int64 `json:"gasconsumed,string"`
Script string `json:"script"` Script []byte `json:"script"`
Stack json.RawMessage `json:"stack"` Stack json.RawMessage `json:"stack"`
FaultException string `json:"exception,omitempty"` FaultException string `json:"exception,omitempty"`
Transaction string `json:"tx,omitempty"` Transaction string `json:"tx,omitempty"`

View file

@ -1,12 +1,15 @@
package result package result
import "github.com/nspcc-dev/neo-go/pkg/config/netmode"
type ( type (
// Version model used for reporting server version // Version model used for reporting server version
// info. // info.
Version struct { Version struct {
TCPPort uint16 `json:"tcpport"` Magic netmode.Magic `json:"magic"`
WSPort uint16 `json:"wsport,omitempty"` TCPPort uint16 `json:"tcpport"`
Nonce uint32 `json:"nonce"` WSPort uint16 `json:"wsport,omitempty"`
UserAgent string `json:"useragent"` Nonce uint32 `json:"nonce"`
UserAgent string `json:"useragent"`
} }
) )

View file

@ -5,7 +5,6 @@ import (
"encoding/hex" "encoding/hex"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/fee"
"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/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
@ -25,8 +24,9 @@ func TestClient_NEP5(t *testing.T) {
defer chain.Close() defer chain.Close()
defer rpcSrv.Shutdown() defer rpcSrv.Shutdown()
c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: netmode.UnitTestNet}) c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init())
h, err := util.Uint160DecodeStringLE(testContractHash) h, err := util.Uint160DecodeStringLE(testContractHash)
require.NoError(t, err) require.NoError(t, err)
@ -72,8 +72,9 @@ func TestAddNetworkFee(t *testing.T) {
defer chain.Close() defer chain.Close()
defer rpcSrv.Shutdown() defer rpcSrv.Shutdown()
c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init())
getAccounts := func(t *testing.T, n int) []*wallet.Account { getAccounts := func(t *testing.T, n int) []*wallet.Account {
accs := make([]*wallet.Account, n) accs := make([]*wallet.Account, n)
@ -199,8 +200,9 @@ func TestSignAndPushInvocationTx(t *testing.T) {
defer chain.Close() defer chain.Close()
defer rpcSrv.Shutdown() defer rpcSrv.Shutdown()
c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init())
priv := testchain.PrivateKey(0) priv := testchain.PrivateKey(0)
acc, err := wallet.NewAccountFromWIF(priv.WIF()) acc, err := wallet.NewAccountFromWIF(priv.WIF())
@ -222,8 +224,9 @@ func TestPing(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close() defer chain.Close()
c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init())
require.NoError(t, c.Ping()) require.NoError(t, c.Ping())
require.NoError(t, rpcSrv.Shutdown()) require.NoError(t, rpcSrv.Shutdown())
@ -236,8 +239,9 @@ func TestCreateTxFromScript(t *testing.T) {
defer chain.Close() defer chain.Close()
defer rpcSrv.Shutdown() defer rpcSrv.Shutdown()
c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init())
priv := testchain.PrivateKey(0) priv := testchain.PrivateKey(0)
acc, err := wallet.NewAccountFromWIF(priv.WIF()) acc, err := wallet.NewAccountFromWIF(priv.WIF())
@ -265,8 +269,9 @@ func TestCreateNEP5TransferTx(t *testing.T) {
defer chain.Close() defer chain.Close()
defer rpcSrv.Shutdown() defer rpcSrv.Shutdown()
c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, c.Init())
priv := testchain.PrivateKeyByID(0) priv := testchain.PrivateKeyByID(0)
acc, err := wallet.NewAccountFromWIF(priv.WIF()) acc, err := wallet.NewAccountFromWIF(priv.WIF())

View file

@ -466,6 +466,7 @@ func (s *Server) getVersion(_ request.Params) (interface{}, *response.Error) {
return nil, response.NewInternalServerError("Cannot fetch tcp port", err) return nil, response.NewInternalServerError("Cannot fetch tcp port", err)
} }
return result.Version{ return result.Version{
Magic: s.network,
TCPPort: port, TCPPort: port,
Nonce: s.coreServer.ID(), Nonce: s.coreServer.ID(),
UserAgent: s.coreServer.UserAgent, UserAgent: s.coreServer.UserAgent,
@ -977,7 +978,7 @@ func (s *Server) invokescript(reqParams request.Params) (interface{}, *response.
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
script, err := reqParams[0].GetBytesHex() script, err := reqParams[0].GetBytesBase64()
if err != nil { if err != nil {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
@ -1011,7 +1012,7 @@ func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *resu
result := &result.Invoke{ result := &result.Invoke{
State: vm.State().String(), State: vm.State().String(),
GasConsumed: vm.GasConsumed(), GasConsumed: vm.GasConsumed(),
Script: hex.EncodeToString(script), Script: script,
Stack: vm.Estack().ToArray(), Stack: vm.Estack().ToArray(),
FaultException: faultException, FaultException: faultException,
} }

View file

@ -60,6 +60,7 @@ const deploymentTxHash = "59f7b22b90e26f883a56b916c1580e3ee4f13caded686353cd7757
const verifyContractHash = "c1213693b22cb0454a436d6e0bd76b8c0a3bfdf7" const verifyContractHash = "c1213693b22cb0454a436d6e0bd76b8c0a3bfdf7"
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740" const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
const testVerifyContractAVM = "VwcADBQBDAMOBQYMDQIODw0DDgcJAAAAANswcGgRVUH4J+yMIaonBwAAABFADBQNDwMCCQACAQMHAwQFAgEADgYMCdswcWkRVUH4J+yMIaonBwAAABJAE0A="
var rpcTestCases = map[string][]rpcTestCase{ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
@ -537,7 +538,7 @@ var rpcTestCases = map[string][]rpcTestCase{
check: func(t *testing.T, e *executor, inv interface{}) { check: func(t *testing.T, e *executor, inv interface{}) {
res, ok := inv.(*result.Invoke) res, ok := inv.(*result.Invoke)
require.True(t, ok) require.True(t, ok)
assert.NotEqual(t, "", res.Script) assert.NotNil(t, res.Script)
assert.NotEqual(t, "", res.State) assert.NotEqual(t, "", res.State)
assert.NotEqual(t, 0, res.GasConsumed) assert.NotEqual(t, 0, res.GasConsumed)
}, },
@ -566,7 +567,7 @@ var rpcTestCases = map[string][]rpcTestCase{
"invokescript": { "invokescript": {
{ {
name: "positive", name: "positive",
params: `["51c56b0d48656c6c6f2c20776f726c6421680f4e656f2e52756e74696d652e4c6f67616c7566"]`, params: `["UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`,
result: func(e *executor) interface{} { return &result.Invoke{} }, result: func(e *executor) interface{} { return &result.Invoke{} },
check: func(t *testing.T, e *executor, inv interface{}) { check: func(t *testing.T, e *executor, inv interface{}) {
res, ok := inv.(*result.Invoke) res, ok := inv.(*result.Invoke)
@ -578,8 +579,8 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
name: "positive, good witness", 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 // script is base64-encoded `test_verify.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, testVerifyContractAVM),
result: func(e *executor) interface{} { return &result.Invoke{} }, result: func(e *executor) interface{} { return &result.Invoke{} },
check: func(t *testing.T, e *executor, inv interface{}) { check: func(t *testing.T, e *executor, inv interface{}) {
res, ok := inv.(*result.Invoke) res, ok := inv.(*result.Invoke)
@ -591,7 +592,7 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
name: "positive, bad witness of second hash", name: "positive, bad witness of second hash",
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, testVerifyContractAVM),
result: func(e *executor) interface{} { return &result.Invoke{} }, result: func(e *executor) interface{} { return &result.Invoke{} },
check: func(t *testing.T, e *executor, inv interface{}) { check: func(t *testing.T, e *executor, inv interface{}) {
res, ok := inv.(*result.Invoke) res, ok := inv.(*result.Invoke)
@ -603,7 +604,7 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
name: "positive, no good hashes", name: "positive, no good hashes",
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340"]`, params: fmt.Sprintf(`["%s"]`, testVerifyContractAVM),
result: func(e *executor) interface{} { return &result.Invoke{} }, result: func(e *executor) interface{} { return &result.Invoke{} },
check: func(t *testing.T, e *executor, inv interface{}) { check: func(t *testing.T, e *executor, inv interface{}) {
res, ok := inv.(*result.Invoke) res, ok := inv.(*result.Invoke)
@ -615,7 +616,7 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
name: "positive, bad hashes witness", name: "positive, bad hashes witness",
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, testVerifyContractAVM),
result: func(e *executor) interface{} { return &result.Invoke{} }, result: func(e *executor) interface{} { return &result.Invoke{} },
check: func(t *testing.T, e *executor, inv interface{}) { check: func(t *testing.T, e *executor, inv interface{}) {
res, ok := inv.(*result.Invoke) res, ok := inv.(*result.Invoke)