Merge PR #3696: Cleanup Tx Broadcasting and Encoding
This commit is contained in:
parent
feb98bcd05
commit
6ace1fada2
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
61
client/lcd/swagger-ui/swagger.yaml
vendored
61
client/lcd/swagger-ui/swagger.yaml
vendored
@ -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}:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
107
client/tx/encode.go
Normal 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]
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
|
||||
@ -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]
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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?
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user