Skip to content

Commit

Permalink
Add broadcast command #1954
Browse files Browse the repository at this point in the history
  • Loading branch information
Alessio Treglia committed Sep 6, 2018
1 parent d776283 commit bf1fecd
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 13 deletions.
2 changes: 2 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ FEATURES
* [lcd] \#2110 Add support for `simulate=true` requests query argument to endpoints that send txs to run simulations of transactions
* [lcd] \#966 Add support for `generate_only=true` query argument to generate offline unsigned transactions
* [lcd] \#1953 Add /sign endpoint to sign transactions generated with `generate_only=true`.
* [lcd] \#1954 Add /broadcast endpoint to broadcast transactions signed by the /sign endpoint.

* Gaia CLI (`gaiacli`)
* [cli] Cmds to query staking pool and params
Expand All @@ -59,6 +60,7 @@ FEATURES
* [cli] \#2110 Add --dry-run flag to perform a simulation of a transaction without broadcasting it. The --gas flag is ignored as gas would be automatically estimated.
* [cli] \#966 Add --generate-only flag to build an unsigned transaction and write it to STDOUT.
* [cli] \#1953 New `sign` command to sign transactions generated with the --generate-only flag.
* [cli] \#1954 New `broadcast` command to broadcast transactions generated offline and signed with the `sign` command.

