Merge PR #3696: Cleanup Tx Broadcasting and Encoding

This commit is contained in:
Alexander Bezobchuk 2019-02-26 06:34:27 -05:00 committed by Christopher Goes
parent feb98bcd05
commit 6ace1fada2
20 changed files with 269 additions and 353 deletions

View File

@ -10,6 +10,10 @@
* `password` and `generate_only` have been removed from the `base_req` object
* All txs that used to sign or use the Keybase now only generate the tx
* `keys` routes completely removed
* [\#3692] Update tx encoding and broadcasting endpoints:
* Remove duplicate broadcasting endpoints in favor of POST @ `/txs`
* The `Tx` field now accepts a `StdTx` and not raw tx bytes
* Move encoding endpoint to `/txs/encode`
### Gaia CLI

View File

@ -15,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
"github.com/cosmos/cosmos-sdk/tests"
@ -292,12 +293,9 @@ func TestEncodeTx(t *testing.T) {
var tx auth.StdTx
cdc.UnmarshalJSON([]byte(body), &tx)
// build the request
encodeReq := struct {
Tx auth.StdTx `json:"tx"`
}{Tx: tx}
encodedJSON, _ := cdc.MarshalJSON(encodeReq)
res, body = Request(t, port, "POST", "/tx/encode", encodedJSON)
req := clienttx.EncodeReq{Tx: tx}
encodedJSON, _ := cdc.MarshalJSON(req)
res, body = Request(t, port, "POST", "/txs/encode", encodedJSON)
// Make sure it came back ok, and that we can decode it back to the transaction
// 200 response.

View File

@ -241,8 +241,8 @@ paths:
post:
tags:
- ICS0
summary: broadcast Tx
description: broadcast tx with tendermint rpc
summary: Broadcast a signed tx
description: Broadcast a signed tx to a full node
consumes:
- application/json
produces:
@ -250,57 +250,28 @@ paths:
parameters:
- in: body
name: txBroadcast
description: Build a StdTx transaction and serilize it to a byte array with amino, then the `"tx"` field in the post body will be the base64 encoding of the byte array. The supported return types includes `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away).
required: true
schema:
type: object
properties:
tx:
type: string
return:
type: string
example: block
responses:
200:
description: Broadcast tx result
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
500:
description: Internal Server Error
/tx/broadcast:
post:
tags:
- ICS20
summary: Send a signed Tx
description: Send a signed Tx to a Gaiad full node
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: txBroadcast
description: broadcast tx
description: The tx must be a signed StdTx. The supported return types includes `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away).
required: true
schema:
type: object
properties:
tx:
$ref: "#/definitions/StdTx"
return:
type: string
example: block
responses:
202:
description: Tx was send and will probably be added to the next block
200:
description: Tx broadcasting result
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
400:
description: The Tx was malformated
500:
description: Server internal error
/tx/encode:
description: Internal Server Error
/txs/encode:
post:
tags:
- ICS20
summary: Encode a transaction to wire format
- ICS0
summary: Encode a transaction to the Amino wire format
description: Encode a transaction (signed or not) from JSON to base64-encoded Amino serialized bytes
consumes:
- application/json
@ -309,7 +280,7 @@ paths:
parameters:
- in: body
name: tx
description: The transaction to encode
description: The tx to encode
required: true
schema:
type: object
@ -318,15 +289,15 @@ paths:
$ref: "#/definitions/StdTx"
responses:
200:
description: Transaction was successfully decoded and re-encoded
description: The tx was successfully decoded and re-encoded
schema:
type: object
properties:
tx:
type: string
example: The base64-encoded Amino-serialized bytes for the transaction
example: The base64-encoded Amino-serialized bytes for the tx
400:
description: The Tx was malformated
description: The tx was malformated
500:
description: Server internal error
/bank/balances/{address}:

View File

@ -24,6 +24,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
@ -649,12 +650,12 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
// POST /tx/broadcast Send a signed Tx
func doBroadcast(t *testing.T, port string, tx auth.StdTx) (*http.Response, string) {
txReq := authrest.BroadcastReq{Tx: tx, Return: "block"}
txReq := clienttx.BroadcastReq{Tx: tx, Return: "block"}
req, err := cdc.MarshalJSON(txReq)
require.Nil(t, err)
return Request(t, port, "POST", "/tx/broadcast", req)
return Request(t, port, "POST", "/txs", req)
}
// doTransfer performs a balance transfer with auto gas calculation. It also signs

View File

@ -2,12 +2,19 @@ package tx
import (
"net/http"
"strings"
"github.com/spf13/cobra"
amino "github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
"io/ioutil"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
)
@ -20,44 +27,93 @@ const (
flagBlock = "block"
)
// BroadcastBody Tx Broadcast Body
type BroadcastBody struct {
TxBytes []byte `json:"tx"`
Return string `json:"return"`
// BroadcastReq defines a tx broadcasting request.
type BroadcastReq struct {
Tx auth.StdTx `json:"tx"`
Return string `json:"return"`
}
// BroadcastTxRequest REST Handler
// nolint: gocyclo
// 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(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m BroadcastBody
var req BroadcastReq
body, err := ioutil.ReadAll(r.Body)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
err = cdc.UnmarshalJSON(body, &m)
err = cdc.UnmarshalJSON(body, &req)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
txBytes, err := cdc.MarshalBinaryLengthPrefixed(req.Tx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var res interface{}
switch m.Return {
switch req.Return {
case flagBlock:
res, err = cliCtx.BroadcastTx(m.TxBytes)
res, err = cliCtx.BroadcastTx(txBytes)
case flagSync:
res, err = cliCtx.BroadcastTxSync(m.TxBytes)
res, err = cliCtx.BroadcastTxSync(txBytes)
case flagAsync:
res, err = cliCtx.BroadcastTxAsync(m.TxBytes)
res, err = cliCtx.BroadcastTxAsync(txBytes)
default:
rest.WriteErrorResponse(w, http.StatusInternalServerError,
"unsupported return type. supported types: block, sync, async")
return
}
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
}
// GetBroadcastCommand returns the tx broadcast command.
func GetBroadcastCommand(codec *amino.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "broadcast [file_path]",
Short: "Broadcast transactions generated offline",
Long: strings.TrimSpace(`Broadcast transactions created with the --generate-only
flag and signed with the sign command. Read a transaction from [file_path] and
broadcast it to a node. If you supply a dash (-) argument in place of an input
filename, the command reads from standard input.
$ gaiacli tx broadcast ./mytxn.json
`),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
cliCtx := context.NewCLIContext().WithCodec(codec)
stdTx, err := utils.ReadStdTxFromFile(cliCtx.Codec, args[0])
if err != nil {
return
}
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx)
if err != nil {
return
}
res, err := cliCtx.BroadcastTx(txBytes)
cliCtx.PrintOutput(res)
return err
},
}
return client.PostCommands(cmd)[0]
}

