Skip to content

Commit

Permalink
REST bank transfers fail due to encoding. (cosmos#6536)
Browse files Browse the repository at this point in the history
Bank module REST endpoint bank/accounts/{addr}/transfers
was returning invalid StdTx format.
  • Loading branch information
jgimeno authored Jul 2, 2020
1 parent 37cc040 commit 8f96ec0
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ be used to retrieve the actual proposal `Content`. Also the `NewMsgSubmitProposa

### Bug Fixes

* (x/bank) [\#6536](https://github.com/cosmos/cosmos-sdk/pull/6536) Fix bug in `WriteGeneratedTxResponse` function used by multiple
REST endpoints. Now it writes a Tx in StdTx format.
* (x/staking) [\#6529](https://github.com/cosmos/cosmos-sdk/pull/6529) Export validator addresses (previously was empty).
* (export) [\#6510](https://github.com/cosmos/cosmos-sdk/pull/6510/) Field TimeIotaMs now is included in genesis file while exporting.
* (client) [\#6402](https://github.com/cosmos/cosmos-sdk/issues/6402) Fix `keys add` `--algo` flag which only worked for Tendermint's `secp256k1` default key signing algorithm.
Expand Down
2 changes: 1 addition & 1 deletion client/tx/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func WriteGeneratedTxResponse(
return
}

output, err := ctx.JSONMarshaler.MarshalJSON(tx)
output, err := ctx.JSONMarshaler.MarshalJSON(tx.GetTx())
if rest.CheckInternalServerError(w, err) {
return
}
Expand Down
21 changes: 21 additions & 0 deletions types/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package rest

import (
"bytes"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -442,3 +443,23 @@ func GetRequest(url string) ([]byte, error) {

return body, nil
}

// PostRequest defines a wrapper around an HTTP POST request with a provided URL and data.
// An error is returned if the request or reading the body fails.
func PostRequest(url string, contentType string, data []byte) ([]byte, error) {
res, err := http.Post(url, contentType, bytes.NewBuffer(data)) // nolint:gosec
if err != nil {
return nil, fmt.Errorf("error while sending post request: %w", err)
}

bz, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if err = res.Body.Close(); err != nil {
return nil, err
}

return bz, nil
}
106 changes: 106 additions & 0 deletions x/bank/client/rest/tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package rest_test

import (
"fmt"

"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/rest"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
bankrest "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)

func (s *IntegrationTestSuite) TestCoinSend() {
encodingConfig := simapp.MakeEncodingConfig()
authclient.Codec = encodingConfig.Marshaler

val := s.network.Validators[0]

account, err := getAccountInfo(val)
s.Require().NoError(err)

sendReq := generateSendReq(
account,
types.Coins{types.NewCoin(s.cfg.BondDenom, types.TokensFromConsensusPower(1))},
)

stdTx, err := submitSendReq(val, sendReq)
s.Require().NoError(err)

s.Require().Nil(stdTx.Signatures)
s.Require().Equal([]types.Msg{
&banktypes.MsgSend{
FromAddress: account.GetAddress(),
ToAddress: account.GetAddress(),
Amount: sendReq.Amount,
},
}, stdTx.GetMsgs())
}

func submitSendReq(val *testutil.Validator, req bankrest.SendReq) (authtypes.StdTx, error) {
url := fmt.Sprintf("%s/bank/accounts/%s/transfers", val.APIAddress, val.Address)

bz, err := val.ClientCtx.JSONMarshaler.MarshalJSON(req)
if err != nil {
return authtypes.StdTx{}, errors.Wrap(err, "error encoding SendReq to json")
}

res, err := rest.PostRequest(url, "application/json", bz)
if err != nil {
return authtypes.StdTx{}, err
}

var tx authtypes.StdTx
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &tx)
if err != nil {
return authtypes.StdTx{}, errors.Wrap(err, "error unmarshaling to StdTx SendReq response")
}

return tx, nil
}

func generateSendReq(from authtypes.AccountI, amount types.Coins) bankrest.SendReq {
baseReq := rest.NewBaseReq(
from.GetAddress().String(),
"someMemo",
"some-id",
"10000",
fmt.Sprintf("%f", 1.0),
from.GetAccountNumber(),
from.GetSequence(),
types.NewCoins(),
nil,
false,
)

return bankrest.SendReq{
BaseReq: baseReq,
Amount: amount,
}
}

func getAccountInfo(val *testutil.Validator) (authtypes.AccountI, error) {
url := fmt.Sprintf("%s/auth/accounts/%s", val.APIAddress, val.Address)

resp, err := rest.GetRequest(url)
if err != nil {
return nil, err
}

bz, err := rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp)
if err != nil {
return nil, err
}

var acc authtypes.AccountI
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, &acc)
if err != nil {
return nil, err
}

return acc, nil
}

0 comments on commit 8f96ec0

Please sign in to comment.