feat: use autocli for comet commands (backport #17389) (#17405)

Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2023-08-16 12:03:38 +00:00 committed by GitHub
parent af29f5521d
commit b056224747
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 212 additions and 105 deletions

View File

@ -46,6 +46,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Improvements
* (cli) [#17389](https://github.com/cosmos/cosmos-sdk/pull/17389) gRPC CometBFT commands have been added under `<aapd> q consensus comet`. CometBFT commands placement in the SDK has been simplified. See the exhaustive list below.
* `client/rpc.StatusCommand()` is now at `server.StatusCommand()`
* (cli) [#17187](https://github.com/cosmos/cosmos-sdk/pull/17187) Do not use `ctx.PrintObjectLegacy` in commands anymore.
* `<appd> q gov proposer [proposal-id]` now returns a proposal id as int instead of string.
* (testutil) [#17216](https://github.com/cosmos/cosmos-sdk/issues/17216) Add `DefaultContextWithKeys` to `testutil` package.

View File

@ -0,0 +1,71 @@
package cmtservice
import (
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
cmtv1beta1 "cosmossdk.io/api/cosmos/base/tendermint/v1beta1"
)
var CometBFTAutoCLIDescriptor = &autocliv1.ServiceCommandDescriptor{
Service: cmtv1beta1.Service_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "GetNodeInfo",
Use: "node-info",
Short: "Query the current node info",
},
{
RpcMethod: "GetSyncing",
Use: "syncing",
Short: "Query node syncing status",
},
{
RpcMethod: "GetLatestBlock",
Use: "block-latest",
Short: "Query for the latest committed block",
},
{
RpcMethod: "GetBlockByHeight",
Use: "block-by-height [height]",
Short: "Query for a committed block by height",
Long: "Query for a specific committed block using the CometBFT RPC `block_by_height` method",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "height"}},
},
{
RpcMethod: "GetLatestValidatorSet",
Use: "validator-set",
Alias: []string{"validator-set-latest", "comet-validator-set", "cometbft-validator-set", "tendermint-validator-set"},
Short: "Query for the latest validator set",
},
{
RpcMethod: "GetValidatorSetByHeight",
Use: "validator-set-by-height [height]",
Short: "Query for a validator set by height",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "height"}},
},
{
RpcMethod: "ABCIQuery",
Skip: true,
},
},
}
// NewCometBFTCommands is a fake `appmodule.Module` to be considered as a module
// and be added in AutoCLI.
func NewCometBFTCommands() *cometModule { //nolint:revive // fake module and limiting import of core
return &cometModule{}
}
type cometModule struct{}
func (m cometModule) IsOnePerModuleType() {}
func (m cometModule) IsAppModule() {}
func (m cometModule) Name() string {
return "comet"
}
func (m cometModule) AutoCLIOptions() *autocliv1.ModuleOptions {
return &autocliv1.ModuleOptions{
Query: CometBFTAutoCLIDescriptor,
}
}

View File

