Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R4R: Add tx/encode endpoint and CLI command #3523

Merged
merged 1 commit into from
Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -71,13 +71,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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this from a copy pasta?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ICS20 seemed the most reasonable since it's related to creating and broadcasting transactions.

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 @@ -279,12 +279,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 @@ -625,7 +631,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