fix: ` --dry-run` not working when using tx command (#11558)

## Description

Closes: #11149

- Let `--dry-run` not use the local keyring
- Fixes for required flags not shown
- Make `--generate-only` behavior consistent
---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [x] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
Julien Robert 2022-04-11 17:50:54 +02:00 committed by GitHub
parent 56b0e837cc
commit 4f1cc3aeac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 150 additions and 23 deletions

View File

@ -59,7 +59,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination.
* (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes.
* (store) [\#8664](https://github.com/cosmos/cosmos-sdk/pull/8664) Implementation of ADR-038 file StreamingService
* [\#9837](https://github.com/cosmos/cosmos-sdk/issues/9837) `--generate-only` flag will accept the keyname now.
* [\#9837](https://github.com/cosmos/cosmos-sdk/issues/9837) `--generate-only` flag can be used with a keyname from the keyring.
* [\#10326](https://github.com/cosmos/cosmos-sdk/pull/10326) `x/authz` add all grants by granter query.
* [\#10944](https://github.com/cosmos/cosmos-sdk/pull/10944) `x/authz` add all grants by grantee query
* [\#10348](https://github.com/cosmos/cosmos-sdk/pull/10348) Add `fee.{payer,granter}` and `tip` fields to StdSignDoc for signing tipped transactions.
@ -205,6 +205,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Bug Fixes
* [\#11558](https://github.com/cosmos/cosmos-sdk/pull/11558) Fix `--dry-run` not working when using tx command.
* [\#11354](https://github.com/cosmos/cosmos-sdk/pull/11355) Added missing pagination flag for `bank q total` query.
* [\#11197](https://github.com/cosmos/cosmos-sdk/pull/11197) Signing with multisig now works with multisig address which is not in the keyring.
* (makefile) [\#11285](https://github.com/cosmos/cosmos-sdk/pull/11285) Fix lint-fix make target.

View File

@ -248,7 +248,7 @@ func readTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, err
if clientCtx.From == "" || flagSet.Changed(flags.FlagFrom) {
from, _ := flagSet.GetString(flags.FlagFrom)
fromAddr, fromName, keyType, err := GetFromFields(clientCtx.Keyring, from, clientCtx.GenerateOnly)
fromAddr, fromName, keyType, err := GetFromFields(clientCtx, clientCtx.Keyring, from)
if err != nil {
return clientCtx, err
}

View File

@ -2,6 +2,7 @@ package client
import (
"bufio"
"fmt"
"io"
"os"
@ -52,9 +53,9 @@ type Context struct {
FeePayer sdk.AccAddress
FeeGranter sdk.AccAddress
Viper *viper.Viper
// IsAux is true when the signer is an auxiliary signer (e.g. the tipper).
IsAux bool
IsAux bool
// TODO: Deprecated (remove).
LegacyAmino *codec.LegacyAmino
@ -334,16 +335,31 @@ func (ctx Context) printOutput(out []byte) error {
return nil
}
// GetFromFields returns a from account address, account name and keyring type, given either
// an address or key name. If genOnly is true, only a valid Bech32 cosmos
// address is returned.
func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddress, string, keyring.KeyType, error) {
// GetFromFields returns a from account address, account name and keyring type, given either an address or key name.
// If clientCtx.Simulate is true the keystore is not accessed and a valid address must be provided
// If clientCtx.GenerateOnly is true the keystore is only accessed if a key name is provided
func GetFromFields(clientCtx Context, kr keyring.Keyring, from string) (sdk.AccAddress, string, keyring.KeyType, error) {
if from == "" {
return nil, "", 0, nil
}
addr, err := sdk.AccAddressFromBech32(from)
switch {
case clientCtx.Simulate:
if err != nil {
return nil, "", 0, fmt.Errorf("a valid bech32 address must be provided in simulation mode: %w", err)
}
return addr, "", 0, nil
case clientCtx.GenerateOnly:
if err == nil {
return addr, "", 0, nil
}
}
var k *keyring.Record
if addr, err := sdk.AccAddressFromBech32(from); err == nil {
if err == nil {
k, err = kr.KeyByAddress(addr)
if err != nil {
return nil, "", 0, err
@ -355,7 +371,7 @@ func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddres
}
}
addr, err := k.GetAddress()
addr, err = k.GetAddress()
if err != nil {
return nil, "", 0, err
}
@ -365,7 +381,7 @@ func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddres
// NewKeyringFromBackend gets a Keyring object from a backend
func NewKeyringFromBackend(ctx Context, backend string) (keyring.Keyring, error) {
if ctx.GenerateOnly || ctx.Simulate {
if ctx.Simulate {
backend = keyring.BackendMemory
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"context"
"os"
"strings"
"testing"
"github.com/spf13/viper"
@ -13,6 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/testutil/network"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
@ -114,3 +116,107 @@ func TestCLIQueryConn(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "hello", res.Message)
}
func TestGetFromFields(t *testing.T) {
cfg := network.DefaultConfig()
path := hd.CreateHDPath(118, 0, 0).String()
testCases := []struct {
clientCtx client.Context
keyring func() keyring.Keyring
from string
expectedErr string
}{
{
keyring: func() keyring.Keyring {
kb := keyring.NewInMemory(cfg.Codec)
_, _, err := kb.NewMnemonic("alice", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
require.NoError(t, err)
return kb
},
from: "alice",
},
{
keyring: func() keyring.Keyring {
kb, err := keyring.New(t.Name(), keyring.BackendTest, t.TempDir(), nil, cfg.Codec)
require.NoError(t, err)
_, _, err = kb.NewMnemonic("alice", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
require.NoError(t, err)
return kb
},
from: "alice",
},
{
keyring: func() keyring.Keyring {
return keyring.NewInMemory(cfg.Codec)
},
from: "cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5",
expectedErr: "key with address cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5 not found: key not found",
},
{
keyring: func() keyring.Keyring {
kb, err := keyring.New(t.Name(), keyring.BackendTest, t.TempDir(), nil, cfg.Codec)
require.NoError(t, err)
return kb
},
from: "alice",
expectedErr: "alice.info: key not found",
},
{
keyring: func() keyring.Keyring {
return keyring.NewInMemory(cfg.Codec)
},
from: "cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5",
clientCtx: client.Context{}.WithSimulation(true),
},
{
keyring: func() keyring.Keyring {
return keyring.NewInMemory(cfg.Codec)
},
from: "alice",
clientCtx: client.Context{}.WithSimulation(true),
expectedErr: "a valid bech32 address must be provided in simulation mode",
},
{
keyring: func() keyring.Keyring {
return keyring.NewInMemory(cfg.Codec)
},
from: "cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5",
clientCtx: client.Context{}.WithGenerateOnly(true),
},
{
keyring: func() keyring.Keyring {
return keyring.NewInMemory(cfg.Codec)
},
from: "alice",
clientCtx: client.Context{}.WithGenerateOnly(true),
expectedErr: "alice.info: key not found",
},
{
keyring: func() keyring.Keyring {
kb, err := keyring.New(t.Name(), keyring.BackendTest, t.TempDir(), nil, cfg.Codec)
require.NoError(t, err)
_, _, err = kb.NewMnemonic("alice", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
require.NoError(t, err)
return kb
},
clientCtx: client.Context{}.WithGenerateOnly(true),
from: "alice",
},
}
for _, tc := range testCases {
_, _, _, err := client.GetFromFields(tc.clientCtx, tc.keyring(), tc.from)
if tc.expectedErr == "" {
require.NoError(t, err)
} else {
require.True(t, strings.HasPrefix(err.Error(), tc.expectedErr))
}
}
}

View File

@ -112,8 +112,8 @@ func AddTxFlagsToCmd(cmd *cobra.Command) {
cmd.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
cmd.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
cmd.Flags().StringP(FlagBroadcastMode, "b", BroadcastSync, "Transaction broadcasting mode (sync|async|block)")
cmd.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
cmd.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible)")
cmd.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible)")
cmd.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name)")
cmd.Flags().Bool(FlagOffline, false, "Offline mode (does not allow any online functionality)")
cmd.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation")
cmd.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test|memory)")

View File

@ -337,7 +337,7 @@ func (gr GasEstimateResponse) String() string {
// makeAuxSignerData generates an AuxSignerData from the client inputs.
func makeAuxSignerData(clientCtx client.Context, f Factory, msgs ...sdk.Msg) (tx.AuxSignerData, error) {
b := NewAuxTxBuilder()
fromAddress, name, _, err := client.GetFromFields(clientCtx.Keyring, clientCtx.From, false)
fromAddress, name, _, err := client.GetFromFields(clientCtx, clientCtx.Keyring, clientCtx.From)
if err != nil {
return tx.AuxSignerData{}, err
}

View File

@ -54,9 +54,10 @@ account key. It implies --signature-only.
cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT")
cmd.Flags().Bool(flagSigOnly, true, "Print only the generated signature, then exit")
cmd.Flags().String(flags.FlagChainID, "", "network chain ID")
cmd.MarkFlagRequired(flags.FlagFrom)
flags.AddTxFlagsToCmd(cmd)
cmd.MarkFlagRequired(flags.FlagFrom)
return cmd
}
@ -106,7 +107,7 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
}
if ms == "" {
from, _ := cmd.Flags().GetString(flags.FlagFrom)
_, fromName, _, err := client.GetFromFields(txFactory.Keybase(), from, clientCtx.GenerateOnly)
_, fromName, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
@ -115,7 +116,7 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
return err
}
} else {
multisigAddr, _, _, err := client.GetFromFields(txFactory.Keybase(), ms, clientCtx.GenerateOnly)
multisigAddr, _, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), ms)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
@ -192,9 +193,10 @@ be generated via the 'multisign' command.
cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT")
cmd.Flags().String(flags.FlagChainID, "", "The network chain ID")
cmd.Flags().Bool(flagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint")
cmd.MarkFlagRequired(flags.FlagFrom)
flags.AddTxFlagsToCmd(cmd)
cmd.MarkFlagRequired(flags.FlagFrom)
return cmd
}
@ -235,7 +237,7 @@ func makeSignCmd() func(cmd *cobra.Command, args []string) error {
return err
}
from, _ := cmd.Flags().GetString(flags.FlagFrom)
_, fromName, _, err := client.GetFromFields(txF.Keybase(), from, clientCtx.GenerateOnly)
_, fromName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), from)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
@ -245,7 +247,7 @@ func makeSignCmd() func(cmd *cobra.Command, args []string) error {
multisigAddr, err := sdk.AccAddressFromBech32(multisig)
if err != nil {
// Bech32 decode error, maybe it's a name, we try to fetch from keyring
multisigAddr, _, _, err = client.GetFromFields(txFactory.Keybase(), multisig, clientCtx.GenerateOnly)
multisigAddr, _, _, err = client.GetFromFields(clientCtx, txFactory.Keybase(), multisig)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}

View File

@ -29,8 +29,9 @@ func NewTxCmd() *cobra.Command {
func NewSendTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "send [from_key_or_address] [to_address] [amount]",
Short: `Send funds from one account to another. Note, the'--from' flag is
ignored as it is implied from [from_key_or_address].`,
Short: `Send funds from one account to another.
Note, the '--from' flag is ignored as it is implied from [from_key_or_address].
When using '--dry-run' a key name cannot be used, only a bech32 address.`,
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
cmd.Flags().Set(flags.FlagFrom, args[0])
@ -38,6 +39,7 @@ ignored as it is implied from [from_key_or_address].`,
if err != nil {
return err
}
toAddr, err := sdk.AccAddressFromBech32(args[1])
if err != nil {
return err

View File

@ -316,7 +316,7 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() {
alreadyExistedGrantee := s.addedGrantee
clientCtx := val.ClientCtx
fromAddr, fromName, _, err := client.GetFromFields(clientCtx.Keyring, granter.String(), clientCtx.GenerateOnly)
fromAddr, fromName, _, err := client.GetFromFields(clientCtx, clientCtx.Keyring, granter.String())
s.Require().Equal(fromAddr, granter)
s.Require().NoError(err)