diff --git a/app/app.go b/app/app.go index 4573be686..d25bc5583 100644 --- a/app/app.go +++ b/app/app.go @@ -900,7 +900,13 @@ func New( icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper) - interchainQueriesModule := interchainqueries.NewAppModule(appCodec, app.InterchainQueriesKeeper, app.AccountKeeper, app.BankKeeper) + interchainQueriesModule := interchainqueries.NewAppModule( + appCodec, + keys[interchainqueriesmoduletypes.StoreKey], + app.InterchainQueriesKeeper, + app.AccountKeeper, + app.BankKeeper, + ) interchainTxsModule := interchaintxs.NewAppModule(appCodec, app.InterchainTxsKeeper, app.AccountKeeper, app.BankKeeper) contractManagerModule := contractmanager.NewAppModule(appCodec, app.ContractManagerKeeper) ibcHooksModule := ibchooks.NewAppModule(app.AccountKeeper) diff --git a/proto/neutron/interchainqueries/params.proto b/proto/neutron/interchainqueries/params.proto index 0dca4a206..7b05c1f58 100644 --- a/proto/neutron/interchainqueries/params.proto +++ b/proto/neutron/interchainqueries/params.proto @@ -23,4 +23,10 @@ message Params { // balance between network cleaning speed and EndBlock duration. A zero value // means no limit. uint64 tx_query_removal_limit = 3; + + // Maximum amount of keys in a registered key value query + uint64 max_kv_query_keys_count = 4; + + // max_transactions_filters defines maximum allowed amount of tx filters in msgRegisterInterchainQuery + uint64 max_transactions_filters = 5; } diff --git a/x/interchainqueries/keeper/migrations.go b/x/interchainqueries/keeper/migrations.go new file mode 100644 index 000000000..a036ea33e --- /dev/null +++ b/x/interchainqueries/keeper/migrations.go @@ -0,0 +1,25 @@ +package keeper + +import ( + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + v3 "github.com/neutron-org/neutron/v4/x/interchainqueries/migrations/v3" +) + +// Migrator is a struct for handling in-place store migrations. +type Migrator struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey +} + +// NewMigrator returns a new Migrator. +func NewMigrator(cdc codec.BinaryCodec, storeKey storetypes.StoreKey) Migrator { + return Migrator{storeKey: storeKey, cdc: cdc} +} + +// Migrate2to3 migrates from version 2 to 3. +func (m Migrator) Migrate2to3(ctx sdk.Context) error { + return v3.MigrateParams(ctx, m.cdc, m.storeKey) +} diff --git a/x/interchainqueries/keeper/msg_server.go b/x/interchainqueries/keeper/msg_server.go index 57dc93f46..5b3cc345a 100644 --- a/x/interchainqueries/keeper/msg_server.go +++ b/x/interchainqueries/keeper/msg_server.go @@ -35,14 +35,14 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { func (m msgServer) RegisterInterchainQuery(goCtx context.Context, msg *types.MsgRegisterInterchainQuery) (*types.MsgRegisterInterchainQueryResponse, error) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), LabelRegisterInterchainQuery) + ctx := sdk.UnwrapSDKContext(goCtx) + ctx.Logger().Debug("RegisterInterchainQuery", "msg", msg) + params := m.GetParams(ctx) - if err := msg.Validate(); err != nil { + if err := msg.Validate(params); err != nil { return nil, errors.Wrap(err, "failed to validate MsgRegisterInterchainQuery") } - ctx := sdk.UnwrapSDKContext(goCtx) - ctx.Logger().Debug("RegisterInterchainQuery", "msg", msg) - senderAddr, err := sdk.AccAddressFromBech32(msg.Sender) if err != nil { m.Logger(ctx).Debug("RegisterInterchainQuery: failed to parse sender address", "sender_address", msg.Sender) @@ -62,8 +62,6 @@ func (m msgServer) RegisterInterchainQuery(goCtx context.Context, msg *types.Msg lastID := m.GetLastRegisteredQueryKey(ctx) lastID++ - params := m.GetParams(ctx) - registeredQuery := &types.RegisteredQuery{ Id: lastID, Owner: msg.Sender, @@ -122,12 +120,13 @@ func (m msgServer) RemoveInterchainQuery(goCtx context.Context, msg *types.MsgRe } func (m msgServer) UpdateInterchainQuery(goCtx context.Context, msg *types.MsgUpdateInterchainQueryRequest) (*types.MsgUpdateInterchainQueryResponse, error) { - if err := msg.Validate(); err != nil { - return nil, errors.Wrap(err, "failed to validate MsgUpdateInterchainQueryRequest") - } - ctx := sdk.UnwrapSDKContext(goCtx) ctx.Logger().Debug("UpdateInterchainQuery", "msg", msg) + params := m.GetParams(ctx) + + if err := msg.Validate(params); err != nil { + return nil, errors.Wrap(err, "failed to validate MsgUpdateInterchainQueryRequest") + } query, err := m.GetQueryByID(ctx, msg.GetQueryId()) if err != nil { diff --git a/x/interchainqueries/keeper/msg_server_test.go b/x/interchainqueries/keeper/msg_server_test.go index 0471a360d..ce8f9fe60 100644 --- a/x/interchainqueries/keeper/msg_server_test.go +++ b/x/interchainqueries/keeper/msg_server_test.go @@ -99,7 +99,7 @@ func TestMsgRegisterInterchainQueryValidate(t *testing.T) { "too many keys", types.MsgRegisterInterchainQuery{ QueryType: string(types.InterchainQueryTypeKV), - Keys: make([]*types.KVKey, types.MaxKVQueryKeysCount+1), + Keys: make([]*types.KVKey, types.DefaultMaxKvQueryKeysCount+1), TransactionsFilter: "[]", ConnectionId: "connection-0", UpdatePeriod: 1, @@ -394,7 +394,7 @@ func TestMsgUpdateInterchainQueryRequestValidate(t *testing.T) { "too many keys", types.MsgUpdateInterchainQueryRequest{ QueryId: 1, - NewKeys: make([]*types.KVKey, types.MaxKVQueryKeysCount+1), + NewKeys: make([]*types.KVKey, types.DefaultMaxKvQueryKeysCount+1), NewUpdatePeriod: 0, Sender: testutil.TestOwnerAddress, }, diff --git a/x/interchainqueries/migrations/v3/migration.go b/x/interchainqueries/migrations/v3/migration.go new file mode 100644 index 000000000..a77e4bed8 --- /dev/null +++ b/x/interchainqueries/migrations/v3/migration.go @@ -0,0 +1,28 @@ +package v3 + +import ( + "fmt" + + store "cosmossdk.io/store/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/neutron-org/neutron/v4/x/interchainqueries/types" +) + +func MigrateParams(ctx sdk.Context, cdc codec.BinaryCodec, storeKey store.StoreKey) error { + var params types.Params + st := ctx.KVStore(storeKey) + bz := st.Get(types.ParamsKey) + if bz == nil { + return fmt.Errorf("no params stored in %s", types.ParamsKey) + } + + cdc.MustUnmarshal(bz, ¶ms) + params.MaxTransactionsFilters = types.DefaultMaxTransactionsFilters + params.MaxKvQueryKeysCount = types.DefaultMaxKvQueryKeysCount + bz = cdc.MustMarshal(¶ms) + st.Set(types.ParamsKey, bz) + return nil +} diff --git a/x/interchainqueries/migrations/v3/migration_test.go b/x/interchainqueries/migrations/v3/migration_test.go new file mode 100644 index 000000000..c36014df8 --- /dev/null +++ b/x/interchainqueries/migrations/v3/migration_test.go @@ -0,0 +1,83 @@ +package v3_test + +import ( + "testing" + + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + "gopkg.in/yaml.v2" + + "github.com/neutron-org/neutron/v4/testutil" + v3 "github.com/neutron-org/neutron/v4/x/interchainqueries/migrations/v3" + "github.com/neutron-org/neutron/v4/x/interchainqueries/types" +) + +type V3ICQMigrationTestSuite struct { + testutil.IBCConnectionTestSuite +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(V3ICQMigrationTestSuite)) +} + +// ParamsV2 defines the parameters for the module v2. +type ParamsV2 struct { + // Defines amount of blocks required before query becomes available for + // removal by anybody + QuerySubmitTimeout uint64 `protobuf:"varint,1,opt,name=query_submit_timeout,json=querySubmitTimeout,proto3" json:"query_submit_timeout,omitempty"` + // Amount of coins deposited for the query. + QueryDeposit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=query_deposit,json=queryDeposit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"query_deposit"` + // Amount of tx hashes to be removed during a single EndBlock. Can vary to + // balance between network cleaning speed and EndBlock duration. A zero value + // means no limit. + TxQueryRemovalLimit uint64 `protobuf:"varint,3,opt,name=tx_query_removal_limit,json=txQueryRemovalLimit,proto3" json:"tx_query_removal_limit,omitempty"` +} + +func (p *ParamsV2) Reset() { *p = ParamsV2{} } +func (p *ParamsV2) ProtoMessage() {} + +// String implements the Stringer interface. +func (p ParamsV2) String() string { + out, _ := yaml.Marshal(p) + return string(out) +} + +func (suite *V3ICQMigrationTestSuite) TestParamsMigration() { + var ( + app = suite.GetNeutronZoneApp(suite.ChainA) + storeKey = app.GetKey(types.StoreKey) + ctx = suite.ChainA.GetContext() + cdc = app.AppCodec() + ) + + // preinitialize v2 params + p := ParamsV2{ + QuerySubmitTimeout: types.DefaultQuerySubmitTimeout, + QueryDeposit: types.DefaultQueryDeposit, + TxQueryRemovalLimit: types.DefaultTxQueryRemovalLimit, + } + store := ctx.KVStore(storeKey) + bz, err := cdc.Marshal(&p) + suite.Require().NoError(err) + store.Set(types.ParamsKey, bz) + + paramsOld := app.InterchainQueriesKeeper.GetParams(ctx) + suite.Require().Equal(paramsOld.TxQueryRemovalLimit, p.TxQueryRemovalLimit) + suite.Require().Equal(paramsOld.QuerySubmitTimeout, p.QuerySubmitTimeout) + suite.Require().Equal(paramsOld.QueryDeposit, p.QueryDeposit) + suite.Require().Equal(paramsOld.MaxTransactionsFilters, uint64(0)) + suite.Require().Equal(paramsOld.MaxKvQueryKeysCount, uint64(0)) + + err = v3.MigrateParams(ctx, cdc, storeKey) + suite.Require().NoError(err) + + paramsNew := app.InterchainQueriesKeeper.GetParams(ctx) + params := types.Params{ + QuerySubmitTimeout: types.DefaultQuerySubmitTimeout, + QueryDeposit: types.DefaultQueryDeposit, + TxQueryRemovalLimit: types.DefaultTxQueryRemovalLimit, + MaxKvQueryKeysCount: types.DefaultMaxKvQueryKeysCount, + MaxTransactionsFilters: types.DefaultMaxTransactionsFilters, + } + suite.Require().Equal(params, paramsNew) +} diff --git a/x/interchainqueries/module.go b/x/interchainqueries/module.go index 4f9910975..cf1572bd7 100644 --- a/x/interchainqueries/module.go +++ b/x/interchainqueries/module.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" + storetypes "cosmossdk.io/store/types" + "cosmossdk.io/core/appmodule" abci "github.com/cometbft/cometbft/abci/types" @@ -103,6 +105,7 @@ var _ appmodule.AppModule = AppModule{} type AppModule struct { AppModuleBasic + storeKey storetypes.StoreKey keeper keeper.Keeper accountKeeper types.AccountKeeper bankKeeper types.BankKeeper @@ -110,12 +113,14 @@ type AppModule struct { func NewAppModule( cdc codec.Codec, + storeKey storetypes.StoreKey, keeper keeper.Keeper, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, ) AppModule { return AppModule{ AppModuleBasic: NewAppModuleBasic(cdc), + storeKey: storeKey, keeper: keeper, accountKeeper: accountKeeper, bankKeeper: bankKeeper, @@ -141,6 +146,12 @@ func (AppModule) QuerierRoute() string { return types.QuerierRoute } // RegisterServices registers a GRPC query service to respond to the // module-specific GRPC queries. func (am AppModule) RegisterServices(cfg module.Configurator) { + m := keeper.NewMigrator(am.cdc, am.storeKey) + + if err := cfg.RegisterMigration(types.ModuleName, 2, m.Migrate2to3); err != nil { + panic(fmt.Sprintf("failed to migrate x/interchainqueries from version 2 to 3: %v", err)) + } + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) } diff --git a/x/interchainqueries/types/constants.go b/x/interchainqueries/types/constants.go index 4c93fbeff..fef80f236 100644 --- a/x/interchainqueries/types/constants.go +++ b/x/interchainqueries/types/constants.go @@ -1,3 +1,3 @@ package types -const ConsensusVersion = 2 +const ConsensusVersion = 3 diff --git a/x/interchainqueries/types/genesis.go b/x/interchainqueries/types/genesis.go index c00668e58..29cc30ae1 100644 --- a/x/interchainqueries/types/genesis.go +++ b/x/interchainqueries/types/genesis.go @@ -34,14 +34,14 @@ func (gs GenesisState) Validate() error { switch val.QueryType { case string(InterchainQueryTypeTX): - if err := ValidateTransactionsFilter(val.TransactionsFilter); err != nil { + if err := ValidateTransactionsFilter(val.TransactionsFilter, gs.Params.MaxTransactionsFilters); err != nil { return errors.Wrap(ErrInvalidTransactionsFilter, err.Error()) } case string(InterchainQueryTypeKV): if len(val.Keys) == 0 { return errors.Wrap(ErrEmptyKeys, "keys cannot be empty") } - if err := validateKeys(val.GetKeys()); err != nil { + if err := validateKeys(val.GetKeys(), gs.Params.MaxKvQueryKeysCount); err != nil { return err } default: diff --git a/x/interchainqueries/types/params.go b/x/interchainqueries/types/params.go index ce52e3689..063cefc28 100644 --- a/x/interchainqueries/types/params.go +++ b/x/interchainqueries/types/params.go @@ -14,12 +14,14 @@ import ( var _ paramtypes.ParamSet = (*Params)(nil) var ( - KeyQuerySubmitTimeout = []byte("QuerySubmitTimeout") - DefaultQuerySubmitTimeout = uint64(1036800) // One month, with block_time = 2.5s - KeyQueryDeposit = []byte("QueryDeposit") - DefaultQueryDeposit = sdk.NewCoins(sdk.NewCoin(params.DefaultDenom, math.NewInt(int64(1_000_000)))) - KeyTxQueryRemovalLimit = []byte("TxQueryRemovalLimit") - DefaultTxQueryRemovalLimit = uint64(10_000) + KeyQuerySubmitTimeout = []byte("QuerySubmitTimeout") + DefaultQuerySubmitTimeout = uint64(1036800) // One month, with block_time = 2.5s + KeyQueryDeposit = []byte("QueryDeposit") + DefaultQueryDeposit = sdk.NewCoins(sdk.NewCoin(params.DefaultDenom, math.NewInt(int64(1_000_000)))) + KeyTxQueryRemovalLimit = []byte("TxQueryRemovalLimit") + DefaultTxQueryRemovalLimit = uint64(10_000) + DefaultMaxKvQueryKeysCount = uint64(32) + DefaultMaxTransactionsFilters = uint64(32) ) // ParamKeyTable the param key table for launch module @@ -32,17 +34,19 @@ func ParamKeyTable() paramtypes.KeyTable { } // NewParams creates a new Params instance -func NewParams(querySubmitTimeout uint64, queryDeposit sdk.Coins, txQueryRemovalLimit uint64) Params { +func NewParams(querySubmitTimeout uint64, queryDeposit sdk.Coins, txQueryRemovalLimit, maxKvQueryKeysCount, maxTransactionsFilters uint64) Params { return Params{ - QuerySubmitTimeout: querySubmitTimeout, - QueryDeposit: queryDeposit, - TxQueryRemovalLimit: txQueryRemovalLimit, + QuerySubmitTimeout: querySubmitTimeout, + QueryDeposit: queryDeposit, + TxQueryRemovalLimit: txQueryRemovalLimit, + MaxKvQueryKeysCount: maxKvQueryKeysCount, + MaxTransactionsFilters: maxTransactionsFilters, } } // DefaultParams returns a default set of parameters func DefaultParams() Params { - return NewParams(DefaultQuerySubmitTimeout, DefaultQueryDeposit, DefaultTxQueryRemovalLimit) + return NewParams(DefaultQuerySubmitTimeout, DefaultQueryDeposit, DefaultTxQueryRemovalLimit, DefaultMaxKvQueryKeysCount, DefaultMaxTransactionsFilters) } // ParamSetPairs get the params.ParamSet diff --git a/x/interchainqueries/types/params.pb.go b/x/interchainqueries/types/params.pb.go index 5afc785e0..18d044b6f 100644 --- a/x/interchainqueries/types/params.pb.go +++ b/x/interchainqueries/types/params.pb.go @@ -36,6 +36,10 @@ type Params struct { // balance between network cleaning speed and EndBlock duration. A zero value // means no limit. TxQueryRemovalLimit uint64 `protobuf:"varint,3,opt,name=tx_query_removal_limit,json=txQueryRemovalLimit,proto3" json:"tx_query_removal_limit,omitempty"` + // Maximum amount of keys in a registered key value query + MaxKvQueryKeysCount uint64 `protobuf:"varint,4,opt,name=max_kv_query_keys_count,json=maxKvQueryKeysCount,proto3" json:"max_kv_query_keys_count,omitempty"` + // max_transactions_filters defines maximum allowed amount of tx filters in msgRegisterInterchainQuery + MaxTransactionsFilters uint64 `protobuf:"varint,5,opt,name=max_transactions_filters,json=maxTransactionsFilters,proto3" json:"max_transactions_filters,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -91,6 +95,20 @@ func (m *Params) GetTxQueryRemovalLimit() uint64 { return 0 } +func (m *Params) GetMaxKvQueryKeysCount() uint64 { + if m != nil { + return m.MaxKvQueryKeysCount + } + return 0 +} + +func (m *Params) GetMaxTransactionsFilters() uint64 { + if m != nil { + return m.MaxTransactionsFilters + } + return 0 +} + func init() { proto.RegisterType((*Params)(nil), "neutron.interchainqueries.Params") } @@ -100,28 +118,32 @@ func init() { } var fileDescriptor_752a5f3346da64b1 = []byte{ - // 331 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xb1, 0x4e, 0xf3, 0x30, - 0x10, 0xc7, 0x93, 0xaf, 0x55, 0x87, 0x7c, 0xb0, 0x84, 0x0a, 0xb5, 0x1d, 0xdc, 0x8a, 0x01, 0x75, - 0xa9, 0xdd, 0x52, 0x26, 0xd8, 0x0a, 0x23, 0x03, 0x14, 0x58, 0x58, 0xa2, 0x24, 0xb5, 0x52, 0x8b, - 0x3a, 0x17, 0xec, 0x4b, 0xd5, 0xbe, 0x05, 0x23, 0x23, 0x33, 0x4f, 0xd2, 0xb1, 0x23, 0x13, 0xa0, - 0x76, 0xe0, 0x35, 0x50, 0xec, 0x20, 0x21, 0xc1, 0xe4, 0x93, 0x7f, 0x77, 0xfe, 0xff, 0x74, 0xf6, - 0x0e, 0x53, 0x9e, 0xa3, 0x82, 0x94, 0x89, 0x14, 0xb9, 0x8a, 0xa7, 0xa1, 0x48, 0x1f, 0x72, 0xae, - 0x04, 0xd7, 0x2c, 0x0b, 0x55, 0x28, 0x35, 0xcd, 0x14, 0x20, 0xf8, 0xcd, 0xb2, 0x8f, 0xfe, 0xea, - 0x6b, 0x91, 0x18, 0xb4, 0x04, 0xcd, 0xa2, 0x50, 0x73, 0x36, 0x1f, 0x44, 0x1c, 0xc3, 0x01, 0x8b, - 0x41, 0xa4, 0x76, 0xb4, 0x55, 0x4f, 0x20, 0x01, 0x53, 0xb2, 0xa2, 0xb2, 0xb7, 0x07, 0x9f, 0xae, - 0x57, 0xbb, 0x34, 0x09, 0x7e, 0xdf, 0xab, 0x17, 0x6f, 0x2d, 0x03, 0x9d, 0x47, 0x52, 0x60, 0x80, - 0x42, 0x72, 0xc8, 0xb1, 0xe1, 0x76, 0xdc, 0x6e, 0x75, 0xec, 0x1b, 0x76, 0x6d, 0xd0, 0x8d, 0x25, - 0x7e, 0xe6, 0xed, 0xda, 0x89, 0x09, 0xcf, 0x40, 0x0b, 0x6c, 0xfc, 0xeb, 0x54, 0xba, 0xff, 0x8f, - 0x9a, 0xd4, 0xaa, 0xd0, 0x42, 0x85, 0x96, 0x2a, 0xf4, 0x0c, 0x44, 0x3a, 0xea, 0xaf, 0xde, 0xda, - 0xce, 0xcb, 0x7b, 0xbb, 0x9b, 0x08, 0x9c, 0xe6, 0x11, 0x8d, 0x41, 0xb2, 0xd2, 0xdb, 0x1e, 0x3d, - 0x3d, 0xb9, 0x67, 0xb8, 0xcc, 0xb8, 0x36, 0x03, 0x7a, 0xbc, 0x63, 0x12, 0xce, 0x6d, 0x80, 0x3f, - 0xf4, 0xf6, 0x71, 0x11, 0xd8, 0x50, 0xc5, 0x25, 0xcc, 0xc3, 0x59, 0x30, 0x13, 0x52, 0x60, 0xa3, - 0x62, 0x2c, 0xf7, 0x70, 0x71, 0x55, 0xc0, 0xb1, 0x65, 0x17, 0x05, 0x3a, 0xa9, 0x3e, 0x3d, 0xb7, - 0x9d, 0xd1, 0xed, 0x6a, 0x43, 0xdc, 0xf5, 0x86, 0xb8, 0x1f, 0x1b, 0xe2, 0x3e, 0x6e, 0x89, 0xb3, - 0xde, 0x12, 0xe7, 0x75, 0x4b, 0x9c, 0xbb, 0xd3, 0x1f, 0x32, 0xe5, 0x7e, 0x7b, 0xa0, 0x92, 0xef, - 0x9a, 0xcd, 0x8f, 0xd9, 0xe2, 0x8f, 0x8f, 0x31, 0x96, 0x51, 0xcd, 0xec, 0x71, 0xf8, 0x15, 0x00, - 0x00, 0xff, 0xff, 0xc7, 0x61, 0x00, 0x43, 0xc2, 0x01, 0x00, 0x00, + // 397 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0x31, 0x8f, 0xd3, 0x30, + 0x14, 0xc7, 0x93, 0xbb, 0x72, 0x43, 0x80, 0x25, 0x9c, 0x8e, 0x5c, 0x87, 0xb4, 0x62, 0x40, 0x5d, + 0x1a, 0xb7, 0xb4, 0x03, 0x82, 0xad, 0x45, 0x2c, 0x65, 0x80, 0x52, 0x16, 0x96, 0xc8, 0x49, 0x4d, + 0x6a, 0xb5, 0xf6, 0x0b, 0xf6, 0x4b, 0x94, 0x7c, 0x0b, 0x46, 0x46, 0x66, 0x3e, 0x49, 0xc7, 0x6e, + 0x30, 0x01, 0x6a, 0xbf, 0x08, 0x8a, 0x1d, 0xa4, 0x4a, 0xdc, 0x94, 0xa7, 0xfc, 0xfc, 0xcb, 0xff, + 0x9f, 0xbc, 0x78, 0x4f, 0x25, 0x2b, 0x50, 0x81, 0x24, 0x5c, 0x22, 0x53, 0xe9, 0x86, 0x72, 0xf9, + 0xb9, 0x60, 0x8a, 0x33, 0x4d, 0x72, 0xaa, 0xa8, 0xd0, 0x51, 0xae, 0x00, 0xc1, 0xbf, 0x6d, 0xcf, + 0x45, 0xff, 0x9d, 0xeb, 0x86, 0x29, 0x68, 0x01, 0x9a, 0x24, 0x54, 0x33, 0x52, 0x8e, 0x13, 0x86, + 0x74, 0x4c, 0x52, 0xe0, 0xd2, 0xaa, 0xdd, 0xeb, 0x0c, 0x32, 0x30, 0x23, 0x69, 0x26, 0x7b, 0xf7, + 0xc9, 0x8f, 0x0b, 0xef, 0xea, 0xad, 0x49, 0xf0, 0x47, 0xde, 0x75, 0xf3, 0xac, 0x3a, 0xd6, 0x45, + 0x22, 0x38, 0xc6, 0xc8, 0x05, 0x83, 0x02, 0x03, 0xb7, 0xef, 0x0e, 0x3a, 0x4b, 0xdf, 0xb0, 0xf7, + 0x06, 0xad, 0x2c, 0xf1, 0x73, 0xef, 0xa1, 0x35, 0xd6, 0x2c, 0x07, 0xcd, 0x31, 0xb8, 0xe8, 0x5f, + 0x0e, 0xee, 0x3f, 0xbb, 0x8d, 0x6c, 0x95, 0xa8, 0xa9, 0x12, 0xb5, 0x55, 0xa2, 0x39, 0x70, 0x39, + 0x1b, 0xed, 0x7f, 0xf5, 0x9c, 0xef, 0xbf, 0x7b, 0x83, 0x8c, 0xe3, 0xa6, 0x48, 0xa2, 0x14, 0x04, + 0x69, 0x7b, 0xdb, 0xcb, 0x50, 0xaf, 0xb7, 0x04, 0xeb, 0x9c, 0x69, 0x23, 0xe8, 0xe5, 0x03, 0x93, + 0xf0, 0xca, 0x06, 0xf8, 0x13, 0xef, 0x06, 0xab, 0xd8, 0x86, 0x2a, 0x26, 0xa0, 0xa4, 0xbb, 0x78, + 0xc7, 0x05, 0xc7, 0xe0, 0xd2, 0xb4, 0x7c, 0x84, 0xd5, 0xbb, 0x06, 0x2e, 0x2d, 0x7b, 0xd3, 0x20, + 0x7f, 0xea, 0x3d, 0x16, 0xb4, 0x8a, 0xb7, 0x65, 0x2b, 0x6e, 0x59, 0xad, 0xe3, 0x14, 0x0a, 0x89, + 0x41, 0xc7, 0x5a, 0x82, 0x56, 0x8b, 0xd2, 0x88, 0x0b, 0x56, 0xeb, 0x79, 0x83, 0xfc, 0xe7, 0x5e, + 0xd0, 0x58, 0xa8, 0xa8, 0xd4, 0x34, 0x45, 0x0e, 0x52, 0xc7, 0x9f, 0xf8, 0x0e, 0x99, 0xd2, 0xc1, + 0x3d, 0xa3, 0xdd, 0x08, 0x5a, 0xad, 0xce, 0xf0, 0x6b, 0x4b, 0x5f, 0x74, 0xbe, 0x7e, 0xeb, 0x39, + 0xb3, 0x0f, 0xfb, 0x63, 0xe8, 0x1e, 0x8e, 0xa1, 0xfb, 0xe7, 0x18, 0xba, 0x5f, 0x4e, 0xa1, 0x73, + 0x38, 0x85, 0xce, 0xcf, 0x53, 0xe8, 0x7c, 0x7c, 0x79, 0xf6, 0xf2, 0xed, 0x3e, 0x87, 0xa0, 0xb2, + 0x7f, 0x33, 0x29, 0xa7, 0xa4, 0xba, 0xe3, 0x47, 0x30, 0x5f, 0x25, 0xb9, 0x32, 0x7b, 0x9b, 0xfc, + 0x0d, 0x00, 0x00, 0xff, 0xff, 0x8f, 0x38, 0x73, 0xdc, 0x32, 0x02, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -144,6 +166,16 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.MaxTransactionsFilters != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MaxTransactionsFilters)) + i-- + dAtA[i] = 0x28 + } + if m.MaxKvQueryKeysCount != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.MaxKvQueryKeysCount)) + i-- + dAtA[i] = 0x20 + } if m.TxQueryRemovalLimit != 0 { i = encodeVarintParams(dAtA, i, uint64(m.TxQueryRemovalLimit)) i-- @@ -200,6 +232,12 @@ func (m *Params) Size() (n int) { if m.TxQueryRemovalLimit != 0 { n += 1 + sovParams(uint64(m.TxQueryRemovalLimit)) } + if m.MaxKvQueryKeysCount != 0 { + n += 1 + sovParams(uint64(m.MaxKvQueryKeysCount)) + } + if m.MaxTransactionsFilters != 0 { + n += 1 + sovParams(uint64(m.MaxTransactionsFilters)) + } return n } @@ -310,6 +348,44 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxKvQueryKeysCount", wireType) + } + m.MaxKvQueryKeysCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxKvQueryKeysCount |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxTransactionsFilters", wireType) + } + m.MaxTransactionsFilters = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxTransactionsFilters |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) diff --git a/x/interchainqueries/types/tx.go b/x/interchainqueries/types/tx.go index f77c3b4f3..7e1fbb766 100644 --- a/x/interchainqueries/types/tx.go +++ b/x/interchainqueries/types/tx.go @@ -11,10 +11,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -const ( - MaxKVQueryKeysCount = 32 -) - var ( _ sdk.Msg = &MsgSubmitQueryResult{} _ codectypes.UnpackInterfacesMessage = MsgSubmitQueryResult{} @@ -86,7 +82,7 @@ func (msg MsgRegisterInterchainQuery) Type() string { return "register-interchain-query" } -func (msg MsgRegisterInterchainQuery) Validate() error { +func (msg MsgRegisterInterchainQuery) Validate(params Params) error { if msg.UpdatePeriod == 0 { return errors.Wrap(ErrInvalidUpdatePeriod, "update period can not be equal to zero") } @@ -111,13 +107,13 @@ func (msg MsgRegisterInterchainQuery) Validate() error { if len(msg.Keys) == 0 { return errors.Wrap(ErrEmptyKeys, "keys cannot be empty") } - if err := validateKeys(msg.GetKeys()); err != nil { + if err := validateKeys(msg.GetKeys(), params.MaxKvQueryKeysCount); err != nil { return err } } if InterchainQueryType(msg.QueryType).IsTX() { - if err := ValidateTransactionsFilter(msg.TransactionsFilter); err != nil { + if err := ValidateTransactionsFilter(msg.TransactionsFilter, params.MaxTransactionsFilters); err != nil { return errors.Wrap(ErrInvalidTransactionsFilter, err.Error()) } } @@ -140,7 +136,7 @@ func (msg MsgRegisterInterchainQuery) GetSigners() []sdk.AccAddress { var _ sdk.Msg = &MsgUpdateInterchainQueryRequest{} -func (msg MsgUpdateInterchainQueryRequest) Validate() error { +func (msg MsgUpdateInterchainQueryRequest) Validate(params Params) error { if msg.GetQueryId() == 0 { return errors.Wrap(ErrInvalidQueryID, "query_id cannot be empty or equal to 0") } @@ -163,13 +159,13 @@ func (msg MsgUpdateInterchainQueryRequest) Validate() error { } if len(newKeys) != 0 { - if err := validateKeys(newKeys); err != nil { + if err := validateKeys(newKeys, params.MaxKvQueryKeysCount); err != nil { return err } } if newTxFilter != "" { - if err := ValidateTransactionsFilter(newTxFilter); err != nil { + if err := ValidateTransactionsFilter(newTxFilter, params.MaxTransactionsFilters); err != nil { return errors.Wrap(ErrInvalidTransactionsFilter, err.Error()) } } @@ -226,9 +222,9 @@ func (msg *MsgUpdateParams) Validate() error { return nil } -func validateKeys(keys []*KVKey) error { - if uint64(len(keys)) > MaxKVQueryKeysCount { - return errors.Wrapf(ErrTooManyKVQueryKeys, "keys count cannot be more than %d", MaxKVQueryKeysCount) +func validateKeys(keys []*KVKey, maxKVQueryKeysCount uint64) error { + if uint64(len(keys)) > maxKVQueryKeysCount { + return errors.Wrapf(ErrTooManyKVQueryKeys, "keys count cannot be more than %d", maxKVQueryKeysCount) } duplicates := make(map[string]struct{}) diff --git a/x/interchainqueries/types/types.go b/x/interchainqueries/types/types.go index dafa661f3..be94cc357 100644 --- a/x/interchainqueries/types/types.go +++ b/x/interchainqueries/types/types.go @@ -43,9 +43,6 @@ const ( // AttributeValueQueryRemoved represents the value for the 'action' event attribute. AttributeValueQueryRemoved = "query_removed" - - // maxTransactionsFilters defines maximum allowed amount of tx filters in msgRegisterInterchainQuery - maxTransactionsFilters = 32 ) const ( @@ -107,13 +104,13 @@ type TransactionsFilterItem struct { } // ValidateTransactionsFilter checks if the passed string is a valid TransactionsFilter value. -func ValidateTransactionsFilter(s string) error { +func ValidateTransactionsFilter(s string, maxTransactionsFilters uint64) error { const forbiddenCharacters = "\t\n\r\\()\"'=><" filters := TransactionsFilter{} if err := json.Unmarshal([]byte(s), &filters); err != nil { return fmt.Errorf("failed to unmarshal transactions filter: %w", err) } - if len(filters) > maxTransactionsFilters { + if uint64(len(filters)) > maxTransactionsFilters { return fmt.Errorf("too many transactions filters, provided=%d, max=%d", len(filters), maxTransactionsFilters) } diff --git a/x/interchainqueries/types/types_test.go b/x/interchainqueries/types/types_test.go index 202c8c531..f062a310e 100644 --- a/x/interchainqueries/types/types_test.go +++ b/x/interchainqueries/types/types_test.go @@ -10,37 +10,37 @@ import ( func TestTransactionFilterValidation(t *testing.T) { t.Run("Valid", func(t *testing.T) { // several conditions - assert.NoError(t, ValidateTransactionsFilter(`[{"field":"transfer.recipient","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"},{"field":"tx.height","op":"Gte","value":100}]`)) + assert.NoError(t, ValidateTransactionsFilter(`[{"field":"transfer.recipient","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"},{"field":"tx.height","op":"Gte","value":100}]`, DefaultMaxTransactionsFilters)) // all supported operations with a whole operand - assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Eq","value":1000}]`)) - assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Gt","value":1000}]`)) - assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Gte","value":1000}]`)) - assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Lt","value":1000}]`)) - assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Lte","value":1000}]`)) + assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Eq","value":1000}]`, DefaultMaxTransactionsFilters)) + assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Gt","value":1000}]`, DefaultMaxTransactionsFilters)) + assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Gte","value":1000}]`, DefaultMaxTransactionsFilters)) + assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Lt","value":1000}]`, DefaultMaxTransactionsFilters)) + assert.NoError(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Lte","value":1000}]`, DefaultMaxTransactionsFilters)) }) t.Run("Invalid", func(t *testing.T) { // invalid json - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.recipient","op":"Eq","value":`), "unexpected end of JSON input") + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.recipient","op":"Eq","value":`, DefaultMaxTransactionsFilters), "unexpected end of JSON input") // empty operation - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.recipient","op":"","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), "op '' is expected to be one of: eq, gt, gte, lt, lte") + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.recipient","op":"","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), "op '' is expected to be one of: eq, gt, gte, lt, lte") // empty field - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), "field couldn't be empty") + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), "field couldn't be empty") // field with forbidden symbols const specialSymbolsAreNotAllowed = "special symbols \t\n\r\\()\"'=>< are not allowed" - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\t","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\n","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\r","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\\","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.(","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.)","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\"","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.'","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.=","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.>","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.<","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\t","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\n","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\r","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\\","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.(","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.)","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.\"","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.'","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.=","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.>","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"transfer.<","op":"Eq","value":"neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u"}]`, DefaultMaxTransactionsFilters), specialSymbolsAreNotAllowed) // decimal number - assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Gte","value":15.5}]`), "can't be a decimal number") - assert.ErrorContains(t, ValidateTransactionsFilter(lotsOfTxFilters(t, 40)), "too many transactions filters") + assert.ErrorContains(t, ValidateTransactionsFilter(`[{"field":"tx.height","op":"Gte","value":15.5}]`, DefaultMaxTransactionsFilters), "can't be a decimal number") + assert.ErrorContains(t, ValidateTransactionsFilter(lotsOfTxFilters(t, 40), DefaultMaxTransactionsFilters), "too many transactions filters") }) }