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: Add queries for PSS and consumer commission rate #1733

Merged
merged 42 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b0d4cf9
init commit
insumity Mar 13, 2024
0162242
nit change
insumity Mar 13, 2024
7ad2993
cleaning up
insumity Mar 19, 2024
5cd5ba5
clean up
insumity Mar 19, 2024
5393f19
Merge branch 'feat/partial-set-security' into insumity/add-opt-in-set
insumity Mar 19, 2024
03af237
fix distribution test
insumity Mar 19, 2024
07429fa
Update x/ccv/provider/keeper/hooks.go
insumity Mar 20, 2024
0ab0b32
took into Simon's comments
insumity Mar 20, 2024
1a0fa9e
took into rest of the comments
insumity Mar 20, 2024
56ca38d
nit change
insumity Mar 20, 2024
c614fd2
return an error if validator cannot opt out from a Top N chain
insumity Mar 20, 2024
6503f9b
removed automatic opt-in for validators that vote Yes on proposals
insumity Mar 20, 2024
330c085
tiny fix for E2E tests
insumity Mar 20, 2024
629cfd1
nit change to remove unecessary else
insumity Mar 21, 2024
afd4652
update consumer chains query to return topN
sainoe Mar 25, 2024
2e95f89
update query consu chains proto
sainoe Mar 25, 2024
a9db491
Merge remote-tracking branch 'remotes/upstream/insumity/add-opt-in-se…
sainoe Mar 25, 2024
1e3a8e7
add consumer chains per validator query
sainoe Mar 26, 2024
149ba7a
Add PSS command to provider's cli
sainoe Mar 26, 2024
24be8b2
nits
sainoe Mar 26, 2024
b311f35
add consumer commission rate query
sainoe Mar 26, 2024
22ca561
Merge branch 'feat/partial-set-security' into sainoe/pss-query
sainoe Mar 26, 2024
5509108
nits
sainoe Mar 26, 2024
ad91b0e
big renaming
sainoe Mar 27, 2024
559b28f
fix doc
sainoe Mar 27, 2024
00e43a9
nits
sainoe Mar 27, 2024
4f9084f
nits
sainoe Mar 27, 2024
d4fc4cb
docs
sainoe Mar 27, 2024
938d930
Update proto/interchain_security/ccv/provider/v1/query.proto
sainoe Mar 27, 2024
9739ddd
nit
sainoe Mar 27, 2024
c01ab08
add OptedIn in QueryConsumerChainsValidatorHasToValidate
sainoe Mar 27, 2024
4661a99
remove OptIn field in consumer chains query response
sainoe Mar 27, 2024
219d310
Merge remote-tracking branch 'remotes/upstream/sainoe/pss-query' into…
sainoe Mar 27, 2024
10dbb53
include validators that opt-in during the next epochs
sainoe Mar 27, 2024
d642e8a
update has-to-validate condition
sainoe Mar 28, 2024
69d0b28
Merge branch 'feat/partial-set-security' into sainoe/pss-query
sainoe Mar 28, 2024
f9e63f0
fix tinny bug in the tests after merging feat/partial-security
sainoe Mar 28, 2024
3ae3031
update doc
sainoe Mar 28, 2024
ed402f8
update cli description
sainoe Mar 28, 2024
c75594f
Update x/ccv/provider/keeper/grpc_query.go
sainoe Mar 28, 2024
899f3ea
Merge branch 'sainoe/pss-query' of https://github.com/cosmos/intercha…
sainoe Mar 28, 2024
b1c1beb
changes
sainoe Mar 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ require (
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/protobuf v1.33.0
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.33.0
gopkg.in/yaml.v2 v2.4.0
)

Expand Down
66 changes: 66 additions & 0 deletions proto/interchain_security/ccv/provider/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "interchain_security/ccv/provider/v1/provider.proto";
import "interchain_security/ccv/v1/shared_consumer.proto";
import "interchain_security/ccv/v1/wire.proto";
import "tendermint/crypto/keys.proto";
import "cosmos_proto/cosmos.proto";

