parent
b47e4096fc
commit
8c7bb89549
|
@ -169,6 +169,10 @@ Buffers for state serialization instead of Amino.
|
||||||
* (server) [\#5709](https://github.com/cosmos/cosmos-sdk/pull/5709) There are two new flags for pruning, `--pruning-keep-every`
|
* (server) [\#5709](https://github.com/cosmos/cosmos-sdk/pull/5709) There are two new flags for pruning, `--pruning-keep-every`
|
||||||
and `--pruning-snapshot-every` as an alternative to `--pruning`. They allow to fine tune the strategy for pruning the state.
|
and `--pruning-snapshot-every` as an alternative to `--pruning`. They allow to fine tune the strategy for pruning the state.
|
||||||
* (crypto/keys) [\#5739](https://github.com/cosmos/cosmos-sdk/pull/5739) Print an error message if the password input failed.
|
* (crypto/keys) [\#5739](https://github.com/cosmos/cosmos-sdk/pull/5739) Print an error message if the password input failed.
|
||||||
|
* (client) [\#5810](https://github.com/cosmos/cosmos-sdk/pull/5810) Added a new `--offline` flag that allows commands to
|
||||||
|
be executed without an internet connection. Previously, `--generate-only` served this purpose in addition to only allowing
|
||||||
|
txs to be generated. Now, `--generate-only` solely allows txs to be generated without being broadcasted and disallows
|
||||||
|
Keybase use and `--offline` allows the use of Keybase but does not allow any functionality that requires an online connection.
|
||||||
|
|
||||||
## [v0.38.1] - 2020-02-11
|
## [v0.38.1] - 2020-02-11
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ type CLIContext struct {
|
||||||
UseLedger bool
|
UseLedger bool
|
||||||
Simulate bool
|
Simulate bool
|
||||||
GenerateOnly bool
|
GenerateOnly bool
|
||||||
|
Offline bool
|
||||||
Indent bool
|
Indent bool
|
||||||
SkipConfirm bool
|
SkipConfirm bool
|
||||||
|
|
||||||
|
@ -67,7 +68,8 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !genOnly {
|
offline := viper.GetBool(flags.FlagOffline)
|
||||||
|
if !offline {
|
||||||
nodeURI = viper.GetString(flags.FlagNode)
|
nodeURI = viper.GetString(flags.FlagNode)
|
||||||
if nodeURI != "" {
|
if nodeURI != "" {
|
||||||
rpc, err = rpcclient.NewHTTP(nodeURI, "/websocket")
|
rpc, err = rpcclient.NewHTTP(nodeURI, "/websocket")
|
||||||
|
@ -93,6 +95,7 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
|
||||||
BroadcastMode: viper.GetString(flags.FlagBroadcastMode),
|
BroadcastMode: viper.GetString(flags.FlagBroadcastMode),
|
||||||
Simulate: viper.GetBool(flags.FlagDryRun),
|
Simulate: viper.GetBool(flags.FlagDryRun),
|
||||||
GenerateOnly: genOnly,
|
GenerateOnly: genOnly,
|
||||||
|
Offline: offline,
|
||||||
FromAddress: fromAddress,
|
FromAddress: fromAddress,
|
||||||
FromName: fromName,
|
FromName: fromName,
|
||||||
Indent: viper.GetBool(flags.FlagIndentResponse),
|
Indent: viper.GetBool(flags.FlagIndentResponse),
|
||||||
|
@ -286,7 +289,7 @@ func GetFromFields(input io.Reader, from string, genOnly bool) (sdk.AccAddress,
|
||||||
if genOnly {
|
if genOnly {
|
||||||
addr, err := sdk.AccAddressFromBech32(from)
|
addr, err := sdk.AccAddressFromBech32(from)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.Wrap(err, "must provide a valid Bech32 address for generate-only")
|
return nil, "", errors.Wrap(err, "must provide a valid Bech32 address in generate-only mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
return addr, "", nil
|
return addr, "", nil
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCLIContext_WithOffline(t *testing.T) {
|
||||||
|
viper.Set(flags.FlagOffline, true)
|
||||||
|
viper.Set(flags.FlagNode, "tcp://localhost:26657")
|
||||||
|
|
||||||
|
ctx := NewCLIContext()
|
||||||
|
require.True(t, ctx.Offline)
|
||||||
|
require.Nil(t, ctx.Client)
|
||||||
|
|
||||||
|
viper.Reset()
|
||||||
|
|
||||||
|
viper.Set(flags.FlagOffline, false)
|
||||||
|
viper.Set(flags.FlagNode, "tcp://localhost:26657")
|
||||||
|
|
||||||
|
ctx = NewCLIContext()
|
||||||
|
require.False(t, ctx.Offline)
|
||||||
|
require.NotNil(t, ctx.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCLIContext_WithGenOnly(t *testing.T) {
|
||||||
|
viper.Set(flags.FlagGenerateOnly, true)
|
||||||
|
|
||||||
|
validFromAddr := "cosmos1q7380u26f7ntke3facjmynajs4umlr329vr4ja"
|
||||||
|
fromAddr, err := sdk.AccAddressFromBech32(validFromAddr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
from string
|
||||||
|
expectedFromAddr sdk.AccAddress
|
||||||
|
expectedFromName string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid from",
|
||||||
|
from: validFromAddr,
|
||||||
|
expectedFromAddr: fromAddr,
|
||||||
|
expectedFromName: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty from",
|
||||||
|
from: "",
|
||||||
|
expectedFromAddr: nil,
|
||||||
|
expectedFromName: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx := NewCLIContextWithFrom(tt.from)
|
||||||
|
|
||||||
|
require.Equal(t, tt.expectedFromAddr, ctx.FromAddress)
|
||||||
|
require.Equal(t, tt.expectedFromName, ctx.FromName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ import (
|
||||||
// error is returned.
|
// error is returned.
|
||||||
func (ctx CLIContext) GetNode() (rpcclient.Client, error) {
|
func (ctx CLIContext) GetNode() (rpcclient.Client, error) {
|
||||||
if ctx.Client == nil {
|
if ctx.Client == nil {
|
||||||
return nil, errors.New("no RPC client defined")
|
return nil, errors.New("no RPC client is defined in offline mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Client, nil
|
return ctx.Client, nil
|
||||||
|
|
|
@ -57,6 +57,7 @@ const (
|
||||||
FlagBroadcastMode = "broadcast-mode"
|
FlagBroadcastMode = "broadcast-mode"
|
||||||
FlagDryRun = "dry-run"
|
FlagDryRun = "dry-run"
|
||||||
FlagGenerateOnly = "generate-only"
|
FlagGenerateOnly = "generate-only"
|
||||||
|
FlagOffline = "offline"
|
||||||
FlagIndentResponse = "indent"
|
FlagIndentResponse = "indent"
|
||||||
FlagListenAddr = "laddr"
|
FlagListenAddr = "laddr"
|
||||||
FlagMaxOpenConnections = "max-open"
|
FlagMaxOpenConnections = "max-open"
|
||||||
|
@ -114,7 +115,8 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||||
c.Flags().StringP(FlagBroadcastMode, "b", BroadcastSync, "Transaction broadcasting mode (sync|async|block)")
|
c.Flags().StringP(FlagBroadcastMode, "b", BroadcastSync, "Transaction broadcasting mode (sync|async|block)")
|
||||||
c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)")
|
c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)")
|
||||||
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
|
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
|
||||||
c.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible and the node operates offline)")
|
c.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible)")
|
||||||
|
c.Flags().Bool(FlagOffline, false, "Offline mode (does not allow any online functionality")
|
||||||
c.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation")
|
c.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation")
|
||||||
c.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|test)")
|
c.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|test)")
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -26,6 +27,11 @@ $ <appcli> tx broadcast ./mytxn.json
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
|
|
||||||
|
if cliCtx.Offline {
|
||||||
|
return errors.New("cannot broadcast tx during offline mode")
|
||||||
|
}
|
||||||
|
|
||||||
stdTx, err := client.ReadStdTxFromFile(cliCtx.Codec, args[0])
|
stdTx, err := client.ReadStdTxFromFile(cliCtx.Codec, args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/tendermint/go-amino"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetBroadcastCommand_OfflineFlag(t *testing.T) {
|
||||||
|
codec := amino.NewCodec()
|
||||||
|
cmd := GetBroadcastCommand(codec)
|
||||||
|
|
||||||
|
viper.Set(flags.FlagOffline, true)
|
||||||
|
|
||||||
|
err := cmd.RunE(nil, []string{})
|
||||||
|
require.EqualError(t, err, "cannot broadcast tx during offline mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetBroadcastCommand_WithoutOfflineFlag(t *testing.T) {
|
||||||
|
codec := amino.NewCodec()
|
||||||
|
cmd := GetBroadcastCommand(codec)
|
||||||
|
|
||||||
|
viper.Set(flags.FlagOffline, false)
|
||||||
|
|
||||||
|
testDir, cleanFunc := tests.NewTestCaseDir(t)
|
||||||
|
t.Cleanup(cleanFunc)
|
||||||
|
|
||||||
|
// Create new file with tx
|
||||||
|
txContents := []byte("{\"type\":\"cosmos-sdk/StdTx\",\"value\":{\"msg\":[{\"type\":\"cosmos-sdk/MsgSend\",\"value\":{\"from_address\":\"cosmos1cxlt8kznps92fwu3j6npahx4mjfutydyene2qw\",\"to_address\":\"cosmos1wc8mpr8m3sy3ap3j7fsgqfzx36um05pystems4\",\"amount\":[{\"denom\":\"stake\",\"amount\":\"10000\"}]}}],\"fee\":{\"amount\":[],\"gas\":\"200000\"},\"signatures\":null,\"memo\":\"\"}}")
|
||||||
|
txFileName := filepath.Join(testDir, "tx.json")
|
||||||
|
err := ioutil.WriteFile(txFileName, txContents, 0644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = cmd.RunE(cmd, []string{txFileName})
|
||||||
|
|
||||||
|
// We test it tries to broadcast but we set unsupported tx to get the error.
|
||||||
|
require.EqualError(t, err, "unsupported return type ; supported types: sync, async, block")
|
||||||
|
}
|
|
@ -51,7 +51,6 @@ recommended to set such parameters manually.
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit")
|
cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit")
|
||||||
cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query a full node")
|
|
||||||
cmd.Flags().String(flagOutfile, "", "The document will be written to the given file instead of STDOUT")
|
cmd.Flags().String(flagOutfile, "", "The document will be written to the given file instead of STDOUT")
|
||||||
|
|
||||||
// Add the flags here and return the command
|
// Add the flags here and return the command
|
||||||
|
@ -85,7 +84,7 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string)
|
||||||
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
|
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
|
||||||
txBldr := types.NewTxBuilderFromCLI(inBuf)
|
txBldr := types.NewTxBuilderFromCLI(inBuf)
|
||||||
|
|
||||||
if !viper.GetBool(flagOffline) {
|
if !cliCtx.Offline {
|
||||||
accnum, seq, err := types.NewAccountRetriever(client.Codec, cliCtx).GetAccountNumberSequence(multisigInfo.GetAddress())
|
accnum, seq, err := types.NewAccountRetriever(client.Codec, cliCtx).GetAccountNumberSequence(multisigInfo.GetAddress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -22,7 +22,6 @@ const (
|
||||||
flagMultisig = "multisig"
|
flagMultisig = "multisig"
|
||||||
flagAppend = "append"
|
flagAppend = "append"
|
||||||
flagValidateSigs = "validate-signatures"
|
flagValidateSigs = "validate-signatures"
|
||||||
flagOffline = "offline"
|
|
||||||
flagSigOnly = "signature-only"
|
flagSigOnly = "signature-only"
|
||||||
flagOutfile = "output-document"
|
flagOutfile = "output-document"
|
||||||
)
|
)
|
||||||
|
@ -71,12 +70,7 @@ be generated via the 'multisign' command.
|
||||||
"Print the addresses that must sign the transaction, those who have already signed it, and make sure that signatures are in the correct order",
|
"Print the addresses that must sign the transaction, those who have already signed it, and make sure that signatures are in the correct order",
|
||||||
)
|
)
|
||||||
cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit")
|
cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit")
|
||||||
cmd.Flags().Bool(
|
|
||||||
flagOffline, false,
|
|
||||||
"Offline mode; Do not query a full node. --account and --sequence options would be required if offline is set",
|
|
||||||
)
|
|
||||||
cmd.Flags().String(flagOutfile, "", "The document will be written to the given file instead of STDOUT")
|
cmd.Flags().String(flagOutfile, "", "The document will be written to the given file instead of STDOUT")
|
||||||
|
|
||||||
cmd = flags.PostCommands(cmd)[0]
|
cmd = flags.PostCommands(cmd)[0]
|
||||||
cmd.MarkFlagRequired(flags.FlagFrom)
|
cmd.MarkFlagRequired(flags.FlagFrom)
|
||||||
|
|
||||||
|
@ -86,7 +80,7 @@ be generated via the 'multisign' command.
|
||||||
func preSignCmd(cmd *cobra.Command, _ []string) {
|
func preSignCmd(cmd *cobra.Command, _ []string) {
|
||||||
// Conditionally mark the account and sequence numbers required as no RPC
|
// Conditionally mark the account and sequence numbers required as no RPC
|
||||||
// query will be done.
|
// query will be done.
|
||||||
if viper.GetBool(flagOffline) {
|
if viper.GetBool(flags.FlagOffline) {
|
||||||
cmd.MarkFlagRequired(flags.FlagAccountNumber)
|
cmd.MarkFlagRequired(flags.FlagAccountNumber)
|
||||||
cmd.MarkFlagRequired(flags.FlagSequence)
|
cmd.MarkFlagRequired(flags.FlagSequence)
|
||||||
}
|
}
|
||||||
|
@ -100,12 +94,11 @@ func makeSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
inBuf := bufio.NewReader(cmd.InOrStdin())
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||||
offline := viper.GetBool(flagOffline)
|
|
||||||
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
|
cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc)
|
||||||
txBldr := types.NewTxBuilderFromCLI(inBuf)
|
txBldr := types.NewTxBuilderFromCLI(inBuf)
|
||||||
|
|
||||||
if viper.GetBool(flagValidateSigs) {
|
if viper.GetBool(flagValidateSigs) {
|
||||||
if !printAndValidateSigs(cliCtx, txBldr.ChainID(), stdTx, offline) {
|
if !printAndValidateSigs(cliCtx, txBldr.ChainID(), stdTx, cliCtx.Offline) {
|
||||||
return fmt.Errorf("signatures validation failed")
|
return fmt.Errorf("signatures validation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,14 +117,13 @@ func makeSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
newTx, err = client.SignStdTxWithSignerAddress(
|
newTx, err = client.SignStdTxWithSignerAddress(
|
||||||
txBldr, cliCtx, multisigAddr, cliCtx.GetFromName(), stdTx, offline,
|
txBldr, cliCtx, multisigAddr, cliCtx.GetFromName(), stdTx, cliCtx.Offline,
|
||||||
)
|
)
|
||||||
generateSignatureOnly = true
|
generateSignatureOnly = true
|
||||||
} else {
|
} else {
|
||||||
appendSig := viper.GetBool(flagAppend) && !generateSignatureOnly
|
appendSig := viper.GetBool(flagAppend) && !generateSignatureOnly
|
||||||
newTx, err = client.SignStdTx(txBldr, cliCtx, cliCtx.GetFromName(), stdTx, appendSig, offline)
|
newTx, err = client.SignStdTx(txBldr, cliCtx, cliCtx.GetFromName(), stdTx, appendSig, cliCtx.Offline)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -327,8 +327,8 @@ func PrepareTxBuilder(txBldr authtypes.TxBuilder, cliCtx context.CLIContext) (au
|
||||||
|
|
||||||
func buildUnsignedStdTxOffline(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx authtypes.StdTx, err error) {
|
func buildUnsignedStdTxOffline(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx authtypes.StdTx, err error) {
|
||||||
if txBldr.SimulateAndExecute() {
|
if txBldr.SimulateAndExecute() {
|
||||||
if cliCtx.GenerateOnly {
|
if cliCtx.Offline {
|
||||||
return stdTx, errors.New("cannot estimate gas with generate-only")
|
return stdTx, errors.New("cannot estimate gas in offline mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs)
|
txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs)
|
||||||
|
|
|
@ -150,8 +150,8 @@ $ %s tx distribution withdraw-all-rewards --from mykey
|
||||||
|
|
||||||
// The transaction cannot be generated offline since it requires a query
|
// The transaction cannot be generated offline since it requires a query
|
||||||
// to get all the validators.
|
// to get all the validators.
|
||||||
if cliCtx.GenerateOnly {
|
if cliCtx.Offline {
|
||||||
return fmt.Errorf("command disabled with the provided flag: %s", flags.FlagGenerateOnly)
|
return fmt.Errorf("cannot generate tx in offline mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, queryRoute, delAddr)
|
msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, queryRoute, delAddr)
|
||||||
|
|
Loading…
Reference in New Issue