Merge PR #3523: Add tx/encode endpoint and CLI command
This commit is contained in:
parent
d759bef4d1
commit
9348750eb4
|
@ -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
|
||||
|
@ -73,6 +73,7 @@ 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.
|
||||
|
@ -80,6 +81,7 @@ IMPROVEMENTS
|
|||
* [\#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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package lcd
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package clitest
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -732,7 +735,7 @@ 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
|
||||
|
@ -740,7 +743,7 @@ func TestGaiaCLIMultisignInsufficientCosigners(t *testing.T) {
|
|||
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
|
||||
|
@ -748,7 +751,7 @@ func TestGaiaCLIMultisignInsufficientCosigners(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
|
||||
|
@ -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)
|
||||
|
@ -785,7 +824,7 @@ 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
|
||||
|
@ -793,7 +832,7 @@ func TestGaiaCLIMultisignSortSignatures(t *testing.T) {
|
|||
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
|
||||
|
@ -801,7 +840,7 @@ func TestGaiaCLIMultisignSortSignatures(t *testing.T) {
|
|||
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
|
||||
|
@ -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
|
||||
|
@ -848,7 +887,7 @@ 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
|
||||
|
@ -856,7 +895,7 @@ func TestGaiaCLIMultisign(t *testing.T) {
|
|||
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
|
||||
|
@ -864,7 +903,7 @@ func TestGaiaCLIMultisign(t *testing.T) {
|
|||
require.True(t, success)
|
||||
|
||||
// Write the output to disk
|
||||
barSignatureFile := writeToNewTempFile(t, stdout)
|
||||
barSignatureFile := WriteToNewTempFile(t, stdout)
|
||||
defer os.Remove(barSignatureFile.Name())
|
||||
|
||||
// Multisign
|
||||
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -10,7 +8,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
||||
)
|
||||
|
||||
// GetSignCommand returns the sign command
|
||||
|
@ -27,7 +25,7 @@ $ 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 := readAndUnmarshalStdTx(cliCtx.Codec, args[0])
|
||||
stdTx, err := authclient.ReadStdTxFromFile(cliCtx.Codec, args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -45,19 +43,3 @@ $ gaiacli tx broadcast ./mytxn.json
|
|||
|
||||
return client.PostCommands(cmd)[0]
|
||||
}
|
||||
|
||||
func readAndUnmarshalStdTx(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
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
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]
|
||||
}
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
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"
|
||||
)
|
||||
|
||||
|
@ -51,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 := readAndUnmarshalStdTx(cdc, args[0])
|
||||
stdTx, err := authclient.ReadStdTxFromFile(cdc, args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package cli
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -15,6 +14,7 @@ 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"
|
||||
)
|
||||
|
||||
|
@ -75,7 +75,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 := readAndUnmarshalStdTx(cdc, args[0])
|
||||
stdTx, err := authclient.ReadStdTxFromFile(cdc, args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -222,14 +222,3 @@ func printAndValidateSigs(
|
|||
fmt.Println("")
|
||||
return success
|
||||
}
|
||||
|
||||
func readAndUnmarshalStdTx(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) {
|
||||
var bytes []byte
|
||||
if bytes, err = ioutil.ReadFile(filename); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cdc.UnmarshalJSON(bytes, &stdTx); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func BroadcastTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) ht
|
|||
}
|
||||
}
|
||||
|
||||
func unmarshalBodyOrReturnBadRequest(cliCtx context.CLIContext, w http.ResponseWriter, r *http.Request, m *broadcastBody) bool {
|
||||
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())
|
|
@ -0,0 +1,46 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/rest"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"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)
|
||||
}
|
||||
}
|
|
@ -22,6 +22,14 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec,
|
|||
"/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")
|
||||
r.HandleFunc(
|
||||
"/tx/sign",
|
||||
SignTxRequestHandlerFn(cdc, cliCtx),
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
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")
|
||||
}
|
|
@ -17,7 +17,6 @@ import (
|
|||
// RegisterRoutes - Central function to define routes that get registered by the main application
|
||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) {
|
||||
r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(cdc, kb, cliCtx)).Methods("POST")
|
||||
r.HandleFunc("/tx/broadcast", BroadcastTxRequestHandlerFn(cdc, cliCtx)).Methods("POST")
|
||||
}
|
||||
|
||||
type sendReq struct {
|
||||
|
|
Loading…
Reference in New Issue