feat: Add CLI commands: 1) simulate a transaction, 2) query block results (#16887)
Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com>
This commit is contained in:
parent
3f4563e286
commit
ca74dcce7e
@ -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.
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -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)
|
||||
@ -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",
|
||||
|
||||
@ -246,6 +246,7 @@ func queryCommand() *cobra.Command {
|
||||
authcmd.QueryTxsByEventsCmd(),
|
||||
server.QueryBlocksCmd(),
|
||||
authcmd.QueryTxCmd(),
|
||||
server.QueryBlockResultsCmd(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
@ -270,6 +271,7 @@ func txCommand() *cobra.Command {
|
||||
authcmd.GetEncodeCommand(),
|
||||
authcmd.GetDecodeCommand(),
|
||||
authcmd.GetAuxToFeeCommand(),
|
||||
authcmd.GetSimulateCmd(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
||||
@ -263,6 +263,7 @@ func queryCommand() *cobra.Command {
|
||||
authcmd.QueryTxsByEventsCmd(),
|
||||
server.QueryBlocksCmd(),
|
||||
authcmd.QueryTxCmd(),
|
||||
server.QueryBlockResultsCmd(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
@ -287,6 +288,7 @@ func txCommand() *cobra.Command {
|
||||
authcmd.GetEncodeCommand(),
|
||||
authcmd.GetDecodeCommand(),
|
||||
authcmd.GetAuxToFeeCommand(),
|
||||
authcmd.GetSimulateCmd(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
||||
101
x/auth/client/cli/tx_simulate.go
Normal file
101
x/auth/client/cli/tx_simulate.go
Normal file
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user