107
client/tx/encode.go Normal file
View File

@ -0,0 +1,107 @@
package tx
import (
"encoding/base64"
"io/ioutil"
"net/http"
"github.com/spf13/cobra"
amino "github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
)
type (
// EncodeReq defines a tx encoding request.
EncodeReq struct {
Tx auth.StdTx `json:"tx"`
}
// EncodeResp defines a tx encoding response.
EncodeResp struct {
Tx string `json:"tx"`
}
)
// EncodeTxRequestHandlerFn returns the encode tx REST handler. In particular,
// it takes a json-formatted transaction, encodes it to the Amino wire protocol,
// and responds with base64-encoded bytes.
func EncodeTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req EncodeReq
body, err := ioutil.ReadAll(r.Body)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
err = cdc.UnmarshalJSON(body, &req)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// re-encode it via the Amino wire protocol
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(req.Tx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// base64 encode the encoded tx bytes
txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes)
response := EncodeResp{Tx: txBytesBase64}
rest.PostProcessResponse(w, cdc, response, cliCtx.Indent)
}
}
// txEncodeRespStr implements a simple Stringer wrapper for a encoded tx.
type txEncodeRespStr string
func (txr txEncodeRespStr) String() string {
return string(txr)
}
// GetEncodeCommand returns the encode command to take a JSONified transaction and turn it into
// Amino-serialized bytes
func GetEncodeCommand(codec *amino.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "encode [file]",
Short: "Encode transactions generated offline",
Long: `Encode transactions created with the --generate-only flag and signed with the sign command.
Read a transaction from <file>, serialize it to the Amino wire protocol, and output it as base64.
If you supply a dash (-) argument in place of an input filename, the command reads from standard input.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
cliCtx := context.NewCLIContext().WithCodec(codec)
stdTx, err := utils.ReadStdTxFromFile(cliCtx.Codec, args[0])
if err != nil {
return
}
// re-encode it via the Amino wire protocol
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx)
if err != nil {
return err
}
// base64 encode the encoded tx bytes
txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes)
response := txEncodeRespStr(txBytesBase64)
cliCtx.PrintOutput(response)
return nil
},
}
return client.PostCommands(cmd)[0]
}

View File

@ -12,4 +12,5 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec)
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET")
r.HandleFunc("/txs", SearchTxRequestHandlerFn(cliCtx, cdc)).Methods("GET")
r.HandleFunc("/txs", BroadcastTxRequest(cliCtx, cdc)).Methods("POST")
r.HandleFunc("/txs/encode", EncodeTxRequestHandlerFn(cdc, cliCtx)).Methods("POST")
}

View File

@ -3,6 +3,7 @@ package utils
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"github.com/cosmos/cosmos-sdk/client"
@ -124,20 +125,27 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *
// PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout.
// Don't perform online validation or lookups if offline is true.
func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool) (err error) {
func PrintUnsignedStdTx(
txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool,
) (err error) {
var stdTx auth.StdTx
if offline {
stdTx, err = buildUnsignedStdTxOffline(txBldr, cliCtx, msgs)
} else {
stdTx, err = buildUnsignedStdTx(txBldr, cliCtx, msgs)
}
if err != nil {
return
}
json, err := cliCtx.Codec.MarshalJSON(stdTx)
if err == nil {
fmt.Fprintf(cliCtx.Output, "%s\n", json)
}
return
}
@ -204,6 +212,23 @@ func SignStdTxWithSignerAddress(txBldr authtxb.TxBuilder, cliCtx context.CLICont
return txBldr.SignStdTx(name, passphrase, stdTx, false)
}
// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin.
func ReadStdTxFromFile(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) {
var bytes []byte
if filename == "-" {
bytes, err = ioutil.ReadAll(os.Stdin)
} else {
bytes, err = ioutil.ReadFile(filename)
}
if err != nil {
return
}
if err = cdc.UnmarshalJSON(bytes, &stdTx); err != nil {
return
}
return
}
func populateAccountFromState(txBldr authtxb.TxBuilder, cliCtx context.CLIContext,
addr sdk.AccAddress) (authtxb.TxBuilder, error) {
if txBldr.AccountNumber() == 0 {

View File

@ -3,19 +3,19 @@ package utils
import (
"encoding/json"
"errors"
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
)
var (
@ -110,3 +110,32 @@ func makeCodec() *codec.Codec {
cdc.RegisterConcrete(sdk.TestMsg{}, "cosmos-sdk/Test", nil)
return cdc
}
func TestReadStdTxFromFile(t *testing.T) {
cdc := codec.New()
sdk.RegisterCodec(cdc)
// Build a test transaction
fee := auth.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)})
stdTx := auth.NewStdTx([]sdk.Msg{}, fee, []auth.StdSignature{}, "foomemo")
// Write it to the file
encodedTx, _ := cdc.MarshalJSON(stdTx)
jsonTxFile := writeToNewTempFile(t, string(encodedTx))
defer os.Remove(jsonTxFile.Name())
// Read it back
decodedTx, err := ReadStdTxFromFile(cdc, jsonTxFile.Name())
require.Nil(t, err)
require.Equal(t, decodedTx.Memo, "foomemo")
}
func writeToNewTempFile(t *testing.T, data string) *os.File {
fp, err := ioutil.TempFile(os.TempDir(), "client_tx_test")
require.Nil(t, err)
_, err = fp.WriteString(data)
require.Nil(t, err)
return fp
}

View File

@ -141,8 +141,8 @@ func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command {
client.LineBreak,
authcmd.GetSignCommand(cdc),
authcmd.GetMultiSignCommand(cdc),
authcmd.GetBroadcastCommand(cdc),
authcmd.GetEncodeCommand(cdc),
tx.GetBroadcastCommand(cdc),
tx.GetEncodeCommand(cdc),
client.LineBreak,
)

View File

@ -1,45 +0,0 @@
package cli
import (
"strings"
"github.com/spf13/cobra"
amino "github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
)
// GetSignCommand returns the sign command
func GetBroadcastCommand(codec *amino.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "broadcast [file_path]",
Short: "Broadcast transactions generated offline",
Long: strings.TrimSpace(`Broadcast transactions created with the --generate-only flag and signed with the sign command.
Read a transaction from [file_path] and broadcast it to a node. If you supply a dash (-) argument
in place of an input filename, the command reads from standard input.
$ gaiacli tx broadcast ./mytxn.json
`),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
cliCtx := context.NewCLIContext().WithCodec(codec)
stdTx, err := authclient.ReadStdTxFromFile(cliCtx.Codec, args[0])
if err != nil {
return
}
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx)
if err != nil {
return
}
res, err := cliCtx.BroadcastTx(txBytes)
cliCtx.PrintOutput(res)
return err
},
}
return client.PostCommands(cmd)[0]
}

View File

@ -1,55 +0,0 @@
package cli
import (
"encoding/base64"
"github.com/spf13/cobra"
amino "github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
)
// PrintOutput requires a Stringer, so we wrap string
type encodeResp string
func (e encodeResp) String() string {
return string(e)
}
// GetEncodeCommand returns the encode command to take a JSONified transaction and turn it into
// Amino-serialized bytes
func GetEncodeCommand(codec *amino.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "encode [file]",
Short: "encode transactions generated offline",
Long: `Encode transactions created with the --generate-only flag and signed with the sign command.
Read a transaction from <file>, serialize it to the Amino wire protocol, and output it as base64.
If you supply a dash (-) argument in place of an input filename, the command reads from standard input.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
cliCtx := context.NewCLIContext().WithCodec(codec)
stdTx, err := authclient.ReadStdTxFromFile(cliCtx.Codec, args[0])
if err != nil {
return
}
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx)
if err != nil {
return err
}
// Encode the bytes to base64
txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes)
// Write it back
response := encodeResp(txBytesBase64)
cliCtx.PrintOutput(response)
return nil
},
}
return client.PostCommands(cmd)[0]
}