service Query {
// ConsumerGenesis queries the genesis state needed to start a consumer chain
Expand Down Expand Up @@ -99,6 +100,33 @@ service Query {
option (google.api.http).get =
"/interchain_security/ccv/provider/params";
}

// QueryConsumerChainOptedInValidators returns a list of validators consensus addresses
// that opted-in to the given consumer chain
rpc QueryConsumerChainOptedInValidators(
QueryConsumerChainOptedInValidatorsRequest)
returns (QueryConsumerChainOptedInValidatorsResponse) {
option (google.api.http).get =
"/interchain_security/ccv/provider/opted_in_validators";
}

// QueryConsumerChainsValidatorHasToValidate returns a list of consumer chains
// that a given validator must validate
rpc QueryConsumerChainsValidatorHasToValidate(
QueryConsumerChainsValidatorHasToValidateRequest)
returns (QueryConsumerChainsValidatorHasToValidateResponse) {
option (google.api.http).get =
"/interchain_security/ccv/provider/consumer_chains_per_validator";
}

// QueryValidatorConsumerCommissionRate returns the commission rate a given
// validator charges on a given consumer chain
rpc QueryValidatorConsumerCommissionRate(
QueryValidatorConsumerCommissionRateRequest)
returns (QueryValidatorConsumerCommissionRateResponse) {
option (google.api.http).get =
"/interchain_security/ccv/provider/consumer_commission_rate";
}
}

