diff --git a/cli/candidate_test.go b/cli/candidate_test.go index 4600e2644..4358b992b 100644 --- a/cli/candidate_test.go +++ b/cli/candidate_test.go @@ -17,7 +17,7 @@ func TestRegisterCandidate(t *testing.T) { e.In.WriteString("one\r") 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, "neo:"+validatorPriv.Address()+":10", @@ -26,7 +26,7 @@ func TestRegisterCandidate(t *testing.T) { e.In.WriteString("one\r") e.Run(t, "neo-go", "wallet", "candidate", "register", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, "--wallet", validatorWallet, "--address", validatorPriv.Address()) e.checkTxPersisted(t) @@ -40,7 +40,7 @@ func TestRegisterCandidate(t *testing.T) { t.Run("Vote", func(t *testing.T) { e.In.WriteString("one\r") e.Run(t, "neo-go", "wallet", "candidate", "vote", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, "--wallet", validatorWallet, "--address", validatorPriv.Address(), "--candidate", hex.EncodeToString(validatorPriv.PublicKey().Bytes())) @@ -55,7 +55,7 @@ func TestRegisterCandidate(t *testing.T) { e.In.WriteString("one\r") e.Run(t, "neo-go", "wallet", "candidate", "unregister", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, "--wallet", validatorWallet, "--address", validatorPriv.Address()) e.checkTxPersisted(t) diff --git a/cli/contract_test.go b/cli/contract_test.go index 99c93ea4b..002274a9f 100644 --- a/cli/contract_test.go +++ b/cli/contract_test.go @@ -40,7 +40,7 @@ func TestComlileAndInvokeFunction(t *testing.T) { e.In.WriteString("one\r") e.Run(t, "neo-go", "contract", "deploy", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, "--wallet", validatorWallet, "--address", validatorAddr, "--in", nefName, "--manifest", manifestName) @@ -53,7 +53,7 @@ func TestComlileAndInvokeFunction(t *testing.T) { e.In.WriteString("one\r") e.Run(t, "neo-go", "contract", "testinvokefunction", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, h.StringLE(), "getValue") res := new(result.Invoke) @@ -84,7 +84,7 @@ func TestComlileAndInvokeFunction(t *testing.T) { e.In.WriteString("one\r") e.Run(t, "neo-go", "contract", "invokefunction", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, "--wallet", validatorWallet, "--address", validatorAddr, h.StringLE(), "update", "bytes:"+hex.EncodeToString(realNef.Script), @@ -94,7 +94,7 @@ func TestComlileAndInvokeFunction(t *testing.T) { e.In.WriteString("one\r") 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") res := new(result.Invoke) diff --git a/cli/multisig_test.go b/cli/multisig_test.go index 1e3e87093..66d093067 100644 --- a/cli/multisig_test.go +++ b/cli/multisig_test.go @@ -53,7 +53,7 @@ func TestSignMultisigTx(t *testing.T) { // Transfer funds to the multisig. e.In.WriteString("one\r") 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, "neo:"+multisigAddr+":4", @@ -68,14 +68,14 @@ func TestSignMultisigTx(t *testing.T) { defer os.Remove(txPath) e.In.WriteString("pass\r") 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, "--to", priv.Address(), "--token", "neo", "--amount", "1", "--out", txPath) e.In.WriteString("pass\r") 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, "--in", txPath, "--out", txPath) e.checkTxPersisted(t) @@ -88,7 +88,7 @@ func TestSignMultisigTx(t *testing.T) { t.Run("via invokefunction", func(t *testing.T) { e.In.WriteString("pass\r") e.Run(t, "neo-go", "contract", "invokefunction", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, "--wallet", wallet1Path, "--address", multisigAddr, "--out", txPath, client.NeoContractHash.StringLE(), "transfer", @@ -99,7 +99,7 @@ func TestSignMultisigTx(t *testing.T) { e.In.WriteString("pass\r") 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, "--in", txPath, "--out", txPath) e.checkTxPersisted(t) diff --git a/cli/nep5_test.go b/cli/nep5_test.go index 3c07ffceb..cdd7e8b42 100644 --- a/cli/nep5_test.go +++ b/cli/nep5_test.go @@ -109,7 +109,6 @@ func TestNEP5Transfer(t *testing.T) { defer e.Close(t) args := []string{ "neo-go", "wallet", "nep5", "transfer", - "--unittest", "--rpc-endpoint", "http://" + e.RPC.Addr, "--wallet", validatorWallet, "--from", validatorAddr, @@ -141,7 +140,7 @@ func TestNEP5MultiTransfer(t *testing.T) { defer e.Close(t) args := []string{ "neo-go", "wallet", "nep5", "multitransfer", - "--unittest", "--rpc-endpoint", "http://" + e.RPC.Addr, + "--rpc-endpoint", "http://" + e.RPC.Addr, "--wallet", validatorWallet, "--from", validatorAddr, "neo:" + privs[0].Address() + ":42", diff --git a/cli/options/options.go b/cli/options/options.go index 19f4c7e4e..56a65db50 100644 --- a/cli/options/options.go +++ b/cli/options/options.go @@ -34,10 +34,6 @@ var RPC = []cli.Flag{ Name: "timeout, s", 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'") @@ -73,7 +69,11 @@ func GetRPCClient(gctx context.Context, ctx *cli.Context) (*client.Client, cli.E if len(endpoint) == 0 { 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 { return nil, cli.NewExitError(err, 1) } diff --git a/cli/options/options_test.go b/cli/options/options_test.go index bd92c6416..ba0c48ecf 100644 --- a/cli/options/options_test.go +++ b/cli/options/options_test.go @@ -2,10 +2,13 @@ package options import ( "flag" + "net/http" + "net/http/httptest" "testing" "time" "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/stretchr/testify/require" "github.com/urfave/cli" ) @@ -56,6 +59,17 @@ func TestGetTimeoutContext(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) { set := flag.NewFlagSet("flagSet", flag.ExitOnError) ctx := cli.NewContext(cli.NewApp(), set, nil) @@ -66,7 +80,7 @@ func TestGetRPCClient(t *testing.T) { t.Run("success", func(t *testing.T) { set := flag.NewFlagSet("flagSet", flag.ExitOnError) - set.String(RPCEndpointFlag, "http://localhost:50333", "") + set.String(RPCEndpointFlag, srv.URL, "") ctx := cli.NewContext(cli.NewApp(), set, nil) gctx, _ := GetTimeoutContext(ctx) _, ec := GetRPCClient(gctx, ctx) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index fbd6098be..bb4054401 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -487,6 +487,9 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { if err != nil { return err } + if err = c.Init(); err != nil { + return err + } resp, err = c.InvokeFunction(script, operation, params, cosigners) if err != nil { diff --git a/cli/wallet_test.go b/cli/wallet_test.go index b2707f257..bae2449d6 100644 --- a/cli/wallet_test.go +++ b/cli/wallet_test.go @@ -176,7 +176,7 @@ func TestClaimGas(t *testing.T) { balanceBefore := e.Chain.GetUtilityTokenBalance(validatorHash) e.In.WriteString("one\r") e.Run(t, "neo-go", "wallet", "claim", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, "--wallet", validatorWallet, "--address", validatorAddr) tx, end := e.checkTxPersisted(t) @@ -195,7 +195,7 @@ func TestImportDeployed(t *testing.T) { e.In.WriteString("one\r") e.Run(t, "neo-go", "contract", "deploy", - "--unittest", "--rpc-endpoint", "http://"+e.RPC.Addr, + "--rpc-endpoint", "http://"+e.RPC.Addr, "--wallet", validatorWallet, "--address", validatorAddr, "--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.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(), "--contract", h.StringLE()) @@ -232,7 +232,7 @@ func TestImportDeployed(t *testing.T) { t.Run("Sign", func(t *testing.T) { e.In.WriteString("one\r") 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, "neo:"+contractAddr+":10", "gas:"+contractAddr+":10") @@ -243,7 +243,7 @@ func TestImportDeployed(t *testing.T) { e.In.WriteString("pass\r") 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, "--to", privTo.Address(), "--token", "neo", "--amount", "1") e.checkTxPersisted(t) diff --git a/pkg/rpc/client/client.go b/pkg/rpc/client/client.go index 1eae743fa..44fe25f29 100644 --- a/pkg/rpc/client/client.go +++ b/pkg/rpc/client/client.go @@ -29,6 +29,8 @@ const ( type Client struct { cli *http.Client endpoint *url.URL + network netmode.Magic + initDone bool ctx context.Context opts Options requestF func(*request.Raw) (*response.Raw, error) @@ -46,7 +48,6 @@ type Options struct { CACert string DialTimeout time.Duration RequestTimeout time.Duration - Network netmode.Magic } // cache stores cache values for the RPC client methods @@ -61,7 +62,8 @@ type calculateValidUntilBlockCache struct { 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) { url, err := url.Parse(endpoint) if err != nil { @@ -99,6 +101,19 @@ func New(ctx context.Context, endpoint string, opts Options) (*Client, error) { 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 { var r = request.Raw{ JSONRPC: request.JSONRPCVersion, diff --git a/pkg/rpc/client/doc_test.go b/pkg/rpc/client/doc_test.go index e3db66617..496ca8d4d 100644 --- a/pkg/rpc/client/doc_test.go +++ b/pkg/rpc/client/doc_test.go @@ -5,14 +5,13 @@ import ( "fmt" "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/rpc/client" ) func Example() { endpoint := "http://seed5.bridgeprotocol.io:10332" - opts := client.Options{Network: netmode.MainNet} + opts := client.Options{} c, err := client.New(context.TODO(), endpoint, opts) if err != nil { diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index e92ee09ca..3de9fbae7 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -152,7 +152,8 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, gas int64, recip } // 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, cosigners ...transaction.Signer) (*transaction.Transaction, error) { from, err := address.StringToUint160(acc.Address) @@ -172,7 +173,10 @@ func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee, 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.ValidUntilBlock, err = c.CalculateValidUntilBlock() diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index e9122fcde..33a596c01 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -5,6 +5,7 @@ import ( "errors" "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/block" "github.com/nspcc-dev/neo-go/pkg/core/fee" @@ -20,6 +21,8 @@ import ( "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. func (c *Client) GetApplicationLog(hash util.Uint256) (*state.AppExecResult, error) { var ( @@ -50,12 +53,14 @@ func (c *Client) GetBlockCount() (uint32, error) { 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) { 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) { return c.getBlock(request.NewRawParams(hash.StringLE())) } @@ -66,6 +71,9 @@ func (c *Client) getBlock(params request.RawParams) (*block.Block, error) { err error b *block.Block ) + if !c.initDone { + return nil, errNetworkNotInitialized + } if err = c.performRequest("getblock", params, &resp); err != nil { return nil, err } @@ -74,7 +82,7 @@ func (c *Client) getBlock(params request.RawParams) (*block.Block, error) { return nil, err } r := io.NewBinReaderFromBuf(blockBytes) - b = block.New(c.opts.Network) + b = block.New(c.GetNetwork()) b.DecodeBinary(r) if r.Err != nil { 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 -// 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. func (c *Client) GetBlockByIndexVerbose(index uint32) (*result.Block, error) { return c.getBlockVerbose(request.NewRawParams(index, 1)) } // 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) { 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{} 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 { 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 -// 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) { var ( params = request.NewRawParams(hash.StringLE()) resp string h *block.Header ) + if !c.initDone { + return nil, errNetworkNotInitialized + } if err := c.performRequest("getblockheader", params, &resp); err != nil { return nil, err } @@ -136,7 +151,7 @@ func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) { } r := io.NewBinReaderFromBuf(headerBytes) h = new(block.Header) - h.Network = c.opts.Network + h.Network = c.GetNetwork() h.DecodeBinary(r) if r.Err != nil { return nil, r.Err @@ -271,13 +286,17 @@ func (c *Client) GetRawMemPool() ([]util.Uint256, error) { 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) { var ( params = request.NewRawParams(hash.StringLE()) resp string err error ) + if !c.initDone { + return nil, errNetworkNotInitialized + } if err = c.performRequest("getrawtransaction", params, &resp); err != nil { return nil, err } @@ -285,7 +304,7 @@ func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction, if err != nil { return nil, err } - tx, err := transaction.NewTransactionFromBytes(c.opts.Network, txBytes) + tx, err := transaction.NewTransactionFromBytes(c.GetNetwork(), txBytes) if err != nil { return nil, err } @@ -293,7 +312,8 @@ func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction, } // 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. func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.TransactionOutputRaw, error) { var ( @@ -301,7 +321,10 @@ func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.Transactio resp = &result.TransactionOutputRaw{} 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 { return nil, err } @@ -569,3 +592,8 @@ func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs tx.NetworkFee += int64(size)*fee + extraFee return nil } + +// GetNetwork returns the network magic of the RPC node client connected to. +func (c *Client) GetNetwork() netmode.Magic { + return c.network +} diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index bc1a733d0..1be95bba9 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "encoding/hex" + "encoding/json" "fmt" "math/big" "net/http" @@ -1247,12 +1248,6 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ return c.GetNextBlockValidators() }, }, - { - name: "getversion_unmarshalling_error", - invoke: func(c *Client) (interface{}, error) { - return c.GetVersion() - }, - }, { name: "invokefunction_unmarshalling_error", invoke: func(c *Client) (interface{}, error) { @@ -1293,13 +1288,17 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ func TestRPCClients(t *testing.T) { t.Run("Client", func(t *testing.T) { 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) { testRPCClient(t, func(ctx context.Context, endpoint string, opts Options) (*Client, error) { wsc, err := NewWS(ctx, httpURLtoWS(endpoint), opts) require.NoError(t, err) + require.NoError(t, wsc.Init()) return &wsc.Client, nil }) }) @@ -1314,7 +1313,7 @@ func testRPCClient(t *testing.T, newClient func(context.Context, string, Options defer srv.Close() endpoint := srv.URL - opts := Options{Network: netmode.UnitTestNet} + opts := Options{} c, err := newClient(context.TODO(), endpoint, opts) if err != nil { t.Fatal(err) @@ -1338,7 +1337,7 @@ func testRPCClient(t *testing.T, newClient func(context.Context, string, Options defer srv.Close() endpoint := srv.URL - opts := Options{Network: netmode.UnitTestNet} + opts := Options{} c, err := newClient(context.TODO(), endpoint, opts) if err != nil { t.Fatal(err) @@ -1365,12 +1364,24 @@ func initTestServer(t *testing.T, resp string) *httptest.Server { require.NoError(t, err) for { ws.SetReadDeadline(time.Now().Add(2 * time.Second)) - _, _, err = ws.ReadMessage() + _, p, err := ws.ReadMessage() if err != nil { 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)) - err = ws.WriteMessage(1, []byte(resp)) + err = ws.WriteMessage(1, []byte(response)) if err != nil { break } @@ -1378,16 +1389,27 @@ func initTestServer(t *testing.T, resp string) *httptest.Server { ws.Close() 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 } -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") - _, 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 { t.Fatalf("Error writing response: %s", err.Error()) } @@ -1412,10 +1434,8 @@ func TestCalculateValidUntilBlock(t *testing.T) { case "getnextblockvalidators": 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}]}` - default: - t.Fatalf("Bad request method: %s", r.Method) } - requestHandler(t, w, response) + requestHandler(t, r.Method, w, response) })) defer srv.Close() @@ -1425,6 +1445,7 @@ func TestCalculateValidUntilBlock(t *testing.T) { if err != nil { t.Fatal(err) } + require.NoError(t, c.Init()) validUntilBlock, err := c.CalculateValidUntilBlock() assert.NoError(t, err) @@ -1439,3 +1460,32 @@ func TestCalculateValidUntilBlock(t *testing.T) { assert.Equal(t, 2, getBlockCountCalled) 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()) + }) +} diff --git a/pkg/rpc/client/wsclient.go b/pkg/rpc/client/wsclient.go index 9b7a7eb73..266257ef1 100644 --- a/pkg/rpc/client/wsclient.go +++ b/pkg/rpc/client/wsclient.go @@ -69,6 +69,8 @@ const ( // 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`. +// 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) { cl, err := New(ctx, endpoint, opts) if err != nil { @@ -137,9 +139,9 @@ readloop: var val interface{} switch event { case response.BlockEventID: - val = block.New(c.opts.Network) + val = block.New(c.GetNetwork()) case response.TransactionEventID: - val = &transaction.Transaction{Network: c.opts.Network} + val = &transaction.Transaction{Network: c.GetNetwork()} case response.NotificationEventID: val = new(state.NotificationEvent) case response.ExecutionEventID: diff --git a/pkg/rpc/client/wsclient_test.go b/pkg/rpc/client/wsclient_test.go index 7af7b8ca5..d9fdd30ee 100644 --- a/pkg/rpc/client/wsclient_test.go +++ b/pkg/rpc/client/wsclient_test.go @@ -18,7 +18,7 @@ import ( func TestWSClientClose(t *testing.T) { srv := initTestServer(t, "") 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) wsc.Close() } @@ -43,8 +43,9 @@ func TestWSClientSubscription(t *testing.T) { t.Run(name, func(t *testing.T) { srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`) 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, wsc.Init()) id, err := f(wsc) require.NoError(t, err) require.Equal(t, "55aaff00", id) @@ -56,8 +57,9 @@ func TestWSClientSubscription(t *testing.T) { t.Run(name, func(t *testing.T) { srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "error":{"code":-32602,"message":"Invalid Params"}}`) 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, wsc.Init()) _, err = f(wsc) require.Error(t, err) }) @@ -105,8 +107,9 @@ func TestWSClientUnsubscription(t *testing.T) { t.Run(name, func(t *testing.T) { srv := initTestServer(t, rc.response) 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, wsc.Init()) 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) + wsc.network = netmode.UnitTestNet for range events { select { case _, ok = <-wsc.Notifications: @@ -162,8 +166,9 @@ func TestWSExecutionVMStateCheck(t *testing.T) { // Will answer successfully if request slips through. srv := initTestServer(t, `{"jsonrpc": "2.0", "id": 1, "result": "55aaff00"}`) 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, wsc.Init()) filter := "NONE" _, err = wsc.SubscribeForTransactionExecutions(&filter) require.Error(t, err) @@ -326,8 +331,9 @@ func TestWSFilteredSubscriptions(t *testing.T) { } })) 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) + wsc.network = netmode.UnitTestNet c.clientCode(t, wsc) wsc.Close() }) @@ -339,11 +345,12 @@ func TestNewWS(t *testing.T) { defer srv.Close() 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, c.Init()) }) 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) }) } diff --git a/pkg/rpc/server/client_test.go b/pkg/rpc/server/client_test.go index 11f845ead..f5e8adc96 100644 --- a/pkg/rpc/server/client_test.go +++ b/pkg/rpc/server/client_test.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "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/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" @@ -25,8 +24,9 @@ func TestClient_NEP5(t *testing.T) { defer chain.Close() 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, c.Init()) h, err := util.Uint160DecodeStringLE(testContractHash) require.NoError(t, err) @@ -72,8 +72,9 @@ func TestAddNetworkFee(t *testing.T) { defer chain.Close() 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, c.Init()) getAccounts := func(t *testing.T, n int) []*wallet.Account { accs := make([]*wallet.Account, n) @@ -199,8 +200,9 @@ func TestSignAndPushInvocationTx(t *testing.T) { defer chain.Close() 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, c.Init()) priv := testchain.PrivateKey(0) acc, err := wallet.NewAccountFromWIF(priv.WIF()) @@ -222,8 +224,9 @@ func TestPing(t *testing.T) { chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) 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, c.Init()) require.NoError(t, c.Ping()) require.NoError(t, rpcSrv.Shutdown()) @@ -236,8 +239,9 @@ func TestCreateTxFromScript(t *testing.T) { defer chain.Close() 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, c.Init()) priv := testchain.PrivateKey(0) acc, err := wallet.NewAccountFromWIF(priv.WIF()) @@ -265,8 +269,9 @@ func TestCreateNEP5TransferTx(t *testing.T) { defer chain.Close() 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, c.Init()) priv := testchain.PrivateKeyByID(0) acc, err := wallet.NewAccountFromWIF(priv.WIF())