View File

@ -14,9 +14,9 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/utils"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/x/auth"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
)
@ -52,7 +52,7 @@ recommended to set such parameters manually.
func makeMultiSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) (err error) {
stdTx, err := authclient.ReadStdTxFromFile(cdc, args[0])
stdTx, err := utils.ReadStdTxFromFile(cdc, args[0])
if err != nil {
return
}

View File

@ -13,7 +13,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/utils"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
)
@ -74,7 +73,7 @@ be generated via the 'multisign' command.
func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) (err error) {
stdTx, err := authclient.ReadStdTxFromFile(cdc, args[0])
stdTx, err := utils.ReadStdTxFromFile(cdc, args[0])
if err != nil {
return
}

View File

@ -1,53 +0,0 @@
package rest
import (
"io/ioutil"
"net/http"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
)
type broadcastBody struct {
Tx auth.StdTx `json:"tx"`
}
// BroadcastTxRequestHandlerFn returns the broadcast tx REST handler
func BroadcastTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m broadcastBody
if ok := unmarshalBodyOrReturnBadRequest(cliCtx, w, r, &m); !ok {
return
}
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(m.Tx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
}
func unmarshalBodyOrReturnBadRequest(cliCtx context.CLIContext, w http.ResponseWriter, r *http.Request, m interface{}) bool {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return false
}
err = cliCtx.Codec.UnmarshalJSON(body, m)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return false
}
return true
}

