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 fields to consumer validators query #2167

Merged
merged 6 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
26 changes: 25 additions & 1 deletion proto/interchain_security/ccv/provider/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ 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";
import "cosmos/staking/v1beta1/staking.proto";

service Query {
// ConsumerGenesis queries the genesis state needed to start a consumer chain
Expand Down Expand Up @@ -342,10 +343,33 @@ message QueryConsumerValidatorsValidator {
// The power of the validator used on the consumer chain
int64 power = 3;
// The rate to charge delegators on the consumer chain, as a fraction
string rate = 4 [
string consumer_commission_rate = 4 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// The rate to charge delegators on the provider chain, as a fraction
string provider_commission_rate = 5 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// description defines the description terms for the validator
cosmos.staking.v1beta1.Description description = 6 [(gogoproto.nullable) = false];
// provider_operator_address defines the address of the validator's operator
string provider_operator_address = 7 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
// jailed defined whether the validator has been jailed from bonded status or not.
bool jailed = 8;
// status is the validator status (bonded/unbonding/unbonded).
cosmos.staking.v1beta1.BondStatus status = 9;
// provider_tokens defines the delegated tokens (incl. self-delegation).
string provider_tokens = 10 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false
];
// The power of the validator used on the provider chain
int64 provider_power = 11;
Copy link
Contributor

Choose a reason for hiding this comment

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

Shall we deprecate the previously defined power and introduce the consumer_power field.

Copy link
Contributor

Choose a reason for hiding this comment

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

That breaks backward compatibility, let's not.

Copy link
Contributor

Choose a reason for hiding this comment

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

How exactly does it break backward compatibility?

Copy link
Contributor

@MSalopek MSalopek Aug 22, 2024

Choose a reason for hiding this comment

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

You are renaming a proto field while keeping the same enum index.

That means that users of the previous query will need to be upgraded because will no longer be able to parse your data.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please re-read my initial comment:

Shall we deprecate the previously defined power and introduce the consumer_power field.

I never talked about renaming anything. I clearly said to deprecate power and introduce consumer_power. So, something like this:

// DEPRECATED blah blah ...
int64 power = 3 [deprecated = true];
...
int64 consumer_power = ...;

As far as I know, this is backwards compatible.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for pointing it out, my appologies.

I have no preference if that's the way you want to go, as long as there's a field and it's not burried in a nested JSON - great 😄

// validates_current_epoch defines whether the validator has to validate for the current epoch or not
bool validates_current_epoch = 12;
}

message QueryConsumerValidatorsResponse {
Expand Down
44 changes: 27 additions & 17 deletions x/ccv/provider/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"google.golang.org/grpc/status"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"

Expand Down Expand Up @@ -346,30 +345,41 @@ func (k Keeper) QueryConsumerValidators(goCtx context.Context, req *types.QueryC
return nil, status.Error(codes.Internal, err.Error())
}

for _, v := range consumerValSet {
for _, consumerVal := range consumerValSet {
provAddr := types.ProviderConsAddress{Address: consumerVal.ProviderConsAddr}
consAddr := provAddr.ToSdkConsAddr()

consAddr, err := sdk.ConsAddressFromBech32(sdk.ConsAddress(v.ProviderConsAddr).String())
providerVal, err := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid provider address")
k.Logger(ctx).Error("cannot find consensus address for provider address:%s", provAddr.String())
continue
}

hasToValidate, err := k.hasToValidate(ctx, provAddr, consumerId)
if err != nil {
k.Logger(ctx).Error("cannot define if validator %s has to validate for consumer %s for current epoch",
provAddr.String(), consumerId)
continue
}

var rate math.LegacyDec
consumerRate, found := k.GetConsumerCommissionRate(ctx, consumerId, types.NewProviderConsAddress(consAddr))
if found {
rate = consumerRate
} else {
v, err := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
if err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown validator: %s", consAddr.String()))
}
rate = v.Commission.Rate
if !found {
consumerRate = providerVal.Commission.Rate
}

validators = append(validators, &types.QueryConsumerValidatorsValidator{
ProviderAddress: sdk.ConsAddress(v.ProviderConsAddr).String(),
ConsumerKey: v.PublicKey,
Power: v.Power,
Rate: rate,
ProviderAddress: sdk.ConsAddress(consumerVal.ProviderConsAddr).String(),
ConsumerKey: consumerVal.PublicKey,
Power: consumerVal.Power,
ConsumerCommissionRate: consumerRate,
Description: providerVal.Description,
ProviderOperatorAddress: providerVal.OperatorAddress,
Jailed: providerVal.Jailed,
Status: providerVal.Status,
ProviderTokens: providerVal.Tokens,
ProviderCommissionRate: providerVal.Commission.Rate,
ProviderPower: providerVal.GetConsensusPower(k.stakingKeeper.PowerReduction(ctx)),
ValidatesCurrentEpoch: hasToValidate,
})
}
return &types.QueryConsumerValidatorsResponse{
Expand Down
81 changes: 59 additions & 22 deletions x/ccv/provider/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,43 +112,80 @@ func TestQueryConsumerValidators(t *testing.T) {
_, err := pk.QueryConsumerValidators(ctx, &req)
require.Error(t, err)

providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1"))
consumerKey1 := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey()
consumerValidator1 := types.ConsensusValidator{ProviderConsAddr: providerAddr1.ToSdkConsAddr(), Power: 1, PublicKey: &consumerKey1}
expectedCommissionRate1 := math.LegacyMustNewDecFromStr("0.123")
pk.SetConsumerCommissionRate(ctx, consumerId, providerAddr1, expectedCommissionRate1)
val1 := createStakingValidator(ctx, mocks, 1, 1, 1)
valConsAddr1, _ := val1.GetConsAddr()
providerAddr1 := types.NewProviderConsAddress(valConsAddr1)
pk1, _ := val1.CmtConsPublicKey()
consumerValidator1 := types.ConsensusValidator{ProviderConsAddr: providerAddr1.ToSdkConsAddr(), Power: 1, PublicKey: &pk1}
val1.Tokens = sdk.TokensFromConsensusPower(1, sdk.DefaultPowerReduction)
val1.Description = stakingtypes.Description{Moniker: "ConsumerValidator1"}
val1.Commission.Rate = math.LegacyMustNewDecFromStr("0.123")

val2 := createStakingValidator(ctx, mocks, 1, 2, 2)
valConsAddr2, _ := val2.GetConsAddr()
providerAddr2 := types.NewProviderConsAddress(valConsAddr2)
pk2, _ := val2.CmtConsPublicKey()
consumerValidator2 := types.ConsensusValidator{ProviderConsAddr: providerAddr2.ToSdkConsAddr(), Power: 2, PublicKey: &pk2}
val2.Tokens = sdk.TokensFromConsensusPower(2, sdk.DefaultPowerReduction)
val2.Description = stakingtypes.Description{Moniker: "ConsumerValidator2"}
val2.Commission.Rate = math.LegacyMustNewDecFromStr("0.123")

providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2"))
consumerKey2 := cryptotestutil.NewCryptoIdentityFromIntSeed(2).TMProtoCryptoPublicKey()
consumerValidator2 := types.ConsensusValidator{ProviderConsAddr: providerAddr2.ToSdkConsAddr(), Power: 2, PublicKey: &consumerKey2}
expectedCommissionRate2 := math.LegacyMustNewDecFromStr("0.123")
pk.SetConsumerCommissionRate(ctx, consumerId, providerAddr2, expectedCommissionRate2)
// set up the client id so the chain looks like it "started"
pk.SetConsumerClientId(ctx, consumerId, "clientID")
pk.SetConsumerValSet(ctx, consumerId, []types.ConsensusValidator{consumerValidator1, consumerValidator2})
// set a consumer commission rate for val1
val1ConsComRate := math.LegacyMustNewDecFromStr("0.456")
pk.SetConsumerCommissionRate(ctx, consumerId, providerAddr1, val1ConsComRate)

expectedResponse := types.QueryConsumerValidatorsResponse{
Validators: []*types.QueryConsumerValidatorsValidator{
{ProviderAddress: providerAddr1.String(), ConsumerKey: &consumerKey1, Power: 1, Rate: expectedCommissionRate1},
{ProviderAddress: providerAddr2.String(), ConsumerKey: &consumerKey2, Power: 2, Rate: expectedCommissionRate2},
{
ProviderAddress: providerAddr1.String(),
ConsumerKey: &pk1,
Power: 1,
ConsumerCommissionRate: val1ConsComRate,
Description: val1.Description,
ProviderOperatorAddress: val1.OperatorAddress,
Jailed: val1.Jailed,
Status: val1.Status,
ProviderTokens: val1.Tokens,
ProviderCommissionRate: val1.Commission.Rate,
ProviderPower: 1,
ValidatesCurrentEpoch: true,
},
{
ProviderAddress: providerAddr2.String(),
ConsumerKey: &pk2,
Power: 2,
ConsumerCommissionRate: val2.Commission.Rate,
Description: val2.Description,
ProviderOperatorAddress: val2.OperatorAddress,
Jailed: val2.Jailed,
Status: val2.Status,
ProviderTokens: val2.Tokens,
ProviderCommissionRate: val2.Commission.Rate,
ProviderPower: 2,
ValidatesCurrentEpoch: true,
},
},
}

// set up the client id so the chain looks like it "started"
pk.SetConsumerClientId(ctx, consumerId, "clientID")
pk.SetConsumerValSet(ctx, consumerId, []types.ConsensusValidator{consumerValidator1, consumerValidator2})
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr1).Return(val1, nil).AnyTimes()
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr2).Return(val2, nil).AnyTimes()
mocks.MockStakingKeeper.EXPECT().PowerReduction(ctx).Return(sdk.DefaultPowerReduction).AnyTimes()

testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 2, []stakingtypes.Validator{val1, val2}, []int64{1, 2}, -1) // -1 to allow the calls "AnyTimes"

res, err := pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Equal(t, &expectedResponse, res)

// validator with no set consumer commission rate
pk.DeleteConsumerCommissionRate(ctx, consumerId, providerAddr1)
expectedCommissionRate := math.LegacyMustNewDecFromStr("0.456")
// because no consumer commission rate is set, the validator's set commission rate on the provider is used
val := stakingtypes.Validator{Commission: stakingtypes.Commission{CommissionRates: stakingtypes.CommissionRates{Rate: expectedCommissionRate}}}
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(
ctx, providerAddr1.ToSdkConsAddr()).Return(val, nil).Times(1)
res, _ = pk.QueryConsumerValidators(ctx, &req)
require.Equal(t, expectedCommissionRate, res.Validators[0].Rate)

res, err = pk.QueryConsumerValidators(ctx, &req)
require.NoError(t, err)
require.Equal(t, val1.Commission.Rate, res.Validators[0].ConsumerCommissionRate)
}

func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions x/ccv/provider/keeper/validator_set_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func createStakingValidator(ctx sdk.Context, mocks testkeeper.MockedKeepers, ind
return stakingtypes.Validator{
OperatorAddress: providerValidatorAddr.String(),
ConsensusPubkey: pkAny,
Status: stakingtypes.Bonded,
}
}

Expand Down
Loading
Loading