diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index fcb2f2c2b..31e8c09cf 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -364,7 +364,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error { operation string params = make([]smartcontract.Parameter, 0) paramsStart = 1 - resp *response.InvokeScript + resp *response.InvokeResult wif *keys.WIF ) @@ -414,10 +414,10 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error { return cli.NewExitError(err, 1) } if signAndPush { - if len(resp.Result.Script) == 0 { + if len(resp.Script) == 0 { return cli.NewExitError(errors.New("no script returned from the RPC node"), 1) } - script, err := hex.DecodeString(resp.Result.Script) + script, err := hex.DecodeString(resp.Script) if err != nil { return cli.NewExitError(fmt.Errorf("bad script returned from the RPC node: %v", err), 1) } @@ -427,7 +427,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error { } fmt.Printf("Sent invocation transaction %s\n", txHash.StringLE()) } else { - b, err := json.MarshalIndent(resp.Result, "", " ") + b, err := json.MarshalIndent(resp, "", " ") if err != nil { return cli.NewExitError(err, 1) } @@ -464,7 +464,7 @@ func testInvokeScript(ctx *cli.Context) error { return cli.NewExitError(err, 1) } - b, err = json.MarshalIndent(resp.Result, "", " ") + b, err = json.MarshalIndent(resp, "", " ") if err != nil { return cli.NewExitError(err, 1) } diff --git a/pkg/rpc/client/client.go b/pkg/rpc/client/client.go index 58e1e95ad..8f9298e70 100644 --- a/pkg/rpc/client/client.go +++ b/pkg/rpc/client/client.go @@ -15,6 +15,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/rpc/request" + "github.com/CityOfZion/neo-go/pkg/rpc/response" "github.com/CityOfZion/neo-go/pkg/util" "github.com/pkg/errors" ) @@ -162,13 +163,10 @@ func (c *Client) CalculateInputs(address string, asset util.Uint256, cost util.F var utxos state.UnspentBalances resp, err := c.GetUnspents(address) - if err != nil || resp.Error != nil { - if err == nil { - err = fmt.Errorf("remote returned %d: %s", resp.Error.Code, resp.Error.Message) - } + if err != nil { return nil, util.Fixed8(0), errors.Wrapf(err, "cannot get balance for address %v", address) } - for _, ubi := range resp.Result.Balance { + for _, ubi := range resp.Balance { if asset.Equals(ubi.AssetHash) { utxos = ubi.Unspents break @@ -187,6 +185,7 @@ func (c *Client) performRequest(method string, p request.RawParams, v interface{ ID: 1, } buf = new(bytes.Buffer) + raw = &response.Raw{} ) if err := json.NewEncoder(buf).Encode(r); err != nil { @@ -205,9 +204,17 @@ func (c *Client) performRequest(method string, p request.RawParams, v interface{ // The node might send us proper JSON anyway, so look there first and if // it parses, then it has more relevant data than HTTP error code. - err = json.NewDecoder(resp.Body).Decode(v) - if resp.StatusCode != http.StatusOK && err != nil { + err = json.NewDecoder(resp.Body).Decode(raw) + if err == nil { + if raw.Error != nil { + err = raw.Error + } else { + err = json.Unmarshal(raw.Result, v) + } + } else if resp.StatusCode != http.StatusOK { err = fmt.Errorf("HTTP %d/%s", resp.StatusCode, http.StatusText(resp.StatusCode)) + } else { + err = errors.Wrap(err, "JSON decoding") } return err diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index d4e2c1e0b..44f69e419 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -2,13 +2,13 @@ package client import ( "encoding/hex" - "fmt" "github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/rpc/request" "github.com/CityOfZion/neo-go/pkg/rpc/response" + "github.com/CityOfZion/neo-go/pkg/rpc/response/result" "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/util" "github.com/pkg/errors" @@ -32,10 +32,10 @@ import ( // } // GetAccountState returns detailed information about a NEO account. -func (c *Client) GetAccountState(address string) (*response.AccountState, error) { +func (c *Client) GetAccountState(address string) (*result.AccountState, error) { var ( params = request.NewRawParams(address) - resp = &response.AccountState{} + resp = &result.AccountState{} ) if err := c.performRequest("getaccountstate", params, resp); err != nil { return nil, err @@ -44,10 +44,10 @@ func (c *Client) GetAccountState(address string) (*response.AccountState, error) } // GetUnspents returns UTXOs for the given NEO account. -func (c *Client) GetUnspents(address string) (*response.Unspent, error) { +func (c *Client) GetUnspents(address string) (*result.Unspents, error) { var ( params = request.NewRawParams(address) - resp = &response.Unspent{} + resp = &result.Unspents{} ) if err := c.performRequest("getunspents", params, resp); err != nil { return nil, err @@ -57,10 +57,10 @@ func (c *Client) GetUnspents(address string) (*response.Unspent, error) { // InvokeScript returns the result of the given script after running it true the VM. // NOTE: This is a test invoke and will not affect the blockchain. -func (c *Client) InvokeScript(script string) (*response.InvokeScript, error) { +func (c *Client) InvokeScript(script string) (*response.InvokeResult, error) { var ( params = request.NewRawParams(script) - resp = &response.InvokeScript{} + resp = &response.InvokeResult{} ) if err := c.performRequest("invokescript", params, resp); err != nil { return nil, err @@ -71,10 +71,10 @@ func (c *Client) InvokeScript(script string) (*response.InvokeScript, error) { // InvokeFunction returns the results after calling the smart contract scripthash // with the given operation and parameters. // NOTE: this is test invoke and will not affect the blockchain. -func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter) (*response.InvokeScript, error) { +func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter) (*response.InvokeResult, error) { var ( p = request.NewRawParams(script, operation, params) - resp = &response.InvokeScript{} + resp = &response.InvokeResult{} ) if err := c.performRequest("invokefunction", p, resp); err != nil { return nil, err @@ -84,10 +84,10 @@ func (c *Client) InvokeFunction(script, operation string, params []smartcontract // Invoke returns the results after calling the smart contract scripthash // with the given parameters. -func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*response.InvokeScript, error) { +func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*response.InvokeResult, error) { var ( p = request.NewRawParams(script, params) - resp = &response.InvokeScript{} + resp = &response.InvokeResult{} ) if err := c.performRequest("invoke", p, resp); err != nil { return nil, err @@ -112,15 +112,18 @@ func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*respo // 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) (*response.SendRawTx, error) { +func (c *Client) sendRawTransaction(rawTX *transaction.Transaction) error { var ( params = request.NewRawParams(hex.EncodeToString(rawTX.Bytes())) - resp = &response.SendRawTx{} + resp bool ) - if err := c.performRequest("sendrawtransaction", params, resp); err != nil { - return nil, err + if err := c.performRequest("sendrawtransaction", params, &resp); err != nil { + return err } - return resp, nil + if !resp { + return errors.New("sendrawtransaction returned false") + } + return nil } // SendToAddress sends an amount of specific asset to a given address. @@ -137,21 +140,16 @@ func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.F WIF: c.WIF(), Balancer: c.Balancer(), } - respRaw *response.SendRawTx - resp = util.Uint256{} + resp util.Uint256 ) if rawTx, err = request.CreateRawContractTransaction(txParams); err != nil { return resp, errors.Wrap(err, "failed to create raw transaction for `sendtoaddress`") } - if respRaw, err = c.sendRawTransaction(rawTx); err != nil { + if err = c.sendRawTransaction(rawTx); err != nil { return resp, errors.Wrap(err, "failed to send raw transaction") } - if respRaw.Result { - return rawTx.Hash(), nil - } else { - return resp, errors.New("failed to send raw transaction") - } + return rawTx.Hash(), nil } // SignAndPushInvocationTx signs and pushes given script as an invocation @@ -175,13 +173,10 @@ func (c *Client) SignAndPushInvocationTx(script []byte, wif *keys.WIF, gas util. return txHash, errors.Wrap(err, "failed to sign tx") } txHash = tx.Hash() - resp, err := c.sendRawTransaction(tx) + err = c.sendRawTransaction(tx) if err != nil { return txHash, errors.Wrap(err, "failed sendning tx") } - if resp.Error != nil { - return txHash, fmt.Errorf("remote returned %d: %s", resp.Error.Code, resp.Error.Message) - } return txHash, nil } diff --git a/pkg/rpc/response/types.go b/pkg/rpc/response/types.go index 917bed066..6b13cb018 100644 --- a/pkg/rpc/response/types.go +++ b/pkg/rpc/response/types.go @@ -8,12 +8,6 @@ import ( "github.com/CityOfZion/neo-go/pkg/vm" ) -// InvokeScript stores response for the invoke script call. -type InvokeScript struct { - HeaderAndError - Result *InvokeResult `json:"result,omitempty"` -} - // InvokeResult represents the outcome of a script that is // executed by the NEO VM. type InvokeResult struct { @@ -23,18 +17,6 @@ type InvokeResult struct { Stack []request.StackParam } -// AccountState holds the getaccountstate response. -type AccountState struct { - Header - Result *result.AccountState `json:"result"` -} - -// Unspent represents server response to the `getunspents` command. -type Unspent struct { - HeaderAndError - Result *result.Unspents `json:"result,omitempty"` -} - // Header is a generic JSON-RPC 2.0 response header (ID and JSON-RPC version). type Header struct { ID json.RawMessage `json:"id"` @@ -52,13 +34,7 @@ type HeaderAndError struct { // response: http://www.jsonrpc.org/specification#response_object. type Raw struct { HeaderAndError - Result interface{} `json:"result,omitempty"` -} - -// SendToAddress stores response for the sendtoaddress call. -type SendToAddress struct { - HeaderAndError - Result *result.TransactionOutputRaw + Result json.RawMessage `json:"result,omitempty"` } // GetTxOut represents result of `gettxout` RPC call. @@ -72,9 +48,3 @@ type GetRawTx struct { HeaderAndError Result *result.TransactionOutputRaw `json:"result"` } - -// SendRawTx represents a `sendrawtransaction` RPC call response. -type SendRawTx struct { - HeaderAndError - Result bool `json:"result"` -} diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index d4ecb4518..805c41168 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -590,6 +590,14 @@ func (s *Server) WriteErrorResponse(r *request.In, w http.ResponseWriter, err er // WriteResponse encodes the response and writes it to the ResponseWriter. func (s *Server) WriteResponse(r *request.In, w http.ResponseWriter, result interface{}) { + resJSON, err := json.Marshal(result) + if err != nil { + s.log.Error("Error encountered while encoding response", + zap.String("err", err.Error()), + zap.String("method", r.Method)) + return + } + resp := response.Raw{ HeaderAndError: response.HeaderAndError{ Header: response.Header{ @@ -597,7 +605,7 @@ func (s *Server) WriteResponse(r *request.In, w http.ResponseWriter, result inte ID: r.RawID, }, }, - Result: result, + Result: resJSON, } s.writeServerResponse(r, w, resp)