From 13f27c6e5eb2ef10a1a6775530600e1541c079b5 Mon Sep 17 00:00:00 2001 From: Artur Troian Date: Sun, 8 Sep 2024 14:48:40 -0500 Subject: [PATCH] feat(go/cli): pull all cosmos-sdk commands into repo Signed-off-by: Artur Troian --- go/cli/audit_query.go | 30 +- go/cli/audit_tx.go | 33 +- go/cli/auth_encode_test.go | 103 ++ go/cli/auth_flags.go | 16 +- go/cli/auth_multisign.go | 42 +- go/cli/auth_query.go | 221 ++-- go/cli/auth_query_test.go | 34 + go/cli/auth_suite_test.go | 1629 +++++++++++++++++++++++++++++ go/cli/auth_tips.go | 80 ++ go/cli/auth_tx.go | 153 ++- go/cli/authz_query.go | 198 ++++ go/cli/authz_query_test.go | 239 +++++ go/cli/authz_suite_test.go | 150 +++ go/cli/authz_tx.go | 324 ++++++ go/cli/authz_tx_test.go | 827 +++++++++++++++ go/cli/bank_query.go | 319 ++++++ go/cli/bank_query_test.go | 384 +++++++ go/cli/bank_suite_test.go | 46 + go/cli/bank_tx.go | 61 +- go/cli/bank_tx_test.go | 189 ++++ go/cli/broadcast.go | 28 +- go/cli/cctx.go | 311 ++++++ go/cli/cert_query.go | 13 +- go/cli/cert_tx.go | 114 +- go/cli/client.go | 2 +- go/cli/crisis_tx.go | 69 ++ go/cli/deployment_query.go | 52 +- go/cli/deployment_tx.go | 118 ++- go/cli/distribution_query.go | 403 +++++++ go/cli/distribution_tx.go | 350 +++++++ go/cli/escrow_query.go | 17 +- go/cli/escrow_tx.go | 2 +- go/cli/evidence_query.go | 71 ++ go/cli/evidence_tx.go | 45 + go/cli/feegrant_query.go | 180 ++++ go/cli/feegrant_tx.go | 222 ++++ go/cli/flags/client.go | 9 + go/cli/flags/flags.go | 242 ++++- go/cli/genesis.go | 37 + go/cli/genesis_collect.go | 69 ++ go/cli/genesis_genaccount.go | 88 ++ go/cli/genesis_genaccount_test.go | 108 ++ go/cli/genesis_gentx.go | 263 +++++ go/cli/genesis_gentx_test.go | 95 ++ go/cli/genesis_init.go | 169 +++ go/cli/genesis_init_test.go | 363 +++++++ go/cli/genesis_migrate.go | 141 +++ go/cli/genesis_migrate_test.go | 60 ++ go/cli/genesis_suite_test.go | 44 + go/cli/genesis_validate.go | 72 ++ go/cli/genesis_validate_test.go | 102 ++ go/cli/gentxs.go | 100 -- go/cli/go.mod | 56 +- go/cli/go.sum | 1280 ++++++++++++++++++++++- go/cli/gov_prompt_test.go | 87 ++ go/cli/gov_query.go | 631 +++++++++++ go/cli/gov_query_test.go | 359 +++++++ go/cli/gov_suite_test.go | 77 ++ go/cli/gov_tx.go | 838 +++++++++++++++ go/cli/gov_tx_test.go | 417 ++++++++ go/cli/gov_util.go | 62 ++ go/cli/gov_util_test.go | 697 ++++++++++++ go/cli/init.go | 9 + go/cli/market_query.go | 76 +- go/cli/market_tx.go | 72 +- go/cli/mint_query.go | 113 ++ go/cli/params_query.go | 54 + go/cli/params_tx.go | 91 ++ go/cli/provider_query.go | 32 +- go/cli/provider_tx.go | 26 +- go/cli/query.go | 68 +- go/cli/slashing_query.go | 138 +++ go/cli/slashing_tx.go | 59 ++ go/cli/staking_query.go | 1036 ++++++++++++++++++ go/cli/staking_tx.go | 960 +++++++++++++++++ go/cli/suite_test.go | 28 + go/cli/test_helpers.go | 749 +++++++------ go/cli/testutil/auth.go | 90 ++ go/cli/testutil/authz.go | 15 + go/cli/testutil/certs.go | 50 + go/cli/testutil/cmd.go | 77 ++ go/cli/testutil/deployment.go | 93 ++ go/cli/testutil/gov.go | 49 + go/cli/testutil/market.go | 60 ++ go/cli/testutil/provider.go | 43 + go/cli/testutil/tm_mocks.go | 41 + go/cli/tx.go | 88 +- go/cli/upgrade_query.go | 148 +++ go/cli/upgrade_tx.go | 202 ++++ go/cli/validate.go | 58 + go/cli/vesting_tx.go | 29 +- 91 files changed, 16972 insertions(+), 1123 deletions(-) create mode 100644 go/cli/auth_encode_test.go create mode 100644 go/cli/auth_query_test.go create mode 100644 go/cli/auth_suite_test.go create mode 100644 go/cli/auth_tips.go create mode 100644 go/cli/authz_query.go create mode 100644 go/cli/authz_query_test.go create mode 100644 go/cli/authz_suite_test.go create mode 100644 go/cli/authz_tx.go create mode 100644 go/cli/authz_tx_test.go create mode 100644 go/cli/bank_query.go create mode 100644 go/cli/bank_query_test.go create mode 100644 go/cli/bank_suite_test.go create mode 100644 go/cli/bank_tx_test.go create mode 100644 go/cli/cctx.go create mode 100644 go/cli/crisis_tx.go create mode 100644 go/cli/distribution_query.go create mode 100644 go/cli/distribution_tx.go create mode 100644 go/cli/evidence_query.go create mode 100644 go/cli/evidence_tx.go create mode 100644 go/cli/feegrant_query.go create mode 100644 go/cli/feegrant_tx.go create mode 100644 go/cli/genesis.go create mode 100644 go/cli/genesis_collect.go create mode 100644 go/cli/genesis_genaccount.go create mode 100644 go/cli/genesis_genaccount_test.go create mode 100644 go/cli/genesis_gentx.go create mode 100644 go/cli/genesis_gentx_test.go create mode 100644 go/cli/genesis_init.go create mode 100644 go/cli/genesis_init_test.go create mode 100644 go/cli/genesis_migrate.go create mode 100644 go/cli/genesis_migrate_test.go create mode 100644 go/cli/genesis_suite_test.go create mode 100644 go/cli/genesis_validate.go create mode 100644 go/cli/genesis_validate_test.go create mode 100644 go/cli/gov_prompt_test.go create mode 100644 go/cli/gov_query.go create mode 100644 go/cli/gov_query_test.go create mode 100644 go/cli/gov_suite_test.go create mode 100644 go/cli/gov_tx.go create mode 100644 go/cli/gov_tx_test.go create mode 100644 go/cli/gov_util.go create mode 100644 go/cli/gov_util_test.go create mode 100644 go/cli/init.go create mode 100644 go/cli/mint_query.go create mode 100644 go/cli/params_query.go create mode 100644 go/cli/params_tx.go create mode 100644 go/cli/slashing_query.go create mode 100644 go/cli/slashing_tx.go create mode 100644 go/cli/staking_query.go create mode 100644 go/cli/staking_tx.go create mode 100644 go/cli/suite_test.go create mode 100644 go/cli/testutil/auth.go create mode 100644 go/cli/testutil/authz.go create mode 100644 go/cli/testutil/certs.go create mode 100644 go/cli/testutil/cmd.go create mode 100644 go/cli/testutil/deployment.go create mode 100644 go/cli/testutil/gov.go create mode 100644 go/cli/testutil/market.go create mode 100644 go/cli/testutil/provider.go create mode 100644 go/cli/testutil/tm_mocks.go create mode 100644 go/cli/upgrade_query.go create mode 100644 go/cli/upgrade_tx.go create mode 100644 go/cli/validate.go diff --git a/go/cli/audit_query.go b/go/cli/audit_query.go index 90b39ffd..ffe3ea2a 100644 --- a/go/cli/audit_query.go +++ b/go/cli/audit_query.go @@ -6,13 +6,13 @@ import ( "github.com/spf13/cobra" sdkclient "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" + cflags "pkg.akt.dev/go/cli/flags" types "pkg.akt.dev/go/node/audit/v1" ) -func GetAuditQueryCmd() *cobra.Command { +func GetQueryAuditCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Audit query commands", @@ -21,17 +21,18 @@ func GetAuditQueryCmd() *cobra.Command { } cmd.AddCommand( - cmdAuditGetProviders(), - cmdAuditGetProvider(), + GetAuditProvidersCmd(), + GetAuditProviderCmd(), ) return cmd } -func cmdAuditGetProviders() *cobra.Command { +func GetAuditProvidersCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Short: "Query for all providers", + Use: "list", + Short: "Query for all providers", + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -54,17 +55,18 @@ func cmdAuditGetProviders() *cobra.Command { }, } - flags.AddQueryFlagsToCmd(cmd) - flags.AddPaginationFlagsToCmd(cmd, "providers") + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "providers") return cmd } -func cmdAuditGetProvider() *cobra.Command { +func GetAuditProviderCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "get [owner address] [auditor address]", - Short: "Query provider", - Args: cobra.RangeArgs(1, 2), + Use: "get [owner address] [auditor address]", + Short: "Query provider", + Args: cobra.RangeArgs(1, 2), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -103,7 +105,7 @@ func cmdAuditGetProvider() *cobra.Command { }, } - flags.AddQueryFlagsToCmd(cmd) + cflags.AddQueryFlagsToCmd(cmd) return cmd } diff --git a/go/cli/audit_tx.go b/go/cli/audit_tx.go index 72b97d27..551c6293 100644 --- a/go/cli/audit_tx.go +++ b/go/cli/audit_tx.go @@ -7,7 +7,6 @@ import ( "github.com/spf13/cobra" sdkclient "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" cflags "pkg.akt.dev/go/cli/flags" @@ -16,8 +15,8 @@ import ( attrtypes "pkg.akt.dev/go/node/types/attributes/v1" ) -// GetAuditTxCmd returns the transaction commands for audit module -func GetAuditTxCmd() *cobra.Command { +// GetTxAuditCmd returns the transaction commands for audit module +func GetTxAuditCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Audit transaction subcommands", @@ -26,31 +25,32 @@ func GetAuditTxCmd() *cobra.Command { } cmd.AddCommand( - cmdAttributes(), + GetTxAuditAttributesCmd(), ) return cmd } -func cmdAttributes() *cobra.Command { +func GetTxAuditAttributesCmd() *cobra.Command { cmd := &cobra.Command{ Use: "attr", Short: "Manage provider attributes", } cmd.AddCommand( - cmdCreateProviderAttributes(), - cmdDeleteProviderAttributes(), + CmdCreateProviderAttributes(), + CmdDeleteProviderAttributes(), ) return cmd } -func cmdCreateProviderAttributes() *cobra.Command { +func CmdCreateProviderAttributes() *cobra.Command { cmd := &cobra.Command{ - Use: "create [provider]", - Short: "Create/update provider attributes", - Args: cobra.MinimumNArgs(1), + Use: "create [provider]", + Short: "Create/update provider attributes", + Args: cobra.MinimumNArgs(1), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { if ((len(args) - 1) % 2) != 0 { return fmt.Errorf("attributes must be provided as pairs") @@ -98,11 +98,12 @@ func cmdCreateProviderAttributes() *cobra.Command { return cmd } -func cmdDeleteProviderAttributes() *cobra.Command { +func CmdDeleteProviderAttributes() *cobra.Command { cmd := &cobra.Command{ - Use: "delete [provider]", - Short: "Delete provider attributes", - Args: cobra.MinimumNArgs(1), + Use: "delete [provider]", + Short: "Delete provider attributes", + Args: cobra.MinimumNArgs(1), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -145,7 +146,7 @@ func cmdDeleteProviderAttributes() *cobra.Command { func setCmdProviderFlags(cmd *cobra.Command) { cflags.AddTxFlagsToCmd(cmd) - if err := cmd.MarkFlagRequired(flags.FlagFrom); err != nil { + if err := cmd.MarkFlagRequired(cflags.FlagFrom); err != nil { panic(err.Error()) } } diff --git a/go/cli/auth_encode_test.go b/go/cli/auth_encode_test.go new file mode 100644 index 00000000..47cfc39d --- /dev/null +++ b/go/cli/auth_encode_test.go @@ -0,0 +1,103 @@ +package cli_test + +// import ( +// "context" +// "encoding/base64" +// "testing" +// +// "github.com/stretchr/testify/require" +// +// "cosmossdk.io/depinject" +// "github.com/cosmos/cosmos-sdk/client" +// "github.com/cosmos/cosmos-sdk/codec" +// "github.com/cosmos/cosmos-sdk/testutil" +// sdk "github.com/cosmos/cosmos-sdk/types" +// authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil" +// +// "pkg.akt.dev/go/cli" +// ) +// +// func TestGetCommandEncode(t *testing.T) { +// var ( +// txCfg client.TxConfig +// legacyAmino *codec.LegacyAmino +// codec codec.Codec +// ) +// +// err := depinject.Inject( +// authtestutil.AppConfig, +// &txCfg, +// &legacyAmino, +// &codec, +// ) +// require.NoError(t, err) +// +// cmd := cli.GetEncodeCommand() +// _ = testutil.ApplyMockIODiscardOutErr(cmd) +// +// // Build a test transaction +// builder := txCfg.NewTxBuilder() +// builder.SetGasLimit(50000) +// builder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 150)}) +// builder.SetMemo("foomemo") +// jsonEncoded, err := txCfg.TxJSONEncoder()(builder.GetTx()) +// require.NoError(t, err) +// +// txFile := testutil.WriteToNewTempFile(t, string(jsonEncoded)) +// txFileName := txFile.Name() +// +// ctx := context.Background() +// clientCtx := client.Context{}. +// WithTxConfig(txCfg). +// WithCodec(codec) +// ctx = context.WithValue(ctx, cli.ClientContextKey, &clientCtx) +// +// cmd.SetArgs([]string{txFileName}) +// err = cmd.ExecuteContext(ctx) +// require.NoError(t, err) +// } +// +// func TestGetCommandDecode(t *testing.T) { +// var ( +// txCfg client.TxConfig +// legacyAmino *codec.LegacyAmino +// codec codec.Codec +// ) +// +// err := depinject.Inject( +// authtestutil.AppConfig, +// &txCfg, +// &legacyAmino, +// &codec, +// ) +// require.NoError(t, err) +// +// clientCtx := client.Context{}. +// WithTxConfig(txCfg). +// WithCodec(codec) +// +// cmd := cli.GetDecodeCommand() +// _ = testutil.ApplyMockIODiscardOutErr(cmd) +// +// clientCtx = clientCtx.WithTxConfig(txCfg) +// +// // Build a test transaction +// builder := txCfg.NewTxBuilder() +// builder.SetGasLimit(50000) +// builder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 150)}) +// builder.SetMemo("foomemo") +// +// // Encode transaction +// txBytes, err := clientCtx.TxConfig.TxEncoder()(builder.GetTx()) +// require.NoError(t, err) +// +// // Convert the transaction into base64 encoded string +// base64Encoded := base64.StdEncoding.EncodeToString(txBytes) +// +// ctx := context.Background() +// ctx = context.WithValue(ctx, cli.ClientContextKey, &clientCtx) +// +// // Execute the command +// cmd.SetArgs([]string{base64Encoded}) +// require.NoError(t, cmd.ExecuteContext(ctx)) +// } diff --git a/go/cli/auth_flags.go b/go/cli/auth_flags.go index 7696e7b6..65e36109 100644 --- a/go/cli/auth_flags.go +++ b/go/cli/auth_flags.go @@ -93,19 +93,19 @@ func SignTxWithSignerAddress(txFactory tx.Factory, clientCtx client.Context, add // ReadTxFromFile and decode a StdTx from the given filename. Can pass "-" to read from stdin. func ReadTxFromFile(ctx client.Context, filename string) (tx sdk.Tx, err error) { - var bytes []byte + var data []byte if filename == "-" { - bytes, err = io.ReadAll(os.Stdin) + data, err = io.ReadAll(os.Stdin) } else { - bytes, err = os.ReadFile(filename) + data, err = os.ReadFile(filename) } if err != nil { return } - return ctx.TxConfig.TxJSONDecoder()(bytes) + return ctx.TxConfig.TxJSONDecoder()(data) } // ReadTxsFromInput reads multiples txs from the given filename(s). Can pass "-" to read from stdin. @@ -119,12 +119,12 @@ func ReadTxsFromInput(txCfg client.TxConfig, filenames ...string) (scanner *Batc if filenames[0] != "-" { buf := new(bytes.Buffer) for _, f := range filenames { - bytes, err := os.ReadFile(filepath.Clean(f)) + data, err := os.ReadFile(filepath.Clean(f)) if err != nil { return nil, fmt.Errorf("couldn't read %s: %w", f, err) } - if _, err := buf.WriteString(string(bytes)); err != nil { + if _, err := buf.WriteString(string(data)); err != nil { return nil, fmt.Errorf("couldn't write to merged file: %w", err) } } @@ -161,8 +161,8 @@ func (bs *BatchScanner) Scan() bool { return false } - tx, err := bs.cfg.TxJSONDecoder()(bs.Bytes()) - bs.theTx = tx + txb, err := bs.cfg.TxJSONDecoder()(bs.Bytes()) + bs.theTx = txb if err != nil && bs.unmarshalErr == nil { bs.unmarshalErr = err return false diff --git a/go/cli/auth_multisign.go b/go/cli/auth_multisign.go index dea802c3..67498178 100644 --- a/go/cli/auth_multisign.go +++ b/go/cli/auth_multisign.go @@ -5,24 +5,24 @@ import ( "os" "strings" - errorsmod "cosmossdk.io/errors" - "github.com/spf13/cobra" "github.com/spf13/viper" + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/crypto/keyring" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" "github.com/cosmos/cosmos-sdk/crypto/types/multisig" sdk "github.com/cosmos/cosmos-sdk/types" - signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/version" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" "github.com/cosmos/cosmos-sdk/x/auth/signing" + + cflags "pkg.akt.dev/go/cli/flags" ) // BroadcastReq defines a tx broadcasting request. @@ -31,8 +31,8 @@ type BroadcastReq struct { Mode string `json:"mode" yaml:"mode"` } -// GetMultiSignCommand returns the multi-sign command -func GetMultiSignCommand() *cobra.Command { +// GetAuthMultiSignCmd returns the multi-sign command +func GetAuthMultiSignCmd() *cobra.Command { cmd := &cobra.Command{ Use: "multi-sign [file] [name] [[signature]...]", Aliases: []string{"multisign"}, @@ -63,11 +63,11 @@ The SIGN_MODE_DIRECT sign mode is not supported.' Args: cobra.MinimumNArgs(3), } - cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit") - cmd.Flags().String(flags.FlagOutputDocument, "", "The document is written to the given file instead of STDOUT") - cmd.Flags().Bool(flagAmino, false, "Generate Amino-encoded JSON suitable for submitting to the txs REST endpoint") - flags.AddTxFlagsToCmd(cmd) - _ = cmd.Flags().MarkHidden(flags.FlagOutput) + cmd.Flags().Bool(cflags.FlagSigOnly, false, "Print only the generated signature, then exit") + cmd.Flags().String(cflags.FlagOutputDocument, "", "The document is written to the given file instead of STDOUT") + cmd.Flags().Bool(cflags.FlagAmino, false, "Generate Amino-encoded JSON suitable for submitting to the txs REST endpoint") + cflags.AddTxFlagsToCmd(cmd) + _ = cmd.Flags().MarkHidden(cflags.FlagOutput) return cmd } @@ -165,9 +165,9 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) (err error) { return err } - sigOnly, _ := cmd.Flags().GetBool(flagSigOnly) + sigOnly, _ := cmd.Flags().GetBool(cflags.FlagSigOnly) - aminoJSON, _ := cmd.Flags().GetBool(flagAmino) + aminoJSON, _ := cmd.Flags().GetBool(cflags.FlagAmino) var json []byte @@ -227,14 +227,14 @@ The SIGN_MODE_DIRECT sign mode is not supported.' Args: cobra.MinimumNArgs(3), } - cmd.Flags().Bool(flagNoAutoIncrement, false, "disable sequence auto increment") + cmd.Flags().Bool(cflags.FlagNoAutoIncrement, false, "disable sequence auto increment") cmd.Flags().String( - flagMultisig, "", + cflags.FlagMultisig, "", "Address of the multisig account that the transaction signs on behalf of", ) - cmd.Flags().String(flags.FlagOutputDocument, "", "The document is written to the given file instead of STDOUT") - flags.AddTxFlagsToCmd(cmd) - _ = cmd.Flags().MarkHidden(flags.FlagOutput) // signing makes sense to output only json + cmd.Flags().String(cflags.FlagOutputDocument, "", "The document is written to the given file instead of STDOUT") + cflags.AddTxFlagsToCmd(cmd) + _ = cmd.Flags().MarkHidden(cflags.FlagOutput) // signing makes sense to output only json return cmd } @@ -342,8 +342,8 @@ func makeBatchMultisignCmd() func(cmd *cobra.Command, args []string) error { return err } - sigOnly, _ := cmd.Flags().GetBool(flagSigOnly) - aminoJSON, _ := cmd.Flags().GetBool(flagAmino) + sigOnly, _ := cmd.Flags().GetBool(cflags.FlagSigOnly) + aminoJSON, _ := cmd.Flags().GetBool(cflags.FlagAmino) var json []byte @@ -372,7 +372,7 @@ func makeBatchMultisignCmd() func(cmd *cobra.Command, args []string) error { return err } - if viper.GetBool(flagNoAutoIncrement) { + if viper.GetBool(cflags.FlagNoAutoIncrement) { continue } sequence := txFactory.Sequence() + 1 diff --git a/go/cli/auth_query.go b/go/cli/auth_query.go index 57312ce8..ede7598c 100644 --- a/go/cli/auth_query.go +++ b/go/cli/auth_query.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "strconv" "strings" @@ -10,21 +9,19 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - "cosmossdk.io/errors" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/query" "github.com/cosmos/cosmos-sdk/version" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/cosmos/cosmos-sdk/x/auth/types" + + cflags "pkg.akt.dev/go/cli/flags" + nutils "pkg.akt.dev/go/node/utils" ) const ( - flagEvents = "events" - flagType = "type" - typeHash = "hash" typeAccSeq = "acc_seq" typeSig = "signature" @@ -32,8 +29,8 @@ const ( eventFormat = "{eventType}.{eventAttribute}={value}" ) -// GetAuthQueryCmd returns the transaction commands for this module -func GetAuthQueryCmd() *cobra.Command { +// GetQueryAuthCmd returns the transaction commands for this module +func GetQueryAuthCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Querying commands for the auth module", @@ -43,19 +40,19 @@ func GetAuthQueryCmd() *cobra.Command { } cmd.AddCommand( - GetAccountQueryCmd(), - GetAccountAddressByIDCmd(), - GetAccountsCmd(), - QueryParamsCmd(), - QueryModuleAccountsCmd(), - QueryModuleAccountByNameCmd(), + GetQueryAuthAccountCmd(), + GetQueryAuthAccountAddressByIDCmd(), + GetQueryAuthAccountsCmd(), + GetQueryAuthParamsCmd(), + GetQueryAuthModuleAccountsCmd(), + GetQueryAuthModuleAccountByNameCmd(), ) return cmd } -// QueryParamsCmd returns the command handler for evidence parameter querying. -func QueryParamsCmd() *cobra.Command { +// GetQueryAuthParamsCmd returns the command handler for evidence parameter querying. +func GetQueryAuthParamsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "params", Short: "Query the current auth parameters", @@ -64,6 +61,7 @@ func QueryParamsCmd() *cobra.Command { $ query auth params `), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -73,22 +71,23 @@ $ query auth params return err } - return cl.PrintMessage(res.Params) + return cl.PrintMessage(&res.Params) }, } - flags.AddQueryFlagsToCmd(cmd) + cflags.AddQueryFlagsToCmd(cmd) return cmd } -// GetAccountQueryCmd returns a query account that will display the state of the +// GetQueryAuthAccountCmd returns a query account that will display the state of the // account at a given address. -func GetAccountQueryCmd() *cobra.Command { +func GetQueryAuthAccountCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "account [address]", - Short: "Query for account by address", - Args: cobra.ExactArgs(1), + Use: "account [address]", + Short: "Query for account by address", + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -98,7 +97,7 @@ func GetAccountQueryCmd() *cobra.Command { return err } - res, err := cl.Query().Auth().Account(cmd.Context(), &types.QueryAccountRequest{Address: key.String()}) + res, err := cl.Query().Auth().Account(ctx, &types.QueryAccountRequest{Address: key.String()}) if err != nil { info, err2 := cl.Node().SyncInfo(ctx) if err2 != nil { @@ -107,28 +106,29 @@ func GetAccountQueryCmd() *cobra.Command { catchingUp := info.CatchingUp if !catchingUp { - return errors.Wrapf(err, "your node may be syncing, please check node status using `/status`") + return errorsmod.Wrapf(err, "your node may be syncing, please check node status using `/status`") } return err } - return cl.PrintMessage(res.Account) + return cl.PrintMessage(&res.Account) }, } - flags.AddQueryFlagsToCmd(cmd) + cflags.AddQueryFlagsToCmd(cmd) return cmd } -// GetAccountAddressByIDCmd returns a query account that will display the account address of a given account id. -func GetAccountAddressByIDCmd() *cobra.Command { +// GetQueryAuthAccountAddressByIDCmd returns a query account that will display the account address of a given account id. +func GetQueryAuthAccountAddressByIDCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "address-by-acc-num [acc-num]", - Aliases: []string{"address-by-id"}, - Short: "Query for an address by account number", - Args: cobra.ExactArgs(1), - Example: fmt.Sprintf("%s q auth address-by-acc-num 1", version.AppName), + Use: "address-by-acc-num [acc-num]", + Aliases: []string{"address-by-id"}, + Short: "Query for an address by account number", + Args: cobra.ExactArgs(1), + Example: fmt.Sprintf("%s q auth address-by-acc-num 1", version.AppName), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -138,7 +138,7 @@ func GetAccountAddressByIDCmd() *cobra.Command { return err } - res, err := cl.Query().Auth().AccountAddressByID(cmd.Context(), &types.QueryAccountAddressByIDRequest{ + res, err := cl.Query().Auth().AccountAddressByID(ctx, &types.QueryAccountAddressByIDRequest{ AccountId: accNum, }) if err != nil { @@ -149,106 +149,98 @@ func GetAccountAddressByIDCmd() *cobra.Command { }, } - flags.AddQueryFlagsToCmd(cmd) + cflags.AddQueryFlagsToCmd(cmd) return cmd } -// GetAccountsCmd returns a query command that will display a list of accounts -func GetAccountsCmd() *cobra.Command { +// GetQueryAuthAccountsCmd returns a query command that will display a list of accounts +func GetQueryAuthAccountsCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "accounts", - Short: "Query all the accounts", + Use: "accounts", + Short: "Query all the accounts", + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - queryClient := types.NewQueryClient(clientCtx) - res, err := queryClient.Accounts(cmd.Context(), &types.QueryAccountsRequest{Pagination: pageReq}) + res, err := cl.Query().Auth().Accounts(ctx, &types.QueryAccountsRequest{Pagination: pageReq}) if err != nil { return err } - return clientCtx.PrintProto(res) + return cl.PrintMessage(res) }, } - flags.AddQueryFlagsToCmd(cmd) - flags.AddPaginationFlagsToCmd(cmd, "all-accounts") + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "all-accounts") return cmd } -// QueryModuleAccountsCmd returns a list of all the existing module accounts with their account information and permissions -func QueryModuleAccountsCmd() *cobra.Command { +// GetQueryAuthModuleAccountsCmd returns a list of all the existing module accounts with their account information and permissions +func GetQueryAuthModuleAccountsCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "module-accounts", - Short: "Query all module accounts", + Use: "module-accounts", + Short: "Query all module accounts", + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } - - queryClient := types.NewQueryClient(clientCtx) + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) - res, err := queryClient.ModuleAccounts(context.Background(), &types.QueryModuleAccountsRequest{}) + res, err := cl.Query().Auth().ModuleAccounts(ctx, &types.QueryModuleAccountsRequest{}) if err != nil { return err } - return clientCtx.PrintProto(res) + return cl.PrintMessage(res) }, } - flags.AddQueryFlagsToCmd(cmd) + cflags.AddQueryFlagsToCmd(cmd) return cmd } -// QueryModuleAccountByNameCmd returns a command to -func QueryModuleAccountByNameCmd() *cobra.Command { +// GetQueryAuthModuleAccountByNameCmd returns a command to +func GetQueryAuthModuleAccountByNameCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "module-account [module-name]", - Short: "Query module account info by module name", - Args: cobra.ExactArgs(1), - Example: fmt.Sprintf("%s q auth module-account auth", version.AppName), + Use: "module-account [module-name]", + Short: "Query module account info by module name", + Args: cobra.ExactArgs(1), + Example: fmt.Sprintf("%s q auth module-account auth", version.AppName), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) moduleName := args[0] if len(moduleName) == 0 { return fmt.Errorf("module name should not be empty") } - queryClient := types.NewQueryClient(clientCtx) - - res, err := queryClient.ModuleAccountByName(context.Background(), &types.QueryModuleAccountByNameRequest{Name: moduleName}) + res, err := cl.Query().Auth().ModuleAccountByName(ctx, &types.QueryModuleAccountByNameRequest{Name: moduleName}) if err != nil { return err } - return clientCtx.PrintProto(res) + return cl.PrintMessage(res) }, } - flags.AddQueryFlagsToCmd(cmd) + cflags.AddQueryFlagsToCmd(cmd) return cmd } -// QueryTxsByEventsCmd returns a command to search through transactions by events. -func QueryTxsByEventsCmd() *cobra.Command { +// GetQueryAuthTxsByEventsCmd returns a command to search through transactions by events. +func GetQueryAuthTxsByEventsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "txs", Short: "Query for paginated transactions that match a set of events", @@ -261,14 +253,15 @@ documents its respective events under 'xx_events.md'. Example: $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator_reward' --page 1 --limit 30 -`, eventFormat, version.AppName, flagEvents), +`, eventFormat, version.AppName, cflags.FlagEvents), ), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } - eventsRaw, _ := cmd.Flags().GetString(flagEvents) + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + cctx := cl.ClientContext() + + eventsRaw, _ := cmd.Flags().GetString(cflags.FlagEvents) eventsStr := strings.Trim(eventsRaw, "'") var events []string @@ -297,29 +290,29 @@ $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator tmEvents = append(tmEvents, event) } - page, _ := cmd.Flags().GetInt(flags.FlagPage) - limit, _ := cmd.Flags().GetInt(flags.FlagLimit) + page, _ := cmd.Flags().GetInt(cflags.FlagPage) + limit, _ := cmd.Flags().GetInt(cflags.FlagLimit) - txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, page, limit, "") + txs, err := nutils.QueryTxsByEvents(ctx, cctx, tmEvents, page, limit, "") if err != nil { return err } - return clientCtx.PrintProto(txs) + return cctx.PrintProto(txs) }, } - flags.AddQueryFlagsToCmd(cmd) - cmd.Flags().Int(flags.FlagPage, query.DefaultPage, "Query a specific page of paginated results") - cmd.Flags().Int(flags.FlagLimit, query.DefaultLimit, "Query number of transactions results per page returned") - cmd.Flags().String(flagEvents, "", fmt.Sprintf("list of transaction events in the form of %s", eventFormat)) - _ = cmd.MarkFlagRequired(flagEvents) + cflags.AddQueryFlagsToCmd(cmd) + cmd.Flags().Int(cflags.FlagPage, query.DefaultPage, "Query a specific page of paginated results") + cmd.Flags().Int(cflags.FlagLimit, query.DefaultLimit, "Query number of transactions results per page returned") + cmd.Flags().String(cflags.FlagEvents, "", fmt.Sprintf("list of transaction events in the form of %s", eventFormat)) + _ = cmd.MarkFlagRequired(cflags.FlagEvents) return cmd } -// GetTxQueryCmd implements the default command for a tx query. -func GetTxQueryCmd() *cobra.Command { +// GetQueryAuthTxCmd implements the default command for a tx query. +func GetQueryAuthTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "tx --type=[hash|acc_seq|signature] [hash|acc_seq|signature]", Short: "Query for a transaction by hash, \"/\" combination or comma-separated signatures in a committed block", @@ -330,16 +323,16 @@ $ %s query tx --%s=%s / $ %s query tx --%s=%s , `, version.AppName, - version.AppName, flagType, typeAccSeq, - version.AppName, flagType, typeSig)), - Args: cobra.ExactArgs(1), + version.AppName, cflags.FlagType, typeAccSeq, + version.AppName, cflags.FlagType, typeSig)), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + cctx := cl.ClientContext() - typ, _ := cmd.Flags().GetString(flagType) + typ, _ := cmd.Flags().GetString(cflags.FlagType) switch typ { case typeHash: @@ -349,7 +342,7 @@ $ %s query tx --%s=%s , } // If hash is given, then query the tx by hash. - output, err := authtx.QueryTx(clientCtx, args[0]) + output, err := nutils.QueryTx(ctx, cctx, args[0]) if err != nil { return err } @@ -358,7 +351,7 @@ $ %s query tx --%s=%s , return fmt.Errorf("no transaction found with hash %s", args[0]) } - return clientCtx.PrintProto(output) + return cl.PrintMessage(output) } case typeSig: { @@ -371,7 +364,7 @@ $ %s query tx --%s=%s , tmEvents[i] = fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sig) } - txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") + txs, err := nutils.QueryTxsByEvents(ctx, cctx, tmEvents, query.DefaultPage, query.DefaultLimit, "") if err != nil { return err } @@ -383,7 +376,7 @@ $ %s query tx --%s=%s , return sdkerrors.ErrLogic.Wrapf("found %d txs matching given signatures", len(txs.Txs)) } - return clientCtx.PrintProto(txs.Txs[0]) + return cl.PrintMessage(txs.Txs[0]) } case typeAccSeq: { @@ -394,7 +387,7 @@ $ %s query tx --%s=%s , tmEvents := []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeyAccountSequence, args[0]), } - txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, query.DefaultPage, query.DefaultLimit, "") + txs, err := nutils.QueryTxsByEvents(ctx, cctx, tmEvents, query.DefaultPage, query.DefaultLimit, "") if err != nil { return err } @@ -406,16 +399,16 @@ $ %s query tx --%s=%s , return fmt.Errorf("found %d txs matching given address and sequence combination", len(txs.Txs)) } - return clientCtx.PrintProto(txs.Txs[0]) + return cl.PrintMessage(txs.Txs[0]) } default: - return fmt.Errorf("unknown --%s value %s", flagType, typ) + return fmt.Errorf("unknown --%s value %s", cflags.FlagType, typ) } }, } - flags.AddQueryFlagsToCmd(cmd) - cmd.Flags().String(flagType, typeHash, fmt.Sprintf("The type to be used when querying tx, can be one of \"%s\", \"%s\", \"%s\"", typeHash, typeAccSeq, typeSig)) + cflags.AddQueryFlagsToCmd(cmd) + cmd.Flags().String(cflags.FlagType, typeHash, fmt.Sprintf("The type to be used when querying tx, can be one of \"%s\", \"%s\", \"%s\"", typeHash, typeAccSeq, typeSig)) return cmd } diff --git a/go/cli/auth_query_test.go b/go/cli/auth_query_test.go new file mode 100644 index 00000000..b166abd9 --- /dev/null +++ b/go/cli/auth_query_test.go @@ -0,0 +1,34 @@ +package cli_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "pkg.akt.dev/go/cli" +) + +func TestParseSigs(t *testing.T) { + cases := []struct { + name string + args []string + expErr bool + expNumSigs int + }{ + {"no args", []string{}, true, 0}, + {"empty args", []string{""}, true, 0}, + {"too many args", []string{"foo", "bar"}, true, 0}, + {"1 sig", []string{"foo"}, false, 1}, + {"3 sigs", []string{"foo,bar,baz"}, false, 3}, + } + + for _, tc := range cases { + sigs, err := cli.ParseSigArgs(tc.args) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expNumSigs, len(sigs)) + } + } +} diff --git a/go/cli/auth_suite_test.go b/go/cli/auth_suite_test.go new file mode 100644 index 00000000..0e17ce4f --- /dev/null +++ b/go/cli/auth_suite_test.go @@ -0,0 +1,1629 @@ +package cli_test + +import ( + "bytes" + "context" + "fmt" + "io" + "strings" + + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + "github.com/cosmos/cosmos-sdk/types/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + + // "cosmossdk.io/math" + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + // "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil" + // "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + // "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/x/auth" + // authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + // authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/bank" + // banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/gov" + // govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" + // govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + + "pkg.akt.dev/go/cli" + cflags "pkg.akt.dev/go/cli/flags" + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +type AuthCLITestSuite struct { + CLITestSuite + val sdk.AccAddress + val1 sdk.AccAddress +} + +func (s *AuthCLITestSuite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(auth.AppModuleBasic{}, bank.AppModuleBasic{}, gov.AppModuleBasic{}) + s.kr = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithLegacyAmino(s.encCfg.Amino). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID("test-chain") + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + } + s.cctx = ctxGen().WithOutput(&outBuf).WithSignModeStr("direct") + + kb := s.cctx.Keyring + valAcc, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + s.Require().NoError(err) + s.val, err = valAcc.GetAddress() + s.Require().NoError(err) + + account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + s.Require().NoError(err) + s.val1, err = account1.GetAddress() + s.Require().NoError(err) + + account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + s.Require().NoError(err) + pub1, err := account1.GetPubKey() + s.Require().NoError(err) + pub2, err := account2.GetPubKey() + s.Require().NoError(err) + + // Create a dummy account for testing purpose + _, _, err = kb.NewMnemonic("dummyAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + s.Require().NoError(err) + + multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{pub1, pub2}) + _, err = kb.SaveMultisig("multi", multi) + s.Require().NoError(err) +} + +func (s *AuthCLITestSuite) TestCLIValidateSignatures() { + sendTokens := sdk.NewCoins( + sdk.NewCoin("testtoken", sdk.NewInt(10)), + sdk.NewCoin("uakt", sdk.NewInt(10))) + + res, err := s.createBankMsg( + s.cctx, + s.val, + sendTokens, + cli.TestFlags().WithGenerateOnly()...) + s.Require().NoError(err) + + // write unsigned tx to file + unsignedTx := testutil.WriteToNewTempFile(s.T(), res.String()) + defer func() { + _ = unsignedTx.Close() + }() + + res, err = clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + unsignedTx.Name(), + ). + WithFrom(s.val.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + signedTx, err := s.cctx.TxConfig.TxJSONDecoder()(res.Bytes()) + s.Require().NoError(err) + + signedTxFile := testutil.WriteToNewTempFile(s.T(), res.String()) + defer func() { + _ = signedTxFile.Close() + }() + + txBuilder, err := s.cctx.TxConfig.WrapTxBuilder(signedTx) + s.Require().NoError(err) + _, err = clitestutil.TxValidateSignaturesExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + signedTxFile.Name(), + )...) + s.Require().NoError(err) + + txBuilder.SetMemo("MODIFIED TX") + bz, err := s.cctx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + + modifiedTxFile := testutil.WriteToNewTempFile(s.T(), string(bz)) + defer func() { + _ = modifiedTxFile.Close() + }() + + _, err = clitestutil.TxValidateSignaturesExec(context.Background(), s.cctx, modifiedTxFile.Name()) + s.Require().EqualError(err, "signatures validation failed") +} + +func (s *AuthCLITestSuite) TestCLISignBatch() { + sendTokens := sdk.NewCoins( + sdk.NewCoin("testtoken", sdk.NewInt(10)), + sdk.NewCoin("uakt", sdk.NewInt(10)), + ) + + generatedStd, err := s.createBankMsg( + s.cctx, + s.val, + sendTokens, + fmt.Sprintf("--%s=true", cflags.FlagGenerateOnly)) + s.Require().NoError(err) + + outputFile := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 3)) + defer func() { + _ = outputFile.Close() + }() + s.cctx.HomeDir = strings.Replace(s.cctx.HomeDir, "simd", "simcli", 1) + + // sign-batch file - offline is set but account-number and sequence are not + _, err = clitestutil.TxSignBatchExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + outputFile.Name(), + ). + WithFrom(s.val.String()). + WithChainID(s.cctx.ChainID). + WithOffline()...) + s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") + + // sign-batch file - offline and sequence is set but account-number is not set + _, err = clitestutil.TxSignBatchExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + outputFile.Name(), + ). + WithFrom(s.val.String()). + WithChainID(s.cctx.ChainID). + WithOffline(). + WithSequence(1)...) + s.Require().EqualError(err, "required flag(s) \"account-number\" not set") + + // sign-batch file - offline and account-number is set but sequence is not set + _, err = clitestutil.TxSignBatchExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + outputFile.Name(), + ). + WithFrom(s.val.String()). + WithChainID(s.cctx.ChainID). + WithOffline(). + WithAccountNumber(1)...) + s.Require().EqualError(err, "required flag(s) \"sequence\" not set") +} + +func (s *AuthCLITestSuite) TestCLIQueryTxCmdByHash() { + sendTokens := sdk.NewInt64Coin("uakt", 10) + + // Send coins. + out, err := s.createBankMsg( + s.cctx, s.val, + sdk.NewCoins(sendTokens), + ) + s.Require().NoError(err) + + var txRes sdk.TxResponse + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) + + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "not enough args", + []string{}, + "", + }, + { + "with invalid hash", + cli.TestFlags(). + With("somethinginvalid"). + WithOutputJSON(), + `[somethinginvalid --output=json]`, + }, + { + "with valid and not existing hash", + cli.TestFlags(). + With("C7E7D3A86A17AB3A321172239F3B61357937AF0F25D9FA4D2F4DCCAD9B0D7747"). + WithOutputJSON(), + `[C7E7D3A86A17AB3A321172239F3B61357937AF0F25D9FA4D2F4DCCAD9B0D7747 --output=json`, + }, + { + "happy case", + cli.TestFlags(). + With(txRes.TxHash). + WithOutputJSON(), + fmt.Sprintf("%s --%s=json", txRes.TxHash, cflags.FlagOutput), + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetQueryAuthTxCmd() + cmd.SetArgs(tc.args) + + if len(tc.args) != 0 { + s.Require().Contains(fmt.Sprint(cmd), tc.expCmdOutput) + } + }) + } +} + +func (s *AuthCLITestSuite) TestCLIQueryTxCmdByEvents() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "invalid --type", + cli.TestFlags(). + WithType("foo"). + With("bar"). + WithOutputJSON(), + "--type=foo bar --output=json", + }, + { + "--type=acc_seq with no addr+seq", + cli.TestFlags(). + WithType("acc_seq"). + With(""). + WithOutputJSON(), + "--type=acc_seq --output=json", + }, + { + "non-existing addr+seq combo", + cli.TestFlags(). + WithType("acc_seq"). + With("foobar"). + WithOutputJSON(), + "--type=acc_seq foobar --output=json", + }, + { + "--type=signature with no signature", + cli.TestFlags(). + WithType("signature"). + With(""). + WithOutputJSON(), + "--type=signature --output=json", + }, + { + "non-existing signatures", + cli.TestFlags(). + WithType("signature"). + With("foo"). + WithOutputJSON(), + "--type=signature foo --output=json", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetQueryAuthTxCmd() + cmd.SetArgs(tc.args) + + if len(tc.args) != 0 { + s.Require().Contains(fmt.Sprint(cmd), tc.expCmdOutput) + } + }) + } +} + +func (s *AuthCLITestSuite) TestCLIQueryTxsCmdByEvents() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "fee event happy case", + cli.TestFlags(). + WithEvents(fmt.Sprintf("tx.fee=%s", sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))).String())). + WithOutputJSON(), + "", + }, + { + "no matching fee event", + cli.TestFlags(). + WithEvents(fmt.Sprintf("tx.fee=%s", sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(0))).String())). + WithOutputJSON(), + "", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetQueryAuthTxsByEventsCmd() + + if len(tc.args) != 0 { + s.Require().Contains(fmt.Sprint(cmd), tc.expCmdOutput) + } + }) + } +} + +func (s *AuthCLITestSuite) TestCLISendGenerateSignAndBroadcast() { + sendTokens := sdk.NewCoin("uakt", sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) + + normalGeneratedTx, err := s.createBankMsg( + s.cctx, + s.val, + sdk.NewCoins(sendTokens), + cli.TestFlags(). + WithGas(cflags.DefaultGasLimit). + WithGenerateOnly()...) + s.Require().NoError(err) + + txCfg := s.cctx.TxConfig + + normalGeneratedStdTx, err := txCfg.TxJSONDecoder()(normalGeneratedTx.Bytes()) + s.Require().NoError(err) + + txBuilder, err := txCfg.WrapTxBuilder(normalGeneratedStdTx) + s.Require().NoError(err) + s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(cflags.DefaultGasLimit)) + s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) + + sigs, err := txBuilder.GetTx().GetSignaturesV2() + s.Require().NoError(err) + s.Require().Equal(0, len(sigs)) + + // Test generate sendTx with --gas=$amount + limitedGasGeneratedTx, err := s.createBankMsg( + s.cctx, + s.val, + sdk.NewCoins(sendTokens), + cli.TestFlags(). + WithGas(100). + WithGenerateOnly()...) + s.Require().NoError(err) + + limitedGasStdTx, err := txCfg.TxJSONDecoder()(limitedGasGeneratedTx.Bytes()) + s.Require().NoError(err) + + txBuilder, err = txCfg.WrapTxBuilder(limitedGasStdTx) + s.Require().NoError(err) + s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(100)) + s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) + + sigs, err = txBuilder.GetTx().GetSignaturesV2() + s.Require().NoError(err) + s.Require().Equal(0, len(sigs)) + + // Test generate sendTx, estimate gas + finalGeneratedTx, err := s.createBankMsg( + s.cctx, + s.val, + sdk.NewCoins(sendTokens), + cli.TestFlags(). + WithGas(cflags.DefaultGasLimit). + WithGenerateOnly()...) + s.Require().NoError(err) + + finalStdTx, err := txCfg.TxJSONDecoder()(finalGeneratedTx.Bytes()) + s.Require().NoError(err) + + txBuilder, err = txCfg.WrapTxBuilder(finalStdTx) + s.Require().NoError(err) + s.Require().Equal(uint64(flags.DefaultGasLimit), txBuilder.GetTx().GetGas()) + s.Require().Equal(len(finalStdTx.GetMsgs()), 1) + + // Write the output to disk + unsignedTxFile := testutil.WriteToNewTempFile(s.T(), finalGeneratedTx.String()) + defer func() { + _ = unsignedTxFile.Close() + }() + + // Test validate-signatures + res, err := clitestutil.TxValidateSignaturesExec( + context.Background(), + s.cctx, + unsignedTxFile.Name()) + s.Require().EqualError(err, "signatures validation failed") + s.Require().True(strings.Contains(res.String(), fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", s.val.String()))) + + // Test sign + + // Does not work in offline mode + _, err = clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(unsignedTxFile.Name()). + WithFrom(s.val.String()). + WithOffline()...) + s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") + + // But works offline if we set account number and sequence + s.cctx.HomeDir = strings.Replace(s.cctx.HomeDir, "simd", "simcli", 1) + _, err = clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(unsignedTxFile.Name()). + WithFrom(s.val.String()). + WithOffline(). + WithAccountNumber(1). + WithSequence(1)...) + s.Require().NoError(err) + + // Sign transaction + signedTx, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(unsignedTxFile.Name()). + WithFrom(s.val.String())...) + s.Require().NoError(err) + + signedFinalTx, err := txCfg.TxJSONDecoder()(signedTx.Bytes()) + s.Require().NoError(err) + + txBuilder, err = s.cctx.TxConfig.WrapTxBuilder(signedFinalTx) + s.Require().NoError(err) + s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) + + sigs, err = txBuilder.GetTx().GetSignaturesV2() + s.Require().NoError(err) + s.Require().Equal(1, len(sigs)) + s.Require().Equal(s.val.String(), txBuilder.GetTx().GetSigners()[0].String()) + + // Write the output to disk + signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) + defer func() { + _ = signedTxFile.Close() + }() + + // validate Signature + res, err = clitestutil.TxValidateSignaturesExec( + context.Background(), + s.cctx, + signedTxFile.Name()) + s.Require().NoError(err) + s.Require().True(strings.Contains(res.String(), "[OK]")) + + // Test broadcast + + // Does not work in offline mode + _, err = clitestutil.TxBroadcastExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(signedTxFile.Name()). + WithFrom(s.val.String()). + WithOffline()...) + s.Require().EqualError(err, "cannot broadcast tx during offline mode") + + // Broadcast correct transaction. + _, err = clitestutil.TxBroadcastExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(signedTxFile.Name()). + WithFrom(s.val.String()). + WithBroadcastModeSync()...) + s.Require().NoError(err) +} + +func (s *AuthCLITestSuite) TestCLIMultisignInsufficientCosigners() { + // Fetch account and a multisig info + account1, err := s.cctx.Keyring.Key("newAccount1") + s.Require().NoError(err) + + multisigRecord, err := s.cctx.Keyring.Key("multi") + s.Require().NoError(err) + + addr, err := multisigRecord.GetAddress() + s.Require().NoError(err) + // Send coins from validator to multisig. + _, err = s.createBankMsg( + s.cctx, + addr, + sdk.NewCoins( + sdk.NewInt64Coin("uakt", 10), + ), + ) + s.Require().NoError(err) + + // Generate multisig transaction. + multiGeneratedTx, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + addr.String(), + s.val.String(), + sdk.NewCoins( + sdk.NewInt64Coin("uakt", 5), + ).String(), + ). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithGenerateOnly()...) + s.Require().NoError(err) + + // Save tx to file + multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) + defer func() { + _ = multiGeneratedTxFile.Close() + }() + + // Multisign, sign with one signature + s.cctx.HomeDir = strings.Replace(s.cctx.HomeDir, "simd", "simcli", 1) + addr1, err := account1.GetAddress() + s.Require().NoError(err) + + account1Signature, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiGeneratedTxFile.Name(), + ). + WithFrom(addr1.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON). + WithMultisig(addr.String())...) + s.Require().NoError(err) + + sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) + defer func() { + _ = sign1File.Close() + }() + + multiSigWith1Signature, err := clitestutil.TxMultiSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiGeneratedTxFile.Name(), + multisigRecord.Name, + sign1File.Name(), + )...) + s.Require().NoError(err) + + // Save tx to file + multiSigWith1SignatureFile := testutil.WriteToNewTempFile(s.T(), multiSigWith1Signature.String()) + defer func() { + _ = multiSigWith1SignatureFile.Close() + }() + + _, err = clitestutil.TxValidateSignaturesExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiSigWith1SignatureFile.Name())...) + s.Require().Error(err) +} + +func (s *AuthCLITestSuite) TestCLIEncode() { + sendTokens := sdk.NewCoin("uakt", sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) + + normalGeneratedTx, err := s.createBankMsg( + s.cctx, + s.val, + sdk.NewCoins(sendTokens), + cli.TestFlags(). + WithGenerateOnly(). + WithNote("deadbeef"). + WithFrom(s.val.String())...) + s.Require().NoError(err) + + savedTxFile := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String()) + defer func() { + _ = savedTxFile.Close() + }() + + // Encode + encodeExec, err := clitestutil.TxEncodeExec( + context.Background(), + s.cctx, + savedTxFile.Name()) + s.Require().NoError(err) + + trimmedBase64 := strings.Trim(encodeExec.String(), "\"\n") + // Check that the transaction decodes as expected + decodedTx, err := clitestutil.TxDecodeExec( + context.Background(), + s.cctx, + trimmedBase64) + s.Require().NoError(err) + + txCfg := s.cctx.TxConfig + theTx, err := txCfg.TxJSONDecoder()(decodedTx.Bytes()) + s.Require().NoError(err) + txBuilder, err := s.cctx.TxConfig.WrapTxBuilder(theTx) + s.Require().NoError(err) + s.Require().Equal("deadbeef", txBuilder.GetTx().GetMemo()) +} + +func (s *AuthCLITestSuite) TestCLIMultisignSortSignatures() { + // Generate 2 accounts and a multisig. + account1, err := s.cctx.Keyring.Key("newAccount1") + s.Require().NoError(err) + + account2, err := s.cctx.Keyring.Key("newAccount2") + s.Require().NoError(err) + + multisigRecord, err := s.cctx.Keyring.Key("multi") + s.Require().NoError(err) + + // Generate dummy account which is not a part of multisig. + dummyAcc, err := s.cctx.Keyring.Key("dummyAccount") + s.Require().NoError(err) + + addr, err := multisigRecord.GetAddress() + s.Require().NoError(err) + + // Generate multisig transaction. + multiGeneratedTx, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + addr.String(), + s.val.String(), + sdk.NewCoins( + sdk.NewInt64Coin("uakt", 5), + ).String(), + ). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithGenerateOnly()...) + s.Require().NoError(err) + + // Save tx to file + multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) + defer func() { + _ = multiGeneratedTxFile.Close() + }() + + // Sign with account1 + addr1, err := account1.GetAddress() + s.Require().NoError(err) + s.cctx.HomeDir = strings.Replace(s.cctx.HomeDir, "simd", "simcli", 1) + account1Signature, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiGeneratedTxFile.Name(), + ). + WithFrom(addr1.String()). + WithMultisig(addr.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + + sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) + defer func() { + _ = sign1File.Close() + }() + + // Sign with account2 + addr2, err := account2.GetAddress() + s.Require().NoError(err) + account2Signature, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiGeneratedTxFile.Name(), + ). + WithFrom(addr2.String()). + WithMultisig(addr.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + + sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) + defer func() { + _ = sign2File.Close() + }() + + // Sign with dummy account + dummyAddr, err := dummyAcc.GetAddress() + s.Require().NoError(err) + _, err = clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiGeneratedTxFile.Name(), + ). + WithFrom(dummyAddr.String()). + WithMultisig(addr.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().Error(err) + s.Require().Contains(err.Error(), "signing key is not a part of multisig key") + + multiSigWith2Signatures, err := clitestutil.TxMultiSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiGeneratedTxFile.Name(), + multisigRecord.Name, + sign1File.Name(), + sign2File.Name()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + + // Write the output to disk + signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) + defer func() { + _ = signedTxFile.Close() + }() + + _, err = clitestutil.TxValidateSignaturesExec( + context.Background(), + s.cctx, + signedTxFile.Name()) + s.Require().NoError(err) + + _, err = clitestutil.TxBroadcastExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(signedTxFile.Name()). + WithBroadcastModeSync()...) + s.Require().NoError(err) +} + +func (s *AuthCLITestSuite) TestSignWithMultisig() { + // Generate a account for signing. + account1, err := s.cctx.Keyring.Key("newAccount1") + s.Require().NoError(err) + + addr1, err := account1.GetAddress() + s.Require().NoError(err) + + // Create an address that is not in the keyring, will be used to simulate `--multisig` + multisig := "akash1hd6fsrvnz6qkp87s3u86ludegq97agxsqdr9ad" + multisigAddr, err := sdk.AccAddressFromBech32(multisig) + s.Require().NoError(err) + + // Generate a transaction for testing --multisig with an address not in the keyring. + multisigTx, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + s.val.String(), + s.val.String(), + sdk.NewCoins( + sdk.NewInt64Coin("uakt", 5), + ).String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithGenerateOnly(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))...) + s.Require().NoError(err) + + // Save multi tx to file + multiGeneratedTx2File := testutil.WriteToNewTempFile(s.T(), multisigTx.String()) + defer func() { + _ = multiGeneratedTx2File.Close() + }() + + // Sign using multisig. We're signing a tx on behalf of the multisig address, + // even though the tx signer is NOT the multisig address. This is fine though, + // as the main point of this test is to test the `--multisig` flag with an address + // that is not in the keyring. + _, err = clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(multiGeneratedTx2File.Name()). + WithFrom(addr1.String()). + WithMultisig(multisigAddr.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + + s.Require().Contains(err.Error(), "error getting account from keybase") +} + +func (s *AuthCLITestSuite) TestCLIMultisign() { + // Generate 2 accounts and a multisig. + account1, err := s.cctx.Keyring.Key("newAccount1") + s.Require().NoError(err) + + account2, err := s.cctx.Keyring.Key("newAccount2") + s.Require().NoError(err) + + multisigRecord, err := s.cctx.Keyring.Key("multi") + s.Require().NoError(err) + + addr, err := multisigRecord.GetAddress() + s.Require().NoError(err) + + // Generate multisig transaction. + multiGeneratedTx, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + addr.String(), + s.val.String(), + sdk.NewCoins( + sdk.NewInt64Coin("uakt", 5), + ).String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithGenerateOnly(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))..., + ) + s.Require().NoError(err) + + // Save tx to file + multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) + defer func() { + _ = multiGeneratedTxFile.Close() + }() + + addr1, err := account1.GetAddress() + s.Require().NoError(err) + // Sign with account1 + s.cctx.HomeDir = strings.Replace(s.cctx.HomeDir, "simd", "simcli", 1) + account1Signature, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(multiGeneratedTxFile.Name()). + WithFrom(addr1.String()). + WithMultisig(addr.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + + sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) + defer func() { + _ = sign1File.Close() + }() + + addr2, err := account2.GetAddress() + s.Require().NoError(err) + // Sign with account2 + account2Signature, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiGeneratedTxFile.Name()). + WithFrom(addr2.String()). + WithMultisig(addr.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + + sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) + defer func() { + _ = sign2File.Close() + }() + + s.cctx.Offline = false + multiSigWith2Signatures, err := clitestutil.TxMultiSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + multiGeneratedTxFile.Name(), + multisigRecord.Name, + sign1File.Name(), + sign2File.Name()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + + // Write the output to disk + signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) + defer func() { + _ = signedTxFile.Close() + }() + + _, err = clitestutil.TxValidateSignaturesExec(context.Background(), s.cctx, signedTxFile.Name()) + s.Require().NoError(err) + + _, err = clitestutil.TxBroadcastExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(signedTxFile.Name()). + WithBroadcastModeSync()...) + s.Require().NoError(err) +} + +func (s *AuthCLITestSuite) TestSignBatchMultisig() { + // Fetch 2 accounts and a multisig. + account1, err := s.cctx.Keyring.Key("newAccount1") + s.Require().NoError(err) + account2, err := s.cctx.Keyring.Key("newAccount2") + s.Require().NoError(err) + multisigRecord, err := s.cctx.Keyring.Key("multi") + s.Require().NoError(err) + + addr, err := multisigRecord.GetAddress() + s.Require().NoError(err) + // Send coins from validator to multisig. + sendTokens := sdk.NewInt64Coin("uakt", 10) + _, err = s.createBankMsg( + s.cctx, + addr, + sdk.NewCoins(sendTokens), + ) + s.Require().NoError(err) + + generatedStd, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + addr.String(), + s.val.String(), + sdk.NewCoins( + sdk.NewCoin("uakt", sdk.NewInt(1)), + ).String(), + ). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithGenerateOnly()..., + ) + s.Require().NoError(err) + + // Write the output to disk + filename := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 1)) + defer func() { + _ = filename.Close() + }() + + s.cctx.HomeDir = strings.Replace(s.cctx.HomeDir, "simd", "simcli", 1) + + addr1, err := account1.GetAddress() + s.Require().NoError(err) + // sign-batch file + res, err := clitestutil.TxSignBatchExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + filename.Name(), + ). + WithFrom(addr1.String()). + WithChainID(s.cctx.ChainID). + WithSignatureOnly(). + WithMultisig(addr.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + // write sigs to file + file1 := testutil.WriteToNewTempFile(s.T(), res.String()) + defer func() { + _ = file1.Close() + }() + + addr2, err := account2.GetAddress() + s.Require().NoError(err) + // sign-batch file with account2 + res, err = clitestutil.TxSignBatchExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + filename.Name(), + ). + WithFrom(addr2.String()). + WithChainID(s.cctx.ChainID). + WithSignatureOnly(). + WithMultisig(addr.String()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + // write sigs to file2 + file2 := testutil.WriteToNewTempFile(s.T(), res.String()) + defer func() { + _ = file2.Close() + }() + _, err = clitestutil.TxMultiSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + filename.Name(), + multisigRecord.Name, + file1.Name(), + file2.Name()). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) +} + +func (s *AuthCLITestSuite) TestGetBroadcastCommandOfflineFlag() { + cmd := cli.GetBroadcastCommand() + _ = testutil.ApplyMockIODiscardOutErr(cmd) + cmd.SetArgs(cli.TestFlags().With("").WithOffline()) + + s.Require().EqualError(cmd.Execute(), "cannot broadcast tx during offline mode") +} + +func (s *AuthCLITestSuite) TestGetBroadcastCommandWithoutOfflineFlag() { + txCfg := s.cctx.TxConfig + cctx := client.Context{} + cctx = cctx.WithTxConfig(txCfg) + + // Create new file with tx + builder := txCfg.NewTxBuilder() + builder.SetGasLimit(200000) + from, err := sdk.AccAddressFromBech32("akash1cxlt8kznps92fwu3j6npahx4mjfutydy5g5de5") + s.Require().NoError(err) + + to, err := sdk.AccAddressFromBech32("akash1cxlt8kznps92fwu3j6npahx4mjfutydy5g5de5") + s.Require().NoError(err) + + err = builder.SetMsgs(banktypes.NewMsgSend(from, to, sdk.Coins{sdk.NewInt64Coin("uakt", 10000)})) + s.Require().NoError(err) + + txContents, err := txCfg.TxJSONEncoder()(builder.GetTx()) + s.Require().NoError(err) + + txFile := testutil.WriteToNewTempFile(s.T(), string(txContents)) + defer func() { + _ = txFile.Close() + }() + + out, err := clitestutil.TxBroadcastExec( + context.Background(), + cctx, + cli.TestFlags(). + With(txFile.Name()). + WithBroadcastModeSync()..., + ) + + s.Require().Error(err) + s.Require().Contains(err.Error(), "connect: connection refused") + s.Require().Contains(out.String(), "connect: connection refused") +} + +func (s *AuthCLITestSuite) TestQueryParamsCmd() { + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "happy case", + cli.TestFlags(). + WithOutputJSON(), + false, + }, + { + "with specific height", + cli.TestFlags(). + WithHeight(1). + WithOutputJSON(), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetQueryAuthParamsCmd() + cctx := s.cctx + + out, err := clitestutil.ExecTestCLICmd(context.Background(), cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + s.Require().NotEqual("internal", err.Error()) + } else { + var authParams authtypes.Params + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &authParams)) + s.Require().NotNil(authParams.MaxMemoCharacters) + } + }) + } +} + +// TestTxWithoutPublicKey makes sure sending a proto tx message without the +// public key doesn't cause any error in the RPC layer (broadcast). +// See https://github.com/cosmos/cosmos-sdk/issues/7585 for more details. +func (s *AuthCLITestSuite) TestTxWithoutPublicKey() { + txCfg := s.cctx.TxConfig + + // Create a txBuilder with an unsigned tx. + txBuilder := txCfg.NewTxBuilder() + msg := banktypes.NewMsgSend( + s.val, + s.val, + sdk.NewCoins( + sdk.NewCoin("Stake", sdk.NewInt(10)), + )) + err := txBuilder.SetMsgs(msg) + s.Require().NoError(err) + + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("Stake", sdk.NewInt(150)))) + txBuilder.SetGasLimit(testdata.NewTestGasLimit()) + + // Create a file with the unsigned tx. + txJSON, err := txCfg.TxJSONEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + + unsignedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) + defer func() { + _ = unsignedTxFile.Close() + }() + + // Sign the file with the unsignedTx. + signedTx, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(unsignedTxFile.Name()). + WithFrom(s.val.String()). + WithOverwrite()...) + s.Require().NoError(err) + + // Remove the signerInfo's `public_key` field manually from the signedTx. + // Note: this method is only used for test purposes! In general, one should + // use txBuilder and TxEncoder/TxDecoder to manipulate txs. + var tx tx.Tx + err = s.cctx.Codec.UnmarshalJSON(signedTx.Bytes(), &tx) + s.Require().NoError(err) + + tx.AuthInfo.SignerInfos[0].PublicKey = nil + // Re-encode the tx again, to another file. + txJSON, err = s.cctx.Codec.MarshalJSON(&tx) + s.Require().NoError(err) + + signedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) + defer func() { + _ = signedTxFile.Close() + }() + s.Require().True(strings.Contains(string(txJSON), "\"public_key\":null")) + + // Broadcast tx, test that it shouldn't panic. + out, err := clitestutil.TxBroadcastExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(signedTxFile.Name()). + WithBroadcastModeSync()...) + s.Require().NoError(err) + + var res sdk.TxResponse + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &res)) + s.Require().NotEqual(0, res.Code) +} + +// TestSignWithMultiSignersAminoJSON tests the case where a transaction with 2 +// messages which has to be signed with 2 different keys. Sign and append the +// signatures using the CLI with Amino signing mode. Finally, send the +// transaction to the blockchain. +func (s *AuthCLITestSuite) TestSignWithMultiSignersAminoJSON() { + val0, val1 := s.val, s.val1 + val0Coin := sdk.NewCoin("test1token", sdk.NewInt(10)) + val1Coin := sdk.NewCoin("test2token", sdk.NewInt(10)) + _, _, addr1 := testdata.KeyTestPubAddr() + + // Creating a tx with 2 msgs from 2 signers: val0 and val1. + // The validators need to sign with SIGN_MODE_LEGACY_AMINO_JSON, + // because DIRECT doesn't support multi signers via the CLI. + // Since we use amino, we don't need to pre-populate signer_infos. + txBuilder := s.cctx.TxConfig.NewTxBuilder() + err := txBuilder.SetMsgs( + banktypes.NewMsgSend(val0, addr1, sdk.NewCoins(val0Coin)), + banktypes.NewMsgSend(val1, addr1, sdk.NewCoins(val1Coin)), + ) + s.Require().NoError(err) + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))) + txBuilder.SetGasLimit(testdata.NewTestGasLimit() * 2) + s.Require().Equal([]sdk.AccAddress{val0, val1}, txBuilder.GetTx().GetSigners()) + + // Write the unsigned tx into a file. + txJSON, err := s.cctx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + unsignedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) + defer func() { + _ = unsignedTxFile.Close() + }() + + // Let val0 sign first the file with the unsignedTx. + signedByVal0, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(unsignedTxFile.Name()). + WithFrom(val0.String()). + WithOverwrite(). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + signedByVal0File := testutil.WriteToNewTempFile(s.T(), signedByVal0.String()) + defer func() { + _ = signedByVal0File.Close() + }() + + // Then let val1 sign the file with signedByVal0. + val1AccNum, val1Seq, err := s.cctx.AccountRetriever.GetAccountNumberSequence(s.cctx, val1) + s.Require().NoError(err) + + signedTx, err := clitestutil.TxSignExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(signedByVal0File.Name()). + WithFrom(val1.String()). + WithOffline(). + WithAccountNumber(val1AccNum). + WithSequence(val1Seq). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) + defer func() { + _ = signedTxFile.Close() + }() + + res, err := clitestutil.TxBroadcastExec( + context.Background(), + s.cctx, + cli.TestFlags(). + With(signedTxFile.Name()). + WithBroadcastModeSync()...) + s.Require().NoError(err) + + var txRes sdk.TxResponse + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(res.Bytes(), &txRes)) + s.Require().Equal(uint32(0), txRes.Code, txRes.RawLog) +} + +func (s *AuthCLITestSuite) TestAuxSigner() { + val0Coin := sdk.NewCoin("testtoken", sdk.NewInt(10)) + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "error with SIGN_MODE_DIRECT_AUX and --aux unset", + cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux), + true, + }, + { + "no error with SIGN_MODE_DIRECT_AUX mode and generate-only set (ignores generate-only)", + cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux). + WithGenerateOnly(), + false, + }, + { + "no error with SIGN_MODE_DIRECT_AUX mode and generate-only, tip flag set", + cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux). + WithGenerateOnly(). + WithTip(val0Coin), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetTxGovSubmitLegacyProposalCmd() + _, err := clitestutil.ExecTestCLICmd( + context.Background(), + s.cctx, + cmd, + cli.TestFlags(). + WithTitle("Text Proposal"). + WithDescription("test desc"). + WithProposalType(govtypes.ProposalTypeText). + WithFrom(s.val.String()). + Append(tc.args)...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + } + }) + } +} + +func (s *AuthCLITestSuite) TestAuxToFeeWithTips() { + // Skipping this test as it needs a simapp with the TipDecorator in post handler. + s.T().Skip() + + require := s.Require() + + kb := s.cctx.Keyring + acc, _, err := kb.NewMnemonic("tipperAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + require.NoError(err) + + tipper, err := acc.GetAddress() + require.NoError(err) + tipperInitialBal := sdk.NewCoin("testtoken", sdk.NewInt(10000)) + + feePayer := s.val + fee := sdk.NewCoin("uakt", sdk.NewInt(1000)) + tip := sdk.NewCoin("testtoken", sdk.NewInt(1000)) + + _, err = s.createBankMsg(s.cctx, tipper, sdk.NewCoins(tipperInitialBal)) + require.NoError(err) + + bal := s.getBalances(s.cctx, tipper, tip.Denom) + require.True(bal.Equal(tipperInitialBal.Amount)) + + testCases := []struct { + name string + tipper sdk.AccAddress + feePayer sdk.AccAddress + tip sdk.Coin + expectErrAux bool + expectErrBroadCast bool + errMsg string + tipperArgs []string + feePayerArgs []string + }{ + { + name: "when --aux and --sign-mode = direct set: error", + tipper: tipper, + feePayer: feePayer, + tip: tip, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirect). + WithTip(tip). + WithAux(), + expectErrAux: true, + feePayerArgs: cli.TestFlags(). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()). + WithFees(sdk.Coins{fee}), + }, + { + name: "both tipper, fee payer uses AMINO: no error", + tipper: tipper, + feePayer: feePayer, + tip: tip, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeLegacyAminoJSON). + WithTip(tip). + WithAux(), + feePayerArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeLegacyAminoJSON). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()). + WithFees(sdk.Coins{fee}), + }, + { + name: "tipper uses DIRECT_AUX, fee payer uses AMINO: no error", + tipper: tipper, + feePayer: feePayer, + tip: tip, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux). + WithTip(tip). + WithAux(), + feePayerArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeLegacyAminoJSON). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()). + WithFees(sdk.Coins{fee}), + }, + { + name: "--tip flag unset: no error", + tipper: tipper, + feePayer: feePayer, + tip: sdk.Coin{Denom: "testtoken", Amount: sdk.NewInt(0)}, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux). + WithAux(), + feePayerArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeLegacyAminoJSON). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()). + WithFees(sdk.Coins{fee}), + }, + { + name: "legacy amino json: no error", + tipper: tipper, + feePayer: feePayer, + tip: tip, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeLegacyAminoJSON). + WithTip(tip). + WithAux(), + feePayerArgs: cli.TestFlags(). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()). + WithFees(sdk.Coins{fee}), + }, + { + name: "tipper uses direct aux, fee payer uses direct: happy case", + tipper: tipper, + feePayer: feePayer, + tip: tip, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux). + WithTip(tip). + WithAux(), + feePayerArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirect). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()). + WithFees(sdk.Coins{fee}), + }, + { + name: "chain-id mismatch: error", + tipper: tipper, + feePayer: feePayer, + tip: tip, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux). + WithTip(tip). + WithAux(), + expectErrAux: false, + feePayerArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirect). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()). + WithFees(sdk.Coins{fee}). + WithChainID("foobar"), + expectErrBroadCast: true, + }, + { + name: "wrong denom in tip: error", + tipper: tipper, + feePayer: feePayer, + tip: sdk.Coin{Denom: "testtoken", Amount: sdk.NewInt(0)}, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux). + WithTip(sdk.Coin{Denom: "wrongDenom", Amount: sdk.NewInt(100)}). + WithAux(), + feePayerArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirect). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()). + WithFees(sdk.Coins{fee}), + errMsg: "insufficient funds", + }, + { + name: "insufficient fees: error", + tipper: tipper, + feePayer: feePayer, + tip: sdk.Coin{Denom: "testtoken", Amount: sdk.NewInt(0)}, + tipperArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirectAux). + WithTip(tip). + WithAux(), + feePayerArgs: cli.TestFlags(). + WithSignMode(cflags.SignModeDirect). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFrom(feePayer.String()), + errMsg: "insufficient fees", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetTxGovSubmitLegacyProposalCmd() + res, err := clitestutil.ExecTestCLICmd( + context.Background(), + s.cctx, + cmd, + cli.TestFlags(). + WithTitle("test"). + WithDescription("test desc"). + WithProposalType(govtypes.ProposalTypeText). + WithFrom(s.val.String()). + Append(tc.tipperArgs)...) + + if tc.expectErrAux { + require.Error(err) + } else { + require.NoError(err) + genTxFile := testutil.WriteToNewTempFile(s.T(), string(res.Bytes())) + defer func() { + _ = genTxFile.Close() + }() + + // broadcast the tx + res, err = clitestutil.TxAuxToFeeExec( + context.Background(), + s.cctx, + genTxFile.Name(), + tc.feePayerArgs..., + ) + + switch { + case tc.expectErrBroadCast: + require.Error(err) + + case tc.errMsg != "": + require.NoError(err) + + var txRes sdk.TxResponse + require.NoError(s.cctx.Codec.UnmarshalJSON(res.Bytes(), &txRes)) + + require.Contains(txRes.RawLog, tc.errMsg) + + default: + require.NoError(err) + + var txRes sdk.TxResponse + require.NoError(s.cctx.Codec.UnmarshalJSON(res.Bytes(), &txRes)) + + require.Equal(uint32(0), txRes.Code) + require.NotNil(int64(0), txRes.Height) + + bal = s.getBalances(s.cctx, tipper, tc.tip.Denom) + tipperInitialBal = tipperInitialBal.Sub(tc.tip) + require.True(bal.Equal(tipperInitialBal.Amount)) + } + } + }) + } +} + +func (s *AuthCLITestSuite) getBalances(cctx client.Context, addr sdk.AccAddress, denom string) math.Int { + resp, err := clitestutil.QueryBalancesExec(context.Background(), cctx, addr) + s.Require().NoError(err) + + var balRes banktypes.QueryAllBalancesResponse + err = cctx.Codec.UnmarshalJSON(resp.Bytes(), &balRes) + s.Require().NoError(err) + startTokens := balRes.Balances.AmountOf(denom) + return startTokens +} + +func (s *AuthCLITestSuite) createBankMsg(cctx client.Context, toAddr sdk.AccAddress, amount sdk.Coins, extraFlags ...string) (testutil.BufferWriter, error) { + return clitestutil.ExecSend( + context.Background(), + cctx, + cli.TestFlags(). + With( + s.val.String(), + toAddr.String(), + amount.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + Append(extraFlags)...) +} diff --git a/go/cli/auth_tips.go b/go/cli/auth_tips.go new file mode 100644 index 00000000..42b5e55b --- /dev/null +++ b/go/cli/auth_tips.go @@ -0,0 +1,80 @@ +package cli + +import ( + "fmt" + "os" + + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/spf13/cobra" + + cflags "pkg.akt.dev/go/cli/flags" +) + +func GetAuxToFeeCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "aux-to-fee ", + Short: "Includes the aux signer data in the tx, broadcast the tx, and sends the tip amount to the broadcaster", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + auxSignerData := tx.AuxSignerData{} + + bytes, err := os.ReadFile(args[0]) + if err != nil { + return err + } + + err = cctx.Codec.UnmarshalJSON(bytes, &auxSignerData) + if err != nil { + return err + } + + if auxSignerData.SignDoc.ChainId != cctx.ChainID { + return fmt.Errorf("expected chain-id %s, got %s in aux signer data", cctx.ChainID, auxSignerData.SignDoc.ChainId) + } + + txBuilder := cctx.TxConfig.NewTxBuilder() + err = txBuilder.AddAuxSignerData(auxSignerData) + if err != nil { + return err + } + + txBuilder.SetFeePayer(cctx.FromAddress) + // txBuilder.SetFeeAmount(f.Fees()) + // txBuilder.SetGasLimit(f.Gas()) + + // if cctx.GenerateOnly { + // json, err := cctx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + // if err != nil { + // return err + // } + // return cctx.PrintString(fmt.Sprintf("%s\n", json)) + // } + + // err = authclient.SignTx(f, cctx, cctx.FromName, txBuilder, cctx.Offline, false) + // if err != nil { + // return err + // } + // + // txBytes, err := cctx.TxConfig.TxEncoder()(txBuilder.GetTx()) + // if err != nil { + // return err + // } + + // broadcast to a Tendermint node + res, err := cl.Tx().BroadcastTx(ctx, txBuilder.GetTx()) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/auth_tx.go b/go/cli/auth_tx.go index 7fe3a1fa..1f804851 100644 --- a/go/cli/auth_tx.go +++ b/go/cli/auth_tx.go @@ -3,10 +3,10 @@ package cli import ( "encoding/base64" "encoding/hex" + "errors" "fmt" "os" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -19,15 +19,6 @@ import ( cflags "pkg.akt.dev/go/cli/flags" ) -const ( - flagMultisig = "multisig" - flagOverwrite = "overwrite" - flagSigOnly = "signature-only" - flagAmino = "amino" - flagNoAutoIncrement = "no-auto-increment" - flagAppend = "append" -) - // GetSignBatchCommand returns the transaction sign-batch command. func GetSignBatchCommand() *cobra.Command { cmd := &cobra.Command{ @@ -57,10 +48,10 @@ account key. It implies --signature-only. Args: cobra.MinimumNArgs(1), } - cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") + cmd.Flags().String(cflags.FlagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") cmd.Flags().String(cflags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") - cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit") - cmd.Flags().Bool(flagAppend, false, "Combine all message and generate single signed transaction for broadcast.") + cmd.Flags().Bool(cflags.FlagSigOnly, false, "Print only the generated signature, then exit") + cmd.Flags().Bool(cflags.FlagAppend, false, "Combine all message and generate single signed transaction for broadcast.") cflags.AddTxFlagsToCmd(cmd) @@ -71,18 +62,18 @@ account key. It implies --signature-only. func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) + cctx, err := client.GetClientTxContext(cmd) if err != nil { return err } - txFactory, err := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + txFactory, err := tx.NewFactoryCLI(cctx, cmd.Flags()) if err != nil { return err } - txCfg := clientCtx.TxConfig - printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly) + txCfg := cctx.TxConfig + printSignatureOnly, _ := cmd.Flags().GetBool(cflags.FlagSigOnly) - ms, err := cmd.Flags().GetString(flagMultisig) + ms, err := cmd.Flags().GetString(cflags.FlagMultisig) if err != nil { return err } @@ -93,7 +84,7 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { return err } defer closeFunc() - clientCtx.WithOutput(cmd.OutOrStdout()) + cctx.WithOutput(cmd.OutOrStdout()) // reads tx from args scanner, err := ReadTxsFromInput(txCfg, args...) @@ -101,19 +92,19 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { return err } - if !clientCtx.Offline { + if !cctx.Offline { if ms == "" { - from, err := cmd.Flags().GetString(flags.FlagFrom) + from, err := cmd.Flags().GetString(cflags.FlagFrom) if err != nil { return err } - addr, _, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from) + addr, _, _, err := client.GetFromFields(cctx, txFactory.Keybase(), from) if err != nil { return err } - acc, err := txFactory.AccountRetriever().GetAccount(clientCtx, addr) + acc, err := txFactory.AccountRetriever().GetAccount(cctx, addr) if err != nil { return err } @@ -124,16 +115,16 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { } } - appendMessagesToSingleTx, _ := cmd.Flags().GetBool(flagAppend) + appendMessagesToSingleTx, _ := cmd.Flags().GetBool(cflags.FlagAppend) // Combines all tx msgs and create single signed transaction if appendMessagesToSingleTx { - txBuilder := clientCtx.TxConfig.NewTxBuilder() + txBuilder := cctx.TxConfig.NewTxBuilder() msgs := make([]sdk.Msg, 0) newGasLimit := uint64(0) for scanner.Scan() { unsignedStdTx := scanner.Tx() - fe, err := clientCtx.TxConfig.WrapTxBuilder(unsignedStdTx) + fe, err := cctx.TxConfig.WrapTxBuilder(unsignedStdTx) if err != nil { return err } @@ -148,20 +139,20 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { // set the memo,fees,feeGranter,feePayer from cmd flags txBuilder.SetMemo(txFactory.Memo()) txBuilder.SetFeeAmount(txFactory.Fees()) - txBuilder.SetFeeGranter(clientCtx.FeeGranter) - txBuilder.SetFeePayer(clientCtx.FeePayer) + txBuilder.SetFeeGranter(cctx.FeeGranter) + txBuilder.SetFeePayer(cctx.FeePayer) // set the gasLimit txBuilder.SetGasLimit(newGasLimit) // sign the txs if ms == "" { - from, _ := cmd.Flags().GetString(flags.FlagFrom) - if err := sign(clientCtx, txBuilder, txFactory, from); err != nil { + from, _ := cmd.Flags().GetString(cflags.FlagFrom) + if err := sign(cctx, txBuilder, txFactory, from); err != nil { return err } } else { - if err := multisigSign(clientCtx, txBuilder, txFactory, ms); err != nil { + if err := multisigSign(cctx, txBuilder, txFactory, ms); err != nil { return err } } @@ -184,12 +175,12 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { // sign the txs if ms == "" { - from, _ := cmd.Flags().GetString(flags.FlagFrom) - if err := sign(clientCtx, txBuilder, txFactory, from); err != nil { + from, _ := cmd.Flags().GetString(cflags.FlagFrom) + if err := sign(cctx, txBuilder, txFactory, from); err != nil { return err } } else { - if err := multisigSign(clientCtx, txBuilder, txFactory, ms); err != nil { + if err := multisigSign(cctx, txBuilder, txFactory, ms); err != nil { return err } } @@ -245,7 +236,7 @@ func multisigSign(clientCtx client.Context, txBuilder client.TxBuilder, txFactor } func setOutputFile(cmd *cobra.Command) (func(), error) { - outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) + outputDoc, _ := cmd.Flags().GetString(cflags.FlagOutputDocument) if outputDoc == "" { return func() {}, nil } @@ -284,14 +275,14 @@ be generated via the 'multisign' command. Args: cobra.ExactArgs(1), } - cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") - cmd.Flags().Bool(flagOverwrite, false, "Overwrite existing signatures with a new one. If disabled, new signature will be appended") - cmd.Flags().Bool(flagSigOnly, false, "Print only the signatures") - cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") - cmd.Flags().Bool(flagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint") - flags.AddTxFlagsToCmd(cmd) + cmd.Flags().String(cflags.FlagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") + cmd.Flags().Bool(cflags.FlagOverwrite, false, "Overwrite existing signatures with a new one. If disabled, new signature will be appended") + cmd.Flags().Bool(cflags.FlagSigOnly, false, "Print only the signatures") + cmd.Flags().String(cflags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") + cmd.Flags().Bool(cflags.FlagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint") + cflags.AddTxFlagsToCmd(cmd) - _ = cmd.MarkFlagRequired(flags.FlagFrom) + _ = cmd.MarkFlagRequired(cflags.FlagFrom) return cmd } @@ -299,80 +290,82 @@ be generated via the 'multisign' command. func preSignCmd(cmd *cobra.Command, _ []string) { // Conditionally mark the account and sequence numbers required as no RPC // query will be done. - if offline, _ := cmd.Flags().GetBool(flags.FlagOffline); offline { - _ = cmd.MarkFlagRequired(flags.FlagAccountNumber) - _ = cmd.MarkFlagRequired(flags.FlagSequence) + if offline, _ := cmd.Flags().GetBool(cflags.FlagOffline); offline { + _ = cmd.MarkFlagRequired(cflags.FlagAccountNumber) + _ = cmd.MarkFlagRequired(cflags.FlagSequence) } } func makeSignCmd() func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) (err error) { - var clientCtx client.Context - - clientCtx, err = client.GetClientTxContext(cmd) + cctx, err := client.GetClientTxContext(cmd) if err != nil { return err } - clientCtx, txF, newTx, err := readTxAndInitContexts(clientCtx, cmd, args[0]) + cctx, txF, newTx, err := readTxAndInitContexts(cctx, cmd, args[0]) if err != nil { return err } - return signTx(cmd, clientCtx, txF, newTx) + return signTx(cmd, cctx, txF, newTx) } } -func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx sdk.Tx) error { +func signTx(cmd *cobra.Command, cctx client.Context, txF tx.Factory, newTx sdk.Tx) error { f := cmd.Flags() - txCfg := clientCtx.TxConfig + txCfg := cctx.TxConfig txBuilder, err := txCfg.WrapTxBuilder(newTx) if err != nil { return err } - printSignatureOnly, err := cmd.Flags().GetBool(flagSigOnly) + printSignatureOnly, err := cmd.Flags().GetBool(cflags.FlagSigOnly) if err != nil { return err } - multisig, err := cmd.Flags().GetString(flagMultisig) + multisig, err := cmd.Flags().GetString(cflags.FlagMultisig) if err != nil { return err } - from, err := cmd.Flags().GetString(flags.FlagFrom) + if (multisig != "") && (cctx.SignModeStr != cflags.SignModeLegacyAminoJSON) { + return errors.New("multisig supports only \"amino-json\" sign mode") + } + + from, err := cmd.Flags().GetString(cflags.FlagFrom) if err != nil { return err } - _, fromName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), from) + _, fromName, _, err := client.GetFromFields(cctx, txF.Keybase(), from) if err != nil { return fmt.Errorf("error getting account from keybase: %w", err) } - overwrite, err := f.GetBool(flagOverwrite) + overwrite, err := f.GetBool(cflags.FlagOverwrite) if err != nil { return err } if multisig != "" { // Bech32 decode error, maybe it's a name, we try to fetch from keyring - multisigAddr, multisigName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), multisig) + multisigAddr, multisigName, _, err := client.GetFromFields(cctx, txF.Keybase(), multisig) if err != nil { return fmt.Errorf("error getting account from keybase: %w", err) } - multisigkey, err := getMultisigRecord(clientCtx, multisigName) + multisigKey, err := getMultisigRecord(cctx, multisigName) if err != nil { return err } - multisigPubKey, err := multisigkey.GetPubKey() + multisigPubKey, err := multisigKey.GetPubKey() if err != nil { return err } multisigLegacyPub := multisigPubKey.(*kmultisig.LegacyAminoPubKey) - fromRecord, err := clientCtx.Keyring.Key(fromName) + fromRecord, err := cctx.Keyring.Key(fromName) if err != nil { return fmt.Errorf("error getting account from keybase: %w", err) } @@ -391,24 +384,24 @@ func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx return fmt.Errorf("signing key is not a part of multisig key") } err = SignTxWithSignerAddress( - txF, clientCtx, multisigAddr, fromName, txBuilder, clientCtx.Offline, overwrite) + txF, cctx, multisigAddr, fromName, txBuilder, cctx.Offline, overwrite) if err != nil { return err } printSignatureOnly = true } else { - err = SignTx(txF, clientCtx, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, overwrite) + err = SignTx(txF, cctx, cctx.GetFromName(), txBuilder, cctx.Offline, overwrite) } if err != nil { return err } - aminoJSON, err := f.GetBool(flagAmino) + aminoJSON, err := f.GetBool(cflags.FlagAmino) if err != nil { return err } - bMode, err := f.GetString(flags.FlagBroadcastMode) + bMode, err := f.GetString(cflags.FlagBroadcastMode) if err != nil { return err } @@ -420,11 +413,11 @@ func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx } defer closeFunc() - clientCtx.WithOutput(cmd.OutOrStdout()) + cctx.WithOutput(cmd.OutOrStdout()) var json []byte if aminoJSON { - stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBuilder.GetTx()) + stdTx, err := tx.ConvertTxToStdTx(cctx.LegacyAmino, txBuilder.GetTx()) if err != nil { return err } @@ -432,7 +425,7 @@ func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx Tx: stdTx, Mode: bMode, } - json, err = clientCtx.LegacyAmino.MarshalJSON(req) + json, err = cctx.LegacyAmino.MarshalJSON(req) if err != nil { return err } @@ -478,23 +471,23 @@ transaction will be not be performed as that will require RPC communication with Args: cobra.ExactArgs(1), } - flags.AddTxFlagsToCmd(cmd) + cflags.AddTxFlagsToCmd(cmd) return cmd } func makeValidateSignaturesCmd() func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) + cctx, err := client.GetClientTxContext(cmd) if err != nil { return err } - clientCtx, txBldr, stdTx, err := readTxAndInitContexts(clientCtx, cmd, args[0]) + cctx, txBldr, stdTx, err := readTxAndInitContexts(cctx, cmd, args[0]) if err != nil { return err } - if !printAndValidateSigs(cmd, clientCtx, txBldr.ChainID(), stdTx, clientCtx.Offline) { + if !printAndValidateSigs(cmd, cctx, txBldr.ChainID(), stdTx, cctx.Offline) { return fmt.Errorf("signatures validation failed") } @@ -506,10 +499,10 @@ func makeValidateSignaturesCmd() func(cmd *cobra.Command, args []string) error { // expected signers. In addition, if offline has not been supplied, the signature is // verified over the transaction sign bytes. Returns false if the validation fails. func printAndValidateSigs( - cmd *cobra.Command, clientCtx client.Context, chainID string, tx sdk.Tx, offline bool, + cmd *cobra.Command, cctx client.Context, chainID string, tx sdk.Tx, offline bool, ) bool { sigTx := tx.(authsigning.SigVerifiableTx) - signModeHandler := clientCtx.TxConfig.SignModeHandler() + signModeHandler := cctx.TxConfig.SignModeHandler() cmd.Println("Signers:") signers := sigTx.GetSigners() @@ -546,7 +539,7 @@ func printAndValidateSigs( // validate the actual signature over the transaction bytes since we can // reach out to a full node to query accounts. if !offline && success { - accNum, accSeq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, sigAddr) + accNum, accSeq, err := cctx.AccountRetriever.GetAccountNumberSequence(cctx, sigAddr) if err != nil { cmd.PrintErrf("failed to get account: %s\n", sigAddr) return false @@ -600,19 +593,19 @@ If you supply a dash (-) argument in place of an input filename, the command rea RunE: func(cmd *cobra.Command, args []string) error { cctx := client.GetClientContextFromCmd(cmd) - tx, err := authclient.ReadTxFromFile(cctx, args[0]) + txt, err := authclient.ReadTxFromFile(cctx, args[0]) if err != nil { return err } // re-encode it - txBytes, err := cctx.TxConfig.TxEncoder()(tx) + txb, err := cctx.TxConfig.TxEncoder()(txt) if err != nil { return err } // base64 encode the encoded tx bytes - txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes) + txBytesBase64 := base64.StdEncoding.EncodeToString(txb) return cctx.PrintString(txBytesBase64 + "\n") }, @@ -646,12 +639,12 @@ func GetDecodeCommand() *cobra.Command { return err } - tx, err := cctx.TxConfig.TxDecoder()(txBytes) + txb, err := cctx.TxConfig.TxDecoder()(txBytes) if err != nil { return err } - json, err := cctx.TxConfig.TxJSONEncoder()(tx) + json, err := cctx.TxConfig.TxJSONEncoder()(txb) if err != nil { return err } diff --git a/go/cli/authz_query.go b/go/cli/authz_query.go new file mode 100644 index 00000000..402c0e68 --- /dev/null +++ b/go/cli/authz_query.go @@ -0,0 +1,198 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/authz" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetQueryAuthzCmd returns the cli query commands for this module +func GetQueryAuthzCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: authz.ModuleName, + Short: "Querying commands for the authz module", + Long: "", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetQueryAuthzGrantsCmd(), + GetQueryAuthzGranterGrantsCmd(), + GetQueryAuthzGranteeGrantsCmd(), + ) + + return cmd +} + +// GetQueryAuthzGrantsCmd implements the query authorization command. +func GetQueryAuthzGrantsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "grants [granter-addr] [grantee-addr] [msg-type-url]?", + Args: cobra.RangeArgs(2, 3), + Short: "query grants for a granter-grantee pair and optionally a msg-type-url", + Long: strings.TrimSpace( + fmt.Sprintf(`Query authorization grants for a granter-grantee pair. If msg-type-url +is set, it will select grants only for that msg type. +Examples: +$ %s query %s grants cosmos1skj.. cosmos1skjwj.. +$ %s query %s grants cosmos1skjw.. cosmos1skjwj.. %s +`, + version.AppName, authz.ModuleName, + version.AppName, authz.ModuleName, bank.SendAuthorization{}.MsgTypeURL()), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := authz.NewQueryClient(clientCtx) + + granter, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + grantee, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + msgAuthorized := "" + if len(args) >= 3 { + msgAuthorized = args[2] + } + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := queryClient.Grants( + cmd.Context(), + &authz.QueryGrantsRequest{ + Granter: granter.String(), + Grantee: grantee.String(), + MsgTypeUrl: msgAuthorized, + Pagination: pageReq, + }, + ) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "grants") + + return cmd +} + +// GetQueryAuthzGranterGrantsCmd returns cmd to query for all grants for a granter. +func GetQueryAuthzGranterGrantsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "grants-by-granter [granter-addr]", + Args: cobra.ExactArgs(1), + Short: "query authorization grants granted by granter", + Long: strings.TrimSpace( + fmt.Sprintf(`Query authorization grants granted by granter. +Examples: +$ %s q %s grants-by-granter cosmos1skj.. +`, + version.AppName, authz.ModuleName), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + granter, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := cl.Query().Authz().GranterGrants( + cmd.Context(), + &authz.QueryGranterGrantsRequest{ + Granter: granter.String(), + Pagination: pageReq, + }, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "granter-grants") + + return cmd +} + +// GetQueryAuthzGranteeGrantsCmd returns cmd to query for all grants for a grantee. +func GetQueryAuthzGranteeGrantsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "grants-by-grantee [grantee-addr]", + Args: cobra.ExactArgs(1), + Short: "query authorization grants granted to a grantee", + Long: strings.TrimSpace( + fmt.Sprintf(`Query authorization grants granted to a grantee. +Examples: +$ %s q %s grants-by-grantee cosmos1skj.. +`, + version.AppName, authz.ModuleName), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + grantee, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := cl.Query().Authz().GranteeGrants( + cmd.Context(), + &authz.QueryGranteeGrantsRequest{ + Grantee: grantee.String(), + Pagination: pageReq, + }, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "grantee-grants") + + return cmd +} diff --git a/go/cli/authz_query_test.go b/go/cli/authz_query_test.go new file mode 100644 index 00000000..929a11a9 --- /dev/null +++ b/go/cli/authz_query_test.go @@ -0,0 +1,239 @@ +package cli_test + +import ( + "context" + "fmt" + "time" + + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + + "pkg.akt.dev/go/cli" + cflags "pkg.akt.dev/go/cli/flags" + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +func (s *AuthzCLITestSuite) TestQueryAuthorizations() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With(grantee.String(), "send"). + WithSpendLimit("100uakt"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))..., + ) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expErrMsg string + }{ + { + "Error: Invalid grantee", + []string{ + val[0].Address.String(), + "invalid grantee", + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + true, + "decoding bech32 failed: invalid character in string: ' '", + }, + { + "Error: Invalid granter", + []string{ + "invalid granter", + grantee.String(), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + true, + "decoding bech32 failed: invalid character in string: ' '", + }, + { + "Valid txn (json)", + []string{ + val[0].Address.String(), + grantee.String(), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + false, + ``, + }, + } + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryAuthzGrantsCmd() + resp, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(string(resp.Bytes()), tc.expErrMsg) + } else { + s.Require().NoError(err) + var grants authz.QueryGrantsResponse + err = s.cctx.Codec.UnmarshalJSON(resp.Bytes(), &grants) + s.Require().NoError(err) + } + }) + } +} + +func (s *AuthzCLITestSuite) TestQueryAuthorization() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + grantee.String(), + "send", + fmt.Sprintf("--%s=100stake", cflags.FlagSpendLimit), + fmt.Sprintf("--%s=true", cflags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", cflags.FlagFrom, val[0].Address), + fmt.Sprintf("--%s=%s", cflags.FlagBroadcastMode, cflags.BroadcastSync), + fmt.Sprintf("--%s=%d", cflags.FlagExpiration, twoHours), + fmt.Sprintf("--%s=%s", cflags.FlagFees, sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))).String()), + ) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "Error: Invalid grantee", + []string{ + val[0].Address.String(), + "invalid grantee", + typeMsgSend, + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + true, + }, + { + "Error: Invalid granter", + []string{ + "invalid granter", + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + true, + }, + { + "Valid txn (json)", + []string{ + val[0].Address.String(), + grantee.String(), + typeMsgSend, + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + false, + }, + { + "Valid txn with allowed list (json)", + []string{ + val[0].Address.String(), + s.grantee[3].String(), + typeMsgSend, + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + false, + }, + } + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryAuthzGrantsCmd() + _, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + } + }) + } +} + +func (s *AuthzCLITestSuite) TestQueryGranterGrants() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + require := s.Require() + + testCases := []struct { + name string + args []string + expectErr bool + expectedErr string + }{ + { + "invalid address", + []string{ + "invalid-address", + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + true, + "decoding bech32 failed", + }, + { + "no authorization found", + []string{ + grantee.String(), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + false, + "", + }, + { + "valid case", + []string{ + val[0].Address.String(), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + false, + "", + }, + { + "valid case with pagination", + []string{ + val[0].Address.String(), + "--limit=2", + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + false, + "", + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + cmd := cli.GetQueryAuthzGranterGrantsCmd() + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + if tc.expectErr { + require.Error(err) + require.Contains(out.String(), tc.expectedErr) + } else { + require.NoError(err) + var grants authz.QueryGranterGrantsResponse + require.NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &grants)) + } + }) + } +} diff --git a/go/cli/authz_suite_test.go b/go/cli/authz_suite_test.go new file mode 100644 index 00000000..d7c84aea --- /dev/null +++ b/go/cli/authz_suite_test.go @@ -0,0 +1,150 @@ +package cli_test + +import ( + "bytes" + "context" + "io" + "time" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/bank" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/gov" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + + "pkg.akt.dev/go/cli" + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +var ( + typeMsgSend = banktypes.SendAuthorization{}.MsgTypeURL() + typeMsgVote = sdk.MsgTypeURL(&govv1.MsgVote{}) + typeMsgSubmitProposal = sdk.MsgTypeURL(&govv1.MsgSubmitProposal{}) +) + +type AuthzCLITestSuite struct { + CLITestSuite + grantee []sdk.AccAddress + addrs []sdk.AccAddress +} + +func (s *AuthzCLITestSuite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(gov.AppModuleBasic{}, bank.AppModuleBasic{}) + s.kr = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithLegacyAmino(s.encCfg.Amino). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID("test-chain") + + ctxGen := func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + } + var outBuf bytes.Buffer + + s.cctx = ctxGen(). + WithOutput(&outBuf). + WithSignModeStr("direct") + + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + s.grantee = make([]sdk.AccAddress, 6) + + s.addrs = make([]sdk.AccAddress, 1) + s.addrs[0] = s.createAccount("validator address") + + // Send some funds to the new account. + // Create new account in the keyring. + s.grantee[0] = s.createAccount("grantee1") + s.msgSendExec(s.grantee[0]) + + // create a proposal with deposit + _, err := clitestutil.ExecGovSubmitLegacyProposal( + context.Background(), + s.cctx, + cli.TestFlags(). + WithFrom(val[0].Address.String()). + WithTitle("Text Proposal 1"). + WithSkipConfirm(). + WithDescription("Where is the title!?"). + WithProposalType(govv1beta1.ProposalTypeText). + WithDeposit(sdk.NewCoin("uakt", govv1.DefaultMinDepositTokens))...) + s.Require().NoError(err) + + // Create new account in the keyring. + s.grantee[1] = s.createAccount("grantee2") + // Send some funds to the new account. + s.msgSendExec(s.grantee[1]) + + // grant send authorization to grantee2 + out, err := clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With(s.grantee[1].String(), "send"). + WithSpendLimit("100uakt"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)))). + WithExpiration(time.Now().Add(time.Minute*time.Duration(120)).Unix())...) + s.Require().NoError(err) + + var response sdk.TxResponse + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + + // Create new account in the keyring. + s.grantee[2] = s.createAccount("grantee3") + + // grant send authorization to grantee3 + _, err = clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With(s.grantee[2].String(), "send"). + WithSpendLimit("100uakt"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)))). + WithExpiration(time.Now().Add(time.Minute*time.Duration(120)).Unix())...) + s.Require().NoError(err) + + // Create new accounts in the keyring. + s.grantee[3] = s.createAccount("grantee4") + s.msgSendExec(s.grantee[3]) + + s.grantee[4] = s.createAccount("grantee5") + s.grantee[5] = s.createAccount("grantee6") + + // grant send authorization with allow list to grantee4 + out, err = clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With(s.grantee[3].String(), "send"). + WithSpendLimit("100uakt"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)))). + WithExpiration(time.Now().Add(time.Minute*time.Duration(120)).Unix()). + WithAllowList(s.grantee[4].String())...) + s.Require().NoError(err) + + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) +} diff --git a/go/cli/authz_tx.go b/go/cli/authz_tx.go new file mode 100644 index 00000000..7281f1b2 --- /dev/null +++ b/go/cli/authz_tx.go @@ -0,0 +1,324 @@ +package cli + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/authz" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + staking "github.com/cosmos/cosmos-sdk/x/staking/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// Flag names and values +const ( + delegate = "delegate" + redelegate = "redelegate" + unbond = "unbond" +) + +// GetTxAuthzCmd returns the transaction commands for this module +func GetTxAuthzCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: authz.ModuleName, + Short: "Authorization transactions subcommands", + Long: "Authorize and revoke access to execute transactions on behalf of your address", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetTxAuthzGrantAuthorizationCmd(), + GetTxAuthzRevokeAuthorizationCmd(), + GetTxAuthzExecAuthorizationCmd(), + ) + + return cmd +} + +// GetTxAuthzGrantAuthorizationCmd returns a CLI command handler for creating a MsgGrant transaction. +func GetTxAuthzGrantAuthorizationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "grant --from ", + Short: "Grant authorization to an address", + Long: strings.TrimSpace( + fmt.Sprintf(`create a new grant authorization to an address to execute a transaction on your behalf: + +Examples: + $ %s tx %s grant akash1skjw.. send --spend-limit=1000uakt --from= + $ %s tx %s grant akash1skjw.. generic --msg-type=/cosmos.gov.v1.MsgVote --from= + `, version.AppName, authz.ModuleName, version.AppName, authz.ModuleName), + ), + Args: cobra.ExactArgs(2), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + grantee, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + var authorization authz.Authorization + switch args[1] { + case "send": + limit, err := cmd.Flags().GetString(cflags.FlagSpendLimit) + if err != nil { + return err + } + + spendLimit, err := sdk.ParseCoinsNormalized(limit) + if err != nil { + return err + } + + if !spendLimit.IsAllPositive() { + return fmt.Errorf("spend-limit should be greater than zero") + } + + allowList, err := cmd.Flags().GetStringSlice(cflags.FlagAllowList) + if err != nil { + return err + } + + allowed, err := bech32toAccAddresses(allowList) + if err != nil { + return err + } + + authorization = bank.NewSendAuthorization(spendLimit, allowed) + + case "generic": + msgType, err := cmd.Flags().GetString(cflags.FlagMsgType) + if err != nil { + return err + } + + authorization = authz.NewGenericAuthorization(msgType) + case delegate, unbond, redelegate: + limit, err := cmd.Flags().GetString(cflags.FlagSpendLimit) + if err != nil { + return err + } + + allowValidators, err := cmd.Flags().GetStringSlice(cflags.FlagAllowedValidators) + if err != nil { + return err + } + + denyValidators, err := cmd.Flags().GetStringSlice(cflags.FlagDenyValidators) + if err != nil { + return err + } + + var delegateLimit *sdk.Coin + if limit != "" { + spendLimit, err := sdk.ParseCoinNormalized(limit) + if err != nil { + return err + } + + res, err := cl.Query().Staking().Params(cmd.Context(), &staking.QueryParamsRequest{}) + if err != nil { + return err + } + + if spendLimit.Denom != res.Params.BondDenom { + return fmt.Errorf("invalid denom %s; coin denom should match the current bond denom %s", spendLimit.Denom, res.Params.BondDenom) + } + + if !spendLimit.IsPositive() { + return fmt.Errorf("spend-limit should be greater than zero") + } + delegateLimit = &spendLimit + } + + allowed, err := bech32toValAddresses(allowValidators) + if err != nil { + return err + } + + denied, err := bech32toValAddresses(denyValidators) + if err != nil { + return err + } + + switch args[1] { + case delegate: + authorization, err = staking.NewStakeAuthorization(allowed, denied, staking.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, delegateLimit) + case unbond: + authorization, err = staking.NewStakeAuthorization(allowed, denied, staking.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, delegateLimit) + default: + authorization, err = staking.NewStakeAuthorization(allowed, denied, staking.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, delegateLimit) + } + if err != nil { + return err + } + + default: + return fmt.Errorf("invalid authorization type, %s", args[1]) + } + + expire, err := getExpireTime(cmd) + if err != nil { + return err + } + + msg, err := authz.NewMsgGrant(cctx.GetFromAddress(), grantee, authorization, expire) + if err != nil { + return err + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + cmd.Flags().String(cflags.FlagMsgType, "", "The Msg method name for which we are creating a GenericAuthorization") + cmd.Flags().String(cflags.FlagSpendLimit, "", "SpendLimit for Send Authorization, an array of Coins allowed spend") + cmd.Flags().StringSlice(cflags.FlagAllowedValidators, []string{}, "Allowed validators addresses separated by ,") + cmd.Flags().StringSlice(cflags.FlagDenyValidators, []string{}, "Deny validators addresses separated by ,") + cmd.Flags().StringSlice(cflags.FlagAllowList, []string{}, "Allowed addresses grantee is allowed to send funds separated by ,") + cmd.Flags().Int64(cflags.FlagExpiration, 0, "Expire time as Unix timestamp. Set zero (0) for no expiry. Default is 0.") + + return cmd +} + +func getExpireTime(cmd *cobra.Command) (*time.Time, error) { + exp, err := cmd.Flags().GetInt64(cflags.FlagExpiration) + if err != nil { + return nil, err + } + if exp == 0 { + return nil, nil + } + e := time.Unix(exp, 0) + return &e, nil +} + +// GetTxAuthzRevokeAuthorizationCmd returns a CLI command handler for creating a MsgRevoke transaction. +func GetTxAuthzRevokeAuthorizationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "revoke [grantee] [msg-type-url] --from=[granter]", + Short: "revoke authorization", + Long: strings.TrimSpace( + fmt.Sprintf(`revoke authorization from a granter to a grantee: +Example: + $ %s tx %s revoke akash1skj.. %s --from= + `, version.AppName, authz.ModuleName, bank.SendAuthorization{}.MsgTypeURL()), + ), + Args: cobra.ExactArgs(2), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + grantee, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + granter := cctx.GetFromAddress() + msgAuthorized := args[1] + msg := authz.NewMsgRevoke(granter, grantee, msgAuthorized) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{&msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + cflags.AddTxFlagsToCmd(cmd) + return cmd +} + +// GetTxAuthzExecAuthorizationCmd returns a CLI command handler for creating a MsgExec transaction. +func GetTxAuthzExecAuthorizationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "exec [tx-json-file] --from [grantee]", + Short: "execute tx on behalf of granter account", + Long: strings.TrimSpace( + fmt.Sprintf(`execute tx on behalf of granter account: +Example: + $ %s tx %s exec tx.json --from grantee + $ %s tx bank send --from --chain-id --generate-only > tx.json && %s tx %s exec tx.json --from grantee + `, version.AppName, authz.ModuleName, version.AppName, version.AppName, authz.ModuleName), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + grantee := cctx.GetFromAddress() + + if offline, _ := cmd.Flags().GetBool(cflags.FlagOffline); offline { + return errors.New("cannot broadcast tx during offline mode") + } + + theTx, err := ReadTxFromFile(cctx, args[0]) + if err != nil { + return err + } + msg := authz.NewMsgExec(grantee, theTx.GetMsgs()) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{&msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// bech32toValAddresses returns []ValAddress from a list of Bech32 string addresses. +func bech32toValAddresses(validators []string) ([]sdk.ValAddress, error) { + vals := make([]sdk.ValAddress, len(validators)) + for i, validator := range validators { + addr, err := sdk.ValAddressFromBech32(validator) + if err != nil { + return nil, err + } + vals[i] = addr + } + return vals, nil +} + +// bech32toAccAddresses returns []AccAddress from a list of Bech32 string addresses. +func bech32toAccAddresses(accAddrs []string) ([]sdk.AccAddress, error) { + addrs := make([]sdk.AccAddress, len(accAddrs)) + for i, addr := range accAddrs { + accAddr, err := sdk.AccAddressFromBech32(addr) + if err != nil { + return nil, err + } + addrs[i] = accAddr + } + return addrs, nil +} diff --git a/go/cli/authz_tx_test.go b/go/cli/authz_tx_test.go new file mode 100644 index 00000000..8c11f9a6 --- /dev/null +++ b/go/cli/authz_tx_test.go @@ -0,0 +1,827 @@ +package cli_test + +import ( + "context" + "fmt" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + authzclitestutil "github.com/cosmos/cosmos-sdk/x/authz/client/testutil" + "github.com/cosmos/gogoproto/proto" + + "pkg.akt.dev/go/cli" + cflags "pkg.akt.dev/go/cli/flags" + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +func (s *AuthzCLITestSuite) createAccount(uid string) sdk.AccAddress { + // Create new account in the keyring. + k, _, err := s.cctx.Keyring.NewMnemonic(uid, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + s.Require().NoError(err) + + addr, err := k.GetAddress() + s.Require().NoError(err) + + return addr +} + +func (s *AuthzCLITestSuite) msgSendExec(grantee sdk.AccAddress) { + val := s.addrs[0] + + // Send some funds to the new account. + out, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + val.String(), + grantee.String(), + sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(200))).String(), + ). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))...) + s.Require().NoError(err) + s.Require().Contains(out.String(), `"code":0`) +} + +func (s *AuthzCLITestSuite) TestCLITxGrantAuthorization() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + + twoHours := time.Now().Add(time.Minute * 120).Unix() + pastHour := time.Now().Add(-time.Minute * 60).Unix() + + testCases := []struct { + name string + args []string + expectErr bool + expErrMsg string + }{ + { + "Invalid granter Address", + cli.TestFlags(). + With( + "grantee_addr", + "send", + ). + WithSpendLimit("100uakt"). + WithFrom("granter"). + WithGenerateOnly(). + WithExpiration(twoHours), + true, + "key not found", + }, + { + "Invalid grantee Address", + cli.TestFlags(). + With( + "grantee_addr", + "send", + ). + WithSpendLimit("100uakt"). + WithFrom(val[0].Address.String()). + WithGenerateOnly(). + WithExpiration(twoHours), + true, + "invalid separator index", + }, + { + "Invalid expiration time", + cli.TestFlags(). + With( + grantee.String(), + "send", + ). + WithSpendLimit("100uakt"). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(pastHour), + true, + "", + }, + { + "fail with error invalid msg-type", + cli.TestFlags(). + With( + grantee.String(), + "generic", + ). + WithMsgType("invalid-msg-type"). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithExpiration(twoHours), + false, + "", + }, + { + "invalid bond denom for tx delegate authorization allowed validators", + cli.TestFlags(). + With( + grantee.String(), + "delegate", + ). + WithSpendLimit("100xyz"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithAllowedValidators(sdk.ValAddress(s.addrs[0]).String()), + true, + "invalid denom", + }, + { + "invalid bond denom for tx delegate authorization deny validators", + cli.TestFlags(). + With( + grantee.String(), + "delegate", + ). + WithSpendLimit("100xyz"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithDenyValidators(sdk.ValAddress(s.addrs[0]).String()), + true, + "invalid denom", + }, + { + "invalid bond denom for tx undelegate authorization", + cli.TestFlags(). + With( + grantee.String(), + "unbond", + ). + WithSpendLimit("100xyz"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithAllowedValidators(sdk.ValAddress(s.addrs[0]).String()), + true, + "invalid denom", + }, + { + "invalid bond denom for tx redelegate authorization", + cli.TestFlags(). + With( + grantee.String(), + "redelegate", + ). + WithSpendLimit("100xyz"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithAllowedValidators(sdk.ValAddress(s.addrs[0]).String()), + true, + "invalid denom", + }, + { + "invalid decimal coin expression with more than single coin", + cli.TestFlags(). + With( + grantee.String(), + "delegate", + ). + WithSpendLimit("100uakt,20xyz"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithAllowedValidators(sdk.ValAddress(s.addrs[0]).String()), + true, + "invalid decimal coin expression", + }, + { + "Valid tx send authorization", + cli.TestFlags(). + With( + grantee.String(), + "send", + ). + WithSpendLimit("100uakt"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, + "", + }, + { + "Valid tx send authorization with allow list", + cli.TestFlags(). + With( + grantee.String(), + "send", + ). + WithSpendLimit("100uakt"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithAllowList(s.grantee[1].String()), + false, + "", + }, + { + "Invalid tx send authorization with duplicate allow list", + cli.TestFlags(). + With( + grantee.String(), + "send", + ). + WithSpendLimit("100uakt"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithAllowList(fmt.Sprintf("%s,%s", s.grantee[1], s.grantee[1])), + true, + "duplicate entry", + }, + { + "Valid tx generic authorization", + cli.TestFlags(). + With( + grantee.String(), + "generic", + ). + WithMsgType(typeMsgVote). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, + "", + }, + { + "fail when granter = grantee", + cli.TestFlags(). + With( + grantee.String(), + "generic", + ). + WithMsgType(typeMsgVote). + WithSkipConfirm(). + WithFrom(grantee.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + true, + "grantee and granter should be different", + }, + { + "Valid tx with amino", + cli.TestFlags(). + With( + grantee.String(), + "generic", + ). + WithMsgType(typeMsgVote). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithSignMode(cflags.SignModeLegacyAminoJSON), + false, + "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + out, err := authzclitestutil.CreateGrant(s.cctx, + tc.args, + ) + if tc.expectErr { + s.Require().Error(err, out) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + var txResp sdk.TxResponse + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &txResp), out.String()) + } + }) + } +} + +func (s *AuthzCLITestSuite) TestCmdRevokeAuthorizations() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + // send-authorization + _, err := clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + grantee.String(), + "send", + ). + WithSpendLimit("100uakt"). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))...) + s.Require().NoError(err) + + // generic-authorization + _, err = clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + grantee.String(), + "generic", + ). + WithMsgType(typeMsgVote). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))...) + s.Require().NoError(err) + + // generic-authorization used for amino testing + _, err = clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + grantee.String(), + "generic", + ). + WithMsgType(typeMsgSubmitProposal). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithSignMode(cflags.SignModeLegacyAminoJSON)...) + s.Require().NoError(err) + testCases := []struct { + name string + args []string + respType proto.Message + expectErr bool + }{ + { + "invalid grantee address", + cli.TestFlags(). + With( + "invalid grantee", + typeMsgSend, + ). + WithFrom(val[0].Address.String()). + WithGenerateOnly(), + nil, + true, + }, + { + "invalid granter address", + cli.TestFlags(). + With( + grantee.String(), + typeMsgSend, + ). + WithFrom("granter"). + WithGenerateOnly(), + nil, + true, + }, + { + "Valid tx send authorization", + cli.TestFlags(). + With( + grantee.String(), + typeMsgSend, + ). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + &sdk.TxResponse{}, + false, + }, + { + "Valid tx generic authorization", + cli.TestFlags(). + With( + grantee.String(), + typeMsgVote, + ). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + &sdk.TxResponse{}, + false, + }, + { + "Valid tx with amino", + cli.TestFlags(). + With( + grantee.String(), + typeMsgSubmitProposal, + ). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithSignMode(cflags.SignModeLegacyAminoJSON), + &sdk.TxResponse{}, + false, + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetTxAuthzRevokeAuthorizationCmd() + + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *AuthzCLITestSuite) TestExecAuthorizationWithExpiration() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + grantee := s.grantee[0] + tenSeconds := time.Now().Add(time.Second * time.Duration(10)).Unix() + + _, err := clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + grantee.String(), + "generic", + ). + WithMsgType(typeMsgVote). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(tenSeconds). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))...) + s.Require().NoError(err) + // msg vote + voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1.MsgVote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val[0].Address.String()) + execMsg := testutil.WriteToNewTempFile(s.T(), voteTx) + defer func() { + _ = execMsg.Close() + }() + + // waiting for authorization to expire + time.Sleep(12 * time.Second) + + cmd := cli.GetTxAuthzExecAuthorizationCmd() + + out, err := clitestutil.ExecTestCLICmd( + context.Background(), + s.cctx, + cmd, + cli.TestFlags(). + With( + execMsg.Name(), + ). + WithFrom(grantee.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))...) + s.Require().NoError(err) + var response sdk.TxResponse + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) +} + +func (s *AuthzCLITestSuite) TestNewExecGenericAuthorized() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + grantee.String(), + "generic", + ). + WithMsgType(typeMsgVote). + WithSkipConfirm(). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithExpiration(twoHours). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))))...) + s.Require().NoError(err) + + // msg vote + voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1.MsgVote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val[0].Address.String()) + execMsg := testutil.WriteToNewTempFile(s.T(), voteTx) + defer func() { + _ = execMsg.Close() + }() + + testCases := []struct { + name string + args []string + respType proto.Message + expectErr bool + }{ + { + "fail invalid grantee", + cli.TestFlags(). + With( + execMsg.Name(), + ). + WithFrom("grantee"). + WithBroadcastModeSync(). + WithGenerateOnly(), + nil, + true, + }, + { + "fail invalid json path", + cli.TestFlags(). + With( + "/invalid/file.txt", + ). + WithFrom(grantee.String()). + WithBroadcastModeSync(), + nil, + true, + }, + { + "valid txn", + cli.TestFlags(). + With( + execMsg.Name(), + ). + WithFrom(grantee.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + &sdk.TxResponse{}, + false, + }, + { + "valid tx with amino", + cli.TestFlags(). + With( + execMsg.Name(), + ). + WithFrom(grantee.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithSignMode(cflags.SignModeLegacyAminoJSON), + &sdk.TxResponse{}, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetTxAuthzExecAuthorizationCmd() + + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *AuthzCLITestSuite) TestNewExecGrantAuthorized() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + grantee := s.grantee[0] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + grantee.String(), + "send", + ). + WithSpendLimit("12uakt"). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithExpiration(twoHours)...) + s.Require().NoError(err) + + tokens := sdk.NewCoins( + sdk.NewCoin("testtoken", sdk.NewInt(12)), + ) + + normalGeneratedTx, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + val[0].Address.String(), + grantee.String(), + tokens.String(), + ). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithGenerateOnly()...) + s.Require().NoError(err) + execMsg := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String()) + defer func() { + _ = execMsg.Close() + }() + + testCases := []struct { + name string + args []string + expectErr bool + expectErrMsg string + }{ + { + "valid txn", + cli.TestFlags(). + With( + execMsg.Name(), + ). + WithFrom(grantee.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, + "", + }, + { + "error over spent", + cli.TestFlags(). + With( + execMsg.Name(), + ). + WithFrom(grantee.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, + "", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetTxAuthzExecAuthorizationCmd() + cctx := s.cctx + + var response sdk.TxResponse + out, err := clitestutil.ExecTestCLICmd(context.Background(), cctx, cmd, tc.args...) + + switch { + case tc.expectErrMsg != "": + s.Require().NoError(cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + s.Require().Contains(response.RawLog, tc.expectErrMsg) + + case tc.expectErr: + s.Require().Error(err) + + default: + s.Require().NoError(err) + s.Require().NoError(cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + } + }) + } +} + +func (s *AuthzCLITestSuite) TestExecSendAuthzWithAllowList() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + grantee := s.grantee[3] + + allowedAddr := s.grantee[4] + notAllowedAddr := s.grantee[5] + twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() + + _, err := clitestutil.ExecCreateGrant( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + grantee.String(), + "send", + ). + WithSpendLimit("100uakt"). + WithFrom(val[0].Address.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithExpiration(twoHours). + WithAllowList(allowedAddr.String())...) + s.Require().NoError(err) + + tokens := sdk.NewCoins( + sdk.NewCoin("stake", sdk.NewInt(12)), + ) + + validGeneratedTx, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + val[0].Address.String(), + allowedAddr.String(), + tokens.String(), + ). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithGenerateOnly()...) + + s.Require().NoError(err) + execMsg := testutil.WriteToNewTempFile(s.T(), validGeneratedTx.String()) + defer func() { + _ = execMsg.Close() + }() + + invalidGeneratedTx, err := clitestutil.ExecSend( + context.Background(), + s.cctx, + cli.TestFlags(). + With( + val[0].Address.String(), + notAllowedAddr.String(), + tokens.String(), + ). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))). + WithGenerateOnly()...) + s.Require().NoError(err) + execMsg1 := testutil.WriteToNewTempFile(s.T(), invalidGeneratedTx.String()) + defer func() { + _ = execMsg1.Close() + }() + + // test sending to allowed address + args := cli.TestFlags(). + With( + execMsg.Name(), + ). + WithFrom(grantee.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))) + + var response sdk.TxResponse + + cmd := cli.GetTxAuthzExecAuthorizationCmd() + + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, args...) + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + + // test sending to not allowed address + args = cli.TestFlags(). + With( + execMsg1.Name(), + ). + WithFrom(grantee.String()). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))) + + out, err = clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, args...) + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) +} diff --git a/go/cli/bank_query.go b/go/cli/bank_query.go new file mode 100644 index 00000000..31e14e4d --- /dev/null +++ b/go/cli/bank_query.go @@ -0,0 +1,319 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/bank/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetQueryBankCmd returns the parent command for all x/bank CLi query commands. The +// provided cctx should have, at a minimum, a verifier, Tendermint RPC client, +// and marshaler set. +func GetQueryBankCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the bank module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetQueryBankBalancesCmd(), + GetQueryBankSpendableBalancesCmd(), + GetQueryBankTotalSupplyCmd(), + GetQueryBankDenomsMetadataCmd(), + GetQueryBankSendEnabledCmd(), + ) + + return cmd +} + +func GetQueryBankBalancesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "balances [address]", + Short: "Query for account balances by address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the total balance of an account or of a specific denomination. + +Example: + $ %s query %s balances [address] + $ %s query %s balances [address] --denom=[denom] +`, + version.AppName, types.ModuleName, version.AppName, types.ModuleName, + ), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + denom, err := cmd.Flags().GetString(cflags.FlagDenom) + if err != nil { + return err + } + + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + if denom == "" { + params := types.NewQueryAllBalancesRequest(addr, pageReq) + + res, err := cl.Query().Bank().AllBalances(ctx, params) + if err != nil { + return err + } + + return cl.PrintMessage(&res) + } + + params := types.NewQueryBalanceRequest(addr, denom) + + res, err := cl.Query().Bank().Balance(ctx, params) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Balance) + }, + } + + cmd.Flags().String(cflags.FlagDenom, "", "The specific balance denomination to query for") + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "all balances") + + return cmd +} + +func GetQueryBankSpendableBalancesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "spendable-balances [address]", + Short: "Query for account spendable balances by address", + Example: fmt.Sprintf("$ %s query %s spendable-balances [address]", version.AppName, types.ModuleName), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + denom, err := cmd.Flags().GetString(cflags.FlagDenom) + if err != nil { + return err + } + + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + if denom == "" { + params := types.NewQuerySpendableBalancesRequest(addr, pageReq) + + res, err := cl.Query().Bank().SpendableBalances(ctx, params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + } + + params := types.NewQuerySpendableBalanceByDenomRequest(addr, denom) + + res, err := cl.Query().Bank().SpendableBalanceByDenom(ctx, params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cmd.Flags().String(cflags.FlagDenom, "", "The specific balance denomination to query for") + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "spendable balances") + + return cmd +} + +// GetQueryBankDenomsMetadataCmd defines the cobra command to query client denomination metadata. +func GetQueryBankDenomsMetadataCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "denom-metadata", + Short: "Query the client metadata for coin denominations", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the client metadata for all the registered coin denominations + +Example: + To query for the client metadata of all coin denominations use: + $ %s query %s denom-metadata + +To query for the client metadata of a specific coin denomination use: + $ %s query %s denom-metadata --denom=[denom] +`, + version.AppName, types.ModuleName, version.AppName, types.ModuleName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + denom, err := cmd.Flags().GetString(cflags.FlagDenom) + if err != nil { + return err + } + + if denom == "" { + res, err := cl.Query().Bank().DenomsMetadata(cmd.Context(), &types.QueryDenomsMetadataRequest{}) + if err != nil { + return err + } + + return cl.PrintMessage(res) + } + + res, err := cl.Query().Bank().DenomMetadata(cmd.Context(), &types.QueryDenomMetadataRequest{Denom: denom}) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cmd.Flags().String(cflags.FlagDenom, "", "The specific denomination to query client metadata for") + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func GetQueryBankTotalSupplyCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "total", + Short: "Query the total supply of coins of the chain", + Args: cobra.NoArgs, + Long: strings.TrimSpace( + fmt.Sprintf(`Query total supply of coins that are held by accounts in the chain. + +Example: + $ %s query %s total + +To query for the total supply of a specific coin denomination use: + $ %s query %s total --denom=[denom] +`, + version.AppName, types.ModuleName, version.AppName, types.ModuleName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + denom, err := cmd.Flags().GetString(cflags.FlagDenom) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + if denom == "" { + res, err := cl.Query().Bank().TotalSupply(ctx, &types.QueryTotalSupplyRequest{Pagination: pageReq}) + if err != nil { + return err + } + + return cl.PrintMessage(res) + } + + res, err := cl.Query().Bank().SupplyOf(ctx, &types.QuerySupplyOfRequest{Denom: denom}) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Amount) + }, + } + + cmd.Flags().String(cflags.FlagDenom, "", "The specific balance denomination to query for") + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "all supply totals") + + return cmd +} + +func GetQueryBankSendEnabledCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "send-enabled [denom1 ...]", + Short: "Query for send enabled entries", + Long: strings.TrimSpace(`Query for send enabled entries that have been specifically set. + +To look up one or more specific denoms, supply them as arguments to this command. +To look up all denoms, do not provide any arguments. +`, + ), + Example: strings.TrimSpace( + fmt.Sprintf(`Getting one specific entry: + $ %[1]s query %[2]s send-enabled foocoin + +Getting two specific entries: + $ %[1]s query %[2]s send-enabled foocoin barcoin + +Getting all entries: + $ %[1]s query %[2]s send-enabled +`, + version.AppName, types.ModuleName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + reqPag, err := client.ReadPageRequest(client.MustFlagSetWithPageKeyDecoded(cmd.Flags())) + if err != nil { + return err + } + + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + req := &types.QuerySendEnabledRequest{ + Denoms: args, + Pagination: reqPag, + } + + res, err := cl.Query().Bank().SendEnabled(cmd.Context(), req) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "send enabled entries") + + return cmd +} diff --git a/go/cli/bank_query_test.go b/go/cli/bank_query_test.go new file mode 100644 index 00000000..64947b42 --- /dev/null +++ b/go/cli/bank_query_test.go @@ -0,0 +1,384 @@ +package cli_test + +import ( + "context" + "fmt" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/gogoproto/proto" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" + + "pkg.akt.dev/go/cli" + cflags "pkg.akt.dev/go/cli/flags" + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +func (s *BankCLITestSuite) TestGetBalancesCmd() { + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + testCases := []struct { + name string + ctxGen func() client.Context + args []string + expectResult proto.Message + expectErr bool + }{ + { + "valid query", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QueryAllBalancesResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + cli.TestFlags(). + With(accounts[0].Address.String()). + WithOutputJSON(), + &types.QueryAllBalancesResponse{}, + false, + }, + { + "valid query with denom", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QueryBalanceResponse{ + Balance: &sdk.Coin{}, + }) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + cli.TestFlags(). + With(accounts[0].Address.String()). + WithDenom("photon"). + WithOutputJSON(), + &sdk.Coin{}, + false, + }, + { + "invalid Address", + func() client.Context { + return s.baseCtx + }, + cli.TestFlags(). + With("foo"), + nil, + true, + }, + { + "invalid denom", + func() client.Context { + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Code: 1, + }) + return s.baseCtx.WithClient(c) + }, + cli.TestFlags(). + With(accounts[0].Address.String()). + WithDenom("foo"). + WithOutputJSON(), + nil, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryBankBalancesCmd() + out, err := clitestutil.ExecTestCLICmd(context.Background(), tc.ctxGen(), cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult)) + } + }) + } +} + +func (s *BankCLITestSuite) TestGetSpendableBalancesCmd() { + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + testCases := []struct { + name string + ctxGen func() client.Context + args []string + expectResult proto.Message + expectErr bool + }{ + { + "valid query", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QuerySpendableBalancesResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + accounts[0].Address.String(), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + &types.QuerySpendableBalancesResponse{}, + false, + }, + { + "valid query with denom flag", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QuerySpendableBalanceByDenomRequest{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + accounts[0].Address.String(), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + fmt.Sprintf("--%s=photon", cflags.FlagDenom), + }, + &types.QuerySpendableBalanceByDenomResponse{}, + false, + }, + { + "invalid Address", + func() client.Context { + return s.baseCtx + }, + []string{ + "foo", + }, + nil, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryBankSpendableBalancesCmd() + out, err := clitestutil.ExecTestCLICmd(context.Background(), tc.ctxGen(), cmd, tc.args...) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult)) + } + }) + } +} + +func (s *BankCLITestSuite) TestGetCmdDenomsMetadata() { + testCases := []struct { + name string + ctxGen func() client.Context + args []string + expectResult proto.Message + expectErr bool + }{ + { + "valid query", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QueryDenomsMetadataResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + &types.QueryDenomsMetadataResponse{}, + false, + }, + { + "valid query with denom", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QueryDenomMetadataResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + fmt.Sprintf("--%s=photon", cflags.FlagDenom), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + &types.QueryDenomMetadataResponse{}, + false, + }, + { + "invalid query with denom", + func() client.Context { + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Code: 1, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + fmt.Sprintf("--%s=foo", cflags.FlagDenom), + }, + nil, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryBankDenomsMetadataCmd() + out, err := clitestutil.ExecTestCLICmd(context.Background(), tc.ctxGen(), cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult)) + s.Require().NoError(err) + } + }) + } +} + +func (s *BankCLITestSuite) TestGetCmdQueryTotalSupply() { + testCases := []struct { + name string + ctxGen func() client.Context + args []string + expectResult proto.Message + expectErr bool + }{ + { + "valid query", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QueryTotalSupplyResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + &types.QueryTotalSupplyResponse{}, + false, + }, + { + "valid query with denom", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QuerySupplyOfResponse{ + Amount: sdk.Coin{}, + }) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + fmt.Sprintf("--%s=photon", cflags.FlagDenom), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + &sdk.Coin{}, + false, + }, + { + "invalid query with denom", + func() client.Context { + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Code: 1, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + fmt.Sprintf("--%s=foo", cflags.FlagDenom), + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + nil, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryBankTotalSupplyCmd() + out, err := clitestutil.ExecTestCLICmd(context.Background(), tc.ctxGen(), cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult)) + s.Require().NoError(err) + } + }) + } +} + +func (s *BankCLITestSuite) TestGetCmdQuerySendEnabled() { + testCases := []struct { + name string + ctxGen func() client.Context + args []string + expectResult proto.Message + expectErr bool + }{ + { + "valid query", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QuerySendEnabledResponse{ + SendEnabled: []*types.SendEnabled{}, + }) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + &types.QuerySendEnabledResponse{}, + false, + }, + { + "valid query with denoms", + func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&types.QuerySendEnabledResponse{ + SendEnabled: []*types.SendEnabled{}, + }) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + }, + []string{ + "photon", + "stake", + fmt.Sprintf("--%s=json", cflags.FlagOutput), + }, + &types.QuerySendEnabledResponse{}, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryBankSendEnabledCmd() + out, err := clitestutil.ExecTestCLICmd(context.Background(), tc.ctxGen(), cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult)) + s.Require().NoError(err) + } + }) + } +} diff --git a/go/cli/bank_suite_test.go b/go/cli/bank_suite_test.go new file mode 100644 index 00000000..c6b7ca0d --- /dev/null +++ b/go/cli/bank_suite_test.go @@ -0,0 +1,46 @@ +package cli_test + +import ( + "bytes" + "io" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/bank" +) + +type BankCLITestSuite struct { + CLITestSuite +} + +func (s *BankCLITestSuite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(bank.AppModuleBasic{}) + s.kr = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithLegacyAmino(s.encCfg.Amino). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithSignModeStr("direct") + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + } + + s.cctx = ctxGen(). + WithOutput(&outBuf). + WithSignModeStr("direct") +} diff --git a/go/cli/bank_tx.go b/go/cli/bank_tx.go index a1ded923..35fd23ef 100644 --- a/go/cli/bank_tx.go +++ b/go/cli/bank_tx.go @@ -9,13 +9,13 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/spf13/cobra" -) -var FlagSplit = "split" + cflags "pkg.akt.dev/go/cli/flags" +) -// NewBankTxCmd returns a root CLI command handler for all x/bank transaction commands. -func NewBankTxCmd() *cobra.Command { - txCmd := &cobra.Command{ +// GetTxBankCmd returns a root CLI command handler for all x/bank transaction commands. +func GetTxBankCmd() *cobra.Command { + cmd := &cobra.Command{ Use: types.ModuleName, Short: "Bank transaction subcommands", DisableFlagParsing: true, @@ -23,32 +23,34 @@ func NewBankTxCmd() *cobra.Command { RunE: client.ValidateCmd, } - txCmd.AddCommand( - NewBankSendTxCmd(), - NewBankMultiSendTxCmd(), + cmd.AddCommand( + GetTxBankSendTxCmd(), + GetTxBankMultiSendTxCmd(), ) - return txCmd + return cmd } -// NewBankSendTxCmd returns a CLI command handler for creating a MsgSend transaction. -func NewBankSendTxCmd() *cobra.Command { +// GetTxBankSendTxCmd returns a CLI command handler for creating a MsgSend transaction. +func GetTxBankSendTxCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "send [from_key_or_address] [to_address] [amount]", + Use: "send [to_address] [amount]", Short: "Send funds from one account to another.", Long: `Send funds from one account to another. -Note, the '--from' flag is ignored as it is implied from [from_key_or_address]. +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 { - ctx := cmd.Context() - - err := cmd.Flags().Set(flags.FlagFrom, args[0]) - if err != nil { + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if err := cmd.Flags().Set(flags.FlagFrom, args[0]); err != nil { return err } + return TxPersistentPreRunE(cmd, args) + }, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) toAddr, err := sdk.AccAddressFromBech32(args[1]) @@ -72,14 +74,14 @@ When using '--dry-run' a key name cannot be used, only a bech32 address. }, } - flags.AddTxFlagsToCmd(cmd) + cflags.AddTxFlagsToCmd(cmd) return cmd } -// NewBankMultiSendTxCmd returns a CLI command handler for creating a MsgMultiSend transaction. +// GetTxBankMultiSendTxCmd returns a CLI command handler for creating a MsgMultiSend transaction. // For a better UX this command is limited to send funds from one account to two or more accounts. -func NewBankMultiSendTxCmd() *cobra.Command { +func GetTxBankMultiSendTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "multi-send [from_key_or_address] [to_address_1 to_address_2 ...] [amount]", Short: "Send funds from one account to two or more accounts.", @@ -91,14 +93,15 @@ separate addresses with space. When using '--dry-run' a key name cannot be used, only a bech32 address.`, Example: fmt.Sprintf("%s tx bank multi-send cosmos1... cosmos1... cosmos1... cosmos1... 10stake", version.AppName), Args: cobra.MinimumNArgs(4), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - - err := cmd.Flags().Set(flags.FlagFrom, args[0]) - if err != nil { + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if err := cmd.Flags().Set(flags.FlagFrom, args[0]); err != nil { return err } + return TxPersistentPreRunE(cmd, args) + }, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() cl := MustClientFromContext(ctx) coins, err := sdk.ParseCoinsNormalized(args[len(args)-1]) @@ -110,7 +113,7 @@ When using '--dry-run' a key name cannot be used, only a bech32 address.`, return fmt.Errorf("must send positive amount") } - split, err := cmd.Flags().GetBool(FlagSplit) + split, err := cmd.Flags().GetBool(cflags.FlagSplit) if err != nil { return err } @@ -153,8 +156,8 @@ When using '--dry-run' a key name cannot be used, only a bech32 address.`, }, } - cmd.Flags().Bool(FlagSplit, false, "Send the equally split token amount to each address") - flags.AddTxFlagsToCmd(cmd) + cmd.Flags().Bool(cflags.FlagSplit, false, "Send the equally split token amount to each address") + cflags.AddTxFlagsToCmd(cmd) return cmd } diff --git a/go/cli/bank_tx_test.go b/go/cli/bank_tx_test.go new file mode 100644 index 00000000..73a2068a --- /dev/null +++ b/go/cli/bank_tx_test.go @@ -0,0 +1,189 @@ +package cli_test + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + + "pkg.akt.dev/go/cli" + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +func (s *BankCLITestSuite) TestSendTxCmd() { + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + commonArgs := cli.TestFlags(). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("photon", sdk.NewInt(10)))). + WithChainID("test-chain") + + testCases := []struct { + name string + ctxGen func() client.Context + args []string + expectErr bool + }{ + { + "valid transaction", + func() client.Context { + return s.baseCtx + }, + cli.TestFlags(). + With( + accounts[0].Address.String(), + accounts[0].Address.String(), + sdk.NewCoins( + sdk.NewCoin("uakt", sdk.NewInt(10)), + sdk.NewCoin("photon", sdk.NewInt(40)), + ).String()). + Append(commonArgs), + false, + }, + { + "invalid to Address", + func() client.Context { + return s.baseCtx + }, + cli.TestFlags(). + With( + accounts[0].Address.String(), + sdk.AccAddress{}.String(), + sdk.NewCoins( + sdk.NewCoin("uakt", sdk.NewInt(10)), + sdk.NewCoin("photon", sdk.NewInt(40)), + ).String()). + Append(commonArgs), + true, + }, + { + "invalid coins", + func() client.Context { + return s.baseCtx + }, + cli.TestFlags(). + With( + accounts[0].Address.String(), + accounts[0].Address.String(), + ). + Append(commonArgs), + true, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cctx := tc.ctxGen() + + cmd := cli.GetTxBankSendTxCmd() + out, err := clitestutil.ExecTestCLICmd(context.Background(), cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + var response sdk.TxResponse + s.Require().NoError(err) + s.Require().NoError(cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + } + }) + } +} + +func (s *BankCLITestSuite) TestMultiSendTxCmd() { + accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 3) + + commonArgs := cli.TestFlags(). + WithBroadcastModeSync(). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("photon", sdk.NewInt(10)))). + WithChainID("test-chain") + + testCases := []struct { + name string + ctxGen func() client.Context + args []string + expectErr bool + }{ + { + "valid transaction", + func() client.Context { + return s.baseCtx + }, + cli.TestFlags(). + With( + accounts[0].Address.String(), + accounts[1].Address.String(), + accounts[2].Address.String(), + sdk.NewCoins( + sdk.NewCoin("uakt", sdk.NewInt(10)), + sdk.NewCoin("photon", sdk.NewInt(40))).String()). + Append(commonArgs), + false, + }, + { + "invalid from Address", + func() client.Context { + return s.baseCtx + }, + cli.TestFlags(). + With( + "foo", + accounts[1].Address.String(), + accounts[2].Address.String(), + sdk.NewCoins( + sdk.NewCoin("uakt", sdk.NewInt(10)), + sdk.NewCoin("photon", sdk.NewInt(40))).String()). + Append(commonArgs), + + true, + }, + { + "invalid recipients", + func() client.Context { + return s.baseCtx + }, + cli.TestFlags(). + With( + accounts[0].Address.String(), + accounts[1].Address.String(), + "bar", + sdk.NewCoins( + sdk.NewCoin("uakt", sdk.NewInt(10)), + sdk.NewCoin("photon", sdk.NewInt(40))).String()). + Append(commonArgs), + true, + }, + { + "invalid amount", + func() client.Context { + return s.baseCtx + }, + cli.TestFlags(). + With( + accounts[0].Address.String(), + accounts[1].Address.String(), + accounts[2].Address.String()). + Append(commonArgs), + true, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cctx := tc.ctxGen() + + cmd := cli.GetTxBankMultiSendTxCmd() + out, err := clitestutil.ExecTestCLICmd(context.Background(), cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + var response sdk.TxResponse + s.Require().NoError(err) + s.Require().NoError(cctx.Codec.UnmarshalJSON(out.Bytes(), &response), out.String()) + } + }) + } +} diff --git a/go/cli/broadcast.go b/go/cli/broadcast.go index b5568cda..0fc074ac 100644 --- a/go/cli/broadcast.go +++ b/go/cli/broadcast.go @@ -4,10 +4,10 @@ import ( "errors" "strings" - "github.com/cosmos/cosmos-sdk/client/flags" - authclient "github.com/cosmos/cosmos-sdk/x/auth/client" "github.com/spf13/cobra" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + cflags "pkg.akt.dev/go/cli/flags" ) @@ -25,31 +25,35 @@ $ tx broadcast ./mytxn.json `), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - if offline, _ := cmd.Flags().GetBool(cflags.FlagOffline); offline { - return errors.New("cannot broadcast tx during offline mode") + cctx, err := GetClientTxContext(cmd) + if err != nil { + return err } - ctx := cmd.Context() - - cl := MustClientFromContext(ctx) - - cctx := cl.ClientContext() + if cctx.Offline { + return errors.New("cannot broadcast tx during offline mode") + } stdTx, err := authclient.ReadTxFromFile(cctx, args[0]) if err != nil { return err } - res, err := cl.Tx().BroadcastTx(ctx, stdTx) + txb, err := cctx.TxConfig.TxEncoder()(stdTx) + if err != nil { + return err + } + + res, err := cctx.BroadcastTx(txb) if err != nil { return err } - return cl.PrintMessage(res) + return cctx.PrintProto(res) }, } - flags.AddTxFlagsToCmd(cmd) + cflags.AddTxFlagsToCmd(cmd) return cmd } diff --git a/go/cli/cctx.go b/go/cli/cctx.go new file mode 100644 index 00000000..1f0d755a --- /dev/null +++ b/go/cli/cctx.go @@ -0,0 +1,311 @@ +package cli + +import ( + "crypto/tls" + "errors" + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "google.golang.org/grpc" + + sdkclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + + cflags "pkg.akt.dev/go/cli/flags" +) + +const ClientContextKey = sdk.ContextKey("client.context") +const ServerContextKey = sdk.ContextKey("server.context") + +// SetCmdClientContextHandler is to be used in a command pre-hook execution to +// read flags that populate a Context and sets that to the command's Context. +func SetCmdClientContextHandler(cctx sdkclient.Context, cmd *cobra.Command) (err error) { + cctx, err = ReadPersistentCommandFlags(cctx, cmd.Flags()) + if err != nil { + return err + } + + return SetCmdClientContext(cmd, cctx) +} + +// GetClientContextFromCmd returns a Context from a command or an empty Context +// if it has not been set. +func GetClientContextFromCmd(cmd *cobra.Command) sdkclient.Context { + if v := cmd.Context().Value(ClientContextKey); v != nil { + clientCtxPtr := v.(*sdkclient.Context) + return *clientCtxPtr + } + + return sdkclient.Context{} +} + +// SetCmdClientContext sets a command's Context value to the provided argument. +func SetCmdClientContext(cmd *cobra.Command, cctx sdkclient.Context) error { + v := cmd.Context().Value(ClientContextKey) + if v == nil { + return errors.New("client context not set") + } + + clientCtxPtr := v.(*sdkclient.Context) + *clientCtxPtr = cctx + + return nil +} + +// GetClientQueryContext returns a Context from a command with fields set based on flags +// defined in AddQueryFlagsToCmd. An error is returned if any flag query fails. +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func GetClientQueryContext(cmd *cobra.Command) (sdkclient.Context, error) { + ctx := GetClientContextFromCmd(cmd) + return ReadQueryCommandFlags(ctx, cmd.Flags()) +} + +// GetClientTxContext returns a Context from a command with fields set based on flags +// defined in AddTxFlagsToCmd. An error is returned if any flag query fails. +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func GetClientTxContext(cmd *cobra.Command) (sdkclient.Context, error) { + ctx := GetClientContextFromCmd(cmd) + return ReadTxCommandFlags(ctx, cmd.Flags()) +} + +// ReadQueryCommandFlags returns an updated Context with fields set based on flags +// defined in AddQueryFlagsToCmd. An error is returned if any flag query fails. +// +// Note, the provided clientCtx may have field pre-populated. The following order +// of precedence occurs: +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func ReadQueryCommandFlags(cctx sdkclient.Context, flagSet *pflag.FlagSet) (sdkclient.Context, error) { + if cctx.Height == 0 || flagSet.Changed(cflags.FlagHeight) { + height, _ := flagSet.GetInt64(cflags.FlagHeight) + cctx = cctx.WithHeight(height) + } + + if !cctx.UseLedger || flagSet.Changed(cflags.FlagUseLedger) { + useLedger, _ := flagSet.GetBool(cflags.FlagUseLedger) + cctx = cctx.WithUseLedger(useLedger) + } + + return ReadPersistentCommandFlags(cctx, flagSet) +} + +// ReadPersistentCommandFlags returns a Context with fields set for "persistent" +// or common flags that do not necessarily change with context. +// +// Note, the provided clientCtx may have field pre-populated. The following order +// of precedence occurs: +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func ReadPersistentCommandFlags(cctx sdkclient.Context, flagSet *pflag.FlagSet) (sdkclient.Context, error) { + if cctx.OutputFormat == "" || flagSet.Changed(cflags.FlagOutput) { + output, _ := flagSet.GetString(cflags.FlagOutput) + cctx = cctx.WithOutputFormat(output) + } + + if cctx.HomeDir == "" || flagSet.Changed(cflags.FlagHome) { + homeDir, _ := flagSet.GetString(cflags.FlagHome) + cctx = cctx.WithHomeDir(homeDir) + } + + if !cctx.Simulate || flagSet.Changed(cflags.FlagDryRun) { + dryRun, _ := flagSet.GetBool(cflags.FlagDryRun) + cctx = cctx.WithSimulation(dryRun) + } + + if cctx.KeyringDir == "" || flagSet.Changed(cflags.FlagKeyringDir) { + keyringDir, _ := flagSet.GetString(cflags.FlagKeyringDir) + + // The keyring directory is optional and falls back to the home directory + // if omitted. + if keyringDir == "" { + keyringDir = cctx.HomeDir + } + + cctx = cctx.WithKeyringDir(keyringDir) + } + + if cctx.ChainID == "" || flagSet.Changed(cflags.FlagChainID) { + chainID, _ := flagSet.GetString(cflags.FlagChainID) + cctx = cctx.WithChainID(chainID) + } + + if cctx.Keyring == nil || flagSet.Changed(cflags.FlagKeyringBackend) { + keyringBackend, _ := flagSet.GetString(cflags.FlagKeyringBackend) + + if keyringBackend != "" { + kr, err := sdkclient.NewKeyringFromBackend(cctx, keyringBackend) + if err != nil { + return cctx, err + } + + cctx = cctx.WithKeyring(kr) + } + } + + if cctx.Client == nil || flagSet.Changed(cflags.FlagNode) { + rpcURI, _ := flagSet.GetString(cflags.FlagNode) + if rpcURI != "" { + cctx = cctx.WithNodeURI(rpcURI) + + client, err := sdkclient.NewClientFromNode(rpcURI) + if err != nil { + return cctx, err + } + + cctx = cctx.WithClient(client) + } + } + + if cctx.GRPCClient == nil || flagSet.Changed(cflags.FlagGRPC) { + grpcURI, _ := flagSet.GetString(cflags.FlagGRPC) + if grpcURI != "" { + var dialOpts []grpc.DialOption + + useInsecure, _ := flagSet.GetBool(cflags.FlagGRPCInsecure) + if useInsecure { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } else { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + MinVersion: tls.VersionTLS12, + }))) + } + + grpcClient, err := grpc.Dial(grpcURI, dialOpts...) + if err != nil { + return sdkclient.Context{}, err + } + cctx = cctx.WithGRPCClient(grpcClient) + } + } + + return cctx, nil +} + +// ReadTxCommandFlags returns an updated Context with fields set based on flags +// defined in AddTxFlagsToCmd. An error is returned if any flag query fails. +// +// Note, the provided clientCtx may have field pre-populated. The following order +// of precedence occurs: +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func ReadTxCommandFlags(cctx sdkclient.Context, flagSet *pflag.FlagSet) (sdkclient.Context, error) { + cctx, err := ReadPersistentCommandFlags(cctx, flagSet) + if err != nil { + return cctx, err + } + + if !cctx.GenerateOnly || flagSet.Changed(cflags.FlagGenerateOnly) { + genOnly, _ := flagSet.GetBool(cflags.FlagGenerateOnly) + cctx = cctx.WithGenerateOnly(genOnly) + } + + if !cctx.Offline || flagSet.Changed(cflags.FlagOffline) { + offline, _ := flagSet.GetBool(cflags.FlagOffline) + cctx = cctx.WithOffline(offline) + } + + if !cctx.UseLedger || flagSet.Changed(cflags.FlagUseLedger) { + useLedger, _ := flagSet.GetBool(cflags.FlagUseLedger) + cctx = cctx.WithUseLedger(useLedger) + } + + if cctx.BroadcastMode == "" || flagSet.Changed(cflags.FlagBroadcastMode) { + bMode, _ := flagSet.GetString(cflags.FlagBroadcastMode) + cctx = cctx.WithBroadcastMode(bMode) + } + + if !cctx.SkipConfirm || flagSet.Changed(cflags.FlagSkipConfirmation) { + skipConfirm, _ := flagSet.GetBool(cflags.FlagSkipConfirmation) + cctx = cctx.WithSkipConfirmation(skipConfirm) + } + + if cctx.SignModeStr == "" || flagSet.Changed(cflags.FlagSignMode) { + signModeStr, _ := flagSet.GetString(cflags.FlagSignMode) + cctx = cctx.WithSignModeStr(signModeStr) + } + + if cctx.FeePayer == nil || flagSet.Changed(cflags.FlagFeePayer) { + payer, _ := flagSet.GetString(cflags.FlagFeePayer) + + if payer != "" { + payerAcc, err := sdk.AccAddressFromBech32(payer) + if err != nil { + return cctx, err + } + + cctx = cctx.WithFeePayerAddress(payerAcc) + } + } + + if cctx.FeeGranter == nil || flagSet.Changed(cflags.FlagFeeGranter) { + granter, _ := flagSet.GetString(cflags.FlagFeeGranter) + + if granter != "" { + granterAcc, err := sdk.AccAddressFromBech32(granter) + if err != nil { + return cctx, err + } + + cctx = cctx.WithFeeGranterAddress(granterAcc) + } + } + + if cctx.From == "" || flagSet.Changed(cflags.FlagFrom) { + from, _ := flagSet.GetString(cflags.FlagFrom) + fromAddr, fromName, keyType, err := sdkclient.GetFromFields(cctx, cctx.Keyring, from) + if err != nil { + return cctx, err + } + + cctx = cctx.WithFrom(from).WithFromAddress(fromAddr).WithFromName(fromName) + + // If the `from` signer account is a ledger key, we need to use + // SIGN_MODE_AMINO_JSON, because ledger doesn't support proto yet. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8109 + if keyType == keyring.TypeLedger && cctx.SignModeStr != cflags.SignModeLegacyAminoJSON && !cctx.LedgerHasProtobuf { + fmt.Println("Default sign-mode 'direct' not supported by Ledger, using sign-mode 'amino-json'.") + cctx = cctx.WithSignModeStr(cflags.SignModeLegacyAminoJSON) + } + } + + if !cctx.IsAux || flagSet.Changed(cflags.FlagAux) { + isAux, _ := flagSet.GetBool(cflags.FlagAux) + cctx = cctx.WithAux(isAux) + if isAux { + // If the user didn't explicitly set an --output flag, use JSON by + // default. + if cctx.OutputFormat == "" || !flagSet.Changed(cflags.FlagOutput) { + cctx = cctx.WithOutputFormat("json") + } + + // If the user didn't explicitly set a --sign-mode flag, use + // DIRECT_AUX by default. + if cctx.SignModeStr == "" || !flagSet.Changed(cflags.FlagSignMode) { + cctx = cctx.WithSignModeStr(cflags.SignModeDirectAux) + } + } + } + + return cctx, nil +} diff --git a/go/cli/cert_query.go b/go/cli/cert_query.go index da5ef4e4..85dd7270 100644 --- a/go/cli/cert_query.go +++ b/go/cli/cert_query.go @@ -14,7 +14,7 @@ import ( utiltls "pkg.akt.dev/go/util/tls" ) -func GetCertQueryCmd() *cobra.Command { +func GetQueryCertCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Certificate query commands", @@ -23,17 +23,18 @@ func GetCertQueryCmd() *cobra.Command { } cmd.AddCommand( - cmdGetCertificates(), + GetQueryCertCertificatesCmd(), ) return cmd } -func cmdGetCertificates() *cobra.Command { +func GetQueryCertCertificatesCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Short: "Query for all certificates", - SilenceUsage: true, + Use: "list", + Short: "Query for all certificates", + SilenceUsage: true, + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) diff --git a/go/cli/cert_tx.go b/go/cli/cert_tx.go index a7c7ab78..f8b5424a 100644 --- a/go/cli/cert_tx.go +++ b/go/cli/cert_tx.go @@ -12,7 +12,6 @@ import ( "github.com/spf13/viper" sdkclient "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/genutil" @@ -36,7 +35,7 @@ var ( errCannotOverwriteCertificate = fmt.Errorf("%w: cannot overwrite certificate", utiltls.ErrCertificate) ) -func GetCertTxCmd() *cobra.Command { +func GetTxCertCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Certificates transaction subcommands", @@ -50,16 +49,16 @@ func GetCertTxCmd() *cobra.Command { // 3. Revoke - revoke a key pair on the blockchain cmd.AddCommand( - cmdGenerate(), - cmdPublish(), - cmdRevoke(), + GetTxCertGenerateCmd(), + GetTxCertPublishCmd(), + GetTxCertRevokeCmd(), ) return cmd } -func doGenerateCmd(cmd *cobra.Command, domains []string) error { - allowOverwrite := viper.GetBool(flagOverwrite) +func doCertGenerateCmd(cmd *cobra.Command, domains []string) error { + allowOverwrite := viper.GetBool(cflags.FlagOverwrite) cctx, err := sdkclient.GetClientTxContext(cmd) if err != nil { @@ -95,7 +94,7 @@ func doGenerateCmd(cmd *cobra.Command, domains []string) error { return kpm.Generate(startTime, startTime.Add(validDuration), domains) } -func doPublishCmd(cmd *cobra.Command) error { +func doPublishCmd(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) cctx := cl.ClientContext() @@ -158,7 +157,7 @@ func doPublishCmd(cmd *cobra.Command) error { return cl.PrintMessage(resp) } -func doRevokeCmd(cmd *cobra.Command) error { +func doRevokeCmd(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) cctx := cl.ClientContext() @@ -223,7 +222,7 @@ func doRevokeCmd(cmd *cobra.Command) error { return cl.PrintMessage(resp) } -func cmdGenerate() *cobra.Command { +func GetTxCertGenerateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "generate", Short: "", @@ -231,14 +230,15 @@ func cmdGenerate() *cobra.Command { RunE: sdkclient.ValidateCmd, } - cmd.AddCommand(cmdGenerateClient(), - cmdGenerateServer(), + cmd.AddCommand( + GetTxCertGenerateClientCmd(), + GetTxCertGenerateServerCmd(), ) return cmd } -func addGenerateFlags(cmd *cobra.Command) error { +func addTxCertGenerateFlags(cmd *cobra.Command) error { cmd.Flags().String(flagStart, "", "certificate is not valid before this date. default current timestamp. RFC3339") if err := viper.BindPFlag(flagStart, cmd.Flags().Lookup(flagStart)); err != nil { return err @@ -248,25 +248,26 @@ func addGenerateFlags(cmd *cobra.Command) error { if err := viper.BindPFlag(flagValidTime, cmd.Flags().Lookup(flagValidTime)); err != nil { return err } - cmd.Flags().Bool(flagOverwrite, false, "overwrite existing certificate if present") - if err := viper.BindPFlag(flagOverwrite, cmd.Flags().Lookup(flagOverwrite)); err != nil { + cmd.Flags().Bool(cflags.FlagOverwrite, false, "overwrite existing certificate if present") + if err := viper.BindPFlag(cflags.FlagOverwrite, cmd.Flags().Lookup(cflags.FlagOverwrite)); err != nil { return err } - flags.AddTxFlagsToCmd(cmd) // TODO - add just the keyring flags? not all the TX ones + cflags.AddTxFlagsToCmd(cmd) // TODO - add just the keyring flags? not all the TX ones return nil } -func cmdGenerateClient() *cobra.Command { +func GetTxCertGenerateClientCmd() *cobra.Command { cmd := &cobra.Command{ Use: "client", Short: "", SuggestionsMinimumDistance: 2, - RunE: doGenerateCmd, + PersistentPreRunE: TxPersistentPreRunE, + RunE: doCertGenerateCmd, SilenceUsage: true, Args: cobra.ExactArgs(0), } - err := addGenerateFlags(cmd) + err := addTxCertGenerateFlags(cmd) if err != nil { panic(err) } @@ -274,16 +275,17 @@ func cmdGenerateClient() *cobra.Command { return cmd } -func cmdGenerateServer() *cobra.Command { +func GetTxCertGenerateServerCmd() *cobra.Command { cmd := &cobra.Command{ Use: "server", Short: "", SuggestionsMinimumDistance: 2, - RunE: doGenerateCmd, + PersistentPreRunE: TxPersistentPreRunE, + RunE: doCertGenerateCmd, SilenceUsage: true, Args: cobra.MinimumNArgs(1), } - err := addGenerateFlags(cmd) + err := addTxCertGenerateFlags(cmd) if err != nil { panic(err) } @@ -291,7 +293,7 @@ func cmdGenerateServer() *cobra.Command { return cmd } -func cmdPublish() *cobra.Command { +func GetTxCertPublishCmd() *cobra.Command { cmd := &cobra.Command{ Use: "publish", Short: "", @@ -299,24 +301,24 @@ func cmdPublish() *cobra.Command { RunE: sdkclient.ValidateCmd, } - cmd.AddCommand(cmdPublishClient(), - cmdPublishServer()) + cmd.AddCommand( + GetTxCertPublishClientCmd(), + GetTxCertPublishServerCmd()) return cmd } -func cmdPublishClient() *cobra.Command { +func GetTxCertPublishClientCmd() *cobra.Command { cmd := &cobra.Command{ Use: "client", Short: "", SuggestionsMinimumDistance: 2, - RunE: func(cmd *cobra.Command, _ []string) error { - return doPublishCmd(cmd) - }, - SilenceUsage: true, - Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, + RunE: doPublishCmd, + SilenceUsage: true, + Args: cobra.ExactArgs(0), } - err := addPublishFlags(cmd) + err := addTxCertPublishFlags(cmd) if err != nil { panic(err) } @@ -324,18 +326,17 @@ func cmdPublishClient() *cobra.Command { return cmd } -func cmdPublishServer() *cobra.Command { +func GetTxCertPublishServerCmd() *cobra.Command { cmd := &cobra.Command{ Use: "server", Short: "", SuggestionsMinimumDistance: 2, - RunE: func(cmd *cobra.Command, _ []string) error { - return doPublishCmd(cmd) - }, - SilenceUsage: true, - Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, + RunE: doPublishCmd, + SilenceUsage: true, + Args: cobra.ExactArgs(0), } - err := addPublishFlags(cmd) + err := addTxCertPublishFlags(cmd) if err != nil { panic(err) } @@ -343,13 +344,13 @@ func cmdPublishServer() *cobra.Command { return cmd } -func addPublishFlags(cmd *cobra.Command) error { +func addTxCertPublishFlags(cmd *cobra.Command) error { cmd.Flags().Bool(flagToGenesis, false, "add to genesis") if err := viper.BindPFlag(flagToGenesis, cmd.Flags().Lookup(flagToGenesis)); err != nil { return err } - flags.AddTxFlagsToCmd(cmd) + cflags.AddTxFlagsToCmd(cmd) return nil } @@ -400,31 +401,33 @@ func addCertToGenesis(cmd *cobra.Command, cert types.GenesisCertificate) error { return genutil.ExportGenesisFile(genDoc, genFile) } -func cmdRevoke() *cobra.Command { +func GetTxCertRevokeCmd() *cobra.Command { cmd := &cobra.Command{ Use: "revoke", Short: "", SuggestionsMinimumDistance: 2, RunE: sdkclient.ValidateCmd, } - cmd.AddCommand(cmdRevokeClient(), - cmdRevokeServer()) + cmd.AddCommand( + GetTxCertsRevokeClientCmd(), + GetTxCertRevokeServerCmd()) return cmd } -func cmdRevokeClient() *cobra.Command { +func GetTxCertsRevokeClientCmd() *cobra.Command { cmd := &cobra.Command{ Use: "client", Short: "", SuggestionsMinimumDistance: 2, - RunE: func(cmd *cobra.Command, _ []string) error { - return doRevokeCmd(cmd) - }, - SilenceUsage: true, - Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, + RunE: doRevokeCmd, + SilenceUsage: true, + Args: cobra.ExactArgs(0), } + err := addRevokeCmdFlags(cmd) + if err != nil { panic(err) } @@ -432,16 +435,15 @@ func cmdRevokeClient() *cobra.Command { return cmd } -func cmdRevokeServer() *cobra.Command { +func GetTxCertRevokeServerCmd() *cobra.Command { cmd := &cobra.Command{ Use: "server", Short: "", SuggestionsMinimumDistance: 2, - RunE: func(cmd *cobra.Command, _ []string) error { - return doRevokeCmd(cmd) - }, - SilenceUsage: true, - Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, + RunE: doRevokeCmd, + SilenceUsage: true, + Args: cobra.ExactArgs(0), } err := addRevokeCmdFlags(cmd) if err != nil { diff --git a/go/cli/client.go b/go/cli/client.go index 85f7ce1e..3e51c291 100644 --- a/go/cli/client.go +++ b/go/cli/client.go @@ -63,7 +63,7 @@ func DiscoverClient(ctx context.Context, cctx sdkclient.Context, opts ...cltypes func RPCAkash(_ *cmtrpctypes.Context) (*aclient.Akash, error) { result := &aclient.Akash{ ClientInfo: &aclient.ClientInfo{ - ApiVersion: "v1beta2", + ApiVersion: "v1beta3", }, } diff --git a/go/cli/crisis_tx.go b/go/cli/crisis_tx.go new file mode 100644 index 00000000..ffbf618d --- /dev/null +++ b/go/cli/crisis_tx.go @@ -0,0 +1,69 @@ +package cli + +import ( + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/crisis/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetTxCrisisCmd returns a root CLI command handler for all x/crisis transaction commands. +func GetTxCrisisCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Crisis transactions subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + GetTxCrisisVerifyInvariantTxCmd(), + ) + + return txCmd +} + +// GetTxCrisisVerifyInvariantTxCmd returns a CLI command handler for creating a +// MsgVerifyInvariant transaction. +func GetTxCrisisVerifyInvariantTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "invariant-broken [module-name] [invariant-route]", + Short: "Submit proof that an invariant broken to halt the chain", + Args: cobra.ExactArgs(2), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + moduleName, route := args[0], args[1] + if moduleName == "" { + return errors.New("invalid module name") + } + if route == "" { + return errors.New("invalid invariant route") + } + + senderAddr := cctx.GetFromAddress() + + msg := types.NewMsgVerifyInvariant(senderAddr, moduleName, route) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/deployment_query.go b/go/cli/deployment_query.go index 17f79564..f655b37b 100644 --- a/go/cli/deployment_query.go +++ b/go/cli/deployment_query.go @@ -1,19 +1,18 @@ package cli import ( - "context" - "github.com/spf13/cobra" sdkclient "github.com/cosmos/cosmos-sdk/client" - cflags "pkg.akt.dev/go/cli/flags" "pkg.akt.dev/go/node/deployment/v1" "pkg.akt.dev/go/node/deployment/v1beta4" + + cflags "pkg.akt.dev/go/cli/flags" ) -// GetDeploymentQueryCmd returns the query commands for the deployment module -func GetDeploymentQueryCmd() *cobra.Command { +// GetQueryDeploymentCmds returns the query commands for the deployment module +func GetQueryDeploymentCmds() *cobra.Command { cmd := &cobra.Command{ Use: v1.ModuleName, Short: "Deployment query commands", @@ -22,19 +21,20 @@ func GetDeploymentQueryCmd() *cobra.Command { } cmd.AddCommand( - cmdDeployments(), - cmdDeployment(), - getGroupCmd(), + GetQueryDeploymentsCmd(), + GetQueryDeploymentCmd(), + GetQueryDeploymentGroupCmds(), ) return cmd } -func cmdDeployments() *cobra.Command { +func GetQueryDeploymentsCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Short: "Query for all deployments", - Args: cobra.ExactArgs(0), + Use: "list", + Short: "Query for all deployments", + Args: cobra.ExactArgs(0), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -54,7 +54,7 @@ func cmdDeployments() *cobra.Command { Pagination: pageReq, } - res, err := cl.Query().Deployment().Deployments(context.Background(), params) + res, err := cl.Query().Deployment().Deployments(ctx, params) if err != nil { return err } @@ -70,11 +70,12 @@ func cmdDeployments() *cobra.Command { return cmd } -func cmdDeployment() *cobra.Command { +func GetQueryDeploymentCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "get", - Short: "Query deployment", - Args: cobra.ExactArgs(0), + Use: "get", + Short: "Query deployment", + Args: cobra.ExactArgs(0), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -84,7 +85,7 @@ func cmdDeployment() *cobra.Command { return err } - res, err := cl.Query().Deployment().Deployment(context.Background(), &v1beta4.QueryDeploymentRequest{ID: id}) + res, err := cl.Query().Deployment().Deployment(ctx, &v1beta4.QueryDeploymentRequest{ID: id}) if err != nil { return err } @@ -100,7 +101,7 @@ func cmdDeployment() *cobra.Command { return cmd } -func getGroupCmd() *cobra.Command { +func GetQueryDeploymentGroupCmds() *cobra.Command { cmd := &cobra.Command{ Use: "group", Short: "Deployment group query commands", @@ -109,17 +110,18 @@ func getGroupCmd() *cobra.Command { } cmd.AddCommand( - cmdGetGroup(), + GetQueryDeploymentGroupCmd(), ) return cmd } -func cmdGetGroup() *cobra.Command { +func GetQueryDeploymentGroupCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "get", - Short: "Query group of deployment", - Args: cobra.ExactArgs(0), + Use: "get", + Short: "Query group of deployment", + Args: cobra.ExactArgs(0), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -129,7 +131,7 @@ func cmdGetGroup() *cobra.Command { return err } - res, err := cl.Query().Deployment().Group(cmd.Context(), &v1beta4.QueryGroupRequest{ID: id}) + res, err := cl.Query().Deployment().Group(ctx, &v1beta4.QueryGroupRequest{ID: id}) if err != nil { return err } diff --git a/go/cli/deployment_tx.go b/go/cli/deployment_tx.go index dfc8ab7c..4d0611d0 100644 --- a/go/cli/deployment_tx.go +++ b/go/cli/deployment_tx.go @@ -11,7 +11,6 @@ import ( "github.com/spf13/cobra" sdkclient "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz" @@ -29,8 +28,8 @@ var ( errDeploymentUpdateGroupsChanged = fmt.Errorf("%w: groups are different than existing deployment, you cannot update groups", errDeploymentUpdate) ) -// GetDeploymentTxCmd returns the transaction commands for this module -func GetDeploymentTxCmd() *cobra.Command { +// GetTxDeploymentCmds returns the transaction commands for this module +func GetTxDeploymentCmds() *cobra.Command { cmd := &cobra.Command{ Use: dv1.ModuleName, Short: "Deployment transaction subcommands", @@ -38,21 +37,22 @@ func GetDeploymentTxCmd() *cobra.Command { RunE: sdkclient.ValidateCmd, } cmd.AddCommand( - cmdDeploymentCreate(), - cmdDeploymentUpdate(), - cmdDeploymentDeposit(), - cmdDeploymentClose(), - cmdDeploymentGroup(), - cmdDeploymentAuthz(), + GetTxDeploymentCreateCmd(), + GetTxDeploymentUpdateCmd(), + GetTxDeploymentDepositCmd(), + GetTxDeploymentCloseCmd(), + GetTxDeploymentGroupCmds(), + GetTxDeploymentAuthzCmd(), ) return cmd } -func cmdDeploymentCreate() *cobra.Command { +func GetTxDeploymentCreateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "create [sdl-file]", - Short: "Create deployment", - Args: cobra.ExactArgs(1), + Use: "create [sdl-file]", + Short: "Create deployment", + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -147,11 +147,12 @@ func cmdDeploymentCreate() *cobra.Command { return cmd } -func cmdDeploymentDeposit() *cobra.Command { +func GetTxDeploymentDepositCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "deposit ", - Short: "Deposit funds to deployment", - Args: cobra.ExactArgs(1), + Use: "deposit ", + Short: "Deposit funds to deployment", + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -194,11 +195,12 @@ func cmdDeploymentDeposit() *cobra.Command { return cmd } -func cmdDeploymentClose() *cobra.Command { +func GetTxDeploymentCloseCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "close", - Short: "Close deployment", - Args: cobra.ExactArgs(0), + Use: "close", + Short: "Close deployment", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -225,11 +227,12 @@ func cmdDeploymentClose() *cobra.Command { return cmd } -func cmdDeploymentUpdate() *cobra.Command { +func GetTxDeploymentUpdateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "update [sdl-file]", - Short: "update deployment", - Args: cobra.ExactArgs(1), + Use: "update [sdl-file]", + Short: "update deployment", + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -297,27 +300,28 @@ func cmdDeploymentUpdate() *cobra.Command { return cmd } -func cmdDeploymentGroup() *cobra.Command { +func GetTxDeploymentGroupCmds() *cobra.Command { cmd := &cobra.Command{ Use: "group", Short: "Modify a Deployment's specific Group", } cmd.AddCommand( - cmdDeploymentGroupClose(), - cmdDeploymentGroupPause(), - cmdDeploymentGroupStart(), + GetTxDeploymentGroupCloseCmd(), + GetDeploymentGroupPauseCmd(), + GetDeploymentGroupStartCmd(), ) return cmd } -func cmdDeploymentGroupClose() *cobra.Command { +func GetTxDeploymentGroupCloseCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "close", - Short: "close a Deployment's specific Group", - Example: "akash tx deployment group-close --owner=[Account Address] --dseq=[uint64] --gseq=[uint32]", - Args: cobra.ExactArgs(0), + Use: "close", + Short: "close a Deployment's specific Group", + Example: "akash tx deployment group-close --owner=[Account Address] --dseq=[uint64] --gseq=[uint32]", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -351,12 +355,13 @@ func cmdDeploymentGroupClose() *cobra.Command { return cmd } -func cmdDeploymentGroupPause() *cobra.Command { +func GetDeploymentGroupPauseCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "pause", - Short: "pause a Deployment's specific Group", - Example: "akash tx deployment group pause --owner=[Account Address] --dseq=[uint64] --gseq=[uint32]", - Args: cobra.ExactArgs(0), + Use: "pause", + Short: "pause a Deployment's specific Group", + Example: "akash tx deployment group pause --owner=[Account Address] --dseq=[uint64] --gseq=[uint32]", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -390,12 +395,13 @@ func cmdDeploymentGroupPause() *cobra.Command { return cmd } -func cmdDeploymentGroupStart() *cobra.Command { +func GetDeploymentGroupStartCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "start", - Short: "start a Deployment's specific Group", - Example: "akash tx deployment group pause --owner=[Account Address] --dseq=[uint64] --gseq=[uint32]", - Args: cobra.ExactArgs(0), + Use: "start", + Short: "start a Deployment's specific Group", + Example: "akash tx deployment group pause --owner=[Account Address] --dseq=[uint64] --gseq=[uint32]", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -429,7 +435,7 @@ func cmdDeploymentGroupStart() *cobra.Command { return cmd } -func cmdDeploymentAuthz() *cobra.Command { +func GetTxDeploymentAuthzCmd() *cobra.Command { cmd := &cobra.Command{ Use: "authz", Short: "Deployment authorization transaction subcommands", @@ -437,14 +443,14 @@ func cmdDeploymentAuthz() *cobra.Command { } cmd.AddCommand( - cmdDeploymentGrantAuthorization(), - cmdDeploymentRevokeAuthorization(), + GetTxDeploymentGrantAuthorizationCmd(), + GetTxDeploymentRevokeAuthorizationCmd(), ) return cmd } -func cmdDeploymentGrantAuthorization() *cobra.Command { +func GetTxDeploymentGrantAuthorizationCmd() *cobra.Command { cmd := &cobra.Command{ Use: "grant --from ", Short: "Grant deposit deployment authorization to an address", @@ -456,7 +462,8 @@ Examples: $ akash tx %s authz grant akash1skjw.. 50akt --from=akash1skl.. --expiration=1661020200 `, dv1.ModuleName, dv1.ModuleName), ), - Args: cobra.ExactArgs(2), + Args: cobra.ExactArgs(2), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -498,14 +505,14 @@ Examples: }, } - flags.AddTxFlagsToCmd(cmd) + cflags.AddTxFlagsToCmd(cmd) cmd.Flags().Int64(cflags.FlagExpiration, time.Now().AddDate(1, 0, 0).Unix(), "The Unix timestamp. Default is one year.") - _ = cmd.MarkFlagRequired(flags.FlagFrom) + _ = cmd.MarkFlagRequired(cflags.FlagFrom) return cmd } -func cmdDeploymentRevokeAuthorization() *cobra.Command { +func GetTxDeploymentRevokeAuthorizationCmd() *cobra.Command { cmd := &cobra.Command{ Use: "revoke [grantee] --from=[granter]", Short: "Revoke deposit deployment authorization given to an address", @@ -515,7 +522,8 @@ Example: $ akash tx %s authz revoke akash1skj.. --from=akash1skj.. `, dv1.ModuleName), ), - Args: cobra.ExactArgs(1), + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -539,8 +547,8 @@ Example: }, } - flags.AddTxFlagsToCmd(cmd) - _ = cmd.MarkFlagRequired(flags.FlagFrom) + cflags.AddTxFlagsToCmd(cmd) + _ = cmd.MarkFlagRequired(cflags.FlagFrom) return cmd } diff --git a/go/cli/distribution_query.go b/go/cli/distribution_query.go new file mode 100644 index 00000000..cc6a4d00 --- /dev/null +++ b/go/cli/distribution_query.go @@ -0,0 +1,403 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetQueryDistributionCmd returns the cli query commands for this module +func GetQueryDistributionCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the distribution module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetQueryDistributionParamsCmd(), + GetQueryDistributionValidatorDistributionInfoCmd(), + GetQueryDistributionValidatorOutstandingRewardsCmd(), + GetQueryDistributionValidatorCommissionCmd(), + GetQueryDistributionValidatorSlashesCmd(), + GetQueryDistributionDelegatorRewardsCmd(), + GetQueryDistributionCommunityPoolCmd(), + GetQueryDistributionTokenizeShareRecordRewardCmd(), + ) + + return cmd +} + +// GetQueryDistributionParamsCmd implements the query params command. +func GetQueryDistributionParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Args: cobra.NoArgs, + Short: "Query distribution params", + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Distribution().Params(ctx, &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Params) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GetQueryDistributionValidatorDistributionInfoCmd implements the query validator distribution info command. +func GetQueryDistributionValidatorDistributionInfoCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "validator-distribution-info [validator]", + Args: cobra.ExactArgs(1), + Short: "Query validator distribution info", + Long: strings.TrimSpace( + fmt.Sprintf(`Query validator distribution info. +Example: +$ %s query distribution validator-distribution-info %s1lwjmdnks33xwnmfayc64ycprww49n33mtm92ne +`, + version.AppName, bech32PrefixValAddr, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + validatorAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + res, err := cl.Query().Distribution().ValidatorDistributionInfo(ctx, &types.QueryValidatorDistributionInfoRequest{ + ValidatorAddress: validatorAddr.String(), + }) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GetQueryDistributionValidatorOutstandingRewardsCmd implements the query validator +// outstanding rewards command. +func GetQueryDistributionValidatorOutstandingRewardsCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "validator-outstanding-rewards [validator]", + Args: cobra.ExactArgs(1), + Short: "Query distribution outstanding (un-withdrawn) rewards for a validator and all their delegations", + Long: strings.TrimSpace( + fmt.Sprintf(`Query distribution outstanding (un-withdrawn) rewards for a validator and all their delegations. + +Example: +$ %s query distribution validator-outstanding-rewards %s1lwjmdnks33xwnmfayc64ycprww49n33mtm92ne +`, + version.AppName, bech32PrefixValAddr, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + validatorAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + res, err := cl.Query().Distribution().ValidatorOutstandingRewards( + ctx, + &types.QueryValidatorOutstandingRewardsRequest{ValidatorAddress: validatorAddr.String()}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Rewards) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GetQueryDistributionValidatorCommissionCmd implements the query validator commission command. +func GetQueryDistributionValidatorCommissionCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "commission [validator]", + Args: cobra.ExactArgs(1), + Short: "Query distribution validator commission", + Long: strings.TrimSpace( + fmt.Sprintf(`Query validator commission rewards from delegators to that validator. + +Example: +$ %s query distribution commission %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixValAddr, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + validatorAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + res, err := cl.Query().Distribution().ValidatorCommission( + ctx, + &types.QueryValidatorCommissionRequest{ValidatorAddress: validatorAddr.String()}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Commission) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GetQueryDistributionValidatorSlashesCmd implements the query validator slashes command. +func GetQueryDistributionValidatorSlashesCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "slashes [validator] [start-height] [end-height]", + Args: cobra.ExactArgs(3), + Short: "Query distribution validator slashes", + Long: strings.TrimSpace( + fmt.Sprintf(`Query all slashes of a validator for a given block range. + +Example: +$ %s query distribution slashes %svaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 0 100 +`, + version.AppName, bech32PrefixValAddr, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + validatorAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + startHeight, err := strconv.ParseUint(args[1], 10, 64) + if err != nil { + return fmt.Errorf("start-height %s not a valid uint, please input a valid start-height", args[1]) + } + + endHeight, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return fmt.Errorf("end-height %s not a valid uint, please input a valid end-height", args[2]) + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := cl.Query().Distribution().ValidatorSlashes( + ctx, + &types.QueryValidatorSlashesRequest{ + ValidatorAddress: validatorAddr.String(), + StartingHeight: startHeight, + EndingHeight: endHeight, + Pagination: pageReq, + }, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "validator slashes") + return cmd +} + +// GetQueryDistributionDelegatorRewardsCmd implements the query delegator rewards command. +func GetQueryDistributionDelegatorRewardsCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "rewards [delegator-addr] [validator-addr]", + Args: cobra.RangeArgs(1, 2), + Short: "Query all distribution delegator rewards or rewards from a particular validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query all rewards earned by a delegator, optionally restrict to rewards from a single validator. + +Example: +$ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +$ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixAccAddr, version.AppName, bech32PrefixAccAddr, bech32PrefixValAddr, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + // query for rewards from a particular delegation + if len(args) == 2 { + validatorAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + res, err := cl.Query().Distribution().DelegationRewards( + ctx, + &types.QueryDelegationRewardsRequest{DelegatorAddress: delegatorAddr.String(), ValidatorAddress: validatorAddr.String()}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + } + + res, err := cl.Query().Distribution().DelegationTotalRewards( + ctx, + &types.QueryDelegationTotalRewardsRequest{DelegatorAddress: delegatorAddr.String()}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GetQueryDistributionCommunityPoolCmd returns the command for fetching community pool info. +func GetQueryDistributionCommunityPoolCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "community-pool", + Args: cobra.NoArgs, + Short: "Query the amount of coins in the community pool", + Long: strings.TrimSpace( + fmt.Sprintf(`Query all coins in the community pool which is under Governance control. + +Example: +$ %s query distribution community-pool +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Distribution().CommunityPool(cmd.Context(), &types.QueryCommunityPoolRequest{}) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GetQueryDistributionTokenizeShareRecordRewardCmd implements the query tokenize share record rewards +func GetQueryDistributionTokenizeShareRecordRewardCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "tokenize-share-record-rewards [owner]", + Args: cobra.ExactArgs(1), + Short: "Query distribution tokenize share record rewards", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the query tokenize share record rewards. + +Example: +$ %s query distribution tokenize-share-record-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixAccAddr, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + ownerAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + res, err := cl.Query().Distribution().TokenizeShareRecordReward( + cmd.Context(), + &types.QueryTokenizeShareRecordRewardRequest{OwnerAddress: ownerAddr.String()}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/go/cli/distribution_tx.go b/go/cli/distribution_tx.go new file mode 100644 index 00000000..42f96f3f --- /dev/null +++ b/go/cli/distribution_tx.go @@ -0,0 +1,350 @@ +package cli + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/spf13/cobra" + + cclient "pkg.akt.dev/go/node/client/v1beta3" +) + +// Transaction flags for the x/distribution module +var ( + FlagCommission = "commission" + FlagMaxMessagesPerTx = "max-msgs" +) + +const ( + MaxMessagesPerTxDefault = 0 +) + +// getTxDistributionCmd returns a root CLI command handler for all x/distribution transaction commands. +func getTxDistributionCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Distribution transactions subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + getTxDistributionWithdrawRewardsCmd(), + getTxDistributionWithdrawAllRewardsCmd(), + getTxDistributionSetWithdrawAddrCmd(), + getTxDistributionFundCommunityPoolCmd(), + getTxDistributionWithdrawTokenizeShareRecordRewardCmd(), + getTxDistributionWithdrawAllTokenizeShareRecordRewardCmd(), + ) + + return cmd +} + +type distrGenerateOrBroadcastFunc func(context.Context, []sdk.Msg, ...cclient.BroadcastOption) (interface{}, error) + +func newSplitAndApply( + genOrBroadcastFn distrGenerateOrBroadcastFunc, ctx context.Context, msgs []sdk.Msg, chunkSize int, opts ...cclient.BroadcastOption) error { + if chunkSize == 0 { + if _, err := genOrBroadcastFn(ctx, msgs, opts...); err != nil { + return err + } + } + + // split messages into slices of length chunkSize + totalMessages := len(msgs) + for i := 0; i < len(msgs); i += chunkSize { + sliceEnd := i + chunkSize + if sliceEnd > totalMessages { + sliceEnd = totalMessages + } + + msgChunk := msgs[i:sliceEnd] + _, err := genOrBroadcastFn(ctx, msgChunk, opts...) + if err != nil { + return err + } + } + + return nil +} + +// getTxDistributionWithdrawRewardsCmd returns a CLI command handler for creating a MsgWithdrawDelegatorReward transaction. +func getTxDistributionWithdrawRewardsCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "withdraw-rewards [validator-addr]", + Short: "Withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator", + Long: strings.TrimSpace( + fmt.Sprintf(`Withdraw rewards from a given delegation address, +and optionally withdraw validator commission if the delegation address given is a validator operator. + +Example: +$ %s tx distribution withdraw-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey +$ %s tx distribution withdraw-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey --commission +`, + version.AppName, bech32PrefixValAddr, version.AppName, bech32PrefixValAddr, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + delAddr := cctx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + msgs := []sdk.Msg{types.NewMsgWithdrawDelegatorReward(delAddr, valAddr)} + + if commission, _ := cmd.Flags().GetBool(FlagCommission); commission { + msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr)) + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, msgs) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cmd.Flags().Bool(FlagCommission, false, "Withdraw the validator's commission in addition to the rewards") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// getTxDistributionWithdrawAllRewardsCmd returns a CLI command handler for creating a MsgWithdrawDelegatorReward transaction. +func getTxDistributionWithdrawAllRewardsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-all-rewards", + Short: "withdraw all delegations rewards for a delegator", + Long: strings.TrimSpace( + fmt.Sprintf(`Withdraw all rewards for a single delegator. +Note that if you use this command with --%[2]s=%[3]s or --%[2]s=%[4]s, the %[5]s flag will automatically be set to 0. + +Example: +$ %[1]s tx distribution withdraw-all-rewards --from mykey +`, + version.AppName, flags.FlagBroadcastMode, flags.BroadcastSync, flags.BroadcastAsync, FlagMaxMessagesPerTx, + ), + ), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + delAddr := cctx.GetFromAddress() + + // The transaction cannot be generated offline since it requires a query + // to get all the validators. + if cctx.Offline { + return fmt.Errorf("cannot generate tx in offline mode") + } + + delValsRes, err := cl.Query().Distribution().DelegatorValidators(cmd.Context(), &types.QueryDelegatorValidatorsRequest{DelegatorAddress: delAddr.String()}) + if err != nil { + return err + } + + validators := delValsRes.Validators + // build multi-message transaction + msgs := make([]sdk.Msg, 0, len(validators)) + for _, valAddr := range validators { + val, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + return err + } + + msg := types.NewMsgWithdrawDelegatorReward(delAddr, val) + msgs = append(msgs, msg) + } + + chunkSize, _ := cmd.Flags().GetInt(FlagMaxMessagesPerTx) + + return newSplitAndApply(cl.Tx().BroadcastMsgs, ctx, msgs, chunkSize) + }, + } + + cmd.Flags().Int(FlagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// getTxDistributionSetWithdrawAddrCmd returns a CLI command handler for creating a MsgSetWithdrawAddress transaction. +func getTxDistributionSetWithdrawAddrCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "set-withdraw-addr [withdraw-addr]", + Short: "change the default withdraw address for rewards associated with an address", + Long: strings.TrimSpace( + fmt.Sprintf(`Set the withdraw address for rewards associated with a delegator address. + +Example: +$ %s tx distribution set-withdraw-addr %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from mykey +`, + version.AppName, bech32PrefixAccAddr, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + delAddr := cctx.GetFromAddress() + withdrawAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// getTxDistributionFundCommunityPoolCmd returns a CLI command handler for creating a MsgFundCommunityPool transaction. +func getTxDistributionFundCommunityPoolCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "fund-community-pool [amount]", + Args: cobra.ExactArgs(1), + Short: "Funds the community pool with the specified amount", + Long: strings.TrimSpace( + fmt.Sprintf(`Funds the community pool with the specified amount + +Example: +$ %s tx distribution fund-community-pool 100uatom --from mykey +`, + version.AppName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + depositorAddr := cctx.GetFromAddress() + amount, err := sdk.ParseCoinsNormalized(args[0]) + if err != nil { + return err + } + + msg := types.NewMsgFundCommunityPool(amount, depositorAddr) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// WithdrawAllTokenizeShareRecordReward defines a method to withdraw reward for all owning TokenizeShareRecord +func getTxDistributionWithdrawAllTokenizeShareRecordRewardCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-all-tokenize-share-rewards", + Args: cobra.ExactArgs(0), + Short: "Withdraw reward for all owning TokenizeShareRecord", + Long: strings.TrimSpace( + fmt.Sprintf(`Withdraw reward for all owned TokenizeShareRecord + +Example: +$ %s tx distribution withdraw-tokenize-share-rewards --from mykey +`, + version.AppName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + msg := types.NewMsgWithdrawAllTokenizeShareRecordReward(cctx.GetFromAddress()) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// WithdrawTokenizeShareRecordReward defines a method to withdraw reward for an owning TokenizeShareRecord +func getTxDistributionWithdrawTokenizeShareRecordRewardCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-tokenize-share-rewards", + Args: cobra.ExactArgs(1), + Short: "Withdraw reward for an owning TokenizeShareRecord", + Long: strings.TrimSpace( + fmt.Sprintf(`Withdraw reward for an owned TokenizeShareRecord + +Example: +$ %s tx distribution withdraw-tokenize-share-rewards 1 --from mykey +`, + version.AppName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + recordID, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + + msg := types.NewMsgWithdrawTokenizeShareRecordReward(cctx.GetFromAddress(), uint64(recordID)) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/escrow_query.go b/go/cli/escrow_query.go index c55e6f9c..191f0286 100644 --- a/go/cli/escrow_query.go +++ b/go/cli/escrow_query.go @@ -20,7 +20,9 @@ import ( mv1beta5 "pkg.akt.dev/go/node/market/v1beta5" ) -func GetEscrowQueryCmd() *cobra.Command { +var errNoLeaseMatches = errors.New("leases for deployment do not exist") + +func GetQueryEscrowCmd() *cobra.Command { cmd := &cobra.Command{ Use: etypes.ModuleName, Short: "Escrow query commands", @@ -29,19 +31,18 @@ func GetEscrowQueryCmd() *cobra.Command { } cmd.AddCommand( - cmdBlocksRemaining(), + GetQueryEscrowBlocksRemainingCmd(), ) return cmd } -var errNoLeaseMatches = errors.New("leases for deployment do not exist") - -func cmdBlocksRemaining() *cobra.Command { +func GetQueryEscrowBlocksRemainingCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "blocks-remaining", - Short: "Compute the number of blocks remaining for an ecrow account", - Args: cobra.ExactArgs(0), + Use: "blocks-remaining", + Short: "Compute the number of blocks remaining for an ecrow account", + Args: cobra.ExactArgs(0), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) diff --git a/go/cli/escrow_tx.go b/go/cli/escrow_tx.go index 6fd92889..351f5152 100644 --- a/go/cli/escrow_tx.go +++ b/go/cli/escrow_tx.go @@ -2,6 +2,6 @@ package cli import "github.com/spf13/cobra" -func GetTxCmd() *cobra.Command { +func GetTxEscrowCmd() *cobra.Command { return nil } diff --git a/go/cli/evidence_query.go b/go/cli/evidence_query.go new file mode 100644 index 00000000..1d431e01 --- /dev/null +++ b/go/cli/evidence_query.go @@ -0,0 +1,71 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/evidence/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetQueryEvidenceCmd returns the CLI command with all evidence module query commands +// mounted. +func GetQueryEvidenceCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Query for evidence by hash or for all (paginated) submitted evidence", + Long: strings.TrimSpace( + fmt.Sprintf(`Query for specific submitted evidence by hash or query for all (paginated) evidence: + +Example: +$ %s query %s DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660 +$ %s query %s --page=2 --limit=50 +`, + version.AppName, types.ModuleName, version.AppName, types.ModuleName, + ), + ), + Args: cobra.MaximumNArgs(1), + SuggestionsMinimumDistance: 2, + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + if len(args) > 0 { + params := &types.QueryEvidenceRequest{Hash: args[0]} + res, err := cl.Query().Evidence().Evidence(ctx, params) + if err != nil { + return err + } + + return cl.PrintMessage(res.Evidence) + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QueryAllEvidenceRequest{ + Pagination: pageReq, + } + + res, err := cl.Query().Evidence().AllEvidence(ctx, params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "evidence") + + return cmd +} diff --git a/go/cli/evidence_tx.go b/go/cli/evidence_tx.go new file mode 100644 index 00000000..9eccf2ad --- /dev/null +++ b/go/cli/evidence_tx.go @@ -0,0 +1,45 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/evidence/types" + + "github.com/spf13/cobra" +) + +// GetTxEvidenceCmd returns a CLI command that has all the native evidence module tx +// commands mounted. In addition, it mounts all childCmds, implemented by outside +// modules, under a sub-command. This allows external modules to implement custom +// Evidence types and Handlers while having the ability to create and sign txs +// containing them all from a single root command. +func GetTxEvidenceCmd(childCmds []*cobra.Command) *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Evidence transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + submitEvidenceCmd := SubmitEvidenceCmd() + + for _, childCmd := range childCmds { + submitEvidenceCmd.AddCommand(childCmd) + } + + // TODO: Add tx commands. + + return cmd +} + +// SubmitEvidenceCmd returns the top-level evidence submission command handler. +// All concrete evidence submission child command handlers should be registered +// under this command. +func SubmitEvidenceCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "submit", + Short: "Submit arbitrary evidence of misbehavior", + } + + return cmd +} diff --git a/go/cli/feegrant_query.go b/go/cli/feegrant_query.go new file mode 100644 index 00000000..2642a5b2 --- /dev/null +++ b/go/cli/feegrant_query.go @@ -0,0 +1,180 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/feegrant" +) + +// GetQueryFeegrantCmd returns the cli query commands for this module +func GetQueryFeegrantCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: feegrant.ModuleName, + Short: "Querying commands for the feegrant module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetQueryFeeGrantCmd(), + GetQueryFeeGrantsByGranteeCmd(), + GetQueryFeeGrantsByGranterCmd(), + ) + + return cmd +} + +// GetQueryFeeGrantCmd returns cmd to query for a grant between granter and grantee. +func GetQueryFeeGrantCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "grant [granter] [grantee]", + Args: cobra.ExactArgs(2), + Short: "Query details of a single grant", + Long: strings.TrimSpace( + fmt.Sprintf(`Query details for a grant. +You can find the fee-grant of a granter and grantee. + +Example: +$ %s query feegrant grant [granter] [grantee] +`, version.AppName), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + granterAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + granteeAddr, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + res, err := cl.Query().Feegrant().Allowance( + cmd.Context(), + &feegrant.QueryAllowanceRequest{ + Granter: granterAddr.String(), + Grantee: granteeAddr.String(), + }, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res.Allowance) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryFeeGrantsByGranteeCmd returns cmd to query for all grants for a grantee. +func GetQueryFeeGrantsByGranteeCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "grants-by-grantee [grantee]", + Args: cobra.ExactArgs(1), + Short: "Query all grants of a grantee", + Long: strings.TrimSpace( + fmt.Sprintf(`Queries all the grants for a grantee address. + +Example: +$ %s query feegrant grants-by-grantee [grantee] +`, version.AppName), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + granteeAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := cl.Query().Feegrant().Allowances( + cmd.Context(), + &feegrant.QueryAllowancesRequest{ + Grantee: granteeAddr.String(), + Pagination: pageReq, + }, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "grants") + + return cmd +} + +// GetQueryFeeGrantsByGranterCmd returns cmd to query for all grants by a granter. +func GetQueryFeeGrantsByGranterCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "grants-by-granter [granter]", + Args: cobra.ExactArgs(1), + Short: "Query all grants by a granter", + Long: strings.TrimSpace( + fmt.Sprintf(`Queries all the grants issued for a granter address. + +Example: +$ %s query feegrant grants-by-granter [granter] +`, version.AppName), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + granterAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := cl.Query().Feegrant().AllowancesByGranter( + cmd.Context(), + &feegrant.QueryAllowancesByGranterRequest{ + Granter: granterAddr.String(), + Pagination: pageReq, + }, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "grants") + + return cmd +} diff --git a/go/cli/feegrant_tx.go b/go/cli/feegrant_tx.go new file mode 100644 index 00000000..44478702 --- /dev/null +++ b/go/cli/feegrant_tx.go @@ -0,0 +1,222 @@ +package cli + +import ( + "fmt" + "strings" + "time" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/feegrant" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetTxFeegrantCmd returns the transaction commands for this module +func GetTxFeegrantCmd() *cobra.Command { + feegrantTxCmd := &cobra.Command{ + Use: feegrant.ModuleName, + Short: "Feegrant transactions subcommands", + Long: "Grant and revoke fee allowance for a grantee by a granter", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + feegrantTxCmd.AddCommand( + GetTxFeegrantGrantCmd(), + GetTxFeegrantRevokeCmd(), + ) + + return feegrantTxCmd +} + +// GetTxFeegrantGrantCmd returns a CLI command handler for creating a MsgGrantAllowance transaction. +func GetTxFeegrantGrantCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "grant [granter_key_or_address] [grantee]", + Short: "Grant Fee allowance to an address", + Long: strings.TrimSpace( + fmt.Sprintf( + `Grant authorization to pay fees from your address. + +Examples: +%s tx %s grant akash1skjw... --spend-limit 100uakt --from --expiration 2022-01-30T15:04:05Z or +%s tx %s grant akash1skjw... --spend-limit 100uakt --from --period 3600 --period-limit 10stake --expiration 2022-01-30T15:04:05Z or +%s tx %s grant akash1skjw... --spend-limit 100uakt --from --expiration 2022-01-30T15:04:05Z + --allowed-messages "/cosmos.gov.v1beta1.MsgSubmitProposal,/cosmos.gov.v1beta1.MsgVote" + `, version.AppName, feegrant.ModuleName, version.AppName, feegrant.ModuleName, version.AppName, feegrant.ModuleName, + ), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + grantee, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + granter := cctx.GetFromAddress() + sl, err := cmd.Flags().GetString(cflags.FlagSpendLimit) + if err != nil { + return err + } + + // if `FlagSpendLimit` isn't set, limit will be nil + limit, err := sdk.ParseCoinsNormalized(sl) + if err != nil { + return err + } + + exp, err := cmd.Flags().GetString(cflags.FlagExpiration) + if err != nil { + return err + } + + basic := feegrant.BasicAllowance{ + SpendLimit: limit, + } + + var expiresAtTime time.Time + if exp != "" { + expiresAtTime, err = time.Parse(time.RFC3339, exp) + if err != nil { + return err + } + basic.Expiration = &expiresAtTime + } + + var grant feegrant.FeeAllowanceI + grant = &basic + + periodClock, err := cmd.Flags().GetInt64(cflags.FlagPeriod) + if err != nil { + return err + } + + periodLimitVal, err := cmd.Flags().GetString(cflags.FlagPeriodLimit) + if err != nil { + return err + } + + // Check any of period or periodLimit flags set, If set consider it as periodic fee allowance. + if periodClock > 0 || periodLimitVal != "" { + periodLimit, err := sdk.ParseCoinsNormalized(periodLimitVal) + if err != nil { + return err + } + + if periodClock <= 0 { + return fmt.Errorf("period clock was not set") + } + + if periodLimit == nil { + return fmt.Errorf("period limit was not set") + } + + periodReset := getPeriodReset(periodClock) + if exp != "" && periodReset.Sub(expiresAtTime) > 0 { + return fmt.Errorf("period (%d) cannot reset after expiration (%v)", periodClock, exp) + } + + periodic := feegrant.PeriodicAllowance{ + Basic: basic, + Period: getPeriod(periodClock), + PeriodReset: getPeriodReset(periodClock), + PeriodSpendLimit: periodLimit, + PeriodCanSpend: periodLimit, + } + + grant = &periodic + } + + allowedMsgs, err := cmd.Flags().GetStringSlice(cflags.FlagAllowedMsgs) + if err != nil { + return err + } + + if len(allowedMsgs) > 0 { + grant, err = feegrant.NewAllowedMsgAllowance(grant, allowedMsgs) + if err != nil { + return err + } + } + + msg, err := feegrant.NewMsgGrantAllowance(grant, granter, grantee) + if err != nil { + return err + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + cmd.Flags().StringSlice(cflags.FlagAllowedMsgs, []string{}, "Set of allowed messages for fee allowance") + cmd.Flags().String(cflags.FlagExpiration, "", "The RFC 3339 timestamp after which the grant expires for the user") + cmd.Flags().String(cflags.FlagSpendLimit, "", "Spend limit specifies the max limit can be used, if not mentioned there is no limit") + cmd.Flags().Int64(cflags.FlagPeriod, 0, "period specifies the time duration(in seconds) in which period_limit coins can be spent before that allowance is reset (ex: 3600)") + cmd.Flags().String(cflags.FlagPeriodLimit, "", "period limit specifies the maximum number of coins that can be spent in the period") + + return cmd +} + +// GetTxFeegrantRevokeCmd returns a CLI command handler for creating a MsgRevokeAllowance transaction. +func GetTxFeegrantRevokeCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "revoke [granter] [grantee]", + Short: "revoke fee-grant", + Long: strings.TrimSpace( + fmt.Sprintf(`revoke fee grant from a granter to a grantee.. + +Example: + $ %s tx %s revoke akash1skj.. --from + `, version.AppName, feegrant.ModuleName), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + grantee, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + msg := feegrant.NewMsgRevokeAllowance(cctx.GetFromAddress(), grantee) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{&msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func getPeriodReset(duration int64) time.Time { + return time.Now().Add(getPeriod(duration)) +} + +func getPeriod(duration int64) time.Duration { + return time.Duration(duration) * time.Second +} diff --git a/go/cli/flags/client.go b/go/cli/flags/client.go index 235f4a0a..43cee130 100644 --- a/go/cli/flags/client.go +++ b/go/cli/flags/client.go @@ -9,6 +9,8 @@ import ( cltypes "pkg.akt.dev/go/node/client/types" ) +// ClientOptionsFromFlags reads client options from cli flag set. +// func ClientOptionsFromFlags(flagSet *pflag.FlagSet) ([]cltypes.ClientOption, error) { opts := make([]cltypes.ClientOption, 0) @@ -56,6 +58,13 @@ func ClientOptionsFromFlags(flagSet *pflag.FlagSet) ([]cltypes.ClientOption, err opts = append(opts, cltypes.WithGasPrices(gasPrices)) } + signMode := SignModeDirect + if flagSet.Changed(FlagSignMode) { + signMode, _ = flagSet.GetString(FlagSignMode) + } + + opts = append(opts, cltypes.WithSignMode(signMode)) + return opts, nil } diff --git a/go/cli/flags/flags.go b/go/cli/flags/flags.go index d34574aa..f306e82f 100644 --- a/go/cli/flags/flags.go +++ b/go/cli/flags/flags.go @@ -7,6 +7,7 @@ import ( "github.com/spf13/pflag" "github.com/cosmos/cosmos-sdk/crypto/keyring" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" cmcli "github.com/cometbft/cometbft/libs/cli" ) @@ -41,60 +42,103 @@ const ( ) const ( - FlagDeposit = "deposit" - FlagState = "state" - FlagOwner = "owner" - FlagDSeq = "dseq" - FlagGSeq = "gseq" - FlagOSeq = "oseq" - FlagProvider = "provider" - FlagSerial = "serial" - FlagPrice = "price" - FlagDepositorAccount = "depositor-account" - FlagExpiration = "expiration" - FlagHome = cmcli.HomeFlag - FlagKeyringDir = "keyring-dir" - FlagUseLedger = "ledger" - FlagChainID = "chain-id" - FlagNode = "node" - FlagGRPC = "grpc-addr" - FlagGRPCInsecure = "grpc-insecure" - FlagHeight = "height" - FlagGasAdjustment = "gas-adjustment" - FlagFrom = "from" - FlagName = "name" - FlagAccountNumber = "account-number" - FlagSequence = "sequence" - FlagNote = "note" - FlagFees = "fees" - FlagGas = "gas" - FlagGasPrices = "gas-prices" - FlagBroadcastMode = "broadcast-mode" - FlagDryRun = "dry-run" - FlagGenerateOnly = "generate-only" - FlagOffline = "offline" - FlagOutputDocument = "output-document" // inspired by wget -O - FlagSkipConfirmation = "yes" - FlagProve = "prove" - FlagKeyringBackend = "keyring-backend" - FlagPage = "page" - FlagLimit = "limit" - FlagSignMode = "sign-mode" - FlagPageKey = "page-key" - FlagOffset = "offset" - FlagCountTotal = "count-total" - FlagTimeoutHeight = "timeout-height" - FlagKeyType = "key-type" - FlagFeePayer = "fee-payer" - FlagFeeGranter = "fee-granter" - FlagReverse = "reverse" - FlagTip = "tip" - FlagAux = "aux" - FlagInitHeight = "initial-height" - FlagDelayed = "delayed" + FlagGenesisTime = "genesis-time" + FlagGenTxDir = "gentx-dir" + FlagRecover = "recover" + // FlagDefaultBondDenom defines the default denom to use in the genesis file. + FlagDefaultBondDenom = "default-denom" + FlagDenom = "denom" + FlagVestingStart = "vesting-start-time" + FlagVestingEnd = "vesting-end-time" + FlagVestingAmt = "vesting-amount" + FlagAppendMode = "append" + FlagEvents = "events" + FlagType = "type" + FlagMultisig = "multisig" + FlagOverwrite = "overwrite" + FlagSigOnly = "signature-only" + FlagAmino = "amino" + FlagNoAutoIncrement = "no-auto-increment" + FlagAppend = "append" + FlagTitle = "title" + // Deprecated: only used for v1beta1 legacy proposals. + FlagDescription = "description" + // Deprecated: only used for v1beta1 legacy proposals. + FlagProposalType = "type" + // Deprecated: only used for v1beta1 legacy proposals. + FlagUpgradeHeight = "upgrade-height" + // Deprecated: only used for v1beta1 legacy proposals. + FlagUpgradeInfo = "upgrade-info" + FlagMetadata = "metadata" + FlagSummary = "summary" + // Deprecated: only used for v1beta1 legacy proposals. + FlagProposal = "proposal" + FlagNoValidate = "no-validate" + FlagDaemonName = "daemon-name" + FlagPeriod = "period" + FlagPeriodLimit = "period-limit" + FlagAllowedMsgs = "allowed-messages" + FlagMsgType = "msg-type" + FlagAllowedValidators = "allowed-validators" + FlagDenyValidators = "deny-validators" + FlagAllowList = "allow-list" + FlagDeposit = "deposit" + FlagStatus = "status" + FlagState = "state" + FlagOwner = "owner" + FlagDSeq = "dseq" + FlagGSeq = "gseq" + FlagOSeq = "oseq" + FlagProvider = "provider" + FlagSerial = "serial" + FlagPrice = "price" + FlagDepositorAccount = "depositor-account" + FlagExpiration = "expiration" + FlagSpendLimit = "spend-limit" + FlagHome = cmcli.HomeFlag + FlagKeyringDir = "keyring-dir" + FlagUseLedger = "ledger" + FlagChainID = "chain-id" + FlagNode = "node" + FlagGRPC = "grpc-addr" + FlagGRPCInsecure = "grpc-insecure" + FlagHeight = "height" + FlagGasAdjustment = "gas-adjustment" + FlagFrom = "from" + FlagName = "name" + FlagAccountNumber = "account-number" + FlagSequence = "sequence" + FlagNote = "note" + FlagFees = "fees" + FlagGas = "gas" + FlagGasPrices = "gas-prices" + FlagBroadcastMode = "broadcast-mode" + FlagDryRun = "dry-run" + FlagGenerateOnly = "generate-only" + FlagOffline = "offline" + FlagOutputDocument = "output-document" // inspired by wget -O + FlagSkipConfirmation = "yes" + FlagProve = "prove" + FlagKeyringBackend = "keyring-backend" + FlagPage = "page" + FlagLimit = "limit" + FlagSignMode = "sign-mode" + FlagPageKey = "page-key" + FlagOffset = "offset" + FlagCountTotal = "count-total" + FlagTimeoutHeight = "timeout-height" + FlagKeyType = "key-type" + FlagFeePayer = "fee-payer" + FlagFeeGranter = "fee-granter" + FlagReverse = "reverse" + FlagTip = "tip" + FlagAux = "aux" + FlagInitHeight = "initial-height" + FlagDelayed = "delayed" // FlagOutput is the flag to set the output format. // This differs from FlagOutputDocument that is used to set the output file. FlagOutput = cmcli.OutputFlag + FlagSplit = "split" // CometBFT logging flags FlagLogLevel = "log_level" @@ -103,8 +147,47 @@ const ( FlagLogColor = "log_color" FlagLogTimestamp = "log_timestamp" FlagTrace = "trace" + + FlagAddressValidator = "validator" + FlagAddressValidatorSrc = "addr-validator-source" + FlagAddressValidatorDst = "addr-validator-dest" + FlagPubKey = "pubkey" + FlagAmount = "amount" + FlagSharesAmount = "shares-amount" + FlagSharesFraction = "shares-fraction" + + FlagMoniker = "moniker" + FlagEditMoniker = "new-moniker" + FlagIdentity = "identity" + FlagWebsite = "website" + FlagSecurityContact = "security-contact" + FlagDetails = "details" + + FlagCommissionRate = "commission-rate" + FlagCommissionMaxRate = "commission-max-rate" + FlagCommissionMaxChangeRate = "commission-max-change-rate" + + FlagGenesisFormat = "genesis-format" + FlagNodeID = "node-id" + FlagIP = "ip" + FlagP2PPort = "p2p-port" ) +// common flagsets to add to various functions +var ( + fsShares = pflag.NewFlagSet("", pflag.ContinueOnError) + fsValidator = pflag.NewFlagSet("", pflag.ContinueOnError) + fsRedelegation = pflag.NewFlagSet("", pflag.ContinueOnError) +) + +func init() { + fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal") + fsShares.String(FlagSharesFraction, "", "Fraction of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1") + fsValidator.String(FlagAddressValidator, "", "The Bech32 address of the validator") + fsRedelegation.String(FlagAddressValidatorSrc, "", "The Bech32 address of the source validator") + fsRedelegation.String(FlagAddressValidatorDst, "", "The Bech32 address of the destination validator") +} + func AddDepositFlags(flags *pflag.FlagSet) { flags.String(FlagDeposit, "", "Deposit amount") } @@ -177,3 +260,60 @@ func AddPaginationFlagsToCmd(cmd *cobra.Command, query string) { cmd.Flags().Bool(FlagCountTotal, false, fmt.Sprintf("count total number of records in %s to query for", query)) cmd.Flags().Bool(FlagReverse, false, "results are sorted in descending order") } + +// FlagSetCommissionCreate Returns the FlagSet used for commission create. +func FlagSetCommissionCreate() *pflag.FlagSet { + fs := pflag.NewFlagSet("", pflag.ContinueOnError) + + fs.String(FlagCommissionRate, "", "The initial commission rate percentage") + fs.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") + fs.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") + + return fs +} + +// FlagSetAmount Returns the FlagSet for amount related operations. +func FlagSetAmount() *pflag.FlagSet { + fs := pflag.NewFlagSet("", pflag.ContinueOnError) + fs.String(FlagAmount, "", "Amount of coins to bond") + return fs +} + +// FlagSetPublicKey Returns the flagset for Public Key related operations. +func FlagSetPublicKey() *pflag.FlagSet { + fs := pflag.NewFlagSet("", pflag.ContinueOnError) + fs.String(FlagPubKey, "", "The validator's Protobuf JSON encoded public key") + return fs +} + +func FlagSetDescriptionEdit() *pflag.FlagSet { + fs := pflag.NewFlagSet("", pflag.ContinueOnError) + + fs.String(FlagEditMoniker, stakingtypes.DoNotModifyDesc, "The validator's name") + fs.String(FlagIdentity, stakingtypes.DoNotModifyDesc, "The (optional) identity signature (ex. UPort or Keybase)") + fs.String(FlagWebsite, stakingtypes.DoNotModifyDesc, "The validator's (optional) website") + fs.String(FlagSecurityContact, stakingtypes.DoNotModifyDesc, "The validator's (optional) security contact email") + fs.String(FlagDetails, stakingtypes.DoNotModifyDesc, "The validator's (optional) details") + + return fs +} + +func FlagSetCommissionUpdate() *pflag.FlagSet { + fs := pflag.NewFlagSet("", pflag.ContinueOnError) + + fs.String(FlagCommissionRate, "", "The new commission rate percentage") + + return fs +} + +func FlagSetDescriptionCreate() *pflag.FlagSet { + fs := pflag.NewFlagSet("", pflag.ContinueOnError) + + fs.String(FlagMoniker, "", "The validator's name") + fs.String(FlagIdentity, "", "The optional identity signature (ex. UPort or Keybase)") + fs.String(FlagWebsite, "", "The validator's (optional) website") + fs.String(FlagSecurityContact, "", "The validator's (optional) security contact email") + fs.String(FlagDetails, "", "The validator's (optional) details") + + return fs +} diff --git a/go/cli/genesis.go b/go/cli/genesis.go new file mode 100644 index 00000000..7302e93c --- /dev/null +++ b/go/cli/genesis.go @@ -0,0 +1,37 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/types/module" + gentypes "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +func GetGenesisCmd( + mbm module.BasicManager, + txCfg client.TxEncodingConfig, + defaultNodeHome string, +) *cobra.Command { + cmd := &cobra.Command{ + Use: "genesis", + Short: "Genesis control commands", + DisableFlagParsing: false, + SuggestionsMinimumDistance: 2, + RunE: ValidateCmd, + } + + gentxModule := mbm[gentypes.ModuleName].(genutil.AppModuleBasic) + + cmd.AddCommand( + getGenesisValidateCmd(mbm), + GetGenesisGenTxCmd(mbm, txCfg, banktypes.GenesisBalancesIterator{}, defaultNodeHome), + GetGenesisAddAccountCmd(defaultNodeHome), + GetGenesisInitCmd(mbm, defaultNodeHome), + GetGenesisCollectCmd(banktypes.GenesisBalancesIterator{}, defaultNodeHome, gentxModule.GenTxValidator), + ) + + return cmd +} diff --git a/go/cli/genesis_collect.go b/go/cli/genesis_collect.go new file mode 100644 index 00000000..73137644 --- /dev/null +++ b/go/cli/genesis_collect.go @@ -0,0 +1,69 @@ +package cli + +import ( + "encoding/json" + "path/filepath" + + tmtypes "github.com/cometbft/cometbft/types" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetGenesisCollectCmd - return the cobra command to collect genesis transactions +func GetGenesisCollectCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeHome string, validator types.MessageValidator) *cobra.Command { + cmd := &cobra.Command{ + Use: "collect", + Short: "Collect genesis txs and output a genesis.json file", + RunE: func(cmd *cobra.Command, _ []string) error { + sctx := server.GetServerContextFromCmd(cmd) + cctx := client.GetClientContextFromCmd(cmd) + + config := sctx.Config + cdc := cctx.Codec + + config.SetRoot(cctx.HomeDir) + + nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config) + if err != nil { + return errors.Wrap(err, "failed to initialize node validator files") + } + + genDoc, err := tmtypes.GenesisDocFromFile(config.GenesisFile()) + if err != nil { + return errors.Wrap(err, "failed to read genesis doc from file") + } + + genTxDir, _ := cmd.Flags().GetString(cflags.FlagGenTxDir) + genTxsDir := genTxDir + if genTxsDir == "" { + genTxsDir = filepath.Join(config.RootDir, "config", "gentx") + } + + toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage("")) + initCfg := types.NewInitConfig(genDoc.ChainID, genTxsDir, nodeID, valPubKey) + + appMessage, err := genutil.GenAppStateFromConfig(cdc, + cctx.TxConfig, + config, initCfg, *genDoc, genBalIterator, validator) + if err != nil { + return errors.Wrap(err, "failed to get genesis app state from config") + } + + toPrint.AppMessage = appMessage + + return displayInfo(toPrint) + }, + } + + cmd.Flags().String(cflags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(cflags.FlagGenTxDir, "", "override default \"gentx\" directory from which collect and execute genesis transactions; default [--home]/config/gentx/") + + return cmd +} diff --git a/go/cli/genesis_genaccount.go b/go/cli/genesis_genaccount.go new file mode 100644 index 00000000..7eb5790e --- /dev/null +++ b/go/cli/genesis_genaccount.go @@ -0,0 +1,88 @@ +package cli + +import ( + "bufio" + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/helpers" + + cflags "pkg.akt.dev/go/cli/flags" +) + +const ( + +) + +// GetGenesisAddAccountCmd returns add-genesis-account cobra Command. +// This command is provided as a default, applications are expected to provide their own command if custom genesis accounts are needed. +func GetGenesisAddAccountCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "add-account [address_or_key_name] [coin][,[coin]]", + Short: "Add a genesis account to genesis.json", + Long: `Add a genesis account to genesis.json. The provided account must specify +the account address or key name and a list of initial coins. If a key name is given, +the address will be looked up in the local Keybase. The list of initial tokens must +contain valid denominations. Accounts may optionally be supplied with vesting parameters. +`, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cctx := client.GetClientContextFromCmd(cmd) + sctx := server.GetServerContextFromCmd(cmd) + + config := sctx.Config + + config.SetRoot(cctx.HomeDir) + + var kr keyring.Keyring + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + inBuf := bufio.NewReader(cmd.InOrStdin()) + keyringBackend, _ := cmd.Flags().GetString(cflags.FlagKeyringBackend) + + if keyringBackend != "" && cctx.Keyring == nil { + var err error + kr, err = keyring.New(sdk.KeyringServiceName(), keyringBackend, cctx.HomeDir, inBuf, cctx.Codec) + if err != nil { + return err + } + } else { + kr = cctx.Keyring + } + + k, err := kr.Key(args[0]) + if err != nil { + return fmt.Errorf("failed to get address from Keyring: %w", err) + } + + addr, err = k.GetAddress() + if err != nil { + return err + } + } + + appendFlag, _ := cmd.Flags().GetBool(cflags.FlagAppendMode) + vestingStart, _ := cmd.Flags().GetInt64(cflags.FlagVestingStart) + vestingEnd, _ := cmd.Flags().GetInt64(cflags.FlagVestingEnd) + vestingAmtStr, _ := cmd.Flags().GetString(cflags.FlagVestingAmt) + + return auth.AddGenesisAccount(cctx.Codec, addr, appendFlag, config.GenesisFile(), args[1], vestingAmtStr, vestingStart, vestingEnd) + }, + } + + cmd.Flags().String(cflags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(cflags.FlagKeyringBackend, cflags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + cmd.Flags().String(cflags.FlagVestingAmt, "", "amount of coins for vesting accounts") + cmd.Flags().Int64(cflags.FlagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") + cmd.Flags().Int64(cflags.FlagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") + cmd.Flags().Bool(cflags.FlagAppendMode, false, "append the coins to an account already in the genesis.json file") + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/genesis_genaccount_test.go b/go/cli/genesis_genaccount_test.go new file mode 100644 index 00000000..d2aea528 --- /dev/null +++ b/go/cli/genesis_genaccount_test.go @@ -0,0 +1,108 @@ +package cli_test + +import ( + "context" + "fmt" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/libs/log" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth" + genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" + + "pkg.akt.dev/go/cli" + cflags "pkg.akt.dev/go/cli/flags" +) + +func TestAddGenesisAccountCmd(t *testing.T) { + _, _, addr1 := testdata.KeyTestPubAddr() + tests := []struct { + name string + addr string + denom string + withKeyring bool + expectErr bool + }{ + { + name: "invalid address", + addr: "", + denom: "1000uakt", + withKeyring: false, + expectErr: true, + }, + { + name: "valid address", + addr: addr1.String(), + denom: "1000uakt", + withKeyring: false, + expectErr: false, + }, + { + name: "multiple denoms", + addr: addr1.String(), + denom: "1000uakt, 2000stake", + withKeyring: false, + expectErr: false, + }, + { + name: "with keyring", + addr: "ser", + denom: "1000uakt", + withKeyring: true, + expectErr: false, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + appCodec := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}).Codec + err = genutiltest.ExecInitCmd(testMbm, home, appCodec) + require.NoError(t, err) + + sctx := server.NewContext(viper.New(), cfg, logger) + cctx := client.Context{}.WithCodec(appCodec).WithHomeDir(home) + + if tc.withKeyring { + path := hd.CreateHDPath(118, 0, 0).String() + kr, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendMemory, home, nil, appCodec) + require.NoError(t, err) + _, _, err = kr.NewMnemonic(tc.addr, keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + require.NoError(t, err) + cctx = cctx.WithKeyring(kr) + } + + ctx := context.Background() + ctx = context.WithValue(ctx, cli.ClientContextKey, &cctx) + ctx = context.WithValue(ctx, server.ServerContextKey, sctx) + + cmd := cli.GetGenesisAddAccountCmd(home) + cmd.SetArgs([]string{ + tc.addr, + tc.denom, + fmt.Sprintf("--%s=home", cflags.FlagHome), + }) + + if tc.expectErr { + require.Error(t, cmd.ExecuteContext(ctx)) + } else { + require.NoError(t, cmd.ExecuteContext(ctx)) + } + }) + } +} diff --git a/go/cli/genesis_gentx.go b/go/cli/genesis_gentx.go new file mode 100644 index 00000000..50f8cd5f --- /dev/null +++ b/go/cli/genesis_gentx.go @@ -0,0 +1,263 @@ +package cli + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + + tmtypes "github.com/cometbft/cometbft/types" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetGenesisGenTxCmd builds the application's gentx command. +func GetGenesisGenTxCmd(mbm module.BasicManager, txEncCfg client.TxEncodingConfig, genBalIterator types.GenesisBalancesIterator, defaultNodeHome string) *cobra.Command { + ipDefault, _ := server.ExternalIP() + fsCreateValidator, defaultsDesc := cli.CreateValidatorMsgFlagSet(ipDefault) + + cmd := &cobra.Command{ + Use: "gentx [key_name] [amount]", + Short: "Generate a genesis tx carrying a self delegation", + Args: cobra.ExactArgs(2), + Long: fmt.Sprintf(`Generate a genesis transaction that creates a validator with a self-delegation, +that is signed by the key in the Keyring referenced by a given name. A node ID and Bech32 consensus +pubkey may optionally be provided. If they are omitted, they will be retrieved from the priv_validator.json +file. The following default parameters are included: + %s + +Example: +$ %s gentx my-key-name 1000000uakt --home=/path/to/home/dir --keyring-backend=os --chain-id=test-chain-1 \ + --moniker="myvalidator" \ + --commission-max-change-rate=0.01 \ + --commission-max-rate=1.0 \ + --commission-rate=0.07 \ + --details="..." \ + --security-contact="..." \ + --website="..." +`, defaultsDesc, version.AppName, + ), + RunE: func(cmd *cobra.Command, args []string) error { + stcx := server.GetServerContextFromCmd(cmd) + cctx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + cdc := cctx.Codec + + config := stcx.Config + config.SetRoot(cctx.HomeDir) + + nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(stcx.Config) + if err != nil { + return errors.Wrap(err, "failed to initialize node validator files") + } + + // read --nodeID, if empty take it from priv_validator.json + if nodeIDString, _ := cmd.Flags().GetString(cflags.FlagNodeID); nodeIDString != "" { + nodeID = nodeIDString + } + + // read --pubkey, if empty take it from priv_validator.json + if pkStr, _ := cmd.Flags().GetString(cflags.FlagPubKey); pkStr != "" { + if err := cctx.Codec.UnmarshalInterfaceJSON([]byte(pkStr), &valPubKey); err != nil { + return errors.Wrap(err, "failed to unmarshal validator public key") + } + } + + genDoc, err := tmtypes.GenesisDocFromFile(config.GenesisFile()) + if err != nil { + return errors.Wrapf(err, "failed to read genesis doc file %s", config.GenesisFile()) + } + + var genesisState map[string]json.RawMessage + if err = json.Unmarshal(genDoc.AppState, &genesisState); err != nil { + return errors.Wrap(err, "failed to unmarshal genesis state") + } + + if err = mbm.ValidateGenesis(cdc, txEncCfg, genesisState); err != nil { + return errors.Wrap(err, "failed to validate genesis state") + } + + inBuf := bufio.NewReader(cmd.InOrStdin()) + + name := args[0] + key, err := cctx.Keyring.Key(name) + if err != nil { + return errors.Wrapf(err, "failed to fetch '%s' from the keyring", name) + } + + moniker := config.Moniker + if m, _ := cmd.Flags().GetString(cflags.FlagMoniker); m != "" { + moniker = m + } + + // set flags for creating a gentx + createValCfg, err := cli.PrepareConfigForTxCreateValidator(cmd.Flags(), moniker, nodeID, genDoc.ChainID, valPubKey) + if err != nil { + return errors.Wrap(err, "error creating configuration to create validator msg") + } + + amount := args[1] + coins, err := sdk.ParseCoinsNormalized(amount) + if err != nil { + return errors.Wrap(err, "failed to parse coins") + } + addr, err := key.GetAddress() + if err != nil { + return err + } + err = genutil.ValidateAccountInGenesis(genesisState, genBalIterator, addr, coins, cdc) + if err != nil { + return errors.Wrap(err, "failed to validate account in genesis") + } + + txFactory, err := tx.NewFactoryCLI(cctx, cmd.Flags()) + if err != nil { + return err + } + + pub, err := key.GetAddress() + if err != nil { + return err + } + cctx = cctx.WithInput(inBuf).WithFromAddress(pub) + + // The following line comes from a discrepancy between the `gentx` + // and `create-validator` commands: + // - `gentx` expects amount as an arg, + // - `create-validator` expects amount as a required flag. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8251 + // Since gentx doesn't set the amount flag (which `create-validator` + // reads from), we copy the amount arg into the valCfg directly. + // + // Ideally, the `create-validator` command should take a validator + // config file instead of so many flags. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8177 + createValCfg.Amount = amount + + // create a 'create-validator' message + txBldr, msg, err := cli.BuildCreateValidatorMsg(cctx, createValCfg, txFactory, true) + if err != nil { + return errors.Wrap(err, "failed to build create-validator message") + } + + if key.GetType() == keyring.TypeOffline || key.GetType() == keyring.TypeMulti { + cmd.PrintErrln("Offline key passed in. Use `tx sign` command to sign.") + return txBldr.PrintUnsignedTx(cctx, msg) + } + + // write the unsigned transaction to the buffer + w := bytes.NewBuffer([]byte{}) + cctx = cctx.WithOutput(w) + + if err = msg.ValidateBasic(); err != nil { + return err + } + + if err = txBldr.PrintUnsignedTx(cctx, msg); err != nil { + return errors.Wrap(err, "failed to print unsigned std tx") + } + + // read the transaction + stdTx, err := readUnsignedGenTxFile(cctx, w) + if err != nil { + return errors.Wrap(err, "failed to read unsigned gen tx file") + } + + // sign the transaction and write it to the output file + txBuilder, err := cctx.TxConfig.WrapTxBuilder(stdTx) + if err != nil { + return fmt.Errorf("error creating tx builder: %w", err) + } + + err = authclient.SignTx(txFactory, cctx, name, txBuilder, true, true) + if err != nil { + return errors.Wrap(err, "failed to sign std tx") + } + + outputDocument, _ := cmd.Flags().GetString(cflags.FlagOutputDocument) + if outputDocument == "" { + outputDocument, err = makeOutputFilepath(config.RootDir, nodeID) + if err != nil { + return errors.Wrap(err, "failed to create output file path") + } + } + + if err := writeSignedGenTx(cctx, outputDocument, stdTx); err != nil { + return errors.Wrap(err, "failed to write signed gen tx") + } + + cmd.PrintErrf("Genesis transaction written to %q\n", outputDocument) + return nil + }, + } + + cmd.Flags().String(cflags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(cflags.FlagOutputDocument, "", "Write the genesis transaction JSON document to the given file instead of the default location") + cmd.Flags().AddFlagSet(fsCreateValidator) + cflags.AddTxFlagsToCmd(cmd) + _ = cmd.Flags().MarkHidden(cflags.FlagOutput) // signing makes sense to output only json + + return cmd +} + +func makeOutputFilepath(rootDir, nodeID string) (string, error) { + writePath := filepath.Join(rootDir, "config", "gentx") + if err := os.MkdirAll(writePath, 0o700); err != nil { + return "", fmt.Errorf("could not create directory %q: %w", writePath, err) + } + + return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil +} + +func readUnsignedGenTxFile(clientCtx client.Context, r io.Reader) (sdk.Tx, error) { + bz, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + aTx, err := clientCtx.TxConfig.TxJSONDecoder()(bz) + if err != nil { + return nil, err + } + + return aTx, err +} + +func writeSignedGenTx(clientCtx client.Context, outputDocument string, tx sdk.Tx) error { + outputFile, err := os.OpenFile(outputDocument, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o644) + if err != nil { + return err + } + defer func() { + _ = outputFile.Close() + }() + + txj, err := clientCtx.TxConfig.TxJSONEncoder()(tx) + if err != nil { + return err + } + + _, err = fmt.Fprintf(outputFile, "%s\n", txj) + + return err +} diff --git a/go/cli/genesis_gentx_test.go b/go/cli/genesis_gentx_test.go new file mode 100644 index 00000000..d227db55 --- /dev/null +++ b/go/cli/genesis_gentx_test.go @@ -0,0 +1,95 @@ +package cli_test + +import ( + "context" + "fmt" + "path/filepath" + + "github.com/cosmos/cosmos-sdk/client" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "pkg.akt.dev/go/cli" + cflags "pkg.akt.dev/go/cli/flags" +) + +func (s *GenesisCLITestSuite) TestGenTxCmd() { + amount := sdk.NewCoin("uakt", sdk.NewInt(12)) + + tests := []struct { + name string + args []string + expCmdOutput string + }{ + { + name: "invalid commission rate returns error", + args: []string{ + fmt.Sprintf("--%s=%s", cflags.FlagChainID, s.baseCtx.ChainID), + fmt.Sprintf("--%s=1", cflags.FlagCommissionRate), + "node0", + amount.String(), + }, + expCmdOutput: fmt.Sprintf("--%s=%s --%s=1 %s %s", cflags.FlagChainID, s.baseCtx.ChainID, cflags.FlagCommissionRate, "node0", amount.String()), + }, + { + name: "valid gentx", + args: []string{ + fmt.Sprintf("--%s=%s", cflags.FlagChainID, s.baseCtx.ChainID), + "node0", + amount.String(), + }, + expCmdOutput: fmt.Sprintf("--%s=%s %s %s", cflags.FlagChainID, s.baseCtx.ChainID, "node0", amount.String()), + }, + { + name: "invalid pubkey", + args: []string{ + fmt.Sprintf("--%s=%s", cflags.FlagChainID, "test-chain-1"), + fmt.Sprintf("--%s={\"key\":\"BOIkjkFruMpfOFC9oNPhiJGfmY2pHF/gwHdLDLnrnS0=\"}", cflags.FlagPubKey), + "node0", + amount.String(), + }, + expCmdOutput: fmt.Sprintf("--%s=test-chain-1 --%s={\"key\":\"BOIkjkFruMpfOFC9oNPhiJGfmY2pHF/gwHdLDLnrnS0=\"} %s %s ", cflags.FlagChainID, cflags.FlagPubKey, "node0", amount.String()), + }, + { + name: "valid pubkey flag", + args: []string{ + fmt.Sprintf("--%s=%s", cflags.FlagChainID, "test-chain-1"), + fmt.Sprintf("--%s={\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"BOIkjkFruMpfOFC9oNPhiJGfmY2pHF/gwHdLDLnrnS0=\"}", cflags.FlagPubKey), + "node0", + amount.String(), + }, + expCmdOutput: fmt.Sprintf("--%s=test-chain-1 --%s={\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"BOIkjkFruMpfOFC9oNPhiJGfmY2pHF/gwHdLDLnrnS0=\"} %s %s ", cflags.FlagChainID, cflags.FlagPubKey, "node0", amount.String()), + }, + } + + for _, tc := range tests { + tc := tc + + dir := s.T().TempDir() + genTxFile := filepath.Join(dir, "myTx") + + tc.args = append(tc.args, fmt.Sprintf("--%s=%s", cflags.FlagOutputDocument, genTxFile)) + + s.Run(tc.name, func() { + cctx := s.cctx + ctx := svrcmd.CreateExecuteContext(context.Background()) + + cmd := cli.GetGenesisGenTxCmd( + module.NewBasicManager(), + cctx.TxConfig, + banktypes.GenesisBalancesIterator{}, + cctx.HomeDir, + ) + cmd.SetContext(ctx) + cmd.SetArgs(tc.args) + + s.Require().NoError(client.SetCmdClientContextHandler(cctx, cmd)) + + if len(tc.args) != 0 { + s.Require().Contains(fmt.Sprint(cmd), tc.expCmdOutput) + } + }) + } +} diff --git a/go/cli/genesis_init.go b/go/cli/genesis_init.go new file mode 100644 index 00000000..8be6fc4c --- /dev/null +++ b/go/cli/genesis_init.go @@ -0,0 +1,169 @@ +package cli + +import ( + "bufio" + "encoding/json" + "fmt" + "os" + "path/filepath" + + cfg "github.com/cometbft/cometbft/config" + tmrand "github.com/cometbft/cometbft/libs/rand" + "github.com/cometbft/cometbft/types" + "github.com/cosmos/go-bip39" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + + cflags "pkg.akt.dev/go/cli/flags" +) + +type printInfo struct { + Moniker string `json:"moniker" yaml:"moniker"` + ChainID string `json:"chain_id" yaml:"chain_id"` + NodeID string `json:"node_id" yaml:"node_id"` + GenTxsDir string `json:"gentxs_dir" yaml:"gentxs_dir"` + AppMessage json.RawMessage `json:"app_message" yaml:"app_message"` +} + +func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, appMessage json.RawMessage) printInfo { + return printInfo{ + Moniker: moniker, + ChainID: chainID, + NodeID: nodeID, + GenTxsDir: genTxsDir, + AppMessage: appMessage, + } +} + +func displayInfo(info printInfo) error { + out, err := json.MarshalIndent(info, "", " ") + if err != nil { + return err + } + + _, err = fmt.Fprintf(os.Stderr, "%s\n", sdk.MustSortJSON(out)) + + return err +} + +// GetGenesisInitCmd returns a command that initializes all files needed for Tendermint +// and the respective application. +func GetGenesisInitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "init [moniker]", + Short: "Initialize private validator, p2p, genesis, and application configuration files", + Long: `Initialize validators's and node's configuration files.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + cdc := clientCtx.Codec + + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + config.SetRoot(clientCtx.HomeDir) + + chainID, _ := cmd.Flags().GetString(cflags.FlagChainID) + switch { + case chainID != "": + case clientCtx.ChainID != "": + chainID = clientCtx.ChainID + default: + chainID = fmt.Sprintf("test-chain-%v", tmrand.Str(6)) + } + + // Get bip39 mnemonic + var mnemonic string + recover, _ := cmd.Flags().GetBool(cflags.FlagRecover) + if recover { + inBuf := bufio.NewReader(cmd.InOrStdin()) + value, err := input.GetString("Enter your bip39 mnemonic", inBuf) + if err != nil { + return err + } + + mnemonic = value + if !bip39.IsMnemonicValid(mnemonic) { + return errors.New("invalid mnemonic") + } + } + + // Get initial height + initHeight, _ := cmd.Flags().GetInt64(flags.FlagInitHeight) + if initHeight < 1 { + initHeight = 1 + } + + nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic) + if err != nil { + return err + } + + config.Moniker = args[0] + + genFile := config.GenesisFile() + overwrite, _ := cmd.Flags().GetBool(cflags.FlagOverwrite) + defaultDenom, _ := cmd.Flags().GetString(cflags.FlagDefaultBondDenom) + + // use os.Stat to check if the file exists + _, err = os.Stat(genFile) + if !overwrite && !os.IsNotExist(err) { + return fmt.Errorf("genesis.json file already exists: %v", genFile) + } + + // Overwrites the SDK default denom for side-effects + if defaultDenom != "" { + sdk.DefaultBondDenom = defaultDenom + } + + appGenState := mbm.DefaultGenesis(cdc) + + appState, err := json.MarshalIndent(appGenState, "", " ") + if err != nil { + return errors.Wrap(err, "Failed to marshal default genesis state") + } + + genDoc := &types.GenesisDoc{} + if _, err := os.Stat(genFile); err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + genDoc, err = types.GenesisDocFromFile(genFile) + if err != nil { + return errors.Wrap(err, "Failed to read genesis doc from file") + } + } + + genDoc.ChainID = chainID + genDoc.Validators = nil + genDoc.AppState = appState + genDoc.InitialHeight = initHeight + + if err = genutil.ExportGenesisFile(genDoc, genFile); err != nil { + return errors.Wrap(err, "Failed to export genesis file") + } + + toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState) + + cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + return displayInfo(toPrint) + }, + } + + cmd.Flags().String(cflags.FlagHome, defaultNodeHome, "node's home directory") + cmd.Flags().BoolP(cflags.FlagOverwrite, "o", false, "overwrite the genesis.json file") + cmd.Flags().Bool(cflags.FlagRecover, false, "provide seed phrase to recover existing key instead of creating") + cmd.Flags().String(cflags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(cflags.FlagDefaultBondDenom, "uakt", "genesis file default denomination, if left blank default value is 'uakt'") + cmd.Flags().Int64(cflags.FlagInitHeight, 1, "specify the initial block height at genesis") + + return cmd +} diff --git a/go/cli/genesis_init_test.go b/go/cli/genesis_init_test.go new file mode 100644 index 00000000..2f9ebf9b --- /dev/null +++ b/go/cli/genesis_init_test.go @@ -0,0 +1,363 @@ +package cli_test + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "testing" + "time" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + abci_server "github.com/cometbft/cometbft/abci/server" + "github.com/cometbft/cometbft/libs/log" + tmtypes "github.com/cometbft/cometbft/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/mock" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" + "github.com/cosmos/cosmos-sdk/x/staking" + + "pkg.akt.dev/go/cli" + cflags "pkg.akt.dev/go/cli/flags" +) + +var testMbm = module.NewBasicManager( + staking.AppModuleBasic{}, + genutil.AppModuleBasic{}, +) + +func TestInitCmd(t *testing.T) { + tests := []struct { + name string + flags func(dir string) []string + shouldErr bool + err error + }{ + { + name: "happy path", + flags: func(dir string) []string { + return []string{ + "appnode-test", + } + }, + shouldErr: false, + err: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + sctx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + cctx := client.Context{}. + WithCodec(marshaler). + WithLegacyAmino(makeCodec()). + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, cli.ClientContextKey, &cctx) + ctx = context.WithValue(ctx, server.ServerContextKey, sctx) + + cmd := cli.GetGenesisInitCmd(testMbm, home) + cmd.SetArgs( + tt.flags(home), + ) + + if tt.shouldErr { + err := cmd.ExecuteContext(ctx) + require.EqualError(t, err, tt.err.Error()) + } else { + require.NoError(t, cmd.ExecuteContext(ctx)) + } + }) + } +} + +func TestInitRecover(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + serverCtx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + clientCtx := client.Context{}. + WithCodec(marshaler). + WithLegacyAmino(makeCodec()). + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, cli.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + + cmd := cli.GetGenesisInitCmd(testMbm, home) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + + cmd.SetArgs([]string{ + "appnode-test", + fmt.Sprintf("--%s=true", cflags.FlagRecover), + }) + + // use valid mnemonic and complete recovery key generation successfully + mockIn.Reset("decide praise business actor peasant farm drastic weather extend front hurt later song give verb rhythm worry fun pond reform school tumble august one\n") + require.NoError(t, cmd.ExecuteContext(ctx)) +} + +func TestInitDefaultBondDenom(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + serverCtx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + clientCtx := client.Context{}. + WithCodec(marshaler). + WithLegacyAmino(makeCodec()). + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, cli.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + + cmd := cli.GetGenesisInitCmd(testMbm, home) + + cmd.SetArgs([]string{ + "appnode-test", + fmt.Sprintf("--%s=%s", cflags.FlagHome, home), + fmt.Sprintf("--%s=testtoken", cflags.FlagDefaultBondDenom), + }) + + require.NoError(t, cmd.ExecuteContext(ctx)) +} + +func TestEmptyState(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + serverCtx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + clientCtx := client.Context{}. + WithCodec(marshaler). + WithLegacyAmino(makeCodec()). + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, cli.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + + cmd := cli.GetGenesisInitCmd(testMbm, home) + cmd.SetArgs([]string{"appnode-test", fmt.Sprintf("--%s=%s", cflags.FlagHome, home)}) + + require.NoError(t, cmd.ExecuteContext(ctx)) + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + cmd = server.ExportCmd(nil, home) + cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", cflags.FlagHome, home)}) + require.NoError(t, cmd.ExecuteContext(ctx)) + + outC := make(chan string) + go func() { + var buf bytes.Buffer + _, _ = io.Copy(&buf, r) + outC <- buf.String() + }() + + _ = w.Close() + + os.Stdout = old + out := <-outC + + require.Contains(t, out, "genesis_time") + require.Contains(t, out, "chain_id") + require.Contains(t, out, "consensus_params") + require.Contains(t, out, "app_hash") + require.Contains(t, out, "app_state") +} + +func TestStartStandAlone(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + err := genutiltest.ExecInitCmd(testMbm, home, marshaler) + require.NoError(t, err) + + app, err := mock.NewApp(home, logger) + require.NoError(t, err) + + svrAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + + svr, err := abci_server.NewServer(svrAddr, "socket", app) + require.NoError(t, err, "error creating listener") + + svr.SetLogger(logger.With("module", "abci-server")) + err = svr.Start() + require.NoError(t, err) + + timer := time.NewTimer(time.Duration(2) * time.Second) + for range timer.C { + err = svr.Stop() + require.NoError(t, err) + break + } +} + +func TestInitNodeValidatorFiles(t *testing.T) { + home := t.TempDir() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(cfg) + require.NoError(t, err) + + require.NotEqual(t, "", nodeID) + require.NotEqual(t, 0, len(valPubKey.Bytes())) +} + +func TestInitConfig(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + sctx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + cctx := client.Context{}. + WithCodec(marshaler). + WithLegacyAmino(makeCodec()). + WithChainID("foo"). // add chain-id to cctx + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, cli.ClientContextKey, &cctx) + ctx = context.WithValue(ctx, server.ServerContextKey, sctx) + + cmd := cli.GetGenesisInitCmd(testMbm, home) + cmd.SetArgs([]string{"testnode"}) + + require.NoError(t, cmd.ExecuteContext(ctx)) + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + cmd = server.ExportCmd(nil, home) + require.NoError(t, cmd.ExecuteContext(ctx)) + + outC := make(chan string) + go func() { + var buf bytes.Buffer + _, _ = io.Copy(&buf, r) + outC <- buf.String() + }() + + _ = w.Close() + os.Stdout = old + out := <-outC + + require.Contains(t, out, "\"chain_id\": \"foo\"") +} + +func TestInitWithHeight(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + sctx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + cctx := client.Context{}. + WithCodec(marshaler). + WithLegacyAmino(makeCodec()). + WithChainID("foo"). // add chain-id to cctx + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, cli.ClientContextKey, &cctx) + ctx = context.WithValue(ctx, server.ServerContextKey, sctx) + + testInitialHeight := int64(333) + + cmd := cli.GetGenesisInitCmd(testMbm, home) + cmd.SetArgs([]string{"init-height-test", fmt.Sprintf("--%s=%d", cflags.FlagInitHeight, testInitialHeight)}) + + require.NoError(t, cmd.ExecuteContext(ctx)) + + appGenesis, importErr := tmtypes.GenesisDocFromFile(cfg.GenesisFile()) + require.NoError(t, importErr) + + require.Equal(t, testInitialHeight, appGenesis.InitialHeight) +} + +func TestInitWithNegativeHeight(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + sctx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + cctx := client.Context{}. + WithCodec(marshaler). + WithLegacyAmino(makeCodec()). + WithChainID("foo"). // add chain-id to cctx + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, cli.ClientContextKey, &cctx) + ctx = context.WithValue(ctx, server.ServerContextKey, sctx) + + testInitialHeight := int64(-333) + + cmd := cli.GetGenesisInitCmd(testMbm, home) + cmd.SetArgs([]string{"init-height-test", fmt.Sprintf("--%s=%d", cflags.FlagInitHeight, testInitialHeight)}) + + require.NoError(t, cmd.ExecuteContext(ctx)) + + appGenesis, importErr := tmtypes.GenesisDocFromFile(cfg.GenesisFile()) + require.NoError(t, importErr) + + require.Equal(t, int64(1), appGenesis.InitialHeight) +} + +// custom tx codec +func makeCodec() *codec.LegacyAmino { + cdc := codec.NewLegacyAmino() + + sdk.RegisterLegacyAminoCodec(cdc) + cryptocodec.RegisterCrypto(cdc) + + return cdc +} diff --git a/go/cli/genesis_migrate.go b/go/cli/genesis_migrate.go new file mode 100644 index 00000000..09200c69 --- /dev/null +++ b/go/cli/genesis_migrate.go @@ -0,0 +1,141 @@ +package cli + +import ( + "encoding/json" + "fmt" + "sort" + "time" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "golang.org/x/exp/maps" + + tmjson "github.com/cometbft/cometbft/libs/json" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + v043 "github.com/cosmos/cosmos-sdk/x/genutil/migrations/v043" + v046 "github.com/cosmos/cosmos-sdk/x/genutil/migrations/v046" + v047 "github.com/cosmos/cosmos-sdk/x/genutil/migrations/v047" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// Allow applications to extend and modify the migration process. +// +// Ref: https://github.com/cosmos/cosmos-sdk/issues/5041 +var migrationMap = types.MigrationMap{ + "v0.43": v043.Migrate, // NOTE: v0.43, v0.44 and v0.45 are genesis compatible. + "v0.46": v046.Migrate, + "v0.47": v047.Migrate, +} + +// GetMigrationCallback returns a MigrationCallback for a given version. +func GetMigrationCallback(version string) types.MigrationCallback { + return migrationMap[version] +} + +// GetMigrationVersions get all migration version in a sorted slice. +func GetMigrationVersions() []string { + versions := maps.Keys(migrationMap) + sort.Strings(versions) + + return versions +} + +// MigrateGenesisCmd returns a command to execute genesis state migration. +func MigrateGenesisCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "migrate [target-version] [genesis-file]", + Short: "Migrate genesis to a specified target version", + Long: fmt.Sprintf(`Migrate the source genesis into the target version and print to file or STDOUT. + +Example: +$ %s migrate v0.36 /path/to/genesis.json --chain-id= --genesis-time=2019-04-22T17:00:00Z +`, version.AppName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + return MigrateHandler(cmd, args, migrationMap) + }, + } + + cmd.Flags().String(cflags.FlagGenesisTime, "", "override genesis_time with this flag") + cmd.Flags().String(cflags.FlagChainID, "", "override chain_id with this flag") + + return cmd +} + +// MigrateHandler handles the migration command with a migration map as input, +// returning an error upon failure. +func MigrateHandler(cmd *cobra.Command, args []string, migrations types.MigrationMap) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + var err error + + target := args[0] + importGenesis := args[1] + + genDoc, err := validateGenDoc(importGenesis) + if err != nil { + return err + } + + // Since some default values are valid values, we just print to + // make sure the user didn't forget to update these values. + if genDoc.ConsensusParams.Evidence.MaxBytes == 0 { + fmt.Printf("Warning: consensus_params.evidence.max_bytes is set to 0. If this is"+ + " deliberate, feel free to ignore this warning. If not, please have a look at the chain"+ + " upgrade guide at %s.\n", chainUpgradeGuide) + } + + var initialState types.AppMap + if err := json.Unmarshal(genDoc.AppState, &initialState); err != nil { + return errors.Wrap(err, "failed to JSON unmarshal initial genesis state") + } + + migrationFunc := migrations[target] + if migrationFunc == nil { + return fmt.Errorf("unknown migration function for version: %s", target) + } + + // TODO: handler error from migrationFunc call + newGenState := migrationFunc(initialState, clientCtx) + + genDoc.AppState, err = json.Marshal(newGenState) + if err != nil { + return errors.Wrap(err, "failed to JSON marshal migrated genesis state") + } + + genesisTime, _ := cmd.Flags().GetString(cflags.FlagGenesisTime) + if genesisTime != "" { + var t time.Time + + err := t.UnmarshalText([]byte(genesisTime)) + if err != nil { + return errors.Wrap(err, "failed to unmarshal genesis time") + } + + genDoc.GenesisTime = t + } + + chainID, _ := cmd.Flags().GetString(cflags.FlagChainID) + if chainID != "" { + genDoc.ChainID = chainID + } + + bz, err := tmjson.Marshal(genDoc) + if err != nil { + return errors.Wrap(err, "failed to marshal genesis doc") + } + + sortedBz, err := sdk.SortJSON(bz) + if err != nil { + return errors.Wrap(err, "failed to sort JSON genesis doc") + } + + cmd.Println(string(sortedBz)) + + return nil +} diff --git a/go/cli/genesis_migrate_test.go b/go/cli/genesis_migrate_test.go new file mode 100644 index 00000000..cdbb2822 --- /dev/null +++ b/go/cli/genesis_migrate_test.go @@ -0,0 +1,60 @@ +package cli_test + +import ( + "context" + "testing" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/stretchr/testify/require" + + "pkg.akt.dev/go/cli" + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +func TestGetMigrationCallback(t *testing.T) { + for _, version := range cli.GetMigrationVersions() { + require.NotNil(t, cli.GetMigrationCallback(version)) + } +} + +func (s *GenesisCLITestSuite) TestMigrateGenesis() { + testCases := []struct { + name string + genesis string + target string + expErr bool + expErrMsg string + check func(jsonOut string) + }{ + { + "migrate 0.37 to 0.42", + v037Exported, + "v0.42", + true, "Make sure that you have correctly migrated all Tendermint consensus params", func(_ string) {}, + }, + { + "migrate 0.42 to 0.43", + v040Valid, + "v0.43", + false, "", + func(jsonOut string) { + // Make sure the json output contains the ADR-037 gov weighted votes. + s.Require().Contains(jsonOut, "\"weight\":\"1.000000000000000000\"") + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + genesisFile := testutil.WriteToNewTempFile(s.T(), tc.genesis) + jsonOutput, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cli.MigrateGenesisCmd(), tc.target, genesisFile.Name()) + if tc.expErr { + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + tc.check(jsonOutput.String()) + } + }) + } +} diff --git a/go/cli/genesis_suite_test.go b/go/cli/genesis_suite_test.go new file mode 100644 index 00000000..1e43201a --- /dev/null +++ b/go/cli/genesis_suite_test.go @@ -0,0 +1,44 @@ +package cli_test + +import ( + "bytes" + "io" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/genutil" +) + +type GenesisCLITestSuite struct { + CLITestSuite +} + +func (s *GenesisCLITestSuite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(genutil.AppModuleBasic{}) + s.kr = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithLegacyAmino(s.encCfg.Amino). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID("test-chain") + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + } + s.cctx = ctxGen().WithOutput(&outBuf) +} + diff --git a/go/cli/genesis_validate.go b/go/cli/genesis_validate.go new file mode 100644 index 00000000..31f94294 --- /dev/null +++ b/go/cli/genesis_validate.go @@ -0,0 +1,72 @@ +package cli + +import ( + "encoding/json" + "fmt" + + tmtypes "github.com/cometbft/cometbft/types" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/types/module" +) + +const chainUpgradeGuide = "https://github.com/cosmos/cosmos-sdk/blob/main/UPGRADING.md" + +// getGenesisValidateCmd takes a genesis file, and makes sure that it is valid. +func getGenesisValidateCmd(mbm module.BasicManager) *cobra.Command { + return &cobra.Command{ + Use: "validate [file]", + Args: cobra.RangeArgs(0, 1), + Short: "validates the genesis file at the default location or at the location passed as an arg", + RunE: func(cmd *cobra.Command, args []string) (err error) { + sctx := server.GetServerContextFromCmd(cmd) + cctx := client.GetClientContextFromCmd(cmd) + + cdc := cctx.Codec + + // Load default if passed no args, otherwise load passed file + var genesis string + if len(args) == 0 { + genesis = sctx.Config.GenesisFile() + } else { + genesis = args[0] + } + + genDoc, err := validateGenDoc(genesis) + if err != nil { + return err + } + + var genState map[string]json.RawMessage + if err = json.Unmarshal(genDoc.AppState, &genState); err != nil { + return fmt.Errorf("error unmarshalling genesis doc %s: %s", genesis, err.Error()) + } + + if err = mbm.ValidateGenesis(cdc, cctx.TxConfig, genState); err != nil { + return fmt.Errorf("error validating genesis file %s: %s", genesis, err.Error()) + } + + fmt.Printf("File at %s is a valid genesis file\n", genesis) + + return nil + }, + } +} + +// validateGenDoc reads a genesis file and validates that it is a correct +// Tendermint GenesisDoc. This function does not do any cosmos-related +// validation. +func validateGenDoc(importGenesisFile string) (*tmtypes.GenesisDoc, error) { + genDoc, err := tmtypes.GenesisDocFromFile(importGenesisFile) + if err != nil { + return nil, fmt.Errorf("%s. Make sure that"+ + " you have correctly migrated all Tendermint consensus params, please see the"+ + " chain migration guide at %s for more info", + err.Error(), chainUpgradeGuide, + ) + } + + return genDoc, nil +} diff --git a/go/cli/genesis_validate_test.go b/go/cli/genesis_validate_test.go new file mode 100644 index 00000000..008d25a4 --- /dev/null +++ b/go/cli/genesis_validate_test.go @@ -0,0 +1,102 @@ +package cli_test + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +// An example exported genesis file from a 0.37 chain. Note that evidence +// parameters only contains `max_age`. +var v037Exported = `{ + "app_hash": "", + "app_state": {}, + "chain_id": "test", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" + }, + "evidence": { "max_age": "100000" }, + "validator": { "pub_key_types": ["ed25519"] } + }, + "genesis_time": "2020-09-29T20:16:29.172362037Z", + "validators": [] +}` + +// An example exported genesis file that's 0.40 compatible. +// We added the following app_state: +// +// - x/gov: added votes to test ADR-037 split votes migration. +var v040Valid = `{ + "app_hash": "", + "app_state": { + "gov": { + "starting_proposal_id": "0", + "deposits": [], + "votes": [ + { + "proposal_id": "5", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", + "option": "VOTE_OPTION_YES" + } + ], + "proposals": [], + "deposit_params": { "min_deposit": [], "max_deposit_period": "0s" }, + "voting_params": { "voting_period": "0s" }, + "tally_params": { "quorum": "0", "threshold": "0", "veto_threshold": "0" } + } + }, + "chain_id": "test", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" + }, + "evidence": { + "max_age_num_blocks": "100000", + "max_age_duration": "172800000000000", + "max_bytes": "0" + }, + "validator": { "pub_key_types": ["ed25519"] } + }, + "genesis_time": "2020-09-29T20:16:29.172362037Z", + "validators": [] +}` + +func (s *GenesisCLITestSuite) TestValidateGenesis() { + testCases := []struct { + name string + genesis string + expErr bool + }{ + { + "exported 0.37 genesis file", + v037Exported, + true, + }, + { + "valid 0.40 genesis file", + v040Valid, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + genesisFile := testutil.WriteToNewTempFile(s.T(), tc.genesis) + _, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cli.ValidateGenesisCmd(nil), genesisFile.Name()) + if tc.expErr { + s.Require().Contains(err.Error(), "Make sure that you have correctly migrated all Tendermint consensus params") + } else { + s.Require().NoError(err) + } + }) + } +} diff --git a/go/cli/gentxs.go b/go/cli/gentxs.go index ab578323..c56a8056 100644 --- a/go/cli/gentxs.go +++ b/go/cli/gentxs.go @@ -1,102 +1,2 @@ package cli -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - tmtypes "github.com/cometbft/cometbft/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/x/genutil" - "github.com/cosmos/cosmos-sdk/x/genutil/types" - - cflags "pkg.akt.dev/go/cli/flags" -) - -const flagGenTxDir = "gentx-dir" - -// CollectGenTxsCmd - return the cobra command to collect genesis transactions -func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeHome string, validator types.MessageValidator) *cobra.Command { - cmd := &cobra.Command{ - Use: "collect-gentxs", - Short: "Collect genesis txs and output a genesis.json file", - RunE: func(cmd *cobra.Command, _ []string) error { - serverCtx := server.GetServerContextFromCmd(cmd) - config := serverCtx.Config - - clientCtx := client.GetClientContextFromCmd(cmd) - cdc := clientCtx.Codec - - config.SetRoot(clientCtx.HomeDir) - - nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config) - if err != nil { - return fmt.Errorf("%w: failed to initialize node validator files", err) - } - - genDoc, err := tmtypes.GenesisDocFromFile(config.GenesisFile()) - if err != nil { - return fmt.Errorf("%w: failed to read genesis doc from file", err) - } - - genTxDir, _ := cmd.Flags().GetString(flagGenTxDir) - genTxsDir := genTxDir - if genTxsDir == "" { - genTxsDir = filepath.Join(config.RootDir, "config", "gentx") - } - - toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage("")) - initCfg := types.NewInitConfig(genDoc.ChainID, genTxsDir, nodeID, valPubKey) - - appMessage, err := genutil.GenAppStateFromConfig(cdc, - clientCtx.TxConfig, - config, initCfg, *genDoc, genBalIterator, validator) - if err != nil { - return fmt.Errorf("%w: failed to get genesis app state from config", err) - } - - toPrint.AppMessage = appMessage - - return displayInfo(toPrint) - }, - } - - cmd.Flags().String(cflags.FlagHome, defaultNodeHome, "The application home directory") - cmd.Flags().String(flagGenTxDir, "", "override default \"gentx\" directory from which collect and execute genesis transactions; default [--home]/config/gentx/") - - return cmd -} - -type printInfo struct { - Moniker string `json:"moniker" yaml:"moniker"` - ChainID string `json:"chain_id" yaml:"chain_id"` - NodeID string `json:"node_id" yaml:"node_id"` - GenTxsDir string `json:"gentxs_dir" yaml:"gentxs_dir"` - AppMessage json.RawMessage `json:"app_message" yaml:"app_message"` -} - -func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, appMessage json.RawMessage) printInfo { - return printInfo{ - Moniker: moniker, - ChainID: chainID, - NodeID: nodeID, - GenTxsDir: genTxsDir, - AppMessage: appMessage, - } -} - -func displayInfo(info printInfo) error { - out, err := json.MarshalIndent(info, "", " ") - if err != nil { - return err - } - - _, err = fmt.Fprintf(os.Stderr, "%s\n", sdk.MustSortJSON(out)) - - return err -} diff --git a/go/cli/go.mod b/go/cli/go.mod index a9bc155b..e5f39079 100644 --- a/go/cli/go.mod +++ b/go/cli/go.mod @@ -6,32 +6,52 @@ toolchain go1.22.5 require ( cosmossdk.io/errors v1.0.1 + cosmossdk.io/math v1.3.0 + github.com/chzyer/readline v1.5.1 github.com/cometbft/cometbft v0.37.6 github.com/cosmos/cosmos-sdk v0.47.16-ics-lsm + github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogoproto v1.4.12 + github.com/manifoldco/promptui v0.9.0 + github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 + github.com/stretchr/testify v1.9.0 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d + google.golang.org/grpc v1.64.0 gopkg.in/yaml.v3 v3.0.1 - pkg.akt.dev/go v0.0.1-rc6 + pkg.akt.dev/go v0.0.1-rc9 pkg.akt.dev/go/sdl v0.0.1-rc6 ) -replace github.com/gogo/protobuf => github.com/cosmos/gogoproto v1.3.3-alpha.regen.1 +replace ( + github.com/cosmos/gogoproto => github.com/cosmos/gogoproto v1.4.10 + github.com/gogo/protobuf => github.com/cosmos/gogoproto v1.3.3-alpha.regen.1 + golang.org/x/exp => golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb + google.golang.org/genproto/googleapis/api => google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 +) require ( + cloud.google.com/go v0.113.0 // indirect + cloud.google.com/go/auth v0.4.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/iam v1.1.8 // indirect + cloud.google.com/go/storage v1.40.0 // indirect cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect cosmossdk.io/log v1.3.1 // indirect - cosmossdk.io/math v1.3.0 // indirect cosmossdk.io/tools/rosetta v0.2.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect + github.com/aws/aws-sdk-go v1.44.203 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/boz/go-lifecycle v0.1.1 // indirect @@ -39,6 +59,7 @@ require ( github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.10.0 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect @@ -47,7 +68,6 @@ require ( github.com/confio/ics23/go v0.9.1 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect - github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.20.1 // indirect github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect @@ -70,16 +90,23 @@ require ( github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect github.com/golang/glog v1.2.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/orderedcode v0.0.1 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.4 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect @@ -88,13 +115,18 @@ require ( github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.7.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/hashicorp/hcl v1.0.1-0.20191016231534-914dc3f8dd7c // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.1-0.20191019112844-b572e7f4cdac // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.0 // indirect @@ -108,13 +140,14 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/highwayhash v1.0.2 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -131,29 +164,36 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect - github.com/stretchr/testify v1.9.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.6.0 // indirect + github.com/ulikunitz/xz v0.5.11 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.7 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect go.step.sm/crypto v0.45.1 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/term v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.181.0 // indirect google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect - google.golang.org/grpc v1.64.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go/cli/go.sum b/go/cli/go.sum index ea00aab7..b5905ac1 100644 --- a/go/cli/go.sum +++ b/go/cli/go.sum @@ -1,5 +1,525 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.113.0 h1:g3C70mn3lWfckKBiCVsAshabrDg01pQ0pnX1MNtnMkA= +cloud.google.com/go v0.113.0/go.mod h1:glEqlogERKYeePz6ZdkcLJ28Q2I6aERgDDErBg9GzO8= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg= +cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0= +cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJahPw= +cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= @@ -14,9 +534,10 @@ cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= @@ -24,10 +545,10 @@ github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTB github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -42,14 +563,21 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/ github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -59,16 +587,23 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.203 h1:pcsP805b9acL3wUqa4JR2vg1k2wnItkDYNvfmcy6F+U= +github.com/aws/aws-sdk-go v1.44.203/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boz/go-lifecycle v0.1.1 h1:tG/wff7Zxbkf19g4D4I0G8Y4sq83iT5QjD4rzEf/zrI= github.com/boz/go-lifecycle v0.1.1/go.mod h1:zdagAUMcC2C0OmQkBlJZFV77uF4GCVaGphAexGi7oho= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= @@ -86,27 +621,40 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -146,9 +694,8 @@ github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiK github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= github.com/cosmos/gogoproto v1.3.3-alpha.regen.1 h1:Qmv/wAw4xHnjN5iZ9qHergfk1O7nnYl7ZsIY5lF+E9k= github.com/cosmos/gogoproto v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= -github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= -github.com/cosmos/gogoproto v1.4.12 h1:vB6Lbe/rtnYGjQuFxkPiPYiCybqFT8QvLipDZP8JpFE= -github.com/cosmos/gogoproto v1.4.12/go.mod h1:LnZob1bXRdUoqMMtwYlcR3wjiElmlC+FkjaZRv1/eLY= +github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoKuI= +github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= github.com/cosmos/iavl v0.20.1 h1:rM1kqeG3/HBT85vsZdoSNsehciqUQPWrR4BYmqE2+zg= github.com/cosmos/iavl v0.20.1/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw= @@ -187,6 +734,7 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -203,10 +751,18 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -217,6 +773,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -238,7 +796,11 @@ github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -246,13 +808,20 @@ github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -281,12 +850,26 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -294,6 +877,7 @@ github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -304,7 +888,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -315,13 +901,21 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -329,12 +923,61 @@ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg= +github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -355,6 +998,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -367,6 +1012,10 @@ github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= +github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -374,12 +1023,16 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -400,6 +1053,8 @@ github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0Jr github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= @@ -410,6 +1065,10 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.1-0.20191019112844-b572e7f4cdac h1:GcJkaxD5Wy/Ucn+L0USlpbGJy9O6+7r0nBI7ftJ7Uu0= github.com/jmhodges/levigo v1.0.1-0.20191019112844-b572e7f4cdac/go.mod h1:dM7ihgFM8Do6WGIfOXWPgpJ+4bKGR/4ZkYh8HKDdFy4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -421,20 +1080,32 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -450,6 +1121,9 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -467,6 +1141,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -474,12 +1150,17 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -556,8 +1237,12 @@ github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9 github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -566,6 +1251,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -607,9 +1294,11 @@ github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Ung github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -622,6 +1311,8 @@ github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWR github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= @@ -650,6 +1341,9 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -685,6 +1379,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -706,12 +1401,22 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= @@ -722,8 +1427,30 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.step.sm/crypto v0.45.1 h1:Xb8XldsbqT6pDYsg46BVPP1euASNbeNAhzrlvUP3QWo= go.step.sm/crypto v0.45.1/go.mod h1:XtJBuMuZb11YeJpG8uP3fyBl2MerXWJ/pWQX/Au+Kt8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -743,33 +1470,61 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us= +golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -782,34 +1537,113 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -823,105 +1657,424 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.181.0 h1:rPdjwnWgiPPOJx3IcSAQ2III5aX5tCer6wMpa/xmZi4= +google.golang.org/api v0.181.0/go.mod h1:MnQ+M0CFsfUwA5beZ+g/vCBCPXvtmZwRz2qzZk8ih1k= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw= google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw= -google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No= -google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -929,21 +2082,50 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -957,7 +2139,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -968,6 +2152,7 @@ gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= @@ -998,8 +2183,13 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY= k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM= k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U= @@ -1008,14 +2198,52 @@ k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= -pkg.akt.dev/go v0.0.1-rc6 h1:XALrpVAkloA0PfnjOmIfbJVjQhVz96jEMXaTdz4bUww= -pkg.akt.dev/go v0.0.1-rc6/go.mod h1:zT4oCfg4GtuicSiH65mmlt3Y7InU3f5KUvFsdLZ7IYs= +pkg.akt.dev/go v0.0.1-rc9 h1:2zBqEbE10HyWiSu7JM3k0zYwbqyPomFvImGbN04HM/A= +pkg.akt.dev/go v0.0.1-rc9/go.mod h1:nuijviJzKCbydXYxtdVCE2vvlmFCzgkxcuHV88wAJFE= pkg.akt.dev/go/sdl v0.0.1-rc6 h1:a32CL5fDMZweehEvpLc+t0oWThw/4ofY9YC2vTEcse0= pkg.akt.dev/go/sdl v0.0.1-rc6/go.mod h1:bDr0Kkziyh7CLRhPTKQbEJWs3a+m/ZvubeAGbaMbHxQ= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/go/cli/gov_prompt_test.go b/go/cli/gov_prompt_test.go new file mode 100644 index 00000000..4f6cd6bd --- /dev/null +++ b/go/cli/gov_prompt_test.go @@ -0,0 +1,87 @@ +//go:build !race + +// Disabled -race because the package github.com/manifoldco/promptui@v0.9.0 +// has a data race and this code exposes it, but fixing it would require +// holding up the associated change to this. + +package cli_test + +import ( + "fmt" + "math" + "os" + "testing" + + "github.com/chzyer/readline" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "pkg.akt.dev/go/cli" +) + +type st struct { + I int +} + +// Tests that we successfully report overflows in parsing ints +// See https://github.com/cosmos/cosmos-sdk/issues/13346 +func TestPromptIntegerOverflow(t *testing.T) { + // Intentionally sending values out of the range of int. + intOverflowers := []string{ + "-9223372036854775809", + "9223372036854775808", + "9923372036854775809", + "-9923372036854775809", + "18446744073709551616", + "-18446744073709551616", + } + + for _, intOverflower := range intOverflowers { + overflowStr := intOverflower + t.Run(overflowStr, func(t *testing.T) { + origStdin := readline.Stdin + defer func() { + readline.Stdin = origStdin + }() + + fin, fw := readline.NewFillableStdin(os.Stdin) + readline.Stdin = fin + _, _ = fw.Write([]byte(overflowStr + "\n")) + + v, err := cli.Prompt(st{}, "") + assert.Equal(t, st{}, v, "expected a value of zero") + require.NotNil(t, err, "expected a report of an overflow") + require.Contains(t, err.Error(), "range") + }) + } +} + +func TestPromptParseInteger(t *testing.T) { + // Intentionally sending a value out of the range of + values := []struct { + in string + want int + }{ + {fmt.Sprintf("%d", math.MinInt), math.MinInt}, + {"19991", 19991}, + {"991000000199", 991000000199}, + } + + for _, tc := range values { + tc := tc + t.Run(tc.in, func(t *testing.T) { + origStdin := readline.Stdin + defer func() { + readline.Stdin = origStdin + }() + + fin, fw := readline.NewFillableStdin(os.Stdin) + readline.Stdin = fin + _, _ = fw.Write([]byte(tc.in + "\n")) + + v, err := cli.Prompt(st{}, "") + assert.Nil(t, err, "expected a nil error") + assert.Equal(t, tc.want, v.I, "expected %d = %d", tc.want, v.I) + }) + } +} diff --git a/go/cli/gov_query.go b/go/cli/gov_query.go new file mode 100644 index 00000000..cea502ab --- /dev/null +++ b/go/cli/gov_query.go @@ -0,0 +1,631 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" + "github.com/cosmos/cosmos-sdk/x/gov/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + + cflags "pkg.akt.dev/go/cli/flags" + cutils "pkg.akt.dev/go/node/utils" +) + +// GetQueryGovCmd returns the cli query commands for this module +func GetQueryGovCmd() *cobra.Command { + // Group gov queries under a subcommand + govQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the governance module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + govQueryCmd.AddCommand( + GetQueryGovProposalCmd(), + GetQueryGovProposalsCmd(), + GetQueryGovVoteCmd(), + GetQueryGovVotesCmd(), + GetQueryGovQueryParamsCmd(), + GetQueryGovParamCmd(), + GetQueryGovProposerCmd(), + GetQueryGovDepositCmd(), + GetQueryGovDepositsCmd(), + GetQueryGovTallyCmd(), + ) + + return govQueryCmd +} + +// GetQueryGovProposalCmd implements the query proposal command. +func GetQueryGovProposalCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "proposal [proposal-id]", + Args: cobra.ExactArgs(1), + Short: "Query details of a single proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Query details for a proposal. You can find the +proposal-id by running "%s query gov proposals". + +Example: +$ %s query gov proposal 1 +`, + version.AppName, version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0]) + } + + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Gov().Proposal(ctx, &v1.QueryProposalRequest{ProposalId: proposalID}) + if err != nil { + return err + } + + return cl.PrintMessage(res.Proposal) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovProposalsCmd implements a query proposals command. Command to Get +// Proposals Information. +func GetQueryGovProposalsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "proposals", + Short: "Query proposals with optional filters", + Long: strings.TrimSpace( + fmt.Sprintf(`Query for a all paginated proposals that match optional filters: + +Example: +$ %s query gov proposals --depositor akash1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk +$ %s query gov proposals --voter akash1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk +$ %s query gov proposals --status (DepositPeriod|VotingPeriod|Passed|Rejected) +$ %s query gov proposals --page=2 --limit=100 +`, + version.AppName, version.AppName, version.AppName, version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + cl := MustQueryClientFromContext(ctx) + + bechDepositorAddr, _ := cmd.Flags().GetString(flagDepositor) + bechVoterAddr, _ := cmd.Flags().GetString(flagVoter) + strProposalStatus, _ := cmd.Flags().GetString(flagStatus) + + var proposalStatus v1.ProposalStatus + + if len(bechDepositorAddr) != 0 { + _, err := sdk.AccAddressFromBech32(bechDepositorAddr) + if err != nil { + return err + } + } + + if len(bechVoterAddr) != 0 { + _, err := sdk.AccAddressFromBech32(bechVoterAddr) + if err != nil { + return err + } + } + + if len(strProposalStatus) != 0 { + proposalStatus1, err := v1.ProposalStatusFromString(gcutils.NormalizeProposalStatus(strProposalStatus)) + proposalStatus = proposalStatus1 + if err != nil { + return err + } + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := cl.Query().Gov().Proposals(ctx, &v1.QueryProposalsRequest{ + ProposalStatus: proposalStatus, + Voter: bechVoterAddr, + Depositor: bechDepositorAddr, + Pagination: pageReq, + }) + + if err != nil { + return err + } + + if len(res.GetProposals()) == 0 { + return fmt.Errorf("no proposals found") + } + + return cl.PrintMessage(res) + }, + } + + cmd.Flags().String(flagDepositor, "", "(optional) filter by proposals deposited on by depositor") + cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted") + cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected") + cflags.AddPaginationFlagsToCmd(cmd, "proposals") + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovVoteCmd implements the query proposal vote command. Command to Get a +// Vote Information. +func GetQueryGovVoteCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "vote [proposal-id] [voter-addr]", + Args: cobra.ExactArgs(2), + Short: "Query details of a single vote", + Long: strings.TrimSpace( + fmt.Sprintf(`Query details for a single vote on a proposal given its identifier. + +Example: +$ %s query gov vote 1 akash1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) + } + + cl := MustQueryClientFromContext(ctx) + + // check to see if the proposal is in the store + _, err = cl.Query().Gov().Proposal( + ctx, + &v1.QueryProposalRequest{ProposalId: proposalID}, + ) + if err != nil { + return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) + } + + voterAddr, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + res, err := cl.Query().Gov().Vote( + ctx, + &v1.QueryVoteRequest{ProposalId: proposalID, Voter: args[1]}, + ) + if err != nil { + return err + } + + vote := res.GetVote() + if vote.Empty() { + params := v1.NewQueryVoteParams(proposalID, voterAddr) + resByTxQuery, err := cutils.QueryVoteByTxQuery(ctx, cl.ClientContext(), params) + if err != nil { + return err + } + + if err := cl.ClientContext().Codec.UnmarshalJSON(resByTxQuery, vote); err != nil { + return err + } + } + + return cl.PrintMessage(res.Vote) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovVotesCmd implements the command to query for proposal votes. +func GetQueryGovVotesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "votes [proposal-id]", + Args: cobra.ExactArgs(1), + Short: "Query votes on a proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Query vote details for a single proposal by its identifier. + +Example: +$ %[1]s query gov votes 1 +$ %[1]s query gov votes 1 --page=2 --limit=100 +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + cctx := cl.ClientContext() + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) + } + + // check to see if the proposal is in the store + proposalRes, err := cl.Query().Gov().Proposal( + ctx, + &v1.QueryProposalRequest{ProposalId: proposalID}, + ) + if err != nil { + return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) + } + + propStatus := proposalRes.GetProposal().Status + if !(propStatus == v1.StatusVotingPeriod || propStatus == v1.StatusDepositPeriod) { + page, _ := cmd.Flags().GetInt(cflags.FlagPage) + limit, _ := cmd.Flags().GetInt(cflags.FlagLimit) + + params := v1.NewQueryProposalVotesParams(proposalID, page, limit) + resByTxQuery, err := cutils.QueryVotesByTxQuery(ctx, cctx, params) + if err != nil { + return err + } + + var votes v1.Votes + // TODO migrate to use JSONCodec (implement MarshalJSONArray + // or wrap lists of proto.Message in some other message) + cctx.LegacyAmino.MustUnmarshalJSON(resByTxQuery, &votes) + return cctx.PrintObjectLegacy(votes) + + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := cl.Query().Gov().Votes( + ctx, + &v1.QueryVotesRequest{ProposalId: proposalID, Pagination: pageReq}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddPaginationFlagsToCmd(cmd, "votes") + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovDepositCmd implements the query proposal deposit command. Command to +// get a specific Deposit Information. +func GetQueryGovDepositCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "deposit [proposal-id] [depositer-addr]", + Args: cobra.ExactArgs(2), + Short: "Query details of a deposit", + Long: strings.TrimSpace( + fmt.Sprintf(`Query details for a single proposal deposit on a proposal by its identifier. + +Example: +$ %s query gov deposit 1 akash1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0]) + } + + // check to see if the proposal is in the store + _, err = cl.Query().Gov().Proposal( + ctx, + &v1.QueryProposalRequest{ProposalId: proposalID}, + ) + if err != nil { + return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) + } + + res, err := cl.Query().Gov().Deposit( + ctx, + &v1.QueryDepositRequest{ProposalId: proposalID, Depositor: args[1]}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res.Deposit) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovDepositsCmd implements the command to query for proposal deposits. +func GetQueryGovDepositsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "deposits [proposal-id]", + Args: cobra.ExactArgs(1), + Short: "Query deposits on a proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Query details for all deposits on a proposal. +You can find the proposal-id by running "%s query gov proposals". + +Example: +$ %s query gov deposits 1 +`, + version.AppName, version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0]) + } + + // check to see if the proposal is in the store + _, err = cl.Query().Gov().Proposal( + ctx, + &v1.QueryProposalRequest{ProposalId: proposalID}, + ) + if err != nil { + return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + res, err := cl.Query().Gov().Deposits( + ctx, + &v1.QueryDepositsRequest{ProposalId: proposalID, Pagination: pageReq}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddPaginationFlagsToCmd(cmd, "deposits") + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovTallyCmd implements the command to query for proposal tally result. +func GetQueryGovTallyCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "tally [proposal-id]", + Args: cobra.ExactArgs(1), + Short: "Get the tally of a proposal vote", + Long: strings.TrimSpace( + fmt.Sprintf(`Query tally of votes on a proposal. You can find +the proposal-id by running "%s query gov proposals". + +Example: +$ %s query gov tally 1 +`, + version.AppName, version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) + } + + // check to see if the proposal is in the store + _, err = cl.Query().Gov().Proposal( + ctx, + &v1.QueryProposalRequest{ProposalId: proposalID}, + ) + if err != nil { + return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) + } + + // Query store + res, err := cl.Query().Gov().TallyResult( + ctx, + &v1.QueryTallyResultRequest{ProposalId: proposalID}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res.Tally) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovQueryParamsCmd implements the query params command. +// +// nolint:staticcheck // this function contains deprecated commands that we need. +func GetQueryGovQueryParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the parameters of the governance process", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the all the parameters for the governance process. + +Example: +$ %s query gov params +`, + version.AppName, + ), + ), + Args: cobra.NoArgs, + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + // Query store for all 3 params + res, err := cl.Query().Gov().Params( + ctx, + &v1.QueryParamsRequest{ParamsType: "deposit"}, + ) + if err != nil { + return err + } + + vp := v1.NewVotingParams(res.Params.VotingPeriod) + res.VotingParams = &vp + + tp := v1.NewTallyParams(res.Params.Quorum, res.Params.Threshold, res.Params.VetoThreshold) + res.TallyParams = &tp + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovParamCmd implements the query param command. +func GetQueryGovParamCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "param [param-type]", + Args: cobra.ExactArgs(1), + Short: "Query the parameters (voting|tallying|deposit) of the governance process", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the all the parameters for the governance process. +Example: +$ %s query gov param voting +$ %s query gov param tallying +$ %s query gov param deposit +`, + version.AppName, version.AppName, version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + // Query store + res, err := cl.Query().Gov().Params( + cmd.Context(), + &v1.QueryParamsRequest{ParamsType: args[0]}, + ) + if err != nil { + return err + } + + var out fmt.Stringer + //nolint:staticcheck // this switch statement contains deprecated commands that we need. + switch args[0] { + case "voting": + out = res.GetVotingParams() + case "tallying": + out = res.GetTallyParams() + case "deposit": + out = res.GetDepositParams() + default: + return fmt.Errorf("argument must be one of (voting|tallying|deposit), was %s", args[0]) + } + + return cl.PrintMessage(out) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryGovProposerCmd implements the query proposer command. +func GetQueryGovProposerCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "proposer [proposal-id]", + Args: cobra.ExactArgs(1), + Short: "Query the proposer of a governance proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Query which address proposed a proposal with a given ID. + +Example: +$ %s query gov proposer 1 +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + cctx := cl.ClientContext() + + // validate that the proposalID is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s is not a valid uint", args[0]) + } + + prop, err := cutils.QueryProposerByTxQuery(ctx, cctx, proposalID) + if err != nil { + return err + } + + return cctx.PrintObjectLegacy(prop) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/gov_query_test.go b/go/cli/gov_query_test.go new file mode 100644 index 00000000..2c890d93 --- /dev/null +++ b/go/cli/gov_query_test.go @@ -0,0 +1,359 @@ +package cli_test + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/testutil" + + "pkg.akt.dev/go/cli" + cflags "pkg.akt.dev/go/cli/flags" +) + +func (s *GovCLITestSuite) TestCmdParams() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "json output", + []string{fmt.Sprintf("--%s=json", cflags.FlagOutput)}, + "--output=json", + }, + { + "text output", + cli.TestFlags(). + WithOutputText(), + "--output=text", + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryGovParamCmd() + cmd.SetArgs(tc.args) + + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdParam() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "voting params", + cli.TestFlags(). + With("voting"). + WithOutputJSON(), + `voting --output=json`, + }, + { + "tally params", + cli.TestFlags(). + With("tallying"). + WithOutputJSON(), + `tallying --output=json`, + }, + { + "deposit params", + cli.TestFlags(). + With("deposit"). + WithOutputJSON(), + `deposit --output=json`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryGovParamCmd() + cmd.SetArgs(tc.args) + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdProposer() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "without proposal id", + cli.TestFlags(). + WithOutputJSON(), + "--output=json", + }, + { + "with proposal id", + cli.TestFlags(). + With("1"). + WithOutputJSON(), + "1 --output=json", + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryGovProposerCmd() + cmd.SetArgs(tc.args) + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdTally() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "without proposal id", + cli.TestFlags(). + WithOutputJSON(), + "--output=json", + }, + { + "with proposal id (json output)", + cli.TestFlags(). + With("2"). + WithOutputJSON(), + "2 --output=json", + }, + { + "with proposal id (text output)", + cli.TestFlags(). + With("1"). + WithOutputText(), + "1 --output=text", + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryGovTallyCmd() + cmd.SetArgs(tc.args) + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdGetProposal() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "get proposal with json response", + cli.TestFlags(). + With("1"). + WithOutputJSON(), + "1 --output=json", + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryGovProposalCmd() + cmd.SetArgs(tc.args) + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdGetProposals() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "get proposals as json response", + cli.TestFlags(). + WithOutputJSON(), + "--output=json", + }, + { + "get proposals with invalid status", + cli.TestFlags(). + WithStatus("unknown"). + WithOutputJSON(), + "--status=unknown --output=json", + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetQueryGovProposalsCmd() + cmd.SetArgs(tc.args) + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdQueryDeposits() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "get deposits", + cli.TestFlags(). + With("10"), + "10", + }, + { + "get deposits(json output)", + cli.TestFlags(). + With("1"). + WithOutputJSON(), + "1 --output=json", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetQueryGovDepositsCmd() + cmd.SetArgs(tc.args) + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdQueryDeposit() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "get deposit with no depositor", + cli.TestFlags(). + With("1"), + "1", + }, + { + "get deposit with wrong deposit address", + cli.TestFlags(). + With("1", "wrong address"), + "1 wrong address", + }, + { + "get deposit (valid req)", + cli.TestFlags(). + With("1", val[0].Address.String()). + WithOutputJSON(), + fmt.Sprintf("1 %s --output=json", val[0].Address.String()), + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetQueryGovDepositCmd() + cmd.SetArgs(tc.args) + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdQueryVotes() { + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "get votes with no proposal id", + []string{}, + "", + }, + { + "get votes of a proposal", + cli.TestFlags(). + With("10"), + "10", + }, + { + "get votes of a proposal (json output)", + cli.TestFlags(). + With("1"). + WithOutputJSON(), + "1 --output=json", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetQueryGovVotesCmd() + cmd.SetArgs(tc.args) + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + }) + } +} + +func (s *GovCLITestSuite) TestCmdQueryVote() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + testCases := []struct { + name string + args []string + expCmdOutput string + }{ + { + "get vote of a proposal", + cli.TestFlags(). + With("10", val[0].Address.String()), + fmt.Sprintf("10 %s", val[0].Address.String()), + }, + { + "get vote by wrong voter", + cli.TestFlags(). + With("1", "wrong address"), + "1 wrong address", + }, + { + "get vote of a proposal (json output)", + cli.TestFlags(). + With("1", val[0].Address.String()). + WithOutputJSON(), + fmt.Sprintf("1 %s --output=json", val[0].Address.String()), + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetQueryGovVoteCmd() + cmd.SetArgs(tc.args) + + if len(tc.args) != 0 { + s.Require().Contains(fmt.Sprint(cmd), strings.TrimSpace(tc.expCmdOutput)) + } + }) + } +} diff --git a/go/cli/gov_suite_test.go b/go/cli/gov_suite_test.go new file mode 100644 index 00000000..25b090d5 --- /dev/null +++ b/go/cli/gov_suite_test.go @@ -0,0 +1,77 @@ +package cli_test + +import ( + "bytes" + "fmt" + "io" + + abci "github.com/cometbft/cometbft/abci/types" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + govclitestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +type GovCLITestSuite struct { + CLITestSuite +} + +func (s *GovCLITestSuite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(gov.AppModuleBasic{}) + s.kr = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithLegacyAmino(s.encCfg.Amino). + WithClient(clitestutil.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID("test-chain") + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := clitestutil.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + } + s.cctx = ctxGen().WithOutput(&outBuf) + + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + // create a proposal with deposit + _, err := govclitestutil.MsgSubmitLegacyProposal(s.cctx, val[0].Address.String(), + "Text Proposal 1", "Where is the title!?", v1beta1.ProposalTypeText, + fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin("uakt", v1.DefaultMinDepositTokens).String())) + s.Require().NoError(err) + + // vote for proposal + _, err = govclitestutil.MsgVote(s.cctx, val[0].Address.String(), "1", "yes") + s.Require().NoError(err) + + // create a proposal without deposit + _, err = govclitestutil.MsgSubmitLegacyProposal(s.cctx, val[0].Address.String(), + "Text Proposal 2", "Where is the title!?", v1beta1.ProposalTypeText) + s.Require().NoError(err) + + // create a proposal3 with deposit + _, err = govclitestutil.MsgSubmitLegacyProposal(s.cctx, val[0].Address.String(), + "Text Proposal 3", "Where is the title!?", v1beta1.ProposalTypeText, + fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin("uakt", v1.DefaultMinDepositTokens).String())) + s.Require().NoError(err) + + // vote for proposal3 as val + _, err = govclitestutil.MsgVote(s.cctx, val[0].Address.String(), "3", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05") + s.Require().NoError(err) +} + diff --git a/go/cli/gov_tx.go b/go/cli/gov_tx.go new file mode 100644 index 00000000..8795cf10 --- /dev/null +++ b/go/cli/gov_tx.go @@ -0,0 +1,838 @@ +package cli + +import ( + "encoding/json" + "fmt" + "os" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/manifoldco/promptui" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" + "github.com/cosmos/cosmos-sdk/x/gov/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// Proposal flags +const ( + flagVoter = "voter" + flagDepositor = "depositor" + flagStatus = "status" +) + +// ProposalFlags defines the core required fields of a legacy proposal. It is used to +// verify that these values are not provided in conjunction with a JSON proposal +// file. +var ProposalFlags = []string{ + cflags.FlagTitle, + cflags.FlagDescription, + cflags.FlagProposalType, + cflags.FlagDeposit, +} + +const ( + proposalText = "text" + proposalOther = "other" + draftProposalFileName = "draft_proposal.json" + draftMetadataFileName = "draft_metadata.json" +) + +var suggestedProposalTypes = []proposalType{ + { + Name: proposalText, + MsgType: "", // no message for text proposal + }, + { + Name: "community-pool-spend", + MsgType: "/cosmos.distribution.v1beta1.MsgCommunityPoolSpend", + }, + { + Name: "software-upgrade", + MsgType: "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade", + }, + { + Name: "cancel-software-upgrade", + MsgType: "/cosmos.upgrade.v1beta1.MsgCancelUpgrade", + }, + { + Name: proposalOther, + MsgType: "", // user will input the message type + }, +} + +type proposalType struct { + Name string + MsgType string + Msg sdk.Msg +} + +// proposal defines the new Msg-based proposal. +type proposalMsg struct { + // Msgs defines an array of sdk.Msgs proto-JSON-encoded as Anys. + Messages []json.RawMessage `json:"messages,omitempty"` + Metadata string `json:"metadata"` + Deposit string `json:"deposit"` + Title string `json:"title"` + Summary string `json:"summary"` + Expedited bool `json:"expedited"` +} + +type legacyProposal struct { + Title string + Description string + Type string + Deposit string +} + +// GetTxGovCmd returns the transaction commands for this module +// governance ModuleClient is slightly different from other ModuleClients in that +// it contains a slice of legacy "proposal" child commands. These commands are respective +// to the proposal type handlers that are implemented in other modules but are mounted +// under the governance CLI (eg. parameter change proposals). +func GetTxGovCmd(legacyPropCmds []*cobra.Command) *cobra.Command { + govTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Governance transactions subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmdSubmitLegacyProp := GetTxGovSubmitLegacyProposalCmd() + for _, propCmd := range legacyPropCmds { + cflags.AddTxFlagsToCmd(propCmd) + cmdSubmitLegacyProp.AddCommand(propCmd) + } + + govTxCmd.AddCommand( + GetTxGovDepositCmd(), + GetTxGovVoteCmd(), + GetTxGovWeightedVoteCmd(), + GetTxGovSubmitProposalCmd(), + GetTxGovDraftProposalCmd(), + + // Deprecated + cmdSubmitLegacyProp, + ) + + return govTxCmd +} + +// GetTxGovSubmitProposalCmd implements submitting a proposal transaction command. +func GetTxGovSubmitProposalCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "submit-proposal [path/to/proposal.json]", + Short: "Submit a proposal along with some messages, metadata and deposit", + Args: cobra.ExactArgs(1), + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a proposal along with some messages, metadata and deposit. +They should be defined in a JSON file. + +Example: +$ %s tx gov submit-proposal path/to/proposal.json + +Where proposal.json contains: + +{ + // array of proto-JSON-encoded sdk.Msgs + "messages": [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + "from_address": "cosmos1...", + "to_address": "cosmos1...", + "amount":[{"denom": "uakt","amount": "10"}] + } + ], + // metadata can be any of base64 encoded, raw text, stringified json, IPFS link to json + // see below for example metadata + "metadata": "4pIMOgIGx1vZGU=", + "deposit": "10stake", + "title": "My proposal", + "summary": "A short summary of my proposal" + "expedited": false +} + +metadata example: +{ + "title": "", + "authors": [""], + "summary": "", + "details": "", + "proposal_forum_url": "", + "vote_option_context": "", +} +`, + version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + proposal, msgs, deposit, err := parseSubmitProposal(cctx.Codec, args[0]) + if err != nil { + return err + } + + msg, err := v1.NewMsgSubmitProposal( + msgs, + deposit, + cctx.GetFromAddress().String(), + proposal.Metadata, + proposal.Title, + proposal.Summary, + proposal.Expedited, + ) + if err != nil { + return fmt.Errorf("invalid message: %w", err) + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxGovSubmitLegacyProposalCmd implements submitting a proposal transaction command. +// Deprecated: please use GetTxGovSubmitProposalCmd instead. +func GetTxGovSubmitLegacyProposalCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "submit-legacy-proposal", + Short: "Submit a legacy proposal along with an initial deposit", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a legacy proposal along with an initial deposit. +Proposal title, description, type and deposit can be given directly or through a proposal JSON file. + +Example: +$ %s tx gov submit-legacy-proposal --proposal="path/to/proposal.json" --from mykey + +Where proposal.json contains: + +{ + "title": "Test Proposal", + "description": "My awesome proposal", + "type": "Text", + "deposit": "10test" +} + +Which is equivalent to: + +$ %s tx gov submit-legacy-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="10test" --from mykey +`, + version.AppName, version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + proposal, err := parseSubmitLegacyProposal(cmd.Flags()) + if err != nil { + return fmt.Errorf("failed to parse proposal: %w", err) + } + + amount, err := sdk.ParseCoinsNormalized(proposal.Deposit) + if err != nil { + return err + } + + content, ok := v1beta1.ContentFromProposalType(proposal.Title, proposal.Description, proposal.Type) + if !ok { + return fmt.Errorf("failed to create proposal content: unknown proposal type %s", proposal.Type) + } + + msg, err := v1beta1.NewMsgSubmitProposal(content, amount, cctx.GetFromAddress()) + if err != nil { + return fmt.Errorf("invalid message: %w", err) + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cmd.Flags().String(cflags.FlagTitle, "", "The proposal title") + cmd.Flags().String(cflags.FlagDescription, "", "The proposal description") + cmd.Flags().String(cflags.FlagProposalType, "", "The proposal Type") + cmd.Flags().String(cflags.FlagDeposit, "", "The proposal deposit") + cmd.Flags().String(cflags.FlagProposal, "", "Proposal file path (if this path is given, other proposal flags are ignored)") + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxGovDepositCmd implements depositing tokens for an active proposal. +func GetTxGovDepositCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "deposit [proposal-id] [deposit]", + Args: cobra.ExactArgs(2), + Short: "Deposit tokens for an active proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a deposit for an active proposal. You can +find the proposal-id by running "%s query gov proposals". + +Example: +$ %s tx gov deposit 1 10stake --from mykey +`, + version.AppName, version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0]) + } + + // Get depositor address + from := cctx.GetFromAddress() + + // Get amount of coins + amount, err := sdk.ParseCoinsNormalized(args[1]) + if err != nil { + return err + } + + msg := v1.NewMsgDeposit(from, proposalID, amount) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxGovVoteCmd implements creating a new vote command. +func GetTxGovVoteCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "vote [proposal-id] [option]", + Args: cobra.ExactArgs(2), + Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a vote for an active proposal. You can +find the proposal-id by running "%s query gov proposals". + +Example: +$ %s tx gov vote 1 yes --from mykey +`, + version.AppName, version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + // Get voting address + from := cctx.GetFromAddress() + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) + } + + // Find out which vote option user chose + byteVoteOption, err := v1.VoteOptionFromString(govutils.NormalizeVoteOption(args[1])) + if err != nil { + return err + } + + metadata, err := cmd.Flags().GetString(cflags.FlagMetadata) + if err != nil { + return err + } + + // Build vote message and run basic validation + msg := v1.NewMsgVote(from, proposalID, byteVoteOption, metadata) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cmd.Flags().String(cflags.FlagMetadata, "", "Specify metadata of the vote") + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxGovWeightedVoteCmd implements creating a new weighted vote command. +func GetTxGovWeightedVoteCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "weighted-vote [proposal-id] [weighted-options]", + Args: cobra.ExactArgs(2), + Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a vote for an active proposal. You can +find the proposal-id by running "%s query gov proposals". + +Example: +$ %s tx gov weighted-vote 1 yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05 --from mykey +`, + version.AppName, version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + // Get voter address + from := cctx.GetFromAddress() + + // validate that the proposal id is a uint + proposalID, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) + } + + // Figure out which vote options user chose + options, err := v1.WeightedVoteOptionsFromString(govutils.NormalizeWeightedVoteOptions(args[1])) + if err != nil { + return err + } + + metadata, err := cmd.Flags().GetString(cflags.FlagMetadata) + if err != nil { + return err + } + + // Build vote message and run basic validation + msg := v1.NewMsgVoteWeighted(from, proposalID, options, metadata) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cmd.Flags().String(cflags.FlagMetadata, "", "Specify metadata of the weighted vote") + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxGovDraftProposalCmd let a user generate a draft proposal. +func GetTxGovDraftProposalCmd() *cobra.Command { + flagSkipMetadata := "skip-metadata" + + cmd := &cobra.Command{ + Use: "draft-proposal", + Short: "Generate a draft proposal json file. The generated proposal json contains only one message (skeleton).", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + // prompt proposal type + proposalTypesPrompt := promptui.Select{ + Label: "Select proposal type", + Items: getProposalSuggestions(), + } + + _, selectedProposalType, err := proposalTypesPrompt.Run() + if err != nil { + return fmt.Errorf("failed to prompt proposal types: %w", err) + } + + var proposal proposalType + for _, p := range suggestedProposalTypes { + if strings.EqualFold(p.Name, selectedProposalType) { + proposal = p + break + } + } + + // create any proposal type + if proposal.Name == proposalOther { + // prompt proposal type + msgPrompt := promptui.Select{ + Label: "Select proposal message type:", + Items: func() []string { + msgs := clientCtx.InterfaceRegistry.ListImplementations(sdk.MsgInterfaceProtoName) + sort.Strings(msgs) + return msgs + }(), + } + + _, result, err := msgPrompt.Run() + if err != nil { + return fmt.Errorf("failed to prompt proposal types: %w", err) + } + + proposal.MsgType = result + } + + if proposal.MsgType != "" { + proposal.Msg, err = sdk.GetMsgFromTypeURL(clientCtx.Codec, proposal.MsgType) + if err != nil { + // should never happen + panic(err) + } + } + + skipMetadataPrompt, _ := cmd.Flags().GetBool(flagSkipMetadata) + + result, metadata, err := proposal.Prompt(clientCtx.Codec, skipMetadataPrompt) + if err != nil { + return err + } + + if err := writeFile(draftProposalFileName, result); err != nil { + return err + } + + if !skipMetadataPrompt { + if err := writeFile(draftMetadataFileName, metadata); err != nil { + return err + } + } + + cmd.Println("The draft proposal has successfully been generated.\nProposals should contain off-chain metadata, please upload the metadata JSON to IPFS.\nThen, replace the generated metadata field with the IPFS CID.") + + return nil + }, + } + + cflags.AddTxFlagsToCmd(cmd) + cmd.Flags().Bool(flagSkipMetadata, false, "skip metadata prompt") + + return cmd +} + +// getProposalSuggestions suggests a list of proposal types +func getProposalSuggestions() []string { + types := make([]string, len(suggestedProposalTypes)) + for i, p := range suggestedProposalTypes { + types[i] = p.Name + } + return types +} + +// Prompt the proposal type values and return the proposal and its metadata +func (p *proposalType) Prompt(cdc codec.Codec, skipMetadata bool) (*proposalMsg, types.ProposalMetadata, error) { + metadata, err := PromptMetadata(skipMetadata) + if err != nil { + return nil, metadata, fmt.Errorf("failed to set proposal metadata: %w", err) + } + + proposal := &proposalMsg{ + Metadata: "ipfs://CID", // the metadata must be saved on IPFS, set placeholder + Title: metadata.Title, + Summary: metadata.Summary, + } + + // set deposit + depositPrompt := promptui.Prompt{ + Label: "Enter proposal deposit", + Validate: client.ValidatePromptCoins, + } + proposal.Deposit, err = depositPrompt.Run() + if err != nil { + return nil, metadata, fmt.Errorf("failed to set proposal deposit: %w", err) + } + + if p.Msg == nil { + return proposal, metadata, nil + } + + // set messages field + result, err := Prompt(p.Msg, "msg") + if err != nil { + return nil, metadata, fmt.Errorf("failed to set proposal message: %w", err) + } + + message, err := cdc.MarshalInterfaceJSON(result) + if err != nil { + return nil, metadata, fmt.Errorf("failed to marshal proposal message: %w", err) + } + proposal.Messages = append(proposal.Messages, message) + + return proposal, metadata, nil +} + +// Prompt prompts the user for all values of the given type. +// data is the struct to be filled +// namePrefix is the name to be displayed as "Enter " +func Prompt[T any](data T, namePrefix string) (T, error) { + v := reflect.ValueOf(&data).Elem() + if v.Kind() == reflect.Interface { + v = reflect.ValueOf(data) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + } + + for i := 0; i < v.NumField(); i++ { + // if the field is a struct skip or not slice of string or int then skip + switch v.Field(i).Kind() { + case reflect.Struct: + // TODO(@julienrbrt) in the future we can add a recursive call to Prompt + continue + case reflect.Slice: + if v.Field(i).Type().Elem().Kind() != reflect.String && v.Field(i).Type().Elem().Kind() != reflect.Int { + continue + } + } + + // create prompts + prompt := promptui.Prompt{ + Label: fmt.Sprintf("Enter %s %s", namePrefix, strings.ToLower(client.CamelCaseToString(v.Type().Field(i).Name))), + Validate: client.ValidatePromptNotEmpty, + } + + fieldName := strings.ToLower(v.Type().Field(i).Name) + + if strings.EqualFold(fieldName, "authority") { + // pre-fill with gov address + prompt.Default = authtypes.NewModuleAddress(types.ModuleName).String() + prompt.Validate = client.ValidatePromptAddress + } + + // TODO(@julienrbrt) use scalar annotation instead of dumb string name matching + if strings.Contains(fieldName, "addr") || + strings.Contains(fieldName, "sender") || + strings.Contains(fieldName, "voter") || + strings.Contains(fieldName, "depositor") || + strings.Contains(fieldName, "granter") || + strings.Contains(fieldName, "grantee") || + strings.Contains(fieldName, "recipient") { + prompt.Validate = client.ValidatePromptAddress + } + + result, err := prompt.Run() + if err != nil { + return data, fmt.Errorf("failed to prompt for %s: %w", fieldName, err) + } + + switch v.Field(i).Kind() { + case reflect.String: + v.Field(i).SetString(result) + case reflect.Int: + resultInt, err := strconv.ParseInt(result, 10, 0) + if err != nil { + return data, fmt.Errorf("invalid value for int: %w", err) + } + // If a value was successfully parsed the ranges of: + // [minInt, maxInt] + // are within the ranges of: + // [minInt64, maxInt64] + // of which on 64-bit machines, which are most common, + // int==int64 + v.Field(i).SetInt(resultInt) + case reflect.Slice: + switch v.Field(i).Type().Elem().Kind() { + case reflect.String: + v.Field(i).Set(reflect.ValueOf([]string{result})) + case reflect.Int: + resultInt, err := strconv.ParseInt(result, 10, 0) + if err != nil { + return data, fmt.Errorf("invalid value for int: %w", err) + } + + v.Field(i).Set(reflect.ValueOf([]int{int(resultInt)})) + } + default: + // skip any other types + continue + } + } + + return data, nil +} + +// PromptMetadata prompts for proposal metadata or only title and summary if skip is true +func PromptMetadata(skip bool) (types.ProposalMetadata, error) { + var ( + metadata types.ProposalMetadata + err error + ) + + if !skip { + metadata, err = Prompt(types.ProposalMetadata{}, "proposal") + if err != nil { + return metadata, fmt.Errorf("failed to set proposal metadata: %w", err) + } + } else { + // prompt for title and summary + titlePrompt := promptui.Prompt{ + Label: "Enter proposal title", + Validate: client.ValidatePromptNotEmpty, + } + + metadata.Title, err = titlePrompt.Run() + if err != nil { + return metadata, fmt.Errorf("failed to set proposal title: %w", err) + } + + summaryPrompt := promptui.Prompt{ + Label: "Enter proposal summary", + Validate: client.ValidatePromptNotEmpty, + } + + metadata.Summary, err = summaryPrompt.Run() + if err != nil { + return metadata, fmt.Errorf("failed to set proposal summary: %w", err) + } + } + + return metadata, nil +} + +// writeFile writes the input to the file +func writeFile(fileName string, input any) error { + raw, err := json.MarshalIndent(input, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal proposal: %w", err) + } + + if err := os.WriteFile(fileName, raw, 0o600); err != nil { + return err + } + + return nil +} + +// parseSubmitProposal reads and parses the proposal. +func parseSubmitProposal(cdc codec.Codec, path string) (proposalMsg, []sdk.Msg, sdk.Coins, error) { + var proposal proposalMsg + + contents, err := os.ReadFile(path) + if err != nil { + return proposal, nil, nil, err + } + + err = json.Unmarshal(contents, &proposal) + if err != nil { + return proposal, nil, nil, err + } + + msgs := make([]sdk.Msg, len(proposal.Messages)) + for i, anyJSON := range proposal.Messages { + var msg sdk.Msg + err := cdc.UnmarshalInterfaceJSON(anyJSON, &msg) + if err != nil { + return proposal, nil, nil, err + } + + msgs[i] = msg + } + + deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit) + if err != nil { + return proposal, nil, nil, err + } + + return proposal, msgs, deposit, nil +} + +// parseSubmitLegacyProposal reads and parses the legacy proposal. +func parseSubmitLegacyProposal(fs *pflag.FlagSet) (*legacyProposal, error) { + proposal := &legacyProposal{} + proposalFile, _ := fs.GetString(cflags.FlagProposal) + + if proposalFile == "" { + proposalType, _ := fs.GetString(cflags.FlagProposalType) + proposal.Title, _ = fs.GetString(cflags.FlagTitle) + proposal.Description, _ = fs.GetString(cflags.FlagDescription) + proposal.Type = govutils.NormalizeProposalType(proposalType) + proposal.Deposit, _ = fs.GetString(cflags.FlagDeposit) + if err := proposal.validate(); err != nil { + return nil, err + } + + return proposal, nil + } + + for _, flag := range ProposalFlags { + if v, _ := fs.GetString(flag); v != "" { + return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag) + } + } + + contents, err := os.ReadFile(proposalFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(contents, proposal) + if err != nil { + return nil, err + } + + if err := proposal.validate(); err != nil { + return nil, err + } + + return proposal, nil +} + +// validate the legacyProposal +func (p legacyProposal) validate() error { + if p.Type == "" { + return fmt.Errorf("proposal type is required") + } + + if p.Title == "" { + return fmt.Errorf("proposal title is required") + } + + if p.Description == "" { + return fmt.Errorf("proposal description is required") + } + return nil +} diff --git a/go/cli/gov_tx_test.go b/go/cli/gov_tx_test.go new file mode 100644 index 00000000..a6585bce --- /dev/null +++ b/go/cli/gov_tx_test.go @@ -0,0 +1,417 @@ +package cli_test + +import ( + "context" + "encoding/base64" + "fmt" + + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/gogoproto/proto" + + "pkg.akt.dev/go/cli" + clitestutil "pkg.akt.dev/go/cli/testutil" +) + +func (s *GovCLITestSuite) TestNewCmdSubmitProposal() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + // Create a legacy proposal JSON, make sure it doesn't pass this new CLI + // command. + invalidProp := `{ + "title": "", + "description": "Where is the title!?", + "type": "Text", + "deposit": "-324foocoin" + }` + invalidPropFile := testutil.WriteToNewTempFile(s.T(), invalidProp) + defer func() { + _ = invalidPropFile.Close() + }() + + // Create a valid new proposal JSON. + propMetadata := []byte{42} + validProp := fmt.Sprintf(` + { + "messages": [ + { + "@type": "/cosmos.gov.v1.MsgExecLegacyContent", + "authority": "%s", + "content": { + "@type": "/cosmos.gov.v1beta1.TextProposal", + "title": "My awesome title", + "description": "My awesome description" + } + } + ], + "title": "My awesome title", + "summary": "My awesome description", + "metadata": "%s", + "deposit": "%s" + }`, authtypes.NewModuleAddress(types.ModuleName), base64.StdEncoding.EncodeToString(propMetadata), sdk.NewCoin("uakt", sdk.NewInt(5431))) + validPropFile := testutil.WriteToNewTempFile(s.T(), validProp) + + defer func() { + _ = validPropFile.Close() + }() + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + }{ + { + "invalid proposal", + cli.TestFlags(). + With(invalidPropFile.Name()). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + true, nil, + }, + { + "valid proposal", + cli.TestFlags(). + With(validPropFile.Name()). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetTxGovSubmitProposalCmd() + + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *GovCLITestSuite) TestNewCmdSubmitLegacyProposal() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + invalidProp := `{ + "title": "", + "description": "Where is the title!?", + "type": "Text", + "deposit": "-324foocoin" + }` + invalidPropFile := testutil.WriteToNewTempFile(s.T(), invalidProp) + + defer func() { + _ = invalidPropFile.Close() + }() + + validProp := fmt.Sprintf(`{ + "title": "Text Proposal", + "description": "Hello, World!", + "type": "Text", + "deposit": "%s" + }`, sdk.NewCoin("uakt", sdk.NewInt(5431))) + validPropFile := testutil.WriteToNewTempFile(s.T(), validProp) + defer func() { + _ = validPropFile.Close() + }() + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + }{ + { + "invalid proposal (file)", + cli.TestFlags(). + WithProposal(invalidPropFile.Name()). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + true, nil, + }, + { + "invalid proposal", + cli.TestFlags(). + WithDescription("Where is the title!?"). + WithProposalType(v1beta1.ProposalTypeText). + WithDeposit(sdk.NewCoin("uakt", sdk.NewInt(5431))). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + true, nil, + }, + { + "valid transaction (file)", + cli.TestFlags(). + WithProposal(validPropFile.Name()). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, &sdk.TxResponse{}, + }, + { + "valid transaction", + cli.TestFlags(). + WithTitle("Text Proposal"). + WithDescription("'Where is the title!?"). + WithProposalType(v1beta1.ProposalTypeText). + WithDeposit(sdk.NewCoin("uakt", sdk.NewInt(5431))). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetTxGovSubmitLegacyProposalCmd() + + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *GovCLITestSuite) TestNewCmdDeposit() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "without proposal id", + cli.TestFlags(). + With(sdk.NewCoin("uakt", sdk.NewInt(10)).String()). // 10uakt + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + true, + }, + { + "without deposit amount", + cli.TestFlags(). + With("1"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + true, + }, + { + "deposit on a proposal", + cli.TestFlags(). + With("1", sdk.NewCoin("uakt", sdk.NewInt(10)).String()). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, + }, + } + + for _, tc := range testCases { + tc := tc + var resp sdk.TxResponse + + s.Run(tc.name, func() { + cmd := cli.GetTxGovDepositCmd() + + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String()) + } + }) + } +} + +func (s *GovCLITestSuite) TestNewCmdVote() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + }{ + { + "invalid vote", + []string{}, + true, 0, + }, + { + "vote for invalid proposal", + cli.TestFlags(). + With("10", "yes"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithMetadata("AQ=="). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, 3, + }, + { + "valid vote", + cli.TestFlags(). + With("1", "yes"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, 0, + }, + { + "valid vote with metadata", + cli.TestFlags(). + With("1", "yes"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithMetadata("AQ=="). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, 0, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetTxGovVoteCmd() + var txResp sdk.TxResponse + + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &txResp), out.String()) + } + }) + } +} + +func (s *GovCLITestSuite) TestNewCmdWeightedVote() { + val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + }{ + { + "invalid vote", + []string{}, + true, 0, + }, + { + "vote for invalid proposal", + cli.TestFlags(). + With("10", "yes"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithMetadata("AQ=="). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, 3, + }, + { + "valid vote", + cli.TestFlags(). + With("1", "yes"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, 0, + }, + { + "valid vote with metadata", + cli.TestFlags(). + With("1", "yes"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, 0, + }, + { + "invalid valid split vote string", + cli.TestFlags(). + With("1", "yes/0.6,no/0.3,abstain/0.05,no_with_veto/0.05"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + true, 0, + }, + { + "valid split vote", + cli.TestFlags(). + With("1", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05"). + WithFrom(val[0].Address.String()). + WithSkipConfirm(). + WithBroadcastModeSync(). + WithSignMode("direct"). + WithFees(sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10)))), + false, 0, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetTxGovWeightedVoteCmd() + var txResp sdk.TxResponse + + out, err := clitestutil.ExecTestCLICmd(context.Background(), s.cctx, cmd, tc.args...) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(s.cctx.Codec.UnmarshalJSON(out.Bytes(), &txResp), out.String()) + } + }) + } +} diff --git a/go/cli/gov_util.go b/go/cli/gov_util.go new file mode 100644 index 00000000..d24110b3 --- /dev/null +++ b/go/cli/gov_util.go @@ -0,0 +1,62 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// AddGovPropFlagsToCmd adds flags for defining MsgSubmitProposal fields. +// +// See also ReadGovPropFlags. +func AddGovPropFlagsToCmd(cmd *cobra.Command) { + cmd.Flags().String(cflags.FlagDeposit, "", "The deposit to include with the governance proposal") + cmd.Flags().String(cflags.FlagMetadata, "", "The metadata to include with the governance proposal") + cmd.Flags().String(cflags.FlagTitle, "", "The title to put on the governance proposal") + cmd.Flags().String(cflags.FlagSummary, "", "The summary to include with the governance proposal") +} + +// ReadGovPropFlags parses a MsgSubmitProposal from the provided context and flags. +// Setting the messages is up to the caller. +// +// See also AddGovPropFlagsToCmd. +func ReadGovPropFlags(clientCtx client.Context, flagSet *pflag.FlagSet) (*govv1.MsgSubmitProposal, error) { + rv := &govv1.MsgSubmitProposal{} + + deposit, err := flagSet.GetString(cflags.FlagDeposit) + if err != nil { + return nil, fmt.Errorf("could not read deposit: %w", err) + } + if len(deposit) > 0 { + rv.InitialDeposit, err = sdk.ParseCoinsNormalized(deposit) + if err != nil { + return nil, fmt.Errorf("invalid deposit: %w", err) + } + } + + rv.Metadata, err = flagSet.GetString(cflags.FlagMetadata) + if err != nil { + return nil, fmt.Errorf("could not read metadata: %w", err) + } + + rv.Title, err = flagSet.GetString(cflags.FlagTitle) + if err != nil { + return nil, fmt.Errorf("could not read title: %w", err) + } + + rv.Summary, err = flagSet.GetString(cflags.FlagSummary) + if err != nil { + return nil, fmt.Errorf("could not read summary: %w", err) + } + + rv.Proposer = clientCtx.GetFromAddress().String() + + return rv, nil +} diff --git a/go/cli/gov_util_test.go b/go/cli/gov_util_test.go new file mode 100644 index 00000000..220066bb --- /dev/null +++ b/go/cli/gov_util_test.go @@ -0,0 +1,697 @@ +package cli + +import ( + "bytes" + "encoding/base64" + "fmt" + "io" + "os" + "strings" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +func TestParseSubmitLegacyProposal(t *testing.T) { + okJSON := testutil.WriteToNewTempFile(t, ` +{ + "title": "Test Proposal", + "description": "My awesome proposal", + "type": "Text", + "deposit": "1000test" +} +`) + + badJSON := testutil.WriteToNewTempFile(t, "bad json") + fs := GetTxGovSubmitLegacyProposalCmd().Flags() + + // nonexistent json + err := fs.Set(cflags.FlagProposal, "fileDoesNotExist") + require.NoError(t, err) + _, err = parseSubmitLegacyProposal(fs) + require.Error(t, err) + + // invalid json + err = fs.Set(cflags.FlagProposal, badJSON.Name()) + require.NoError(t, err) + _, err = parseSubmitLegacyProposal(fs) + require.Error(t, err) + + // ok json + fs.Set(cflags.FlagProposal, okJSON.Name()) + proposal1, err := parseSubmitLegacyProposal(fs) + require.Nil(t, err, "unexpected error") + require.Equal(t, "Test Proposal", proposal1.Title) + require.Equal(t, "My awesome proposal", proposal1.Description) + require.Equal(t, "Text", proposal1.Type) + require.Equal(t, "1000test", proposal1.Deposit) + + // flags that can't be used with --proposal + for _, incompatibleFlag := range ProposalFlags { + fs.Set(incompatibleFlag, "some value") + _, err := parseSubmitLegacyProposal(fs) + require.Error(t, err) + fs.Set(incompatibleFlag, "") + } + + // no --proposal, only flags + err = fs.Set(cflags.FlagProposal, "") + require.NoError(t, err) + flagTestCases := map[string]struct { + pTitle string + pDescription string + pType string + expErr bool + errMsg string + }{ + "valid flags": { + pTitle: proposal1.Title, + pDescription: proposal1.Description, + pType: proposal1.Type, + }, + "empty type": { + pTitle: proposal1.Title, + pDescription: proposal1.Description, + expErr: true, + errMsg: "proposal type is required", + }, + "empty title": { + pDescription: proposal1.Description, + pType: proposal1.Type, + expErr: true, + errMsg: "proposal title is required", + }, + "empty description": { + pTitle: proposal1.Title, + pType: proposal1.Type, + expErr: true, + errMsg: "proposal description is required", + }, + } + for name, tc := range flagTestCases { + t.Run(name, func(t *testing.T) { + err = fs.Set(cflags.FlagTitle, tc.pTitle) + require.NoError(t, err) + err = fs.Set(cflags.FlagDescription, tc.pDescription) + require.NoError(t, err) + err = fs.Set(cflags.FlagProposalType, tc.pType) + require.NoError(t, err) + err = fs.Set(cflags.FlagDeposit, proposal1.Deposit) + require.NoError(t, err) + proposal2, err := parseSubmitLegacyProposal(fs) + + if tc.expErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errMsg) + } else { + require.NoError(t, err) + require.Equal(t, proposal1.Title, proposal2.Title) + require.Equal(t, proposal1.Description, proposal2.Description) + require.Equal(t, proposal1.Type, proposal2.Type) + require.Equal(t, proposal1.Deposit, proposal2.Deposit) + } + }) + } + + err = okJSON.Close() + require.Nil(t, err, "unexpected error") + err = badJSON.Close() + require.Nil(t, err, "unexpected error") +} + +func TestParseSubmitProposal(t *testing.T) { + _, _, addr := testdata.KeyTestPubAddr() + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + banktypes.RegisterInterfaces(interfaceRegistry) + stakingtypes.RegisterInterfaces(interfaceRegistry) + v1beta1.RegisterInterfaces(interfaceRegistry) + v1.RegisterInterfaces(interfaceRegistry) + expectedMetadata := []byte{42} + + okJSON := testutil.WriteToNewTempFile(t, fmt.Sprintf(` +{ + "messages": [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + "from_address": "%s", + "to_address": "%s", + "amount":[{"denom": "uakt","amount": "10"}] + }, + { + "@type": "/cosmos.staking.v1beta1.MsgDelegate", + "delegator_address": "%s", + "validator_address": "%s", + "amount":{"denom": "uakt","amount": "10"} + }, + { + "@type": "/cosmos.gov.v1.MsgExecLegacyContent", + "authority": "%s", + "content": { + "@type": "/cosmos.gov.v1beta1.TextProposal", + "title": "My awesome title", + "description": "My awesome description" + } + } + ], + "metadata": "%s", + "title": "My awesome title", + "summary": "My awesome summary", + "deposit": "1000test", + "expedited": true +} +`, addr, addr, addr, addr, addr, base64.StdEncoding.EncodeToString(expectedMetadata))) + + badJSON := testutil.WriteToNewTempFile(t, "bad json") + + // nonexistent json + _, _, _, err := parseSubmitProposal(cdc, "fileDoesNotExist") // nolint: dogsled + require.Error(t, err) + + // invalid json + _, _, _, err = parseSubmitProposal(cdc, badJSON.Name()) // nolint: dogsled + require.Error(t, err) + + // ok json + proposal, msgs, deposit, err := parseSubmitProposal(cdc, okJSON.Name()) + require.NoError(t, err, "unexpected error") + require.Equal(t, sdk.NewCoins(sdk.NewCoin("test", sdk.NewInt(1000))), deposit) + require.Equal(t, base64.StdEncoding.EncodeToString(expectedMetadata), proposal.Metadata) + require.Len(t, msgs, 3) + msg1, ok := msgs[0].(*banktypes.MsgSend) + require.True(t, ok) + require.Equal(t, addr.String(), msg1.FromAddress) + require.Equal(t, addr.String(), msg1.ToAddress) + require.Equal(t, sdk.NewCoins(sdk.NewCoin("uakt", sdk.NewInt(10))), msg1.Amount) + msg2, ok := msgs[1].(*stakingtypes.MsgDelegate) + require.True(t, ok) + require.Equal(t, addr.String(), msg2.DelegatorAddress) + require.Equal(t, addr.String(), msg2.ValidatorAddress) + require.Equal(t, sdk.NewCoin("uakt", sdk.NewInt(10)), msg2.Amount) + msg3, ok := msgs[2].(*v1.MsgExecLegacyContent) + require.True(t, ok) + require.Equal(t, addr.String(), msg3.Authority) + textProp, ok := msg3.Content.GetCachedValue().(*v1beta1.TextProposal) + require.True(t, ok) + require.Equal(t, "My awesome title", textProp.Title) + require.Equal(t, "My awesome description", textProp.Description) + require.Equal(t, "My awesome title", proposal.Title) + require.Equal(t, "My awesome summary", proposal.Summary) + require.Equal(t, true, proposal.Expedited) + + err = okJSON.Close() + require.Nil(t, err, "unexpected error") + err = badJSON.Close() + require.Nil(t, err, "unexpected error") +} + +func getCommandHelp(t *testing.T, cmd *cobra.Command) string { + // Create a pipe, so we can capture the help sent to stdout. + reader, writer, err := os.Pipe() + require.NoError(t, err, "creating os.Pipe()") + outChan := make(chan string) + defer func(origCmdOut io.Writer) { + cmd.SetOut(origCmdOut) + // Ignoring these errors since we're just ensuring cleanup here, + // and they will return an error if already called (which we don't care about). + _ = reader.Close() + _ = writer.Close() + close(outChan) + }(cmd.OutOrStdout()) + cmd.SetOut(writer) + + // Do the reading in a separate goroutine from the writing (a best practice). + go func() { + var b bytes.Buffer + _, buffErr := io.Copy(&b, reader) + if buffErr != nil { + // Due to complexities of goroutines and multiple channels, I'm sticking with a + // single channel and just putting the error in there (which I'll test for later). + b.WriteString("buffer error: " + buffErr.Error()) + } + outChan <- b.String() + }() + + err = cmd.Help() + require.NoError(t, err, "cmd.Help()") + require.NoError(t, writer.Close(), "pipe writer .Close()") + rv := <-outChan + require.NotContains(t, rv, "buffer error: ", "buffer output") + return rv +} + +func TestAddGovPropFlagsToCmd(t *testing.T) { + cmd := &cobra.Command{ + Short: "Just a test command that does nothing but we can add flags to it.", + Run: func(cmd *cobra.Command, args []string) { + t.Errorf("The cmd has run with the args %q, but Run shouldn't have been called.", args) + }, + } + testFunc := func() { + AddGovPropFlagsToCmd(cmd) + } + require.NotPanics(t, testFunc, "AddGovPropFlagsToCmd") + + help := getCommandHelp(t, cmd) + + expDepositDesc := "The deposit to include with the governance proposal" + expMetadataDesc := "The metadata to include with the governance proposal" + expTitleDesc := "The title to put on the governance proposal" + expSummaryDesc := "The summary to include with the governance proposal" + // Regexp notes: (?m:...) = multi-line mode so ^ and $ match the beginning and end of each line. + // Each regexp assertion checks for a line containing only a specific flag and its description. + assert.Regexp(t, `(?m:^\s+--`+cflags.FlagDeposit+` string\s+`+expDepositDesc+`$)`, help, "help output") + assert.Regexp(t, `(?m:^\s+--`+cflags.FlagMetadata+` string\s+`+expMetadataDesc+`$)`, help, "help output") + assert.Regexp(t, `(?m:^\s+--`+cflags.FlagTitle+` string\s+`+expTitleDesc+`$)`, help, "help output") + assert.Regexp(t, `(?m:^\s+--`+cflags.FlagSummary+` string\s+`+expSummaryDesc+`$)`, help, "help output") +} + +func TestReadGovPropFlags(t *testing.T) { + fromAddr := sdk.AccAddress("from_addr___________") + argDeposit := "--" + cflags.FlagDeposit + argMetadata := "--" + cflags.FlagMetadata + argTitle := "--" + cflags.FlagTitle + argSummary := "--" + cflags.FlagSummary + + // cz is a shorter way to define coins objects for these tests. + cz := func(coins string) sdk.Coins { + rv, err := sdk.ParseCoinsNormalized(coins) + require.NoError(t, err, "ParseCoinsNormalized(%q)", coins) + return rv + } + + tests := []struct { + name string + fromAddr sdk.AccAddress + args []string + exp *v1.MsgSubmitProposal + expErr []string + }{ + { + name: "no args no from", + fromAddr: nil, + args: []string{}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "only from defined", + fromAddr: fromAddr, + args: []string{}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: fromAddr.String(), + Metadata: "", + Title: "", + Summary: "", + }, + }, + + // only deposit tests. + { + name: "only deposit empty string", + fromAddr: nil, + args: []string{argDeposit, ""}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "only deposit one coin", + fromAddr: nil, + args: []string{argDeposit, "1bigcoin"}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("1bigcoin"), + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "only deposit invalid coins", + fromAddr: nil, + args: []string{argDeposit, "not really coins"}, + expErr: []string{"invalid deposit", "invalid decimal coin expression", "not really coins"}, + }, + { + name: "only deposit two coins", + fromAddr: nil, + args: []string{argDeposit, "1acoin,2bcoin"}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("1acoin,2bcoin"), + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "only deposit two coins other order", + fromAddr: nil, + args: []string{argDeposit, "2bcoin,1acoin"}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("1acoin,2bcoin"), + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "only deposit coin 1 of 3 bad", + fromAddr: nil, + args: []string{argDeposit, "1bad^coin,2bcoin,3ccoin"}, + expErr: []string{"invalid deposit", "invalid decimal coin expression", "1bad^coin"}, + }, + { + name: "only deposit coin 2 of 3 bad", + fromAddr: nil, + args: []string{argDeposit, "1acoin,2bad^coin,3ccoin"}, + expErr: []string{"invalid deposit", "invalid decimal coin expression", "2bad^coin"}, + }, + { + name: "only deposit coin 3 of 3 bad", + fromAddr: nil, + args: []string{argDeposit, "1acoin,2bcoin,3bad^coin"}, + expErr: []string{"invalid deposit", "invalid decimal coin expression", "3bad^coin"}, + }, + // As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag. + // So I don't have a test for the "could not read deposit" error case. + + // only metadata tests. + { + name: "only metadata empty", + fromAddr: nil, + args: []string{argMetadata, ""}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "only metadata simple", + fromAddr: nil, + args: []string{argMetadata, "just some metadata"}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "just some metadata", + Title: "", + Summary: "", + }, + }, + { + name: "only metadata super long", + fromAddr: nil, + args: []string{argMetadata, strings.Repeat("Long", 1_000_000)}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: strings.Repeat("Long", 1_000_000), + Title: "", + Summary: "", + }, + }, + // As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag. + // So I don't have a test for the "could not read metadata" error case. + + // only title tests. + { + name: "only title empty", + fromAddr: nil, + args: []string{argTitle, ""}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "only title simple", + fromAddr: nil, + args: []string{argTitle, "just a title"}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "just a title", + Summary: "", + }, + }, + { + name: "only title super long", + fromAddr: nil, + args: []string{argTitle, strings.Repeat("Long", 1_000_000)}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: strings.Repeat("Long", 1_000_000), + Summary: "", + }, + }, + // As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag. + // So I don't have a test for the "could not read title" error case. + + // only summary tests. + { + name: "only summary empty", + fromAddr: nil, + args: []string{argSummary, ""}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "only summary simple", + fromAddr: nil, + args: []string{argSummary, "just a short summary"}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "", + Summary: "just a short summary", + }, + }, + { + name: "only summary super long", + fromAddr: nil, + args: []string{argSummary, strings.Repeat("Long", 1_000_000)}, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "", + Summary: strings.Repeat("Long", 1_000_000), + }, + }, + // As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag. + // So I don't have a test for the "could not read summary" error case. + + // Combo tests. + { + name: "all together order 1", + fromAddr: fromAddr, + args: []string{ + argDeposit, "56depcoin", + argMetadata, "my proposal is cool", + argTitle, "Simple Gov Prop Title", + argSummary, "This is just a test summary on a simple gov prop.", + }, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("56depcoin"), + Proposer: fromAddr.String(), + Metadata: "my proposal is cool", + Title: "Simple Gov Prop Title", + Summary: "This is just a test summary on a simple gov prop.", + }, + }, + { + name: "all together order 2", + fromAddr: fromAddr, + args: []string{ + argTitle, "This title is a *bit* more complex.", + argSummary, "This\nis\na\ncrazy\nsummary", + argDeposit, "78coolcoin", + argMetadata, "this proposal is cooler", + }, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("78coolcoin"), + Proposer: fromAddr.String(), + Metadata: "this proposal is cooler", + Title: "This title is a *bit* more complex.", + Summary: "This\nis\na\ncrazy\nsummary", + }, + }, + { + name: "all except proposer", + fromAddr: nil, + args: []string{ + argMetadata, "https://example.com/lucky", + argDeposit, "33luckycoin", + argSummary, "This proposal will bring you luck and good fortune in the new year.", + argTitle, "Increase Luck", + }, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("33luckycoin"), + Proposer: "", + Metadata: "https://example.com/lucky", + Title: "Increase Luck", + Summary: "This proposal will bring you luck and good fortune in the new year.", + }, + }, + { + name: "all except proposer but all empty", + fromAddr: nil, + args: []string{ + argMetadata, "", + argDeposit, "", + argSummary, "", + argTitle, "", + }, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: "", + Metadata: "", + Title: "", + Summary: "", + }, + }, + { + name: "all except deposit", + fromAddr: fromAddr, + args: []string{ + argTitle, "This is a Title", + argSummary, "This is a useless summary", + argMetadata, "worthless metadata", + }, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: nil, + Proposer: fromAddr.String(), + Metadata: "worthless metadata", + Title: "This is a Title", + Summary: "This is a useless summary", + }, + expErr: nil, + }, + { + name: "all except metadata", + fromAddr: fromAddr, + args: []string{ + argTitle, "Bland Title", + argSummary, "Boring summary", + argDeposit, "99mdcoin", + }, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("99mdcoin"), + Proposer: fromAddr.String(), + Metadata: "", + Title: "Bland Title", + Summary: "Boring summary", + }, + }, + { + name: "all except title", + fromAddr: fromAddr, + args: []string{ + argMetadata, "this metadata does not have the title either", + argDeposit, "71whatcoin", + argSummary, "This is a summary on a titleless proposal.", + }, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("71whatcoin"), + Proposer: fromAddr.String(), + Metadata: "this metadata does not have the title either", + Title: "", + Summary: "This is a summary on a titleless proposal.", + }, + expErr: nil, + }, + { + name: "all except summary", + fromAddr: fromAddr, + args: []string{ + argMetadata, "28", + argTitle, "Now This is What I Call A Governance Proposal 28", + argDeposit, "42musiccoin", + }, + exp: &v1.MsgSubmitProposal{ + InitialDeposit: cz("42musiccoin"), + Proposer: fromAddr.String(), + Metadata: "28", + Title: "Now This is What I Call A Governance Proposal 28", + Summary: "", + }, + expErr: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + cmd := &cobra.Command{ + Short: tc.name, + Run: func(cmd *cobra.Command, args []string) { + t.Errorf("The cmd for %q has run with the args %q, but Run shouldn't have been called.", tc.name, args) + }, + } + AddGovPropFlagsToCmd(cmd) + err := cmd.ParseFlags(tc.args) + require.NoError(t, err, "parsing test case args using cmd: %q", tc.args) + flagSet := cmd.Flags() + + clientCtx := client.Context{ + FromAddress: tc.fromAddr, + } + + var msg *v1.MsgSubmitProposal + testFunc := func() { + msg, err = ReadGovPropFlags(clientCtx, flagSet) + } + require.NotPanics(t, testFunc, "ReadGovPropFlags") + if len(tc.expErr) > 0 { + require.Error(t, err, "ReadGovPropFlags error") + for _, exp := range tc.expErr { + assert.ErrorContains(t, err, exp, "ReadGovPropFlags error") + } + } else { + require.NoError(t, err, "ReadGovPropFlags error") + } + assert.Equal(t, tc.exp, msg, "ReadGovPropFlags msg") + }) + } +} diff --git a/go/cli/init.go b/go/cli/init.go new file mode 100644 index 00000000..62ae6e06 --- /dev/null +++ b/go/cli/init.go @@ -0,0 +1,9 @@ +package cli + +import ( + "github.com/spf13/cobra" +) + +func init() { + cobra.EnableTraverseRunHooks = true +} diff --git a/go/cli/market_query.go b/go/cli/market_query.go index 01249b90..8cae3e31 100644 --- a/go/cli/market_query.go +++ b/go/cli/market_query.go @@ -10,8 +10,8 @@ import ( "pkg.akt.dev/go/node/market/v1beta5" ) -// GetMarketQueryCmd returns the transaction commands for the market module -func GetMarketQueryCmd() *cobra.Command { +// GetQueryMarketCmds returns the transaction commands for the market module +func GetQueryMarketCmds() *cobra.Command { cmd := &cobra.Command{ Use: v1beta5.ModuleName, Short: "Market query commands", @@ -20,15 +20,15 @@ func GetMarketQueryCmd() *cobra.Command { } cmd.AddCommand( - getOrderCmd(), - getBidCmd(), - getLeaseCmd(), + GetQueryMarketOrderCmds(), + GetQueryMarketBidCmds(), + GetQueryMarketLeaseCmds(), ) return cmd } -func getOrderCmd() *cobra.Command { +func GetQueryMarketOrderCmds() *cobra.Command { cmd := &cobra.Command{ Use: "order", Short: "Order query commands", @@ -37,14 +37,14 @@ func getOrderCmd() *cobra.Command { } cmd.AddCommand( - cmdGetOrders(), - cmdGetOrder(), + GetQueryMarketOrdersCmd(), + GetQueryMarketOrderCmd(), ) return cmd } -func getBidCmd() *cobra.Command { +func GetQueryMarketBidCmds() *cobra.Command { cmd := &cobra.Command{ Use: "bid", Short: "Bid query commands", @@ -53,14 +53,14 @@ func getBidCmd() *cobra.Command { } cmd.AddCommand( - cmdGetBids(), - cmdGetBid(), + GetQueryMarketBidsCmd(), + GetQueryMarketBidCmd(), ) return cmd } -func getLeaseCmd() *cobra.Command { +func GetQueryMarketLeaseCmds() *cobra.Command { cmd := &cobra.Command{ Use: "lease", Short: "Lease query commands", @@ -69,17 +69,18 @@ func getLeaseCmd() *cobra.Command { } cmd.AddCommand( - cmdGetLeases(), - cmdGetLease(), + GetQueryMarketLeasesCmd(), + GetQueryMarketLeaseCmd(), ) return cmd } -func cmdGetOrders() *cobra.Command { +func GetQueryMarketOrdersCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Short: "Query for all orders", + Use: "list", + Short: "Query for all orders", + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -115,11 +116,12 @@ func cmdGetOrders() *cobra.Command { return cmd } -func cmdGetOrder() *cobra.Command { +func GetQueryMarketOrderCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "get", - Short: "Query order", - Args: cobra.ExactArgs(0), + Use: "get", + Short: "Query order", + Args: cobra.ExactArgs(0), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -146,10 +148,11 @@ func cmdGetOrder() *cobra.Command { return cmd } -func cmdGetBids() *cobra.Command { +func GetQueryMarketBidsCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Short: "Query for all bids", + Use: "list", + Short: "Query for all bids", + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -185,11 +188,12 @@ func cmdGetBids() *cobra.Command { return cmd } -func cmdGetBid() *cobra.Command { +func GetQueryMarketBidCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "get", - Short: "Query order", - Args: cobra.ExactArgs(0), + Use: "get", + Short: "Query order", + Args: cobra.ExactArgs(0), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -215,10 +219,11 @@ func cmdGetBid() *cobra.Command { return cmd } -func cmdGetLeases() *cobra.Command { +func GetQueryMarketLeasesCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Short: "Query for all leases", + Use: "list", + PersistentPreRunE: QueryPersistentPreRunE, + Short: "Query for all leases", RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -254,11 +259,12 @@ func cmdGetLeases() *cobra.Command { return cmd } -func cmdGetLease() *cobra.Command { +func GetQueryMarketLeaseCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "get", - Short: "Query order", - Args: cobra.ExactArgs(0), + Use: "get", + Short: "Query order", + Args: cobra.ExactArgs(0), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) diff --git a/go/cli/market_tx.go b/go/cli/market_tx.go index 54403f76..34da5748 100644 --- a/go/cli/market_tx.go +++ b/go/cli/market_tx.go @@ -7,13 +7,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" cflags "pkg.akt.dev/go/cli/flags" - types "pkg.akt.dev/go/node/market/v1beta5" - mv1beta "pkg.akt.dev/go/node/market/v1beta5" + types "pkg.akt.dev/go/node/market/v1beta5" ) -// GetMarketTxCmd returns the transaction commands for market module -func GetMarketTxCmd() *cobra.Command { +// GetTxMarketCmds returns the transaction commands for market module +func GetTxMarketCmds() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Transaction subcommands", @@ -21,13 +20,13 @@ func GetMarketTxCmd() *cobra.Command { RunE: sdkclient.ValidateCmd, } cmd.AddCommand( - cmdMarketBid(), - cmdMarketLease(), + GetTxMarketBidCmds(), + GetTxMarketLeaseCmds(), ) return cmd } -func cmdMarketBid() *cobra.Command { +func GetTxMarketBidCmds() *cobra.Command { cmd := &cobra.Command{ Use: "bid", Short: "Bid subcommands", @@ -35,17 +34,18 @@ func cmdMarketBid() *cobra.Command { RunE: sdkclient.ValidateCmd, } cmd.AddCommand( - cmdMarketBidCreate(), - cmdMarketBidClose(), + GetTxMarketBidCreateCmd(), + GetTxMarketBidCloseCmd(), ) return cmd } -func cmdMarketBidCreate() *cobra.Command { +func GetTxMarketBidCreateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "create", - Short: "Create a market bid", - Args: cobra.ExactArgs(0), + Use: "create", + Short: "Create a market bid", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -100,11 +100,12 @@ func cmdMarketBidCreate() *cobra.Command { return cmd } -func cmdMarketBidClose() *cobra.Command { +func GetTxMarketBidCloseCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "close", - Short: "Close a market bid", - Args: cobra.ExactArgs(0), + Use: "close", + Short: "Close a market bid", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -138,26 +139,29 @@ func cmdMarketBidClose() *cobra.Command { return cmd } -func cmdMarketLease() *cobra.Command { +func GetTxMarketLeaseCmds() *cobra.Command { cmd := &cobra.Command{ Use: "lease", Short: "Lease subcommands", SuggestionsMinimumDistance: 2, RunE: sdkclient.ValidateCmd, } + cmd.AddCommand( - cmdMarketLeaseCreate(), - cmdMarketLeaseWithdraw(), - cmdMarketLeaseClose(), + GetTxMarketLeaseCreateCmd(), + GetTxMarketLeaseWithdrawCmd(), + GetTxMarketLeaseCloseCmd(), ) + return cmd } -func cmdMarketLeaseCreate() *cobra.Command { +func GetTxMarketLeaseCreateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "create", - Short: "Create a market lease", - Args: cobra.ExactArgs(0), + Use: "create", + Short: "Create a market lease", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -192,11 +196,12 @@ func cmdMarketLeaseCreate() *cobra.Command { return cmd } -func cmdMarketLeaseWithdraw() *cobra.Command { +func GetTxMarketLeaseWithdrawCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "withdraw", - Short: "Settle and withdraw available funds from market order escrow account", - Args: cobra.ExactArgs(0), + Use: "withdraw", + Short: "Settle and withdraw available funds from market order escrow account", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -232,11 +237,12 @@ func cmdMarketLeaseWithdraw() *cobra.Command { return cmd } -func cmdMarketLeaseClose() *cobra.Command { +func GetTxMarketLeaseCloseCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "close", - Short: "Close a market order", - Args: cobra.ExactArgs(0), + Use: "close", + Short: "Close a market order", + Args: cobra.ExactArgs(0), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) diff --git a/go/cli/mint_query.go b/go/cli/mint_query.go new file mode 100644 index 00000000..78502f0b --- /dev/null +++ b/go/cli/mint_query.go @@ -0,0 +1,113 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +// GetQueryMintCmd returns the cli query commands for the minting module. +func GetQueryMintCmd() *cobra.Command { + mintingQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the minting module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + mintingQueryCmd.AddCommand( + GetQueryMintParamsCmd(), + GetQueryMintInflationCmd(), + GetQueryMintAnnualProvisionsCmd(), + ) + + return mintingQueryCmd +} + +// GetQueryMintParamsCmd implements a command to return the current minting +// parameters. +func GetQueryMintParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current minting parameters", + Args: cobra.NoArgs, + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + params := &types.QueryParamsRequest{} + res, err := cl.Query().Mint().Params(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Params) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryMintInflationCmd implements a command to return the current minting +// inflation value. +func GetQueryMintInflationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "inflation", + Short: "Query the current minting inflation value", + Args: cobra.NoArgs, + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + cctx := cl.ClientContext() + + params := &types.QueryInflationRequest{} + res, err := cl.Query().Mint().Inflation(cmd.Context(), params) + if err != nil { + return err + } + + return cctx.PrintString(fmt.Sprintf("%s\n", res.Inflation)) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryMintAnnualProvisionsCmd implements a command to return the current minting +// annual provisions value. +func GetQueryMintAnnualProvisionsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "annual-provisions", + Short: "Query the current minting annual provisions value", + Args: cobra.NoArgs, + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + cctx := cl.ClientContext() + + params := &types.QueryAnnualProvisionsRequest{} + res, err := cl.Query().Mint().AnnualProvisions(cmd.Context(), params) + if err != nil { + return err + } + + return cctx.PrintString(fmt.Sprintf("%s\n", res.AnnualProvisions)) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/params_query.go b/go/cli/params_query.go new file mode 100644 index 00000000..5ccfd8da --- /dev/null +++ b/go/cli/params_query.go @@ -0,0 +1,54 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +// GetQueryParamsCmd returns a root CLI command handler for all x/params query commands. +func GetQueryParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the params module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetQueryParamsSubspaceCmd(), + ) + + return cmd +} + +// GetQueryParamsSubspaceCmd returns a CLI command handler for querying subspace +// parameters managed by the x/params module. +func GetQueryParamsSubspaceCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "subspace [subspace] [key]", + Short: "Query for raw parameters by subspace and key", + Args: cobra.ExactArgs(2), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + params := proposal.QueryParamsRequest{Subspace: args[0], Key: args[1]} + res, err := cl.Query().Params().Params(cmd.Context(), ¶ms) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Param) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/params_tx.go b/go/cli/params_tx.go new file mode 100644 index 00000000..2970b4eb --- /dev/null +++ b/go/cli/params_tx.go @@ -0,0 +1,91 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + paramscutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" + paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +// GetTxParamsSubmitParamChangeProposalCmd returns a CLI command handler for creating +// a parameter change proposal governance transaction. +func GetTxParamsSubmitParamChangeProposalCmd() *cobra.Command { + return &cobra.Command{ + Use: "param-change [proposal-file]", + Args: cobra.ExactArgs(1), + Short: "Submit a parameter change proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a parameter proposal along with an initial deposit. +The proposal details must be supplied via a JSON file. For values that contains +objects, only non-empty fields will be updated. + +IMPORTANT: Currently parameter changes are evaluated but not validated, so it is +very important that any "value" change is valid (ie. correct type and within bounds) +for its respective parameter, eg. "MaxValidators" should be an integer and not a decimal. + +Proper vetting of a parameter change proposal should prevent this from happening +(no deposits should occur during the governance process), but it should be noted +regardless. + +Example: +$ %s tx gov submit-proposal param-change --from= + +Where proposal.json contains: + +{ + "title": "Staking Param Change", + "description": "Update max validators", + "changes": [ + { + "subspace": "staking", + "key": "MaxValidators", + "value": 105 + } + ], + "deposit": "1000uakt" +} +`, + version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + proposal, err := paramscutils.ParseParamChangeProposalJSON(cctx.LegacyAmino, args[0]) + if err != nil { + return err + } + + from := cctx.GetFromAddress() + content := paramproposal.NewParameterChangeProposal( + proposal.Title, proposal.Description, proposal.Changes.ToParamChanges(), + ) + + deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit) + if err != nil { + return err + } + + msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from) + if err != nil { + return err + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } +} diff --git a/go/cli/provider_query.go b/go/cli/provider_query.go index fa5abf2b..08d98f90 100644 --- a/go/cli/provider_query.go +++ b/go/cli/provider_query.go @@ -4,14 +4,14 @@ import ( "github.com/spf13/cobra" sdkclient "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" + cflags "pkg.akt.dev/go/cli/flags" types "pkg.akt.dev/go/node/provider/v1beta4" ) -// GetProviderQueryCmd returns the transaction commands for the provider module -func GetProviderQueryCmd() *cobra.Command { +// GetQueryProviderCmds returns the transaction commands for the provider module +func GetQueryProviderCmds() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Provider query commands", @@ -20,17 +20,18 @@ func GetProviderQueryCmd() *cobra.Command { } cmd.AddCommand( - cmdGetProviders(), - cmdGetProvider(), + GetQueryGetProvidersCmd(), + GetQueryProviderCmd(), ) return cmd } -func cmdGetProviders() *cobra.Command { +func GetQueryGetProvidersCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Short: "Query for all providers", + Use: "list", + Short: "Query for all providers", + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -53,17 +54,18 @@ func cmdGetProviders() *cobra.Command { }, } - flags.AddQueryFlagsToCmd(cmd) - flags.AddPaginationFlagsToCmd(cmd, "providers") + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "providers") return cmd } -func cmdGetProvider() *cobra.Command { +func GetQueryProviderCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "get [address]", - Short: "Query provider", - Args: cobra.ExactArgs(1), + Use: "get [address]", + Short: "Query provider", + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustQueryClientFromContext(ctx) @@ -82,7 +84,7 @@ func cmdGetProvider() *cobra.Command { }, } - flags.AddQueryFlagsToCmd(cmd) + cflags.AddQueryFlagsToCmd(cmd) return cmd } diff --git a/go/cli/provider_tx.go b/go/cli/provider_tx.go index da55a767..a5d33cc7 100644 --- a/go/cli/provider_tx.go +++ b/go/cli/provider_tx.go @@ -55,8 +55,8 @@ func ReadProviderConfigPath(path string) (ProviderConfig, error) { return val, err } -// GetProviderTxCmd returns the transaction commands for provider module -func GetProviderTxCmd() *cobra.Command { +// GetTxProviderCmd returns the transaction commands for provider module +func GetTxProviderCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Provider transaction subcommands", @@ -64,17 +64,18 @@ func GetProviderTxCmd() *cobra.Command { RunE: sdkclient.ValidateCmd, } cmd.AddCommand( - cmdProviderCreate(), - cmdProviderUpdate(), + GetTxProviderCreateCmd(), + GetTxProviderUpdateCmd(), ) return cmd } -func cmdProviderCreate() *cobra.Command { +func GetTxProviderCreateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "create [config-file]", - Short: "Create a provider", - Args: cobra.ExactArgs(1), + Use: "create [config-file]", + Short: "Create a provider", + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) @@ -112,11 +113,12 @@ func cmdProviderCreate() *cobra.Command { return cmd } -func cmdProviderUpdate() *cobra.Command { +func GetTxProviderUpdateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "update [config-file]", - Short: "Update provider", - Args: cobra.ExactArgs(1), + Use: "update [config-file]", + Short: "Update provider", + Args: cobra.ExactArgs(1), + PersistentPreRunE: TxPersistentPreRunE, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() cl := MustClientFromContext(ctx) diff --git a/go/cli/query.go b/go/cli/query.go index 9883caa0..eb5e6d60 100644 --- a/go/cli/query.go +++ b/go/cli/query.go @@ -5,53 +5,65 @@ import ( "github.com/spf13/cobra" - sdkclient "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/rpc" cflags "pkg.akt.dev/go/cli/flags" ) -func QueryCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "query", - Aliases: []string{"q"}, - Short: "Querying subcommands", - PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { - ctx := cmd.Context() +func QueryPersistentPreRunE(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() - cctx, err := sdkclient.GetClientTxContext(cmd) - if err != nil { - return err - } + cctx, err := GetClientTxContext(cmd) + if err != nil { + return err + } - cl, err := DiscoverQueryClient(ctx, cctx) - if err != nil { - return err - } + cl, err := DiscoverQueryClient(ctx, cctx) + if err != nil { + return err + } - ctx = context.WithValue(ctx, ContextTypeQueryClient, cl) + ctx = context.WithValue(ctx, ContextTypeQueryClient, cl) - cmd.SetContext(ctx) + cmd.SetContext(ctx) - return nil - }, + return nil +} + +func QueryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", } cmd.AddCommand( - GetAuthQueryCmd(), + GetQueryAuthCmd(), + GetQueryAuthzCmd(), + GetQueryBankCmd(), + GetQueryDistributionCmd(), + GetQueryEvidenceCmd(), + GetQueryFeegrantCmd(), + GetQueryMintCmd(), + GetQueryParamsCmd(), cflags.LineBreak, rpc.ValidatorCommand(), rpc.BlockCommand(), - QueryTxsByEventsCmd(), - GetTxQueryCmd(), + GetQueryAuthTxsByEventsCmd(), + GetQueryAuthTxCmd(), + GetQueryGovCmd(), + GetQuerySlashingCmd(), + GetQueryStakingCmd(), cflags.LineBreak, - GetAuditQueryCmd(), - GetCertQueryCmd(), - GetDeploymentQueryCmd(), - GetMarketQueryCmd(), - GetProviderQueryCmd(), + GetQueryAuditCmd(), + GetQueryCertCmd(), + GetQueryDeploymentCmds(), + GetQueryMarketCmds(), + GetQueryEscrowCmd(), + GetQueryProviderCmds(), ) cmd.PersistentFlags().String(cflags.FlagChainID, "", "The network chain ID") + return cmd } diff --git a/go/cli/slashing_query.go b/go/cli/slashing_query.go new file mode 100644 index 00000000..d9ebb1b8 --- /dev/null +++ b/go/cli/slashing_query.go @@ -0,0 +1,138 @@ +package cli + +import ( + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetQuerySlashingCmd returns the cli query commands for this module +func GetQuerySlashingCmd() *cobra.Command { + // Group slashing queries under a subcommand + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the slashing module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetQuerySlashingSigningInfoCmd(), + GetQuerySlashingParamsCmd(), + GetQuerySlashingSigningInfosCmd(), + ) + + return cmd +} + +// GetQuerySlashingSigningInfoCmd implements the command to query signing info. +func GetQuerySlashingSigningInfoCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "signing-info [validator-conspub]", + Short: "Query a validator's signing information", + Long: strings.TrimSpace(`Use a validators' consensus public key to find the signing-info for that validator: + +$ query slashing signing-info '{"@type":"/cosmos.crypto.ed25519.PubKey","key":"OauFcTKbN5Lx3fJL689cikXBqe+hcp6Y+x0rYUdR9Jk="}' +`), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + cctx := cl.ClientContext() + + var pk cryptotypes.PubKey + if err := cctx.Codec.UnmarshalInterfaceJSON([]byte(args[0]), &pk); err != nil { + return err + } + + consAddr := sdk.ConsAddress(pk.Address()) + params := &types.QuerySigningInfoRequest{ConsAddress: consAddr.String()} + res, err := cl.Query().Slashing().SigningInfo(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(&res.ValSigningInfo) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQuerySlashingSigningInfosCmd implements the command to query signing infos. +func GetQuerySlashingSigningInfosCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "signing-infos", + Short: "Query signing information of all validators", + Long: strings.TrimSpace(`signing infos of validators: + +$ query slashing signing-infos +`), + Args: cobra.NoArgs, + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QuerySigningInfosRequest{Pagination: pageReq} + res, err := cl.Query().Slashing().SigningInfos(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "signing infos") + + return cmd +} + +// GetQuerySlashingParamsCmd implements a command to fetch slashing parameters. +func GetQuerySlashingParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current slashing parameters", + Args: cobra.NoArgs, + Long: strings.TrimSpace(`Query genesis parameters for the slashing module: + +$ query slashing params +`), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + params := &types.QueryParamsRequest{} + res, err := cl.Query().Slashing().Params(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Params) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/slashing_tx.go b/go/cli/slashing_tx.go new file mode 100644 index 00000000..d0e469f2 --- /dev/null +++ b/go/cli/slashing_tx.go @@ -0,0 +1,59 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetTxSlashingCmd returns a root CLI command handler for all x/slashing transaction commands. +func GetTxSlashingCmd() *cobra.Command { + slashingTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Slashing transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + slashingTxCmd.AddCommand(getTxSlashingUnjailCmd()) + return slashingTxCmd +} + +// getTxSlashingUnjailCmd returns a CLI command handler for creating a MsgUnjail transaction. +func getTxSlashingUnjailCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "unjail", + Args: cobra.NoArgs, + Short: "unjail validator previously jailed for downtime", + Long: `unjail a jailed validator: + +$ tx slashing unjail --from mykey +`, + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + valAddr := cctx.GetFromAddress() + + msg := types.NewMsgUnjail(sdk.ValAddress(valAddr)) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/staking_query.go b/go/cli/staking_query.go new file mode 100644 index 00000000..05d4a573 --- /dev/null +++ b/go/cli/staking_query.go @@ -0,0 +1,1036 @@ +package cli + +import ( + "fmt" + "strconv" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/staking/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetQueryStakingCmd returns the cli query commands for this module +func GetQueryStakingCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the staking module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetQueryStakingDelegationCmd(), + GetQueryStakingDelegationsCmd(), + GetQueryStakingUnbondingDelegationCmd(), + GetQueryStakingUnbondingDelegationsCmd(), + GetQueryStakingRedelegationCmd(), + GetQueryStakingRedelegationsCmd(), + GetQueryStakingValidatorCmd(), + GetQueryStakingValidatorsCmd(), + GetQueryStakingValidatorDelegationsCmd(), + GetQueryStakingValidatorUnbondingDelegationsCmd(), + GetQueryStakingValidatorRedelegationsCmd(), + GetQueryStakingHistoricalInfoCmd(), + GetQueryStakingParamsCmd(), + GetQueryStakingPoolCmd(), + GetQueryStakingTokenizeShareRecordByIDCmd(), + GetQueryStakingTokenizeShareRecordByDenomCmd(), + GetQueryStakingTokenizeShareRecordsOwnedCmd(), + GetQueryStakingAllTokenizeShareRecordsCmd(), + GetQueryStakingLastTokenizeShareRecordIDCmd(), + GetQueryStakingTotalTokenizeSharedAssetsCmd(), + GetQueryStakingTokenizeShareLockInfoCmd(), + GetQueryStakingTotalLiquidStakedCmd(), + ) + + return cmd +} + +// GetQueryStakingValidatorCmd implements the validator query command. +func GetQueryStakingValidatorCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "validator [validator-addr]", + Short: "Query a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query details about an individual validator. + +Example: +$ %s query staking validator %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixValAddr, + ), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + addr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + params := &types.QueryValidatorRequest{ValidatorAddr: addr.String()} + res, err := cl.Query().Staking().Validator(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Validator) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingValidatorsCmd implements the query all validators command. +func GetQueryStakingValidatorsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "validators", + Short: "Query for all validators", + Args: cobra.NoArgs, + Long: strings.TrimSpace( + fmt.Sprintf(`Query details about all validators on a network. + +Example: +$ %s query staking validators +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + result, err := cl.Query().Staking().Validators(cmd.Context(), &types.QueryValidatorsRequest{ + // Leaving status empty on purpose to query all validators. + Pagination: pageReq, + }) + if err != nil { + return err + } + + return cl.PrintMessage(result) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "validators") + + return cmd +} + +// GetQueryStakingValidatorUnbondingDelegationsCmd implements the query all unbonding delegatations from a validator command. +func GetQueryStakingValidatorUnbondingDelegationsCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "unbonding-delegations-from [validator-addr]", + Short: "Query all unbonding delegatations from a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations that are unbonding _from_ a validator. + +Example: +$ %s query staking unbonding-delegations-from %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixValAddr, + ), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QueryValidatorUnbondingDelegationsRequest{ + ValidatorAddr: valAddr.String(), + Pagination: pageReq, + } + + res, err := cl.Query().Staking().ValidatorUnbondingDelegations(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "unbonding delegations") + + return cmd +} + +// GetQueryStakingValidatorRedelegationsCmd implements the query all redelegatations +// from a validator command. +func GetQueryStakingValidatorRedelegationsCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "redelegations-from [validator-addr]", + Short: "Query all outgoing redelegatations from a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations that are redelegating _from_ a validator. + +Example: +$ %s query staking redelegations-from %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixValAddr, + ), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QueryRedelegationsRequest{ + SrcValidatorAddr: valSrcAddr.String(), + Pagination: pageReq, + } + + res, err := cl.Query().Staking().Redelegations(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "validator redelegations") + + return cmd +} + +// GetQueryStakingDelegationCmd the query delegation command. +func GetQueryStakingDelegationCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "delegation [delegator-addr] [validator-addr]", + Short: "Query a delegation based on address and validator address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations for an individual delegator on an individual validator. + +Example: +$ %s query staking delegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixAccAddr, bech32PrefixValAddr, + ), + ), + Args: cobra.ExactArgs(2), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + valAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + params := &types.QueryDelegationRequest{ + DelegatorAddr: delAddr.String(), + ValidatorAddr: valAddr.String(), + } + + res, err := cl.Query().Staking().Delegation(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res.DelegationResponse) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingDelegationsCmd implements the command to query all the delegations +// made from one delegator. +func GetQueryStakingDelegationsCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "delegations [delegator-addr]", + Short: "Query all delegations made by one delegator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations for an individual delegator on all validators. + +Example: +$ %s query staking delegations %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +`, + version.AppName, bech32PrefixAccAddr, + ), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QueryDelegatorDelegationsRequest{ + DelegatorAddr: delAddr.String(), + Pagination: pageReq, + } + + res, err := cl.Query().Staking().DelegatorDelegations(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "delegations") + + return cmd +} + +// GetQueryStakingValidatorDelegationsCmd implements the command to query all the +// delegations to a specific validator. +func GetQueryStakingValidatorDelegationsCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "delegations-to [validator-addr]", + Short: "Query all delegations made to one validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations on an individual validator. + +Example: +$ %s query staking delegations-to %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixValAddr, + ), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QueryValidatorDelegationsRequest{ + ValidatorAddr: valAddr.String(), + Pagination: pageReq, + } + + res, err := cl.Query().Staking().ValidatorDelegations(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "validator delegations") + + return cmd +} + +// GetQueryStakingUnbondingDelegationCmd implements the command to query a single +// unbonding-delegation record. +func GetQueryStakingUnbondingDelegationCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "unbonding-delegation [delegator-addr] [validator-addr]", + Short: "Query an unbonding-delegation record based on delegator and validator address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query unbonding delegations for an individual delegator on an individual validator. + +Example: +$ %s query staking unbonding-delegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixAccAddr, bech32PrefixValAddr, + ), + ), + Args: cobra.ExactArgs(2), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + valAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + params := &types.QueryUnbondingDelegationRequest{ + DelegatorAddr: delAddr.String(), + ValidatorAddr: valAddr.String(), + } + + res, err := cl.Query().Staking().UnbondingDelegation(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Unbond) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingUnbondingDelegationsCmd implements the command to query all the +// unbonding-delegation records for a delegator. +func GetQueryStakingUnbondingDelegationsCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "unbonding-delegations [delegator-addr]", + Short: "Query all unbonding-delegations records for one delegator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query unbonding delegations for an individual delegator. + +Example: +$ %s query staking unbonding-delegations %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +`, + version.AppName, bech32PrefixAccAddr, + ), + ), + Args: cobra.ExactArgs(1), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QueryDelegatorUnbondingDelegationsRequest{ + DelegatorAddr: delegatorAddr.String(), + Pagination: pageReq, + } + + res, err := cl.Query().Staking().DelegatorUnbondingDelegations(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "unbonding delegations") + + return cmd +} + +// GetQueryStakingRedelegationCmd implements the command to query a single +// redelegation record. +func GetQueryStakingRedelegationCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "redelegation [delegator-addr] [src-validator-addr] [dst-validator-addr]", + Short: "Query a redelegation record based on delegator and a source and destination validator address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query a redelegation record for an individual delegator between a source and destination validator. + +Example: +$ %s query staking redelegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixAccAddr, bech32PrefixValAddr, bech32PrefixValAddr, + ), + ), + Args: cobra.ExactArgs(3), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + valSrcAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + valDstAddr, err := sdk.ValAddressFromBech32(args[2]) + if err != nil { + return err + } + + params := &types.QueryRedelegationsRequest{ + DelegatorAddr: delAddr.String(), + DstValidatorAddr: valDstAddr.String(), + SrcValidatorAddr: valSrcAddr.String(), + } + + res, err := cl.Query().Staking().Redelegations(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingRedelegationsCmd implements the command to query all the +// redelegation records for a delegator. +func GetQueryStakingRedelegationsCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "redelegations [delegator-addr]", + Args: cobra.ExactArgs(1), + Short: "Query all redelegations records for one delegator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query all redelegation records for an individual delegator. + +Example: +$ %s query staking redelegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +`, + version.AppName, bech32PrefixAccAddr, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QueryRedelegationsRequest{ + DelegatorAddr: delAddr.String(), + Pagination: pageReq, + } + + res, err := cl.Query().Staking().Redelegations(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "delegator redelegations") + + return cmd +} + +// GetQueryStakingHistoricalInfoCmd implements the historical info query command +func GetQueryStakingHistoricalInfoCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "historical-info [height]", + Args: cobra.ExactArgs(1), + Short: "Query historical info at given height", + Long: strings.TrimSpace( + fmt.Sprintf(`Query historical info at given height. + +Example: +$ %s query staking historical-info 5 +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + height, err := strconv.ParseInt(args[0], 10, 64) + if err != nil || height < 0 { + return fmt.Errorf("height argument provided must be a non-negative-integer: %v", err) + } + + params := &types.QueryHistoricalInfoRequest{Height: height} + res, err := cl.Query().Staking().HistoricalInfo(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res.Hist) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingPoolCmd implements the pool query command. +func GetQueryStakingPoolCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "pool", + Args: cobra.NoArgs, + Short: "Query the current staking pool values", + Long: strings.TrimSpace( + fmt.Sprintf(`Query values for amounts stored in the staking pool. + +Example: +$ %s query staking pool +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Staking().Pool(cmd.Context(), &types.QueryPoolRequest{}) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Pool) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingParamsCmd implements the params query command. +func GetQueryStakingParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Args: cobra.NoArgs, + Short: "Query the current staking parameters information", + Long: strings.TrimSpace( + fmt.Sprintf(`Query values set as staking parameters. + +Example: +$ %s query staking params +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Staking().Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return cl.PrintMessage(&res.Params) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingTokenizeShareRecordByIDCmd implements the query for individual tokenize share record information by share by id +func GetQueryStakingTokenizeShareRecordByIDCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "tokenize-share-record-by-id [id]", + Args: cobra.ExactArgs(1), + Short: "Query individual tokenize share record information by share by id", + Long: strings.TrimSpace( + fmt.Sprintf(`Query individual tokenize share record information by share by id. + +Example: +$ %s query staking tokenize-share-record-by-id [id] +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + id, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + + res, err := cl.Query().Staking().TokenizeShareRecordById(cmd.Context(), &types.QueryTokenizeShareRecordByIdRequest{ + Id: uint64(id), + }) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingTokenizeShareRecordByDenomCmd implements the query for individual tokenize share record information by share denom +func GetQueryStakingTokenizeShareRecordByDenomCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "tokenize-share-record-by-denom", + Args: cobra.ExactArgs(1), + Short: "Query individual tokenize share record information by share denom", + Long: strings.TrimSpace( + fmt.Sprintf(`Query individual tokenize share record information by share denom. + +Example: +$ %s query staking tokenize-share-record-by-denom +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Staking().TokenizeShareRecordByDenom(cmd.Context(), &types.QueryTokenizeShareRecordByDenomRequest{ + Denom: args[0], + }) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingTokenizeShareRecordsOwnedCmd implements the query tokenize share records by address +func GetQueryStakingTokenizeShareRecordsOwnedCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "tokenize-share-records-owned", + Args: cobra.ExactArgs(1), + Short: "Query tokenize share records by address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query tokenize share records by address. + +Example: +$ %s query staking tokenize-share-records-owned [owner] +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + owner, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + res, err := cl.Query().Staking().TokenizeShareRecordsOwned(cmd.Context(), &types.QueryTokenizeShareRecordsOwnedRequest{ + Owner: owner.String(), + }) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingAllTokenizeShareRecordsCmd implements the query for all tokenize share records +func GetQueryStakingAllTokenizeShareRecordsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "all-tokenize-share-records", + Args: cobra.NoArgs, + Short: "Query for all tokenize share records", + Long: strings.TrimSpace( + fmt.Sprintf(`Query for all tokenize share records. + +Example: +$ %s query staking all-tokenize-share-records +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QueryAllTokenizeShareRecordsRequest{ + Pagination: pageReq, + } + + res, err := cl.Query().Staking().AllTokenizeShareRecords(cmd.Context(), params) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + cflags.AddPaginationFlagsToCmd(cmd, "tokenize share records") + + return cmd +} + +// GetQueryStakingLastTokenizeShareRecordIDCmd implements the query for last tokenize share record id +func GetQueryStakingLastTokenizeShareRecordIDCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "last-tokenize-share-record-id", + Args: cobra.NoArgs, + Short: "Query for last tokenize share record id", + Long: strings.TrimSpace( + fmt.Sprintf(`Query for last tokenize share record id. + +Example: +$ %s query staking last-tokenize-share-record-id +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Staking().LastTokenizeShareRecordId(cmd.Context(), &types.QueryLastTokenizeShareRecordIdRequest{}) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingTotalTokenizeSharedAssetsCmd implements the query for total tokenized staked assets +func GetQueryStakingTotalTokenizeSharedAssetsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "total-tokenize-share-assets", + Args: cobra.NoArgs, + Short: "Query for total tokenized staked assets", + Long: strings.TrimSpace( + fmt.Sprintf(`Query for total tokenized staked assets. + +Example: +$ %s query staking total-tokenize-share-assets +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Staking().TotalTokenizeSharedAssets(cmd.Context(), &types.QueryTotalTokenizeSharedAssetsRequest{}) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingTotalLiquidStakedCmd implements the query for total liquid staked tokens +func GetQueryStakingTotalLiquidStakedCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "total-liquid-staked", + Args: cobra.NoArgs, + Short: "Query for total liquid staked tokens", + Long: strings.TrimSpace( + fmt.Sprintf(`Query for total number of liquid staked tokens. +Liquid staked tokens are identified as either a tokenized delegation, +or tokens owned by an interchain account. +Example: +$ %s query staking total-liquid-staked +`, + version.AppName, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + res, err := cl.Query().Staking().TotalLiquidStaked(cmd.Context(), &types.QueryTotalLiquidStaked{}) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetQueryStakingTokenizeShareLockInfoCmd returns the tokenize share lock status for a user +func GetQueryStakingTokenizeShareLockInfoCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "tokenize-share-lock-info [address]", + Args: cobra.ExactArgs(1), + Short: "Query tokenize share lock information", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the status of a tokenize share lock for a given account +Example: +$ %s query staking tokenize-share-lock-info %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.AppName, bech32PrefixAccAddr, + ), + ), + PersistentPreRunE: QueryPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + address := args[0] + if _, err := sdk.AccAddressFromBech32(address); err != nil { + return err + } + + res, err := cl.Query().Staking().TokenizeShareLockInfo( + cmd.Context(), + &types.QueryTokenizeShareLockInfo{Address: address}, + ) + if err != nil { + return err + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/staking_tx.go b/go/cli/staking_tx.go new file mode 100644 index 00000000..2a8c5b3c --- /dev/null +++ b/go/cli/staking_tx.go @@ -0,0 +1,960 @@ +package cli + +import ( + "errors" + "fmt" + "os" + "strconv" + "strings" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + "github.com/spf13/cobra" + flag "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + cflags "pkg.akt.dev/go/cli/flags" + cclient "pkg.akt.dev/go/node/client/v1beta3" +) + +// default values +var ( + DefaultTokens = sdk.DefaultPowerReduction.Mul(sdk.NewInt(100)) + defaultAmount = DefaultTokens.String() + sdk.DefaultBondDenom + defaultCommissionRate = "0.1" + defaultCommissionMaxRate = "0.2" + defaultCommissionMaxChangeRate = "0.01" +) + +// GetTxStakingCmd returns a root CLI command handler for all x/staking transaction commands. +func GetTxStakingCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: stakingtypes.ModuleName, + Short: "Staking transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetTxStakingCreateValidatorCmd(), + GetTxStakingEditValidatorCmd(), + GetTxStakingDelegateCmd(), + GetTxStakingRedelegateCmd(), + GetTxStakingUnbondCmd(), + GetTxStakingUnbondValidatorCmd(), + GetTxStakingCancelUnbondingDelegation(), + GetTxStakingTokenizeSharesCmd(), + GetTxStakingRedeemTokensCmd(), + GetTxStakingTransferTokenizeShareRecordCmd(), + GetTxStakingDisableTokenizeShares(), + GetTxStakingEnableTokenizeShares(), + GetTxStakingValidatorBondCmd(), + ) + + return cmd +} + +// GetTxStakingCreateValidatorCmd returns a CLI command handler for creating a MsgCreateValidator transaction. +func GetTxStakingCreateValidatorCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-validator", + Short: "create new validator", + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + msg, err := newBuildCreateValidatorMsg(cctx, cmd.Flags()) + if err != nil { + return err + } + + var opts []cclient.BroadcastOption + + genOnly, _ := cmd.Flags().GetBool(cflags.FlagGenerateOnly) + if genOnly { + ip, _ := cmd.Flags().GetString(cflags.FlagIP) + p2pPort, _ := cmd.Flags().GetUint(cflags.FlagP2PPort) + nodeID, _ := cmd.Flags().GetString(cflags.FlagNodeID) + + if nodeID != "" && ip != "" && p2pPort > 0 { + opts = append(opts, cclient.WithNote(fmt.Sprintf("%s@%s:%d", nodeID, ip, p2pPort))) + } + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cmd.Flags().AddFlagSet(cflags.FlagSetPublicKey()) + cmd.Flags().AddFlagSet(cflags.FlagSetAmount()) + cmd.Flags().AddFlagSet(cflags.FlagSetDescriptionCreate()) + cmd.Flags().AddFlagSet(cflags.FlagSetCommissionCreate()) + + cmd.Flags().String(cflags.FlagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", cflags.FlagGenerateOnly)) + cmd.Flags().String(cflags.FlagNodeID, "", "The node's ID") + cflags.AddTxFlagsToCmd(cmd) + + _ = cmd.MarkFlagRequired(cflags.FlagFrom) + _ = cmd.MarkFlagRequired(cflags.FlagAmount) + _ = cmd.MarkFlagRequired(cflags.FlagPubKey) + _ = cmd.MarkFlagRequired(cflags.FlagMoniker) + + return cmd +} + +// GetTxStakingEditValidatorCmd returns a CLI command handler for creating a MsgEditValidator transaction. +func GetTxStakingEditValidatorCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "edit-validator", + Short: "edit an existing validator account", + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + valAddr := cctx.GetFromAddress() + moniker, _ := cmd.Flags().GetString(cflags.FlagEditMoniker) + identity, _ := cmd.Flags().GetString(cflags.FlagIdentity) + website, _ := cmd.Flags().GetString(cflags.FlagWebsite) + security, _ := cmd.Flags().GetString(cflags.FlagSecurityContact) + details, _ := cmd.Flags().GetString(cflags.FlagDetails) + description := stakingtypes.NewDescription(moniker, identity, website, security, details) + + var newRate *sdk.Dec + + commissionRate, _ := cmd.Flags().GetString(cflags.FlagCommissionRate) + if commissionRate != "" { + rate, err := sdkmath.LegacyNewDecFromStr(commissionRate) + if err != nil { + return fmt.Errorf("invalid new commission rate: %v", err) + } + + newRate = &rate + } + + msg := stakingtypes.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cmd.Flags().AddFlagSet(cflags.FlagSetDescriptionEdit()) + cmd.Flags().AddFlagSet(cflags.FlagSetCommissionUpdate()) + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingDelegateCmd returns a CLI command handler for creating a MsgDelegate transaction. +func GetTxStakingDelegateCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "delegate [validator-addr] [amount]", + Args: cobra.ExactArgs(2), + Short: "Delegate liquid tokens to a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Delegate an amount of liquid coins to a validator from your wallet. + +Example: +$ %s tx staking delegate %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --from mykey +`, + version.AppName, bech32PrefixValAddr, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + amount, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return err + } + + delAddr := cctx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + msg := stakingtypes.NewMsgDelegate(delAddr, valAddr, amount) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingRedelegateCmd returns a CLI command handler for creating a MsgBeginRedelegate transaction. +func GetTxStakingRedelegateCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "redelegate [src-validator-addr] [dst-validator-addr] [amount]", + Short: "Redelegate illiquid tokens from one validator to another", + Args: cobra.ExactArgs(3), + Long: strings.TrimSpace( + fmt.Sprintf(`Redelegate an amount of illiquid staking tokens from one validator to another. + +Example: +$ %s tx staking redelegate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100stake --from mykey +`, + version.AppName, bech32PrefixValAddr, bech32PrefixValAddr, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + delAddr := cctx.GetFromAddress() + valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + valDstAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoinNormalized(args[2]) + if err != nil { + return err + } + + msg := stakingtypes.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingUnbondCmd returns a CLI command handler for creating a MsgUndelegate transaction. +func GetTxStakingUnbondCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "unbond [validator-addr] [amount]", + Short: "Unbond shares from a validator", + Args: cobra.ExactArgs(2), + Long: strings.TrimSpace( + fmt.Sprintf(`Unbond an amount of bonded shares from a validator. + +Example: +$ %s tx staking unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey +`, + version.AppName, bech32PrefixValAddr, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + delAddr := cctx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return err + } + + msg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, amount) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func GetTxStakingUnbondValidatorCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "unbond-validator", + Short: "Unbond a validator", + Args: cobra.ExactArgs(0), + Long: strings.TrimSpace( + fmt.Sprintf(`Unbond a validator. + +Example: +$ %s tx staking unbond-validator --from mykey +`, + version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + msg := stakingtypes.NewMsgUnbondValidator(sdk.ValAddress(cctx.GetFromAddress())) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingCancelUnbondingDelegation returns a CLI command handler for creating a MsgCancelUnbondingDelegation transaction. +func GetTxStakingCancelUnbondingDelegation() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "cancel-unbond [validator-addr] [amount] [creation-height]", + Short: "Cancel unbonding delegation and delegate back to the validator", + Args: cobra.ExactArgs(3), + Long: strings.TrimSpace( + fmt.Sprintf(`Cancel Unbonding Delegation and delegate back to the validator. + +Example: +$ %s tx staking cancel-unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake 2 --from mykey +`, + version.AppName, bech32PrefixValAddr, + ), + ), + Example: fmt.Sprintf(`$ %s tx staking cancel-unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake 2 --from mykey`, + version.AppName, bech32PrefixValAddr), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + delAddr := cctx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return err + } + + creationHeight, err := strconv.ParseInt(args[2], 10, 64) + if err != nil { + return errorsmod.Wrap(fmt.Errorf("invalid height: %d", creationHeight), "invalid height") + } + + msg := stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, valAddr, creationHeight, amount) + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func newBuildCreateValidatorMsg(clientCtx client.Context, fs *flag.FlagSet) (*stakingtypes.MsgCreateValidator, error) { + fAmount, _ := fs.GetString(cflags.FlagAmount) + amount, err := sdk.ParseCoinNormalized(fAmount) + if err != nil { + return nil, err + } + + valAddr := clientCtx.GetFromAddress() + pkStr, err := fs.GetString(cflags.FlagPubKey) + if err != nil { + return nil, err + } + + var pk cryptotypes.PubKey + if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(pkStr), &pk); err != nil { + return nil, err + } + + moniker, _ := fs.GetString(cflags.FlagMoniker) + identity, _ := fs.GetString(cflags.FlagIdentity) + website, _ := fs.GetString(cflags.FlagWebsite) + security, _ := fs.GetString(cflags.FlagSecurityContact) + details, _ := fs.GetString(cflags.FlagDetails) + description := stakingtypes.NewDescription( + moniker, + identity, + website, + security, + details, + ) + + // get the initial validator commission parameters + rateStr, _ := fs.GetString(cflags.FlagCommissionRate) + maxRateStr, _ := fs.GetString(cflags.FlagCommissionMaxRate) + maxChangeRateStr, _ := fs.GetString(cflags.FlagCommissionMaxChangeRate) + + commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) + if err != nil { + return nil, err + } + + msg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(valAddr), pk, amount, description, commissionRates, + ) + if err != nil { + return nil, err + } + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + return msg, nil +} + +// CreateValidatorMsgFlagSet returns the flagset, particular flags, and a description of defaults +// this is anticipated to be used with the gen-tx +func CreateValidatorMsgFlagSet(ipDefault string) (fs *flag.FlagSet, defaultsDesc string) { + fsCreateValidator := flag.NewFlagSet("", flag.ContinueOnError) + fsCreateValidator.String(cflags.FlagIP, ipDefault, "The node's public P2P IP") + fsCreateValidator.Uint(cflags.FlagP2PPort, 26656, "The node's public P2P port") + fsCreateValidator.String(cflags.FlagNodeID, "", "The node's NodeID") + fsCreateValidator.String(cflags.FlagMoniker, "", "The validator's (optional) moniker") + fsCreateValidator.String(cflags.FlagWebsite, "", "The validator's (optional) website") + fsCreateValidator.String(cflags.FlagSecurityContact, "", "The validator's (optional) security contact email") + fsCreateValidator.String(cflags.FlagDetails, "", "The validator's (optional) details") + fsCreateValidator.String(cflags.FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") + fsCreateValidator.AddFlagSet(cflags.FlagSetCommissionCreate()) + fsCreateValidator.AddFlagSet(cflags.FlagSetAmount()) + fsCreateValidator.AddFlagSet(cflags.FlagSetPublicKey()) + + defaultsDesc = fmt.Sprintf(` + delegation amount: %s + commission rate: %s + commission max rate: %s + commission max change rate: %s +`, defaultAmount, defaultCommissionRate, + defaultCommissionMaxRate, defaultCommissionMaxChangeRate) + + return fsCreateValidator, defaultsDesc +} + +type TxCreateValidatorConfig struct { + ChainID string + NodeID string + Moniker string + + Amount string + + CommissionRate string + CommissionMaxRate string + CommissionMaxChangeRate string + + PubKey cryptotypes.PubKey + + IP string + P2PPort uint + Website string + SecurityContact string + Details string + Identity string +} + +func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, chainID string, valPubKey cryptotypes.PubKey) (TxCreateValidatorConfig, error) { + c := TxCreateValidatorConfig{} + + ip, err := flagSet.GetString(cflags.FlagIP) + if err != nil { + return c, err + } + + if ip == "" { + _, _ = fmt.Fprintf(os.Stderr, "failed to retrieve an external IP; the tx's memo field will be unset") + } + + p2pPort, err := flagSet.GetUint(cflags.FlagP2PPort) + if err != nil { + return c, err + } + + website, err := flagSet.GetString(cflags.FlagWebsite) + if err != nil { + return c, err + } + + securityContact, err := flagSet.GetString(cflags.FlagSecurityContact) + if err != nil { + return c, err + } + + details, err := flagSet.GetString(cflags.FlagDetails) + if err != nil { + return c, err + } + + identity, err := flagSet.GetString(cflags.FlagIdentity) + if err != nil { + return c, err + } + + c.Amount, err = flagSet.GetString(cflags.FlagAmount) + if err != nil { + return c, err + } + + c.CommissionRate, err = flagSet.GetString(cflags.FlagCommissionRate) + if err != nil { + return c, err + } + + c.CommissionMaxRate, err = flagSet.GetString(cflags.FlagCommissionMaxRate) + if err != nil { + return c, err + } + + c.CommissionMaxChangeRate, err = flagSet.GetString(cflags.FlagCommissionMaxChangeRate) + if err != nil { + return c, err + } + + c.IP = ip + c.P2PPort = p2pPort + c.Website = website + c.SecurityContact = securityContact + c.Identity = identity + c.NodeID = nodeID + c.PubKey = valPubKey + c.Website = website + c.SecurityContact = securityContact + c.Details = details + c.Identity = identity + c.ChainID = chainID + c.Moniker = moniker + + if c.Amount == "" { + c.Amount = defaultAmount + } + + if c.CommissionRate == "" { + c.CommissionRate = defaultCommissionRate + } + + if c.CommissionMaxRate == "" { + c.CommissionMaxRate = defaultCommissionMaxRate + } + + if c.CommissionMaxChangeRate == "" { + c.CommissionMaxChangeRate = defaultCommissionMaxChangeRate + } + + return c, nil +} + +// BuildCreateValidatorMsg makes a new MsgCreateValidator. +func BuildCreateValidatorMsg(clientCtx client.Context, config TxCreateValidatorConfig, txBldr tx.Factory, generateOnly bool) (tx.Factory, sdk.Msg, error) { + amounstStr := config.Amount + amount, err := sdk.ParseCoinNormalized(amounstStr) + if err != nil { + return txBldr, nil, err + } + + valAddr := clientCtx.GetFromAddress() + description := stakingtypes.NewDescription( + config.Moniker, + config.Identity, + config.Website, + config.SecurityContact, + config.Details, + ) + + // get the initial validator commission parameters + rateStr := config.CommissionRate + maxRateStr := config.CommissionMaxRate + maxChangeRateStr := config.CommissionMaxChangeRate + commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) + if err != nil { + return txBldr, nil, err + } + + msg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(valAddr), + config.PubKey, + amount, + description, + commissionRates, + ) + if err != nil { + return txBldr, msg, err + } + + if generateOnly { + ip := config.IP + p2pPort := config.P2PPort + nodeID := config.NodeID + + if nodeID != "" && ip != "" && p2pPort > 0 { + txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:%d", nodeID, ip, p2pPort)) + } + } + + return txBldr, msg, nil +} + +// GetTxStakingTokenizeSharesCmd defines a command for tokenizing shares from a validator. +func GetTxStakingTokenizeSharesCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "tokenize-share [validator-addr] [amount] [rewardOwner]", + Short: "Tokenize delegation to share tokens", + Args: cobra.ExactArgs(3), + Long: strings.TrimSpace( + fmt.Sprintf(`Tokenize delegation to share tokens. + +Example: +$ %s tx staking tokenize-share %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey +`, + version.AppName, bech32PrefixValAddr, bech32PrefixAccAddr, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + delAddr := cctx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return err + } + + rewardOwner, err := sdk.AccAddressFromBech32(args[2]) + if err != nil { + return err + } + + msg := &stakingtypes.MsgTokenizeShares{ + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Amount: amount, + TokenizedShareOwner: rewardOwner.String(), + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingRedeemTokensCmd defines a command for redeeming tokens from a validator for shares. +func GetTxStakingRedeemTokensCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "redeem-tokens [amount]", + Short: "Redeem specified amount of share tokens to delegation", + Args: cobra.ExactArgs(1), + Long: strings.TrimSpace( + fmt.Sprintf(`Redeem specified amount of share tokens to delegation. + +Example: +$ %s tx staking redeem-tokens 100sharetoken --from mykey +`, + version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + delAddr := cctx.GetFromAddress() + + amount, err := sdk.ParseCoinNormalized(args[0]) + if err != nil { + return err + } + + msg := &stakingtypes.MsgRedeemTokensForShares{ + DelegatorAddress: delAddr.String(), + Amount: amount, + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingTransferTokenizeShareRecordCmd defines a command to transfer ownership of TokenizeShareRecord +func GetTxStakingTransferTokenizeShareRecordCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "transfer-tokenize-share-record [record-id] [new-owner]", + Short: "Transfer ownership of TokenizeShareRecord", + Args: cobra.ExactArgs(2), + Long: strings.TrimSpace( + fmt.Sprintf(`Transfer ownership of TokenizeShareRecord. + +Example: +$ %s tx staking transfer-tokenize-share-record 1 %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey +`, + version.AppName, bech32PrefixAccAddr, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + recordID, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + + ownerAddr, err := sdk.AccAddressFromBech32(args[1]) + if err != nil { + return err + } + + msg := &stakingtypes.MsgTransferTokenizeShareRecord{ + Sender: cctx.GetFromAddress().String(), + TokenizeShareRecordId: uint64(recordID), + NewOwner: ownerAddr.String(), + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingDisableTokenizeShares defines a command to disable tokenization for an address +func GetTxStakingDisableTokenizeShares() *cobra.Command { + cmd := &cobra.Command{ + Use: "disable-tokenize-shares", + Short: "Disable tokenization of shares", + Args: cobra.ExactArgs(0), + Long: strings.TrimSpace( + fmt.Sprintf(`Disables the tokenization of shares for an address. The account +must explicitly re-enable if they wish to tokenize again, at which point they must wait +the chain's unbonding period. + +Example: +$ %s tx staking disable-tokenize-shares --from mykey +`, version.AppName), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + msg := &stakingtypes.MsgDisableTokenizeShares{ + DelegatorAddress: cctx.GetFromAddress().String(), + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingEnableTokenizeShares defines a command to re-enable tokenization for an address +func GetTxStakingEnableTokenizeShares() *cobra.Command { + cmd := &cobra.Command{ + Use: "enable-tokenize-shares", + Short: "Enable tokenization of shares", + Args: cobra.ExactArgs(0), + Long: strings.TrimSpace( + fmt.Sprintf(`Enables the tokenization of shares for an address after +it had been disable. This transaction queues the enablement of tokenization, but +the address must wait 1 unbonding period from the time of this transaction before +tokenization is permitted. + +Example: +$ %s tx staking enable-tokenize-shares --from mykey +`, version.AppName), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + msg := &stakingtypes.MsgEnableTokenizeShares{ + DelegatorAddress: cctx.GetFromAddress().String(), + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetTxStakingValidatorBondCmd defines a command to mark a delegation as a validator self bond +func GetTxStakingValidatorBondCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "validator-bond [validator]", + Short: "Mark a delegation as a validator self-bond", + Args: cobra.ExactArgs(1), + Long: strings.TrimSpace( + fmt.Sprintf(`Mark a delegation as a validator self-bond. + +Example: +$ %s tx staking validator-bond cosmosvaloper13h5xdxhsdaugwdrkusf8lkgu406h8t62jkqv3h --from mykey +`, + version.AppName, + ), + ), + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + msg := &stakingtypes.MsgValidatorBond{ + DelegatorAddress: cctx.GetFromAddress().String(), + ValidatorAddress: args[0], + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cflags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commission stakingtypes.CommissionRates, err error) { + if rateStr == "" || maxRateStr == "" || maxChangeRateStr == "" { + return commission, errors.New("must specify all validator commission parameters") + } + + rate, err := sdkmath.LegacyNewDecFromStr(rateStr) + if err != nil { + return commission, err + } + + maxRate, err := sdkmath.LegacyNewDecFromStr(maxRateStr) + if err != nil { + return commission, err + } + + maxChangeRate, err := sdkmath.LegacyNewDecFromStr(maxChangeRateStr) + if err != nil { + return commission, err + } + + commission = stakingtypes.NewCommissionRates(rate, maxRate, maxChangeRate) + + return commission, nil +} diff --git a/go/cli/suite_test.go b/go/cli/suite_test.go new file mode 100644 index 00000000..49bca77f --- /dev/null +++ b/go/cli/suite_test.go @@ -0,0 +1,28 @@ +package cli_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" +) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + encCfg testutilmod.TestEncodingConfig + baseCtx client.Context + cctx client.Context +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(AuthCLITestSuite)) + suite.Run(t, new(AuthzCLITestSuite)) + suite.Run(t, new(BankCLITestSuite)) + suite.Run(t, new(GovCLITestSuite)) + suite.Run(t, new(GenesisCLITestSuite)) +} diff --git a/go/cli/test_helpers.go b/go/cli/test_helpers.go index 1b374da2..cff25076 100644 --- a/go/cli/test_helpers.go +++ b/go/cli/test_helpers.go @@ -1,21 +1,12 @@ package cli import ( - "context" "fmt" - "strings" - "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - sdktest "github.com/cosmos/cosmos-sdk/testutil" - "github.com/cosmos/cosmos-sdk/x/bank/client/cli" cflags "pkg.akt.dev/go/cli/flags" dv1 "pkg.akt.dev/go/node/deployment/v1" - dv1beta4 "pkg.akt.dev/go/node/deployment/v1beta4" mtypes "pkg.akt.dev/go/node/market/v1" ) @@ -26,10 +17,29 @@ func TestFlags() FlagsSet { } func (df FlagsSet) With(flags ...string) FlagsSet { - res := make([]string, len(df)+len(flags)) + res := make([]string, len(df), len(df)+len(flags)) + + copy(res, df) + res = append(res, flags...) + + return res +} + +func (df FlagsSet) Append(rhs FlagsSet) FlagsSet { + res := make([]string, len(df), len(df)+len(rhs)) + + copy(res, df) + res = append(res, rhs...) + + return res +} + +func (df FlagsSet) WithGas(val int) FlagsSet { + res := make([]string, len(df), len(df)+3) copy(res, df) - copy(res, flags) + + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGas, val)) return res } @@ -46,575 +56,670 @@ func (df FlagsSet) WithGasAutoFlags() FlagsSet { return res } -func (df FlagsSet) WithSkipConfirm() FlagsSet { +func (df FlagsSet) WithGenerateOnly() FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=true", cflags.FlagSkipConfirmation)) + res = append(res, fmt.Sprintf("--%s=true", cflags.FlagGenerateOnly)) return res } -func (df FlagsSet) WithBroadcastModeBlock() FlagsSet { +func (df FlagsSet) WithOverwrite() FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagBroadcastMode, cflags.BroadcastBlock)) + res = append(res, fmt.Sprintf("--%s=true", cflags.FlagOverwrite)) return res } -func (df FlagsSet) WithDeposit(coin sdk.Coin) FlagsSet { +func (df FlagsSet) WithOffline() FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagDeposit, coin)) + res = append(res, fmt.Sprintf("--%s=true", cflags.FlagOffline)) return res } -func (df FlagsSet) WithPrice(coin sdk.DecCoin) FlagsSet { +func (df FlagsSet) WithAccountNumber(val uint64) FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagPrice, coin)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagAccountNumber, val)) return res } -func (df FlagsSet) WithFrom(acc sdk.Address) FlagsSet { +func (df FlagsSet) WithSequence(val uint64) FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagFrom, acc)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagSequence, val)) return res } -func (df FlagsSet) WithDepositor(acc sdk.Address) FlagsSet { +func (df FlagsSet) WithSkipConfirm() FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagDepositorAccount, acc)) + res = append(res, fmt.Sprintf("--%s=true", cflags.FlagSkipConfirmation)) return res } -func (df FlagsSet) WithOutput(val string) FlagsSet { +func (df FlagsSet) WithSignatureOnly() FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOutput, val)) + res = append(res, fmt.Sprintf("--%s=true", cflags.FlagSigOnly)) return res } -func (df FlagsSet) WithSerial(val string) FlagsSet { +func (df FlagsSet) WithNote(val string) FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagSerial, val)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagNote, val)) return res } -func (df FlagsSet) WithOwner(val string) FlagsSet { +func (df FlagsSet) WithSpendLimit(val string) FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagSpendLimit, val)) return res } -func (df FlagsSet) WithProvider(val string) FlagsSet { +func (df FlagsSet) WithEvents(val string) FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagProvider, val)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagEvents, val)) return res } -func (df FlagsSet) WithDseq(val uint64) FlagsSet { +func (df FlagsSet) WithDenom(val string) FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagDenom, val)) return res } -func (df FlagsSet) WithGseq(val uint32) FlagsSet { +func (df FlagsSet) WithMsgType(val string) FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagMsgType, val)) return res } -func (df FlagsSet) WithOseq(val uint32) FlagsSet { +func (df FlagsSet) WithBroadcastModeSync() FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagOSeq, val)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagBroadcastMode, cflags.BroadcastSync)) return res } -func (df FlagsSet) WithDeploymentID(val dv1.DeploymentID) FlagsSet { - res := make([]string, len(df), len(df)+2) +func (df FlagsSet) WithExpiration(val int64) FlagsSet { + res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagExpiration, val)) return res } -func (df FlagsSet) WithGroupID(val dv1.GroupID) FlagsSet { - res := make([]string, len(df), len(df)+3) +func (df FlagsSet) WithAllowList(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val.GSeq)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagAllowList, val)) return res } -func (df FlagsSet) WithOrderID(val mtypes.OrderID) FlagsSet { - res := make([]string, len(df), len(df)+4) +func (df FlagsSet) WithAllowedValidators(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val.GSeq)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagOSeq, val.OSeq)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagAllowedValidators, val)) return res } -func (df FlagsSet) WithBidID(val mtypes.BidID) FlagsSet { - res := make([]string, len(df), len(df)+5) +func (df FlagsSet) WithDenyValidators(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val.GSeq)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagOSeq, val.OSeq)) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagProvider, val.Provider)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagDenyValidators, val)) return res } -func (df FlagsSet) WithLeaseID(val mtypes.LeaseID) FlagsSet { - res := make([]string, len(df), len(df)+5) +func (df FlagsSet) WithSignMode(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val.GSeq)) - res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagOSeq, val.OSeq)) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagProvider, val.Provider)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagSignMode, val)) return res } -func (df FlagsSet) WithState(val string) FlagsSet { +func (df FlagsSet) WithTip(val sdk.Coin) FlagsSet { res := make([]string, len(df), len(df)+1) copy(res, df) - res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagState, val)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagTip, val.String())) return res } -func (df FlagsSet) WithOutputJSON() FlagsSet { - return df.WithOutput("json") -} +func (df FlagsSet) WithAux() FlagsSet { + res := make([]string, len(df), len(df)+1) -// ExecTxTestCLICmd builds the client context, mocks the output and executes the command. -func ExecTxTestCLICmd(ctx context.Context, cctx client.Context, cmd *cobra.Command, extraArgs ...string) (sdktest.BufferWriter, error) { - _, out := sdktest.ApplyMockIO(cmd) + copy(res, df) - { - dupFlags := make(map[string]bool) - for _, arg := range extraArgs { - if !strings.HasPrefix(arg, "--") { - continue - } + res = append(res, fmt.Sprintf("--%s=true", cflags.FlagAux)) - arg = strings.TrimPrefix(arg, "--") - tokens := strings.Split(arg, "=") + return res +} - if _, exists := dupFlags[tokens[0]]; exists { - return out, fmt.Errorf("test: duplicated flag \"%s\"", tokens[0]) - } +func (df FlagsSet) WithMultisig(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - dupFlags[tokens[0]] = true - } - } + copy(res, df) - cmd.SetArgs(extraArgs) - err := cmd.ParseFlags(extraArgs) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagMultisig, val)) - if err != nil { - return out, err - } + return res +} - cctx = cctx.WithOutput(out) +func (df FlagsSet) WithMetadata(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - if ctx == nil { - ctx = context.Background() - } + copy(res, df) - opts, err := cflags.ClientOptionsFromFlags(cmd.Flags()) - if err != nil { - return out, err - } + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagMetadata, val)) - ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{}) - ctx = context.WithValue(ctx, server.ServerContextKey, server.NewDefaultContext()) - cmd.SetContext(ctx) + return res +} - if err = client.SetCmdClientContextHandler(cctx, cmd); err != nil { - return out, err - } +func (df FlagsSet) WithProposal(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - tctx, err := client.GetClientTxContext(cmd) - if err != nil { - return out, err - } + copy(res, df) - cl, err := DiscoverClient(ctx, tctx, opts...) - if err != nil { - return out, err - } + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagProposal, val)) - ctx = context.WithValue(ctx, ContextTypeClient, cl) + return res +} - cmd.SetContext(ctx) +func (df FlagsSet) WithTitle(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) + + copy(res, df) - if err := cmd.Execute(); err != nil { - return out, err - } + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagTitle, val)) - return out, nil + return res } -// ExecQueryTestCLICmd builds the client context, mocks the output and executes the command. -func ExecQueryTestCLICmd(ctx context.Context, cctx client.Context, cmd *cobra.Command, extraArgs ...string) (sdktest.BufferWriter, error) { - _, out := sdktest.ApplyMockIO(cmd) +func (df FlagsSet) WithType(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - { - dupFlags := make(map[string]bool) - for _, arg := range extraArgs { - if !strings.HasPrefix(arg, "--") { - continue - } + copy(res, df) - arg = strings.TrimPrefix(arg, "--") - tokens := strings.Split(arg, "=") + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagType, val)) - if _, exists := dupFlags[tokens[0]]; exists { - return out, fmt.Errorf("test: duplicated flag \"%s\"", tokens[0]) - } + return res +} - dupFlags[tokens[0]] = true - } - } +func (df FlagsSet) WithProposalType(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - cmd.SetArgs(extraArgs) - err := cmd.ParseFlags(extraArgs) + copy(res, df) - if err != nil { - return out, err - } + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagProposalType, val)) - cctx = cctx.WithOutput(out) + return res +} - if ctx == nil { - ctx = context.Background() - } +func (df FlagsSet) WithDescription(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{}) - ctx = context.WithValue(ctx, server.ServerContextKey, server.NewDefaultContext()) - cmd.SetContext(ctx) + copy(res, df) - if err = client.SetCmdClientContextHandler(cctx, cmd); err != nil { - return out, err - } + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagDescription, val)) - qctx, err := client.GetClientQueryContext(cmd) - if err != nil { - return out, err - } + return res +} - qcl, err := DiscoverQueryClient(ctx, qctx) - if err != nil { - return out, err - } +func (df FlagsSet) WithBroadcastModeBlock() FlagsSet { + res := make([]string, len(df), len(df)+1) - ctx = context.WithValue(ctx, ContextTypeQueryClient, qcl) - cmd.SetContext(ctx) + copy(res, df) - if err := cmd.Execute(); err != nil { - return out, err - } + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagBroadcastMode, cflags.BroadcastBlock)) - return out, nil + return res } -func MsgSendExec(ctx context.Context, cctx client.Context, from, to, amount fmt.Stringer, extraArgs ...string) (sdktest.BufferWriter, error) { - args := []string{from.String(), to.String(), amount.String()} - args = append(args, extraArgs...) +func (df FlagsSet) WithHome(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - return ExecTxTestCLICmd(ctx, cctx, NewBankSendTxCmd(), args...) -} + copy(res, df) -func QueryBalancesExec(ctx context.Context, cctx client.Context, address fmt.Stringer, extraArgs ...string) (sdktest.BufferWriter, error) { - args := []string{address.String(), fmt.Sprintf("--%s=json", cflags.FlagOutput)} - args = append(args, extraArgs...) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagHome, val)) - return ExecQueryTestCLICmd(ctx, cctx, cli.GetBalancesCmd(), args...) + return res } -// ======= Certificate commands ========= - -// TxGenerateServerExec is used for testing create server certificate tx -func TxGenerateServerExec(ctx context.Context, cctx client.Context, host string, extraArgs ...string) (sdktest.BufferWriter, error) { - var args []string +func (df FlagsSet) WithChainID(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - if len(host) != 0 { // for testing purposes, of passing no arguments - args = []string{host} - } + copy(res, df) - args = append(args, extraArgs...) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagChainID, val)) - return ExecTxTestCLICmd(ctx, cctx, cmdGenerateServer(), args...) + return res } -// TxGenerateClientExec is used for testing create client certificate tx -func TxGenerateClientExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdGenerateClient(), extraArgs...) -} +func (df FlagsSet) WithFees(coins sdk.Coins) FlagsSet { + res := make([]string, len(df), len(df)+1) -// TxPublishServerExec is used for testing create server certificate tx -func TxPublishServerExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdPublishServer(), extraArgs...) -} + copy(res, df) -// TxPublishClientExec is used for testing create client certificate tx -func TxPublishClientExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdPublishClient(), extraArgs...) -} + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagFees, coins.String())) -// TxRevokeServerExec is used for testing create server certificate tx -func TxRevokeServerExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdRevokeServer(), extraArgs...) + return res } -// TxRevokeClientExec is used for testing create client certificate tx -func TxRevokeClientExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdRevokeClient(), extraArgs...) -} +func (df FlagsSet) WithDeposit(coin sdk.Coin) FlagsSet { + res := make([]string, len(df), len(df)+1) -// QueryCertificatesExec is used for testing certificates query -func QueryCertificatesExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetCertificates(), extraArgs...) -} + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagDeposit, coin)) -// QueryCertificateExec is used for testing certificate query -func QueryCertificateExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetCertificates(), extraArgs...) + return res } -// ======= Deployment commands ========= +func (df FlagsSet) WithPrice(coin sdk.DecCoin) FlagsSet { + res := make([]string, len(df), len(df)+1) -// TxCreateDeploymentExec is used for testing create deployment tx -func TxCreateDeploymentExec(ctx context.Context, cctx client.Context, filePath string, extraArgs ...string) (sdktest.BufferWriter, error) { - args := []string{ - filePath, - } + copy(res, df) - args = append(args, extraArgs...) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagPrice, coin)) - return ExecTxTestCLICmd(ctx, cctx, cmdDeploymentCreate(), args...) + return res } -// TxUpdateDeploymentExec is used for testing update deployment tx -func TxUpdateDeploymentExec(ctx context.Context, cctx client.Context, filePath string, extraArgs ...string) (sdktest.BufferWriter, error) { - args := []string{ - filePath, - } +func (df FlagsSet) WithFrom(acc string) FlagsSet { + res := make([]string, len(df), len(df)+1) - args = append(args, extraArgs...) + copy(res, df) - return ExecTxTestCLICmd(ctx, cctx, cmdDeploymentUpdate(), args...) -} + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagFrom, acc)) -// TxCloseDeploymentExec is used for testing close deployment tx -// requires --dseq, --fees -func TxCloseDeploymentExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdDeploymentClose(), extraArgs...) + return res } -// TxDepositDeploymentExec is used for testing deposit deployment tx -func TxDepositDeploymentExec(ctx context.Context, cctx client.Context, deposit sdk.Coin, extraArgs ...string) (sdktest.BufferWriter, error) { - args := []string{ - deposit.String(), - } +func (df FlagsSet) WithDepositor(acc sdk.Address) FlagsSet { + res := make([]string, len(df), len(df)+1) - args = append(args, extraArgs...) + copy(res, df) - return ExecTxTestCLICmd(ctx, cctx, cmdDeploymentDeposit(), args...) -} + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagDepositorAccount, acc)) -// TxCloseGroupExec is used for testing close group tx -func TxCloseGroupExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdDeploymentGroupClose(), extraArgs...) + return res } -// QueryDeploymentsExec is used for testing deployments query -func QueryDeploymentsExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdDeployments(), extraArgs...) -} +func (df FlagsSet) WithOutput(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) -// QueryDeploymentExec is used for testing deployment query -func QueryDeploymentExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdDeployment(), extraArgs...) -} + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOutput, val)) -// QueryGroupExec is used for testing group query -func QueryGroupExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetGroup(), extraArgs...) + return res } -func TxGrantAuthorizationExec(ctx context.Context, cctx client.Context, grantee sdk.AccAddress, extraArgs ...string) (sdktest.BufferWriter, error) { - dmin, _ := dv1beta4.DefaultParams().MinDepositFor("uakt") +func (df FlagsSet) WithSerial(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - spendLimit := sdk.NewCoin(dmin.Denom, dmin.Amount.MulRaw(3)) - args := []string{ - grantee.String(), - spendLimit.String(), - } - args = append(args, extraArgs...) + copy(res, df) - return ExecTxTestCLICmd(ctx, cctx, cmdDeploymentGrantAuthorization(), args...) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagSerial, val)) + + return res } -func TxRevokeAuthorizationExec(ctx context.Context, cctx client.Context, grantee sdk.AccAddress, extraArgs ...string) (sdktest.BufferWriter, error) { - args := []string{ - grantee.String(), - } - args = append(args, extraArgs...) +func (df FlagsSet) WithOwner(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - return ExecTxTestCLICmd(ctx, cctx, cmdDeploymentRevokeAuthorization(), args...) -} + copy(res, df) -// ======= Market commands ============= + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val)) -// TxCreateBidExec is used for testing create bid tx -func TxCreateBidExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdMarketBidCreate(), extraArgs...) + return res } -// TxCloseBidExec is used for testing close bid tx -func TxCloseBidExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdMarketBidClose(), extraArgs...) +func (df FlagsSet) WithProvider(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagProvider, val)) + + return res } -// TxCreateLeaseExec is used for creating a lease -func TxCreateLeaseExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdMarketLeaseCreate(), extraArgs...) +func (df FlagsSet) WithDseq(val uint64) FlagsSet { + res := make([]string, len(df), len(df)+1) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val)) + + return res } -// TxCloseLeaseExec is used for testing close order tx -func TxCloseLeaseExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecTxTestCLICmd(ctx, cctx, cmdMarketLeaseClose(), extraArgs...) +func (df FlagsSet) WithGseq(val uint32) FlagsSet { + res := make([]string, len(df), len(df)+1) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val)) + + return res } -// QueryOrdersExec is used for testing orders query -func QueryOrdersExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetOrders(), args...) +func (df FlagsSet) WithOseq(val uint32) FlagsSet { + res := make([]string, len(df), len(df)+1) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagOSeq, val)) + + return res } -// QueryOrderExec is used for testing order query -func QueryOrderExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetOrder(), extraArgs...) +func (df FlagsSet) WithDeploymentID(val dv1.DeploymentID) FlagsSet { + res := make([]string, len(df), len(df)+2) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) + + return res } -// QueryBidsExec is used for testing bids query -func QueryBidsExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetBids(), args...) +func (df FlagsSet) WithGroupID(val dv1.GroupID) FlagsSet { + res := make([]string, len(df), len(df)+3) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val.GSeq)) + + return res } -// QueryBidExec is used for testing bid query -func QueryBidExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetBid(), extraArgs...) +func (df FlagsSet) WithOrderID(val mtypes.OrderID) FlagsSet { + res := make([]string, len(df), len(df)+4) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val.GSeq)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagOSeq, val.OSeq)) + + return res } -// QueryLeasesExec is used for testing leases query -func QueryLeasesExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetLeases(), args...) +func (df FlagsSet) WithBidID(val mtypes.BidID) FlagsSet { + res := make([]string, len(df), len(df)+5) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val.GSeq)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagOSeq, val.OSeq)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagProvider, val.Provider)) + + return res } -// QueryLeaseExec is used for testing lease query -func QueryLeaseExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetLease(), extraArgs...) +func (df FlagsSet) WithLeaseID(val mtypes.LeaseID) FlagsSet { + res := make([]string, len(df), len(df)+5) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagOwner, val.Owner)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagDSeq, val.DSeq)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagGSeq, val.GSeq)) + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagOSeq, val.OSeq)) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagProvider, val.Provider)) + + return res } -// ======= Provider commands =========== +func (df FlagsSet) WithState(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) -// TxCreateProviderExec is used for testing create provider tx -func TxCreateProviderExec(ctx context.Context, cctx client.Context, filepath string, extraArgs ...string) (sdktest.BufferWriter, error) { - args := []string{ - filepath, - } + copy(res, df) - args = append(args, extraArgs...) + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagState, val)) - return ExecTxTestCLICmd(ctx, cctx, cmdProviderCreate(), args...) + return res } -// TxUpdateProviderExec is used for testing update provider tx -func TxUpdateProviderExec(ctx context.Context, cctx client.Context, filepath string, extraArgs ...string) (sdktest.BufferWriter, error) { - args := []string{ - filepath, - } +func (df FlagsSet) WithStatus(val string) FlagsSet { + res := make([]string, len(df), len(df)+1) - args = append(args, extraArgs...) + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%s", cflags.FlagStatus, val)) - return ExecTxTestCLICmd(ctx, cctx, cmdProviderUpdate(), args...) + return res } -// QueryProvidersExec is used for testing providers query -func QueryProvidersExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetProviders(), args...) +func (df FlagsSet) WithHeight(val uint64) FlagsSet { + res := make([]string, len(df), len(df)+1) + + copy(res, df) + + res = append(res, fmt.Sprintf("--%s=%d", cflags.FlagHeight, val)) + + return res } -// QueryProviderExec is used for testing provider query -func QueryProviderExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { - return ExecQueryTestCLICmd(ctx, cctx, cmdGetProvider(), extraArgs...) +func (df FlagsSet) WithOutputJSON() FlagsSet { + return df.WithOutput("json") } + +func (df FlagsSet) WithOutputText() FlagsSet { + return df.WithOutput("text") +} + +// // ExecTestCLICmd builds the client context, mocks the output and executes the command. +// func ExecTestCLICmd(ctx context.Context, cctx client.Context, cmd *cobra.Command, extraArgs ...string) (sdktest.BufferWriter, error) { +// _, out := sdktest.ApplyMockIO(cmd) +// +// { +// dupFlags := make(map[string]bool) +// for _, arg := range extraArgs { +// if !strings.HasPrefix(arg, "--") { +// continue +// } +// +// arg = strings.TrimPrefix(arg, "--") +// tokens := strings.Split(arg, "=") +// +// if _, exists := dupFlags[tokens[0]]; exists { +// return out, fmt.Errorf("test: duplicated flag \"%s\"", tokens[0]) +// } +// +// dupFlags[tokens[0]] = true +// } +// } +// +// cmd.SetArgs(extraArgs) +// err := cmd.ParseFlags(extraArgs) +// +// if err != nil { +// return out, err +// } +// +// cctx = cctx.WithOutput(out) +// +// if ctx == nil { +// ctx = context.Background() +// } +// +// opts, err := cflags.ClientOptionsFromFlags(cmd.Flags()) +// if err != nil { +// return out, err +// } +// +// ctx = context.WithValue(ctx, cli.ClientContextKey, &client.Context{}) +// ctx = context.WithValue(ctx, server.ServerContextKey, server.NewDefaultContext()) +// cmd.SetContext(ctx) +// +// if err = client.SetCmdClientContextHandler(cctx, cmd); err != nil { +// return out, err +// } +// +// tctx, err := client.GetClientTxContext(cmd) +// if err != nil { +// return out, err +// } +// +// cl, err := DiscoverClient(ctx, tctx, opts...) +// if err != nil { +// return out, err +// } +// +// ctx = context.WithValue(ctx, ContextTypeClient, cl) +// +// cmd.SetContext(ctx) +// +// if err := cmd.Execute(); err != nil { +// return out, err +// } +// +// return out, nil +// } +// +// // ExecQueryTestCLICmd builds the client context, mocks the output and executes the command. +// func ExecQueryTestCLICmd(ctx context.Context, cctx client.Context, cmd *cobra.Command, extraArgs ...string) (sdktest.BufferWriter, error) { +// _, out := sdktest.ApplyMockIO(cmd) +// +// { +// dupFlags := make(map[string]bool) +// for _, arg := range extraArgs { +// if !strings.HasPrefix(arg, "--") { +// continue +// } +// +// arg = strings.TrimPrefix(arg, "--") +// tokens := strings.Split(arg, "=") +// +// if _, exists := dupFlags[tokens[0]]; exists { +// return out, fmt.Errorf("test: duplicated flag \"%s\"", tokens[0]) +// } +// +// dupFlags[tokens[0]] = true +// } +// } +// +// cmd.SetArgs(extraArgs) +// err := cmd.ParseFlags(extraArgs) +// +// if err != nil { +// return out, err +// } +// +// cctx = cctx.WithOutput(out) +// +// if ctx == nil { +// ctx = context.Background() +// } +// +// ctx = context.WithValue(ctx, cli.ClientContextKey, &client.Context{}) +// ctx = context.WithValue(ctx, server.ServerContextKey, server.NewDefaultContext()) +// cmd.SetContext(ctx) +// +// if err = client.SetCmdClientContextHandler(cctx, cmd); err != nil { +// return out, err +// } +// +// qctx, err := client.GetClientQueryContext(cmd) +// if err != nil { +// return out, err +// } +// +// qcl, err := DiscoverQueryClient(ctx, qctx) +// if err != nil { +// return out, err +// } +// +// ctx = context.WithValue(ctx, ContextTypeQueryClient, qcl) +// cmd.SetContext(ctx) +// +// if err := cmd.Execute(); err != nil { +// return out, err +// } +// +// return out, nil +// } +// +// func MsgSendExec(ctx context.Context, cctx client.Context, from, to, amount fmt.Stringer, extraArgs ...string) (sdktest.BufferWriter, error) { +// args := []string{from.String(), to.String(), amount.String()} +// args = append(args, extraArgs...) +// +// return ExecTestCLICmd(ctx, cctx, GetTxBankSendTxCmd(), args...) +// } +// +// func QueryBalancesExec(ctx context.Context, cctx client.Context, address fmt.Stringer, extraArgs ...string) (sdktest.BufferWriter, error) { +// args := []string{address.String(), fmt.Sprintf("--%s=json", cflags.FlagOutput)} +// args = append(args, extraArgs...) +// +// return ExecQueryTestCLICmd(ctx, cctx, cli.GetBalancesCmd(), args...) +// } diff --git a/go/cli/testutil/auth.go b/go/cli/testutil/auth.go new file mode 100644 index 00000000..4f0de83b --- /dev/null +++ b/go/cli/testutil/auth.go @@ -0,0 +1,90 @@ +package testutil + +import ( + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + + "pkg.akt.dev/go/cli" +) + +func TxSignExec(ctx context.Context, cctx client.Context, args ...string) (testutil.BufferWriter, error) { + cmd := cli.GetSignCommand() + + return ExecTestCLICmd(ctx, cctx, cmd, + cli.TestFlags(). + WithChainID(cctx.ChainID). + Append(args)...) +} + +func TxBroadcastExec(ctx context.Context, cctx client.Context, args ...string) (testutil.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetBroadcastCommand(), args...) +} + +func TxEncodeExec(ctx context.Context, cctx client.Context, filename string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + filename, + } + + return ExecTestCLICmd(ctx, cctx, cli.GetEncodeCommand(), append(args, extraArgs...)...) +} + +func TxValidateSignaturesExec(ctx context.Context, cctx client.Context, args ...string) (testutil.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetValidateSignaturesCommand(), + cli.TestFlags(). + WithChainID(cctx.ChainID). + Append(args)...) +} + +func TxMultiSignExec(ctx context.Context, cctx client.Context, args ...string) (testutil.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetAuthMultiSignCmd(), cli.TestFlags().Append(args).WithChainID(cctx.ChainID)...) +} + +func TxSignBatchExec(ctx context.Context, cctx client.Context, args ...string) (testutil.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetSignBatchCommand(), args...) +} + +func TxDecodeExec(ctx context.Context, cctx client.Context, encodedTx string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + encodedTx, + } + + return ExecTestCLICmd(ctx, cctx, cli.GetDecodeCommand(), append(args, extraArgs...)...) +} + +// TxAuxToFeeExec executes `GetAuxToFeeCommand` cli command with given args. +func TxAuxToFeeExec(ctx context.Context, cctx client.Context, filename string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + filename, + } + + return ExecTestCLICmd(ctx, cctx, cli.GetAuxToFeeCommand(), append(args, extraArgs...)...) +} + +// func QueryAccountExec(ctx context.Context, cctx client.Context, address fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) { +// args := []string{address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)} +// +// return ExecTestCLICmd(ctx, cctx, cli.GetAuthAccountCmd(), append(args, extraArgs...)...) +// } + +func TxMultiSignBatchExec(ctx context.Context, cctx client.Context, filename string, from string, sigFile1 string, sigFile2 string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + filename, + from, + sigFile1, + sigFile2, + } + + args = append(args, extraArgs...) + + return ExecTestCLICmd(ctx, cctx, cli.GetMultiSignBatchCmd(), args...) +} + +// DONTCOVER diff --git a/go/cli/testutil/authz.go b/go/cli/testutil/authz.go new file mode 100644 index 00000000..ec694ee5 --- /dev/null +++ b/go/cli/testutil/authz.go @@ -0,0 +1,15 @@ +package testutil + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/testutil" + + "pkg.akt.dev/go/cli" +) + +func ExecCreateGrant(ctx context.Context, cctx client.Context, args ...string) (testutil.BufferWriter, error) { + cmd := cli.GetTxAuthzGrantAuthorizationCmd() + return ExecTestCLICmd(ctx, cctx, cmd, args...) +} diff --git a/go/cli/testutil/certs.go b/go/cli/testutil/certs.go new file mode 100644 index 00000000..9a5cb6cb --- /dev/null +++ b/go/cli/testutil/certs.go @@ -0,0 +1,50 @@ +package testutil + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + sdktest "github.com/cosmos/cosmos-sdk/testutil" + + "pkg.akt.dev/go/cli" +) + +// TxGenerateServerExec is used for testing create server certificate tx +func TxGenerateServerExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxCertGenerateServerCmd(), args...) +} + +// TxGenerateClientExec is used for testing create client certificate tx +func TxGenerateClientExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxCertGenerateClientCmd(), args...) +} + +// TxPublishServerExec is used for testing create server certificate tx +func TxPublishServerExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxCertPublishServerCmd(), args...) +} + +// TxPublishClientExec is used for testing create client certificate tx +func TxPublishClientExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxCertPublishClientCmd(), args...) +} + +// TxRevokeServerExec is used for testing create server certificate tx +func TxRevokeServerExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxCertRevokeCmd(), args...) +} + +// TxRevokeClientExec is used for testing create client certificate tx +func TxRevokeClientExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxCertsRevokeClientCmd(), args...) +} + +// QueryCertificatesExec is used for testing certificates query +func QueryCertificatesExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryCertCertificatesCmd(), args...) +} + +// QueryCertificateExec is used for testing certificate query +func QueryCertificateExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryCertCertificatesCmd(), args...) +} diff --git a/go/cli/testutil/cmd.go b/go/cli/testutil/cmd.go new file mode 100644 index 00000000..6ad43d71 --- /dev/null +++ b/go/cli/testutil/cmd.go @@ -0,0 +1,77 @@ +package testutil + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/testutil" + + "pkg.akt.dev/go/cli" +) + +func execSetContext(ctx context.Context, cctx client.Context, cmd *cobra.Command, extraArgs ...string) (testutil.BufferWriter, error) { + cmd.SetArgs(extraArgs) + err := cmd.ParseFlags(extraArgs) + if err != nil { + return nil, err + } + + _, out := testutil.ApplyMockIO(cmd) + cctx = cctx.WithOutput(out) + + ctx = context.WithValue(ctx, cli.ClientContextKey, &client.Context{}) + ctx = context.WithValue(ctx, server.ServerContextKey, server.NewDefaultContext()) + + cmd.SetContext(ctx) + + if err := cli.SetCmdClientContextHandler(cctx, cmd); err != nil { + return nil, err + } + + return out, nil +} + +// ExecTestCLICmd builds the client context, mocks the output and executes the command. +func ExecTestCLICmd(ctx context.Context, cctx client.Context, cmd *cobra.Command, extraArgs ...string) (testutil.BufferWriter, error) { + { + dupFlags := make(map[string]bool) + for _, arg := range extraArgs { + if !strings.HasPrefix(arg, "--") { + continue + } + + arg = strings.TrimPrefix(arg, "--") + tokens := strings.Split(arg, "=") + + if _, exists := dupFlags[tokens[0]]; exists { + return nil, fmt.Errorf("test: duplicated flag \"%s\"", tokens[0]) + } + + dupFlags[tokens[0]] = true + } + } + + out, err := execSetContext(ctx, cctx, cmd, extraArgs...) + if err != nil { + return nil, err + } + + if err := cmd.Execute(); err != nil { + return out, err + } + + return out, nil +} + +func ExecSend(ctx context.Context, cctx client.Context, args ...string) (testutil.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxBankSendTxCmd(), args...) +} + +func QueryBalancesExec(ctx context.Context, cctx client.Context, address fmt.Stringer, args ...string) (testutil.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryBankBalancesCmd(), args...) +} diff --git a/go/cli/testutil/deployment.go b/go/cli/testutil/deployment.go new file mode 100644 index 00000000..28b895fa --- /dev/null +++ b/go/cli/testutil/deployment.go @@ -0,0 +1,93 @@ +package testutil + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + sdktest "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + + "pkg.akt.dev/go/cli" + dv1beta4 "pkg.akt.dev/go/node/deployment/v1beta4" +) + +// TxCreateDeploymentExec is used for testing create deployment tx +func TxCreateDeploymentExec(ctx context.Context, cctx client.Context, filePath string, extraArgs ...string) (sdktest.BufferWriter, error) { + args := []string{ + filePath, + } + + args = append(args, extraArgs...) + + return ExecTestCLICmd(ctx, cctx, cli.GetTxDeploymentCreateCmd(), args...) +} + +// TxUpdateDeploymentExec is used for testing update deployment tx +func TxUpdateDeploymentExec(ctx context.Context, cctx client.Context, filePath string, extraArgs ...string) (sdktest.BufferWriter, error) { + args := []string{ + filePath, + } + + args = append(args, extraArgs...) + + return ExecTestCLICmd(ctx, cctx, cli.GetTxDeploymentUpdateCmd(), args...) +} + +// TxCloseDeploymentExec is used for testing close deployment tx +// requires --dseq, --fees +func TxCloseDeploymentExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxDeploymentCloseCmd(), extraArgs...) +} + +// TxDepositDeploymentExec is used for testing deposit deployment tx +func TxDepositDeploymentExec(ctx context.Context, cctx client.Context, deposit sdk.Coin, extraArgs ...string) (sdktest.BufferWriter, error) { + args := []string{ + deposit.String(), + } + + args = append(args, extraArgs...) + + return ExecTestCLICmd(ctx, cctx, cli.GetTxDeploymentDepositCmd(), args...) +} + +// TxCloseGroupExec is used for testing close group tx +func TxCloseGroupExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxDeploymentGroupCloseCmd(), extraArgs...) +} + +func TxGrantAuthorizationExec(ctx context.Context, cctx client.Context, grantee sdk.AccAddress, extraArgs ...string) (sdktest.BufferWriter, error) { + dmin, _ := dv1beta4.DefaultParams().MinDepositFor("uakt") + + spendLimit := sdk.NewCoin(dmin.Denom, dmin.Amount.MulRaw(3)) + args := []string{ + grantee.String(), + spendLimit.String(), + } + args = append(args, extraArgs...) + + return ExecTestCLICmd(ctx, cctx, cli.GetTxDeploymentGrantAuthorizationCmd(), args...) +} + +func TxRevokeAuthorizationExec(ctx context.Context, cctx client.Context, grantee sdk.AccAddress, extraArgs ...string) (sdktest.BufferWriter, error) { + args := []string{ + grantee.String(), + } + args = append(args, extraArgs...) + + return ExecTestCLICmd(ctx, cctx, cli.GetTxDeploymentRevokeAuthorizationCmd(), args...) +} + +// QueryDeploymentsExec is used for testing deployments query +func QueryDeploymentsExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryDeploymentsCmd(), extraArgs...) +} + +// QueryDeploymentExec is used for testing deployment query +func QueryDeploymentExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryDeploymentCmd(), extraArgs...) +} + +// QueryGroupExec is used for testing group query +func QueryGroupExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryDeploymentGroupCmd(), extraArgs...) +} diff --git a/go/cli/testutil/gov.go b/go/cli/testutil/gov.go new file mode 100644 index 00000000..80733538 --- /dev/null +++ b/go/cli/testutil/gov.go @@ -0,0 +1,49 @@ +package testutil + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" +) + +// var commonArgs = []string{ +// fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), +// fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), +// fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), +// } + +// ExecGovSubmitLegacyProposal creates a tx for submit legacy proposal +// +//nolint:staticcheck // we are intentionally using a deprecated flag here. +func ExecGovSubmitLegacyProposal(ctx context.Context, cctx client.Context, args ...string) (testutil.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, govcli.NewCmdSubmitLegacyProposal(), args...) +} + +// MsgVote votes for a proposal +func MsgVote(clientCtx client.Context, from, id, vote string, args ...string) (testutil.BufferWriter, error) { + // args := append([]string{ + // id, + // vote, + // fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + // }, commonArgs...) + // + // args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdWeightedVote(), args) +} + +// MsgDeposit deposits on a proposal +func MsgDeposit(clientCtx client.Context, from, id, deposit string, args ...string) (testutil.BufferWriter, error) { + // args := append([]string{ + // id, + // deposit, + // fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + // }, commonArgs...) + // + // args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdDeposit(), args) +} diff --git a/go/cli/testutil/market.go b/go/cli/testutil/market.go new file mode 100644 index 00000000..44ce3551 --- /dev/null +++ b/go/cli/testutil/market.go @@ -0,0 +1,60 @@ +package testutil + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + sdktest "github.com/cosmos/cosmos-sdk/testutil" + + "pkg.akt.dev/go/cli" +) + +// TxCreateBidExec is used for testing create bid tx +func TxCreateBidExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxMarketBidCreateCmd(), extraArgs...) +} + +// TxCloseBidExec is used for testing close bid tx +func TxCloseBidExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxMarketBidCloseCmd(), extraArgs...) +} + +// TxCreateLeaseExec is used for creating a lease +func TxCreateLeaseExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxMarketLeaseCreateCmd(), extraArgs...) +} + +// TxCloseLeaseExec is used for testing close order tx +func TxCloseLeaseExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetTxMarketLeaseCloseCmd(), extraArgs...) +} + +// QueryOrdersExec is used for testing orders query +func QueryOrdersExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryMarketOrdersCmd(), args...) +} + +// QueryOrderExec is used for testing order query +func QueryOrderExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryMarketOrderCmd(), extraArgs...) +} + +// QueryBidsExec is used for testing bids query +func QueryBidsExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryMarketBidsCmd(), args...) +} + +// QueryBidExec is used for testing bid query +func QueryBidExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryMarketBidCmd(), extraArgs...) +} + +// QueryLeasesExec is used for testing leases query +func QueryLeasesExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryMarketLeasesCmd(), args...) +} + +// QueryLeaseExec is used for testing lease query +func QueryLeaseExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryMarketLeaseCmd(), extraArgs...) +} diff --git a/go/cli/testutil/provider.go b/go/cli/testutil/provider.go new file mode 100644 index 00000000..76727574 --- /dev/null +++ b/go/cli/testutil/provider.go @@ -0,0 +1,43 @@ +package testutil + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + sdktest "github.com/cosmos/cosmos-sdk/testutil" + + "pkg.akt.dev/go/cli" +) + +// TxCreateProviderExec is used for testing create provider tx +func TxCreateProviderExec(ctx context.Context, cctx client.Context, filepath string, extraArgs ...string) (sdktest.BufferWriter, error) { + args := []string{ + filepath, + } + + args = append(args, extraArgs...) + + return ExecTestCLICmd(ctx, cctx, cli.GetTxProviderCreateCmd(), args...) +} + +// TxUpdateProviderExec is used for testing update provider tx +func TxUpdateProviderExec(ctx context.Context, cctx client.Context, filepath string, extraArgs ...string) (sdktest.BufferWriter, error) { + args := []string{ + filepath, + } + + args = append(args, extraArgs...) + + return ExecTestCLICmd(ctx, cctx, cli.GetTxProviderUpdateCmd(), args...) +} + +// QueryProvidersExec is used for testing providers query +func QueryProvidersExec(ctx context.Context, cctx client.Context, args ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryGetProvidersCmd(), args...) +} + +// QueryProviderExec is used for testing provider query +func QueryProviderExec(ctx context.Context, cctx client.Context, extraArgs ...string) (sdktest.BufferWriter, error) { + return ExecTestCLICmd(ctx, cctx, cli.GetQueryProviderCmd(), extraArgs...) +} + diff --git a/go/cli/testutil/tm_mocks.go b/go/cli/testutil/tm_mocks.go new file mode 100644 index 00000000..f3c96032 --- /dev/null +++ b/go/cli/testutil/tm_mocks.go @@ -0,0 +1,41 @@ +package testutil + +import ( + "context" + + abci "github.com/cometbft/cometbft/abci/types" + tmbytes "github.com/cometbft/cometbft/libs/bytes" + rpcclient "github.com/cometbft/cometbft/rpc/client" + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + coretypes "github.com/cometbft/cometbft/rpc/core/types" + tmtypes "github.com/cometbft/cometbft/types" + + "github.com/cosmos/cosmos-sdk/client" +) + +var _ client.TendermintRPC = (*MockTendermintRPC)(nil) + +type MockTendermintRPC struct { + rpcclientmock.Client + + responseQuery abci.ResponseQuery +} + +// NewMockTendermintRPC returns a mock TendermintRPC implementation. +// It is used for CLI testing. +func NewMockTendermintRPC(respQuery abci.ResponseQuery) MockTendermintRPC { + return MockTendermintRPC{responseQuery: respQuery} +} + +func (MockTendermintRPC) BroadcastTxSync(context.Context, tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { + return &coretypes.ResultBroadcastTx{Code: 0}, nil +} + +func (m MockTendermintRPC) ABCIQueryWithOptions( + _ context.Context, + _ string, + _ tmbytes.HexBytes, + _ rpcclient.ABCIQueryOptions, +) (*coretypes.ResultABCIQuery, error) { + return &coretypes.ResultABCIQuery{Response: m.responseQuery}, nil +} diff --git a/go/cli/tx.go b/go/cli/tx.go index c13ba502..72037a28 100644 --- a/go/cli/tx.go +++ b/go/cli/tx.go @@ -2,12 +2,13 @@ package cli import ( "context" + "errors" - sdkclient "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" - cflags "pkg.akt.dev/go/cli/flags" "pkg.akt.dev/go/node/client/v1beta3" + + cflags "pkg.akt.dev/go/cli/flags" ) type ContextType string @@ -17,52 +18,75 @@ const ( ContextTypeQueryClient = ContextType("context-query-client") ) -func TxCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "tx", - Short: "Transactions subcommands", - PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { - ctx := cmd.Context() +func TxPersistentPreRunE(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + + cctx, err := GetClientTxContext(cmd) + if err != nil { + return err + } + + if cctx.Codec == nil { + return errors.New("codec is not initialized") + } + + if cctx.LegacyAmino == nil { + return errors.New("legacy amino codec is not initialized") + } - cctx, err := sdkclient.GetClientTxContext(cmd) - if err != nil { - return err - } + opts, err := cflags.ClientOptionsFromFlags(cmd.Flags()) + if err != nil { + return err + } - opts, err := cflags.ClientOptionsFromFlags(cmd.Flags()) - if err != nil { - return err - } + cl, err := DiscoverClient(ctx, cctx, opts...) + if err != nil { + return err + } - cl, err := DiscoverClient(ctx, cctx, opts...) - if err != nil { - return err - } + ctx = context.WithValue(ctx, ContextTypeClient, cl) - ctx = context.WithValue(ctx, ContextTypeClient, cl) + cmd.SetContext(ctx) - cmd.SetContext(ctx) + return nil +} - return nil - }, +func TxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "tx", + Short: "Transactions subcommands", } cmd.AddCommand( - NewBankTxCmd(), + GetTxAuthzCmd(), + GetTxBankCmd(), + GetTxCrisisCmd(), + getTxDistributionCmd(), + GetTxFeegrantCmd(), + GetTxEvidenceCmd([]*cobra.Command{}), GetSignCommand(), GetSignBatchCommand(), - GetMultiSignCommand(), + GetAuthMultiSignCmd(), GetValidateSignaturesCommand(), GetBroadcastCommand(), GetEncodeCommand(), GetDecodeCommand(), - GetVestingTxCmd(), + GetTxVestingCmd(), cflags.LineBreak, - GetAuditTxCmd(), - GetCertTxCmd(), - GetDeploymentTxCmd(), - GetMarketTxCmd(), - GetProviderTxCmd(), + GetTxAuditCmd(), + GetTxCertCmd(), + GetTxDeploymentCmds(), + GetTxMarketCmds(), + GetTxProviderCmd(), + GetTxGovCmd( + []*cobra.Command{ + GetTxParamsSubmitParamChangeProposalCmd(), + GetTxUpgradeSubmitLegacyUpgradeProposal(), + GetTxUpgradeSubmitLegacyCancelUpgradeProposal(), + }, + ), + GetTxSlashingCmd(), + GetTxStakingCmd(), ) cmd.PersistentFlags().String(cflags.FlagChainID, "", "The network chain ID") diff --git a/go/cli/upgrade_query.go b/go/cli/upgrade_query.go new file mode 100644 index 00000000..f0cd925d --- /dev/null +++ b/go/cli/upgrade_query.go @@ -0,0 +1,148 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetQueryUpgradeCmd returns the parent command for all x/upgrade CLI query commands. +func GetQueryUpgradeCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the upgrade module", + } + + cmd.AddCommand( + getQueryUpgradeCurrentPlanCmd(), + getQueryUpgradeAppliedPlanCmd(), + getQueryUpgradeModuleVersionsCmd(), + ) + + return cmd +} + +// getQueryUpgradeCurrentPlanCmd returns the query upgrade plan command. +func getQueryUpgradeCurrentPlanCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "plan", + Short: "get upgrade plan (if one exists)", + Long: "Gets the currently scheduled upgrade plan, if one exists", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + params := types.QueryCurrentPlanRequest{} + res, err := cl.Query().Upgrade().CurrentPlan(cmd.Context(), ¶ms) + if err != nil { + return err + } + + if res.Plan == nil { + return fmt.Errorf("no upgrade scheduled") + } + + return cl.PrintMessage(res.GetPlan()) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// getQueryUpgradeAppliedPlanCmd returns information about the block at which a completed +// upgrade was applied. +func getQueryUpgradeAppliedPlanCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "applied [upgrade-name]", + Short: "block header for height at which a completed upgrade was applied", + Long: "If upgrade-name was previously executed on the chain, this returns the header for the block at which it was applied.\n" + + "This helps a client determine which binary was valid over a given range of blocks, as well as more context to understand past migrations.", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + cctx := cl.ClientContext() + + params := types.QueryAppliedPlanRequest{Name: args[0]} + res, err := cl.Query().Upgrade().AppliedPlan(ctx, ¶ms) + if err != nil { + return err + } + + if res.Height == 0 { + return fmt.Errorf("no upgrade found") + } + + // we got the height, now let's return the headers + node, err := cctx.GetNode() + if err != nil { + return err + } + headers, err := node.BlockchainInfo(ctx, res.Height, res.Height) + if err != nil { + return err + } + if len(headers.BlockMetas) == 0 { + return fmt.Errorf("no headers returned for height %d", res.Height) + } + + // always output json as Header is unreadable in toml ([]byte is a long list of numbers) + bz, err := cctx.LegacyAmino.MarshalJSONIndent(headers.BlockMetas[0], "", " ") + if err != nil { + return err + } + return cctx.PrintString(fmt.Sprintf("%s\n", bz)) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetModuleVersionsCmd returns the module version list from state +func getQueryUpgradeModuleVersionsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "module_versions [optional module_name]", + Short: "get the list of module versions", + Long: "Gets a list of module names and their respective consensus versions.\n" + + "Following the command with a specific module name will return only\n" + + "that module's information.", + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustQueryClientFromContext(ctx) + + var params types.QueryModuleVersionsRequest + + if len(args) == 1 { + params = types.QueryModuleVersionsRequest{ModuleName: args[0]} + } else { + params = types.QueryModuleVersionsRequest{} + } + + res, err := cl.Query().Upgrade().ModuleVersions(cmd.Context(), ¶ms) + if err != nil { + return err + } + + if res.ModuleVersions == nil { + return errors.ErrNotFound + } + + return cl.PrintMessage(res) + }, + } + + cflags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/go/cli/upgrade_tx.go b/go/cli/upgrade_tx.go new file mode 100644 index 00000000..1838be85 --- /dev/null +++ b/go/cli/upgrade_tx.go @@ -0,0 +1,202 @@ +package cli + +import ( + "os" + "path/filepath" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/upgrade/plan" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + cflags "pkg.akt.dev/go/cli/flags" +) + +// GetTxUpgradeCmd returns the transaction commands for this module +func GetTxUpgradeCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Upgrade transaction subcommands", + } + + return cmd +} + +// GetTxUpgradeSubmitLegacyUpgradeProposal implements a command handler for submitting a software upgrade proposal transaction. +func GetTxUpgradeSubmitLegacyUpgradeProposal() *cobra.Command { + cmd := &cobra.Command{ + Use: "software-upgrade [name] (--upgrade-height [height]) (--upgrade-info [info]) [flags]", + Args: cobra.ExactArgs(1), + Short: "Submit a software upgrade proposal", + Long: "Submit a software upgrade along with an initial deposit.\n" + + "Please specify a unique name and height for the upgrade to take effect.\n" + + "You may include info to reference a binary download link, in a format compatible with: https://github.com/cosmos/cosmos-sdk/tree/main/cosmovisor", + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + name := args[0] + content, err := upgradeParseArgsToContent(cmd.Flags(), name) + if err != nil { + return err + } + noValidate, err := cmd.Flags().GetBool(cflags.FlagNoValidate) + if err != nil { + return err + } + if !noValidate { + prop := content.(*types.SoftwareUpgradeProposal) //nolint:staticcheck // we are intentionally using a deprecated proposal type. + var daemonName string + if daemonName, err = cmd.Flags().GetString(cflags.FlagDaemonName); err != nil { + return err + } + var planInfo *plan.Info + if planInfo, err = plan.ParseInfo(prop.Plan.Info); err != nil { + return err + } + if err = planInfo.ValidateFull(daemonName); err != nil { + return err + } + } + + from := cctx.GetFromAddress() + + depositStr, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return err + } + deposit, err := sdk.ParseCoinsNormalized(depositStr) + if err != nil { + return err + } + + msg, err := gov.NewMsgSubmitProposal(content, deposit, from) + if err != nil { + return err + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cmd.Flags().String(cflags.FlagTitle, "", "title of proposal") + cmd.Flags().String(cflags.FlagDescription, "", "description of proposal") //nolint:staticcheck // we are intentionally using a deprecated flag here. + cmd.Flags().String(cflags.FlagDeposit, "", "deposit of proposal") + cmd.Flags().Int64(cflags.FlagUpgradeHeight, 0, "The height at which the upgrade must happen") + cmd.Flags().String(cflags.FlagUpgradeInfo, "", "Info for the upgrade plan such as new version download urls, etc.") + cmd.Flags().Bool(cflags.FlagNoValidate, false, "Skip validation of the upgrade info") + cmd.Flags().String(cflags.FlagDaemonName, getDefaultDaemonName(), "The name of the executable being upgraded (for upgrade-info validation). Default is the DAEMON_NAME env var if set, or else this executable") + + return cmd +} + +// GetTxUpgradeSubmitLegacyCancelUpgradeProposal implements a command handler for submitting a software upgrade cancel proposal transaction. +// Deprecated: please use NewCmdSubmitCancelUpgradeProposal instead. +func GetTxUpgradeSubmitLegacyCancelUpgradeProposal() *cobra.Command { + cmd := &cobra.Command{ + Use: "cancel-software-upgrade [flags]", + Args: cobra.ExactArgs(0), + Short: "Cancel the current software upgrade proposal", + Long: "Cancel a software upgrade along with an initial deposit.", + PersistentPreRunE: TxPersistentPreRunE, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + cl := MustClientFromContext(ctx) + cctx := cl.ClientContext() + + from := cctx.GetFromAddress() + + depositStr, err := cmd.Flags().GetString(cli.FlagDeposit) + if err != nil { + return err + } + + deposit, err := sdk.ParseCoinsNormalized(depositStr) + if err != nil { + return err + } + + title, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return err + } + + description, err := cmd.Flags().GetString(cli.FlagDescription) //nolint:staticcheck // we are intentionally using a deprecated flag here. + if err != nil { + return err + } + + content := types.NewCancelSoftwareUpgradeProposal(title, description) + + msg, err := gov.NewMsgSubmitProposal(content, deposit, from) + if err != nil { + return err + } + + resp, err := cl.Tx().BroadcastMsgs(ctx, []sdk.Msg{msg}) + if err != nil { + return err + } + + return cl.PrintMessage(resp) + }, + } + + cmd.Flags().String(cflags.FlagTitle, "", "title of proposal") + cmd.Flags().String(cflags.FlagDescription, "", "description of proposal") //nolint:staticcheck // we are intentionally using a deprecated flag here. + cmd.Flags().String(cflags.FlagDeposit, "", "deposit of proposal") + _ = cmd.MarkFlagRequired(cflags.FlagTitle) + _ = cmd.MarkFlagRequired(cflags.FlagDescription) //nolint:staticcheck // we are intentionally using a deprecated flag here. + + return cmd +} + +// getDefaultDaemonName gets the default name to use for the daemon. +// If a DAEMON_NAME env var is set, that is used. +// Otherwise, the last part of the currently running executable is used. +func getDefaultDaemonName() string { + // DAEMON_NAME is specifically used here to correspond with the Cosmovisor setup env vars. + name := os.Getenv("DAEMON_NAME") + if len(name) == 0 { + _, name = filepath.Split(os.Args[0]) + } + return name +} + +func upgradeParseArgsToContent(fs *pflag.FlagSet, name string) (gov.Content, error) { + title, err := fs.GetString(cli.FlagTitle) + if err != nil { + return nil, err + } + + description, err := fs.GetString(cli.FlagDescription) //nolint:staticcheck // we are intentionally using a deprecated flag here. + if err != nil { + return nil, err + } + + height, err := fs.GetInt64(cflags.FlagUpgradeHeight) + if err != nil { + return nil, err + } + + info, err := fs.GetString(cflags.FlagUpgradeInfo) + if err != nil { + return nil, err + } + + plan := types.Plan{Name: name, Height: height, Info: info} + content := types.NewSoftwareUpgradeProposal(title, description, plan) + + return content, nil +} diff --git a/go/cli/validate.go b/go/cli/validate.go new file mode 100644 index 00000000..b7731dcd --- /dev/null +++ b/go/cli/validate.go @@ -0,0 +1,58 @@ +package cli + +import ( + "errors" + "fmt" + "strings" + + "github.com/spf13/cobra" +) + +// ValidateCmd returns unknown command error or Help display if help flag set +func ValidateCmd(cmd *cobra.Command, args []string) error { + var unknownCmd string + var skipNext bool + + for _, arg := range args { + // search for help flag + if arg == "--help" || arg == "-h" { + return cmd.Help() + } + + // check if the current arg is a flag + switch { + case len(arg) > 0 && (arg[0] == '-'): + // the next arg should be skipped if the current arg is a + // flag and does not use "=" to assign the flag's value + if !strings.Contains(arg, "=") { + skipNext = true + } else { + skipNext = false + } + case skipNext: + // skip current arg + skipNext = false + case unknownCmd == "": + // unknown command found + // continue searching for help flag + unknownCmd = arg + } + } + + // return the help screen if no unknown command is found + if unknownCmd != "" { + err := fmt.Sprintf("unknown command \"%s\" for \"%s\"", unknownCmd, cmd.CalledAs()) + + // build suggestions for unknown argument + if suggestions := cmd.SuggestionsFor(unknownCmd); len(suggestions) > 0 { + err += "\n\nDid you mean this?\n" + for _, s := range suggestions { + err += fmt.Sprintf("\t%v\n", s) + } + } + return errors.New(err) + } + + return cmd.Help() +} + diff --git a/go/cli/vesting_tx.go b/go/cli/vesting_tx.go index 4f975e41..dd3506e6 100644 --- a/go/cli/vesting_tx.go +++ b/go/cli/vesting_tx.go @@ -9,15 +9,14 @@ import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" cflags "pkg.akt.dev/go/cli/flags" ) -// GetVestingTxCmd returns vesting module's transaction commands. -func GetVestingTxCmd() *cobra.Command { +// GetTxVestingCmd returns vesting module's transaction commands. +func GetTxVestingCmd() *cobra.Command { txCmd := &cobra.Command{ Use: types.ModuleName, Short: "Vesting transaction subcommands", @@ -27,17 +26,17 @@ func GetVestingTxCmd() *cobra.Command { } txCmd.AddCommand( - NewMsgCreateVestingAccountCmd(), - NewMsgCreatePermanentLockedAccountCmd(), - NewMsgCreatePeriodicVestingAccountCmd(), + getTxVestingCreateAccountCmd(), + getTxVestingCreatePermanentLockedAccountCmd(), + getTxVestingCreatePeriodicAccountCmd(), ) return txCmd } -// NewMsgCreateVestingAccountCmd returns a CLI command handler for creating a +// getTxVestingCreateAccountCmd returns a CLI command handler for creating a // MsgCreateVestingAccount transaction. -func NewMsgCreateVestingAccountCmd() *cobra.Command { +func getTxVestingCreateAccountCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create-vesting-account [to_address] [amount] [end_time]", Short: "Create a new vesting account funded with an allocation of tokens.", @@ -81,14 +80,14 @@ timestamp.`, } cmd.Flags().Bool(cflags.FlagDelayed, false, "Create a delayed vesting account if true") - flags.AddTxFlagsToCmd(cmd) + cflags.AddTxFlagsToCmd(cmd) return cmd } -// NewMsgCreatePermanentLockedAccountCmd returns a CLI command handler for creating a +// getTxVestingCreatePermanentLockedAccountCmd returns a CLI command handler for creating a // MsgCreatePermanentLockedAccount transaction. -func NewMsgCreatePermanentLockedAccountCmd() *cobra.Command { +func getTxVestingCreatePermanentLockedAccountCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create-permanent-locked-account [to_address] [amount]", Short: "Create a new permanently locked account funded with an allocation of tokens.", @@ -122,7 +121,7 @@ tokens.`, }, } - flags.AddTxFlagsToCmd(cmd) + cflags.AddTxFlagsToCmd(cmd) return cmd } @@ -137,9 +136,9 @@ type InputPeriod struct { Length int64 `json:"length_seconds"` } -// NewMsgCreatePeriodicVestingAccountCmd returns a CLI command handler for creating a +// getTxVestingCreatePeriodicAccountCmd returns a CLI command handler for creating a // MsgCreatePeriodicVestingAccountCmd transaction. -func NewMsgCreatePeriodicVestingAccountCmd() *cobra.Command { +func getTxVestingCreatePeriodicAccountCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create-periodic-vesting-account [to_address] [periods_json_file]", Short: "Create a new vesting account funded with an allocation of tokens.", @@ -213,7 +212,7 @@ func NewMsgCreatePeriodicVestingAccountCmd() *cobra.Command { }, } - flags.AddTxFlagsToCmd(cmd) + cflags.AddTxFlagsToCmd(cmd) return cmd }