message QueryConsumerGenesisRequest { string chain_id = 1; }
Expand Down Expand Up @@ -127,6 +155,10 @@ message QueryConsumerChainStopProposalsResponse {
message Chain {
string chain_id = 1;
string client_id = 2;
// If the `chainID` is an Opt-In chain, i.e., no validator is forced to validate chain `chainID`
bool opt_In = 3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? (it's more clear for users is a perfectly fine reason if you think it applies)

Can't we keep the TopN == 0 means it's an opt-in chain, and just have the TopN field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need it in the logic. The intention was to make it clearer for the user. But after some thought, it can actually be removed.

// If chain with `chainID` is a Top-N chain, i.e., enforces at least one validator to validate chain `chainID`
uint32 top_N = 4;
}

message QueryValidatorConsumerAddrRequest {
Expand Down Expand Up @@ -212,3 +244,37 @@ message QueryParamsResponse {
Params params = 1 [(gogoproto.nullable) = false];
}


message QueryConsumerChainOptedInValidatorsRequest {
string chain_id = 1;
}

message QueryConsumerChainOptedInValidatorsResponse {
// The consensus addresses of the validators on the provider chain
repeated string validators_provider_addresses = 1;
}


message QueryConsumerChainsValidatorHasToValidateRequest {
// The consensus address of the validator on the provider chain
string provider_address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ];
}

message QueryConsumerChainsValidatorHasToValidateResponse {
repeated string consumer_chain_ids = 1;
}

message QueryValidatorConsumerCommissionRateRequest {
string chain_id = 1;
// The consensus address of the validator on the provider chain
string provider_address = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ];
}

message QueryValidatorConsumerCommissionRateResponse {
// The rate to charge delegators on the consumer chain, as a fraction
string rate = 1 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
123 changes: 123 additions & 0 deletions x/ccv/provider/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ func NewQueryCmd() *cobra.Command {
cmd.AddCommand(CmdProposedConsumerChains())
cmd.AddCommand(CmdAllPairsValConAddrByConsumerChainID())
cmd.AddCommand(CmdProviderParameters())
cmd.AddCommand(CmdConsumerChainOptedInValidators())
cmd.AddCommand(CmdConsumerChainsValidatorHasToValidate())
cmd.AddCommand(CmdValidatorConsumerCommissionRate())
return cmd
}

Expand Down Expand Up @@ -409,3 +412,123 @@ $ %s query provider params

return cmd
}

// Command to query opted-in validators by consumer chain ID
func CmdConsumerChainOptedInValidators() *cobra.Command {
cmd := &cobra.Command{
Use: "consumer-opted-in-validators [chainid]",
Short: "Query opted-in validators for a given consumer chain",
Long: strings.TrimSpace(
fmt.Sprintf(`Query opted-in validators for a given consumer chain.
Example:
$ %s consumer-opted-in-validators foochain
`, version.AppName),
),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

res, err := queryClient.QueryConsumerChainOptedInValidators(cmd.Context(),
&types.QueryConsumerChainOptedInValidatorsRequest{ChainId: args[0]})
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// Command to query the consumer chains list a given validator has to validate
func CmdConsumerChainsValidatorHasToValidate() *cobra.Command {
bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix()
cmd := &cobra.Command{
Use: "has-to-validate [provider-validator-address]",
Short: "Query the consumer chains list a given validator has to validate",
Long: strings.TrimSpace(
fmt.Sprintf(`Query the consumer chains list a given validator has to validate.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make this a bit more explicit?

Example:
$ %s has-to-validate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj
`, version.AppName, bech32PrefixConsAddr),
),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

addr, err := sdk.ConsAddressFromBech32(args[0])
if err != nil {
return err
}

res, err := queryClient.QueryConsumerChainsValidatorHasToValidate(cmd.Context(),
&types.QueryConsumerChainsValidatorHasToValidateRequest{
ProviderAddress: addr.String(),
})
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// Command to query the consumer commission rate a validator charges
// on a consumer chain
func CmdValidatorConsumerCommissionRate() *cobra.Command {
bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix()
cmd := &cobra.Command{
Use: "validator-consumer-commission-rate [chainid] [provider-validator-address]",
Short: "Query the consumer commission rate a validator charges on a consumer chain",
Long: strings.TrimSpace(
fmt.Sprintf(`Query the consumer commission rate a validator charges on a consumer chain.
Example:
$ %s validator-consumer-commission-rate foochain %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj
`, version.AppName, bech32PrefixConsAddr),
),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

addr, err := sdk.ConsAddressFromBech32(args[1])
if err != nil {
return err
}

res, err := queryClient.QueryValidatorConsumerCommissionRate(cmd.Context(),
&types.QueryValidatorConsumerCommissionRateRequest{
ChainId: args[0],
ProviderAddress: addr.String(),
})
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
101 changes: 100 additions & 1 deletion x/ccv/provider/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"
"fmt"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -42,7 +43,6 @@ func (k Keeper) QueryConsumerChains(goCtx context.Context, req *types.QueryConsu

ctx := sdk.UnwrapSDKContext(goCtx)

// convert to array of pointers
chains := []*types.Chain{}
for _, chain := range k.GetAllConsumerChains(ctx) {
// prevent implicit memory aliasing
Expand Down Expand Up @@ -221,3 +221,102 @@ func (k Keeper) QueryParams(c context.Context, _ *types.QueryParamsRequest) (*ty

return &types.QueryParamsResponse{Params: params}, nil
}

// QueryConsumerChainOptedInValidators returns all validators that opted-in to a given consumer chain
func (k Keeper) QueryConsumerChainOptedInValidators(goCtx context.Context, req *types.QueryConsumerChainOptedInValidatorsRequest) (*types.QueryConsumerChainOptedInValidatorsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

consumerChainID := req.ChainId
if consumerChainID == "" {
return nil, status.Error(codes.InvalidArgument, "empty chainId")
}

optedInVals := []string{}
ctx := sdk.UnwrapSDKContext(goCtx)

if !k.IsConsumerProposedOrRegistered(ctx, consumerChainID) {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown consumer chain: %s", consumerChainID))
}

for _, v := range k.GetAllOptedIn(ctx, ctx.ChainID()) {
optedInVals = append(optedInVals, v.ToSdkConsAddr().String())
}

return &types.QueryConsumerChainOptedInValidatorsResponse{
ValidatorsProviderAddresses: optedInVals,
}, nil
}

// QueryConsumerChainsValidatorHasToValidate returns all consumer chains a given validator has to validate
func (k Keeper) QueryConsumerChainsValidatorHasToValidate(goCtx context.Context, req *types.QueryConsumerChainsValidatorHasToValidateRequest) (*types.QueryConsumerChainsValidatorHasToValidateResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if req.ProviderAddress == "" {
return nil, status.Error(codes.InvalidArgument, "empty provider address")
}

consAddr, err := sdk.ConsAddressFromBech32(req.ProviderAddress)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid provider address")
}

ctx := sdk.UnwrapSDKContext(goCtx)

// get all the consumer chains for which the validator is opted-in
consumersToValidate := []string{}
for _, consumer := range k.GetAllConsumerChains(ctx) {
chainID := consumer.ChainId
if k.IsConsumerValidator(ctx, chainID, types.NewProviderConsAddress(consAddr)) {
consumersToValidate = append(consumersToValidate, chainID)
}
}

return &types.QueryConsumerChainsValidatorHasToValidateResponse{
ConsumerChainIds: consumersToValidate,
}, nil
}

// QueryValidatorConsumerCommissionRate returns the commission rate a given
// validator charges on a given consumer chain
func (k Keeper) QueryValidatorConsumerCommissionRate(goCtx context.Context, req *types.QueryValidatorConsumerCommissionRateRequest) (*types.QueryValidatorConsumerCommissionRateResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

consumerChainID := req.ChainId
if consumerChainID == "" {
return nil, status.Error(codes.InvalidArgument, "empty chainId")
}

consAddr, err := sdk.ConsAddressFromBech32(req.ProviderAddress)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid provider address")
}

ctx := sdk.UnwrapSDKContext(goCtx)

if !k.IsConsumerProposedOrRegistered(ctx, consumerChainID) {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown consumer chain: %s", consumerChainID))
}

res := &types.QueryValidatorConsumerCommissionRateResponse{}

// Check if the validator has a commission rate set for the consumer chain,
// otherwise use the commission rate from the validator staking module struct
consumerRate, found := k.GetConsumerCommissionRate(ctx, consumerChainID, types.NewProviderConsAddress(consAddr))
if found {
res.Rate = consumerRate
} else {
v, ok := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
if !ok {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown validator: %s", consAddr.String()))
}
res.Rate = v.Commission.Rate
}

return res, nil
}
9 changes: 8 additions & 1 deletion x/ccv/provider/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,16 @@ func (k Keeper) GetAllConsumerChains(ctx sdk.Context) (chains []types.Chain) {
chainID := string(iterator.Key()[1:])
clientID := string(iterator.Value())

// Get the consumer TopN value by checking
// if the chain is a TopN chain,
// otherwise it's a OptIn chain
topN, isTopN := k.GetTopN(ctx, chainID)

chains = append(chains, types.Chain{
ChainId: chainID,
ClientId: clientID,
Opt_In: !isTopN,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be:

Suggested change
Opt_In: !isTopN,
Opt_In: !isTopN || topN == 0,

GetTopN would return a found on whether TopN is set, but TopN could be set to 0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, below in keeper_test.go you probably won't need to check if topN > 0 { in

if topN > 0 {
    pk.SetTopN(ctx, chainID, topN)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense, thanks. Updated.

Top_N: topN,
})
}

Expand Down Expand Up @@ -1296,4 +1303,4 @@ func (k Keeper) DeleteConsumerCommissionRate(
) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.ConsumerCommissionRateKey(chainID, providerAddr))
}
}
6 changes: 5 additions & 1 deletion x/ccv/provider/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,12 @@ func TestGetAllConsumerChains(t *testing.T) {
expectedGetAllOrder := []types.Chain{}
for i, chainID := range chainIDs {
clientID := fmt.Sprintf("client-%d", len(chainIDs)-i)
topN := uint32(i)
pk.SetConsumerClientId(ctx, chainID, clientID)
expectedGetAllOrder = append(expectedGetAllOrder, types.Chain{ChainId: chainID, ClientId: clientID})
if topN > 0 {
pk.SetTopN(ctx, chainID, topN)
}
expectedGetAllOrder = append(expectedGetAllOrder, types.Chain{ChainId: chainID, ClientId: clientID, Opt_In: topN == 0, Top_N: topN})
}
// sorting by chainID
sort.Slice(expectedGetAllOrder, func(i, j int) bool {
Expand Down
Loading
Loading