From 4dc09ab6582988e7ff2d07a3dae514ad7d41e57c Mon Sep 17 00:00:00 2001 From: Reuven Podmazo Date: Thu, 7 Oct 2021 18:14:09 +0300 Subject: [PATCH] copied /tx POST from terra-money/core/pull/551 --- x/auth/client/rest/broadcast.go | 119 ++++++++++++++++++++++++++++++++ x/auth/client/rest/rest.go | 1 + 2 files changed, 120 insertions(+) create mode 100644 x/auth/client/rest/broadcast.go diff --git a/x/auth/client/rest/broadcast.go b/x/auth/client/rest/broadcast.go new file mode 100644 index 000000000000..8a665189fd8d --- /dev/null +++ b/x/auth/client/rest/broadcast.go @@ -0,0 +1,119 @@ +package rest + +import ( + "errors" + "fmt" + "io/ioutil" + "net/http" + + "github.com/cosmos/cosmos-sdk/client" + clientrest "github.com/cosmos/cosmos-sdk/client/rest" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" +) + +// BroadcastReq defines a tx broadcasting request. +type BroadcastReq struct { + Tx legacytx.StdTx `json:"tx" yaml:"tx"` + Mode string `json:"mode" yaml:"mode"` + Sequences []uint64 `json:"sequences" yaml:"sequences"` + FeeGranter string `json:"fee_granter" yaml:"fee_granter"` +} + +var _ codectypes.UnpackInterfacesMessage = BroadcastReq{} + +// UnpackInterfaces implements the UnpackInterfacesMessage interface. +func (m BroadcastReq) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return m.Tx.UnpackInterfaces(unpacker) +} + +// BroadcastTxRequest implements a tx broadcasting handler that is responsible +// for broadcasting a valid and signed tx to a full node. The tx can be +// broadcasted via a sync|async|block mechanism. +func BroadcastTxRequest(clientCtx client.Context) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req BroadcastReq + + body, err := ioutil.ReadAll(r.Body) + if rest.CheckBadRequestError(w, err) { + return + } + + // NOTE: amino is used intentionally here, don't migrate it! + err = clientCtx.LegacyAmino.UnmarshalJSON(body, &req) + if err != nil { + err := fmt.Errorf("this transaction cannot be broadcasted via legacy REST endpoints, because it does not support"+ + " Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC"+ + " endpoint to broadcast this transaction. The new REST endpoint (via gRPC-gateway) is POST /cosmos/tx/v1beta1/txs."+ + " Please also see the REST endpoints migration guide at %s for more info", clientrest.DeprecationURL) + if rest.CheckBadRequestError(w, err) { + return + } + } + + txBuilder := clientCtx.TxConfig.NewTxBuilder() + txBuilder.SetFeeAmount(req.Tx.GetFee()) + txBuilder.SetGasLimit(req.Tx.GetGas()) + txBuilder.SetMemo(req.Tx.GetMemo()) + if err := txBuilder.SetMsgs(req.Tx.GetMsgs()...); rest.CheckBadRequestError(w, err) { + return + } + + txBuilder.SetTimeoutHeight(req.Tx.GetTimeoutHeight()) + if req.FeeGranter != "" { + addr, err := sdk.AccAddressFromBech32(req.FeeGranter) + if rest.CheckBadRequestError(w, err) { + return + } + + txBuilder.SetFeeGranter(addr) + } + + signatures, err := req.Tx.GetSignaturesV2() + if rest.CheckBadRequestError(w, err) { + return + } + + // if sequence is not given, try fetch from the chain + if len(req.Sequences) == 0 { + for _, sig := range signatures { + _, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, sdk.AccAddress(sig.PubKey.Address().Bytes())) + if rest.CheckBadRequestError(w, err) { + return + } + req.Sequences = append(req.Sequences, seq) + } + } + + // check the sequence nubmer is equal with the signature nubmer + if len(signatures) != len(req.Sequences) { + rest.CheckBadRequestError(w, errors.New("Must provide each signers's sequence number")) + return + } + + // fill sequence number to new signature + for i, seq := range req.Sequences { + signatures[i].Sequence = seq + } + + if err := txBuilder.SetSignatures(signatures...); rest.CheckBadRequestError(w, err) { + return + } + + // compute signature bytes + txBytes, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + if rest.CheckInternalServerError(w, err) { + return + } + + clientCtx = clientCtx.WithBroadcastMode(req.Mode) + res, err := clientCtx.BroadcastTx(txBytes) + if rest.CheckInternalServerError(w, err) { + return + } + + rest.PostProcessResponseBare(w, clientCtx, res) + } +} diff --git a/x/auth/client/rest/rest.go b/x/auth/client/rest/rest.go index 56eb3cf30682..98c7a4673b1c 100644 --- a/x/auth/client/rest/rest.go +++ b/x/auth/client/rest/rest.go @@ -30,6 +30,7 @@ func RegisterTxRoutes(clientCtx client.Context, rtr *mux.Router) { r := rest.WithHTTPDeprecationHeaders(rtr) r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(clientCtx)).Methods("GET") r.HandleFunc("/txs", QueryTxsRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/txs", BroadcastTxRequest(clientCtx)).Methods("POST") r.HandleFunc("/txs/encode", EncodeTxRequest(clientCtx)).Methods("POST") r.HandleFunc("/txs/decode", DecodeTxRequestHandlerFn(clientCtx)).Methods("POST") }