Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gentx): allow custom tx validation #12956

Merged
merged 13 commits into from
Aug 23, 2022
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,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.

Expand Down
2 changes: 1 addition & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ var (
// and genesis verification.
ModuleBasics = module.NewBasicManager(
auth.AppModuleBasic{},
genutil.AppModuleBasic{},
genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved
bank.AppModuleBasic{},
capability.AppModuleBasic{},
staking.AppModuleBasic{},
Expand Down
2 changes: 1 addition & 1 deletion simapp/app_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ var (
// and genesis verification.
ModuleBasics = module.NewBasicManager(
auth.AppModuleBasic{},
genutil.AppModuleBasic{},
genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
bank.AppModuleBasic{},
capability.AppModuleBasic{},
staking.AppModuleBasic{},
Expand Down
9 changes: 7 additions & 2 deletions simapp/simd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion simapp/simd/cmd/testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion testutil/network/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions x/genutil/client/cli/collect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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")
}
Expand Down
7 changes: 4 additions & 3 deletions x/genutil/collect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion x/genutil/collect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
12 changes: 10 additions & 2 deletions x/genutil/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand Down
36 changes: 19 additions & 17 deletions x/genutil/types/genesis_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,37 +93,39 @@ 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
}
}
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) {
tx, err := txJSONDecoder(genTx)
if err != nil {
return tx, fmt.Errorf("failed to decode gentx: %s, error: %s", genTx, err)
}
type MessageValidator func([]sdk.Msg) error

msgs := tx.GetMsgs()
func DefaultMessageValidator(msgs []sdk.Msg) error {
if len(msgs) != 1 {
return tx, fmt.Errorf("unexpected number of GenTx messages; got: %d, expected: 1", len(msgs))
return 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])
return 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 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, 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)
}

return tx, nil
return tx, validator(tx.GetMsgs())
}
4 changes: 2 additions & 2 deletions x/genutil/types/genesis_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand All @@ -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)
}

Expand Down