diff --git a/sei-cosmos/types/query/filtered_pagination.go b/sei-cosmos/types/query/filtered_pagination.go index 950733d552..fccb5fd40e 100644 --- a/sei-cosmos/types/query/filtered_pagination.go +++ b/sei-cosmos/types/query/filtered_pagination.go @@ -5,6 +5,8 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/codec" "github.com/sei-protocol/sei-chain/sei-cosmos/store/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // FilteredPaginate does pagination of all the results in the PrefixStore based on the @@ -36,11 +38,16 @@ func FilteredPaginate( return nil, fmt.Errorf("invalid request, either offset or key is expected, got both") } + if err := VerifyPaginationOffset(offset); err != nil { + return nil, err + } + if limit == 0 { limit = DefaultLimit + } - // count total results when the limit is zero/not supplied - countTotal = true + if err := VerifyPaginationLimit(limit); err != nil { + return nil, err } if len(key) != 0 { @@ -48,11 +55,18 @@ func FilteredPaginate( defer func() { _ = iterator.Close() }() var ( - numHits uint64 - nextKey []byte + numHits uint64 + nextKey []byte + totalIter uint64 ) for ; iterator.Valid(); iterator.Next() { + totalIter++ + if totalIter > MaxScanLimit { + return nil, status.Errorf(codes.InvalidArgument, + "scanned more than %d entries without filling the page; use a more specific key prefix or reduce limit", MaxScanLimit) + } + if numHits == limit { nextKey = iterator.Key() break @@ -83,11 +97,26 @@ func FilteredPaginate( end := offset + limit var ( - numHits uint64 - nextKey []byte + numHits uint64 + nextKey []byte + totalIter uint64 + pageCompleteIter uint64 ) for ; iterator.Valid(); iterator.Next() { + totalIter++ + // Phase 1: page not yet complete — cap raw iterations to prevent full-store + // walks when the filter produces too few hits to fill the page. + if numHits < end && totalIter > offset+MaxScanLimit { + return nil, status.Errorf(codes.InvalidArgument, + "scanned more than %d entries without filling the page; use key-based pagination instead", MaxScanLimit) + } + // Phase 2: page complete — cap how far past the page we scan for nextKey/count_total. + if pageCompleteIter > MaxScanLimit { + return nil, status.Errorf(codes.InvalidArgument, + "scanned more than %d entries past the end of the page; use key-based pagination instead", MaxScanLimit) + } + if iterator.Error() != nil { return nil, iterator.Error() } @@ -102,6 +131,10 @@ func FilteredPaginate( numHits++ } + if numHits >= end { + pageCompleteIter++ + } + if numHits == end+1 { nextKey = iterator.Key() @@ -150,11 +183,16 @@ func GenericFilteredPaginate[T codec.ProtoMarshaler, F codec.ProtoMarshaler]( return results, nil, fmt.Errorf("invalid request, either offset or key is expected, got both") } + if err := VerifyPaginationOffset(offset); err != nil { + return results, nil, err + } + if limit == 0 { limit = DefaultLimit + } - // count total results when the limit is zero/not supplied - countTotal = true + if err := VerifyPaginationLimit(limit); err != nil { + return results, nil, err } if len(key) != 0 { @@ -162,11 +200,18 @@ func GenericFilteredPaginate[T codec.ProtoMarshaler, F codec.ProtoMarshaler]( defer func() { _ = iterator.Close() }() var ( - numHits uint64 - nextKey []byte + numHits uint64 + nextKey []byte + totalIter uint64 ) for ; iterator.Valid(); iterator.Next() { + totalIter++ + if totalIter > MaxScanLimit { + return nil, nil, status.Errorf(codes.InvalidArgument, + "scanned more than %d entries without filling the page; use a more specific key prefix or reduce limit", MaxScanLimit) + } + if numHits == limit { nextKey = iterator.Key() break @@ -205,11 +250,26 @@ func GenericFilteredPaginate[T codec.ProtoMarshaler, F codec.ProtoMarshaler]( end := offset + limit var ( - numHits uint64 - nextKey []byte + numHits uint64 + nextKey []byte + totalIter uint64 + pageCompleteIter uint64 ) for ; iterator.Valid(); iterator.Next() { + totalIter++ + // Phase 1: page not yet complete — cap raw iterations to prevent full-store + // walks when the filter produces too few hits to fill the page. + if numHits < end && totalIter > offset+MaxScanLimit { + return nil, nil, status.Errorf(codes.InvalidArgument, + "scanned more than %d entries without filling the page; use key-based pagination instead", MaxScanLimit) + } + // Phase 2: page complete — cap how far past the page we scan for nextKey/count_total. + if pageCompleteIter > MaxScanLimit { + return nil, nil, status.Errorf(codes.InvalidArgument, + "scanned more than %d entries past the end of the page; use key-based pagination instead", MaxScanLimit) + } + if iterator.Error() != nil { return nil, nil, iterator.Error() } @@ -234,6 +294,10 @@ func GenericFilteredPaginate[T codec.ProtoMarshaler, F codec.ProtoMarshaler]( numHits++ } + if numHits >= end { + pageCompleteIter++ + } + if numHits == end+1 { if nextKey == nil { nextKey = iterator.Key() diff --git a/sei-cosmos/types/query/filtered_pagination_test.go b/sei-cosmos/types/query/filtered_pagination_test.go index b0b6544b0a..4747e70ecf 100644 --- a/sei-cosmos/types/query/filtered_pagination_test.go +++ b/sei-cosmos/types/query/filtered_pagination_test.go @@ -47,7 +47,7 @@ func (s *paginationTestSuite) TestFilteredPaginations() { s.Require().NoError(err) s.Require().NotNil(res) s.Require().Equal(4, len(balances)) - s.Require().Equal(uint64(4), res.Total) + s.Require().Equal(uint64(0), res.Total) s.Require().Nil(res.NextKey) s.T().Log("verify nextKey is returned if there are more results") @@ -79,7 +79,7 @@ func (s *paginationTestSuite) TestFilteredPaginations() { s.Require().NoError(err) s.Require().NotNil(res) s.Require().Equal(4, len(balances)) - s.Require().Equal(uint64(4), res.Total) + s.Require().Equal(uint64(0), res.Total) s.T().Log("verify with offset") pageReq = &query.PageRequest{Offset: 2, Limit: 2} @@ -122,7 +122,7 @@ func (s *paginationTestSuite) TestReverseFilteredPaginations() { s.Require().NoError(err) s.Require().NotNil(res) s.Require().Equal(10, len(balns)) - s.Require().Equal(uint64(10), res.Total) + s.Require().Equal(uint64(0), res.Total) s.Require().Nil(res.NextKey) s.T().Log("verify default limit") @@ -131,7 +131,7 @@ func (s *paginationTestSuite) TestReverseFilteredPaginations() { s.Require().NoError(err) s.Require().NotNil(res) s.Require().Equal(10, len(balns)) - s.Require().Equal(uint64(10), res.Total) + s.Require().Equal(uint64(0), res.Total) s.T().Log("verify nextKey is returned if there are more results") pageReq = &query.PageRequest{Limit: 2, CountTotal: true, Reverse: true} @@ -170,6 +170,68 @@ func (s *paginationTestSuite) TestReverseFilteredPaginations() { } +func (s *paginationTestSuite) TestFilteredPaginateMaxLimitExceeded() { + app, ctx, _ := setupTest(s.T()) + store := ctx.KVStore(app.GetKey(types.StoreKey)) + + _, err := query.FilteredPaginate(store, &query.PageRequest{Limit: query.MaxLimit + 1}, func(_ []byte, _ []byte, _ bool) (bool, error) { + return false, nil + }) + s.Require().Error(err) + s.Require().Contains(err.Error(), "exceeds maximum allowed limit") +} + +func (s *paginationTestSuite) TestFilteredPaginateOffsetExceedsMax() { + app, ctx, _ := setupTest(s.T()) + kvStore := ctx.KVStore(app.GetKey(types.StoreKey)) + + _, err := query.FilteredPaginate(kvStore, &query.PageRequest{Offset: query.MaxOffset + 1}, func(_ []byte, _ []byte, _ bool) (bool, error) { + return false, nil + }) + s.Require().Error(err) + s.Require().Contains(err.Error(), "exceeds maximum allowed offset") + + _, err = query.FilteredPaginate(kvStore, &query.PageRequest{Offset: query.MaxOffset}, func(_ []byte, _ []byte, _ bool) (bool, error) { + return false, nil + }) + s.Require().NoError(err) +} + +func (s *paginationTestSuite) TestFilteredPaginateCountTotalScanLimitExceeded() { + app, ctx, _ := setupTest(s.T()) + kvStore := prefix.NewStore(ctx.KVStore(app.GetKey(types.StoreKey)), []byte("filteredscanlimit/")) + + numItems := int(query.MaxScanLimit) + 2 + for i := 0; i < numItems; i++ { + kvStore.Set([]byte(fmt.Sprintf("%08d", i)), []byte("v")) + } + + _, err := query.FilteredPaginate(kvStore, &query.PageRequest{Limit: 1, CountTotal: true}, func(_ []byte, _ []byte, _ bool) (bool, error) { + return true, nil + }) + s.Require().Error(err) + s.Require().Contains(err.Error(), "scanned more than") +} + +func (s *paginationTestSuite) TestFilteredPaginateCountTotalScanLimitExceededNoHits() { + app, ctx, _ := setupTest(s.T()) + kvStore := prefix.NewStore(ctx.KVStore(app.GetKey(types.StoreKey)), []byte("filteredscanlimitnohits/")) + + // Phase 1 fires when totalIter > offset + MaxScanLimit = 10001 + pageReq := &query.PageRequest{Offset: 1, CountTotal: true} + numItems := int(query.MaxScanLimit) + 2 + for i := 0; i < numItems; i++ { + kvStore.Set([]byte(fmt.Sprintf("%08d", i)), []byte("v")) + } + + // filter returns no hits — numHits never reaches end, Phase 1 guard must fire + _, err := query.FilteredPaginate(kvStore, pageReq, func(_ []byte, _ []byte, _ bool) (bool, error) { + return false, nil + }) + s.Require().Error(err) + s.Require().Contains(err.Error(), "scanned more than") +} + func execFilterPaginate(store sdk.KVStore, pageReq *query.PageRequest, appCodec codec.Codec) (balances sdk.Coins, res *query.PageResponse, err error) { balancesStore := prefix.NewStore(store, types.BalancesPrefix) accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1)) diff --git a/sei-cosmos/types/query/pagination.go b/sei-cosmos/types/query/pagination.go index 3492fb05a8..0290a837b4 100644 --- a/sei-cosmos/types/query/pagination.go +++ b/sei-cosmos/types/query/pagination.go @@ -2,7 +2,6 @@ package query import ( "fmt" - "math" "github.com/sei-protocol/sei-chain/sei-cosmos/store/types" db "github.com/tendermint/tm-db" @@ -14,9 +13,15 @@ import ( // if the `limit` is not supplied, paginate will use `DefaultLimit` const DefaultLimit = 100 -// MaxLimit is the maximum limit the paginate function can handle -// which equals the maximum value that can be stored in uint64 -const MaxLimit = math.MaxUint64 +// MaxLimit is the maximum limit per page the paginate function can handle +const MaxLimit = uint64(1_000) + +// MaxScanLimit is the maximum number of store entries the paginate function +// will iterate past the page end when count_total is requested. +const MaxScanLimit = uint64(10_000) + +// MaxOffset is the maximum offset allowed in a PageRequest. +const MaxOffset = uint64(10_000) // ParsePagination validate PageRequest and returns page number & limit. func ParsePagination(pageReq *PageRequest) (page, limit int, err error) { @@ -30,6 +35,10 @@ func ParsePagination(pageReq *PageRequest) (page, limit int, err error) { if offset < 0 { return 1, 0, status.Error(codes.InvalidArgument, "offset must greater than 0") } + // #nosec G115 -- offset is non-negative after validation above; fits in uint64 + if offsetErr := VerifyPaginationOffset(uint64(offset)); offsetErr != nil { + return 1, 0, offsetErr + } if limit < 0 { return 1, 0, status.Error(codes.InvalidArgument, "limit must greater than 0") @@ -37,40 +46,55 @@ func ParsePagination(pageReq *PageRequest) (page, limit int, err error) { limit = DefaultLimit } + // #nosec G115 -- limit is positive after validation above; fits in uint64 + if limitErr := VerifyPaginationLimit(uint64(limit)); limitErr != nil { + return 1, 0, limitErr + } + page = offset/limit + 1 return page, limit, nil } -// Paginate does pagination of all the results in the PrefixStore based on the -// provided PageRequest. onResult should be used to do actual unmarshaling. +func VerifyPaginationLimit(limit uint64) error { + if limit > MaxLimit { + return status.Errorf(codes.InvalidArgument, "limit %d exceeds maximum allowed limit %d", limit, MaxLimit) + } + return nil +} + +func VerifyPaginationOffset(offset uint64) error { + if offset > MaxOffset { + return status.Errorf(codes.InvalidArgument, "offset %d exceeds maximum allowed offset %d", offset, MaxOffset) + } + return nil +} + func Paginate( prefixStore types.KVStore, pageRequest *PageRequest, onResult func(key []byte, value []byte) error, ) (*PageResponse, error) { - - // if the PageRequest is nil, use default PageRequest if pageRequest == nil { pageRequest = &PageRequest{} } - offset := pageRequest.Offset key := pageRequest.Key limit := pageRequest.Limit - countTotal := pageRequest.CountTotal - reverse := pageRequest.Reverse - + if limit == 0 { + limit = DefaultLimit + } if offset > 0 && key != nil { return nil, fmt.Errorf("invalid request, either offset or key is expected, got both") } - - if limit == 0 { - limit = DefaultLimit - - // count total results when the limit is zero/not supplied - countTotal = true + if err := VerifyPaginationLimit(limit); err != nil { + return nil, err } + if err := VerifyPaginationOffset(offset); err != nil { + return nil, err + } + countTotal := pageRequest.CountTotal + reverse := pageRequest.Reverse if len(key) != 0 { iterator := getIterator(prefixStore, key, reverse) @@ -80,7 +104,6 @@ func Paginate( var nextKey []byte for ; iterator.Valid(); iterator.Next() { - if count == limit { nextKey = iterator.Key() break @@ -92,7 +115,6 @@ func Paginate( if err != nil { return nil, err } - count++ } @@ -112,6 +134,11 @@ func Paginate( for ; iterator.Valid(); iterator.Next() { count++ + if count > offset+MaxScanLimit { + return nil, status.Errorf(codes.InvalidArgument, + "scanned more than %d entries past the end of the page; use key-based pagination instead", MaxScanLimit) + } + if count <= offset { continue } diff --git a/sei-cosmos/types/query/pagination_test.go b/sei-cosmos/types/query/pagination_test.go index f05055670b..e9c2c888bc 100644 --- a/sei-cosmos/types/query/pagination_test.go +++ b/sei-cosmos/types/query/pagination_test.go @@ -15,6 +15,7 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/codec" "github.com/sei-protocol/sei-chain/sei-cosmos/crypto/keys/secp256k1" "github.com/sei-protocol/sei-chain/sei-cosmos/store" + "github.com/sei-protocol/sei-chain/sei-cosmos/store/prefix" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" "github.com/sei-protocol/sei-chain/sei-cosmos/types/query" "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/types" @@ -56,6 +57,37 @@ func (s *paginationTestSuite) TestParsePagination() { s.Require().NoError(err) s.Require().Equal(page, 1) s.Require().Equal(limit, 10) + + s.T().Log("verify limit equal to MaxLimit is accepted") + pageReq = &query.PageRequest{Limit: query.MaxLimit} + _, _, err = query.ParsePagination(pageReq) + s.Require().NoError(err) + + s.T().Log("verify limit exceeding MaxLimit is rejected") + pageReq = &query.PageRequest{Limit: query.MaxLimit + 1} + _, _, err = query.ParsePagination(pageReq) + s.Require().Error(err) + s.Require().Contains(err.Error(), "exceeds maximum allowed limit") + + s.T().Log("verify offset equal to MaxOffset is accepted") + pageReq = &query.PageRequest{Offset: query.MaxOffset, Limit: 1} + _, _, err = query.ParsePagination(pageReq) + s.Require().NoError(err) + + s.T().Log("verify offset exceeding MaxOffset is rejected") + pageReq = &query.PageRequest{Offset: query.MaxOffset + 1, Limit: 1} + _, _, err = query.ParsePagination(pageReq) + s.Require().Error(err) + s.Require().Contains(err.Error(), "exceeds maximum allowed offset") +} + +func (s *paginationTestSuite) TestPaginateMaxLimitExceeded() { + app, ctx, _ := setupTest(s.T()) + store := ctx.KVStore(app.GetKey(types.StoreKey)) + + _, err := query.Paginate(store, &query.PageRequest{Limit: query.MaxLimit + 1}, func(_, _ []byte) error { return nil }) + s.Require().Error(err) + s.Require().Contains(err.Error(), "exceeds maximum allowed limit") } func (s *paginationTestSuite) TestPagination() { @@ -77,12 +109,12 @@ func (s *paginationTestSuite) TestPagination() { app.AccountKeeper.SetAccount(ctx, acc1) s.Require().NoError(apptesting.FundAccount(app.BankKeeper, ctx, addr1, balances)) - s.T().Log("verify empty page request results a max of defaultLimit records and counts total records") + s.T().Log("verify empty page request results a max of defaultLimit records without total count") pageReq := &query.PageRequest{} request := types.NewQueryAllBalancesRequest(addr1, pageReq) res, err := queryClient.AllBalances(gocontext.Background(), request) s.Require().NoError(err) - s.Require().Equal(res.Pagination.Total, uint64(numBalances)) + s.Require().Equal(res.Pagination.Total, uint64(0)) s.Require().NotNil(res.Pagination.NextKey) s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) @@ -291,6 +323,35 @@ func (s *paginationTestSuite) TestReversePagination() { s.Require().Nil(res.Pagination.NextKey) } +func (s *paginationTestSuite) TestPaginateOffsetExceedsMax() { + app, ctx, _ := setupTest(s.T()) + kvStore := ctx.KVStore(app.GetKey(types.StoreKey)) + + _, err := query.Paginate(kvStore, &query.PageRequest{Offset: query.MaxOffset + 1}, func(_, _ []byte) error { return nil }) + s.Require().Error(err) + s.Require().Contains(err.Error(), "exceeds maximum allowed offset") + + _, err = query.Paginate(kvStore, &query.PageRequest{Offset: query.MaxOffset}, func(_, _ []byte) error { return nil }) + s.Require().NoError(err) +} + +func (s *paginationTestSuite) TestPaginateCountTotalScanLimitExceeded() { + app, ctx, _ := setupTest(s.T()) + // Use a dedicated prefix to isolate test data from other store entries. + kvStore := prefix.NewStore(ctx.KVStore(app.GetKey(types.StoreKey)), []byte("scanlimit/")) + + // With offset=1, scan cap fires when count > offset+MaxScanLimit = 10,001. + // Insert 10,002 items to guarantee the cap is exceeded. + numItems := int(query.MaxScanLimit) + 2 + for i := 0; i < numItems; i++ { + kvStore.Set([]byte(fmt.Sprintf("%08d", i)), []byte("v")) + } + + _, err := query.Paginate(kvStore, &query.PageRequest{Limit: 1, CountTotal: true}, func(_, _ []byte) error { return nil }) + s.Require().Error(err) + s.Require().Contains(err.Error(), fmt.Sprintf("scanned more than %d entries", query.MaxScanLimit)) +} + func setupTest(t *testing.T) (*app.App, sdk.Context, codec.Codec) { a := app.Setup(t, false, false, false) ctx := a.BaseApp.NewContext(false, tmproto.Header{Height: 1}) diff --git a/sei-cosmos/x/auth/tx/service.go b/sei-cosmos/x/auth/tx/service.go index e138089018..8b604380b1 100644 --- a/sei-cosmos/x/auth/tx/service.go +++ b/sei-cosmos/x/auth/tx/service.go @@ -192,6 +192,9 @@ func (s txServer) GetBlockWithTxs(ctx context.Context, req *txtypes.GetBlockWith if req.Pagination != nil { offset = req.Pagination.Offset limit = req.Pagination.Limit + if err = pagination.VerifyPaginationLimit(limit); err != nil { + return nil, sdkerrors.ErrInvalidRequest.Wrapf("invalid pagination limit: %d. Max allowed limit is %d", limit, pagination.MaxLimit) + } } else { offset = 0 limit = pagination.DefaultLimit diff --git a/sei-cosmos/x/bank/keeper/genesis.go b/sei-cosmos/x/bank/keeper/genesis.go index afdf134122..4f1e643a5b 100644 --- a/sei-cosmos/x/bank/keeper/genesis.go +++ b/sei-cosmos/x/bank/keeper/genesis.go @@ -4,7 +4,6 @@ import ( "fmt" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-cosmos/types/query" "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/types" ) @@ -60,9 +59,9 @@ func (k BaseKeeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { // ExportGenesis returns the bank module's genesis state. func (k BaseKeeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { - totalSupply, _, err := k.GetPaginatedTotalSupply(ctx, &query.PageRequest{Limit: query.MaxLimit}) + totalSupply, err := collectAllTotalSupply(ctx, k) if err != nil { - panic(fmt.Errorf("unable to fetch total supply %v", err)) + panic(fmt.Errorf("unable to fetch total supply: %w", err)) } weiBalances := []types.WeiBalance{} k.IterateAllWeiBalances(ctx, func(aa sdk.AccAddress, i sdk.Int) bool { diff --git a/sei-cosmos/x/bank/keeper/invariants.go b/sei-cosmos/x/bank/keeper/invariants.go index fde86feeb0..77084a9fb4 100644 --- a/sei-cosmos/x/bank/keeper/invariants.go +++ b/sei-cosmos/x/bank/keeper/invariants.go @@ -4,7 +4,6 @@ import ( "fmt" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - "github.com/sei-protocol/sei-chain/sei-cosmos/types/query" "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/types" ) @@ -13,8 +12,7 @@ func TotalSupply(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { expectedTotal := sdk.Coins{} weiTotal := sdk.NewInt(0) - supply, _, err := k.GetPaginatedTotalSupply(ctx, &query.PageRequest{Limit: query.MaxLimit}) - + supply, err := collectAllTotalSupply(ctx, k) if err != nil { return sdk.FormatInvariant(types.ModuleName, "query supply", fmt.Sprintf("error querying total supply %v", err)), false diff --git a/sei-cosmos/x/bank/keeper/keeper.go b/sei-cosmos/x/bank/keeper/keeper.go index 33cad0af6d..e80f844f93 100644 --- a/sei-cosmos/x/bank/keeper/keeper.go +++ b/sei-cosmos/x/bank/keeper/keeper.go @@ -106,6 +106,28 @@ func (k BaseKeeper) GetPaginatedTotalSupply(ctx sdk.Context, pagination *query.P return supply, pageRes, nil } +// collectAllTotalSupply returns the full supply by paging with MaxLimit. +func collectAllTotalSupply(ctx sdk.Context, k Keeper) (sdk.Coins, error) { + totalSupply := sdk.NewCoins() + pageReq := &query.PageRequest{Limit: query.MaxLimit} + + for { + page, pageRes, err := k.GetPaginatedTotalSupply(ctx, pageReq) + if err != nil { + return nil, err + } + totalSupply = totalSupply.Add(page...) + + if pageRes == nil || len(pageRes.NextKey) == 0 { + return totalSupply, nil + } + pageReq = &query.PageRequest{ + Key: pageRes.NextKey, + Limit: query.MaxLimit, + } + } +} + // NewBaseKeeper returns a new BaseKeeper object with a given codec, dedicated // store key, an AccountKeeper implementation, and a parameter Subspace used to // store and fetch module parameters. The BaseKeeper also accepts a diff --git a/sei-cosmos/x/bank/keeper/keeper_test.go b/sei-cosmos/x/bank/keeper/keeper_test.go index 87d8d7078f..2f9011d925 100644 --- a/sei-cosmos/x/bank/keeper/keeper_test.go +++ b/sei-cosmos/x/bank/keeper/keeper_test.go @@ -157,6 +157,12 @@ func (suite *IntegrationTestSuite) TestSendCoinsAndWei() { require.Equal(sdk.NewInt(53), keeper.GetBalance(ctx, addr3, sdk.DefaultBondDenom).Amount) } +func (suite *IntegrationTestSuite) TestGetPaginatedTotalSupplyMaxLimitExceeded() { + _, _, err := suite.app.BankKeeper.GetPaginatedTotalSupply(suite.ctx, &query.PageRequest{Limit: query.MaxLimit + 1}) + suite.Require().Error(err) + suite.Require().Contains(err.Error(), "exceeds maximum allowed limit") +} + func (suite *IntegrationTestSuite) TestSupply() { ctx := suite.ctx diff --git a/sei-cosmos/x/slashing/client/rest/grpc_query_test.go b/sei-cosmos/x/slashing/client/rest/grpc_query_test.go index bef8d69dff..2bca225b0a 100644 --- a/sei-cosmos/x/slashing/client/rest/grpc_query_test.go +++ b/sei-cosmos/x/slashing/client/rest/grpc_query_test.go @@ -57,7 +57,7 @@ func (s *IntegrationTestSuite) TestGRPCQueries() { }{ { "get signing infos (height specific)", - fmt.Sprintf("%s/cosmos/slashing/v1beta1/signing_infos", baseURL), + fmt.Sprintf("%s/cosmos/slashing/v1beta1/signing_infos?pagination.count_total=true", baseURL), map[string]string{ grpctypes.GRPCBlockHeightHeader: "1", }, diff --git a/sei-cosmos/x/staking/keeper/grpc_query_test.go b/sei-cosmos/x/staking/keeper/grpc_query_test.go index 4bbedef486..ed18b246f0 100644 --- a/sei-cosmos/x/staking/keeper/grpc_query_test.go +++ b/sei-cosmos/x/staking/keeper/grpc_query_test.go @@ -28,7 +28,7 @@ func (suite *KeeperTestSuite) TestGRPCQueryValidators() { { "empty request", func() { - req = &types.QueryValidatorsRequest{} + req = &types.QueryValidatorsRequest{Pagination: &query.PageRequest{CountTotal: true}} }, true, @@ -38,7 +38,7 @@ func (suite *KeeperTestSuite) TestGRPCQueryValidators() { { "empty status returns all the validators", func() { - req = &types.QueryValidatorsRequest{Status: ""} + req = &types.QueryValidatorsRequest{Status: "", Pagination: &query.PageRequest{CountTotal: true}} }, true, len(vals),