REST tx endpoint backwards compatibility (#6801)
* update rest endpoints * Add conversion logic for auth encode/decode/broadcast * Cleanup * Add tx conversion to legacy REST tx endpoints. * Cleanup * Add tests * Add tests for ConvertAndEncodeStdTx * Fix for StdSignature * Test coverage improvements * Add integration test for REST broadcast Co-authored-by: Aaron Craelius <aaronc@users.noreply.github.com> Co-authored-by: Aaron Craelius <aaron@regen.network>
This commit is contained in:
parent
8283165600
commit
c7ad21d162
79
client/tx/legacy.go
Normal file
79
client/tx/legacy.go
Normal file
@ -0,0 +1,79 @@
|
||||
package tx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
// ConvertTxToStdTx converts a transaction to the legacy StdTx format
|
||||
func ConvertTxToStdTx(codec *codec.Codec, tx signing.SigFeeMemoTx) (types.StdTx, error) {
|
||||
if stdTx, ok := tx.(types.StdTx); ok {
|
||||
return stdTx, nil
|
||||
}
|
||||
|
||||
aminoTxConfig := types.StdTxConfig{Cdc: codec}
|
||||
builder := aminoTxConfig.NewTxBuilder()
|
||||
|
||||
err := CopyTx(tx, builder)
|
||||
if err != nil {
|
||||
|
||||
return types.StdTx{}, err
|
||||
}
|
||||
|
||||
stdTx, ok := builder.GetTx().(types.StdTx)
|
||||
if !ok {
|
||||
return types.StdTx{}, fmt.Errorf("expected %T, got %+v", types.StdTx{}, builder.GetTx())
|
||||
}
|
||||
|
||||
return stdTx, nil
|
||||
}
|
||||
|
||||
// CopyTx copies a SigFeeMemoTx to a new TxBuilder, allowing conversion between
|
||||
// different transaction formats.
|
||||
func CopyTx(tx signing.SigFeeMemoTx, builder client.TxBuilder) error {
|
||||
err := builder.SetMsgs(tx.GetMsgs()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sigs, err := tx.GetSignaturesV2()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = builder.SetSignatures(sigs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builder.SetMemo(tx.GetMemo())
|
||||
builder.SetFeeAmount(tx.GetFee())
|
||||
builder.SetGasLimit(tx.GetGas())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ConvertAndEncodeStdTx(txConfig client.TxConfig, stdTx types.StdTx) ([]byte, error) {
|
||||
builder := txConfig.NewTxBuilder()
|
||||
|
||||
var theTx sdk.Tx
|
||||
|
||||
// check if we need a StdTx anyway, in that case don't copy
|
||||
if _, ok := builder.GetTx().(types.StdTx); ok {
|
||||
theTx = stdTx
|
||||
} else {
|
||||
err := CopyTx(stdTx, builder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
theTx = builder.GetTx()
|
||||
}
|
||||
|
||||
return txConfig.TxEncoder()(theTx)
|
||||
}
|
||||
145
client/tx/legacy_test.go
Normal file
145
client/tx/legacy_test.go
Normal file
@ -0,0 +1,145 @@
|
||||
package tx_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp/params"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
tx2 "github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/std"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
types3 "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
|
||||
signing2 "github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
types2 "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
)
|
||||
|
||||
const (
|
||||
memo = "waboom"
|
||||
gas = uint64(10000)
|
||||
)
|
||||
|
||||
var (
|
||||
fee = types.NewCoins(types.NewInt64Coin("bam", 100))
|
||||
_, pub1, addr1 = testdata.KeyTestPubAddr()
|
||||
_, _, addr2 = testdata.KeyTestPubAddr()
|
||||
msg = types2.NewMsgSend(addr1, addr2, types.NewCoins(types.NewInt64Coin("wack", 10000)))
|
||||
sig = signing2.SignatureV2{
|
||||
PubKey: pub1,
|
||||
Data: &signing2.SingleSignatureData{
|
||||
SignMode: signing2.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
Signature: []byte("dummy"),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func buildTestTx(t *testing.T, builder client.TxBuilder) {
|
||||
builder.SetMemo(memo)
|
||||
builder.SetGasLimit(gas)
|
||||
builder.SetFeeAmount(fee)
|
||||
err := builder.SetMsgs(msg)
|
||||
require.NoError(t, err)
|
||||
err = builder.SetSignatures(sig)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
type TestSuite struct {
|
||||
suite.Suite
|
||||
encCfg params.EncodingConfig
|
||||
protoCfg client.TxConfig
|
||||
aminoCfg client.TxConfig
|
||||
}
|
||||
|
||||
func (s *TestSuite) SetupSuite() {
|
||||
encCfg := simapp.MakeEncodingConfig()
|
||||
s.encCfg = encCfg
|
||||
s.protoCfg = tx.NewTxConfig(codec.NewProtoCodec(encCfg.InterfaceRegistry), std.DefaultPublicKeyCodec{}, tx.DefaultSignModeHandler())
|
||||
s.aminoCfg = types3.StdTxConfig{Cdc: encCfg.Amino}
|
||||
}
|
||||
|
||||
func (s *TestSuite) TestCopyTx() {
|
||||
// proto -> amino -> proto
|
||||
protoBuilder := s.protoCfg.NewTxBuilder()
|
||||
buildTestTx(s.T(), protoBuilder)
|
||||
aminoBuilder := s.aminoCfg.NewTxBuilder()
|
||||
err := tx2.CopyTx(protoBuilder.GetTx(), aminoBuilder)
|
||||
s.Require().NoError(err)
|
||||
protoBuilder2 := s.protoCfg.NewTxBuilder()
|
||||
err = tx2.CopyTx(aminoBuilder.GetTx(), protoBuilder2)
|
||||
s.Require().NoError(err)
|
||||
bz, err := s.protoCfg.TxEncoder()(protoBuilder.GetTx())
|
||||
s.Require().NoError(err)
|
||||
bz2, err := s.protoCfg.TxEncoder()(protoBuilder2.GetTx())
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(bz, bz2)
|
||||
|
||||
// amino -> proto -> amino
|
||||
aminoBuilder = s.aminoCfg.NewTxBuilder()
|
||||
buildTestTx(s.T(), aminoBuilder)
|
||||
protoBuilder = s.protoCfg.NewTxBuilder()
|
||||
err = tx2.CopyTx(aminoBuilder.GetTx(), protoBuilder)
|
||||
s.Require().NoError(err)
|
||||
aminoBuilder2 := s.aminoCfg.NewTxBuilder()
|
||||
err = tx2.CopyTx(protoBuilder.GetTx(), aminoBuilder2)
|
||||
s.Require().NoError(err)
|
||||
bz, err = s.aminoCfg.TxEncoder()(aminoBuilder.GetTx())
|
||||
s.Require().NoError(err)
|
||||
bz2, err = s.aminoCfg.TxEncoder()(aminoBuilder2.GetTx())
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(bz, bz2)
|
||||
}
|
||||
|
||||
func (s *TestSuite) TestConvertTxToStdTx() {
|
||||
// proto tx
|
||||
protoBuilder := s.protoCfg.NewTxBuilder()
|
||||
buildTestTx(s.T(), protoBuilder)
|
||||
stdTx, err := tx2.ConvertTxToStdTx(s.encCfg.Amino, protoBuilder.GetTx())
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(memo, stdTx.Memo)
|
||||
s.Require().Equal(gas, stdTx.Fee.Gas)
|
||||
s.Require().Equal(fee, stdTx.Fee.Amount)
|
||||
s.Require().Equal(msg, stdTx.Msgs[0])
|
||||
s.Require().Equal(sig.PubKey, stdTx.Signatures[0].PubKey)
|
||||
s.Require().Equal(sig.Data.(*signing2.SingleSignatureData).Signature, stdTx.Signatures[0].Signature)
|
||||
|
||||
// std tx
|
||||
aminoBuilder := s.aminoCfg.NewTxBuilder()
|
||||
buildTestTx(s.T(), aminoBuilder)
|
||||
stdTx = aminoBuilder.GetTx().(types3.StdTx)
|
||||
stdTx2, err := tx2.ConvertTxToStdTx(s.encCfg.Amino, stdTx)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(stdTx, stdTx2)
|
||||
}
|
||||
|
||||
func (s *TestSuite) TestConvertAndEncodeStdTx() {
|
||||
// convert amino -> proto -> amino
|
||||
aminoBuilder := s.aminoCfg.NewTxBuilder()
|
||||
buildTestTx(s.T(), aminoBuilder)
|
||||
stdTx := aminoBuilder.GetTx().(types3.StdTx)
|
||||
txBz, err := tx2.ConvertAndEncodeStdTx(s.protoCfg, stdTx)
|
||||
s.Require().NoError(err)
|
||||
decodedTx, err := s.protoCfg.TxDecoder()(txBz)
|
||||
s.Require().NoError(err)
|
||||
aminoBuilder2 := s.aminoCfg.NewTxBuilder()
|
||||
s.Require().NoError(tx2.CopyTx(decodedTx.(signing.SigFeeMemoTx), aminoBuilder2))
|
||||
s.Require().Equal(stdTx, aminoBuilder2.GetTx())
|
||||
|
||||
// just use amino everywhere
|
||||
txBz, err = tx2.ConvertAndEncodeStdTx(s.aminoCfg, stdTx)
|
||||
s.Require().NoError(err)
|
||||
decodedTx, err = s.aminoCfg.TxDecoder()(txBz)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(stdTx, decodedTx)
|
||||
}
|
||||
@ -138,6 +138,8 @@ func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
|
||||
// WriteGeneratedTxResponse writes a generated unsigned transaction to the
|
||||
// provided http.ResponseWriter. It will simulate gas costs if requested by the
|
||||
// BaseReq. Upon any error, the error will be written to the http.ResponseWriter.
|
||||
// Note that this function returns the legacy StdTx Amino JSON format for compatibility
|
||||
// with legacy clients.
|
||||
func WriteGeneratedTxResponse(
|
||||
ctx client.Context, w http.ResponseWriter, br rest.BaseReq, msgs ...sdk.Msg,
|
||||
) {
|
||||
@ -175,7 +177,7 @@ func WriteGeneratedTxResponse(
|
||||
txf = txf.WithGas(adjusted)
|
||||
|
||||
if br.Simulate {
|
||||
rest.WriteSimulationResponse(w, ctx.JSONMarshaler, txf.Gas())
|
||||
rest.WriteSimulationResponse(w, ctx.Codec, txf.Gas())
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -185,7 +187,12 @@ func WriteGeneratedTxResponse(
|
||||
return
|
||||
}
|
||||
|
||||
output, err := ctx.JSONMarshaler.MarshalJSON(tx.GetTx())
|
||||
stdTx, err := ConvertTxToStdTx(ctx.Codec, tx.GetTx())
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
output, err := ctx.Codec.MarshalJSON(stdTx)
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -62,6 +62,7 @@ func NewSimApp(val Validator) servertypes.Application {
|
||||
// in-process local testing network.
|
||||
type Config struct {
|
||||
Codec codec.Marshaler
|
||||
LegacyAmino *codec.Codec
|
||||
TxConfig client.TxConfig
|
||||
AccountRetriever client.AccountRetriever
|
||||
AppConstructor AppConstructor // the ABCI application constructor
|
||||
@ -88,6 +89,7 @@ func DefaultConfig() Config {
|
||||
return Config{
|
||||
Codec: encCfg.Marshaler,
|
||||
TxConfig: encCfg.TxConfig,
|
||||
LegacyAmino: encCfg.Amino,
|
||||
AccountRetriever: authtypes.NewAccountRetriever(encCfg.Marshaler),
|
||||
AppConstructor: NewSimApp,
|
||||
GenesisState: simapp.ModuleBasics.DefaultGenesis(encCfg.Marshaler),
|
||||
@ -312,6 +314,7 @@ func New(t *testing.T, cfg Config) *Network {
|
||||
WithHomeDir(tmCfg.RootDir).
|
||||
WithChainID(cfg.ChainID).
|
||||
WithJSONMarshaler(cfg.Codec).
|
||||
WithCodec(cfg.LegacyAmino).
|
||||
WithTxConfig(cfg.TxConfig).
|
||||
WithAccountRetriever(cfg.AccountRetriever)
|
||||
|
||||
|
||||
@ -51,7 +51,8 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, clientCtx client.Context,
|
||||
return
|
||||
}
|
||||
|
||||
output, err := clientCtx.JSONMarshaler.MarshalJSON(types.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo))
|
||||
// NOTE: amino is used intentionally here, don't migrate it
|
||||
output, err := clientCtx.Codec.MarshalJSON(types.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo))
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
@ -27,11 +29,12 @@ func BroadcastTxRequest(clientCtx client.Context) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if err := clientCtx.JSONMarshaler.UnmarshalJSON(body, &req); rest.CheckBadRequestError(w, err) {
|
||||
// NOTE: amino is used intentionally here, don't migrate it!
|
||||
if err := clientCtx.Codec.UnmarshalJSON(body, &req); rest.CheckBadRequestError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
txBytes, err := clientCtx.Codec.MarshalBinaryBare(req.Tx)
|
||||
txBytes, err := tx.ConvertAndEncodeStdTx(clientCtx.TxConfig, req.Tx)
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
@ -43,6 +46,8 @@ func BroadcastTxRequest(clientCtx client.Context) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: amino is set intentionally here, don't migrate it!
|
||||
clientCtx = clientCtx.WithJSONMarshaler(clientCtx.Codec)
|
||||
rest.PostProcessResponseBare(w, clientCtx, res)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,14 @@ package rest
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
@ -32,7 +36,8 @@ func DecodeTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
err = clientCtx.JSONMarshaler.UnmarshalJSON(body, &req)
|
||||
// NOTE: amino is used intentionally here, don't migrate it
|
||||
err = clientCtx.Codec.UnmarshalJSON(body, &req)
|
||||
if rest.CheckBadRequestError(w, err) {
|
||||
return
|
||||
}
|
||||
@ -42,13 +47,26 @@ func DecodeTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
var stdTx authtypes.StdTx
|
||||
err = clientCtx.Codec.UnmarshalBinaryBare(txBytes, &stdTx)
|
||||
tx, err := clientCtx.TxConfig.TxDecoder()(txBytes)
|
||||
if rest.CheckBadRequestError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
sigFeeMemoTx, ok := tx.(signing.SigFeeMemoTx)
|
||||
if !ok {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("%+v is not backwards compatible with %T", tx, authtypes.StdTx{}))
|
||||
return
|
||||
}
|
||||
|
||||
stdTx, err := clienttx.ConvertTxToStdTx(clientCtx.Codec, sigFeeMemoTx)
|
||||
if rest.CheckBadRequestError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
response := DecodeResp(stdTx)
|
||||
|
||||
// NOTE: amino is set intentionally here, don't migrate it
|
||||
clientCtx = clientCtx.WithJSONMarshaler(clientCtx.Codec)
|
||||
rest.PostProcessResponse(w, clientCtx, response)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
@ -27,13 +29,14 @@ func EncodeTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
err = clientCtx.JSONMarshaler.UnmarshalJSON(body, &req)
|
||||
// NOTE: amino is used intentionally here, don't migrate it
|
||||
err = clientCtx.Codec.UnmarshalJSON(body, &req)
|
||||
if rest.CheckBadRequestError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// re-encode it via the Amino wire protocol
|
||||
txBytes, err := clientCtx.Codec.MarshalBinaryBare(req)
|
||||
// re-encode it in the chain's native binary format
|
||||
txBytes, err := tx.ConvertAndEncodeStdTx(clientCtx.TxConfig, req)
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
@ -42,6 +45,8 @@ func EncodeTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes)
|
||||
|
||||
response := EncodeResp{Tx: txBytesBase64}
|
||||
|
||||
// NOTE: amino is set intentionally here, don't migrate it
|
||||
rest.PostProcessResponseBare(w, clientCtx, response)
|
||||
}
|
||||
}
|
||||
|
||||
124
x/auth/client/rest/rest_test.go
Normal file
124
x/auth/client/rest/rest_test.go
Normal file
@ -0,0 +1,124 @@
|
||||
package rest_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
rest2 "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
cfg network.Config
|
||||
network *network.Network
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupSuite() {
|
||||
s.T().Log("setting up integration test suite")
|
||||
|
||||
cfg := network.DefaultConfig()
|
||||
cfg.NumValidators = 1
|
||||
|
||||
s.cfg = cfg
|
||||
s.network = network.New(s.T(), cfg)
|
||||
|
||||
_, err := s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down integration test suite")
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestEncodeDecode() {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
// NOTE: this uses StdTx explicitly, don't migrate it!
|
||||
stdTx := authtypes.StdTx{
|
||||
Msgs: []sdk.Msg{&types.MsgSend{}},
|
||||
Fee: authtypes.StdFee{
|
||||
Amount: sdk.Coins{sdk.NewInt64Coin("foo", 10)},
|
||||
Gas: 10000,
|
||||
},
|
||||
Memo: "FOOBAR",
|
||||
}
|
||||
|
||||
// NOTE: this uses amino explicitly, don't migrate it!
|
||||
cdc := val.ClientCtx.Codec
|
||||
|
||||
bz, err := cdc.MarshalJSON(stdTx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
res, err := rest.PostRequest(fmt.Sprintf("%s/txs/encode", val.APIAddress), "application/json", bz)
|
||||
s.Require().NoError(err)
|
||||
|
||||
var encodeResp rest2.EncodeResp
|
||||
err = cdc.UnmarshalJSON(res, &encodeResp)
|
||||
s.Require().NoError(err)
|
||||
|
||||
bz, err = cdc.MarshalJSON(rest2.DecodeReq{Tx: encodeResp.Tx})
|
||||
s.Require().NoError(err)
|
||||
|
||||
res, err = rest.PostRequest(fmt.Sprintf("%s/txs/decode", val.APIAddress), "application/json", bz)
|
||||
s.Require().NoError(err)
|
||||
|
||||
var respWithHeight rest.ResponseWithHeight
|
||||
err = cdc.UnmarshalJSON(res, &respWithHeight)
|
||||
s.Require().NoError(err)
|
||||
var decodeResp rest2.DecodeResp
|
||||
err = cdc.UnmarshalJSON(respWithHeight.Result, &decodeResp)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(stdTx, authtypes.StdTx(decodeResp))
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestBroadcastTxRequest() {
|
||||
// NOTE: this uses StdTx explicitly, don't migrate it!
|
||||
stdTx := authtypes.StdTx{
|
||||
Msgs: []sdk.Msg{&types.MsgSend{}},
|
||||
Fee: authtypes.StdFee{
|
||||
Amount: sdk.Coins{sdk.NewInt64Coin("foo", 10)},
|
||||
Gas: 10000,
|
||||
},
|
||||
Memo: "FOOBAR",
|
||||
}
|
||||
|
||||
// we just test with async mode because this tx will fail - all we care about is that it got encoded and broadcast correctly
|
||||
res, err := s.broadcastReq(stdTx, "async")
|
||||
s.Require().NoError(err)
|
||||
var txRes sdk.TxResponse
|
||||
// NOTE: this uses amino explicitly, don't migrate it!
|
||||
s.Require().NoError(s.cfg.LegacyAmino.UnmarshalJSON(res, &txRes))
|
||||
// we just check for a non-empty TxHash here, the actual hash will depend on the underlying tx configuration
|
||||
s.Require().NotEmpty(txRes.TxHash)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) broadcastReq(stdTx authtypes.StdTx, mode string) ([]byte, error) {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
// NOTE: this uses amino explicitly, don't migrate it!
|
||||
cdc := val.ClientCtx.Codec
|
||||
|
||||
req := rest2.BroadcastReq{
|
||||
Tx: stdTx,
|
||||
Mode: mode,
|
||||
}
|
||||
bz, err := cdc.MarshalJSON(req)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return rest.PostRequest(fmt.Sprintf("%s/txs", val.APIAddress), "application/json", bz)
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
@ -14,17 +14,3 @@ func RegisterHandlers(clientCtx client.Context, r *mux.Router) {
|
||||
r.HandleFunc("/bank/total", totalSupplyHandlerFn(clientCtx)).Methods("GET")
|
||||
r.HandleFunc("/bank/total/{denom}", supplyOfHandlerFn(clientCtx)).Methods("GET")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Deprecated
|
||||
//
|
||||
// TODO: Remove once client-side Protobuf migration has been completed.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// RegisterRoutes - Central function to define routes that get registered by the main application
|
||||
func RegisterRoutes(clientCtx client.Context, r *mux.Router) {
|
||||
r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(clientCtx)).Methods("POST")
|
||||
r.HandleFunc("/bank/balances/{address}", QueryBalancesRequestHandlerFn(clientCtx)).Methods("GET")
|
||||
r.HandleFunc("/bank/total", totalSupplyHandlerFn(clientCtx)).Methods("GET")
|
||||
r.HandleFunc("/bank/total/{denom}", supplyOfHandlerFn(clientCtx)).Methods("GET")
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
)
|
||||
|
||||
@ -50,43 +49,3 @@ func NewSendRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Deprecated
|
||||
//
|
||||
// TODO: Remove once client-side Protobuf migration has been completed.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// SendRequestHandlerFn - http request handler to send coins to a address.
|
||||
//
|
||||
// TODO: Remove once client-side Protobuf migration has been completed.
|
||||
// ref: https://github.com/cosmos/cosmos-sdk/issues/5864
|
||||
func SendRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
bech32Addr := vars["address"]
|
||||
|
||||
toAddr, err := sdk.AccAddressFromBech32(bech32Addr)
|
||||
if rest.CheckBadRequestError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
var req SendReq
|
||||
if !rest.ReadRESTReq(w, r, clientCtx.Codec, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
req.BaseReq = req.BaseReq.Sanitize()
|
||||
if !req.BaseReq.ValidateBasic(w) {
|
||||
return
|
||||
}
|
||||
|
||||
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
|
||||
if rest.CheckBadRequestError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
msg := types.NewMsgSend(fromAddr, toAddr, req.Amount)
|
||||
authclient.WriteGenerateStdTxResponse(w, clientCtx, req.BaseReq, []sdk.Msg{msg})
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,8 @@ func (s *IntegrationTestSuite) TestCoinSend() {
|
||||
func submitSendReq(val *network.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)
|
||||
// NOTE: this uses amino explicitly, don't migrate it!
|
||||
bz, err := val.ClientCtx.Codec.MarshalJSON(req)
|
||||
if err != nil {
|
||||
return authtypes.StdTx{}, errors.Wrap(err, "error encoding SendReq to json")
|
||||
}
|
||||
@ -55,7 +56,8 @@ func submitSendReq(val *network.Validator, req bankrest.SendReq) (authtypes.StdT
|
||||
}
|
||||
|
||||
var tx authtypes.StdTx
|
||||
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &tx)
|
||||
// NOTE: this uses amino explicitly, don't migrate it!
|
||||
err = val.ClientCtx.Codec.UnmarshalJSON(res, &tx)
|
||||
if err != nil {
|
||||
return authtypes.StdTx{}, errors.Wrap(err, "error unmarshaling to StdTx SendReq response")
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user