feat(x/slashing): add autocli options for tx (#17967)

This commit is contained in:
Julien Robert 2023-10-09 15:57:35 +02:00 committed by GitHub
parent 19cbf9e757
commit 3690d9f7f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 193 deletions

View File

@ -23,6 +23,12 @@ import (
"github.com/cosmos/cosmos-sdk/runtime"
)
const (
AddressStringScalarType = "cosmos.AddressString"
ValidatorAddressStringScalarType = "cosmos.ValidatorAddressString"
ConsensusAddressStringScalarType = "cosmos.ConsensusAddressString"
)
// Builder manages options for building pflag flags for protobuf messages.
type Builder struct {
// TypeResolver specifies how protobuf types will be resolved. If it is
@ -61,9 +67,9 @@ func (b *Builder) init() {
if b.scalarFlagTypes == nil {
b.scalarFlagTypes = map[string]Type{}
b.scalarFlagTypes["cosmos.AddressString"] = addressStringType{}
b.scalarFlagTypes["cosmos.ValidatorAddressString"] = validatorAddressStringType{}
b.scalarFlagTypes["cosmos.ConsensusAddressString"] = consensusAddressStringType{}
b.scalarFlagTypes[AddressStringScalarType] = addressStringType{}
b.scalarFlagTypes[ValidatorAddressStringScalarType] = validatorAddressStringType{}
b.scalarFlagTypes[ConsensusAddressStringScalarType] = consensusAddressStringType{}
}
}
@ -387,10 +393,10 @@ func (b *Builder) resolveFlagType(field protoreflect.FieldDescriptor) Type {
}
func (b *Builder) resolveFlagTypeBasic(field protoreflect.FieldDescriptor) Type {
scalar := proto.GetExtension(field.Options(), cosmos_proto.E_Scalar)
if scalar != nil {
scalar, ok := GetScalarType(field)
if ok {
b.init()
if typ, ok := b.scalarFlagTypes[scalar.(string)]; ok {
if typ, ok := b.scalarFlagTypes[scalar]; ok {
return typ
}
}
@ -413,6 +419,13 @@ func (b *Builder) resolveFlagTypeBasic(field protoreflect.FieldDescriptor) Type
}
}
// GetScalarType gets scalar type of a field.
func GetScalarType(field protoreflect.FieldDescriptor) (string, bool) {
scalar := proto.GetExtension(field.Options(), cosmos_proto.E_Scalar)
scalarStr, ok := scalar.(string)
return scalarStr, ok
}
// GetSignerFieldName gets signer field name of a message.
// AutoCLI supports only one signer field per message.
func GetSignerFieldName(descriptor protoreflect.MessageDescriptor) string {

View File

@ -131,8 +131,21 @@ func (b *Builder) BuildMsgMethodCommand(descriptor protoreflect.MethodDescriptor
// set signer to signer field if empty
fd := input.Descriptor().Fields().ByName(protoreflect.Name(flag.GetSignerFieldName(input.Descriptor())))
if addr := input.Get(fd).String(); addr == "" {
addressCodec := b.Builder.AddressCodec
scalarType, ok := flag.GetScalarType(fd)
if ok {
// override address codec if validator or consensus address
switch scalarType {
case flag.ValidatorAddressStringScalarType:
addressCodec = b.Builder.ValidatorAddressCodec
case flag.ConsensusAddressStringScalarType:
addressCodec = b.Builder.ConsensusAddressCodec
}
}
signerFromFlag := clientCtx.GetFromAddress()
signer, err := b.ClientCtx.AddressCodec.BytesToString(signerFromFlag.Bytes())
signer, err := addressCodec.BytesToString(signerFromFlag.Bytes())
if err != nil {
return fmt.Errorf("failed to set signer on message, got %v: %w", signerFromFlag, err)
}

View File

@ -36,5 +36,20 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
},
},
},
Tx: &autocliv1.ServiceCommandDescriptor{
Service: slashingv1beta.Msg_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "Unjail",
Use: "unjail",
Short: "Unjail a jailed validator",
Example: fmt.Sprintf("%s tx slashing unjail --from [validator]", version.AppName),
},
{
RpcMethod: "UpdateParams",
Skip: true, // skipped because authority gated
},
},
},
}
}

View File

@ -1,5 +0,0 @@
package cli
const (
FlagAddressValidator = "validator"
)

