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 all 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
32 changes: 29 additions & 3 deletions 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/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 @@ -339,13 +340,38 @@
string provider_address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ];
// The consumer public key of the validator used on the consumer chain
tendermint.crypto.PublicKey consumer_key = 2;
// The power of the validator used on the consumer chain
int64 power = 3;
// [DEPRECATED] use consumer_power instead
int64 power = 3 [deprecated = true];
// The power of the validator used on the consumer chain
int64 consumer_power = 4;

Check failure on line 346 in proto/interchain_security/ccv/provider/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "4" with name "consumer_power" on message "QueryConsumerValidatorsValidator" changed option "json_name" from "rate" to "consumerPower".

Check failure on line 346 in proto/interchain_security/ccv/provider/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "4" with name "consumer_power" on message "QueryConsumerValidatorsValidator" changed type from "string" to "int64".

Check failure on line 346 in proto/interchain_security/ccv/provider/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "4" on message "QueryConsumerValidatorsValidator" changed name from "rate" to "consumer_power".
// The rate to charge delegators on the consumer chain, as a fraction
string rate = 4 [
string consumer_commission_rate = 5 [
(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 = 6 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// description defines the description terms for the validator
cosmos.staking.v1beta1.Description description = 7 [(gogoproto.nullable) = false];
// provider_operator_address defines the address of the validator's operator
string provider_operator_address = 8 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
// jailed defined whether the validator has been jailed from bonded status or not.
bool jailed = 9;
// status is the validator status (bonded/unbonding/unbonded).
cosmos.staking.v1beta1.BondStatus status = 10;
// provider_tokens defines the delegated tokens (incl. self-delegation).
string provider_tokens = 11 [
(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 = 12;
// validates_current_epoch defines whether the validator has to validate for the current epoch or not
bool validates_current_epoch = 13;
}

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,
ConsumerPower: 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,
ConsumerPower: 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,
ConsumerPower: 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