The command processes list of transactions from file (one StdTx each line), generate signed transactions or signatures and print their JSON encoding, delimited by '\n'. As the signatures are generated, the command increments the sequence number automatically. Author: @jgimeno Reviewed-by: @alessio
452 lines
15 KiB
Go
452 lines
15 KiB
Go
// +build cli_test
|
|
|
|
package cli_test
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
|
"github.com/cosmos/cosmos-sdk/tests"
|
|
"github.com/cosmos/cosmos-sdk/tests/cli"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/client/testutil"
|
|
bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil"
|
|
)
|
|
|
|
func TestCLIValidateSignatures(t *testing.T) {
|
|
t.Parallel()
|
|
f := cli.InitFixtures(t)
|
|
|
|
// start simd server
|
|
proc := f.SDStart()
|
|
t.Cleanup(func() { proc.Stop(false) })
|
|
|
|
f.ValidateGenesis()
|
|
|
|
fooAddr := f.KeyAddress(cli.KeyFoo)
|
|
barAddr := f.KeyAddress(cli.KeyBar)
|
|
|
|
// generate sendTx with default gas
|
|
success, stdout, stderr := bankcli.TxSend(f, fooAddr.String(), barAddr, sdk.NewInt64Coin("stake", 10), "--generate-only")
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
|
|
// write unsigned tx to file
|
|
unsignedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// validate we can successfully sign
|
|
success, stdout, _ = testutil.TxSign(f, cli.KeyFoo, unsignedTxFile.Name())
|
|
require.True(t, success)
|
|
|
|
stdTx := cli.UnmarshalStdTx(t, f.Cdc, stdout)
|
|
|
|
require.Equal(t, len(stdTx.Msgs), 1)
|
|
require.Equal(t, 1, len(stdTx.GetSignatures()))
|
|
require.Equal(t, fooAddr.String(), stdTx.GetSigners()[0].String())
|
|
|
|
// write signed tx to file
|
|
signedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// validate signatures
|
|
success, _, _ = testutil.TxValidateSignatures(f, signedTxFile.Name())
|
|
require.True(t, success)
|
|
|
|
// modify the transaction
|
|
stdTx.Memo = "MODIFIED-ORIGINAL-TX-BAD"
|
|
bz := cli.MarshalStdTx(t, f.Cdc, stdTx)
|
|
modSignedTxFile, cleanup := tests.WriteToNewTempFile(t, string(bz))
|
|
t.Cleanup(cleanup)
|
|
|
|
// validate signature validation failure due to different transaction sig bytes
|
|
success, _, _ = testutil.TxValidateSignatures(f, modSignedTxFile.Name())
|
|
require.False(t, success)
|
|
|
|
// Cleanup testing directories
|
|
f.Cleanup()
|
|
}
|
|
|
|
func TestCLISignBatch(t *testing.T) {
|
|
t.Parallel()
|
|
f := cli.InitFixtures(t)
|
|
|
|
fooAddr := f.KeyAddress(cli.KeyFoo)
|
|
barAddr := f.KeyAddress(cli.KeyBar)
|
|
|
|
sendTokens := sdk.TokensFromConsensusPower(10)
|
|
success, generatedStdTx, stderr := bankcli.TxSend(f, fooAddr.String(), barAddr, sdk.NewCoin(cli.Denom, sendTokens), "--generate-only")
|
|
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
|
|
// Write the output to disk
|
|
batchfile, cleanup1 := tests.WriteToNewTempFile(t, strings.Repeat(generatedStdTx, 3))
|
|
t.Cleanup(cleanup1)
|
|
|
|
// sign-batch file - offline is set but account-number and sequence are not
|
|
success, _, stderr = testutil.TxSignBatch(f, cli.KeyFoo, batchfile.Name(), "--offline")
|
|
require.Contains(t, stderr, "required flag(s) \"account-number\", \"sequence\" not set")
|
|
require.False(t, success)
|
|
|
|
// sign-batch file
|
|
success, stdout, stderr := testutil.TxSignBatch(f, cli.KeyFoo, batchfile.Name())
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
require.Equal(t, 3, len(strings.Split(strings.Trim(stdout, "\n"), "\n")))
|
|
|
|
// sign-batch file
|
|
success, stdout, stderr = testutil.TxSignBatch(f, cli.KeyFoo, batchfile.Name(), "--signature-only")
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
require.Equal(t, 3, len(strings.Split(strings.Trim(stdout, "\n"), "\n")))
|
|
|
|
malformedFile, cleanup2 := tests.WriteToNewTempFile(t, fmt.Sprintf("%smalformed", generatedStdTx))
|
|
t.Cleanup(cleanup2)
|
|
|
|
// sign-batch file
|
|
success, stdout, stderr = testutil.TxSignBatch(f, cli.KeyFoo, malformedFile.Name())
|
|
require.False(t, success)
|
|
require.Equal(t, 1, len(strings.Split(strings.Trim(stdout, "\n"), "\n")))
|
|
require.Equal(t, "ERROR: cannot parse disfix JSON wrapper: invalid character 'm' looking for beginning of value\n", stderr)
|
|
|
|
// sign-batch file
|
|
success, stdout, _ = testutil.TxSignBatch(f, cli.KeyFoo, malformedFile.Name(), "--signature-only")
|
|
require.False(t, success)
|
|
require.Equal(t, 1, len(strings.Split(strings.Trim(stdout, "\n"), "\n")))
|
|
|
|
f.Cleanup()
|
|
}
|
|
|
|
func TestCLISendGenerateSignAndBroadcast(t *testing.T) {
|
|
t.Parallel()
|
|
f := cli.InitFixtures(t)
|
|
|
|
// start simd server
|
|
proc := f.SDStart()
|
|
t.Cleanup(func() { proc.Stop(false) })
|
|
|
|
fooAddr := f.KeyAddress(cli.KeyFoo)
|
|
barAddr := f.KeyAddress(cli.KeyBar)
|
|
|
|
// Test generate sendTx with default gas
|
|
sendTokens := sdk.TokensFromConsensusPower(10)
|
|
success, stdout, stderr := bankcli.TxSend(f, fooAddr.String(), barAddr, sdk.NewCoin(cli.Denom, sendTokens), "--generate-only")
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
msg := cli.UnmarshalStdTx(t, f.Cdc, stdout)
|
|
require.Equal(t, msg.Fee.Gas, uint64(flags.DefaultGasLimit))
|
|
require.Equal(t, len(msg.Msgs), 1)
|
|
require.Equal(t, 0, len(msg.GetSignatures()))
|
|
|
|
// Test generate sendTx with --gas=$amount
|
|
success, stdout, stderr = bankcli.TxSend(f, fooAddr.String(), barAddr, sdk.NewCoin(cli.Denom, sendTokens), "--gas=100", "--generate-only")
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
|
|
msg = cli.UnmarshalStdTx(t, f.Cdc, stdout)
|
|
require.Equal(t, msg.Fee.Gas, uint64(100))
|
|
require.Equal(t, len(msg.Msgs), 1)
|
|
require.Equal(t, 0, len(msg.GetSignatures()))
|
|
|
|
// Test generate sendTx, estimate gas
|
|
success, stdout, stderr = bankcli.TxSend(f, fooAddr.String(), barAddr, sdk.NewCoin(cli.Denom, sendTokens), "--generate-only")
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
msg = cli.UnmarshalStdTx(t, f.Cdc, stdout)
|
|
require.True(t, msg.Fee.Gas > 0)
|
|
require.Equal(t, len(msg.Msgs), 1)
|
|
|
|
// Write the output to disk
|
|
unsignedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Test validate-signatures
|
|
success, stdout, _ = testutil.TxValidateSignatures(f, unsignedTxFile.Name())
|
|
require.False(t, success)
|
|
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout)
|
|
|
|
// Test sign
|
|
|
|
// Does not work in offline mode
|
|
success, stdout, stderr = testutil.TxSign(f, cli.KeyFoo, unsignedTxFile.Name(), "--offline")
|
|
require.Contains(t, stderr, "required flag(s) \"account-number\", \"sequence\" not set")
|
|
require.False(t, success)
|
|
|
|
// But works offline if we set account number and sequence
|
|
success, _, _ = testutil.TxSign(f, cli.KeyFoo, unsignedTxFile.Name(), "--offline", "--account-number", "1", "--sequence", "1")
|
|
require.True(t, success)
|
|
|
|
// Sign transaction
|
|
success, stdout, _ = testutil.TxSign(f, cli.KeyFoo, unsignedTxFile.Name())
|
|
require.True(t, success)
|
|
msg = cli.UnmarshalStdTx(t, f.Cdc, stdout)
|
|
require.Equal(t, len(msg.Msgs), 1)
|
|
require.Equal(t, 1, len(msg.GetSignatures()))
|
|
require.Equal(t, fooAddr.String(), msg.GetSigners()[0].String())
|
|
|
|
// Write the output to disk
|
|
signedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Test validate-signatures
|
|
success, stdout, _ = testutil.TxValidateSignatures(f, signedTxFile.Name())
|
|
require.True(t, success)
|
|
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t\t\t[OK]\n\n", fooAddr.String(),
|
|
fooAddr.String()), stdout)
|
|
|
|
// Ensure foo has right amount of funds
|
|
startTokens := sdk.TokensFromConsensusPower(50)
|
|
require.Equal(t, startTokens, bankcli.QueryBalances(f, fooAddr).AmountOf(cli.Denom))
|
|
|
|
// Test broadcast
|
|
|
|
// Does not work in offline mode
|
|
success, _, stderr = testutil.TxBroadcast(f, signedTxFile.Name(), "--offline")
|
|
require.Contains(t, stderr, "cannot broadcast tx during offline mode")
|
|
require.False(t, success)
|
|
tests.WaitForNextNBlocksTM(1, f.Port)
|
|
|
|
success, stdout, _ = testutil.TxBroadcast(f, signedTxFile.Name())
|
|
require.True(t, success)
|
|
tests.WaitForNextNBlocksTM(1, f.Port)
|
|
|
|
// Ensure account state
|
|
require.Equal(t, sendTokens, bankcli.QueryBalances(f, barAddr).AmountOf(cli.Denom))
|
|
require.Equal(t, startTokens.Sub(sendTokens), bankcli.QueryBalances(f, fooAddr).AmountOf(cli.Denom))
|
|
|
|
f.Cleanup()
|
|
}
|
|
|
|
func TestCLIMultisignInsufficientCosigners(t *testing.T) {
|
|
t.Parallel()
|
|
f := cli.InitFixtures(t)
|
|
|
|
// start simd server with minimum fees
|
|
proc := f.SDStart()
|
|
t.Cleanup(func() { proc.Stop(false) })
|
|
|
|
fooBarBazAddr := f.KeyAddress(cli.KeyFooBarBaz)
|
|
barAddr := f.KeyAddress(cli.KeyBar)
|
|
|
|
// Send some tokens from one account to the other
|
|
success, _, _ := bankcli.TxSend(f, cli.KeyFoo, fooBarBazAddr, sdk.NewInt64Coin(cli.Denom, 10), "-y")
|
|
require.True(t, success)
|
|
tests.WaitForNextNBlocksTM(1, f.Port)
|
|
|
|
// Test generate sendTx with multisig
|
|
success, stdout, _ := bankcli.TxSend(f, fooBarBazAddr.String(), barAddr, sdk.NewInt64Coin(cli.Denom, 5), "--generate-only")
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
unsignedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Sign with foo's key
|
|
success, stdout, _ = testutil.TxSign(f, cli.KeyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y")
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
fooSignatureFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Multisign, not enough signatures
|
|
success, stdout, _ = testutil.TxMultisign(f, unsignedTxFile.Name(), cli.KeyFooBarBaz, []string{fooSignatureFile.Name()})
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
signedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Validate the multisignature
|
|
success, _, _ = testutil.TxValidateSignatures(f, signedTxFile.Name())
|
|
require.False(t, success)
|
|
|
|
// Broadcast the transaction
|
|
success, stdOut, _ := testutil.TxBroadcast(f, signedTxFile.Name())
|
|
require.Contains(t, stdOut, "signature verification failed")
|
|
require.True(t, success)
|
|
|
|
// Cleanup testing directories
|
|
f.Cleanup()
|
|
}
|
|
|
|
func TestCLIEncode(t *testing.T) {
|
|
t.Parallel()
|
|
f := cli.InitFixtures(t)
|
|
|
|
// start simd server
|
|
proc := f.SDStart()
|
|
t.Cleanup(func() { proc.Stop(false) })
|
|
|
|
// Build a testing transaction and write it to disk
|
|
barAddr := f.KeyAddress(cli.KeyBar)
|
|
keyAddr := f.KeyAddress(cli.KeyFoo)
|
|
|
|
sendTokens := sdk.TokensFromConsensusPower(10)
|
|
success, stdout, stderr := bankcli.TxSend(f, keyAddr.String(), barAddr, sdk.NewCoin(cli.Denom, sendTokens), "--generate-only", "--memo", "deadbeef")
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
|
|
// Write it to disk
|
|
jsonTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Run the encode command, and trim the extras from the stdout capture
|
|
success, base64Encoded, _ := testutil.TxEncode(f, 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, f.Cdc.UnmarshalBinaryBare(decodedBytes, &decodedTx))
|
|
require.Equal(t, "deadbeef", decodedTx.Memo)
|
|
}
|
|
|
|
func TestCLIMultisignSortSignatures(t *testing.T) {
|
|
t.Parallel()
|
|
f := cli.InitFixtures(t)
|
|
|
|
// start simd server with minimum fees
|
|
proc := f.SDStart()
|
|
t.Cleanup(func() { proc.Stop(false) })
|
|
|
|
fooBarBazAddr := f.KeyAddress(cli.KeyFooBarBaz)
|
|
barAddr := f.KeyAddress(cli.KeyBar)
|
|
|
|
// Send some tokens from one account to the other
|
|
success, _, _ := bankcli.TxSend(f, cli.KeyFoo, fooBarBazAddr, sdk.NewInt64Coin(cli.Denom, 10), "-y")
|
|
require.True(t, success)
|
|
tests.WaitForNextNBlocksTM(1, f.Port)
|
|
|
|
// Ensure account balances match expected
|
|
require.Equal(t, int64(10), bankcli.QueryBalances(f, fooBarBazAddr).AmountOf(cli.Denom).Int64())
|
|
|
|
// Test generate sendTx with multisig
|
|
success, stdout, _ := bankcli.TxSend(f, fooBarBazAddr.String(), barAddr, sdk.NewInt64Coin(cli.Denom, 5), "--generate-only")
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
unsignedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Sign with foo's key
|
|
success, stdout, _ = testutil.TxSign(f, cli.KeyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
fooSignatureFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Sign with baz's key
|
|
success, stdout, _ = testutil.TxSign(f, cli.KeyBaz, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String())
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
bazSignatureFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Multisign, keys in different order
|
|
success, stdout, _ = testutil.TxMultisign(f, unsignedTxFile.Name(), cli.KeyFooBarBaz, []string{
|
|
bazSignatureFile.Name(), fooSignatureFile.Name()})
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
signedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Validate the multisignature
|
|
success, _, _ = testutil.TxValidateSignatures(f, signedTxFile.Name())
|
|
require.True(t, success)
|
|
|
|
// Broadcast the transaction
|
|
success, _, _ = testutil.TxBroadcast(f, signedTxFile.Name())
|
|
require.True(t, success)
|
|
|
|
// Cleanup testing directories
|
|
f.Cleanup()
|
|
}
|
|
|
|
func TestCLIMultisign(t *testing.T) {
|
|
t.Parallel()
|
|
f := cli.InitFixtures(t)
|
|
|
|
// start simd server with minimum fees
|
|
proc := f.SDStart()
|
|
t.Cleanup(func() { proc.Stop(false) })
|
|
|
|
fooBarBazAddr := f.KeyAddress(cli.KeyFooBarBaz)
|
|
bazAddr := f.KeyAddress(cli.KeyBaz)
|
|
|
|
// Send some tokens from one account to the other
|
|
success, _, _ := bankcli.TxSend(f, cli.KeyFoo, fooBarBazAddr, sdk.NewInt64Coin(cli.Denom, 10), "-y")
|
|
require.True(t, success)
|
|
tests.WaitForNextNBlocksTM(1, f.Port)
|
|
|
|
// Ensure account balances match expected
|
|
require.Equal(t, int64(10), bankcli.QueryBalances(f, fooBarBazAddr).AmountOf(cli.Denom).Int64())
|
|
|
|
// Test generate sendTx with multisig
|
|
success, stdout, stderr := bankcli.TxSend(f, fooBarBazAddr.String(), bazAddr, sdk.NewInt64Coin(cli.Denom, 10), "--generate-only")
|
|
require.True(t, success)
|
|
require.Empty(t, stderr)
|
|
|
|
// Write the output to disk
|
|
unsignedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Sign with foo's key
|
|
success, stdout, _ = testutil.TxSign(f, cli.KeyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y")
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
fooSignatureFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Sign with bar's key
|
|
success, stdout, _ = testutil.TxSign(f, cli.KeyBar, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y")
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
barSignatureFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Multisign
|
|
|
|
// Does not work in offline mode
|
|
success, stdout, _ = testutil.TxMultisign(f, unsignedTxFile.Name(), cli.KeyFooBarBaz, []string{
|
|
fooSignatureFile.Name(), barSignatureFile.Name()}, "--offline")
|
|
require.Contains(t, "couldn't verify signature", stdout)
|
|
require.False(t, success)
|
|
|
|
// Success multisign
|
|
success, stdout, _ = testutil.TxMultisign(f, unsignedTxFile.Name(), cli.KeyFooBarBaz, []string{
|
|
fooSignatureFile.Name(), barSignatureFile.Name()})
|
|
require.True(t, success)
|
|
|
|
// Write the output to disk
|
|
signedTxFile, cleanup := tests.WriteToNewTempFile(t, stdout)
|
|
t.Cleanup(cleanup)
|
|
|
|
// Validate the multisignature
|
|
success, _, _ = testutil.TxValidateSignatures(f, signedTxFile.Name())
|
|
require.True(t, success)
|
|
|
|
// Broadcast the transaction
|
|
success, _, _ = testutil.TxBroadcast(f, signedTxFile.Name())
|
|
require.True(t, success)
|
|
|
|
// Cleanup testing directories
|
|
f.Cleanup()
|
|
}
|