Skip to content

Commit

Permalink
Merge PR #3523: Add tx/encode endpoint and CLI command
Browse files Browse the repository at this point in the history
  • Loading branch information
ducembarr authored and jackzampolin committed Feb 8, 2019
1 parent d759bef commit 9348750
Show file tree
Hide file tree
Showing 16 changed files with 318 additions and 56 deletions.
4 changes: 3 additions & 1 deletion PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ BREAKING CHANGES
* Reintroduce OR semantics for tx fees

* SDK
* \#2513 Tendermint updates are adjusted by 10^-6 relative to staking tokens,
* \#2513 Tendermint updates are adjusted by 10^-6 relative to staking tokens,
* [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package.
* [\#3490](https://github.com/cosmos/cosmos-sdk/issues/3490) ReadRESTReq() returns bool to avoid callers to write error responses twice.
* [\#3502](https://github.com/cosmos/cosmos-sdk/pull/3502) Fixes issue when comparing genesis states
Expand Down Expand Up @@ -73,13 +73,15 @@ IMPROVEMENTS
* [\#3423](https://github.com/cosmos/cosmos-sdk/issues/3423) Allow simulation
(auto gas) to work with generate only.
* [\#3514](https://github.com/cosmos/cosmos-sdk/pull/3514) REST server calls to keybase does not lock the underlying storage anymore.
* [\#3523](https://github.com/cosmos/cosmos-sdk/pull/3523) Added `/tx/encode` endpoint to serialize a JSON tx to base64-encoded Amino.

* Gaia CLI (`gaiacli`)
* [\#3476](https://github.com/cosmos/cosmos-sdk/issues/3476) New `withdraw-all-rewards` command to withdraw all delegations rewards for delegators.
* [\#3497](https://github.com/cosmos/cosmos-sdk/issues/3497) `gaiad gentx` supports `--ip` and `--node-id` flags to override defaults.
* [\#3518](https://github.com/cosmos/cosmos-sdk/issues/3518) Fix flow in
`keys add` to show the mnemonic by default.
* [\#3517](https://github.com/cosmos/cosmos-sdk/pull/3517) Increased test coverage
* [\#3523](https://github.com/cosmos/cosmos-sdk/pull/3523) Added `tx encode` command to serialize a JSON tx to base64-encoded Amino.

* Gaia
* [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account
Expand Down
41 changes: 41 additions & 0 deletions client/lcd/lcd_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lcd

import (
"encoding/base64"
"encoding/hex"
"fmt"
"net/http"
Expand Down Expand Up @@ -423,6 +424,46 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
require.Equal(t, gasEstimate, resultTx.GasWanted)
}

func TestEncodeTx(t *testing.T) {
// Setup
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
addr, seed := CreateAddr(t, name1, pw, kb)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true)
defer cleanup()

// Make a transaction to test with
res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, "2", 1, false, true, fees)
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)

// Make sure it came back ok, and that we can decode it back to the transaction
// 200 response
require.Equal(t, http.StatusOK, res.StatusCode, body)
encodeResp := struct {
Tx string `json:"tx"`
}{}

// No error decoding the JSON
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &encodeResp))

// Check that the base64 decodes
decodedBytes, err := base64.StdEncoding.DecodeString(encodeResp.Tx)
require.Nil(t, err)

// Check that the transaction decodes as expected
var decodedTx auth.StdTx
require.Nil(t, cdc.UnmarshalBinaryLengthPrefixed(decodedBytes, &decodedTx))
require.Equal(t, memo, decodedTx.Memo)
}

func TestTxs(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
Expand Down
33 changes: 33 additions & 0 deletions client/lcd/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,39 @@ paths:
description: The Tx was malformated
500:
description: Server internal error
/tx/encode:
post:
tags:
- ICS20
summary: Encode a transaction to wire format
description: Encode a transaction (signed or not) from JSON to base64-encoded Amino serialized bytes
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: tx
description: The transaction to encode
required: true
schema:
type: object
properties:
tx:
$ref: "#/definitions/StdTx"
responses:
200:
description: Transaction was successfully decoded and re-encoded
schema:
type: object
properties:
tx:
type: string
example: The base64-encoded Amino-serialized bytes for the transaction
400:
description: The Tx was malformated
500:
description: Server internal error
/bank/balances/{address}:
get:
summary: Get the account balances
Expand Down
71 changes: 55 additions & 16 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package clitest

import (
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"testing"
"time"

Expand All @@ -18,6 +20,7 @@ import (
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/staking"
)
Expand Down Expand Up @@ -587,7 +590,7 @@ func TestGaiaCLIValidateSignatures(t *testing.T) {
require.Empty(t, stderr)

// write unsigned tx to file
unsignedTxFile := writeToNewTempFile(t, stdout)
unsignedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(unsignedTxFile.Name())

// validate we can successfully sign
Expand All @@ -600,7 +603,7 @@ func TestGaiaCLIValidateSignatures(t *testing.T) {
require.Equal(t, fooAddr.String(), stdTx.GetSigners()[0].String())

// write signed tx to file
signedTxFile := writeToNewTempFile(t, stdout)
signedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(signedTxFile.Name())

// validate signatures
Expand All @@ -610,7 +613,7 @@ func TestGaiaCLIValidateSignatures(t *testing.T) {
// modify the transaction
stdTx.Memo = "MODIFIED-ORIGINAL-TX-BAD"
bz := marshalStdTx(t, stdTx)
modSignedTxFile := writeToNewTempFile(t, string(bz))
modSignedTxFile := WriteToNewTempFile(t, string(bz))
defer os.Remove(modSignedTxFile.Name())

// validate signature validation failure due to different transaction sig bytes
Expand Down Expand Up @@ -659,7 +662,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
require.Equal(t, len(msg.Msgs), 1)

// Write the output to disk
unsignedTxFile := writeToNewTempFile(t, stdout)
unsignedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(unsignedTxFile.Name())

// Test sign --validate-signatures
Expand All @@ -676,7 +679,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
require.Equal(t, fooAddr.String(), msg.GetSigners()[0].String())

// Write the output to disk
signedTxFile := writeToNewTempFile(t, stdout)
signedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(signedTxFile.Name())

// Test sign --validate-signatures
Expand Down Expand Up @@ -732,23 +735,23 @@ func TestGaiaCLIMultisignInsufficientCosigners(t *testing.T) {
require.True(t, success)

// Write the output to disk
unsignedTxFile := writeToNewTempFile(t, stdout)
unsignedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(unsignedTxFile.Name())

// Sign with foo's key
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
require.True(t, success)

// Write the output to disk
fooSignatureFile := writeToNewTempFile(t, stdout)
fooSignatureFile := WriteToNewTempFile(t, stdout)
defer os.Remove(fooSignatureFile.Name())

// Multisign, not enough signatures
success, stdout, _ = f.TxMultisign(unsignedTxFile.Name(), keyFooBarBaz, []string{fooSignatureFile.Name()})
require.True(t, success)

// Write the output to disk
signedTxFile := writeToNewTempFile(t, stdout)
signedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(signedTxFile.Name())

// Validate the multisignature
Expand All @@ -760,6 +763,42 @@ func TestGaiaCLIMultisignInsufficientCosigners(t *testing.T) {
require.False(t, success)
}

func TestGaiaCLIEncode(t *testing.T) {
t.Parallel()
f := InitFixtures(t)

// start gaiad server
proc := f.GDStart()
defer proc.Stop(false)

cdc := app.MakeCodec()

// Build a testing transaction and write it to disk
barAddr := f.KeyAddress(keyBar)
sendTokens := staking.TokensFromTendermintPower(10)
success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only", "--memo", "deadbeef")
require.True(t, success)
require.Empty(t, stderr)

// Write it to disk
jsonTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(jsonTxFile.Name())

// Run the encode command, and trim the extras from the stdout capture
success, base64Encoded, _ := f.TxEncode(jsonTxFile.Name())
require.True(t, success)
trimmedBase64 := strings.Trim(base64Encoded, "\"\n")

// Decode the base64
decodedBytes, err := base64.StdEncoding.DecodeString(trimmedBase64)
require.Nil(t, err)

// Check that the transaction decodes as epxceted
var decodedTx auth.StdTx
require.Nil(t, cdc.UnmarshalBinaryLengthPrefixed(decodedBytes, &decodedTx))
require.Equal(t, "deadbeef", decodedTx.Memo)
}

func TestGaiaCLIMultisignSortSignatures(t *testing.T) {
t.Parallel()
f := InitFixtures(t)
Expand All @@ -785,23 +824,23 @@ func TestGaiaCLIMultisignSortSignatures(t *testing.T) {
require.True(t, success)

// Write the output to disk
unsignedTxFile := writeToNewTempFile(t, stdout)
unsignedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(unsignedTxFile.Name())

// Sign with foo's key
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
require.True(t, success)

// Write the output to disk
fooSignatureFile := writeToNewTempFile(t, stdout)
fooSignatureFile := WriteToNewTempFile(t, stdout)
defer os.Remove(fooSignatureFile.Name())

// Sign with baz's key
success, stdout, _ = f.TxSign(keyBaz, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
require.True(t, success)

// Write the output to disk
bazSignatureFile := writeToNewTempFile(t, stdout)
bazSignatureFile := WriteToNewTempFile(t, stdout)
defer os.Remove(bazSignatureFile.Name())

// Multisign, keys in different order
Expand All @@ -810,7 +849,7 @@ func TestGaiaCLIMultisignSortSignatures(t *testing.T) {
require.True(t, success)

// Write the output to disk
signedTxFile := writeToNewTempFile(t, stdout)
signedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(signedTxFile.Name())

// Validate the multisignature
Expand Down Expand Up @@ -848,23 +887,23 @@ func TestGaiaCLIMultisign(t *testing.T) {
require.Empty(t, stderr)

// Write the output to disk
unsignedTxFile := writeToNewTempFile(t, stdout)
unsignedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(unsignedTxFile.Name())

// Sign with foo's key
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
require.True(t, success)

// Write the output to disk
fooSignatureFile := writeToNewTempFile(t, stdout)
fooSignatureFile := WriteToNewTempFile(t, stdout)
defer os.Remove(fooSignatureFile.Name())

// Sign with bar's key
success, stdout, _ = f.TxSign(keyBar, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
require.True(t, success)

// Write the output to disk
barSignatureFile := writeToNewTempFile(t, stdout)
barSignatureFile := WriteToNewTempFile(t, stdout)
defer os.Remove(barSignatureFile.Name())

// Multisign
Expand All @@ -873,7 +912,7 @@ func TestGaiaCLIMultisign(t *testing.T) {
require.True(t, success)

// Write the output to disk
signedTxFile := writeToNewTempFile(t, stdout)
signedTxFile := WriteToNewTempFile(t, stdout)
defer os.Remove(signedTxFile.Name())

// Validate the multisignature
Expand Down
11 changes: 9 additions & 2 deletions cmd/gaia/cli_test/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,12 +294,18 @@ func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, strin
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}

// TxBroadcast is gaiacli tx sign
// TxBroadcast is gaiacli tx broadcast
func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx broadcast %v %v", f.Flags(), fileName)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}

// TxEncode is gaiacli tx encode
func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx encode %v %v", f.Flags(), fileName)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}

// TxMultisign is gaiacli tx multisign
func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string,
flags ...string) (bool, string, string) {
Expand Down Expand Up @@ -640,7 +646,8 @@ func queryTags(tags []string) (out string) {
return strings.TrimSuffix(out, "&")
}

func writeToNewTempFile(t *testing.T, s string) *os.File {
// Write the given string to a new temporary file
func WriteToNewTempFile(t *testing.T, s string) *os.File {
fp, err := ioutil.TempFile(os.TempDir(), "cosmos_cli_test_")
require.Nil(t, err)
_, err = fp.WriteString(s)
Expand Down
3 changes: 2 additions & 1 deletion cmd/gaia/cmd/gaiacli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command {
client.LineBreak,
authcmd.GetSignCommand(cdc),
authcmd.GetMultiSignCommand(cdc),
bankcmd.GetBroadcastCommand(cdc),
authcmd.GetBroadcastCommand(cdc),
authcmd.GetEncodeCommand(cdc),
client.LineBreak,
)

Expand Down
Loading

0 comments on commit 9348750

Please sign in to comment.