From 82f818e76729f8e643f8493b4f766ed5efaae28d Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Fri, 7 Jul 2023 23:16:43 +0200 Subject: [PATCH 1/8] add simulate command --- simapp/simd/cmd/root.go | 1 + simapp/simd/cmd/root_v2.go | 1 + x/auth/client/cli/tx_simulate.go | 107 +++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 x/auth/client/cli/tx_simulate.go diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 6d2cfc34e3b2..4b4f3582ba9f 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -270,6 +270,7 @@ func txCommand() *cobra.Command { authcmd.GetEncodeCommand(), authcmd.GetDecodeCommand(), authcmd.GetAuxToFeeCommand(), + authcmd.GetSimulateCmd(), ) return cmd diff --git a/simapp/simd/cmd/root_v2.go b/simapp/simd/cmd/root_v2.go index c7fad78a06de..0bd3dcca72c9 100644 --- a/simapp/simd/cmd/root_v2.go +++ b/simapp/simd/cmd/root_v2.go @@ -287,6 +287,7 @@ func txCommand() *cobra.Command { authcmd.GetEncodeCommand(), authcmd.GetDecodeCommand(), authcmd.GetAuxToFeeCommand(), + authcmd.GetSimulateCmd(), ) return cmd diff --git a/x/auth/client/cli/tx_simulate.go b/x/auth/client/cli/tx_simulate.go new file mode 100644 index 000000000000..65cb89c8553b --- /dev/null +++ b/x/auth/client/cli/tx_simulate.go @@ -0,0 +1,107 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "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/version" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" +) + +// GetSimulateCmd returns a command that simulates whether a transaction will be +// successful. +func GetSimulateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "simulate /path/to/tx.json --from keyname", + Short: "Simulate the gas usage of a transaction", + Long: strings.TrimSpace( + fmt.Sprintf(`Simulate whether a transaction will be successful: + +- if successful, the simulation result is printed, which includes the gas + consumption, message response data, and events emitted; +- if unsuccessful, the error message is printed. + +Example: +$ %s query simulate tx.json --from keyname + +Where tx.json is a JSON-encoded unsigned transaction, typically generated by any +transaction command with the --generate-only flag. It shoud look like: + +{ + "body": { + "messages": [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + "from_address": "cosmos1...", + "to_address": "cosmos1...", + "amount": [ + { + "denom": "utoken", + "amount": "12345" + } + ] + } + ], + "memo": "", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [] + }, + "auth_info": { + "signer_infos": [], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [] +} + +The --from flag is mandatory. +`, + version.AppName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf, err := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if err != nil { + return err + } + + txf, err = txf.Prepare(clientCtx) + if err != nil { + return err + } + + stdTx, err := authclient.ReadTxFromFile(clientCtx, args[0]) + if err != nil { + return err + } + + simRes, _, err := tx.CalculateGas(clientCtx, txf, stdTx.GetMsgs()...) + if err != nil { + return err + } + + return clientCtx.PrintProto(simRes) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} From e1f73916ea455ba60901becdbb7f8881040245dc Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Fri, 7 Jul 2023 23:25:27 +0200 Subject: [PATCH 2/8] improve helper text --- x/auth/client/cli/tx_simulate.go | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/x/auth/client/cli/tx_simulate.go b/x/auth/client/cli/tx_simulate.go index 65cb89c8553b..9cb49e4778d6 100644 --- a/x/auth/client/cli/tx_simulate.go +++ b/x/auth/client/cli/tx_simulate.go @@ -1,7 +1,6 @@ package cli import ( - "fmt" "strings" "github.com/spf13/cobra" @@ -9,7 +8,6 @@ import ( "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/version" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" ) @@ -17,20 +15,18 @@ import ( // successful. func GetSimulateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "simulate /path/to/tx.json --from keyname", + Use: "simulate /path/to/unsigned-tx.json --from keyname", Short: "Simulate the gas usage of a transaction", - Long: strings.TrimSpace( - fmt.Sprintf(`Simulate whether a transaction will be successful: + Long: strings.TrimSpace(`Simulate whether a transaction will be successful: - if successful, the simulation result is printed, which includes the gas consumption, message response data, and events emitted; - if unsuccessful, the error message is printed. -Example: -$ %s query simulate tx.json --from keyname - -Where tx.json is a JSON-encoded unsigned transaction, typically generated by any -transaction command with the --generate-only flag. It shoud look like: +The user must provide the path to a JSON-encoded unsigned transaction, typically +generated by any transaction command with the --generate-only flag. It should +look like below. Note that the "signer_infos" and "signatures" fields are left +empty; they will be auto-populated by dummy data for simulation purpose. { "body": { @@ -65,11 +61,9 @@ transaction command with the --generate-only flag. It shoud look like: "signatures": [] } -The --from flag is mandatory. -`, - version.AppName, - ), - ), +The --from flag is mandatory, as the signer account's correct sequence number is +necessary for simulation. +`), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) From 0e066be3b6cc16ed104f0f4e3f8532cf2b37b6f7 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 8 Jul 2023 00:09:58 +0200 Subject: [PATCH 3/8] add block-results query command --- client/cometbft.go | 1 + server/cmt_cmds.go | 75 ++++++++++++++++++++++++++++++++++---- simapp/simd/cmd/root.go | 5 ++- simapp/simd/cmd/root_v2.go | 3 +- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/client/cometbft.go b/client/cometbft.go index 595ea6492d24..fcf9ca64b08b 100644 --- a/client/cometbft.go +++ b/client/cometbft.go @@ -16,6 +16,7 @@ type CometRPC interface { Status(context.Context) (*coretypes.ResultStatus, error) Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) BlockByHash(ctx context.Context, hash []byte) (*coretypes.ResultBlock, error) + BlockResults(ctx context.Context, height *int64) (*coretypes.ResultBlockResults, error) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) Tx(ctx context.Context, hash []byte, prove bool) (*coretypes.ResultTx, error) diff --git a/server/cmt_cmds.go b/server/cmt_cmds.go index 733067815eb7..9d553a12ee14 100644 --- a/server/cmt_cmds.go +++ b/server/cmt_cmds.go @@ -1,6 +1,8 @@ package server import ( + "context" + "encoding/json" "fmt" "strconv" "strings" @@ -206,18 +208,13 @@ $ %s query block --%s=%s return fmt.Errorf("argument should be a block height") } - var height *int64 - // optional height + var height *int64 if len(args) > 0 { - h, err := strconv.Atoi(args[0]) + height, err = parseOptionalHeight(args[0]) if err != nil { return err } - if h > 0 { - tmp := int64(h) - height = &tmp - } } output, err := rpc.GetBlockByHeight(clientCtx, height) @@ -261,6 +258,70 @@ $ %s query block --%s=%s return cmd } +// QueryBlockResultCmd implements the default command for a BlockResults query. +func QueryBlockResultCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "block-results [height]", + Short: "Query for a committed block's results by height", + Long: "Query for a specific committed block's results using the CometBFT RPC `block_results` method", + Args: cobra.RangeArgs(0, 1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + node, err := clientCtx.GetNode() + if err != nil { + return err + } + + // optional height + var height *int64 + if len(args) > 0 { + height, err = parseOptionalHeight(args[0]) + if err != nil { + return err + } + } + + blockRes, err := node.BlockResults(context.Background(), height) + if err != nil { + return err + } + + // coretypes.ResultBlockResults doesn't implement proto.Message interface + // so we can't print it using clientCtx.PrintProto + // we choose to serialize it to json and print the json instead + blockResStr, err := json.Marshal(blockRes) + if err != nil { + return err + } + + return clientCtx.PrintString(string(blockResStr) + "\n") + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +func parseOptionalHeight(heightStr string) (*int64, error) { + h, err := strconv.Atoi(heightStr) + if err != nil { + return nil, err + } + + if h == 0 { + return nil, nil + } + + tmp := int64(h) + + return &tmp, nil +} + func BootstrapStateCmd(appCreator types.AppCreator) *cobra.Command { cmd := &cobra.Command{ Use: "bootstrap-state", diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 4b4f3582ba9f..934abdf31bea 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -243,13 +243,14 @@ func queryCommand() *cobra.Command { cmd.AddCommand( rpc.ValidatorCommand(), server.QueryBlockCmd(), - authcmd.QueryTxsByEventsCmd(), server.QueryBlocksCmd(), + server.QueryBlockResultCmd(), + authcmd.QueryTxsByEventsCmd(), authcmd.QueryTxCmd(), ) return cmd -} +}`` func txCommand() *cobra.Command { cmd := &cobra.Command{ diff --git a/simapp/simd/cmd/root_v2.go b/simapp/simd/cmd/root_v2.go index 0bd3dcca72c9..9aa79e3aba8a 100644 --- a/simapp/simd/cmd/root_v2.go +++ b/simapp/simd/cmd/root_v2.go @@ -260,8 +260,9 @@ func queryCommand() *cobra.Command { cmd.AddCommand( rpc.ValidatorCommand(), server.QueryBlockCmd(), - authcmd.QueryTxsByEventsCmd(), server.QueryBlocksCmd(), + server.QueryBlockResultCmd(), + authcmd.QueryTxsByEventsCmd(), authcmd.QueryTxCmd(), ) From 1c09dae56bdbfc66caf525736cec85421786d104 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 8 Jul 2023 00:12:47 +0200 Subject: [PATCH 4/8] remove stuff added by mistake --- simapp/simd/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 934abdf31bea..47b923de8f03 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -250,7 +250,7 @@ func queryCommand() *cobra.Command { ) return cmd -}`` +} func txCommand() *cobra.Command { cmd := &cobra.Command{ From cea21afcb8b85430c3764b239076a927fe8091e1 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 8 Jul 2023 00:14:11 +0200 Subject: [PATCH 5/8] make diff small --- simapp/simd/cmd/root.go | 4 ++-- simapp/simd/cmd/root_v2.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 47b923de8f03..a7f688206505 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -243,10 +243,10 @@ func queryCommand() *cobra.Command { cmd.AddCommand( rpc.ValidatorCommand(), server.QueryBlockCmd(), - server.QueryBlocksCmd(), - server.QueryBlockResultCmd(), authcmd.QueryTxsByEventsCmd(), + server.QueryBlocksCmd(), authcmd.QueryTxCmd(), + server.QueryBlockResultCmd(), ) return cmd diff --git a/simapp/simd/cmd/root_v2.go b/simapp/simd/cmd/root_v2.go index 9aa79e3aba8a..85acd9e47a15 100644 --- a/simapp/simd/cmd/root_v2.go +++ b/simapp/simd/cmd/root_v2.go @@ -260,10 +260,10 @@ func queryCommand() *cobra.Command { cmd.AddCommand( rpc.ValidatorCommand(), server.QueryBlockCmd(), - server.QueryBlocksCmd(), - server.QueryBlockResultCmd(), authcmd.QueryTxsByEventsCmd(), + server.QueryBlocksCmd(), authcmd.QueryTxCmd(), + server.QueryBlockResultCmd(), ) return cmd From d54875781b8c10f71aa6c41a3180f06ce7f078c0 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 8 Jul 2023 00:14:30 +0200 Subject: [PATCH 6/8] fix typo --- server/cmt_cmds.go | 2 +- simapp/simd/cmd/root.go | 2 +- simapp/simd/cmd/root_v2.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/cmt_cmds.go b/server/cmt_cmds.go index 9d553a12ee14..47a4e64e18ff 100644 --- a/server/cmt_cmds.go +++ b/server/cmt_cmds.go @@ -259,7 +259,7 @@ $ %s query block --%s=%s } // QueryBlockResultCmd implements the default command for a BlockResults query. -func QueryBlockResultCmd() *cobra.Command { +func QueryBlockResultsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "block-results [height]", Short: "Query for a committed block's results by height", diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index a7f688206505..dba942219e5b 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -246,7 +246,7 @@ func queryCommand() *cobra.Command { authcmd.QueryTxsByEventsCmd(), server.QueryBlocksCmd(), authcmd.QueryTxCmd(), - server.QueryBlockResultCmd(), + server.QueryBlockResultsCmd(), ) return cmd diff --git a/simapp/simd/cmd/root_v2.go b/simapp/simd/cmd/root_v2.go index 85acd9e47a15..0689cca50fbe 100644 --- a/simapp/simd/cmd/root_v2.go +++ b/simapp/simd/cmd/root_v2.go @@ -263,7 +263,7 @@ func queryCommand() *cobra.Command { authcmd.QueryTxsByEventsCmd(), server.QueryBlocksCmd(), authcmd.QueryTxCmd(), - server.QueryBlockResultCmd(), + server.QueryBlockResultsCmd(), ) return cmd From 9771df12af77dcd6daa2655d3cc61cf3d3e7f671 Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Sat, 8 Jul 2023 13:34:53 +0200 Subject: [PATCH 7/8] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9d8897c4f24..cd11cc04ad6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (cli) [#16887](https://github.com/cosmos/cosmos-sdk/pull/16887) Add two new CLI commands: `tx simulate` for simulating a transaction; `query block-results` for querying Tendermint RPC for block results. + ### Improvements * (cli) [#16856](https://github.com/cosmos/cosmos-sdk/pull/16856) Improve `simd prune` UX by using the app default home directory and set pruning method as first variable argument (defaults to default). From 8acc12d6aa64e04e55b350778672cfd2366595dc Mon Sep 17 00:00:00 2001 From: larry <26318510+larry0x@users.noreply.github.com> Date: Wed, 12 Jul 2023 14:52:28 +0200 Subject: [PATCH 8/8] Update CHANGELOG.md Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd11cc04ad6c..8403c1be640e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features -* (cli) [#16887](https://github.com/cosmos/cosmos-sdk/pull/16887) Add two new CLI commands: `tx simulate` for simulating a transaction; `query block-results` for querying Tendermint RPC for block results. +* (cli) [#16887](https://github.com/cosmos/cosmos-sdk/pull/16887) Add two new CLI commands: `tx simulate` for simulating a transaction; `query block-results` for querying CometBFT RPC for block results. ### Improvements