* Gaia
* [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address`
Expand Down
19 changes: 17 additions & 2 deletions client/lcd/lcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,12 @@ func TestIBCTransfer(t *testing.T) {
// TODO: query ibc egress packet state
}

func TestCoinSendGenerateAndSign(t *testing.T) {
func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
acc := getAccount(t, port, addr)

// generate TX
res, body, _ := doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?generate_only=true")
Expand All @@ -332,7 +333,6 @@ func TestCoinSendGenerateAndSign(t *testing.T) {

// sign tx
var signedMsg auth.StdTx
acc := getAccount(t, port, addr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()

Expand All @@ -353,6 +353,21 @@ func TestCoinSendGenerateAndSign(t *testing.T) {
require.Equal(t, msg.Msgs[0].Type(), signedMsg.Msgs[0].Type())
require.Equal(t, msg.Msgs[0].GetSigners(), signedMsg.Msgs[0].GetSigners())
require.Equal(t, 1, len(signedMsg.Signatures))

// broadcast tx
broadcastPayload := struct {
Tx auth.StdTx `json:"tx"`
}{Tx: signedMsg}
json, err = cdc.MarshalJSON(broadcastPayload)
require.Nil(t, err)
res, body = Request(t, port, "POST", "/broadcast", json)
require.Equal(t, http.StatusOK, res.StatusCode, body)

// check if tx was committed
var resultTx ctypes.ResultBroadcastTxCommit
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx))
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
}

func TestTxs(t *testing.T) {
Expand Down
33 changes: 23 additions & 10 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, " 2 - Apples", proposalsQuery)
}

func TestGaiaCLISendGenerateAndSign(t *testing.T) {
func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
chainID, servAddr, port := initializeFixtures(t)
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)

Expand All @@ -358,26 +358,26 @@ func TestGaiaCLISendGenerateAndSign(t *testing.T) {
require.Equal(t, len(msg.Msgs), 1)
require.Equal(t, 0, len(msg.GetSignatures()))

// Test generate sendTx, estimate gas
// Test generate sendTx with --gas=$amount
success, stdout, stderr = executeWriteRetStdStreams(t, fmt.Sprintf(
"gaiacli send %v --amount=10steak --to=%s --from=foo --gas=0 --generate-only",
"gaiacli send %v --amount=10steak --to=%s --from=foo --gas=100 --generate-only",
flags, barAddr), []string{}...)
require.True(t, success)
require.NotEmpty(t, stderr)
require.Empty(t, stderr)
msg = unmarshalStdTx(t, stdout)
require.NotZero(t, msg.Fee.Gas)
require.Equal(t, msg.Fee.Gas, int64(100))
require.Equal(t, len(msg.Msgs), 1)
require.Equal(t, 0, len(msg.GetSignatures()))

// Test generate sendTx with --gas=$amount
// Test generate sendTx, estimate gas
success, stdout, stderr = executeWriteRetStdStreams(t, fmt.Sprintf(
"gaiacli send %v --amount=10steak --to=%s --from=foo --gas=100 --generate-only",
"gaiacli send %v --amount=10steak --to=%s --from=foo --gas=0 --generate-only",
flags, barAddr), []string{}...)
require.True(t, success)
require.Empty(t, stderr)
require.NotEmpty(t, stderr)
msg = unmarshalStdTx(t, stdout)
require.Equal(t, msg.Fee.Gas, int64(100))
require.NotZero(t, msg.Fee.Gas)
require.Equal(t, len(msg.Msgs), 1)
require.Equal(t, 0, len(msg.GetSignatures()))

// Write the output to disk
unsignedTxFile := writeToNewTempFile(t, stdout)
Expand Down Expand Up @@ -407,6 +407,19 @@ func TestGaiaCLISendGenerateAndSign(t *testing.T) {
"gaiacli sign %v --print-sigs %v", flags, signedTxFile.Name()))
require.True(t, success)
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\n", fooAddr.String(), fooAddr.String()), stdout)

// Test broadcast
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())

success = executeWrite(t, fmt.Sprintf("gaiacli broadcast %v %v", flags, signedTxFile.Name()))
require.True(t, success)
tests.WaitForNextNBlocksTM(2, port)

barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64())
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
}

//___________________________________________________________________________________
Expand Down
1 change: 1 addition & 0 deletions cmd/gaia/cmd/gaiacli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func main() {
client.GetCommands(
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)),
authcmd.GetBroadcastCommand(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
Expand Down
1 change: 0 additions & 1 deletion docs/sdk/clients.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ gaiacli sign \
unsignedSendTx.json > signedSendTx.json
```


### Staking

#### Set up a Validator
Expand Down
33 changes: 33 additions & 0 deletions x/auth/client/cli/broadcast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cli

import (
"os"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/spf13/cobra"
amino "github.com/tendermint/go-amino"
)

// GetSignCommand returns the sign command
func GetBroadcastCommand(codec *amino.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "broadcast <file>",
Short: "Broadcast transactions generated offline",
Long: `Broadcast transactions created with the --generate-only flag and signed with the sign command.
Read a transaction from <file> and broadcast it to a node.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
cliCtx := context.NewCLIContext().WithCodec(codec).WithLogger(os.Stdout)
stdTx, err := readAndUnmarshalStdTx(cliCtx.Codec, args[0])
if err != nil {
return
}
txBytes, err := cliCtx.Codec.MarshalBinary(stdTx)
if err != nil {
return
}
return cliCtx.EnsureBroadcastTx(txBytes)
},
}
return cmd
}
57 changes: 57 additions & 0 deletions x/auth/client/rest/broadcast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package rest

import (
"io/ioutil"
"net/http"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
)

type broadcastBody struct {
Tx auth.StdTx `json:"tx"`
}

// BroadcastTxRequestHandlerFn returns the broadcast tx REST handler
func BroadcastTxRequestHandlerFn(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.MarshalBinary(m.Tx)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

output, err := wire.MarshalJSONIndent(cliCtx.Codec, res)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
w.Write(output)
}
}

func unmarshalBodyOrReturnBadRequest(cliCtx context.CLIContext, w http.ResponseWriter, r *http.Request, m *broadcastBody) bool {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return false
}
err = cliCtx.Codec.UnmarshalJSON(body, m)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return false
}
return true
}
4 changes: 4 additions & 0 deletions x/auth/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, s
"/sign",
SignTxRequestHandlerFn(cdc, cliCtx),
).Methods("POST")
r.HandleFunc(
"/broadcast",
BroadcastTxRequestHandlerFn(cliCtx),
).Methods("POST")
}

// query accountREST Handler
Expand Down

0 comments on commit bf1fecd

Please sign in to comment.