View File

@ -1,47 +0,0 @@
package rest
import (
"encoding/base64"
"net/http"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
)
type encodeReq struct {
Tx auth.StdTx `json:"tx"`
}
type encodeResp struct {
Tx string `json:"tx"`
}
// EncodeTxRequestHandlerFn returns the encode tx REST handler. In particular, it takes a
// json-formatted transaction, encodes it to the Amino wire protocol, and responds with
// base64-encoded bytes
func EncodeTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m encodeReq
// Decode the transaction from JSON
if ok := unmarshalBodyOrReturnBadRequest(cliCtx, w, r, &m); !ok {
return
}
// Re-encode it to the wire protocol
txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(m.Tx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// Encode the bytes to base64
txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes)
// Write it back
response := encodeResp{Tx: txBytesBase64}
rest.PostProcessResponse(w, cdc, response, cliCtx.Indent)
}
}

View File

@ -19,18 +19,11 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec,
"/auth/accounts/{address}",
QueryAccountRequestHandlerFn(storeName, cdc, context.GetAccountDecoder(cdc), cliCtx),
).Methods("GET")
r.HandleFunc(
"/bank/balances/{address}",
QueryBalancesRequestHandlerFn(storeName, cdc, context.GetAccountDecoder(cdc), cliCtx),
).Methods("GET")
r.HandleFunc(
"/tx/broadcast",
BroadcastTxRequestHandlerFn(cdc, cliCtx),
).Methods("POST")
r.HandleFunc(
"/tx/encode",
EncodeTxRequestHandlerFn(cdc, cliCtx),
).Methods("POST")
}
// query accountREST Handler