@ -10,7 +10,7 @@ import (
)
func getBlockHeight(ctx context.Context, clientCtx client.Context) (int64, error) {
status, err := getNodeStatus(ctx, clientCtx)
status, err := GetNodeStatus(ctx, clientCtx)
if err != nil {
return 0, err
}

View File

@ -49,7 +49,7 @@ func NewQueryServer(
// GetSyncing implements ServiceServer.GetSyncing
func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) {
status, err := getNodeStatus(ctx, s.clientCtx)
status, err := GetNodeStatus(ctx, s.clientCtx)
if err != nil {
return nil, err
}
@ -189,7 +189,7 @@ func ValidatorsOutput(ctx context.Context, clientCtx client.Context, height *int
// GetNodeInfo implements ServiceServer.GetNodeInfo
func (s queryServer) GetNodeInfo(ctx context.Context, _ *GetNodeInfoRequest) (*GetNodeInfoResponse, error) {
status, err := getNodeStatus(ctx, s.clientCtx)
status, err := GetNodeStatus(ctx, s.clientCtx)
if err != nil {
return nil, err
}

View File

@ -8,7 +8,8 @@ import (
"github.com/cosmos/cosmos-sdk/client"
)
func getNodeStatus(ctx context.Context, clientCtx client.Context) (*coretypes.ResultStatus, error) {
// GetNodeStatus returns the status of the node.
func GetNodeStatus(ctx context.Context, clientCtx client.Context) (*coretypes.ResultStatus, error) {
node, err := clientCtx.GetNode()
if err != nil {
return &coretypes.ResultStatus{}, err

View File

@ -0,0 +1,30 @@
package cmtservice_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/server"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
)
func TestStatusCommand(t *testing.T) {
cfg, err := network.DefaultConfigWithAppConfig(network.MinimumAppConfig())
require.NoError(t, err)
network, err := network.New(t, t.TempDir(), cfg)
require.NoError(t, err)
require.NoError(t, network.WaitForNextBlock())
val0 := network.Validators[0]
cmd := server.StatusCommand()
out, err := clitestutil.ExecTestCLICmd(val0.ClientCtx, cmd, []string{})
require.NoError(t, err)
// Make sure the output has the validator moniker.
require.Contains(t, out.String(), fmt.Sprintf("\"moniker\":\"%s\"", val0.Moniker))
}

View File

@ -11,8 +11,6 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/cosmos/cosmos-sdk/client/rpc"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
"github.com/cosmos/cosmos-sdk/types/address"
@ -44,17 +42,6 @@ func (s *IntegrationTestSuite) TearDownSuite() {
s.network.Cleanup()
}
func (s *IntegrationTestSuite) TestStatusCommand() {
val0 := s.network.Validators[0]
cmd := rpc.StatusCommand()
out, err := clitestutil.ExecTestCLICmd(val0.ClientCtx, cmd, []string{})
s.Require().NoError(err)
// Make sure the output has the validator moniker.
s.Require().Contains(out.String(), fmt.Sprintf("\"moniker\":\"%s\"", val0.Moniker))
}
func (s *IntegrationTestSuite) TestCLIQueryConn() {
s.T().Skip("data race in comet is causing this to fail")
var header metadata.MD

View File

@ -1,58 +0,0 @@
package rpc
import (
"context"
cmtjson "github.com/cometbft/cometbft/libs/json"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
)
// StatusCommand returns the command to return the status of the network.
func StatusCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "status",
Short: "Query remote node for status",
RunE: func(cmd *cobra.Command, _ []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
status, err := getNodeStatus(clientCtx)
if err != nil {
return err
}
output, err := cmtjson.Marshal(status)
if err != nil {
return err
}
// In order to maintain backwards compatibility, the default json format output
outputFormat, _ := cmd.Flags().GetString(flags.FlagOutput)
if outputFormat == flags.OutputFormatJSON {
clientCtx = clientCtx.WithOutputFormat(flags.OutputFormatJSON)
}
return clientCtx.PrintRaw(output)
},
}
cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
cmd.Flags().StringP(flags.FlagOutput, "o", "json", "Output format (text|json)")
return cmd
}
func getNodeStatus(clientCtx client.Context) (*coretypes.ResultStatus, error) {
node, err := clientCtx.GetNode()
if err != nil {
return &coretypes.ResultStatus{}, err
}
return node.Status(context.Background())
}

View File

@ -177,6 +177,16 @@ https://github.com/cosmos/cosmos-sdk/blob/fa4d87ef7e6d87aaccc94c337ffd2fe90fcb7a
If not set to true, `AutoCLI` will not generate commands for the module if there are already commands registered for the module (when `GetTxCmd()` or `GetTxCmd()` are defined).
### Use AutoCLI for non module commands
It is possible to use `AutoCLI` for non module commands. The trick is still to implement the `appmodule.Module` interface and append it to the `appOptions.ModuleOptions` map.
For example, here is how the SDK does it for `cometbft` gRPC commands:
```go reference
https://github.com/cosmos/cosmos-sdk/blob/julien/autocli-comet/client/grpc/cmtservice/autocli.go#L52-L71
```
## Summary
`autocli` let you generate CLI to your Cosmos SDK-based applications without any cobra boilerplate. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application.

View File

@ -0,0 +1,3 @@
package autocli
var flagNoIndent = "no-indent"

View File

@ -104,6 +104,10 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor
}
cmd, err := b.buildMethodCommandCommon(descriptor, options, func(cmd *cobra.Command, input protoreflect.Message) error {
if noIdent, _ := cmd.Flags().GetBool(flagNoIndent); noIdent {
jsonMarshalOptions.Indent = ""
}
bz, err := jsonMarshalOptions.Marshal(input.Interface())
if err != nil {
return err
@ -114,6 +118,8 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor
if b.AddTxConnFlags != nil {
b.AddTxConnFlags(cmd)
cmd.Flags().BoolP(flagNoIndent, "", false, "Do not indent JSON output")
}
return cmd, err

View File

@ -110,6 +110,10 @@ func (b *Builder) BuildQueryMethodCommand(descriptor protoreflect.MethodDescript
}
cmd, err := b.buildMethodCommandCommon(descriptor, options, func(cmd *cobra.Command, input protoreflect.Message) error {
if noIdent, _ := cmd.Flags().GetBool(flagNoIndent); noIdent {
jsonMarshalOptions.Indent = ""
}
clientConn, err := getClientConn(cmd)
if err != nil {
return err
@ -134,6 +138,8 @@ func (b *Builder) BuildQueryMethodCommand(descriptor protoreflect.MethodDescript
if b.AddQueryConnFlags != nil {
b.AddQueryConnFlags(cmd)
cmd.Flags().BoolP(flagNoIndent, "", false, "Do not indent JSON output")
}
return cmd, nil

View File

@ -37,6 +37,7 @@ Flags:
--keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os")
--keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used
--ledger Use a connected Ledger device
--no-indent Do not indent JSON output
--node string <host>:<port> to CometBFT rpc interface for this chain (default "tcp://localhost:26657")
--note string Note to add a description to the transaction (previously --memo)
--offline Offline mode (does not allow any online functionality)

View File

@ -26,6 +26,7 @@ Flags:
--map-string-coin map[string]cosmos.base.v1beta1.Coin
--map-string-string stringToString (default [])
--map-string-uint32 stringToUint32
--no-indent Do not indent JSON output
--node string <host>:<port> to CometBFT RPC interface for this chain (default "tcp://localhost:26657")
-o, --output string Output format (text|json) (default "text")
--page-count-total

View File

@ -41,6 +41,7 @@ Flags:
--keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os")
--keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used
--ledger Use a connected Ledger device
--no-indent Do not indent JSON output
--node string <host>:<port> to CometBFT rpc interface for this chain (default "tcp://localhost:26657")
--note string Note to add a description to the transaction (previously --memo)
--offline Offline mode (does not allow any online functionality)

View File

@ -32,6 +32,7 @@ Flags:
--map-string-coin map[string]cosmos.base.v1beta1.Coin some map of string to coin
--map-string-string stringToString some map of string to string (default [])
--map-string-uint32 stringToUint32 some map of string to int32
--no-indent Do not indent JSON output
--node string <host>:<port> to CometBFT RPC interface for this chain (default "tcp://localhost:26657")
-o, --output string Output format (text|json) (default "text")
--page-count-total

View File

@ -7,33 +7,35 @@ import (
)
func (m appModule) AutoCLIOptions() *autocliv1.ModuleOptions {
return &autocliv1.ModuleOptions{Query: &autocliv1.ServiceCommandDescriptor{
Service: appv1alpha1.Query_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "Config",
Short: "Queries the current app config",
},
},
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{
"autocli": {
Service: autocliv1.Query_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "AppOptions",
Short: "Queries custom autocli options",
},
return &autocliv1.ModuleOptions{
Query: &autocliv1.ServiceCommandDescriptor{
Service: appv1alpha1.Query_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "Config",
Short: "Query the current app config",
},
},
"reflection": {
Service: reflectionv1.ReflectionService_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "FileDescriptors",
Short: "Queries the app's protobuf file descriptors",
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{
"autocli": {
Service: autocliv1.Query_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "AppOptions",
Short: "Query the custom autocli options",
},
},
},
"reflection": {
Service: reflectionv1.ReflectionService_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "FileDescriptors",
Short: "Query the app's protobuf file descriptors",
},
},
},
},
},
}}
}
}

View File

@ -8,6 +8,7 @@ import (
"strings"
cmtcfg "github.com/cometbft/cometbft/config"
cmtjson "github.com/cometbft/cometbft/libs/json"
"github.com/cometbft/cometbft/light"
"github.com/cometbft/cometbft/node"
"github.com/cometbft/cometbft/p2p"
@ -24,6 +25,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
servercmtlog "github.com/cosmos/cosmos-sdk/server/log"
@ -34,6 +36,43 @@ import (
auth "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
)
// StatusCommand returns the command to return the status of the network.
func StatusCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "status",
Short: "Query remote node for status",
RunE: func(cmd *cobra.Command, _ []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
status, err := cmtservice.GetNodeStatus(context.Background(), clientCtx)
if err != nil {
return err
}
output, err := cmtjson.Marshal(status)
if err != nil {
return err
}
// In order to maintain backwards compatibility, the default json format output
outputFormat, _ := cmd.Flags().GetString(flags.FlagOutput)
if outputFormat == flags.OutputFormatJSON {
clientCtx = clientCtx.WithOutputFormat(flags.OutputFormatJSON)
}
return clientCtx.PrintRaw(output)
},
}
cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
cmd.Flags().StringP(flags.FlagOutput, "o", "json", "Output format (text|json)")
return cmd
}
// ShowNodeIDCmd - ported from CometBFT, dump node ID to stdout
func ShowNodeIDCmd() *cobra.Command {
return &cobra.Command{
@ -48,7 +87,7 @@ func ShowNodeIDCmd() *cobra.Command {
return err
}
fmt.Println(nodeKey.ID())
cmd.Println(nodeKey.ID())
return nil
},
}
@ -80,7 +119,7 @@ func ShowValidatorCmd() *cobra.Command {
return err
}
fmt.Println(string(bz))
cmd.Println(string(bz))
return nil
},
}
@ -100,7 +139,8 @@ func ShowAddressCmd() *cobra.Command {
privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress())
fmt.Println(valConsAddr.String())
cmd.Println(valConsAddr.String())
return nil
},
}
@ -130,7 +170,7 @@ func VersionCmd() *cobra.Command {
return err
}
cmd.Print(string(bs))
cmd.Println(string(bs))
return nil
},
}

View File

@ -19,7 +19,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/pruning"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/snapshot"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
@ -126,7 +125,7 @@ func initRootCmd(
// add keybase, auxiliary RPC, query, genesis, and tx child commands
rootCmd.AddCommand(
rpc.StatusCommand(),
server.StatusCommand(),
genesisCommand(txConfig, basicManager),
queryCommand(),
txCommand(),
@ -159,7 +158,6 @@ func queryCommand() *cobra.Command {
}
cmd.AddCommand(
rpc.ValidatorCommand(),
server.QueryBlockCmd(),
authcmd.QueryTxsByEventsCmd(),
server.QueryBlocksCmd(),

View File

@ -3,6 +3,8 @@ package consensus
import (
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
consensusv1 "cosmossdk.io/api/cosmos/consensus/v1"
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
)
// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
@ -17,6 +19,9 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
Short: "Query the current consensus parameters",
},
},
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{
"comet": cmtservice.CometBFTAutoCLIDescriptor,
},
},
// Tx is purposely left empty, as the only tx is MsgUpdateParams which is gov gated.
}