diff --git a/cli/wallet/multisig.go b/cli/wallet/multisig.go index 2e1bbf3c9..3165de97f 100644 --- a/cli/wallet/multisig.go +++ b/cli/wallet/multisig.go @@ -89,9 +89,12 @@ func signMultisig(ctx *cli.Context) error { if err != nil { return err } - if err := c.SendRawTransaction(tx); err != nil { + res, err := c.SendRawTransaction(tx) + if err != nil { return cli.NewExitError(err, 1) } + fmt.Println(res.StringLE()) + return nil } fmt.Println(tx.Hash().StringLE()) diff --git a/cli/wallet/nep5.go b/cli/wallet/nep5.go index 58c2cfb22..da9f9c3c2 100644 --- a/cli/wallet/nep5.go +++ b/cli/wallet/nep5.go @@ -384,9 +384,12 @@ func transferNEP5(ctx *cli.Context) error { } } else { _ = acc.SignTx(tx) - if err := c.SendRawTransaction(tx); err != nil { + res, err := c.SendRawTransaction(tx) + if err != nil { return cli.NewExitError(err, 1) } + fmt.Println(res.StringLE()) + return nil } fmt.Println(tx.Hash().StringLE()) diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 673503cce..1d7d6de3c 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -159,11 +159,7 @@ func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token util.U return util.Uint256{}, fmt.Errorf("can't sign tx: %v", err) } - if err := c.SendRawTransaction(tx); err != nil { - return util.Uint256{}, err - } - - return tx.Hash(), nil + return c.SendRawTransaction(tx) } func topIntFromStack(st []smartcontract.Parameter) (int64, error) { diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index 00165bd47..3bd80382e 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -2,6 +2,7 @@ package client import ( "encoding/hex" + "fmt" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" @@ -385,18 +386,15 @@ func (c *Client) invokeSomething(method string, p request.RawParams, cosigners [ // The given hex string needs to be signed with a keypair. // When the result of the response object is true, the TX has successfully // been broadcasted to the network. -func (c *Client) SendRawTransaction(rawTX *transaction.Transaction) error { +func (c *Client) SendRawTransaction(rawTX *transaction.Transaction) (util.Uint256, error) { var ( params = request.NewRawParams(hex.EncodeToString(rawTX.Bytes())) - resp bool + resp = new(result.RelayResult) ) - if err := c.performRequest("sendrawtransaction", params, &resp); err != nil { - return err + if err := c.performRequest("sendrawtransaction", params, resp); err != nil { + return util.Uint256{}, err } - if !resp { - return errors.New("sendrawtransaction returned false") - } - return nil + return resp.Hash, nil } // SubmitBlock broadcasts a raw block over the NEO network. @@ -453,11 +451,13 @@ func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sys return txHash, errors.Wrap(err, "failed to sign tx") } txHash = tx.Hash() - err = c.SendRawTransaction(tx) - + actualHash, err := c.SendRawTransaction(tx) if err != nil { return txHash, errors.Wrap(err, "failed sendning tx") } + if !actualHash.Equals(txHash) { + return actualHash, fmt.Errorf("sent and actual tx hashes mismatch:\n\tsent: %v\n\tactual: %v", txHash.StringLE(), actualHash.StringLE()) + } return txHash, nil } diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 38365491c..64c890e8b 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" + "fmt" "math/big" "net/http" "net/http/httptest" @@ -699,12 +700,15 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ { name: "positive", invoke: func(c *Client) (interface{}, error) { - return nil, c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)) + return c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)) }, - serverResponse: `{"jsonrpc":"2.0","id":1,"result":true}`, + serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"hash":"0x72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875"}}`, result: func(c *Client) interface{} { - // no error expected - return nil + h, err := util.Uint256DecodeStringLE("72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875") + if err != nil { + panic(fmt.Errorf("can't decode `sendrawtransaction` result hash: %v", err)) + } + return h }, }, }, @@ -830,7 +834,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ { name: "sendrawtransaction_bad_server_answer", invoke: func(c *Client) (interface{}, error) { - return nil, c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)) + return c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)) }, }, { @@ -986,7 +990,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ { name: "sendrawtransaction_invalid_params_error", invoke: func(c *Client) (interface{}, error) { - return nil, c.SendRawTransaction(&transaction.Transaction{}) + return c.SendRawTransaction(&transaction.Transaction{}) }, }, { @@ -1168,7 +1172,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ { name: "sendrawtransaction_unmarshalling_error", invoke: func(c *Client) (interface{}, error) { - return nil, c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)) + return c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)) }, }, { diff --git a/pkg/rpc/response/result/relay_result.go b/pkg/rpc/response/result/relay_result.go new file mode 100644 index 000000000..492d16377 --- /dev/null +++ b/pkg/rpc/response/result/relay_result.go @@ -0,0 +1,8 @@ +package result + +import "github.com/nspcc-dev/neo-go/pkg/util" + +// RelayResult ia a result of `sendrawtransaction` or `submitblock` RPC calls. +type RelayResult struct { + Hash util.Uint256 `json:"hash"` +} diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 60e4f1086..999b40c30 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -921,7 +921,9 @@ func (s *Server) sendrawtransaction(reqParams request.Params) (interface{}, *res relayReason := s.coreServer.RelayTxn(tx) switch relayReason { case network.RelaySucceed: - results = true + results = result.RelayResult{ + Hash: tx.Hash(), + } case network.RelayAlreadyExists: resultsErr = response.ErrAlreadyExists case network.RelayOutOfMemory: diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 7223259f5..bbf257017 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -590,9 +590,13 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "positive", params: `["000a000000aa8acf859d4fe402b34e673f2156821796a488eb80969800000000009269130000000000b00400000001aa8acf859d4fe402b34e673f2156821796a488eb015d0300e87648170000000c1478ba4c24009fe510e136c9995a2e05215e1be4dc0c14aa8acf859d4fe402b34e673f2156821796a488eb13c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c4040719393aa590d962cb5a48e16360ac75a6c358c9699e9f1a853afede4d601b6783e28f5ec74542aaf59519e76830ba9d267656db324461fdb08d1d51521e103290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b4195440d78"]`, - result: func(e *executor) interface{} { - v := true - return &v + result: func(e *executor) interface{} { return &result.RelayResult{} }, + check: func(t *testing.T, e *executor, inv interface{}) { + res, ok := inv.(*result.RelayResult) + require.True(t, ok) + expectedHash, err := util.Uint256DecodeStringLE("72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875") + require.NoError(t, err) + assert.Equal(t, expectedHash, res.Hash) }, }, {