View File

@ -1,9 +0,0 @@
package rest
import "github.com/cosmos/cosmos-sdk/x/auth"
// BroadcastReq requests broadcasting a transaction
type BroadcastReq struct {
Tx auth.StdTx `json:"tx"`
Return string `json:"return"` // TODO: Do we need this?
}

View File

@ -1,27 +0,0 @@
package client
import (
"io/ioutil"
"os"
"github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/x/auth"
)
// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin.
func ReadStdTxFromFile(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) {
var bytes []byte
if filename == "-" {
bytes, err = ioutil.ReadAll(os.Stdin)
} else {
bytes, err = ioutil.ReadFile(filename)
}
if err != nil {
return
}
if err = cdc.UnmarshalJSON(bytes, &stdTx); err != nil {
return
}
return
}

View File

@ -1,32 +0,0 @@
package client
import (
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
)
func TestReadStdTxFromFile(t *testing.T) {
cdc := codec.New()
sdk.RegisterCodec(cdc)
// Build a test transaction
fee := auth.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)})
stdTx := auth.NewStdTx([]sdk.Msg{}, fee, []auth.StdSignature{}, "foomemo")
// Write it to the file
encodedTx, _ := cdc.MarshalJSON(stdTx)
jsonTxFile := clitest.WriteToNewTempFile(t, string(encodedTx))
defer os.Remove(jsonTxFile.Name())
// Read it back
decodedTx, err := ReadStdTxFromFile(cdc, jsonTxFile.Name())
require.Nil(t, err)
require.Equal(t, decodedTx.Memo, "foomemo")
}