Skip to content

Commit

Permalink
feat: Add CLI commands: 1) simulate a transaction, 2) query block res…
Browse files Browse the repository at this point in the history
…ults (#16887)

Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com>
  • Loading branch information
larry0x and facundomedica authored Jul 12, 2023
1 parent 3f4563e commit ca74dcc
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 CometBFT RPC for block results.

### Improvements

* (types) [#16890](https://github.com/cosmos/cosmos-sdk/pull/16890) Remove `GetTxCmd() *cobra.Command` and `GetQueryCmd() *cobra.Command` from `module.AppModuleBasic` interface.
Expand Down
1 change: 1 addition & 0 deletions client/cometbft.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
75 changes: 68 additions & 7 deletions server/cmt_cmds.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package server

import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
Expand Down Expand Up @@ -206,18 +208,13 @@ $ %s query block --%s=%s <hash>
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)
Expand Down Expand Up @@ -261,6 +258,70 @@ $ %s query block --%s=%s <hash>
return cmd
}

// QueryBlockResultCmd implements the default command for a BlockResults query.
func QueryBlockResultsCmd() *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",
Expand Down
2 changes: 2 additions & 0 deletions simapp/simd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ func queryCommand() *cobra.Command {
authcmd.QueryTxsByEventsCmd(),
server.QueryBlocksCmd(),
authcmd.QueryTxCmd(),
server.QueryBlockResultsCmd(),
)

return cmd
Expand All @@ -270,6 +271,7 @@ func txCommand() *cobra.Command {
authcmd.GetEncodeCommand(),
authcmd.GetDecodeCommand(),
authcmd.GetAuxToFeeCommand(),
authcmd.GetSimulateCmd(),
)

return cmd
Expand Down
2 changes: 2 additions & 0 deletions simapp/simd/cmd/root_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ func queryCommand() *cobra.Command {
authcmd.QueryTxsByEventsCmd(),
server.QueryBlocksCmd(),
authcmd.QueryTxCmd(),
server.QueryBlockResultsCmd(),
)

return cmd
Expand All @@ -287,6 +288,7 @@ func txCommand() *cobra.Command {
authcmd.GetEncodeCommand(),
authcmd.GetDecodeCommand(),
authcmd.GetAuxToFeeCommand(),
authcmd.GetSimulateCmd(),
)

return cmd
Expand Down
101 changes: 101 additions & 0 deletions x/auth/client/cli/tx_simulate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package cli

import (
"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"
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/unsigned-tx.json --from keyname",
Short: "Simulate the gas usage of a transaction",
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.
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": {
"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, 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)
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
}

0 comments on commit ca74dcc

Please sign in to comment.