View File

@ -1,58 +0,0 @@
package cli
import (
"github.com/spf13/cobra"
"cosmossdk.io/core/address"
"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/x/slashing/types"
)
// NewTxCmd returns a root CLI command handler for all x/slashing transaction commands.
func NewTxCmd(ac address.Codec) *cobra.Command {
slashingTxCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Slashing transaction subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
slashingTxCmd.AddCommand(NewUnjailTxCmd(ac))
return slashingTxCmd
}
// NewUnjailTxCmd returns a CLI command handler for creating a MsgUnjail transaction.
func NewUnjailTxCmd(valAc address.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unjail",
Args: cobra.NoArgs,
Short: "unjail validator previously jailed for downtime",
Long: `unjail a jailed validator:
$ <appd> tx slashing unjail --from mykey
`,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
valAddr, err := valAc.BytesToString(clientCtx.GetFromAddress())
if err != nil {
return err
}
msg := types.NewMsgUnjail(valAddr)
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}

View File

@ -1,116 +0,0 @@
package cli_test
import (
"fmt"
"io"
"testing"
abci "github.com/cometbft/cometbft/abci/types"
rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock"
"github.com/stretchr/testify/suite"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/crypto/types"
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/slashing"
"github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
)
type CLITestSuite struct {
suite.Suite
kr keyring.Keyring
baseCtx client.Context
clientCtx client.Context
encCfg testutilmod.TestEncodingConfig
pub types.PubKey
addr sdk.AccAddress
}
func TestCLITestSuite(t *testing.T) {
suite.Run(t, new(CLITestSuite))
}
func (s *CLITestSuite) SetupSuite() {
s.T().Log("setting up integration test suite")
s.encCfg = testutilmod.MakeTestEncodingConfig(slashing.AppModuleBasic{})
s.kr = keyring.NewInMemory(s.encCfg.Codec)
s.baseCtx = client.Context{}.
WithKeyring(s.kr).
WithTxConfig(s.encCfg.TxConfig).
WithCodec(s.encCfg.Codec).
WithClient(clitestutil.MockCometRPC{Client: rpcclientmock.Client{}}).
WithAccountRetriever(client.MockAccountRetriever{}).
WithOutput(io.Discard).
WithChainID("test-chain").
WithAddressCodec(addresscodec.NewBech32Codec("cosmos")).
WithValidatorAddressCodec(addresscodec.NewBech32Codec("cosmosvaloper")).
WithConsensusAddressCodec(addresscodec.NewBech32Codec("cosmosvalcons"))
ctxGen := func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
}
s.clientCtx = ctxGen()
k, _, err := s.clientCtx.Keyring.NewMnemonic("NewValidator", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
s.Require().NoError(err)
pub, err := k.GetPubKey()
s.Require().NoError(err)
s.pub = pub
s.addr = sdk.AccAddress(pub.Address())
}
func (s *CLITestSuite) TestNewUnjailTxCmd() {
val := s.addr
testCases := []struct {
name string
args []string
expectErrMsg string
}{
{
"valid transaction",
[]string{
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), // sync mode as there are no funds yet
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(10))).String()),
},
"",
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.NewUnjailTxCmd(addresscodec.NewBech32Codec("cosmosvaloper"))
clientCtx := s.clientCtx
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErrMsg != "" {
s.Require().Error(err)
} else {
s.Require().NoError(err)
txResp := &sdk.TxResponse{}
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), txResp), out.String())
}
})
}
}

View File

@ -6,7 +6,6 @@ import (
"fmt"
gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"
modulev1 "cosmossdk.io/api/cosmos/slashing/module/v1"
"cosmossdk.io/core/appmodule"
@ -20,7 +19,6 @@ import (
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
"github.com/cosmos/cosmos-sdk/x/slashing/keeper"
"github.com/cosmos/cosmos-sdk/x/slashing/simulation"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
@ -83,11 +81,6 @@ func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *g
}
}
// GetTxCmd returns the root tx command for the slashing module.
func (amb AppModuleBasic) GetTxCmd() *cobra.Command {
return cli.NewTxCmd(amb.cdc.InterfaceRegistry().SigningContext().ValidatorAddressCodec())
}
// AppModule implements an application module for the slashing module.
type AppModule struct {
AppModuleBasic