feat(gentx): allow custom tx validation (#12956)
## Description Closes: #12833 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
This commit is contained in:
parent
c76a00b28c
commit
3e492d4bca
@ -113,6 +113,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* (appModule) Remove `Route`, `QuerierRoute` and `LegacyQuerierHandler` from AppModule Interface.
|
||||
* (x/modules) Remove all LegacyQueries and related code from modules
|
||||
* (store) [#11825](https://github.com/cosmos/cosmos-sdk/pull/11825) Make extension snapshotter interface safer to use, renamed the util function `WriteExtensionItem` to `WriteExtensionPayload`.
|
||||
* (x/genutil)[#12956](https://github.com/cosmos/cosmos-sdk/pull/12956) `genutil.AppModuleBasic` has a new attribute: genesis transaction validation function. The existing validation logic is implemented in `genutiltypes.DefaultMessageValidator`. Use `genutil.NewAppModuleBasic` to create a new genutil Module Basic.
|
||||
* (codec) [#12964](https://github.com/cosmos/cosmos-sdk/pull/12964) `ProtoCodec.MarshalInterface` now returns an error when serializing unregistered types and a subsequent `ProtoCodec.UnmarshalInterface` would fail.
|
||||
* (x/staking) [#12973](https://github.com/cosmos/cosmos-sdk/pull/12973) Removed `stakingkeeper.RandomValidator`. Use `testutil.RandSliceElem(r, sk.GetAllValidators(ctx))` instead.
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ var (
|
||||
// and genesis verification.
|
||||
ModuleBasics = module.NewBasicManager(
|
||||
auth.AppModuleBasic{},
|
||||
genutil.AppModuleBasic{},
|
||||
genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
|
||||
bank.AppModuleBasic{},
|
||||
capability.AppModuleBasic{},
|
||||
staking.AppModuleBasic{},
|
||||
|
||||
@ -107,7 +107,7 @@ var (
|
||||
// and genesis verification.
|
||||
ModuleBasics = module.NewBasicManager(
|
||||
auth.AppModuleBasic{},
|
||||
genutil.AppModuleBasic{},
|
||||
genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
|
||||
bank.AppModuleBasic{},
|
||||
capability.AppModuleBasic{},
|
||||
staking.AppModuleBasic{},
|
||||
|
||||
@ -35,7 +35,9 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/crisis"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
)
|
||||
|
||||
// NewRootCmd creates a new root command for simd. It is called once in the
|
||||
@ -165,12 +167,15 @@ lru_size = 0`
|
||||
func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
|
||||
cfg := sdk.GetConfig()
|
||||
cfg.Seal()
|
||||
gentxModule := simapp.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic)
|
||||
|
||||
rootCmd.AddCommand(
|
||||
genutilcli.InitCmd(simapp.ModuleBasics, simapp.DefaultNodeHome),
|
||||
genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome),
|
||||
genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome,
|
||||
gentxModule.GenTxValidator),
|
||||
genutilcli.MigrateGenesisCmd(),
|
||||
genutilcli.GenTxCmd(simapp.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome),
|
||||
genutilcli.GenTxCmd(simapp.ModuleBasics, encodingConfig.TxConfig,
|
||||
banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome),
|
||||
genutilcli.ValidateGenesisCmd(simapp.ModuleBasics),
|
||||
AddGenesisAccountCmd(simapp.DefaultNodeHome),
|
||||
tmcli.NewCompletionCmd(rootCmd, true),
|
||||
|
||||
@ -421,7 +421,7 @@ func collectGenFiles(
|
||||
return err
|
||||
}
|
||||
|
||||
nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.Codec, clientCtx.TxConfig, nodeConfig, initCfg, *genDoc, genBalIterator)
|
||||
nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.Codec, clientCtx.TxConfig, nodeConfig, initCfg, *genDoc, genBalIterator, genutiltypes.DefaultMessageValidator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error {
|
||||
}
|
||||
|
||||
appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig,
|
||||
tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{})
|
||||
tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{}, genutiltypes.DefaultMessageValidator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
const flagGenTxDir = "gentx-dir"
|
||||
|
||||
// CollectGenTxsCmd - return the cobra command to collect genesis transactions
|
||||
func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeHome string) *cobra.Command {
|
||||
func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeHome string, validator types.MessageValidator) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "collect-gentxs",
|
||||
Short: "Collect genesis txs and output a genesis.json file",
|
||||
@ -52,7 +52,7 @@ func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeH
|
||||
|
||||
appMessage, err := genutil.GenAppStateFromConfig(cdc,
|
||||
clientCtx.TxConfig,
|
||||
config, initCfg, *genDoc, genBalIterator)
|
||||
config, initCfg, *genDoc, genBalIterator, validator)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get genesis app state from config")
|
||||
}
|
||||
|
||||
@ -26,11 +26,11 @@ import (
|
||||
// GenAppStateFromConfig gets the genesis app state from the config
|
||||
func GenAppStateFromConfig(cdc codec.JSONCodec, txEncodingConfig client.TxEncodingConfig,
|
||||
config *cfg.Config, initCfg types.InitConfig, genDoc tmtypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator,
|
||||
validator types.MessageValidator,
|
||||
) (appState json.RawMessage, err error) {
|
||||
// process genesis transactions, else create default genesis.json
|
||||
appGenTxs, persistentPeers, err := CollectTxs(
|
||||
cdc, txEncodingConfig.TxJSONDecoder(), config.Moniker, initCfg.GenTxsDir, genDoc, genBalIterator,
|
||||
)
|
||||
cdc, txEncodingConfig.TxJSONDecoder(), config.Moniker, initCfg.GenTxsDir, genDoc, genBalIterator, validator)
|
||||
if err != nil {
|
||||
return appState, err
|
||||
}
|
||||
@ -69,6 +69,7 @@ func GenAppStateFromConfig(cdc codec.JSONCodec, txEncodingConfig client.TxEncodi
|
||||
// the list of appGenTxs, and persistent peers required to generate genesis.json.
|
||||
func CollectTxs(cdc codec.JSONCodec, txJSONDecoder sdk.TxDecoder, moniker, genTxsDir string,
|
||||
genDoc tmtypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator,
|
||||
validator types.MessageValidator,
|
||||
) (appGenTxs []sdk.Tx, persistentPeers string, err error) {
|
||||
// prepare a map of all balances in genesis state to then validate
|
||||
// against the validators addresses
|
||||
@ -110,7 +111,7 @@ func CollectTxs(cdc codec.JSONCodec, txJSONDecoder sdk.TxDecoder, moniker, genTx
|
||||
return appGenTxs, persistentPeers, err
|
||||
}
|
||||
|
||||
genTx, err := types.ValidateAndGetGenTx(jsonRawTx, txJSONDecoder)
|
||||
genTx, err := types.ValidateAndGetGenTx(jsonRawTx, txJSONDecoder, validator)
|
||||
if err != nil {
|
||||
return appGenTxs, persistentPeers, err
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ func TestCollectTxsHandlesDirectories(t *testing.T) {
|
||||
balItr := new(doNothingIterator)
|
||||
|
||||
dnc := &doNothingUnmarshalJSON{cdc}
|
||||
if _, _, err := genutil.CollectTxs(dnc, txDecoder, "foo", testDir, gdoc, balItr); err != nil {
|
||||
if _, _, err := genutil.CollectTxs(dnc, txDecoder, "foo", testDir, gdoc, balItr, gtypes.DefaultMessageValidator); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,15 @@ var (
|
||||
)
|
||||
|
||||
// AppModuleBasic defines the basic application module used by the genutil module.
|
||||
type AppModuleBasic struct{}
|
||||
type AppModuleBasic struct {
|
||||
GenTxValidator types.MessageValidator
|
||||
}
|
||||
|
||||
// NewAppModuleBasic creates AppModuleBasic, validator is a function used to validate genesis
|
||||
// transactions.
|
||||
func NewAppModuleBasic(validator types.MessageValidator) AppModuleBasic {
|
||||
return AppModuleBasic{validator}
|
||||
}
|
||||
|
||||
// Name returns the genutil module's name.
|
||||
func (AppModuleBasic) Name() string {
|
||||
@ -53,7 +61,7 @@ func (b AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, txEncodingConfig cl
|
||||
return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
|
||||
}
|
||||
|
||||
return types.ValidateGenesis(&data, txEncodingConfig.TxJSONDecoder())
|
||||
return types.ValidateGenesis(&data, txEncodingConfig.TxJSONDecoder(), b.GenTxValidator)
|
||||
}
|
||||
|
||||
// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the genutil module.
|
||||
|
||||
@ -93,9 +93,9 @@ func GenesisStateFromGenFile(genFile string) (genesisState map[string]json.RawMe
|
||||
}
|
||||
|
||||
// ValidateGenesis validates GenTx transactions
|
||||
func ValidateGenesis(genesisState *GenesisState, txJSONDecoder sdk.TxDecoder) error {
|
||||
func ValidateGenesis(genesisState *GenesisState, txJSONDecoder sdk.TxDecoder, validator MessageValidator) error {
|
||||
for _, genTx := range genesisState.GenTxs {
|
||||
_, err := ValidateAndGetGenTx(genTx, txJSONDecoder)
|
||||
_, err := ValidateAndGetGenTx(genTx, txJSONDecoder, validator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -103,27 +103,29 @@ func ValidateGenesis(genesisState *GenesisState, txJSONDecoder sdk.TxDecoder) er
|
||||
return nil
|
||||
}
|
||||
|
||||
type MessageValidator func([]sdk.Msg) error
|
||||
|
||||
func DefaultMessageValidator(msgs []sdk.Msg) error {
|
||||
if len(msgs) != 1 {
|
||||
return fmt.Errorf("unexpected number of GenTx messages; got: %d, expected: 1", len(msgs))
|
||||
}
|
||||
if _, ok := msgs[0].(*stakingtypes.MsgCreateValidator); !ok {
|
||||
return fmt.Errorf("unexpected GenTx message type; expected: MsgCreateValidator, got: %T", msgs[0])
|
||||
}
|
||||
if err := msgs[0].ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("invalid GenTx '%s': %w", msgs[0], err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateAndGetGenTx validates the genesis transaction and returns GenTx if valid
|
||||
// it cannot verify the signature as it is stateless validation
|
||||
func ValidateAndGetGenTx(genTx json.RawMessage, txJSONDecoder sdk.TxDecoder) (sdk.Tx, error) {
|
||||
func ValidateAndGetGenTx(genTx json.RawMessage, txJSONDecoder sdk.TxDecoder, validator MessageValidator) (sdk.Tx, error) {
|
||||
tx, err := txJSONDecoder(genTx)
|
||||
if err != nil {
|
||||
return tx, fmt.Errorf("failed to decode gentx: %s, error: %s", genTx, err)
|
||||
}
|
||||
|
||||
msgs := tx.GetMsgs()
|
||||
if len(msgs) != 1 {
|
||||
return tx, fmt.Errorf("unexpected number of GenTx messages; got: %d, expected: 1", len(msgs))
|
||||
}
|
||||
|
||||
// TODO: abstract back to staking
|
||||
if _, ok := msgs[0].(*stakingtypes.MsgCreateValidator); !ok {
|
||||
return tx, fmt.Errorf("unexpected GenTx message type; expected: MsgCreateValidator, got: %T", msgs[0])
|
||||
}
|
||||
|
||||
if err := msgs[0].ValidateBasic(); err != nil {
|
||||
return tx, fmt.Errorf("invalid GenTx '%s': %s", msgs[0], err)
|
||||
}
|
||||
|
||||
return tx, nil
|
||||
return tx, validator(tx.GetMsgs())
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ func TestValidateGenesisMultipleMessages(t *testing.T) {
|
||||
tx := txBuilder.GetTx()
|
||||
genesisState := types.NewGenesisStateFromTx(txConfig.TxJSONEncoder(), []sdk.Tx{tx})
|
||||
|
||||
err = types.ValidateGenesis(genesisState, txConfig.TxJSONDecoder())
|
||||
err = types.ValidateGenesis(genesisState, txConfig.TxJSONDecoder(), types.DefaultMessageValidator)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ func TestValidateGenesisBadMessage(t *testing.T) {
|
||||
tx := txBuilder.GetTx()
|
||||
genesisState := types.NewGenesisStateFromTx(txConfig.TxJSONEncoder(), []sdk.Tx{tx})
|
||||
|
||||
err = types.ValidateGenesis(genesisState, txConfig.TxJSONDecoder())
|
||||
err = types.ValidateGenesis(genesisState, txConfig.TxJSONDecoder(), types.DefaultMessageValidator)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user