Skip to content

Commit db9176c

Browse files
authored
fix!: update consumer chain queries (#2246)
* fix!: update consumer chain queries * apply review comments * fix err handling and gosec reports
1 parent 3df7b71 commit db9176c

File tree

7 files changed

+399
-248
lines changed

7 files changed

+399
-248
lines changed

Diff for: proto/interchain_security/ccv/provider/v1/query.proto

+15-11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import "interchain_security/ccv/v1/wire.proto";
1212
import "tendermint/crypto/keys.proto";
1313
import "cosmos_proto/cosmos.proto";
1414
import "cosmos/staking/v1beta1/staking.proto";
15+
import "cosmos/base/query/v1beta1/pagination.proto";
1516

1617
service Query {
1718
// ConsumerGenesis queries the genesis state needed to start a consumer chain
@@ -28,7 +29,7 @@ service Query {
2829
rpc QueryConsumerChains(QueryConsumerChainsRequest)
2930
returns (QueryConsumerChainsResponse) {
3031
option (google.api.http).get =
31-
"/interchain_security/ccv/provider/consumer_chains/{phase}/{limit}";
32+
"/interchain_security/ccv/provider/consumer_chains/{phase}";
3233
}
3334

3435
// QueryValidatorConsumerAddr queries the address
@@ -158,12 +159,14 @@ message QueryConsumerChainsRequest {
158159
// The phase of the consumer chains returned (optional)
159160
// Registered=1|Initialized=2|Launched=3|Stopped=4|Deleted=5
160161
ConsumerPhase phase = 1;
161-
// The limit of consumer chains returned (optional)
162-
// default is 100
163-
int32 limit = 2;
162+
163+
cosmos.base.query.v1beta1.PageRequest pagination = 2;
164164
}
165165

166-
message QueryConsumerChainsResponse { repeated Chain chains = 1; }
166+
message QueryConsumerChainsResponse {
167+
repeated Chain chains = 1;
168+
cosmos.base.query.v1beta1.PageResponse pagination = 2;
169+
}
167170

168171
message Chain {
169172
string chain_id = 1;
@@ -372,10 +375,11 @@ message QueryConsumerChainRequest {
372375
}
373376

374377
message QueryConsumerChainResponse {
375-
string chain_id = 1;
376-
string owner_address = 2;
377-
string phase = 3;
378-
ConsumerMetadata metadata = 4 [ (gogoproto.nullable) = false ];
379-
ConsumerInitializationParameters init_params = 5;
380-
PowerShapingParameters power_shaping_params = 6;
378+
string consumer_id = 1;
379+
string chain_id = 2;
380+
string owner_address = 3;
381+
string phase = 4;
382+
ConsumerMetadata metadata = 5 [ (gogoproto.nullable) = false ];
383+
ConsumerInitializationParameters init_params = 6;
384+
PowerShapingParameters power_shaping_params = 7;
381385
}

Diff for: x/ccv/provider/client/cli/query.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func CmdConsumerChains() *cobra.Command {
102102
if err != nil {
103103
return err
104104
}
105-
req.Limit = int32(limit)
105+
req.Pagination.Limit = uint64(limit)
106106
}
107107

108108
res, err := queryClient.QueryConsumerChains(cmd.Context(), req)

Diff for: x/ccv/provider/keeper/grpc_query.go

+25-23
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ package keeper
33
import (
44
"bytes"
55
"context"
6+
"encoding/binary"
67
"fmt"
78
"sort"
89

910
"google.golang.org/grpc/codes"
1011
"google.golang.org/grpc/status"
1112

1213
errorsmod "cosmossdk.io/errors"
14+
"cosmossdk.io/store/prefix"
1315

1416
sdk "github.com/cosmos/cosmos-sdk/types"
17+
"github.com/cosmos/cosmos-sdk/types/query"
1518

1619
"github.com/cosmos/interchain-security/v6/x/ccv/provider/types"
1720
ccvtypes "github.com/cosmos/interchain-security/v6/x/ccv/types"
@@ -48,37 +51,35 @@ func (k Keeper) QueryConsumerChains(goCtx context.Context, req *types.QueryConsu
4851
}
4952

5053
ctx := sdk.UnwrapSDKContext(goCtx)
54+
var chains []*types.Chain
5155

52-
consumerIds := []string{}
53-
for _, consumerID := range k.GetAllConsumerIds(ctx) {
54-
phase := k.GetConsumerPhase(ctx, consumerID)
55-
if req.Phase != types.CONSUMER_PHASE_UNSPECIFIED && req.Phase != phase {
56-
// ignore consumer chain
57-
continue
56+
store := ctx.KVStore(k.storeKey)
57+
storePrefix := types.ConsumerIdToPhaseKeyPrefix()
58+
consumerPhaseStore := prefix.NewStore(store, []byte{storePrefix})
59+
pageRes, err := query.Paginate(consumerPhaseStore, req.Pagination, func(key, value []byte) error {
60+
consumerId, err := types.ParseStringIdWithLenKey(storePrefix, append([]byte{storePrefix}, key...))
61+
if err != nil {
62+
return status.Error(codes.Internal, err.Error())
5863
}
59-
consumerIds = append(consumerIds, consumerID)
60-
}
6164

62-
// set limit to default value
63-
limit := 100
64-
if req.Limit != 0 {
65-
// update limit if specified
66-
limit = int(req.Limit)
67-
}
68-
if len(consumerIds) > limit {
69-
consumerIds = consumerIds[:limit]
70-
}
65+
phase := types.ConsumerPhase(binary.BigEndian.Uint32(value))
66+
if req.Phase != types.CONSUMER_PHASE_UNSPECIFIED && req.Phase != phase {
67+
return nil
68+
}
7169

72-
chains := make([]*types.Chain, len(consumerIds))
73-
for i, cID := range consumerIds {
74-
c, err := k.GetConsumerChain(ctx, cID)
70+
c, err := k.GetConsumerChain(ctx, consumerId)
7571
if err != nil {
76-
return nil, status.Error(codes.Internal, err.Error())
72+
return status.Error(codes.Internal, err.Error())
7773
}
78-
chains[i] = &c
74+
chains = append(chains, &c)
75+
return nil
76+
})
77+
78+
if err != nil {
79+
return nil, status.Error(codes.Internal, err.Error())
7980
}
8081

81-
return &types.QueryConsumerChainsResponse{Chains: chains}, nil
82+
return &types.QueryConsumerChainsResponse{Chains: chains, Pagination: pageRes}, nil
8283
}
8384

8485
// GetConsumerChain returns a Chain data structure with all the necessary fields
@@ -594,6 +595,7 @@ func (k Keeper) QueryConsumerChain(goCtx context.Context, req *types.QueryConsum
594595

595596
return &types.QueryConsumerChainResponse{
596597
ChainId: chainId,
598+
ConsumerId: consumerId,
597599
OwnerAddress: ownerAddress,
598600
Phase: phase.String(),
599601
Metadata: metadata,

Diff for: x/ccv/provider/keeper/grpc_query_test.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
"github.com/cometbft/cometbft/proto/tendermint/crypto"
1919

20+
sdkquery "github.com/cosmos/cosmos-sdk/types/query"
2021
cryptotestutil "github.com/cosmos/interchain-security/v6/testutil/crypto"
2122
testkeeper "github.com/cosmos/interchain-security/v6/testutil/keeper"
2223
"github.com/cosmos/interchain-security/v6/x/ccv/provider/keeper"
@@ -558,6 +559,7 @@ func TestQueryConsumerChain(t *testing.T) {
558559

559560
expRes := types.QueryConsumerChainResponse{
560561
ChainId: chainId,
562+
ConsumerId: consumerId,
561563
OwnerAddress: providerKeeper.GetAuthority(),
562564
Metadata: types.ConsumerMetadata{Name: chainId},
563565
Phase: types.CONSUMER_PHASE_REGISTERED.String(),
@@ -663,7 +665,7 @@ func TestQueryConsumerChains(t *testing.T) {
663665
name string
664666
setup func(ctx sdk.Context, pk keeper.Keeper)
665667
phase_filter types.ConsumerPhase
666-
limit int32
668+
limit uint64
667669
expConsumers []*types.Chain
668670
}{
669671
{
@@ -675,7 +677,7 @@ func TestQueryConsumerChains(t *testing.T) {
675677
name: "expect an amount of consumer equal to the limit",
676678
setup: func(ctx sdk.Context, pk keeper.Keeper) {},
677679
expConsumers: consumers[:3],
678-
limit: int32(3),
680+
limit: 3,
679681
},
680682
{
681683
name: "expect registered consumers when phase filter is set to Registered",
@@ -720,14 +722,19 @@ func TestQueryConsumerChains(t *testing.T) {
720722
tc.setup(ctx, pk)
721723
req := types.QueryConsumerChainsRequest{
722724
Phase: tc.phase_filter,
723-
Limit: tc.limit,
725+
Pagination: &sdkquery.PageRequest{
726+
Limit: tc.limit,
727+
},
724728
}
725729
expectedResponse := types.QueryConsumerChainsResponse{
726730
Chains: tc.expConsumers,
727731
}
728732
res, err := pk.QueryConsumerChains(ctx, &req)
729733
require.NoError(t, err)
730-
require.Equal(t, &expectedResponse, res, tc.name)
734+
require.Equal(t, expectedResponse.GetChains(), res.GetChains(), tc.name)
735+
if tc.limit != 0 {
736+
require.Len(t, res.GetChains(), int(tc.limit), tc.name)
737+
}
731738
})
732739
}
733740
}

Diff for: x/ccv/provider/types/keys.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -674,9 +674,14 @@ func ConsumerIdToPowerShapingParametersKey(consumerId string) []byte {
674674
return StringIdWithLenKey(mustGetKeyPrefix(ConsumerIdToPowerShapingParameters), consumerId)
675675
}
676676

677+
// ConsumerIdToPhaseKeyPrefix returns the key prefix used to iterate over all the consumer ids and their phases.
678+
func ConsumerIdToPhaseKeyPrefix() byte {
679+
return mustGetKeyPrefix(ConsumerIdToPhaseKeyName)
680+
}
681+
677682
// ConsumerIdToPhaseKey returns the key used to store the phase that corresponds to this consumer id
678683
func ConsumerIdToPhaseKey(consumerId string) []byte {
679-
return StringIdWithLenKey(mustGetKeyPrefix(ConsumerIdToPhaseKeyName), consumerId)
684+
return StringIdWithLenKey(ConsumerIdToPhaseKeyPrefix(), consumerId)
680685
}
681686

682687
// ConsumerIdToRemovalTimeKeyPrefix returns the key prefix for storing the removal times of consumer chains
@@ -801,8 +806,8 @@ func StringIdWithLenKey(prefix byte, stringId string) []byte {
801806
func ParseStringIdWithLenKey(prefix byte, bz []byte) (string, error) {
802807
expectedPrefix := []byte{prefix}
803808
prefixL := len(expectedPrefix)
804-
if prefix := bz[:prefixL]; !bytes.Equal(prefix, expectedPrefix) {
805-
return "", fmt.Errorf("invalid prefix; expected: %X, got: %X", expectedPrefix, prefix)
809+
if prefixBz := bz[:prefixL]; !bytes.Equal(prefixBz, expectedPrefix) {
810+
return "", fmt.Errorf("invalid prefix; expected: %X, got: %X, input %X -- len %d", expectedPrefix, prefixBz, prefix, prefixL)
806811
}
807812
stringIdL := sdk.BigEndianToUint64(bz[prefixL : prefixL+8])
808813
stringId := string(bz[prefixL+8 : prefixL+8+int(stringIdL)])

0 commit comments

Comments
 (0)