From 97c463e78c4a9cb7e8ece69312753fc71eb2701b Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 26 Apr 2024 18:40:27 +0200 Subject: [PATCH 01/16] added power shaping --- .../ccv/provider/v1/provider.proto | 14 + tests/mbt/driver/setup.go | 5 +- testutil/keeper/expectations.go | 16 +- testutil/keeper/unit_test_helpers.go | 4 + x/ccv/provider/client/proposal_handler.go | 16 +- x/ccv/provider/keeper/keeper.go | 140 ++++++ x/ccv/provider/keeper/key_assignment_test.go | 4 +- x/ccv/provider/keeper/partial_set_security.go | 127 +++++- .../keeper/partial_set_security_test.go | 192 ++++++++- x/ccv/provider/keeper/proposal.go | 64 ++- x/ccv/provider/keeper/proposal_test.go | 41 +- x/ccv/provider/keeper/relay.go | 10 +- x/ccv/provider/keeper/relay_test.go | 79 ++++ x/ccv/provider/keeper/validator_set_update.go | 18 +- .../keeper/validator_set_update_test.go | 24 +- x/ccv/provider/proposal_handler_test.go | 4 + x/ccv/provider/types/keys.go | 37 ++ x/ccv/provider/types/proposal.go | 12 + x/ccv/provider/types/proposal_test.go | 106 ++++- x/ccv/provider/types/provider.pb.go | 408 +++++++++++++----- x/ccv/provider/types/query.pb.go | 4 +- 21 files changed, 1138 insertions(+), 187 deletions(-) diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 1b5f7a515f..139cc9d25f 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -90,6 +90,20 @@ message ConsumerAdditionProposal { // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. uint32 top_N = 15; + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For instance, if + // `validators_power_cap` is set to 32, it means that no validator can have more than 32% of the voting power on the + // consumer chain. Note that this might not be feasible. For example, think of a consumer chain with only + // 5 validators and with `validators_power_cap` set to 10%. In such a scenario, at least one validator would need + // to have more than 20% of the total voting power. Therefore, `validators_power_cap` operates on a best-effort basis. + uint32 validators_power_cap = 16; + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. + uint32 validator_set_cap = 17; + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + repeated string allowlist = 18; + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. + repeated string denylist = 19; } // ConsumerRemovalProposal is a governance proposal on the provider chain to diff --git a/tests/mbt/driver/setup.go b/tests/mbt/driver/setup.go index deeae01f6b..a467cd7931 100644 --- a/tests/mbt/driver/setup.go +++ b/tests/mbt/driver/setup.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" "log" "testing" "time" @@ -373,8 +374,8 @@ func (s *Driver) ConfigureNewPath(consumerChain, providerChain *ibctesting.TestC stakingValidators = append(stakingValidators, v) } - considerAll := func(validator stakingtypes.Validator) bool { return true } - nextValidators := s.providerKeeper().ComputeNextEpochConsumerValSet(s.providerCtx(), string(consumerChainId), stakingValidators, considerAll) + considerAll := func(providerAddr types.ProviderConsAddress) bool { return true } + nextValidators := s.providerKeeper().FilterValidators(s.providerCtx(), string(consumerChainId), stakingValidators, considerAll) s.providerKeeper().SetConsumerValSet(s.providerCtx(), string(consumerChainId), nextValidators) err = s.providerKeeper().SetConsumerGenesis( diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 5a37f3a164..8f995b5042 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -1,6 +1,7 @@ package keeper import ( + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" time "time" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -45,6 +46,10 @@ func GetMocksForCreateConsumerClient(ctx sdk.Context, mocks *MockedKeepers, ).Return("clientID", nil).Times(1) expectations = append(expectations, createClientExp) + mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return( + cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator(), true).AnyTimes() + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(234)).AnyTimes() + return expectations } @@ -52,13 +57,22 @@ func GetMocksForCreateConsumerClient(ctx sdk.Context, mocks *MockedKeepers, func GetMocksForMakeConsumerGenesis(ctx sdk.Context, mocks *MockedKeepers, unbondingTimeToInject time.Duration, ) []*gomock.Call { + mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return( + cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator(), true).AnyTimes() + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(234)).AnyTimes() + return []*gomock.Call{ mocks.MockStakingKeeper.EXPECT().UnbondingTime(gomock.Any()).Return(unbondingTimeToInject).Times(1), mocks.MockClientKeeper.EXPECT().GetSelfConsensusState(gomock.Any(), clienttypes.GetSelfHeight(ctx)).Return(&ibctmtypes.ConsensusState{}, nil).Times(1), - mocks.MockStakingKeeper.EXPECT().IterateLastValidatorPowers(gomock.Any(), gomock.Any()).Times(1), + mocks.MockStakingKeeper.EXPECT(). + IterateLastValidatorPowers(gomock.Any(), gomock.Any()). + Do(func(ctx sdk.Context, fn func(addr sdk.ValAddress, power int64) (stop bool)) { + fn(sdk.ValAddress("address"), 100) + }). + Return().AnyTimes(), } } diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index 1bb1f7f3ee..abba1a23c9 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -277,6 +277,10 @@ func GetTestConsumerAdditionProp() *providertypes.ConsumerAdditionProposal { types.DefaultTransferTimeoutPeriod, types.DefaultConsumerUnbondingPeriod, 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal) return prop diff --git a/x/ccv/provider/client/proposal_handler.go b/x/ccv/provider/client/proposal_handler.go index 9b5674f4c7..bb88276393 100644 --- a/x/ccv/provider/client/proposal_handler.go +++ b/x/ccv/provider/client/proposal_handler.go @@ -65,7 +65,11 @@ Where proposal.json contains: "ccv_timeout_period": 2419200000000000, "unbonding_period": 1728000000000000, "deposit": "10000stake", - "top_n": 0, + "top_n": 0, + "validators_power_cap": 32, + "validator_set_cap": 50, + "allowlist": [], + "denylist": ["validatorAConsensusAddress", "validatorBConsensusAddress"] } `, RunE: func(cmd *cobra.Command, args []string) error { @@ -87,7 +91,8 @@ Where proposal.json contains: proposal.GenesisHash, proposal.BinaryHash, proposal.SpawnTime, proposal.ConsumerRedistributionFraction, proposal.BlocksPerDistributionTransmission, proposal.DistributionTransmissionChannel, proposal.HistoricalEntries, - proposal.CcvTimeoutPeriod, proposal.TransferTimeoutPeriod, proposal.UnbondingPeriod, proposal.TopN) + proposal.CcvTimeoutPeriod, proposal.TransferTimeoutPeriod, proposal.UnbondingPeriod, proposal.TopN, + proposal.ValidatorsPowerCap, proposal.ValidatorSetCap, proposal.Allowlist, proposal.Denylist) from := clientCtx.GetFromAddress() @@ -242,7 +247,12 @@ type ConsumerAdditionProposalJSON struct { UnbondingPeriod time.Duration `json:"unbonding_period"` Deposit string `json:"deposit"` - TopN uint32 `json:"top_N"` + + TopN uint32 `json:"top_N"` + ValidatorsPowerCap uint32 `json:"validators_power_cap"` + ValidatorSetCap uint32 `json:"validator_set_cap"` + Allowlist []string `json:"allowlist"` + Denylist []string `json:"denylist"` } type ConsumerAdditionProposalReq struct { diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 080bf481c1..39c08f68fe 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1303,3 +1303,143 @@ func (k Keeper) DeleteConsumerCommissionRate( store := ctx.KVStore(k.storeKey) store.Delete(types.ConsumerCommissionRateKey(chainID, providerAddr)) } + +// SetValidatorsPowersCap sets the power-cap value `p` associated to chain with `chainID` +func (k Keeper) SetValidatorsPowersCap( + ctx sdk.Context, + chainID string, + p uint32, +) { + store := ctx.KVStore(k.storeKey) + + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, p) + + store.Set(types.ValidatorsPowerCapKey(chainID), buf) +} + +// DeleteValidatorsPowerCap removes the power-cap value associated to chain with `chainID` +func (k Keeper) DeleteValidatorsPowerCap( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ValidatorsPowerCapKey(chainID)) +} + +// GetValidatorsPowerCap returns `(p, true)` if chain `chainID` has power cap `p` associated with it, and (0, false) otherwise +func (k Keeper) GetValidatorsPowerCap( + ctx sdk.Context, + chainID string, +) (uint32, bool) { + store := ctx.KVStore(k.storeKey) + buf := store.Get(types.ValidatorsPowerCapKey(chainID)) + if buf == nil { + return 0, false + } + return binary.BigEndian.Uint32(buf), true +} + +// SetValidatorSetCap stores the validator-set cap value `c` associated to chain with `chainID` +func (k Keeper) SetValidatorSetCap( + ctx sdk.Context, + chainID string, + c uint32, +) { + store := ctx.KVStore(k.storeKey) + + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, c) + + store.Set(types.ValidatorSetCapKey(chainID), buf) +} + +// DeleteValidatorSetCap removes the validator-set cap value associated to chain with `chainID` +func (k Keeper) DeleteValidatorSetCap( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ValidatorSetCapKey(chainID)) +} + +// GetValidatorSetCap returns `(c, true)` if chain `chainID` has validator-set cap `c` associated with it, and (0, false) otherwise +func (k Keeper) GetValidatorSetCap( + ctx sdk.Context, + chainID string, +) (uint32, bool) { + store := ctx.KVStore(k.storeKey) + buf := store.Get(types.ValidatorSetCapKey(chainID)) + if buf == nil { + return 0, false + } + return binary.BigEndian.Uint32(buf), true +} + +// SetDenylist denylists validator with `providerAddr` address on chain `chainID` +func (k Keeper) SetDenylist( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Set(types.DenylistCapKey(chainID, providerAddr), []byte{}) +} + +// IsDenylisted returns `true` if validator with `providerAddr` has been denylisted on chain `chainID` +func (k Keeper) IsDenylisted( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) bool { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.DenylistCapKey(chainID, providerAddr)) + return bz != nil +} + +// SetAllowlist allowlists validator with `providerAddr` address on chain `chainID` +func (k Keeper) SetAllowlist( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Set(types.AllowlistCapKey(chainID, providerAddr), []byte{}) +} + +// IsAllowlisted returns `true` if validator with `providerAddr` has been allowlisted on chain `chainID` +func (k Keeper) IsAllowlisted( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) bool { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.AllowlistCapKey(chainID, providerAddr)) + return bz != nil +} + +// IsAllowlistEmpty returns `true` if no validator is allowlisted on chain `chainID` +func (k Keeper) IsAllowlistEmpty(ctx sdk.Context, chainID string) bool { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) + defer iterator.Close() + + if iterator.Valid() { + return false + } + + return true +} + +// IsDenylistEmpty returns `true` if no validator is allowlisted on chain `chainID` +func (k Keeper) IsDenylistEmpty(ctx sdk.Context, chainID string) bool { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistPrefix, chainID)) + defer iterator.Close() + + if iterator.Valid() { + return false + } + + return true +} diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 1ea4080d2f..b66b32398b 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -766,8 +766,8 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { }) } - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, CHAINID, bondedValidators, - func(validator stakingtypes.Validator) bool { + nextValidators := k.FilterValidators(ctx, CHAINID, bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { return true }) updates = providerkeeper.DiffValidators(k.GetConsumerValSet(ctx, CHAINID), nextValidators) diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 7c2e6a9723..600748fc93 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -138,11 +138,126 @@ func (k Keeper) ComputeMinPowerToOptIn(ctx sdk.Context, chainID string, bondedVa return 0 } -// ShouldConsiderOnlyOptIn returns true if `validator` is opted in, in `chainID. -func (k Keeper) ShouldConsiderOnlyOptIn(ctx sdk.Context, chainID string, validator stakingtypes.Validator) bool { - consAddr, err := validator.GetConsAddr() - if err != nil { - return false +// CapValidatorSet caps the provided `validators` if chain `chainID` is an Opt In chain with a validator-set cap. If cap +// is `k`, `CapValidatorSet` returns the first `k` validators from `validators` with the highest power. +func (k Keeper) CapValidatorSet(ctx sdk.Context, chainID string, validators []types.ConsumerValidator) []types.ConsumerValidator { + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { + // is a no-op if the chain is a Top N chain + return validators + } + + if validatorSetCap, found := k.GetValidatorSetCap(ctx, chainID); found && validatorSetCap != 0 { + sort.Slice(validators, func(i, j int) bool { + return validators[i].Power > validators[j].Power + }) + + minNumberOfValidators := 0 + if len(validators) < int(validatorSetCap) { + minNumberOfValidators = len(validators) + } else { + minNumberOfValidators = int(validatorSetCap) + } + return validators[:minNumberOfValidators] + } else { + return validators + } +} + +// CapValidatorsPower caps the power of the validators on chain `chainID` and returns an updated slice of validators +// with their new powers. Works on a best-basis effort because there are cases where we cannot guarantee that all validators +// on the consumer chain have less power than the set validators-power cap. For example, if we have 10 validators and +// the power cap is set to 5%, we need at least one validator to have more than 10% of the voting power on the consumer chain. +func (k Keeper) CapValidatorsPower(ctx sdk.Context, chainID string, validators []types.ConsumerValidator) []types.ConsumerValidator { + if p, found := k.GetValidatorsPowerCap(ctx, chainID); found { + return NoMoreThanPercentOfTheSum(validators, p) + } else { + // is a no-op if power cap is not set for `chainID` + return validators } - return k.IsOptedIn(ctx, chainID, types.NewProviderConsAddress(consAddr)) +} + +// sum is a helper function to sum all the validators' power +func sum(validators []types.ConsumerValidator) int64 { + s := int64(0) + for _, v := range validators { + s = s + v.Power + } + return s +} + +// NoMoreThanPercentOfTheSum returns a set of validators with updated powers such that no validator has more than the +// provided `percent` of the sum of all validators' powers. +func NoMoreThanPercentOfTheSum(validators []types.ConsumerValidator, percent uint32) []types.ConsumerValidator { + // The idea behind this algorithm is rather simple: + // - Compute the `maxPower` a validator must have so that it does not have more than `percent` of the voting power. + // - If a validator `v` has power `p`, then: + // - if `p > maxPower` we set `v`'s power to `maxPower` and distribute the `p - maxPower` to validators that + // have less than `maxPower` power + s := sum(validators) + maxPower := int64(float64(s) * (float64(percent) / 100.0)) + if maxPower == 0 { + // edge case: set `maxPower` to 1 to avoid setting the power of a validator to 0 + maxPower = 1 + } + + // Sort by `.Power` in decreasing order. This way, we improve the chances that if a validator `a` has more power + // than a validator `b` in `validators`, `a` has still more than `b` in the return validators. + sort.Slice(validators, func(i, j int) bool { + return validators[i].Power > validators[j].Power + }) + + // `remainingPower` is to be distributed to validators that have power less than `maxPower` + remainingPower := int64(0) + validatorsWithPowerLessThanMaxPower := 0 + for _, v := range validators { + if v.Power >= maxPower { + remainingPower = remainingPower + (v.Power - maxPower) + } else { + validatorsWithPowerLessThanMaxPower++ + } + } + + updatedValidators := make([]types.ConsumerValidator, len(validators)) + + powerPerValidator := int64(0) + remainingValidators := int64(validatorsWithPowerLessThanMaxPower) + if remainingValidators != 0 { + // power to give to each validator in order to distribute the `remainingPower` + powerPerValidator = remainingPower / remainingValidators + } + + for i, v := range validators { + if v.Power >= maxPower { + updatedValidators[i] = validators[i] + updatedValidators[i].Power = maxPower + } else if v.Power+powerPerValidator >= maxPower { + updatedValidators[i] = validators[i] + updatedValidators[i].Power = maxPower + remainingPower = remainingPower - (maxPower - v.Power) + remainingValidators-- + } else { + updatedValidators[i] = validators[i] + updatedValidators[i].Power = v.Power + powerPerValidator + remainingPower = remainingPower - (updatedValidators[i].Power - validators[i].Power) + remainingValidators-- + } + if remainingValidators == 0 { + continue + } + powerPerValidator = remainingPower / remainingValidators + } + + return updatedValidators +} + +// FilterOptedInAndAllowAndDenylistedPredicate filters the opted-in validators that are allowlisted and not denylisted +func (k Keeper) FilterOptedInAndAllowAndDenylistedPredicate(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) bool { + // only consider opted-in validators + return k.IsOptedIn(ctx, chainID, providerAddr) && + // if an allowlist is declared, only consider allowlisted validators + (k.IsAllowlistEmpty(ctx, chainID) || + k.IsAllowlisted(ctx, chainID, providerAddr)) && + // if a denylist is declared, only consider denylisted validators + (k.IsDenylistEmpty(ctx, chainID) || + !k.IsDenylisted(ctx, chainID, providerAddr)) } diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 303bdd633f..62d2d117f0 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -2,6 +2,8 @@ package keeper_test import ( "bytes" + "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" "math" "sort" "testing" @@ -287,16 +289,198 @@ func TestComputeMinPowerToOptIn(t *testing.T) { require.Equal(t, int64(math.MaxInt64), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 0)) } -// TestShouldConsiderOnlyOptIn returns true if `validator` is opted in, in `chainID. -func TestShouldConsiderOnlyOptIn(t *testing.T) { +// TestFilterOptedInAndAllowAndDenylistedPredicate returns true if `validator` is opted in, in `chainID. +func TestFilterOptedInAndAllowAndDenylistedPredicate(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() validator := createStakingValidator(ctx, mocks, 0, 1) consAddr, _ := validator.GetConsAddr() + providerAddr := types.NewProviderConsAddress(consAddr) - require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", types.NewProviderConsAddress(consAddr))) + // with no allowlist or denylist, the validator has to be opted in, in order to consider it + require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) providerKeeper.SetOptedIn(ctx, "chainID", types.NewProviderConsAddress(consAddr)) - require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", types.NewProviderConsAddress(consAddr))) + require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + + // create an allow list but do not add the validator `providerAddr` to it + validatorA := createStakingValidator(ctx, mocks, 1, 1) + consAddrA, _ := validatorA.GetConsAddr() + providerKeeper.SetAllowlist(ctx, "chainID", types.NewProviderConsAddress(consAddrA)) + require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + providerKeeper.SetAllowlist(ctx, "chainID", types.NewProviderConsAddress(consAddr)) + require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + + // create a denylist but do not add validator `providerAddr` to it + providerKeeper.SetDenylist(ctx, "chainID", types.NewProviderConsAddress(consAddrA)) + require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + // add validator `providerAddr` to the denylist + providerKeeper.SetDenylist(ctx, "chainID", types.NewProviderConsAddress(consAddr)) + require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) +} + +func TestCapValidatorSet(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validatorA := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrA"), + Power: 1, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorB := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrB"), + Power: 2, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorC := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrC"), + Power: 3, + ConsumerPublicKey: &crypto.PublicKey{}, + } + validators := []types.ConsumerValidator{validatorA, validatorB, validatorC} + + consumerValidators := providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, validators, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 0) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, validators, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 100) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, validators, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 1) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, []types.ConsumerValidator{validatorC}, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 2) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, []types.ConsumerValidator{validatorC, validatorB}, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 3) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, []types.ConsumerValidator{validatorC, validatorB, validatorA}, consumerValidators) +} + +func TestCapValidatorsPower(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validatorA := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrA"), + Power: 1, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorB := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrB"), + Power: 2, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorC := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrC"), + Power: 3, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorD := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrD"), + Power: 4, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validators := []types.ConsumerValidator{validatorA, validatorB, validatorC, validatorD} + + expectedValidators := make([]types.ConsumerValidator, len(validators)) + copy(expectedValidators, validators) + expectedValidators[0].Power = 2 + expectedValidators[1].Power = 2 + expectedValidators[2].Power = 3 + expectedValidators[3].Power = 3 + + sortValidators := func(validators []types.ConsumerValidator) { + sort.Slice(validators, func(i, j int) bool { + return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 + }) + } + + // no capping takes place because validators power-cap is not set + cappedValidators := providerKeeper.CapValidatorsPower(ctx, "chainID", validators) + sortValidators(validators) + sortValidators(cappedValidators) + require.Equal(t, validators, cappedValidators) + + providerKeeper.SetValidatorsPowersCap(ctx, "chainID", 33) + cappedValidators = providerKeeper.CapValidatorsPower(ctx, "chainID", validators) + sortValidators(expectedValidators) + sortValidators(cappedValidators) + require.Equal(t, expectedValidators, cappedValidators) +} + +func TestNoMoreThanPercentOfTheSum(t *testing.T) { + // returns `true` if no validator in `validators` corresponds to more than `percent` of the total sum of all + // validators' powers + noMoreThanPercent := func(validators []types.ConsumerValidator, percent uint32) bool { + sum := int64(0) + for _, v := range validators { + sum = sum + v.Power + } + + for _, v := range validators { + if (float64(v.Power)/float64(sum))*100.0 > float64(percent) { + return false + } + } + return true + } + + createConsumerValidators := func(powers []int64) []types.ConsumerValidator { + var validators []types.ConsumerValidator + for _, p := range powers { + validators = append(validators, types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddr"), + Power: p, + ConsumerPublicKey: &crypto.PublicKey{}, + }) + } + return validators + } + + // **impossible** case where we only have 9 powers, and we want that no number has more than 10% of the total sum + powers := []int64{1, 2, 3, 4, 5, 6, 7, 8, 9} + percent := uint32(10) + require.False(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 20 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 21 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 25 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 32 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 33 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 34 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + powers = []int64{1, 2, 3, 4, 5} + percent = 50 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index c0aa684243..040f20ff47 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -79,6 +79,7 @@ func (k Keeper) CreateConsumerClient(ctx sdk.Context, prop *types.ConsumerAdditi if err != nil { return err } + err = k.SetConsumerGenesis(ctx, chainID, consumerGen) if err != nil { return err @@ -295,10 +296,25 @@ func (k Keeper) MakeConsumerGenesis( k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) } - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chainID, bondedValidators, - func(validator stakingtypes.Validator) bool { - return k.ShouldConsiderOnlyOptIn(ctx, chainID, validator) + nextValidators := k.FilterValidators(ctx, chainID, bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { + // only consider opted-in validators + return k.IsOptedIn(ctx, chainID, providerAddr) && + // if an allowlist is declared, only consider allowlisted validators + (k.IsAllowlistEmpty(ctx, chainID) || + k.IsAllowlisted(ctx, chainID, providerAddr)) && + // if a denylist is declared, only consider denylisted validators + (k.IsDenylistEmpty(ctx, chainID) || + !k.IsDenylisted(ctx, chainID, providerAddr)) }) + + // TODO: fixme .. use the cached ... no??? + if prop.Top_N > 0 { + nextValidators = k.CapValidatorSet(ctx, chainID, nextValidators) + } + + nextValidators = k.CapValidatorsPower(ctx, chainID, nextValidators) + k.SetConsumerValSet(ctx, chainID, nextValidators) // get the initial updates with the latest set consumer public keys @@ -383,25 +399,49 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { propsToExecute := k.GetConsumerAdditionPropsToExecute(ctx) for _, prop := range propsToExecute { - if prop.Top_N == 0 && len(k.GetAllOptedIn(ctx, prop.ChainId)) == 0 { + // create consumer client in a cached context to handle errors + cachedCtx, writeFn := ctx.CacheContext() + + k.SetTopN(cachedCtx, prop.ChainId, prop.Top_N) + k.SetValidatorSetCap(ctx, prop.ChainId, prop.ValidatorSetCap) + k.SetValidatorsPowersCap(ctx, prop.ChainId, prop.ValidatorsPowerCap) + + for _, address := range prop.Allowlist { + consAddr, err := sdk.ConsAddressFromBech32(address) + if err != nil { + continue + } + + k.SetAllowlist(ctx, prop.ChainId, types.NewProviderConsAddress(consAddr)) + } + + for _, address := range prop.Denylist { + consAddr, err := sdk.ConsAddressFromBech32(address) + if err != nil { + continue + } + + k.SetDenylist(ctx, prop.ChainId, types.NewProviderConsAddress(consAddr)) + } + + err := k.CreateConsumerClient(cachedCtx, &prop) + + consumerGenesis, _ := k.GetConsumerGenesis(cachedCtx, prop.ChainId) + providerInfo := consumerGenesis.GetProvider() + + if len(providerInfo.GetInitialValSet()) == 0 { // drop the proposal - ctx.Logger().Info("consumer client could not be created because no validator has"+ - " opted in for the Opt-In chain: %s", prop.ChainId) + ctx.Logger().Info("consumer client could not be created because no validator exists in the"+ + "initial validator set for chain: %s", prop.ChainId) continue } - // create consumer client in a cached context to handle errors - cachedCtx, writeFn, err := k.CreateConsumerClientInCachedCtx(ctx, prop) if err != nil { // drop the proposal ctx.Logger().Info("consumer client could not be created: %w", err) continue } - // Only set Top N at the moment a chain starts. If we were to do this earlier (e.g., during the proposal), - // then someone could create a bogus ConsumerAdditionProposal to override the Top N for a specific chain. - k.SetTopN(cachedCtx, prop.ChainId, prop.Top_N) - // The cached context is created with a new EventManager so we merge the event // into the original context ctx.EventManager().EmitEvents(cachedCtx.EventManager().Events()) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 69534326e0..f67619f613 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "bytes" "encoding/json" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" "sort" "testing" "time" @@ -65,6 +66,10 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { 100000000000, 100000000000, 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), blockTime: now, expAppendProp: true, @@ -91,6 +96,10 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { 100000000000, 100000000000, 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), blockTime: now, expAppendProp: false, @@ -927,6 +936,10 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 50, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "spawn time passed", "chain2", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -939,6 +952,10 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 50, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "spawn time not passed", "chain3", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -951,6 +968,10 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 50, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "invalid proposal: chain id already exists", "chain2", clienttypes.NewHeight(4, 5), []byte{}, []byte{}, @@ -963,6 +984,10 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 50, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "opt-in chain with no validator opted in", "chain4", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -975,6 +1000,10 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "opt-in chain with at least one validator opted in", "chain5", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -987,6 +1016,10 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), } @@ -995,6 +1028,11 @@ func TestBeginBlockInit(t *testing.T) { expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain2", clienttypes.NewHeight(3, 4))...) expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain5", clienttypes.NewHeight(3, 4))...) + // consider at least one validator + validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() + mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return(validator, true).AnyTimes() + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(123)).AnyTimes() + gomock.InOrder(expectedCalls...) for _, prop := range pendingProps { @@ -1002,7 +1040,8 @@ func TestBeginBlockInit(t *testing.T) { } // opt in a sample validator so the chain's proposal can successfully execute - providerKeeper.SetOptedIn(ctx, pendingProps[5].ChainId, providertypes.NewProviderConsAddress([]byte("providerAddr"))) + consAddr, _ := validator.GetConsAddr() + providerKeeper.SetOptedIn(ctx, pendingProps[5].ChainId, providertypes.NewProviderConsAddress(consAddr)) providerKeeper.BeginBlockInit(ctx) // first proposal is not pending anymore because its spawn time already passed and was executed diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 35c98aa088..ee141d4cec 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -229,10 +229,14 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { k.OptInTopNValidators(ctx, chain.ChainId, bondedValidators, minPower) } - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chain.ChainId, bondedValidators, - func(validator stakingtypes.Validator) bool { - return k.ShouldConsiderOnlyOptIn(ctx, chain.ChainId, validator) + nextValidators := k.FilterValidators(ctx, chain.ChainId, bondedValidators, + func(providerAddr providertypes.ProviderConsAddress) bool { + return k.FilterOptedInAndAllowAndDenylistedPredicate(ctx, chain.ChainId, providerAddr) }) + + nextValidators = k.CapValidatorSet(ctx, chain.ChainId, nextValidators) + nextValidators = k.CapValidatorsPower(ctx, chain.ChainId, nextValidators) + valUpdates := DiffValidators(currentValidators, nextValidators) k.SetConsumerValSet(ctx, chain.ChainId, nextValidators) diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index a0c0b20e48..e464119d3e 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -735,3 +735,82 @@ func TestEndBlockVSU(t *testing.T) { providerKeeper.EndBlockVSU(ctx) require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) } + +// TestQueueVSCPacketsWithPowerCapping tests queueing validator set updates with power capping +func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + providerKeeper.SetValidatorSetUpdateId(ctx, 1) + + valA := createStakingValidator(ctx, mocks, 1, 1) // 3.125% of the total voting power + valAConsAddr, _ := valA.GetConsAddr() + valAPubKey, _ := valA.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(valA, true).AnyTimes() + valB := createStakingValidator(ctx, mocks, 2, 3) // 9.375% of the total voting power + valBConsAddr, _ := valB.GetConsAddr() + valBPubKey, _ := valB.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(valB, true).AnyTimes() + valC := createStakingValidator(ctx, mocks, 3, 4) // 12.5% of the total voting power + valCConsAddr, _ := valC.GetConsAddr() + valCPubKey, _ := valC.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valCConsAddr).Return(valC, true).AnyTimes() + valD := createStakingValidator(ctx, mocks, 4, 8) // 25% of the total voting power + valDConsAddr, _ := valD.GetConsAddr() + //valDPubKey, _ := valD.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, true).AnyTimes() + valE := createStakingValidator(ctx, mocks, 5, 16) // 50% of the total voting power + valEConsAddr, _ := valE.GetConsAddr() + valEPubKey, _ := valE.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valEConsAddr).Return(valE, true).AnyTimes() + + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB, valC, valD, valE}).AnyTimes() + + // add a consumer chain + providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + + providerKeeper.SetTopN(ctx, "chainID", 50) // would opt in E + + // opt in all validators + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valAConsAddr)) + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valBConsAddr)) + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valCConsAddr)) + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valDConsAddr)) + + // denylist validator D + providerKeeper.SetDenylist(ctx, "chainID", providertypes.NewProviderConsAddress(valDConsAddr)) + + // set a power-capping of 40% + providerKeeper.SetValidatorsPowersCap(ctx, "chainID", 40) + + providerKeeper.QueueVSCPackets(ctx) + + actualQueuedVSCPackets := providerKeeper.GetPendingVSCPackets(ctx, "chainID") + expectedQueuedVSCPackets := []ccv.ValidatorSetChangePacketData{ + ccv.NewValidatorSetChangePacketData( + []abci.ValidatorUpdate{ + // validator D is not here because it was denylisted + // powers have changed because of power capping + { + PubKey: valEPubKey, + Power: 9, + }, + { + PubKey: valCPubKey, + Power: 6, + }, + { + PubKey: valBPubKey, + Power: 5, + }, + { + PubKey: valAPubKey, + Power: 4, + }, + }, + 1, + nil), + } + + require.Equal(t, expectedQueuedVSCPackets, actualQueuedVSCPackets) +} diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index b9dc741d1b..746a639a7a 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -2,7 +2,6 @@ package keeper import ( "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -27,7 +26,7 @@ func (k Keeper) SetConsumerValidator( } // SetConsumerValSet resets the current consumer validators with the `nextValidators` computed by -// `ComputeNextEpochConsumerValSet` and hence this method should only be called after `ComputeNextEpochConsumerValSet` has completed. +// `FilterValidators` and hence this method should only be called after `FilterValidators` has completed. func (k Keeper) SetConsumerValSet(ctx sdk.Context, chainID string, nextValidators []types.ConsumerValidator) { k.DeleteConsumerValSet(ctx, chainID) for _, val := range nextValidators { @@ -152,17 +151,22 @@ func (k Keeper) CreateConsumerValidator(ctx sdk.Context, chainID string, validat }, nil } -// ComputeNextEpochConsumerValSet returns the next validator set that is responsible for validating consumer -// chain `chainID`. The next validator set corresponds to the `bondedValidator`s that `shouldConsider` evaluates to `true`. -func (k Keeper) ComputeNextEpochConsumerValSet( +// FilterValidators filters the provided `bondedValidators` according to `predicate` and returns +// the filtered set. +func (k Keeper) FilterValidators( ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator, - shouldConsider func(validator stakingtypes.Validator) bool, + predicate func(providerAddr types.ProviderConsAddress) bool, ) []types.ConsumerValidator { var nextValidators []types.ConsumerValidator for _, val := range bondedValidators { - if shouldConsider(val) { + consAddr, err := val.GetConsAddr() + if err != nil { + continue + } + + if predicate(types.NewProviderConsAddress(consAddr)) { nextValidator, err := k.CreateConsumerValidator(ctx, chainID, val) if err != nil { // this should never happen but is recoverable if we exclude this validator from the next validator set diff --git a/x/ccv/provider/keeper/validator_set_update_test.go b/x/ccv/provider/keeper/validator_set_update_test.go index 1de54fbbde..b93dfe4c95 100644 --- a/x/ccv/provider/keeper/validator_set_update_test.go +++ b/x/ccv/provider/keeper/validator_set_update_test.go @@ -324,8 +324,8 @@ func TestComputeNextEpochConsumerValSetConsiderAll(t *testing.T) { chainID := "chainID" // no consumer validators returned if we have no bonded validators - considerAll := func(validator stakingtypes.Validator) bool { return true } - require.Empty(t, providerKeeper.ComputeNextEpochConsumerValSet(ctx, chainID, []stakingtypes.Validator{}, considerAll)) + considerAll := func(providerAddr types.ProviderConsAddress) bool { return true } + require.Empty(t, providerKeeper.FilterValidators(ctx, chainID, []stakingtypes.Validator{}, considerAll)) var expectedValidators []types.ConsumerValidator @@ -353,7 +353,7 @@ func TestComputeNextEpochConsumerValSetConsiderAll(t *testing.T) { }) bondedValidators := []stakingtypes.Validator{valA, valB} - actualValidators := providerKeeper.ComputeNextEpochConsumerValSet(ctx, chainID, bondedValidators, considerAll) + actualValidators := providerKeeper.FilterValidators(ctx, chainID, bondedValidators, considerAll) require.Equal(t, expectedValidators, actualValidators) } @@ -364,9 +364,9 @@ func TestComputeNextEpochConsumerValSetConsiderOnlyOptIn(t *testing.T) { chainID := "chainID" // no consumer validators returned if we have no opted-in validators - require.Empty(t, providerKeeper.ComputeNextEpochConsumerValSet(ctx, chainID, []stakingtypes.Validator{}, - func(validator stakingtypes.Validator) bool { - return providerKeeper.ShouldConsiderOnlyOptIn(ctx, chainID, validator) + require.Empty(t, providerKeeper.FilterValidators(ctx, chainID, []stakingtypes.Validator{}, + func(providerAddr types.ProviderConsAddress) bool { + return providerKeeper.IsOptedIn(ctx, chainID, providerAddr) })) var expectedValidators []types.ConsumerValidator @@ -401,9 +401,9 @@ func TestComputeNextEpochConsumerValSetConsiderOnlyOptIn(t *testing.T) { // the expected actual validators are the opted-in validators but with the correct power and consumer public keys set bondedValidators := []stakingtypes.Validator{valA, valB} - actualValidators := providerKeeper.ComputeNextEpochConsumerValSet(ctx, "chainID", bondedValidators, - func(validator stakingtypes.Validator) bool { - return providerKeeper.ShouldConsiderOnlyOptIn(ctx, "chainID", validator) + actualValidators := providerKeeper.FilterValidators(ctx, "chainID", bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { + return providerKeeper.IsOptedIn(ctx, chainID, providerAddr) }) // sort validators first to be able to compare @@ -420,9 +420,9 @@ func TestComputeNextEpochConsumerValSetConsiderOnlyOptIn(t *testing.T) { // create a staking validator C that is not opted in, hence `expectedValidators` remains the same valC := createStakingValidator(ctx, mocks, 3, 3) bondedValidators = []stakingtypes.Validator{valA, valB, valC} - actualValidators = providerKeeper.ComputeNextEpochConsumerValSet(ctx, "chainID", bondedValidators, - func(validator stakingtypes.Validator) bool { - return providerKeeper.ShouldConsiderOnlyOptIn(ctx, "chainID", validator) + actualValidators = providerKeeper.FilterValidators(ctx, "chainID", bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { + return providerKeeper.IsOptedIn(ctx, chainID, providerAddr) }) sortValidators(actualValidators) diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index 30c1793360..185db25b07 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -45,6 +45,10 @@ func TestProviderProposalHandler(t *testing.T) { 100000000000, 100000000000, 0, + 0, + 0, + nil, + nil, ), blockTime: hourFromNow, // ctx blocktime is after proposal's spawn time expValidConsumerAddition: true, diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 1e2516ba07..da511d371e 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -159,6 +159,23 @@ const ( // that corresponds to the N% of the top validators that have to validate this consumer chain TopNBytePrefix + // ValidatorsPowerCapPrefix is the byte prefix storing the mapping from a consumer chain to the power-cap value of this chain, + // that corresponds to p% such that no validator can have more than p% of the voting power on the consumer chain. + // Operates on a best-effort basis. + ValidatorsPowerCapPrefix + + // ValidatorSetCapPrefix is the byte prefix storing the mapping from a consumer chain to the validator-set cap value + // of this chain. + ValidatorSetCapPrefix + + // AllowlistPrefix is the byte prefix storing the mapping from a consumer chain to the set of validators that are + // allowlisted. + AllowlistPrefix + + // DenylistPrefix is the byte prefix storing the mapping from a consumer chain to the set of validators that are + // denylisted. + DenylistPrefix + // ConsumerRewardsAllocationBytePrefix is the byte prefix used when storing for each consumer the rewards // it allocated to the consumer rewards pool ConsumerRewardsAllocationBytePrefix @@ -544,6 +561,26 @@ func TopNKey(chainID string) []byte { return ChainIdWithLenKey(TopNBytePrefix, chainID) } +// ValidatorSetPowerKey returns the key of consumer chain `chainID` +func ValidatorsPowerCapKey(chainID string) []byte { + return ChainIdWithLenKey(ValidatorsPowerCapPrefix, chainID) +} + +// ValidatorSetCapKey returns the key of consumer chain `chainID` +func ValidatorSetCapKey(chainID string) []byte { + return ChainIdWithLenKey(ValidatorSetCapPrefix, chainID) +} + +// DenylistCapKey returns the key to a validator's slash log +func DenylistCapKey(chainID string, providerAddr ProviderConsAddress) []byte { + return append(ChainIdWithLenKey(DenylistPrefix, chainID), providerAddr.ToSdkConsAddr().Bytes()...) +} + +// AllowlistCapKey returns the key to a validator's slash log +func AllowlistCapKey(chainID string, providerAddr ProviderConsAddress) []byte { + return append(ChainIdWithLenKey(AllowlistPrefix, chainID), providerAddr.ToSdkConsAddr().Bytes()...) +} + // OptedInKey returns the key of consumer chain `chainID` and validator with `providerAddr` func OptedInKey(chainID string, providerAddr ProviderConsAddress) []byte { prefix := ChainIdWithLenKey(OptedInBytePrefix, chainID) diff --git a/x/ccv/provider/types/proposal.go b/x/ccv/provider/types/proposal.go index 10f3a88865..c290d08ab7 100644 --- a/x/ccv/provider/types/proposal.go +++ b/x/ccv/provider/types/proposal.go @@ -50,6 +50,10 @@ func NewConsumerAdditionProposal(title, description, chainID string, transferTimeoutPeriod time.Duration, unbondingPeriod time.Duration, topN uint32, + validatorsPowerCap uint32, + validatorSetCap uint32, + allowlist []string, + denylist []string, ) govv1beta1.Content { return &ConsumerAdditionProposal{ Title: title, @@ -67,6 +71,10 @@ func NewConsumerAdditionProposal(title, description, chainID string, TransferTimeoutPeriod: transferTimeoutPeriod, UnbondingPeriod: unbondingPeriod, Top_N: topN, + ValidatorsPowerCap: validatorsPowerCap, + ValidatorSetCap: validatorSetCap, + Allowlist: allowlist, + Denylist: denylist, } } @@ -143,6 +151,10 @@ func (cccp *ConsumerAdditionProposal) ValidateBasic() error { return errorsmod.Wrap(ErrInvalidConsumerAdditionProposal, "Top N can either be 0 or in the range [50, 100]") } + if cccp.ValidatorsPowerCap != 0 && cccp.ValidatorSetCap > 100 { + return errorsmod.Wrap(ErrInvalidConsumerAdditionProposal, "validators' power cap has to be in the range [1, 100]") + } + return nil } diff --git a/x/ccv/provider/types/proposal_test.go b/x/ccv/provider/types/proposal_test.go index 13e7edb11e..465a376b47 100644 --- a/x/ccv/provider/types/proposal_test.go +++ b/x/ccv/provider/types/proposal_test.go @@ -37,6 +37,10 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 0, + 0, + 0, + nil, + nil, ), true, }, @@ -50,7 +54,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), true, }, { @@ -63,7 +72,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -76,7 +90,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -109,7 +128,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -122,7 +146,11 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil), false, }, { @@ -135,7 +163,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -148,7 +181,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -161,7 +199,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -174,7 +217,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -187,7 +235,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -200,7 +253,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -213,7 +271,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 0, 100000000000, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -226,7 +289,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 0, - 0), + 0, + 0, + 0, + nil, + nil, + ), false, }, } @@ -251,7 +319,11 @@ func TestMarshalConsumerAdditionProposal(t *testing.T) { 100000000000, 100000000000, 100000000000, - 0) + 0, + 0, + 0, + nil, + nil) cccp, ok := content.(*types.ConsumerAdditionProposal) require.True(t, ok) @@ -294,7 +366,11 @@ func TestConsumerAdditionProposalString(t *testing.T) { 100000000000, 10000000000, 100000000000, - 0) + 0, + 0, + 0, + []string{}, + []string{}) expect := fmt.Sprintf(`CreateConsumerChain Proposal Title: title diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index dee8a9520a..9a8b7e3bcf 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -97,6 +97,20 @@ type ConsumerAdditionProposal struct { // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. Top_N uint32 `protobuf:"varint,15,opt,name=top_N,json=topN,proto3" json:"top_N,omitempty"` + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For example, if + // `validators_power_cap` is set to `32`, it means that no validator can have more than 32% of the voting power on the + // consumer chain. Note that this might not be feasible. For instance, think of a consumer chain with only + // 5 validators and with `validators_power_cap` set to 10%. In such a scenario, at least one validator would need + // to have more than 20% of the total voting power. + ValidatorsPowerCap uint32 `protobuf:"varint,16,opt,name=validators_power_cap,json=validatorsPowerCap,proto3" json:"validators_power_cap,omitempty"` + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains an is a no-op if set for a Top N chain. + ValidatorSetCap uint32 `protobuf:"varint,17,opt,name=validator_set_cap,json=validatorSetCap,proto3" json:"validator_set_cap,omitempty"` + // Corresponds to a list of the provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + Allowlist []string `protobuf:"bytes,18,rep,name=allowlist,proto3" json:"allowlist,omitempty"` + // Corresponds to a list of the provider consensus addresses of validators that CANNOT validate the consumer chain. + Denylist []string `protobuf:"bytes,19,rep,name=denylist,proto3" json:"denylist,omitempty"` } func (m *ConsumerAdditionProposal) Reset() { *m = ConsumerAdditionProposal{} } @@ -1545,123 +1559,127 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1852 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4d, 0x6f, 0x1c, 0xc7, - 0xd1, 0xe6, 0x70, 0x97, 0x1f, 0x5b, 0xcb, 0xcf, 0x21, 0x6d, 0x0d, 0xf5, 0xf2, 0x5d, 0x52, 0xe3, - 0xd8, 0x61, 0xa2, 0x68, 0x26, 0xa4, 0x13, 0x40, 0x10, 0x62, 0x18, 0xe4, 0x52, 0xb6, 0x28, 0xda, - 0x12, 0x3d, 0x64, 0x28, 0x24, 0x39, 0x0c, 0x7a, 0x7b, 0x5a, 0xbb, 0x0d, 0xce, 0x4e, 0x8f, 0xba, - 0x7b, 0x47, 0xde, 0x4b, 0xce, 0xb9, 0x04, 0x70, 0x6e, 0x46, 0x2e, 0x71, 0x02, 0x04, 0x08, 0x72, - 0x49, 0x7e, 0x86, 0x8f, 0x3e, 0xe6, 0x64, 0x07, 0xd2, 0x21, 0x87, 0x5c, 0xf3, 0x03, 0x82, 0xee, - 0xf9, 0xdc, 0x25, 0xa9, 0xac, 0xe0, 0xe4, 0x42, 0xce, 0x54, 0x57, 0x3d, 0x55, 0xdd, 0x55, 0xf5, - 0x54, 0xef, 0xc0, 0x1e, 0x8d, 0x24, 0xe1, 0xb8, 0x87, 0x68, 0xe4, 0x0b, 0x82, 0x07, 0x9c, 0xca, - 0xa1, 0x8b, 0x71, 0xe2, 0xc6, 0x9c, 0x25, 0x34, 0x20, 0xdc, 0x4d, 0x76, 0x8b, 0x67, 0x27, 0xe6, - 0x4c, 0x32, 0xf3, 0xad, 0x2b, 0x6c, 0x1c, 0x8c, 0x13, 0xa7, 0xd0, 0x4b, 0x76, 0x6f, 0xbe, 0x7d, - 0x1d, 0x70, 0xb2, 0xeb, 0x3e, 0xa7, 0x9c, 0xa4, 0x58, 0x37, 0xd7, 0xbb, 0xac, 0xcb, 0xf4, 0xa3, - 0xab, 0x9e, 0x32, 0xe9, 0x56, 0x97, 0xb1, 0x6e, 0x48, 0x5c, 0xfd, 0xd6, 0x19, 0x3c, 0x75, 0x25, - 0xed, 0x13, 0x21, 0x51, 0x3f, 0xce, 0x14, 0x5a, 0xe3, 0x0a, 0xc1, 0x80, 0x23, 0x49, 0x59, 0x94, - 0x03, 0xd0, 0x0e, 0x76, 0x31, 0xe3, 0xc4, 0xc5, 0x21, 0x25, 0x91, 0x54, 0x5e, 0xd3, 0xa7, 0x4c, - 0xc1, 0x55, 0x0a, 0x21, 0xed, 0xf6, 0x64, 0x2a, 0x16, 0xae, 0x24, 0x51, 0x40, 0x78, 0x9f, 0xa6, - 0xca, 0xe5, 0x5b, 0x66, 0xb0, 0x59, 0x59, 0xc7, 0x7c, 0x18, 0x4b, 0xe6, 0x5e, 0x90, 0xa1, 0xc8, - 0x56, 0xdf, 0xc1, 0x4c, 0xf4, 0x99, 0x70, 0x89, 0xda, 0x7f, 0x84, 0x89, 0x9b, 0xec, 0x76, 0x88, - 0x44, 0xbb, 0x85, 0x20, 0x8f, 0x3b, 0xd3, 0xeb, 0x20, 0x51, 0xea, 0x60, 0x46, 0xf3, 0xb8, 0x57, - 0x51, 0x9f, 0x46, 0xcc, 0xd5, 0x7f, 0x53, 0x91, 0xfd, 0xaf, 0x59, 0xb0, 0xda, 0x2c, 0x12, 0x83, - 0x3e, 0xe1, 0xfb, 0x41, 0x40, 0xd5, 0x2e, 0x4f, 0x38, 0x8b, 0x99, 0x40, 0xa1, 0xb9, 0x0e, 0x33, - 0x92, 0xca, 0x90, 0x58, 0xc6, 0xb6, 0xb1, 0xd3, 0xf0, 0xd2, 0x17, 0x73, 0x1b, 0x9a, 0x01, 0x11, - 0x98, 0xd3, 0x58, 0x29, 0x5b, 0xd3, 0x7a, 0xad, 0x2a, 0x32, 0x37, 0x60, 0x3e, 0x4d, 0x0d, 0x0d, - 0xac, 0x9a, 0x5e, 0x9e, 0xd3, 0xef, 0x47, 0x81, 0xf9, 0x21, 0x2c, 0xd1, 0x88, 0x4a, 0x8a, 0x42, - 0xbf, 0x47, 0xd4, 0x01, 0x59, 0xf5, 0x6d, 0x63, 0xa7, 0xb9, 0x77, 0xd3, 0xa1, 0x1d, 0xec, 0xa8, - 0x33, 0x75, 0xb2, 0x93, 0x4c, 0x76, 0x9d, 0x07, 0x5a, 0xe3, 0xa0, 0xfe, 0xe5, 0xd7, 0x5b, 0x53, - 0xde, 0x62, 0x66, 0x97, 0x0a, 0xcd, 0x5b, 0xb0, 0xd0, 0x25, 0x11, 0x11, 0x54, 0xf8, 0x3d, 0x24, - 0x7a, 0xd6, 0xcc, 0xb6, 0xb1, 0xb3, 0xe0, 0x35, 0x33, 0xd9, 0x03, 0x24, 0x7a, 0xe6, 0x16, 0x34, - 0x3b, 0x34, 0x42, 0x7c, 0x98, 0x6a, 0xcc, 0x6a, 0x0d, 0x48, 0x45, 0x5a, 0xa1, 0x0d, 0x20, 0x62, - 0xf4, 0x3c, 0xf2, 0x55, 0x01, 0x58, 0x73, 0x59, 0x20, 0x69, 0xf2, 0x9d, 0x3c, 0xf9, 0xce, 0x59, - 0x5e, 0x1d, 0x07, 0xf3, 0x2a, 0x90, 0xcf, 0xbe, 0xd9, 0x32, 0xbc, 0x86, 0xb6, 0x53, 0x2b, 0xe6, - 0x23, 0x58, 0x19, 0x44, 0x1d, 0x16, 0x05, 0x34, 0xea, 0xfa, 0x31, 0xe1, 0x94, 0x05, 0xd6, 0xbc, - 0x86, 0xda, 0xb8, 0x04, 0x75, 0x98, 0xd5, 0x51, 0x8a, 0xf4, 0xb9, 0x42, 0x5a, 0x2e, 0x8c, 0x4f, - 0xb4, 0xad, 0xf9, 0x09, 0x98, 0x18, 0x27, 0x3a, 0x24, 0x36, 0x90, 0x39, 0x62, 0x63, 0x72, 0xc4, - 0x15, 0x8c, 0x93, 0xb3, 0xd4, 0x3a, 0x83, 0xfc, 0x05, 0xdc, 0x90, 0x1c, 0x45, 0xe2, 0x29, 0xe1, - 0xe3, 0xb8, 0x30, 0x39, 0xee, 0x1b, 0x39, 0xc6, 0x28, 0xf8, 0x03, 0xd8, 0xc6, 0x59, 0x01, 0xf9, - 0x9c, 0x04, 0x54, 0x48, 0x4e, 0x3b, 0x03, 0x65, 0xeb, 0x3f, 0xe5, 0x08, 0xeb, 0x1a, 0x69, 0xea, - 0x22, 0x68, 0xe5, 0x7a, 0xde, 0x88, 0xda, 0x07, 0x99, 0x96, 0xf9, 0x18, 0xbe, 0xd3, 0x09, 0x19, - 0xbe, 0x10, 0x2a, 0x38, 0x7f, 0x04, 0x49, 0xbb, 0xee, 0x53, 0x21, 0x14, 0xda, 0xc2, 0xb6, 0xb1, - 0x53, 0xf3, 0x6e, 0xa5, 0xba, 0x27, 0x84, 0x1f, 0x56, 0x34, 0xcf, 0x2a, 0x8a, 0xe6, 0x1d, 0x30, - 0x7b, 0x54, 0x48, 0xc6, 0x29, 0x46, 0xa1, 0x4f, 0x22, 0xc9, 0x29, 0x11, 0xd6, 0xa2, 0x36, 0x5f, - 0x2d, 0x57, 0xee, 0xa7, 0x0b, 0xe6, 0x43, 0xb8, 0x75, 0xad, 0x53, 0x1f, 0xf7, 0x50, 0x14, 0x91, - 0xd0, 0x5a, 0xd2, 0x5b, 0xd9, 0x0a, 0xae, 0xf1, 0xd9, 0x4e, 0xd5, 0xcc, 0x35, 0x98, 0x91, 0x2c, - 0xf6, 0x1f, 0x59, 0xcb, 0xdb, 0xc6, 0xce, 0xa2, 0x57, 0x97, 0x2c, 0x7e, 0x74, 0x6f, 0xfe, 0x57, - 0x5f, 0x6c, 0x4d, 0x7d, 0xfe, 0xc5, 0xd6, 0x94, 0xfd, 0x17, 0x03, 0x6e, 0xb4, 0x8b, 0xd3, 0xe8, - 0xb3, 0x04, 0x85, 0xff, 0xcb, 0xae, 0xdb, 0x87, 0x86, 0x50, 0xe1, 0xe8, 0x3a, 0xaf, 0xbf, 0x46, - 0x9d, 0xcf, 0x2b, 0x33, 0xb5, 0x60, 0xff, 0xce, 0x80, 0xf5, 0xfb, 0xcf, 0x06, 0x34, 0x61, 0x18, - 0xfd, 0x57, 0x48, 0xe2, 0x18, 0x16, 0x49, 0x05, 0x4f, 0x58, 0xb5, 0xed, 0xda, 0x4e, 0x73, 0xef, - 0x6d, 0x27, 0x25, 0x31, 0xa7, 0xe0, 0xb6, 0x8c, 0xc8, 0x9c, 0xaa, 0x77, 0x6f, 0xd4, 0xf6, 0xde, - 0xb4, 0x65, 0xd8, 0x7f, 0x30, 0xe0, 0xa6, 0x3a, 0xfe, 0x2e, 0xf1, 0xc8, 0x73, 0xc4, 0x83, 0x43, - 0x12, 0xb1, 0xbe, 0xf8, 0xd6, 0x71, 0xda, 0xb0, 0x18, 0x68, 0x24, 0x5f, 0x32, 0x1f, 0x05, 0x81, - 0x8e, 0x53, 0xeb, 0x28, 0xe1, 0x19, 0xdb, 0x0f, 0x02, 0x73, 0x07, 0x56, 0x4a, 0x1d, 0xae, 0xf2, - 0xa9, 0x8e, 0x59, 0xa9, 0x2d, 0xe5, 0x6a, 0x3a, 0xcb, 0xc4, 0xfe, 0xa7, 0x01, 0x2b, 0x1f, 0x86, - 0xac, 0x83, 0xc2, 0xd3, 0x10, 0x89, 0x9e, 0x2a, 0xbd, 0xa1, 0x4a, 0x0f, 0x27, 0x59, 0xcf, 0xeb, - 0xf0, 0x26, 0x4e, 0x8f, 0x32, 0xd3, 0x2c, 0xf4, 0x3e, 0xac, 0x16, 0x5d, 0x58, 0x54, 0x81, 0xde, - 0xcd, 0xc1, 0xda, 0x8b, 0xaf, 0xb7, 0x96, 0xf3, 0x62, 0x6b, 0xeb, 0x8a, 0x38, 0xf4, 0x96, 0xf1, - 0x88, 0x20, 0x30, 0x5b, 0xd0, 0xa4, 0x1d, 0xec, 0x0b, 0xf2, 0xcc, 0x8f, 0x06, 0x7d, 0x5d, 0x40, - 0x75, 0xaf, 0x41, 0x3b, 0xf8, 0x94, 0x3c, 0x7b, 0x34, 0xe8, 0x9b, 0xef, 0xc2, 0x9b, 0xf9, 0x00, - 0xf6, 0x13, 0x14, 0xfa, 0xca, 0x5e, 0x1d, 0x07, 0xd7, 0xf5, 0xb4, 0xe0, 0xad, 0xe5, 0xab, 0xe7, - 0x28, 0x54, 0xce, 0xf6, 0x83, 0x80, 0xdb, 0x2f, 0x67, 0x60, 0xf6, 0x04, 0x71, 0xd4, 0x17, 0xe6, - 0x19, 0x2c, 0x4b, 0xd2, 0x8f, 0x43, 0x24, 0x89, 0x9f, 0x32, 0x7c, 0xb6, 0xd3, 0xdb, 0x9a, 0xf9, - 0xab, 0xc3, 0xd2, 0xa9, 0x8c, 0xc7, 0x64, 0xd7, 0x69, 0x6b, 0xe9, 0xa9, 0x44, 0x92, 0x78, 0x4b, - 0x39, 0x46, 0x2a, 0x34, 0xef, 0x82, 0x25, 0xf9, 0x40, 0xc8, 0x92, 0x7b, 0x4b, 0xd2, 0x49, 0x73, - 0xf9, 0x66, 0xbe, 0x9e, 0xd2, 0x55, 0x41, 0x36, 0x57, 0xd3, 0x6c, 0xed, 0xdb, 0xd0, 0xec, 0x29, - 0xac, 0xa9, 0x19, 0x35, 0x8e, 0x59, 0x9f, 0x1c, 0x73, 0x55, 0xd9, 0x8f, 0x82, 0x7e, 0x02, 0x66, - 0x22, 0xf0, 0x38, 0xe6, 0xcc, 0x6b, 0xc4, 0x99, 0x08, 0x3c, 0x0a, 0x19, 0xc0, 0xa6, 0x50, 0xc5, - 0xe7, 0xf7, 0x89, 0xd4, 0xa4, 0x1d, 0x87, 0x24, 0xa2, 0xa2, 0x97, 0x83, 0xcf, 0x4e, 0x0e, 0xbe, - 0xa1, 0x81, 0x3e, 0x56, 0x38, 0x5e, 0x0e, 0x93, 0x79, 0x69, 0x43, 0xeb, 0x6a, 0x2f, 0x45, 0x82, - 0xe6, 0x74, 0x82, 0xfe, 0xef, 0x0a, 0x88, 0x22, 0x4b, 0x02, 0xde, 0xa9, 0x0c, 0x17, 0xd5, 0xd5, - 0xbe, 0x6e, 0x28, 0x9f, 0x93, 0xae, 0x62, 0x60, 0x94, 0xce, 0x19, 0x42, 0x8a, 0x01, 0x99, 0xb1, - 0x87, 0xba, 0x02, 0x15, 0xcc, 0xd1, 0x66, 0x34, 0xca, 0x6e, 0x11, 0x76, 0x39, 0x83, 0x0a, 0x8e, - 0xf0, 0x2a, 0x58, 0x1f, 0x10, 0xa2, 0xba, 0xb9, 0x32, 0x87, 0x48, 0xcc, 0x70, 0x4f, 0xcf, 0xc9, - 0x9a, 0xb7, 0x54, 0xcc, 0x9c, 0xfb, 0x4a, 0xfa, 0xb0, 0x3e, 0x3f, 0xbf, 0xd2, 0xb0, 0xbf, 0x07, - 0x0d, 0xdd, 0xcc, 0xfb, 0xf8, 0x42, 0x98, 0x9b, 0xd0, 0x50, 0x5d, 0x41, 0x84, 0x20, 0xc2, 0x32, - 0x34, 0x07, 0x94, 0x02, 0x5b, 0xc2, 0xc6, 0x75, 0xb7, 0x2d, 0x61, 0x3e, 0x81, 0xb9, 0x98, 0xe8, - 0xab, 0x80, 0x36, 0x6c, 0xee, 0xbd, 0xe7, 0x4c, 0x70, 0x17, 0x76, 0xae, 0x03, 0xf4, 0x72, 0x34, - 0x9b, 0x97, 0x77, 0xbc, 0xb1, 0x61, 0x23, 0xcc, 0xf3, 0x71, 0xa7, 0x3f, 0x79, 0x2d, 0xa7, 0x63, - 0x78, 0xa5, 0xcf, 0xdb, 0xd0, 0xdc, 0x4f, 0xb7, 0xfd, 0x11, 0x15, 0xf2, 0xf2, 0xb1, 0x2c, 0x54, - 0x8f, 0xe5, 0x21, 0x2c, 0x65, 0x83, 0xf3, 0x8c, 0x69, 0x42, 0x32, 0xff, 0x1f, 0x20, 0x9b, 0xb8, - 0x8a, 0xc8, 0x52, 0xca, 0x6e, 0x64, 0x92, 0xa3, 0x60, 0x64, 0xd6, 0x4d, 0x8f, 0xcc, 0x3a, 0xdb, - 0x83, 0xe5, 0x73, 0x81, 0x7f, 0x9a, 0xdf, 0xaa, 0x1e, 0xc7, 0xc2, 0x7c, 0x03, 0x66, 0x55, 0x0f, - 0x65, 0x40, 0x75, 0x6f, 0x26, 0x11, 0xf8, 0x48, 0xb3, 0x76, 0x79, 0x73, 0x63, 0xb1, 0x4f, 0x03, - 0x61, 0x4d, 0x6f, 0xd7, 0x76, 0xea, 0xde, 0xd2, 0xa0, 0x34, 0x3f, 0x0a, 0x84, 0xfd, 0x33, 0x68, - 0x56, 0x00, 0xcd, 0x25, 0x98, 0x2e, 0xb0, 0xa6, 0x69, 0x60, 0xde, 0x83, 0x8d, 0x12, 0x68, 0x94, - 0x86, 0x53, 0xc4, 0x86, 0x77, 0xa3, 0x50, 0x18, 0x61, 0x62, 0x61, 0x3f, 0x86, 0xf5, 0xa3, 0xb2, - 0xe9, 0x0b, 0x92, 0x1f, 0xd9, 0xa1, 0x31, 0x3a, 0xcd, 0x37, 0xa1, 0x51, 0xfc, 0x62, 0xd1, 0xbb, - 0xaf, 0x7b, 0xa5, 0xc0, 0xee, 0xc3, 0xca, 0xb9, 0xc0, 0xa7, 0x24, 0x0a, 0x4a, 0xb0, 0x6b, 0x0e, - 0xe0, 0x60, 0x1c, 0x68, 0xe2, 0xeb, 0x6f, 0xe9, 0x8e, 0xc1, 0xc6, 0x39, 0x0a, 0x69, 0x80, 0x24, - 0xe3, 0xa7, 0x44, 0xa6, 0x03, 0xf8, 0x04, 0xe1, 0x0b, 0x22, 0x85, 0xe9, 0x41, 0x3d, 0xa4, 0x42, - 0x66, 0x95, 0x75, 0xf7, 0xda, 0xca, 0x4a, 0x76, 0x9d, 0xeb, 0x40, 0x0e, 0x91, 0x44, 0x59, 0xef, - 0x6a, 0x2c, 0xfb, 0xbb, 0xb0, 0xf6, 0x31, 0x92, 0x03, 0x4e, 0x82, 0x91, 0x1c, 0xaf, 0x40, 0x4d, - 0xe5, 0xcf, 0xd0, 0xf9, 0x53, 0x8f, 0xea, 0x3e, 0x60, 0xdd, 0xff, 0x34, 0x66, 0x5c, 0x92, 0xe0, - 0xd2, 0x89, 0xbc, 0xe2, 0x78, 0x2f, 0x60, 0x4d, 0x1d, 0x96, 0x20, 0x51, 0xe0, 0x17, 0xfb, 0x4c, - 0xf3, 0xd8, 0xdc, 0xfb, 0xf1, 0x44, 0xdd, 0x31, 0xee, 0x2e, 0xdb, 0xc0, 0x6a, 0x32, 0x26, 0x17, - 0xf6, 0x6f, 0x0c, 0xb0, 0x8e, 0xc9, 0x70, 0x5f, 0x08, 0xda, 0x8d, 0xfa, 0x24, 0x92, 0x8a, 0x03, - 0x11, 0x26, 0xea, 0xd1, 0x7c, 0x0b, 0x16, 0x8b, 0x99, 0xab, 0x47, 0xad, 0xa1, 0x47, 0xed, 0x42, - 0x2e, 0x54, 0x0d, 0x66, 0xde, 0x03, 0x88, 0x39, 0x49, 0x7c, 0xec, 0x5f, 0x90, 0x61, 0x96, 0xc5, - 0xcd, 0xea, 0x08, 0x4d, 0x7f, 0x4f, 0x3a, 0x27, 0x83, 0x4e, 0x48, 0xf1, 0x31, 0x19, 0x7a, 0xf3, - 0x4a, 0xbf, 0x7d, 0x4c, 0x86, 0xea, 0x4e, 0x14, 0xb3, 0xe7, 0x84, 0xeb, 0xb9, 0x57, 0xf3, 0xd2, - 0x17, 0xfb, 0xb7, 0x06, 0xdc, 0x28, 0xd2, 0x91, 0x97, 0xeb, 0xc9, 0xa0, 0xa3, 0x2c, 0x5e, 0x71, - 0x6e, 0x97, 0xa2, 0x9d, 0xbe, 0x22, 0xda, 0xf7, 0x61, 0xa1, 0x68, 0x10, 0x15, 0x6f, 0x6d, 0x82, - 0x78, 0x9b, 0xb9, 0xc5, 0x31, 0x19, 0xda, 0xbf, 0xac, 0xc4, 0x76, 0x30, 0xac, 0x70, 0x1f, 0xff, - 0x0f, 0xb1, 0x15, 0x6e, 0xab, 0xb1, 0xe1, 0xaa, 0xfd, 0xa5, 0x0d, 0xd4, 0x2e, 0x6f, 0xc0, 0xfe, - 0xbd, 0x01, 0xeb, 0x55, 0xaf, 0xe2, 0x8c, 0x9d, 0xf0, 0x41, 0x44, 0x5e, 0xe5, 0xbd, 0x6c, 0xbf, - 0xe9, 0x6a, 0xfb, 0x3d, 0x81, 0xa5, 0x91, 0xa0, 0x44, 0x76, 0x1a, 0x3f, 0x9c, 0xa8, 0xc6, 0x2a, - 0xec, 0xea, 0x2d, 0x56, 0xf7, 0x21, 0xec, 0x3f, 0x1a, 0xb0, 0x9a, 0xc7, 0x58, 0x1c, 0x96, 0xf9, - 0x03, 0x30, 0x8b, 0xed, 0x95, 0xb7, 0xb7, 0xb4, 0xa4, 0x56, 0xf2, 0x95, 0xfc, 0xea, 0x56, 0x96, - 0xc6, 0x74, 0xa5, 0x34, 0xcc, 0x8f, 0x60, 0xad, 0x08, 0x39, 0xd6, 0x09, 0x9a, 0x38, 0x8b, 0xc5, - 0xfd, 0xb4, 0x10, 0xd9, 0xbf, 0x36, 0xca, 0x71, 0x98, 0xce, 0x63, 0xb1, 0x1f, 0x86, 0xd9, 0xa5, - 0xde, 0x8c, 0x61, 0x2e, 0x1d, 0xf9, 0x22, 0xe3, 0x8f, 0xcd, 0x2b, 0x87, 0xfb, 0x21, 0xc1, 0x7a, - 0xbe, 0xdf, 0x55, 0x2d, 0xf6, 0xe7, 0x6f, 0xb6, 0x6e, 0x77, 0xa9, 0xec, 0x0d, 0x3a, 0x0e, 0x66, - 0x7d, 0x37, 0xfb, 0x1e, 0x92, 0xfe, 0xbb, 0x23, 0x82, 0x0b, 0x57, 0x0e, 0x63, 0x22, 0x72, 0x1b, - 0xf1, 0xa7, 0x7f, 0xfc, 0xf5, 0xfb, 0x86, 0x97, 0xbb, 0x39, 0x78, 0xf2, 0xe5, 0x8b, 0x96, 0xf1, - 0xd5, 0x8b, 0x96, 0xf1, 0xf7, 0x17, 0x2d, 0xe3, 0xb3, 0x97, 0xad, 0xa9, 0xaf, 0x5e, 0xb6, 0xa6, - 0xfe, 0xf6, 0xb2, 0x35, 0xf5, 0xf3, 0xf7, 0x2e, 0x83, 0x96, 0x39, 0xba, 0x53, 0x7c, 0x81, 0x4a, - 0x7e, 0xe4, 0x7e, 0x3a, 0xfa, 0x7d, 0x4b, 0xfb, 0xeb, 0xcc, 0x6a, 0x36, 0x7d, 0xf7, 0xdf, 0x01, - 0x00, 0x00, 0xff, 0xff, 0x8f, 0xff, 0x87, 0xe4, 0x10, 0x13, 0x00, 0x00, + // 1919 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4f, 0x6f, 0x1b, 0xc7, + 0x15, 0xd7, 0x8a, 0x94, 0x45, 0x0e, 0xf5, 0x77, 0xa4, 0xc4, 0x2b, 0x55, 0xa5, 0xe8, 0x4d, 0x93, + 0xaa, 0x71, 0xbd, 0x1b, 0x29, 0x2d, 0x60, 0x18, 0x0d, 0x02, 0x89, 0x72, 0x62, 0x59, 0x89, 0xcd, + 0xac, 0x54, 0x19, 0x6d, 0x0f, 0x8b, 0xe1, 0xec, 0x98, 0x1c, 0x68, 0xb9, 0xb3, 0x9e, 0x19, 0xae, + 0xc2, 0x4b, 0xcf, 0x3d, 0xb4, 0x40, 0x7a, 0x0b, 0x7a, 0x69, 0x5a, 0xa0, 0x40, 0xd1, 0x4b, 0xfb, + 0x31, 0x72, 0xcc, 0xb1, 0xa7, 0xa4, 0xb0, 0x0f, 0x3d, 0xf4, 0x4b, 0x14, 0x33, 0xfb, 0x97, 0x94, + 0xe4, 0xd2, 0x48, 0x73, 0x91, 0x76, 0xdf, 0xbc, 0xf7, 0x7b, 0x6f, 0xe6, 0xbd, 0x79, 0xbf, 0xc7, + 0x05, 0x7b, 0x34, 0x94, 0x84, 0xe3, 0x3e, 0xa2, 0xa1, 0x27, 0x08, 0x1e, 0x72, 0x2a, 0x47, 0x0e, + 0xc6, 0xb1, 0x13, 0x71, 0x16, 0x53, 0x9f, 0x70, 0x27, 0xde, 0xcd, 0x9f, 0xed, 0x88, 0x33, 0xc9, + 0xe0, 0x1b, 0x57, 0xd8, 0xd8, 0x18, 0xc7, 0x76, 0xae, 0x17, 0xef, 0x6e, 0xbe, 0x79, 0x1d, 0x70, + 0xbc, 0xeb, 0x5c, 0x50, 0x4e, 0x12, 0xac, 0xcd, 0xf5, 0x1e, 0xeb, 0x31, 0xfd, 0xe8, 0xa8, 0xa7, + 0x54, 0xba, 0xdd, 0x63, 0xac, 0x17, 0x10, 0x47, 0xbf, 0x75, 0x87, 0x4f, 0x1d, 0x49, 0x07, 0x44, + 0x48, 0x34, 0x88, 0x52, 0x85, 0xe6, 0xa4, 0x82, 0x3f, 0xe4, 0x48, 0x52, 0x16, 0x66, 0x00, 0xb4, + 0x8b, 0x1d, 0xcc, 0x38, 0x71, 0x70, 0x40, 0x49, 0x28, 0x95, 0xd7, 0xe4, 0x29, 0x55, 0x70, 0x94, + 0x42, 0x40, 0x7b, 0x7d, 0x99, 0x88, 0x85, 0x23, 0x49, 0xe8, 0x13, 0x3e, 0xa0, 0x89, 0x72, 0xf1, + 0x96, 0x1a, 0x6c, 0x95, 0xd6, 0x31, 0x1f, 0x45, 0x92, 0x39, 0xe7, 0x64, 0x24, 0xd2, 0xd5, 0xb7, + 0x30, 0x13, 0x03, 0x26, 0x1c, 0xa2, 0xf6, 0x1f, 0x62, 0xe2, 0xc4, 0xbb, 0x5d, 0x22, 0xd1, 0x6e, + 0x2e, 0xc8, 0xe2, 0x4e, 0xf5, 0xba, 0x48, 0x14, 0x3a, 0x98, 0xd1, 0x2c, 0xee, 0x55, 0x34, 0xa0, + 0x21, 0x73, 0xf4, 0xdf, 0x44, 0x64, 0xfd, 0xb6, 0x06, 0xcc, 0x36, 0x0b, 0xc5, 0x70, 0x40, 0xf8, + 0xbe, 0xef, 0x53, 0xb5, 0xcb, 0x0e, 0x67, 0x11, 0x13, 0x28, 0x80, 0xeb, 0x60, 0x4e, 0x52, 0x19, + 0x10, 0xd3, 0x68, 0x19, 0x3b, 0x75, 0x37, 0x79, 0x81, 0x2d, 0xd0, 0xf0, 0x89, 0xc0, 0x9c, 0x46, + 0x4a, 0xd9, 0x9c, 0xd5, 0x6b, 0x65, 0x11, 0xdc, 0x00, 0xb5, 0x24, 0x35, 0xd4, 0x37, 0x2b, 0x7a, + 0x79, 0x5e, 0xbf, 0x1f, 0xf9, 0xf0, 0x43, 0xb0, 0x44, 0x43, 0x2a, 0x29, 0x0a, 0xbc, 0x3e, 0x51, + 0x07, 0x64, 0x56, 0x5b, 0xc6, 0x4e, 0x63, 0x6f, 0xd3, 0xa6, 0x5d, 0x6c, 0xab, 0x33, 0xb5, 0xd3, + 0x93, 0x8c, 0x77, 0xed, 0x07, 0x5a, 0xe3, 0xa0, 0xfa, 0xe5, 0xd7, 0xdb, 0x33, 0xee, 0x62, 0x6a, + 0x97, 0x08, 0xe1, 0x2d, 0xb0, 0xd0, 0x23, 0x21, 0x11, 0x54, 0x78, 0x7d, 0x24, 0xfa, 0xe6, 0x5c, + 0xcb, 0xd8, 0x59, 0x70, 0x1b, 0xa9, 0xec, 0x01, 0x12, 0x7d, 0xb8, 0x0d, 0x1a, 0x5d, 0x1a, 0x22, + 0x3e, 0x4a, 0x34, 0x6e, 0x68, 0x0d, 0x90, 0x88, 0xb4, 0x42, 0x1b, 0x00, 0x11, 0xa1, 0x8b, 0xd0, + 0x53, 0x05, 0x60, 0xce, 0xa7, 0x81, 0x24, 0xc9, 0xb7, 0xb3, 0xe4, 0xdb, 0xa7, 0x59, 0x75, 0x1c, + 0xd4, 0x54, 0x20, 0x9f, 0x7d, 0xb3, 0x6d, 0xb8, 0x75, 0x6d, 0xa7, 0x56, 0xe0, 0x23, 0xb0, 0x32, + 0x0c, 0xbb, 0x2c, 0xf4, 0x69, 0xd8, 0xf3, 0x22, 0xc2, 0x29, 0xf3, 0xcd, 0x9a, 0x86, 0xda, 0xb8, + 0x04, 0x75, 0x98, 0xd6, 0x51, 0x82, 0xf4, 0xb9, 0x42, 0x5a, 0xce, 0x8d, 0x3b, 0xda, 0x16, 0x7e, + 0x02, 0x20, 0xc6, 0xb1, 0x0e, 0x89, 0x0d, 0x65, 0x86, 0x58, 0x9f, 0x1e, 0x71, 0x05, 0xe3, 0xf8, + 0x34, 0xb1, 0x4e, 0x21, 0x7f, 0x05, 0x6e, 0x4a, 0x8e, 0x42, 0xf1, 0x94, 0xf0, 0x49, 0x5c, 0x30, + 0x3d, 0xee, 0x6b, 0x19, 0xc6, 0x38, 0xf8, 0x03, 0xd0, 0xc2, 0x69, 0x01, 0x79, 0x9c, 0xf8, 0x54, + 0x48, 0x4e, 0xbb, 0x43, 0x65, 0xeb, 0x3d, 0xe5, 0x08, 0xeb, 0x1a, 0x69, 0xe8, 0x22, 0x68, 0x66, + 0x7a, 0xee, 0x98, 0xda, 0x07, 0xa9, 0x16, 0x7c, 0x0c, 0x7e, 0xd0, 0x0d, 0x18, 0x3e, 0x17, 0x2a, + 0x38, 0x6f, 0x0c, 0x49, 0xbb, 0x1e, 0x50, 0x21, 0x14, 0xda, 0x42, 0xcb, 0xd8, 0xa9, 0xb8, 0xb7, + 0x12, 0xdd, 0x0e, 0xe1, 0x87, 0x25, 0xcd, 0xd3, 0x92, 0x22, 0xbc, 0x03, 0x60, 0x9f, 0x0a, 0xc9, + 0x38, 0xc5, 0x28, 0xf0, 0x48, 0x28, 0x39, 0x25, 0xc2, 0x5c, 0xd4, 0xe6, 0xab, 0xc5, 0xca, 0xfd, + 0x64, 0x01, 0x3e, 0x04, 0xb7, 0xae, 0x75, 0xea, 0xe1, 0x3e, 0x0a, 0x43, 0x12, 0x98, 0x4b, 0x7a, + 0x2b, 0xdb, 0xfe, 0x35, 0x3e, 0xdb, 0x89, 0x1a, 0x5c, 0x03, 0x73, 0x92, 0x45, 0xde, 0x23, 0x73, + 0xb9, 0x65, 0xec, 0x2c, 0xba, 0x55, 0xc9, 0xa2, 0x47, 0xf0, 0x1d, 0xb0, 0x1e, 0xa3, 0x80, 0xfa, + 0x48, 0x32, 0x2e, 0xbc, 0x88, 0x5d, 0x10, 0xee, 0x61, 0x14, 0x99, 0x2b, 0x5a, 0x07, 0x16, 0x6b, + 0x1d, 0xb5, 0xd4, 0x46, 0x11, 0x7c, 0x1b, 0xac, 0xe6, 0x52, 0x4f, 0x10, 0xa9, 0xd5, 0x57, 0xb5, + 0xfa, 0x72, 0xbe, 0x70, 0x42, 0xa4, 0xd2, 0xdd, 0x02, 0x75, 0x14, 0x04, 0xec, 0x22, 0xa0, 0x42, + 0x9a, 0xb0, 0x55, 0xd9, 0xa9, 0xbb, 0x85, 0x00, 0x6e, 0x82, 0x9a, 0x4f, 0xc2, 0x91, 0x5e, 0x5c, + 0xd3, 0x8b, 0xf9, 0xfb, 0xbd, 0xda, 0x6f, 0xbe, 0xd8, 0x9e, 0xf9, 0xfc, 0x8b, 0xed, 0x19, 0xeb, + 0xef, 0x06, 0xb8, 0xd9, 0xce, 0xb3, 0x34, 0x60, 0x31, 0x0a, 0xbe, 0xcb, 0x6e, 0xb0, 0x0f, 0xea, + 0x42, 0x1d, 0x93, 0xbe, 0x7f, 0xd5, 0x57, 0xb8, 0x7f, 0x35, 0x65, 0xa6, 0x16, 0xac, 0x3f, 0x1a, + 0x60, 0xfd, 0xfe, 0xb3, 0x21, 0x8d, 0x19, 0x46, 0xff, 0x97, 0xe6, 0x75, 0x0c, 0x16, 0x49, 0x09, + 0x4f, 0x98, 0x95, 0x56, 0x65, 0xa7, 0xb1, 0xf7, 0xa6, 0x9d, 0x34, 0x57, 0x3b, 0xef, 0xb9, 0x69, + 0x83, 0xb5, 0xcb, 0xde, 0xdd, 0x71, 0xdb, 0x7b, 0xb3, 0xa6, 0x61, 0xfd, 0xd9, 0x00, 0x9b, 0xaa, + 0x2c, 0x7a, 0xc4, 0x25, 0x17, 0x88, 0xfb, 0x87, 0x24, 0x64, 0x03, 0xf1, 0xad, 0xe3, 0xb4, 0xc0, + 0xa2, 0xaf, 0x91, 0x3c, 0xc9, 0x3c, 0xe4, 0xfb, 0x3a, 0x4e, 0xad, 0xa3, 0x84, 0xa7, 0x6c, 0xdf, + 0xf7, 0xe1, 0x0e, 0x58, 0x29, 0x74, 0xb8, 0xca, 0xa7, 0x3a, 0x66, 0xa5, 0xb6, 0x94, 0xa9, 0xe9, + 0x2c, 0x13, 0xeb, 0x3f, 0x06, 0x58, 0xf9, 0x30, 0x60, 0x5d, 0x14, 0x9c, 0x04, 0x48, 0xf4, 0xd5, + 0x95, 0x18, 0xa9, 0xf4, 0x70, 0x92, 0xf6, 0x22, 0x1d, 0xde, 0xd4, 0xe9, 0x51, 0x66, 0xba, 0x3b, + 0xbe, 0x0f, 0x56, 0xf3, 0xee, 0x90, 0x57, 0x81, 0xde, 0xcd, 0xc1, 0xda, 0xf3, 0xaf, 0xb7, 0x97, + 0xb3, 0x62, 0x6b, 0xeb, 0x8a, 0x38, 0x74, 0x97, 0xf1, 0x98, 0xc0, 0x87, 0x4d, 0xd0, 0xa0, 0x5d, + 0xec, 0x09, 0xf2, 0xcc, 0x0b, 0x87, 0x03, 0x5d, 0x40, 0x55, 0xb7, 0x4e, 0xbb, 0xf8, 0x84, 0x3c, + 0x7b, 0x34, 0x1c, 0xc0, 0x77, 0xc1, 0xeb, 0xd9, 0x60, 0xe0, 0xc5, 0x28, 0xf0, 0x94, 0xbd, 0x3a, + 0x0e, 0xae, 0xeb, 0x69, 0xc1, 0x5d, 0xcb, 0x56, 0xcf, 0x50, 0xa0, 0x9c, 0xed, 0xfb, 0x3e, 0xb7, + 0x5e, 0xcc, 0x81, 0x1b, 0x1d, 0xc4, 0xd1, 0x40, 0xc0, 0x53, 0xb0, 0x2c, 0xc9, 0x20, 0x0a, 0x90, + 0x24, 0x5e, 0xc2, 0x3c, 0xe9, 0x4e, 0x6f, 0x6b, 0x46, 0x2a, 0x93, 0xb8, 0x5d, 0xa2, 0xed, 0x78, + 0xd7, 0x6e, 0x6b, 0xe9, 0x89, 0x44, 0x92, 0xb8, 0x4b, 0x19, 0x46, 0x22, 0x84, 0x77, 0x81, 0x29, + 0xf9, 0x50, 0xc8, 0x82, 0x13, 0x8a, 0x66, 0x98, 0xe4, 0xf2, 0xf5, 0x6c, 0x3d, 0x69, 0xa3, 0x79, + 0x13, 0xbc, 0xba, 0xfd, 0x57, 0xbe, 0x4d, 0xfb, 0x3f, 0x01, 0x6b, 0x8a, 0x3b, 0x27, 0x31, 0xab, + 0xd3, 0x63, 0xae, 0x2a, 0xfb, 0x71, 0xd0, 0x4f, 0x00, 0x8c, 0x05, 0x9e, 0xc4, 0x9c, 0x7b, 0x85, + 0x38, 0x63, 0x81, 0xc7, 0x21, 0x7d, 0xb0, 0x25, 0x54, 0xf1, 0x79, 0x03, 0x22, 0x35, 0x99, 0x44, + 0x01, 0x09, 0xa9, 0xe8, 0x67, 0xe0, 0x37, 0xa6, 0x07, 0xdf, 0xd0, 0x40, 0x1f, 0x2b, 0x1c, 0x37, + 0x83, 0x49, 0xbd, 0xb4, 0x41, 0xf3, 0x6a, 0x2f, 0x79, 0x82, 0xe6, 0x75, 0x82, 0xbe, 0x77, 0x05, + 0x44, 0x9e, 0x25, 0x01, 0xde, 0x2a, 0x91, 0x9e, 0xba, 0xd5, 0x9e, 0xbe, 0x50, 0x1e, 0x27, 0x3d, + 0xc5, 0x0c, 0x28, 0xe1, 0x3f, 0x42, 0x72, 0xe2, 0x4e, 0xbb, 0x87, 0x1a, 0xcd, 0xf2, 0xce, 0xd1, + 0x66, 0x34, 0x4c, 0xa7, 0x1b, 0xab, 0xe0, 0xc6, 0xbc, 0x47, 0xb8, 0x25, 0xac, 0x0f, 0x08, 0x51, + 0xb7, 0xb9, 0xc4, 0x8f, 0x24, 0x62, 0xb8, 0xaf, 0xf9, 0xbb, 0xe2, 0x2e, 0xe5, 0x5c, 0x78, 0x5f, + 0x49, 0x1f, 0x56, 0x6b, 0xb5, 0x95, 0xba, 0xf5, 0x23, 0x50, 0xd7, 0x97, 0x79, 0x1f, 0x9f, 0x0b, + 0xcd, 0x0e, 0xbe, 0xcf, 0x89, 0x10, 0x44, 0x98, 0x46, 0xca, 0x0e, 0x99, 0xc0, 0x92, 0x60, 0xe3, + 0xba, 0x29, 0x50, 0xc0, 0x27, 0x60, 0x3e, 0x22, 0x7a, 0x44, 0xd1, 0x86, 0x8d, 0xbd, 0xf7, 0xec, + 0x29, 0x66, 0x74, 0xfb, 0x3a, 0x40, 0x37, 0x43, 0xb3, 0x78, 0x31, 0x7b, 0x4e, 0x90, 0x8d, 0x80, + 0x67, 0x93, 0x4e, 0x7f, 0xf6, 0x4a, 0x4e, 0x27, 0xf0, 0x0a, 0x9f, 0xb7, 0x41, 0x63, 0x3f, 0xd9, + 0xf6, 0x47, 0x8a, 0x16, 0x2f, 0x1d, 0xcb, 0x42, 0xf9, 0x58, 0x1e, 0x82, 0xa5, 0x94, 0xd0, 0x4f, + 0x99, 0x6e, 0x48, 0xf0, 0xfb, 0x00, 0xa4, 0x93, 0x80, 0x6a, 0x64, 0x49, 0xcb, 0xae, 0xa7, 0x92, + 0x23, 0x7f, 0x8c, 0xeb, 0x66, 0xc7, 0xb8, 0xce, 0x72, 0xc1, 0xf2, 0x99, 0xc0, 0x3f, 0xcf, 0xa6, + 0xbd, 0xc7, 0x91, 0x80, 0xaf, 0x81, 0x1b, 0xea, 0x0e, 0xa5, 0x40, 0x55, 0x77, 0x2e, 0x16, 0xf8, + 0x48, 0x77, 0xed, 0x62, 0xa2, 0x64, 0x91, 0x47, 0x7d, 0x61, 0xce, 0xb6, 0x2a, 0x3b, 0x55, 0x77, + 0x69, 0x58, 0x98, 0x1f, 0xf9, 0xc2, 0xfa, 0x05, 0x68, 0x94, 0x00, 0xe1, 0x12, 0x98, 0xcd, 0xb1, + 0x66, 0xa9, 0x0f, 0xef, 0x81, 0x8d, 0x02, 0x68, 0xbc, 0x0d, 0x27, 0x88, 0x75, 0xf7, 0x66, 0xae, + 0x30, 0xd6, 0x89, 0x85, 0xf5, 0x18, 0xac, 0x1f, 0x15, 0x97, 0x3e, 0x6f, 0xf2, 0x63, 0x3b, 0x34, + 0xc6, 0xd9, 0x7c, 0x0b, 0xd4, 0xf3, 0x5f, 0x52, 0x7a, 0xf7, 0x55, 0xb7, 0x10, 0x58, 0x03, 0xb0, + 0x72, 0x26, 0xf0, 0x09, 0x09, 0xfd, 0x02, 0xec, 0x9a, 0x03, 0x38, 0x98, 0x04, 0x9a, 0x7a, 0x2c, + 0x2f, 0xdc, 0x31, 0xb0, 0x71, 0x56, 0x1e, 0x90, 0x34, 0x01, 0x77, 0x10, 0x3e, 0x27, 0x52, 0x40, + 0x17, 0x54, 0xf5, 0x20, 0x94, 0x54, 0xd6, 0xdd, 0x6b, 0x2b, 0x2b, 0xde, 0xb5, 0xaf, 0x03, 0x39, + 0x44, 0x12, 0xa5, 0x77, 0x57, 0x63, 0x59, 0x3f, 0x04, 0x6b, 0x1f, 0x23, 0x39, 0xe4, 0xc4, 0x1f, + 0xcb, 0xf1, 0x0a, 0xa8, 0xa8, 0xfc, 0x19, 0x3a, 0x7f, 0xea, 0x51, 0xcd, 0x03, 0xe6, 0xfd, 0x4f, + 0x23, 0xc6, 0x25, 0xf1, 0x2f, 0x9d, 0xc8, 0x4b, 0x8e, 0xf7, 0x1c, 0xac, 0xa9, 0xc3, 0x12, 0x24, + 0xf4, 0xbd, 0x7c, 0x9f, 0x49, 0x1e, 0x1b, 0x7b, 0x3f, 0x9d, 0xea, 0x76, 0x4c, 0xba, 0x4b, 0x37, + 0xb0, 0x1a, 0x4f, 0xc8, 0x85, 0xf5, 0x7b, 0x03, 0x98, 0xc7, 0x64, 0xb4, 0x2f, 0x04, 0xed, 0x85, + 0x03, 0x12, 0x4a, 0xd5, 0x03, 0x11, 0x26, 0xea, 0x11, 0xbe, 0x01, 0x16, 0x73, 0xce, 0xd5, 0x54, + 0x6b, 0x68, 0xaa, 0x5d, 0xc8, 0x84, 0xea, 0x82, 0xc1, 0x7b, 0x00, 0x44, 0x9c, 0xc4, 0x1e, 0xf6, + 0xce, 0xc9, 0x28, 0xcd, 0xe2, 0x56, 0x99, 0x42, 0x93, 0xdf, 0xb9, 0x76, 0x67, 0xd8, 0x0d, 0x28, + 0x3e, 0x26, 0x23, 0xb7, 0xa6, 0xf4, 0xdb, 0xc7, 0x64, 0xa4, 0x66, 0x22, 0x3d, 0x1d, 0x6b, 0xde, + 0xab, 0xb8, 0xc9, 0x8b, 0xf5, 0x07, 0x03, 0xdc, 0xcc, 0xd3, 0x91, 0x95, 0x6b, 0x67, 0xd8, 0x55, + 0x16, 0x2f, 0x39, 0xb7, 0x4b, 0xd1, 0xce, 0x5e, 0x11, 0xed, 0xfb, 0x60, 0x21, 0xbf, 0x20, 0x2a, + 0xde, 0xca, 0x14, 0xf1, 0x36, 0x32, 0x8b, 0x63, 0x32, 0xb2, 0x7e, 0x5d, 0x8a, 0xed, 0x60, 0x54, + 0xea, 0x7d, 0xfc, 0x7f, 0xc4, 0x96, 0xbb, 0x2d, 0xc7, 0x86, 0xcb, 0xf6, 0x97, 0x36, 0x50, 0xb9, + 0xbc, 0x01, 0xeb, 0x4f, 0x06, 0x58, 0x2f, 0x7b, 0x15, 0xa7, 0xac, 0xc3, 0x87, 0x21, 0x79, 0x99, + 0xf7, 0xe2, 0xfa, 0xcd, 0x96, 0xaf, 0xdf, 0x13, 0xb0, 0x34, 0x16, 0x94, 0x48, 0x4f, 0xe3, 0x9d, + 0xa9, 0x6a, 0xac, 0xd4, 0x5d, 0xdd, 0xc5, 0xf2, 0x3e, 0x84, 0xf5, 0x17, 0x03, 0xac, 0x66, 0x31, + 0xe6, 0x87, 0x05, 0x7f, 0x0c, 0x60, 0xbe, 0xbd, 0x62, 0x7a, 0x4b, 0x4a, 0x6a, 0x25, 0x5b, 0xc9, + 0x46, 0xb7, 0xa2, 0x34, 0x66, 0x4b, 0xa5, 0x01, 0x3f, 0x02, 0x6b, 0x79, 0xc8, 0x91, 0x4e, 0xd0, + 0xd4, 0x59, 0xcc, 0xe7, 0xd3, 0x5c, 0x64, 0xfd, 0xce, 0x28, 0xe8, 0x30, 0xe1, 0x63, 0xb1, 0x1f, + 0x04, 0xe9, 0x50, 0x0f, 0x23, 0x30, 0x9f, 0x50, 0xbe, 0x48, 0xfb, 0xc7, 0xd6, 0x95, 0xe4, 0x7e, + 0x48, 0xb0, 0xe6, 0xf7, 0xbb, 0xea, 0x8a, 0xfd, 0xed, 0x9b, 0xed, 0xdb, 0x3d, 0x2a, 0xfb, 0xc3, + 0xae, 0x8d, 0xd9, 0xc0, 0x49, 0xbf, 0xd3, 0x24, 0xff, 0xee, 0x08, 0xff, 0xdc, 0x91, 0xa3, 0x88, + 0x88, 0xcc, 0x46, 0xfc, 0xf5, 0xdf, 0xff, 0x78, 0xdb, 0x70, 0x33, 0x37, 0x07, 0x4f, 0xbe, 0x7c, + 0xde, 0x34, 0xbe, 0x7a, 0xde, 0x34, 0xfe, 0xf5, 0xbc, 0x69, 0x7c, 0xf6, 0xa2, 0x39, 0xf3, 0xd5, + 0x8b, 0xe6, 0xcc, 0x3f, 0x5f, 0x34, 0x67, 0x7e, 0xf9, 0xde, 0x65, 0xd0, 0x22, 0x47, 0x77, 0xf2, + 0x2f, 0x63, 0xf1, 0x4f, 0x9c, 0x4f, 0xc7, 0xbf, 0xbb, 0x69, 0x7f, 0xdd, 0x1b, 0xba, 0x9b, 0xbe, + 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xb7, 0x45, 0x0f, 0xa8, 0x13, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -1684,6 +1702,42 @@ func (m *ConsumerAdditionProposal) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + if len(m.Denylist) > 0 { + for iNdEx := len(m.Denylist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Denylist[iNdEx]) + copy(dAtA[i:], m.Denylist[iNdEx]) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Denylist[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x9a + } + } + if len(m.Allowlist) > 0 { + for iNdEx := len(m.Allowlist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Allowlist[iNdEx]) + copy(dAtA[i:], m.Allowlist[iNdEx]) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Allowlist[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x92 + } + } + if m.ValidatorSetCap != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.ValidatorSetCap)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.ValidatorsPowerCap != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.ValidatorsPowerCap)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } if m.Top_N != 0 { i = encodeVarintProvider(dAtA, i, uint64(m.Top_N)) i-- @@ -2883,6 +2937,24 @@ func (m *ConsumerAdditionProposal) Size() (n int) { if m.Top_N != 0 { n += 1 + sovProvider(uint64(m.Top_N)) } + if m.ValidatorsPowerCap != 0 { + n += 2 + sovProvider(uint64(m.ValidatorsPowerCap)) + } + if m.ValidatorSetCap != 0 { + n += 2 + sovProvider(uint64(m.ValidatorSetCap)) + } + if len(m.Allowlist) > 0 { + for _, s := range m.Allowlist { + l = len(s) + n += 2 + l + sovProvider(uint64(l)) + } + } + if len(m.Denylist) > 0 { + for _, s := range m.Denylist { + l = len(s) + n += 2 + l + sovProvider(uint64(l)) + } + } return n } @@ -3813,6 +3885,108 @@ func (m *ConsumerAdditionProposal) Unmarshal(dAtA []byte) error { break } } + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorsPowerCap", wireType) + } + m.ValidatorsPowerCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorsPowerCap |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSetCap", wireType) + } + m.ValidatorSetCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorSetCap |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 18: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allowlist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Allowlist = append(m.Allowlist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 19: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denylist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denylist = append(m.Denylist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index 6ad8667003..ae7d423db4 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -1647,7 +1647,7 @@ type QueryClient interface { QueryAllPairsValConAddrByConsumerChainID(ctx context.Context, in *QueryAllPairsValConAddrByConsumerChainIDRequest, opts ...grpc.CallOption) (*QueryAllPairsValConAddrByConsumerChainIDResponse, error) // QueryParams returns all current values of provider parameters QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) - // QueryConsumerChainOptedInValidators returns a list of validators consensus address + // QueryConsumerChainOptedInValidators returns a list of validators consensus addresses // that opted-in to the given consumer chain QueryConsumerChainOptedInValidators(ctx context.Context, in *QueryConsumerChainOptedInValidatorsRequest, opts ...grpc.CallOption) (*QueryConsumerChainOptedInValidatorsResponse, error) // QueryConsumerChainsValidatorHasToValidate returns a list of consumer chains @@ -1824,7 +1824,7 @@ type QueryServer interface { QueryAllPairsValConAddrByConsumerChainID(context.Context, *QueryAllPairsValConAddrByConsumerChainIDRequest) (*QueryAllPairsValConAddrByConsumerChainIDResponse, error) // QueryParams returns all current values of provider parameters QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) - // QueryConsumerChainOptedInValidators returns a list of validators consensus address + // QueryConsumerChainOptedInValidators returns a list of validators consensus addresses // that opted-in to the given consumer chain QueryConsumerChainOptedInValidators(context.Context, *QueryConsumerChainOptedInValidatorsRequest) (*QueryConsumerChainOptedInValidatorsResponse, error) // QueryConsumerChainsValidatorHasToValidate returns a list of consumer chains From 2033a10a8f2e092b263a353102068d01ab435486 Mon Sep 17 00:00:00 2001 From: insumity Date: Mon, 29 Apr 2024 16:36:57 +0200 Subject: [PATCH 02/16] fixes --- x/ccv/provider/keeper/keeper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index 2e31265d4c..691dd07731 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -682,7 +682,7 @@ func TestGetAllOptedIn(t *testing.T) { // sort validators first to be able to compare sortOptedInValidators := func(addresses []types.ProviderConsAddress) { - sort.Slice(addressess, func(i, j int) bool { + sort.Slice(addresses, func(i, j int) bool { return bytes.Compare(addresses[i].ToSdkConsAddr(), addresses[j].ToSdkConsAddr()) < 0 }) } From 7c90e0b23813ffd80f25a345c3dfc4d545d07d9c Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Tue, 30 Apr 2024 10:17:15 +0200 Subject: [PATCH 03/16] Add property based test for power cap --- .../keeper/partial_set_security_test.go | 194 +++++++++++++++--- 1 file changed, 164 insertions(+), 30 deletions(-) diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 35568c0b52..346d1228d5 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -2,12 +2,14 @@ package keeper_test import ( "bytes" - "github.com/cometbft/cometbft/proto/tendermint/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" "math" "sort" "testing" + "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "pgregory.net/rapid" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -425,34 +427,6 @@ func TestCapValidatorsPower(t *testing.T) { } func TestNoMoreThanPercentOfTheSum(t *testing.T) { - // returns `true` if no validator in `validators` corresponds to more than `percent` of the total sum of all - // validators' powers - noMoreThanPercent := func(validators []types.ConsumerValidator, percent uint32) bool { - sum := int64(0) - for _, v := range validators { - sum = sum + v.Power - } - - for _, v := range validators { - if (float64(v.Power)/float64(sum))*100.0 > float64(percent) { - return false - } - } - return true - } - - createConsumerValidators := func(powers []int64) []types.ConsumerValidator { - var validators []types.ConsumerValidator - for _, p := range powers { - validators = append(validators, types.ConsumerValidator{ - ProviderConsAddr: []byte("providerConsAddr"), - Power: p, - ConsumerPublicKey: &crypto.PublicKey{}, - }) - } - return validators - } - // **impossible** case where we only have 9 powers, and we want that no number has more than 10% of the total sum powers := []int64{1, 2, 3, 4, 5, 6, 7, 8, 9} percent := uint32(10) @@ -486,3 +460,163 @@ func TestNoMoreThanPercentOfTheSum(t *testing.T) { percent = 50 require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) } + +func createConsumerValidators(powers []int64) []types.ConsumerValidator { + var validators []types.ConsumerValidator + for _, p := range powers { + validators = append(validators, types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddr"), + Power: p, + ConsumerPublicKey: &crypto.PublicKey{}, + }) + } + return validators +} + +// returns `true` if no validator in `validators` corresponds to more than `percent` of the total sum of all +// validators' powers +func noMoreThanPercent(validators []types.ConsumerValidator, percent uint32) bool { + sum := int64(0) + for _, v := range validators { + sum = sum + v.Power + } + + for _, v := range validators { + if (float64(v.Power)/float64(sum))*100.0 > float64(percent) { + return false + } + } + return true +} + +func sumPowers(vals []types.ConsumerValidator) int64 { + sum := int64(0) + for _, v := range vals { + sum += v.Power + } + return sum +} + +func CapSatisfiable(vals []types.ConsumerValidator, percent uint32) bool { + // 100 / len(vals) is what each validator gets if each has the same power. + // if this is more than the cap, it cannot be satisfied. + return float64(100)/float64(len(vals)) < float64(percent) +} + +func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { + // define properties to test + + // capRespectedIfSatisfiable: if the cap can be respected, then it will be respected + capRespectedIfSatisfiable := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator, percent uint32) bool { + if CapSatisfiable(valsBefore, percent) { + return noMoreThanPercent(valsAfter, percent) + } + return true + } + + evenPowersIfCapCannotBeSatisfied := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator, percent uint32) bool { + if !CapSatisfiable(valsBefore, percent) { + // if the cap cannot be satisfied, each validator should have the same power + for _, valAfter := range valsAfter { + if valAfter.Power != valsAfter[0].Power { + return false + } + } + } + return true + } + + // fairness: if before, v1 has more power than v2, then afterwards v1 will not have less power than v2 + // (they might get the same power if they are both capped) + fairness := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator) bool { + for i, v := range valsBefore { + // find the validator after with the same address + vAfter := findConsumerValidator(t, v, valsAfter) + + // go through all other validators before (after this one, to avoid double checking) + for j := i + 1; j < len(valsBefore); j++ { + otherV := valsBefore[j] + otherVAfter := findConsumerValidator(t, otherV, valsAfter) + + // v has at least as much power before + if v.Power >= otherV.Power { + // then otherV should not have more power after + if vAfter.Power < otherVAfter.Power { + return false + } + } else { + // v has less power before + // then v should not have more power after + if vAfter.Power > otherVAfter.Power { + return false + } + } + } + } + return true + } + + // non-zero: v has non-zero power before IFF it has non-zero power after + nonZero := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator) bool { + for _, v := range valsBefore { + vAfter := findConsumerValidator(t, v, valsAfter) + if (v.Power == 0) != (vAfter.Power == 0) { + return false + } + } + return true + } + + // equalSumIfCapSatisfiable: the sum of the powers of the validators will not change if the cap can be satisfied + // (except for small changes by rounding errors) + equalSumIfCapSatisfiable := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator, percent uint32) bool { + if CapSatisfiable(valsBefore, percent) { + difference := math.Abs(float64(sumPowers(valsBefore) - sumPowers(valsAfter))) + if difference > 1 { + // if the difference is more than a rounding error, they are not equal + return false + } + } + return true + } + + // num validators: the number of validators will not change + equalNumVals := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator) bool { + return len(valsBefore) == len(valsAfter) + } + + // test setup for pbt + rapid.Check(t, func(t *rapid.T) { + powers := rapid.SliceOf(rapid.Int64Range(1, 1000000000000)).Draw(t, "powers") + percent := uint32(rapid.Int32Range(1, 100).Draw(t, "percent")) + + consumerValidators := createConsumerValidators(powers) + cappedValidators := keeper.NoMoreThanPercentOfTheSum(consumerValidators, percent) + + t.Log("can the cap be satisfied: ", CapSatisfiable(consumerValidators, percent)) + t.Log("before: ", consumerValidators) + t.Log("after: ", cappedValidators) + + // check properties + require.True(t, capRespectedIfSatisfiable(consumerValidators, cappedValidators, percent)) + require.True(t, evenPowersIfCapCannotBeSatisfied(consumerValidators, cappedValidators, percent)) + require.True(t, fairness(consumerValidators, cappedValidators)) + require.True(t, nonZero(consumerValidators, cappedValidators)) + require.True(t, equalSumIfCapSatisfiable(consumerValidators, cappedValidators, percent), "sum before: %v, sum after: %v", sumPowers(consumerValidators), sumPowers(cappedValidators)) + require.True(t, equalNumVals(consumerValidators, cappedValidators), "num before: %v, num after: %v", len(consumerValidators), len(cappedValidators)) + }) +} + +func findConsumerValidator(t *testing.T, v types.ConsumerValidator, valsAfter []types.ConsumerValidator) *types.ConsumerValidator { + var vAfter *types.ConsumerValidator + for _, vA := range valsAfter { + if bytes.Equal(v.ProviderConsAddr, vA.ProviderConsAddr) { + vAfter = &vA + break + } + } + if vAfter == nil { + t.Fatalf("could not find validator with address %v in validators after \n validators after capping: %v", v.ProviderConsAddr, valsAfter) + } + return vAfter +} From 5bd27fd9c147c012094afbeac2af7560af254b7d Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 30 Apr 2024 13:12:40 +0200 Subject: [PATCH 04/16] fixed tests & added algorithm's idea --- tests/integration/stop_consumer.go | 3 +- testutil/ibc_testing/generic_setup.go | 7 ++ testutil/keeper/expectations.go | 21 ++++-- testutil/keeper/unit_test_helpers.go | 1 + x/ccv/provider/keeper/keeper.go | 68 ++++++++++++++----- x/ccv/provider/keeper/partial_set_security.go | 43 +++++++++--- x/ccv/provider/keeper/proposal.go | 35 ++++------ x/ccv/provider/keeper/proposal_test.go | 11 +++ x/ccv/provider/proposal_handler_test.go | 4 +- 9 files changed, 139 insertions(+), 54 deletions(-) diff --git a/tests/integration/stop_consumer.go b/tests/integration/stop_consumer.go index 6e72679ccd..fe82bb9b2d 100644 --- a/tests/integration/stop_consumer.go +++ b/tests/integration/stop_consumer.go @@ -1,10 +1,9 @@ package integration import ( - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) diff --git a/testutil/ibc_testing/generic_setup.go b/testutil/ibc_testing/generic_setup.go index dbc71beb1f..82ea8bcbe1 100644 --- a/testutil/ibc_testing/generic_setup.go +++ b/testutil/ibc_testing/generic_setup.go @@ -3,6 +3,7 @@ package ibc_testing import ( "encoding/json" "fmt" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" "testing" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -140,6 +141,12 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( prop := testkeeper.GetTestConsumerAdditionProp() prop.ChainId = chainID prop.Top_N = consumerTopNParams[index] // isn't used in CreateConsumerClient + + for _, v := range providerApp.GetTestStakingKeeper().GetLastValidators(providerChain.GetContext()) { + consAddr, _ := v.GetConsAddr() + providerKeeper.SetOptedIn(providerChain.GetContext(), chainID, providertypes.NewProviderConsAddress(consAddr)) + } + // NOTE: the initial height passed to CreateConsumerClient // must be the height on the consumer when InitGenesis is called prop.InitialHeight = clienttypes.Height{RevisionNumber: 0, RevisionHeight: 3} diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 8f995b5042..914fd82815 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -2,6 +2,7 @@ package keeper import ( cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" time "time" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -53,14 +54,26 @@ func GetMocksForCreateConsumerClient(ctx sdk.Context, mocks *MockedKeepers, return expectations } +func MockOneOptedInValidator(ctx sdk.Context, mocks *MockedKeepers, providerKeeper keeper.Keeper, chainID string) { + // consider at least one opted-in validator in order to be able to create the consumer genesis + validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() + consAddr, _ := validator.GetConsAddr() + providerKeeper.SetOptedIn(ctx, chainID, providertypes.NewProviderConsAddress(consAddr)) + mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return(validator, true).AnyTimes() + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(123)).AnyTimes() + + mocks.MockStakingKeeper.EXPECT(). + IterateLastValidatorPowers(gomock.Any(), gomock.Any()). + Do(func(ctx sdk.Context, fn func(addr sdk.ValAddress, power int64) (stop bool)) { + fn(sdk.ValAddress("address"), 100) + }). + Return().AnyTimes() +} + // GetMocksForMakeConsumerGenesis returns mock expectations needed to call MakeConsumerGenesis(). func GetMocksForMakeConsumerGenesis(ctx sdk.Context, mocks *MockedKeepers, unbondingTimeToInject time.Duration, ) []*gomock.Call { - mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return( - cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator(), true).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(234)).AnyTimes() - return []*gomock.Call{ mocks.MockStakingKeeper.EXPECT().UnbondingTime(gomock.Any()).Return(unbondingTimeToInject).Times(1), diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index abba1a23c9..9bf7f3bbf5 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -222,6 +222,7 @@ func SetupForStoppingConsumerChain(t *testing.T, ctx sdk.Context, gomock.InOrder(expectations...) prop := GetTestConsumerAdditionProp() + MockOneOptedInValidator(ctx, &mocks, *providerKeeper, "chainID") err := providerKeeper.CreateConsumerClient(ctx, prop) require.NoError(t, err) err = providerKeeper.SetConsumerChain(ctx, "channelID") diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 0202340e4a..d2478591dd 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1376,59 +1376,91 @@ func (k Keeper) GetValidatorSetCap( return binary.BigEndian.Uint32(buf), true } -// SetDenylist denylists validator with `providerAddr` address on chain `chainID` -func (k Keeper) SetDenylist( +// SetAllowlist allowlists validator with `providerAddr` address on chain `chainID` +func (k Keeper) SetAllowlist( ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, ) { store := ctx.KVStore(k.storeKey) - store.Set(types.DenylistCapKey(chainID, providerAddr), []byte{}) + store.Set(types.AllowlistCapKey(chainID, providerAddr), []byte{}) } -// IsDenylisted returns `true` if validator with `providerAddr` has been denylisted on chain `chainID` -func (k Keeper) IsDenylisted( +// IsAllowlisted returns `true` if validator with `providerAddr` has been allowlisted on chain `chainID` +func (k Keeper) IsAllowlisted( ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, ) bool { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.DenylistCapKey(chainID, providerAddr)) + bz := store.Get(types.AllowlistCapKey(chainID, providerAddr)) return bz != nil } -// SetAllowlist allowlists validator with `providerAddr` address on chain `chainID` -func (k Keeper) SetAllowlist( +// DeleteAllowlist deletes all allowlisted validators +func (k Keeper) DeleteAllowlist(ctx sdk.Context, chainID string) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) + defer iterator.Close() + + keysToDel := [][]byte{} + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + + for _, key := range keysToDel { + store.Delete(key) + } +} + +// IsAllowlistEmpty returns `true` if no validator is allowlisted on chain `chainID` +func (k Keeper) IsAllowlistEmpty(ctx sdk.Context, chainID string) bool { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) + defer iterator.Close() + + if iterator.Valid() { + return false + } + + return true +} + +// SetDenylist denylists validator with `providerAddr` address on chain `chainID` +func (k Keeper) SetDenylist( ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, ) { store := ctx.KVStore(k.storeKey) - store.Set(types.AllowlistCapKey(chainID, providerAddr), []byte{}) + store.Set(types.DenylistCapKey(chainID, providerAddr), []byte{}) } -// IsAllowlisted returns `true` if validator with `providerAddr` has been allowlisted on chain `chainID` -func (k Keeper) IsAllowlisted( +// IsDenylisted returns `true` if validator with `providerAddr` has been denylisted on chain `chainID` +func (k Keeper) IsDenylisted( ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, ) bool { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.AllowlistCapKey(chainID, providerAddr)) + bz := store.Get(types.DenylistCapKey(chainID, providerAddr)) return bz != nil } -// IsAllowlistEmpty returns `true` if no validator is allowlisted on chain `chainID` -func (k Keeper) IsAllowlistEmpty(ctx sdk.Context, chainID string) bool { +// DeleteDenylist deletes all denylisted validators +func (k Keeper) DeleteDenylist(ctx sdk.Context, chainID string) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistPrefix, chainID)) defer iterator.Close() - if iterator.Valid() { - return false + keysToDel := [][]byte{} + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) } - return true + for _, key := range keysToDel { + store.Delete(key) + } } // IsDenylistEmpty returns `true` if no validator is allowlisted on chain `chainID` diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 42e7904f8e..65c2006fb3 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -170,7 +170,7 @@ func (k Keeper) CapValidatorSet(ctx sdk.Context, chainID string, validators []ty // on the consumer chain have less power than the set validators-power cap. For example, if we have 10 validators and // the power cap is set to 5%, we need at least one validator to have more than 10% of the voting power on the consumer chain. func (k Keeper) CapValidatorsPower(ctx sdk.Context, chainID string, validators []types.ConsumerValidator) []types.ConsumerValidator { - if p, found := k.GetValidatorsPowerCap(ctx, chainID); found { + if p, found := k.GetValidatorsPowerCap(ctx, chainID); found && p > 0 { return NoMoreThanPercentOfTheSum(validators, p) } else { // is a no-op if power cap is not set for `chainID` @@ -188,22 +188,49 @@ func sum(validators []types.ConsumerValidator) int64 { } // NoMoreThanPercentOfTheSum returns a set of validators with updated powers such that no validator has more than the -// provided `percent` of the sum of all validators' powers. +// provided `percent` of the sum of all validators' powers. Operates on a best-effort basis. func NoMoreThanPercentOfTheSum(validators []types.ConsumerValidator, percent uint32) []types.ConsumerValidator { - // The idea behind this algorithm is rather simple: + // Algorithm's idea + // ---------------- + // Consider the validators' powers to be `a_1, a_2, ... a_n` and `p` to be the percent in [1, 100]. Now, consider + // the sum `s = a_1 + a_2 + ... + a_n`. Then `maxPower = s * p / 100 <=> 100 * maxPower = s * p`. + // The problem of capping the validators' powers to be no more than `p` has no solution if `(100 / n) > p`. For example, + // for n = 10 and p = 5 we do not have a solution. We would need at least one validator with power greater than 10% + // for a solution to exist. + // So, if `(100 / n) > p` there's no solution. We know that `100 * maxPower = s * p` and so `(100 / n) > (100 * maxPower) / s` + // `100 * s > 100 * maxPower * n <=> s > maxPower * n`. Thus, we do not have a solution if `s > n * maxPower`. + + // If `s <= n * maxPower` the idea of the algorithm is rather simple. // - Compute the `maxPower` a validator must have so that it does not have more than `percent` of the voting power. // - If a validator `v` has power `p`, then: // - if `p > maxPower` we set `v`'s power to `maxPower` and distribute the `p - maxPower` to validators that - // have less than `maxPower` power - s := sum(validators) - maxPower := int64(float64(s) * (float64(percent) / 100.0)) + // have less than `maxPower` power. This way, the total sum remains the same and no validator has more than + // `maxPower` and so the power capping is satisfied. + // - Note that in order to avoid setting multiple validators to `maxPower`, we first compute all the `remainingPower` + // we have to distribute and then attempt to add `remainingPower / validatorsWithPowerLessThanMaxPower` to each validator. + // To guarantee that the sum remains the same after the distribution of powers, we sort the powers in descending + // order. This way, we first attempt to add `remainingPower / validatorsWithPowerLessThanMaxPower` to validators + // with greater power and if we cannot add the `remainingPower / validatorsWithPowerLessThanMaxPower` without + // exceeding `maxPower`, we just add enough to reach `maxPower` and add the remaining power to validators with smaller + // power. + + // If `s > n * maxPower` there's no solution and the algorithm would set everything to `maxPower`. + // ---------------- + + // Computes `(sum(validators) * percent) / 100`. Because `sdk.Dec` does not provide a `Floor` function, but only + // a `Ceil` one, we use `Ceil` and subtract one. + maxPower := sdk.NewDec(sum(validators)).Mul(sdk.NewDec(int64(percent))).QuoInt64(100).Ceil().RoundInt64() - 1 + if maxPower == 0 { // edge case: set `maxPower` to 1 to avoid setting the power of a validator to 0 maxPower = 1 } - // Sort by `.Power` in decreasing order. This way, we improve the chances that if a validator `a` has more power - // than a validator `b` in `validators`, `a` has still more than `b` in the return validators. + // Sort by `.Power` in decreasing order. Sorting in descending order is needed because otherwise we would have cases + // like this `powers =[60, 138, 559]` and `p = 35%` where sum is `757` and `maxValue = 264`. + // Because `559 - 264 = 295` we have to distribute 295 to the first 2 numbers (`295/2 = 147` to each number). However, + // note that `138 + 147 > 264`. If we were to add 147 to 60 first, then we cannot give the remaining 147 to 138. + // So, the idea is to first give `126 (= 264 - 138)` to 138, so it becomes 264, and then add `21 (=147 - 26) + 147` to 60. sort.Slice(validators, func(i, j int) bool { return validators[i].Power > validators[j].Power }) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 1c504187d8..71bd9269d3 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -221,6 +221,10 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo } k.DeleteTopN(ctx, chainID) + k.DeleteValidatorsPowerCap(ctx, chainID) + k.DeleteValidatorSetCap(ctx, chainID) + k.DeleteAllowlist(ctx, chainID) + k.DeleteDenylist(ctx, chainID) k.Logger(ctx).Info("consumer chain removed from provider", "chainID", chainID) @@ -278,7 +282,7 @@ func (k Keeper) MakeConsumerGenesis( bondedValidators = append(bondedValidators, val) } - if prop.Top_N > 0 { + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N minPower := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, prop.Top_N) k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) @@ -296,11 +300,7 @@ func (k Keeper) MakeConsumerGenesis( !k.IsDenylisted(ctx, chainID, providerAddr)) }) - // TODO: fixme .. use the cached ... no??? - if prop.Top_N > 0 { - nextValidators = k.CapValidatorSet(ctx, chainID, nextValidators) - } - + nextValidators = k.CapValidatorSet(ctx, chainID, nextValidators) nextValidators = k.CapValidatorsPower(ctx, chainID, nextValidators) k.SetConsumerValSet(ctx, chainID, nextValidators) @@ -308,6 +308,10 @@ func (k Keeper) MakeConsumerGenesis( // get the initial updates with the latest set consumer public keys initialUpdatesWithConsumerKeys := DiffValidators([]types.ConsumerValidator{}, nextValidators) + if len(initialUpdatesWithConsumerKeys) == 0 { + return gen, nil, fmt.Errorf("unable to create a non-empty initial validator set") + } + // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) if err != nil { @@ -391,8 +395,8 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { cachedCtx, writeFn := ctx.CacheContext() k.SetTopN(cachedCtx, prop.ChainId, prop.Top_N) - k.SetValidatorSetCap(ctx, prop.ChainId, prop.ValidatorSetCap) - k.SetValidatorsPowersCap(ctx, prop.ChainId, prop.ValidatorsPowerCap) + k.SetValidatorSetCap(cachedCtx, prop.ChainId, prop.ValidatorSetCap) + k.SetValidatorsPowersCap(cachedCtx, prop.ChainId, prop.ValidatorsPowerCap) for _, address := range prop.Allowlist { consAddr, err := sdk.ConsAddressFromBech32(address) @@ -400,7 +404,7 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { continue } - k.SetAllowlist(ctx, prop.ChainId, types.NewProviderConsAddress(consAddr)) + k.SetAllowlist(cachedCtx, prop.ChainId, types.NewProviderConsAddress(consAddr)) } for _, address := range prop.Denylist { @@ -409,21 +413,10 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { continue } - k.SetDenylist(ctx, prop.ChainId, types.NewProviderConsAddress(consAddr)) + k.SetDenylist(cachedCtx, prop.ChainId, types.NewProviderConsAddress(consAddr)) } err := k.CreateConsumerClient(cachedCtx, &prop) - - consumerGenesis, _ := k.GetConsumerGenesis(cachedCtx, prop.ChainId) - providerInfo := consumerGenesis.GetProvider() - - if len(providerInfo.GetInitialValSet()) == 0 { - // drop the proposal - ctx.Logger().Info("consumer client could not be created because no validator exists in the"+ - "initial validator set for chain: %s", prop.ChainId) - continue - } - if err != nil { // drop the proposal ctx.Logger().Info("consumer client could not be created: %w", err) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index f750ce66fc..7a163d8326 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -113,6 +113,8 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { providerKeeper.SetParams(ctx, providertypes.DefaultParams()) ctx = ctx.WithBlockTime(tc.blockTime) + testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "chainID") + if tc.expAppendProp { // Mock calls are only asserted if we expect a client to be created. gomock.InOrder( @@ -188,6 +190,8 @@ func TestCreateConsumerClient(t *testing.T) { // Test specific setup tc.setup(&providerKeeper, ctx, &mocks) + testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "chainID") + // Call method with same arbitrary values as defined above in mock expectations. err := providerKeeper.CreateConsumerClient(ctx, testkeeper.GetTestConsumerAdditionProp()) @@ -477,6 +481,8 @@ func TestHandleConsumerRemovalProposal(t *testing.T) { providerKeeper.SetParams(ctx, providertypes.DefaultParams()) ctx = ctx.WithBlockTime(tc.blockTime) + testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "chainID") + // Mock expectations and setup for stopping the consumer chain, if applicable // Note: when expAppendProp is false, no mocks are setup, // meaning no external keeper methods are allowed to be called. @@ -810,6 +816,7 @@ func TestMakeConsumerGenesis(t *testing.T) { HistoricalEntries: 10000, UnbondingPeriod: 1728000000000000, } + testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "testchain1") actualGenesis, _, err := providerKeeper.MakeConsumerGenesis(ctx, &prop) require.NoError(t, err) @@ -1034,6 +1041,7 @@ func TestBeginBlockInit(t *testing.T) { mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(123)).AnyTimes() gomock.InOrder(expectedCalls...) + testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, time.Hour) for _, prop := range pendingProps { providerKeeper.SetPendingConsumerAdditionProp(ctx, prop) @@ -1042,6 +1050,7 @@ func TestBeginBlockInit(t *testing.T) { // opt in a sample validator so the chain's proposal can successfully execute consAddr, _ := validator.GetConsAddr() providerKeeper.SetOptedIn(ctx, pendingProps[5].ChainId, providertypes.NewProviderConsAddress(consAddr)) + providerKeeper.BeginBlockInit(ctx) // first proposal is not pending anymore because its spawn time already passed and was executed @@ -1149,6 +1158,8 @@ func TestBeginBlockCCR(t *testing.T) { additionProp := testkeeper.GetTestConsumerAdditionProp() additionProp.ChainId = prop.ChainId additionProp.InitialHeight = clienttypes.NewHeight(2, 3) + testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, prop.ChainId) + err := providerKeeper.CreateConsumerClient(ctx, additionProp) require.NoError(t, err) err = providerKeeper.SetConsumerChain(ctx, "channelID") diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index b7727286e0..379567b25f 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -75,7 +75,7 @@ func TestProviderProposalHandler(t *testing.T) { { name: "unsupported proposal type", // lint rule disabled because this is a test case for an unsupported proposal type - + content: &distributiontypes.CommunityPoolSpendProposal{ Title: "title", Description: "desc", @@ -93,6 +93,8 @@ func TestProviderProposalHandler(t *testing.T) { providerKeeper.SetParams(ctx, providertypes.DefaultParams()) ctx = ctx.WithBlockTime(tc.blockTime) + testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "chainID") + // Mock expectations depending on expected outcome switch { case tc.expValidConsumerAddition: From 1dc88d91df74b69fe5be2fc05cb7dfcdcf0334cd Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 30 Apr 2024 13:25:03 +0200 Subject: [PATCH 05/16] nit changes --- tests/integration/stop_consumer.go | 3 ++- testutil/keeper/expectations.go | 4 ---- x/ccv/provider/keeper/proposal.go | 1 - x/ccv/provider/types/keys.go | 10 +++++----- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/tests/integration/stop_consumer.go b/tests/integration/stop_consumer.go index fe82bb9b2d..6e72679ccd 100644 --- a/tests/integration/stop_consumer.go +++ b/tests/integration/stop_consumer.go @@ -1,9 +1,10 @@ package integration import ( + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 914fd82815..442cd06445 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -47,10 +47,6 @@ func GetMocksForCreateConsumerClient(ctx sdk.Context, mocks *MockedKeepers, ).Return("clientID", nil).Times(1) expectations = append(expectations, createClientExp) - mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return( - cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator(), true).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(234)).AnyTimes() - return expectations } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 71bd9269d3..339acbc097 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -78,7 +78,6 @@ func (k Keeper) CreateConsumerClient(ctx sdk.Context, prop *types.ConsumerAdditi if err != nil { return err } - err = k.SetConsumerGenesis(ctx, chainID, consumerGen) if err != nil { return err diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index e45a4b366d..14521b9289 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -571,16 +571,16 @@ func ValidatorSetCapKey(chainID string) []byte { return ChainIdWithLenKey(ValidatorSetCapPrefix, chainID) } -// DenylistCapKey returns the key to a validator's slash log -func DenylistCapKey(chainID string, providerAddr ProviderConsAddress) []byte { - return append(ChainIdWithLenKey(DenylistPrefix, chainID), providerAddr.ToSdkConsAddr().Bytes()...) -} - // AllowlistCapKey returns the key to a validator's slash log func AllowlistCapKey(chainID string, providerAddr ProviderConsAddress) []byte { return append(ChainIdWithLenKey(AllowlistPrefix, chainID), providerAddr.ToSdkConsAddr().Bytes()...) } +// DenylistCapKey returns the key to a validator's slash log +func DenylistCapKey(chainID string, providerAddr ProviderConsAddress) []byte { + return append(ChainIdWithLenKey(DenylistPrefix, chainID), providerAddr.ToSdkConsAddr().Bytes()...) +} + // OptedInKey returns the key used to store whether a validator is opted in on a consumer chain. func OptedInKey(chainID string, providerAddr ProviderConsAddress) []byte { prefix := ChainIdWithLenKey(OptedInBytePrefix, chainID) From fe3952d707785254bbcd21000645d06453d672eb Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 30 Apr 2024 13:26:31 +0200 Subject: [PATCH 06/16] Update x/ccv/provider/keeper/proposal.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --- x/ccv/provider/keeper/proposal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 339acbc097..27bd81199e 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -294,7 +294,7 @@ func (k Keeper) MakeConsumerGenesis( // if an allowlist is declared, only consider allowlisted validators (k.IsAllowlistEmpty(ctx, chainID) || k.IsAllowlisted(ctx, chainID, providerAddr)) && - // if a denylist is declared, only consider denylisted validators + // if a denylist is declared do not consider denylisted validators (k.IsDenylistEmpty(ctx, chainID) || !k.IsDenylisted(ctx, chainID, providerAddr)) }) From 5a4d85e4bb408d1e82be9cd5fc68e3cba0d2833b Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 30 Apr 2024 14:25:41 +0200 Subject: [PATCH 07/16] remove empty-validator-set check --- testutil/ibc_testing/generic_setup.go | 1 + testutil/keeper/expectations.go | 25 +----------- testutil/keeper/unit_test_helpers.go | 1 - x/ccv/provider/keeper/proposal.go | 6 +-- x/ccv/provider/keeper/proposal_test.go | 52 +++++-------------------- x/ccv/provider/proposal_handler_test.go | 2 - 6 files changed, 14 insertions(+), 73 deletions(-) diff --git a/testutil/ibc_testing/generic_setup.go b/testutil/ibc_testing/generic_setup.go index 82ea8bcbe1..bbe100729a 100644 --- a/testutil/ibc_testing/generic_setup.go +++ b/testutil/ibc_testing/generic_setup.go @@ -142,6 +142,7 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( prop.ChainId = chainID prop.Top_N = consumerTopNParams[index] // isn't used in CreateConsumerClient + // opt-in all validators for _, v := range providerApp.GetTestStakingKeeper().GetLastValidators(providerChain.GetContext()) { consAddr, _ := v.GetConsAddr() providerKeeper.SetOptedIn(providerChain.GetContext(), chainID, providertypes.NewProviderConsAddress(consAddr)) diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 442cd06445..5a37f3a164 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -1,8 +1,6 @@ package keeper import ( - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" time "time" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -50,22 +48,6 @@ func GetMocksForCreateConsumerClient(ctx sdk.Context, mocks *MockedKeepers, return expectations } -func MockOneOptedInValidator(ctx sdk.Context, mocks *MockedKeepers, providerKeeper keeper.Keeper, chainID string) { - // consider at least one opted-in validator in order to be able to create the consumer genesis - validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() - consAddr, _ := validator.GetConsAddr() - providerKeeper.SetOptedIn(ctx, chainID, providertypes.NewProviderConsAddress(consAddr)) - mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return(validator, true).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(123)).AnyTimes() - - mocks.MockStakingKeeper.EXPECT(). - IterateLastValidatorPowers(gomock.Any(), gomock.Any()). - Do(func(ctx sdk.Context, fn func(addr sdk.ValAddress, power int64) (stop bool)) { - fn(sdk.ValAddress("address"), 100) - }). - Return().AnyTimes() -} - // GetMocksForMakeConsumerGenesis returns mock expectations needed to call MakeConsumerGenesis(). func GetMocksForMakeConsumerGenesis(ctx sdk.Context, mocks *MockedKeepers, unbondingTimeToInject time.Duration, @@ -76,12 +58,7 @@ func GetMocksForMakeConsumerGenesis(ctx sdk.Context, mocks *MockedKeepers, mocks.MockClientKeeper.EXPECT().GetSelfConsensusState(gomock.Any(), clienttypes.GetSelfHeight(ctx)).Return(&ibctmtypes.ConsensusState{}, nil).Times(1), - mocks.MockStakingKeeper.EXPECT(). - IterateLastValidatorPowers(gomock.Any(), gomock.Any()). - Do(func(ctx sdk.Context, fn func(addr sdk.ValAddress, power int64) (stop bool)) { - fn(sdk.ValAddress("address"), 100) - }). - Return().AnyTimes(), + mocks.MockStakingKeeper.EXPECT().IterateLastValidatorPowers(gomock.Any(), gomock.Any()).Times(1), } } diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index 9bf7f3bbf5..abba1a23c9 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -222,7 +222,6 @@ func SetupForStoppingConsumerChain(t *testing.T, ctx sdk.Context, gomock.InOrder(expectations...) prop := GetTestConsumerAdditionProp() - MockOneOptedInValidator(ctx, &mocks, *providerKeeper, "chainID") err := providerKeeper.CreateConsumerClient(ctx, prop) require.NoError(t, err) err = providerKeeper.SetConsumerChain(ctx, "channelID") diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 27bd81199e..db11fe329f 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -307,9 +307,9 @@ func (k Keeper) MakeConsumerGenesis( // get the initial updates with the latest set consumer public keys initialUpdatesWithConsumerKeys := DiffValidators([]types.ConsumerValidator{}, nextValidators) - if len(initialUpdatesWithConsumerKeys) == 0 { - return gen, nil, fmt.Errorf("unable to create a non-empty initial validator set") - } + //if len(initialUpdatesWithConsumerKeys) == 0 { + // return gen, nil, fmt.Errorf("unable to create a non-empty initial validator set") + //} // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 7a163d8326..69020e226d 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -113,8 +113,6 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { providerKeeper.SetParams(ctx, providertypes.DefaultParams()) ctx = ctx.WithBlockTime(tc.blockTime) - testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "chainID") - if tc.expAppendProp { // Mock calls are only asserted if we expect a client to be created. gomock.InOrder( @@ -190,8 +188,6 @@ func TestCreateConsumerClient(t *testing.T) { // Test specific setup tc.setup(&providerKeeper, ctx, &mocks) - testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "chainID") - // Call method with same arbitrary values as defined above in mock expectations. err := providerKeeper.CreateConsumerClient(ctx, testkeeper.GetTestConsumerAdditionProp()) @@ -481,8 +477,6 @@ func TestHandleConsumerRemovalProposal(t *testing.T) { providerKeeper.SetParams(ctx, providertypes.DefaultParams()) ctx = ctx.WithBlockTime(tc.blockTime) - testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "chainID") - // Mock expectations and setup for stopping the consumer chain, if applicable // Note: when expAppendProp is false, no mocks are setup, // meaning no external keeper methods are allowed to be called. @@ -816,7 +810,6 @@ func TestMakeConsumerGenesis(t *testing.T) { HistoricalEntries: 10000, UnbondingPeriod: 1728000000000000, } - testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "testchain1") actualGenesis, _, err := providerKeeper.MakeConsumerGenesis(ctx, &prop) require.NoError(t, err) @@ -996,22 +989,6 @@ func TestBeginBlockInit(t *testing.T) { nil, nil, ).(*providertypes.ConsumerAdditionProposal), - providertypes.NewConsumerAdditionProposal( - "title", "opt-in chain with no validator opted in", "chain4", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, - now.Add(-time.Hour*2).UTC(), - "0.75", - 10, - "", - 10000, - 100000000000, - 100000000000, - 100000000000, - 0, - 0, - 0, - nil, - nil, - ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "opt-in chain with at least one validator opted in", "chain5", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, now.Add(-time.Hour*1).UTC(), @@ -1030,26 +1007,24 @@ func TestBeginBlockInit(t *testing.T) { ).(*providertypes.ConsumerAdditionProposal), } - // Expect client creation for only the first, second, and sixth proposals (spawn time already passed and valid) + // Expect client creation for only the first, second, and fifth proposals (spawn time already passed and valid) expectedCalls := testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain1", clienttypes.NewHeight(3, 4)) expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain2", clienttypes.NewHeight(3, 4))...) expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain5", clienttypes.NewHeight(3, 4))...) - // consider at least one validator - validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() - mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return(validator, true).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(123)).AnyTimes() - gomock.InOrder(expectedCalls...) - testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, time.Hour) for _, prop := range pendingProps { providerKeeper.SetPendingConsumerAdditionProp(ctx, prop) } // opt in a sample validator so the chain's proposal can successfully execute + validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() + //mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return(validator, true).AnyTimes() + //mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(123)).AnyTimes() + consAddr, _ := validator.GetConsAddr() - providerKeeper.SetOptedIn(ctx, pendingProps[5].ChainId, providertypes.NewProviderConsAddress(consAddr)) + providerKeeper.SetOptedIn(ctx, pendingProps[4].ChainId, providertypes.NewProviderConsAddress(consAddr)) providerKeeper.BeginBlockInit(ctx) @@ -1084,21 +1059,13 @@ func TestBeginBlockInit(t *testing.T) { // Note that we do not check that `GetConsumerGenesis(ctx, pendingProps[3].ChainId)` returns `false` here because // `pendingProps[3]` is an invalid proposal due to the chain id already existing so the consumer genesis also exists - // fifth proposal is dropped due to it being an Opt-In chain with no validators opted in - _, found = providerKeeper.GetPendingConsumerAdditionProp( - ctx, pendingProps[4].SpawnTime, pendingProps[4].ChainId) - require.False(t, found) - // because the proposal is dropped, no consumer genesis was created - _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[4].ChainId) - require.False(t, found) - - // sixth proposal corresponds to an Opt-In chain with one opted-in validator and hence the proposal gets + // fifth proposal corresponds to an Opt-In chain with one opted-in validator and hence the proposal gets // successfully executed _, found = providerKeeper.GetPendingConsumerAdditionProp( - ctx, pendingProps[5].SpawnTime, pendingProps[5].ChainId) + ctx, pendingProps[4].SpawnTime, pendingProps[4].ChainId) require.False(t, found) // sixth proposal was successfully executed and hence consumer genesis was created - _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[5].ChainId) + _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[4].ChainId) require.True(t, found) // test that Top N is set correctly @@ -1158,7 +1125,6 @@ func TestBeginBlockCCR(t *testing.T) { additionProp := testkeeper.GetTestConsumerAdditionProp() additionProp.ChainId = prop.ChainId additionProp.InitialHeight = clienttypes.NewHeight(2, 3) - testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, prop.ChainId) err := providerKeeper.CreateConsumerClient(ctx, additionProp) require.NoError(t, err) diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index 7884f27d10..185db25b07 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -93,8 +93,6 @@ func TestProviderProposalHandler(t *testing.T) { providerKeeper.SetParams(ctx, providertypes.DefaultParams()) ctx = ctx.WithBlockTime(tc.blockTime) - testkeeper.MockOneOptedInValidator(ctx, &mocks, providerKeeper, "chainID") - // Mock expectations depending on expected outcome switch { case tc.expValidConsumerAddition: From 075448d1d42ffc7c5a646470c3e40f4d7d9a0ef9 Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 30 Apr 2024 14:44:09 +0200 Subject: [PATCH 08/16] implicit memory aliasing issue fixed --- tests/mbt/driver/setup.go | 2 +- testutil/ibc_testing/generic_setup.go | 2 +- .../keeper/partial_set_security_test.go | 20 +++++++++---------- x/ccv/provider/keeper/proposal.go | 6 +++--- x/ccv/provider/keeper/proposal_test.go | 5 +---- x/ccv/provider/keeper/relay_test.go | 1 - x/ccv/provider/keeper/validator_set_update.go | 1 + 7 files changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/mbt/driver/setup.go b/tests/mbt/driver/setup.go index c8e1e2aed2..a49a1ac000 100644 --- a/tests/mbt/driver/setup.go +++ b/tests/mbt/driver/setup.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" "log" "sort" "testing" @@ -35,6 +34,7 @@ import ( "github.com/cosmos/interchain-security/v4/testutil/integration" simibc "github.com/cosmos/interchain-security/v4/testutil/simibc" consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) diff --git a/testutil/ibc_testing/generic_setup.go b/testutil/ibc_testing/generic_setup.go index bbe100729a..09dfa62140 100644 --- a/testutil/ibc_testing/generic_setup.go +++ b/testutil/ibc_testing/generic_setup.go @@ -3,7 +3,6 @@ package ibc_testing import ( "encoding/json" "fmt" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" "testing" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -20,6 +19,7 @@ import ( testutil "github.com/cosmos/interchain-security/v4/testutil/integration" testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) type ( diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 346d1228d5..9e641f261a 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -6,19 +6,19 @@ import ( "sort" "testing" - "github.com/cometbft/cometbft/proto/tendermint/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - "pgregory.net/rapid" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "pgregory.net/rapid" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cometbft/cometbft/proto/tendermint/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) @@ -507,14 +507,14 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { // define properties to test // capRespectedIfSatisfiable: if the cap can be respected, then it will be respected - capRespectedIfSatisfiable := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator, percent uint32) bool { + capRespectedIfSatisfiable := func(valsBefore, valsAfter []types.ConsumerValidator, percent uint32) bool { if CapSatisfiable(valsBefore, percent) { return noMoreThanPercent(valsAfter, percent) } return true } - evenPowersIfCapCannotBeSatisfied := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator, percent uint32) bool { + evenPowersIfCapCannotBeSatisfied := func(valsBefore, valsAfter []types.ConsumerValidator, percent uint32) bool { if !CapSatisfiable(valsBefore, percent) { // if the cap cannot be satisfied, each validator should have the same power for _, valAfter := range valsAfter { @@ -528,7 +528,7 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { // fairness: if before, v1 has more power than v2, then afterwards v1 will not have less power than v2 // (they might get the same power if they are both capped) - fairness := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator) bool { + fairness := func(valsBefore, valsAfter []types.ConsumerValidator) bool { for i, v := range valsBefore { // find the validator after with the same address vAfter := findConsumerValidator(t, v, valsAfter) @@ -557,7 +557,7 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { } // non-zero: v has non-zero power before IFF it has non-zero power after - nonZero := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator) bool { + nonZero := func(valsBefore, valsAfter []types.ConsumerValidator) bool { for _, v := range valsBefore { vAfter := findConsumerValidator(t, v, valsAfter) if (v.Power == 0) != (vAfter.Power == 0) { @@ -569,7 +569,7 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { // equalSumIfCapSatisfiable: the sum of the powers of the validators will not change if the cap can be satisfied // (except for small changes by rounding errors) - equalSumIfCapSatisfiable := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator, percent uint32) bool { + equalSumIfCapSatisfiable := func(valsBefore, valsAfter []types.ConsumerValidator, percent uint32) bool { if CapSatisfiable(valsBefore, percent) { difference := math.Abs(float64(sumPowers(valsBefore) - sumPowers(valsAfter))) if difference > 1 { @@ -581,7 +581,7 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { } // num validators: the number of validators will not change - equalNumVals := func(valsBefore []types.ConsumerValidator, valsAfter []types.ConsumerValidator) bool { + equalNumVals := func(valsBefore, valsAfter []types.ConsumerValidator) bool { return len(valsBefore) == len(valsAfter) } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index db11fe329f..80485b4b7c 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -307,7 +307,7 @@ func (k Keeper) MakeConsumerGenesis( // get the initial updates with the latest set consumer public keys initialUpdatesWithConsumerKeys := DiffValidators([]types.ConsumerValidator{}, nextValidators) - //if len(initialUpdatesWithConsumerKeys) == 0 { + // if len(initialUpdatesWithConsumerKeys) == 0 { // return gen, nil, fmt.Errorf("unable to create a non-empty initial validator set") //} @@ -389,7 +389,7 @@ func (k Keeper) GetPendingConsumerAdditionProp(ctx sdk.Context, spawnTime time.T func (k Keeper) BeginBlockInit(ctx sdk.Context) { propsToExecute := k.GetConsumerAdditionPropsToExecute(ctx) - for _, prop := range propsToExecute { + for i, prop := range propsToExecute { // create consumer client in a cached context to handle errors cachedCtx, writeFn := ctx.CacheContext() @@ -415,7 +415,7 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { k.SetDenylist(cachedCtx, prop.ChainId, types.NewProviderConsAddress(consAddr)) } - err := k.CreateConsumerClient(cachedCtx, &prop) + err := k.CreateConsumerClient(cachedCtx, &propsToExecute[i]) if err != nil { // drop the proposal ctx.Logger().Info("consumer client could not be created: %w", err) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 69020e226d..9b769d2f94 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -3,7 +3,6 @@ package keeper_test import ( "bytes" "encoding/json" - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" "sort" "testing" "time" @@ -18,6 +17,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" @@ -1020,9 +1020,6 @@ func TestBeginBlockInit(t *testing.T) { // opt in a sample validator so the chain's proposal can successfully execute validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() - //mocks.MockStakingKeeper.EXPECT().GetValidator(gomock.Any(), gomock.Any()).Return(validator, true).AnyTimes() - //mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(123)).AnyTimes() - consAddr, _ := validator.GetConsAddr() providerKeeper.SetOptedIn(ctx, pendingProps[4].ChainId, providertypes.NewProviderConsAddress(consAddr)) diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 1193e8322b..77bf04cdb6 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -823,7 +823,6 @@ func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valCConsAddr).Return(valC, true).AnyTimes() valD := createStakingValidator(ctx, mocks, 4, 8) // 25% of the total voting power valDConsAddr, _ := valD.GetConsAddr() - //valDPubKey, _ := valD.TmConsPublicKey() mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, true).AnyTimes() valE := createStakingValidator(ctx, mocks, 5, 16) // 50% of the total voting power valEConsAddr, _ := valE.GetConsAddr() diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index 77be6ed94e..264fdd67f2 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" From cd86b6594989cdea38dd920b5b07f578a80bd786 Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 30 Apr 2024 15:16:23 +0200 Subject: [PATCH 09/16] added keeper tests --- x/ccv/provider/keeper/keeper_test.go | 88 ++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index 691dd07731..2b3d4ac0b3 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -740,3 +740,91 @@ func TestConsumerCommissionRate(t *testing.T) { _, found = providerKeeper.GetConsumerCommissionRate(ctx, "chainID", providerAddr2) require.False(t, found) } + +// TestValidatorsPowersCap tests the `SetValidatorsPowerCap`, `GetValidatorsPowerCap`, and `DeleteValidatorsPowerCap` methods +func TestValidatorsPowersCap(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + expectedPowerCap := uint32(10) + providerKeeper.SetValidatorsPowersCap(ctx, "chainID", expectedPowerCap) + powerCap, found := providerKeeper.GetValidatorsPowerCap(ctx, "chainID") + require.Equal(t, expectedPowerCap, powerCap) + require.True(t, found) + + providerKeeper.DeleteValidatorsPowerCap(ctx, "chainID") + _, found = providerKeeper.GetValidatorsPowerCap(ctx, "chainID") + require.False(t, found) +} + +// TestValidatorSetCap tests the `SetValidatorSetCap`, `GetValidatorSetCap`, and `DeleteValidatorSetCap` methods +func TestValidatorSetCap(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + expectedPowerCap := uint32(10) + providerKeeper.SetValidatorSetCap(ctx, "chainID", expectedPowerCap) + powerCap, found := providerKeeper.GetValidatorSetCap(ctx, "chainID") + require.Equal(t, expectedPowerCap, powerCap) + require.True(t, found) + + providerKeeper.DeleteValidatorSetCap(ctx, "chainID") + _, found = providerKeeper.GetValidatorSetCap(ctx, "chainID") + require.False(t, found) +} + +// TestAllowlist tests the `SetAllowlist`, `IsAllowlisted`, `DeleteAllowlist`, and `IsAllowlistEmpty` methods +func TestAllowlist(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // no validator was allowlisted and hence the allowlist is empty + require.True(t, providerKeeper.IsAllowlistEmpty(ctx, chainID)) + + providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1")) + providerKeeper.SetAllowlist(ctx, chainID, providerAddr1) + require.True(t, providerKeeper.IsAllowlisted(ctx, chainID, providerAddr1)) + + // allowlist is not empty anymore + require.False(t, providerKeeper.IsAllowlistEmpty(ctx, chainID)) + + providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2")) + providerKeeper.SetAllowlist(ctx, chainID, providerAddr2) + require.True(t, providerKeeper.IsAllowlisted(ctx, chainID, providerAddr2)) + require.False(t, providerKeeper.IsAllowlistEmpty(ctx, chainID)) + + providerKeeper.DeleteAllowlist(ctx, chainID) + require.False(t, providerKeeper.IsAllowlisted(ctx, chainID, providerAddr1)) + require.False(t, providerKeeper.IsAllowlisted(ctx, chainID, providerAddr2)) + require.True(t, providerKeeper.IsAllowlistEmpty(ctx, chainID)) +} + +// TestDenylist tests the `SetDenylist`, `IsDenylisted`, `DeleteDenylist`, and `IsDenylistEmpty` methods +func TestDenylist(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // no validator was denylisted and hence the denylist is empty + require.True(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) + + providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1")) + providerKeeper.SetDenylist(ctx, chainID, providerAddr1) + require.True(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr1)) + + // denylist is not empty anymore + require.False(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) + + providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2")) + providerKeeper.SetDenylist(ctx, chainID, providerAddr2) + require.True(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr2)) + require.False(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) + + providerKeeper.DeleteDenylist(ctx, chainID) + require.False(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr1)) + require.False(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr2)) + require.True(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) +} From 5a003ec1dfff50775ed3c11da9cf0b6d37bd1599 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 1 May 2024 10:36:28 +0200 Subject: [PATCH 10/16] updated HasToValidate query --- x/ccv/provider/keeper/keeper.go | 41 +++++++++++-------- x/ccv/provider/keeper/partial_set_security.go | 11 +++++ x/ccv/provider/keeper/proposal.go | 19 +-------- x/ccv/provider/keeper/relay.go | 8 +--- 4 files changed, 37 insertions(+), 42 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index d34c70e78b..fd521eabfc 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1241,28 +1241,35 @@ func (k Keeper) HasToValidate( provAddr types.ProviderConsAddress, chainID string, ) (bool, error) { - // if the validator is opted in or was sent as part of the packet in the last epoch, they have to validate - if k.IsOptedIn(ctx, chainID, provAddr) || k.IsConsumerValidator(ctx, chainID, provAddr) { + // operate on a cached context, so we do not write (e.g., opt-in validators) to the state + cachedCtx, _ := ctx.CacheContext() + + // if the validator was sent as part of the packet in the last epoch, it has to validate + if k.IsConsumerValidator(cachedCtx, chainID, provAddr) { return true, nil } - // otherwise, check whether the validator will be automatically opted in at the end of this epoch - // assuming all powers stay the same - val, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, provAddr.ToSdkConsAddr()) - if !found { - return false, fmt.Errorf("validator not found for address %s", provAddr) - } - power := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) - topN, found := k.GetTopN(ctx, chainID) - if !found || topN == 0 { - return false, nil - } - minPowerToOptIn := k.ComputeMinPowerToOptIn(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx), topN) + // if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch + bondedValidators := k.stakingKeeper.GetLastValidators(cachedCtx) + if topN, found := k.GetTopN(cachedCtx, chainID); found && topN > 0 { + // in a Top-N chain, we automatically opt in all validators that belong to the top N + minPower := k.ComputeMinPowerToOptIn(cachedCtx, chainID, bondedValidators, topN) + k.OptInTopNValidators(cachedCtx, chainID, bondedValidators, minPower) + } - if power < minPowerToOptIn { - return false, nil + // if the validator is opted in and belongs to the validators of the next epoch, then if nothing changes + // the validator would have to validate in the next epoch + if k.IsOptedIn(cachedCtx, chainID, provAddr) { + nextValidators := k.ComputeNextValidators(cachedCtx, chainID, k.stakingKeeper.GetLastValidators(cachedCtx)) + for _, v := range nextValidators { + consAddr := sdk.ConsAddress(v.ProviderConsAddr) + if provAddr.ToSdkConsAddr().Equals(consAddr) { + return true, nil + } + } } - return true, nil + + return false, nil } // SetConsumerCommissionRate sets a per-consumer chain commission rate diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 65c2006fb3..e676a3e960 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -290,3 +290,14 @@ func (k Keeper) FilterOptedInAndAllowAndDenylistedPredicate(ctx sdk.Context, cha (k.IsDenylistEmpty(ctx, chainID) || !k.IsDenylisted(ctx, chainID, providerAddr)) } + +// ComputeNextValidators computes the validators for the upcoming epoch based on the currently `bondedValidators`. +func (k Keeper) ComputeNextValidators(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator) []types.ConsumerValidator { + nextValidators := k.FilterValidators(ctx, chainID, bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { + return k.FilterOptedInAndAllowAndDenylistedPredicate(ctx, chainID, providerAddr) + }) + + nextValidators = k.CapValidatorSet(ctx, chainID, nextValidators) + return k.CapValidatorsPower(ctx, chainID, nextValidators) +} diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 80485b4b7c..68e2ad14df 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -287,30 +287,13 @@ func (k Keeper) MakeConsumerGenesis( k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) } - nextValidators := k.FilterValidators(ctx, chainID, bondedValidators, - func(providerAddr types.ProviderConsAddress) bool { - // only consider opted-in validators - return k.IsOptedIn(ctx, chainID, providerAddr) && - // if an allowlist is declared, only consider allowlisted validators - (k.IsAllowlistEmpty(ctx, chainID) || - k.IsAllowlisted(ctx, chainID, providerAddr)) && - // if a denylist is declared do not consider denylisted validators - (k.IsDenylistEmpty(ctx, chainID) || - !k.IsDenylisted(ctx, chainID, providerAddr)) - }) - - nextValidators = k.CapValidatorSet(ctx, chainID, nextValidators) - nextValidators = k.CapValidatorsPower(ctx, chainID, nextValidators) + nextValidators := k.ComputeNextValidators(ctx, chainID, bondedValidators) k.SetConsumerValSet(ctx, chainID, nextValidators) // get the initial updates with the latest set consumer public keys initialUpdatesWithConsumerKeys := DiffValidators([]types.ConsumerValidator{}, nextValidators) - // if len(initialUpdatesWithConsumerKeys) == 0 { - // return gen, nil, fmt.Errorf("unable to create a non-empty initial validator set") - //} - // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) if err != nil { diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index ee141d4cec..7ce020b2e8 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -229,13 +229,7 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { k.OptInTopNValidators(ctx, chain.ChainId, bondedValidators, minPower) } - nextValidators := k.FilterValidators(ctx, chain.ChainId, bondedValidators, - func(providerAddr providertypes.ProviderConsAddress) bool { - return k.FilterOptedInAndAllowAndDenylistedPredicate(ctx, chain.ChainId, providerAddr) - }) - - nextValidators = k.CapValidatorSet(ctx, chain.ChainId, nextValidators) - nextValidators = k.CapValidatorsPower(ctx, chain.ChainId, nextValidators) + nextValidators := k.ComputeNextValidators(ctx, chain.ChainId, bondedValidators) valUpdates := DiffValidators(currentValidators, nextValidators) k.SetConsumerValSet(ctx, chain.ChainId, nextValidators) From d34d6b199000a36f896cede171a4771c275c1698 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 1 May 2024 10:38:13 +0200 Subject: [PATCH 11/16] Update x/ccv/provider/keeper/keeper.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --- x/ccv/provider/keeper/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index fd521eabfc..f793eca811 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1504,7 +1504,7 @@ func (k Keeper) DeleteDenylist(ctx sdk.Context, chainID string) { } } -// IsDenylistEmpty returns `true` if no validator is allowlisted on chain `chainID` +// IsDenylistEmpty returns `true` if no validator is denylisted on chain `chainID` func (k Keeper) IsDenylistEmpty(ctx sdk.Context, chainID string) bool { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistPrefix, chainID)) From 14e59ee028b65689f988c5afd3938a5271738544 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 1 May 2024 10:40:39 +0200 Subject: [PATCH 12/16] Update x/ccv/provider/keeper/keeper.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --- x/ccv/provider/keeper/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index f793eca811..477cc6b833 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1346,7 +1346,7 @@ func (k Keeper) DeleteConsumerCommissionRate( } // SetValidatorsPowersCap sets the power-cap value `p` associated to chain with `chainID` -func (k Keeper) SetValidatorsPowersCap( +func (k Keeper) SetValidatorsPowerCap( ctx sdk.Context, chainID string, p uint32, From 404eb54a39fe98fed48a745c244d265d101a5d18 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 1 May 2024 10:42:24 +0200 Subject: [PATCH 13/16] took into account comments --- x/ccv/provider/keeper/keeper.go | 2 +- x/ccv/provider/keeper/keeper_test.go | 6 +++--- x/ccv/provider/keeper/partial_set_security_test.go | 2 +- x/ccv/provider/keeper/proposal.go | 2 +- x/ccv/provider/keeper/relay_test.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 477cc6b833..1fb37c527c 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1345,7 +1345,7 @@ func (k Keeper) DeleteConsumerCommissionRate( store.Delete(types.ConsumerCommissionRateKey(chainID, providerAddr)) } -// SetValidatorsPowersCap sets the power-cap value `p` associated to chain with `chainID` +// SetValidatorsPowerCap sets the power-cap value `p` associated to chain with `chainID` func (k Keeper) SetValidatorsPowerCap( ctx sdk.Context, chainID string, diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index 2b3d4ac0b3..71e194f17f 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -741,13 +741,13 @@ func TestConsumerCommissionRate(t *testing.T) { require.False(t, found) } -// TestValidatorsPowersCap tests the `SetValidatorsPowerCap`, `GetValidatorsPowerCap`, and `DeleteValidatorsPowerCap` methods -func TestValidatorsPowersCap(t *testing.T) { +// TestValidatorsPowerCap tests the `SetValidatorsPowerCap`, `GetValidatorsPowerCap`, and `DeleteValidatorsPowerCap` methods +func TestValidatorsPowerCap(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() expectedPowerCap := uint32(10) - providerKeeper.SetValidatorsPowersCap(ctx, "chainID", expectedPowerCap) + providerKeeper.SetValidatorsPowerCap(ctx, "chainID", expectedPowerCap) powerCap, found := providerKeeper.GetValidatorsPowerCap(ctx, "chainID") require.Equal(t, expectedPowerCap, powerCap) require.True(t, found) diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 9e641f261a..571e1ee171 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -419,7 +419,7 @@ func TestCapValidatorsPower(t *testing.T) { sortValidators(cappedValidators) require.Equal(t, validators, cappedValidators) - providerKeeper.SetValidatorsPowersCap(ctx, "chainID", 33) + providerKeeper.SetValidatorsPowerCap(ctx, "chainID", 33) cappedValidators = providerKeeper.CapValidatorsPower(ctx, "chainID", validators) sortValidators(expectedValidators) sortValidators(cappedValidators) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 68e2ad14df..056836afa3 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -378,7 +378,7 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { k.SetTopN(cachedCtx, prop.ChainId, prop.Top_N) k.SetValidatorSetCap(cachedCtx, prop.ChainId, prop.ValidatorSetCap) - k.SetValidatorsPowersCap(cachedCtx, prop.ChainId, prop.ValidatorsPowerCap) + k.SetValidatorsPowerCap(cachedCtx, prop.ChainId, prop.ValidatorsPowerCap) for _, address := range prop.Allowlist { consAddr, err := sdk.ConsAddressFromBech32(address) diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 77bf04cdb6..e5dbeb9f8d 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -846,7 +846,7 @@ func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { providerKeeper.SetDenylist(ctx, "chainID", providertypes.NewProviderConsAddress(valDConsAddr)) // set a power-capping of 40% - providerKeeper.SetValidatorsPowersCap(ctx, "chainID", 40) + providerKeeper.SetValidatorsPowerCap(ctx, "chainID", 40) providerKeeper.QueueVSCPackets(ctx) From 411922c73b22e4981a4db8f14dfd013772ad5bfd Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 1 May 2024 11:58:18 +0200 Subject: [PATCH 14/16] do not use cached ctx --- x/ccv/provider/keeper/keeper.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 1fb37c527c..8210c41879 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1241,26 +1241,23 @@ func (k Keeper) HasToValidate( provAddr types.ProviderConsAddress, chainID string, ) (bool, error) { - // operate on a cached context, so we do not write (e.g., opt-in validators) to the state - cachedCtx, _ := ctx.CacheContext() - // if the validator was sent as part of the packet in the last epoch, it has to validate - if k.IsConsumerValidator(cachedCtx, chainID, provAddr) { + if k.IsConsumerValidator(ctx, chainID, provAddr) { return true, nil } // if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch - bondedValidators := k.stakingKeeper.GetLastValidators(cachedCtx) - if topN, found := k.GetTopN(cachedCtx, chainID); found && topN > 0 { + bondedValidators := k.stakingKeeper.GetLastValidators(ctx) + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower := k.ComputeMinPowerToOptIn(cachedCtx, chainID, bondedValidators, topN) - k.OptInTopNValidators(cachedCtx, chainID, bondedValidators, minPower) + minPower := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN) + k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) } // if the validator is opted in and belongs to the validators of the next epoch, then if nothing changes // the validator would have to validate in the next epoch - if k.IsOptedIn(cachedCtx, chainID, provAddr) { - nextValidators := k.ComputeNextValidators(cachedCtx, chainID, k.stakingKeeper.GetLastValidators(cachedCtx)) + if k.IsOptedIn(ctx, chainID, provAddr) { + nextValidators := k.ComputeNextValidators(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx)) for _, v := range nextValidators { consAddr := sdk.ConsAddress(v.ProviderConsAddr) if provAddr.ToSdkConsAddr().Equals(consAddr) { From 45d26cc6a2f1f2b0d8ad5ca8728e2b0508479139 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 1 May 2024 15:14:31 +0200 Subject: [PATCH 15/16] Fix E2E test. A jailed validator does not have to validate. --- tests/e2e/actions.go | 4 +- tests/e2e/steps_partial_set_security.go | 10 +- x/ccv/provider/keeper/grpc_query.go | 6 +- x/ccv/provider/keeper/keeper.go | 1 - x/ccv/provider/types/provider.pb.go | 14 +- x/ccv/provider/types/query.pb.go | 231 ++++++++++++------------ x/ccv/provider/types/query.pb.gw.go | 154 ++++++++++++---- x/ccv/provider/types/tx.pb.go | 7 +- 8 files changed, 250 insertions(+), 177 deletions(-) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 2fd95abb00..1587608177 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -2309,8 +2309,8 @@ func (tr TestConfig) optIn(action OptInAction, target ExecutionTarget, verbose b } if !tr.useCometmock { // error report only works with --gas auto, which does not work with CometMock, so ignore - if verbose { - fmt.Printf("got expected error during opt in | err: %s | output: %s \n", err, string(bz)) + if err != nil && verbose { + fmt.Printf("got error during opt in | err: %s | output: %s \n", err, string(bz)) } } diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go index 73bb929387..4c63e479df 100644 --- a/tests/e2e/steps_partial_set_security.go +++ b/tests/e2e/steps_partial_set_security.go @@ -343,7 +343,7 @@ func stepsOptInChain() []Step { ValidatorID("carol"): 300, }, HasToValidate: &map[ValidatorID][]ChainID{ - ValidatorID("alice"): {"consu"}, // but alice still is in the consumer valset so has to validate + ValidatorID("alice"): {}, // alice is jailed on the provider and does not have to validate ValidatorID("bob"): {"consu"}, ValidatorID("carol"): {"consu"}, }, @@ -367,7 +367,7 @@ func stepsOptInChain() []Step { ValidatorID("carol"): 300, }, HasToValidate: &map[ValidatorID][]ChainID{ - ValidatorID("alice"): {"consu"}, + ValidatorID("alice"): {}, // alice is jailed on the provider and does not have to validate ValidatorID("bob"): {"consu"}, ValidatorID("carol"): {"consu"}, }, @@ -397,7 +397,7 @@ func stepsOptInChain() []Step { ValidatorID("carol"): 300, }, HasToValidate: &map[ValidatorID][]ChainID{ - ValidatorID("alice"): {"consu"}, + ValidatorID("alice"): {"consu"}, // alice is unjailed and hence has to validate again ValidatorID("bob"): {"consu"}, ValidatorID("carol"): {"consu"}, }, @@ -477,7 +477,7 @@ func stepsOptInChain() []Step { ValidatorID("carol"): 300, }, HasToValidate: &map[ValidatorID][]ChainID{ - ValidatorID("alice"): {"consu"}, + ValidatorID("alice"): {}, // alice is jailed on the provider and does not have to validate ValidatorID("bob"): {"consu"}, ValidatorID("carol"): {"consu"}, }, @@ -508,7 +508,7 @@ func stepsOptInChain() []Step { ValidatorID("carol"): 300, }, HasToValidate: &map[ValidatorID][]ChainID{ - ValidatorID("alice"): {"consu"}, + ValidatorID("alice"): {}, // alice is jailed on the provider and does not have to validate ValidatorID("bob"): {"consu"}, ValidatorID("carol"): {"consu"}, }, diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 4adbcf5cc7..84c5928fea 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -278,11 +278,7 @@ func (k Keeper) QueryConsumerChainsValidatorHasToValidate(goCtx context.Context, for _, consumer := range k.GetAllConsumerChains(ctx) { chainID := consumer.ChainId - hasToValidate, err := k.HasToValidate(ctx, provAddr, chainID) - if err != nil { - return nil, err - } - if hasToValidate { + if hasToValidate, err := k.HasToValidate(ctx, provAddr, chainID); err == nil && hasToValidate { consumersToValidate = append(consumersToValidate, chainID) } } diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 8210c41879..6cd490c9e8 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -22,7 +22,6 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cometbft/cometbft/libs/log" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index 9a8b7e3bcf..4f0a2fd605 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -97,19 +97,19 @@ type ConsumerAdditionProposal struct { // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. Top_N uint32 `protobuf:"varint,15,opt,name=top_N,json=topN,proto3" json:"top_N,omitempty"` - // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For example, if - // `validators_power_cap` is set to `32`, it means that no validator can have more than 32% of the voting power on the - // consumer chain. Note that this might not be feasible. For instance, think of a consumer chain with only + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For instance, if + // `validators_power_cap` is set to 32, it means that no validator can have more than 32% of the voting power on the + // consumer chain. Note that this might not be feasible. For example, think of a consumer chain with only // 5 validators and with `validators_power_cap` set to 10%. In such a scenario, at least one validator would need - // to have more than 20% of the total voting power. + // to have more than 20% of the total voting power. Therefore, `validators_power_cap` operates on a best-effort basis. ValidatorsPowerCap uint32 `protobuf:"varint,16,opt,name=validators_power_cap,json=validatorsPowerCap,proto3" json:"validators_power_cap,omitempty"` // Corresponds to the maximum number of validators that can validate a consumer chain. - // Only applicable to Opt In chains an is a no-op if set for a Top N chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. ValidatorSetCap uint32 `protobuf:"varint,17,opt,name=validator_set_cap,json=validatorSetCap,proto3" json:"validator_set_cap,omitempty"` - // Corresponds to a list of the provider consensus addresses of validators that are the ONLY ones that can validate + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate // the consumer chain. Allowlist []string `protobuf:"bytes,18,rep,name=allowlist,proto3" json:"allowlist,omitempty"` - // Corresponds to a list of the provider consensus addresses of validators that CANNOT validate the consumer chain. + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. Denylist []string `protobuf:"bytes,19,rep,name=denylist,proto3" json:"denylist,omitempty"` } diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index 49c580244a..dbca4d229d 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -1586,121 +1586,122 @@ func init() { } var fileDescriptor_422512d7b7586cd7 = []byte{ - // 1811 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xdb, 0x6f, 0xdc, 0x4e, - 0x15, 0x8e, 0x93, 0x34, 0x24, 0x93, 0xde, 0x98, 0x96, 0x36, 0x75, 0xd2, 0xdd, 0xd4, 0x85, 0x92, - 0xde, 0xec, 0x64, 0x4b, 0xd5, 0x6b, 0x9a, 0x66, 0xb3, 0x49, 0xba, 0x4a, 0xdb, 0x6c, 0xdd, 0x34, - 0x48, 0x80, 0x70, 0x1d, 0x7b, 0xba, 0xb1, 0xea, 0xf5, 0x38, 0x1e, 0xef, 0xb6, 0xab, 0x0a, 0x89, - 0xf2, 0x40, 0x2b, 0x21, 0x41, 0x25, 0xe0, 0xbd, 0x12, 0xe2, 0x1f, 0x40, 0xfc, 0x11, 0x7d, 0xa3, - 0xd0, 0x17, 0xc4, 0x43, 0x40, 0x29, 0x0f, 0x88, 0x27, 0x54, 0x21, 0xf1, 0x84, 0xf4, 0x93, 0xc7, - 0x63, 0x7b, 0xbd, 0xeb, 0xec, 0x7a, 0x37, 0xf9, 0x3d, 0x25, 0x3b, 0x97, 0xef, 0x9c, 0xef, 0xcc, - 0x39, 0x67, 0xe6, 0x33, 0x90, 0x0c, 0xcb, 0x45, 0x8e, 0xb6, 0xa9, 0x1a, 0x96, 0x42, 0x90, 0x56, - 0x75, 0x0c, 0xb7, 0x2e, 0x69, 0x5a, 0x4d, 0xb2, 0x1d, 0x5c, 0x33, 0x74, 0xe4, 0x48, 0xb5, 0x19, - 0x69, 0xab, 0x8a, 0x9c, 0xba, 0x68, 0x3b, 0xd8, 0xc5, 0xf0, 0x6c, 0xc2, 0x06, 0x51, 0xd3, 0x6a, - 0x62, 0xb0, 0x41, 0xac, 0xcd, 0xf0, 0x13, 0x65, 0x8c, 0xcb, 0x26, 0x92, 0x54, 0xdb, 0x90, 0x54, - 0xcb, 0xc2, 0xae, 0xea, 0x1a, 0xd8, 0x22, 0x3e, 0x04, 0x7f, 0xbc, 0x8c, 0xcb, 0x98, 0xfe, 0x2b, - 0x79, 0xff, 0xb1, 0xd1, 0x2c, 0xdb, 0x43, 0x7f, 0x6d, 0x54, 0x9f, 0x49, 0xae, 0x51, 0x41, 0xc4, - 0x55, 0x2b, 0x36, 0x5b, 0x90, 0x4b, 0xe3, 0x6a, 0xe8, 0x85, 0xbf, 0x67, 0x7a, 0xb7, 0x3d, 0xb5, - 0x19, 0x89, 0x6c, 0xaa, 0x0e, 0xd2, 0x15, 0x0d, 0x5b, 0xa4, 0x5a, 0x09, 0x77, 0x7c, 0xa7, 0xcd, - 0x8e, 0x17, 0x86, 0x83, 0xd8, 0xb2, 0x09, 0x17, 0x59, 0x3a, 0x72, 0x2a, 0x86, 0xe5, 0x4a, 0x9a, - 0x53, 0xb7, 0x5d, 0x2c, 0x3d, 0x47, 0xf5, 0x80, 0xe1, 0x29, 0x0d, 0x93, 0x0a, 0x26, 0x8a, 0x4f, - 0xd2, 0xff, 0xe1, 0x4f, 0x09, 0xd7, 0xc1, 0xf8, 0x23, 0x2f, 0x9c, 0x0b, 0xcc, 0xec, 0x32, 0xb2, - 0x10, 0x31, 0x88, 0x8c, 0xb6, 0xaa, 0x88, 0xb8, 0xf0, 0x14, 0x18, 0xf6, 0x6d, 0x1b, 0xfa, 0x18, - 0x37, 0xc9, 0x4d, 0x8d, 0xc8, 0xdf, 0xa0, 0xbf, 0x8b, 0xba, 0xf0, 0x0a, 0x4c, 0x24, 0xef, 0x24, - 0x36, 0xb6, 0x08, 0x82, 0x3f, 0x04, 0x87, 0xca, 0xfe, 0x90, 0x42, 0x5c, 0xd5, 0x45, 0x74, 0xff, - 0x68, 0x6e, 0x5a, 0xdc, 0xed, 0xc4, 0x6a, 0x33, 0x62, 0x13, 0xd6, 0x63, 0x6f, 0x5f, 0x7e, 0xf0, - 0xc3, 0x76, 0xb6, 0x4f, 0x3e, 0x58, 0x6e, 0x18, 0x13, 0x26, 0x00, 0x1f, 0x33, 0xbe, 0xe0, 0xc1, - 0x05, 0x5e, 0x0b, 0x6a, 0x13, 0xa9, 0x60, 0x96, 0x79, 0x96, 0x07, 0x43, 0xd4, 0x3c, 0x19, 0xe3, - 0x26, 0x07, 0xa6, 0x46, 0x73, 0x17, 0xc4, 0x14, 0x49, 0x24, 0x52, 0x10, 0x99, 0xed, 0x14, 0xce, - 0x83, 0xef, 0xb6, 0x9a, 0x78, 0xec, 0xaa, 0x8e, 0x5b, 0x72, 0xb0, 0x8d, 0x89, 0x6a, 0x86, 0xde, - 0xbc, 0xe5, 0xc0, 0x54, 0xe7, 0xb5, 0xcc, 0xb7, 0x1f, 0x81, 0x11, 0x3b, 0x18, 0x64, 0x11, 0xbb, - 0x93, 0xce, 0x3d, 0x06, 0x3e, 0xaf, 0xeb, 0x86, 0x97, 0xdd, 0x11, 0x74, 0x04, 0x28, 0x4c, 0x81, - 0x73, 0x49, 0x9e, 0x60, 0xbb, 0xc5, 0xe9, 0x9f, 0x73, 0xc9, 0x04, 0x63, 0x4b, 0xc3, 0x93, 0x6e, - 0xf1, 0x79, 0xb6, 0x2b, 0x9f, 0x65, 0x54, 0xc1, 0x35, 0xd5, 0x4c, 0x74, 0x79, 0x0d, 0x1c, 0xa0, - 0xa6, 0xdb, 0xa4, 0x22, 0x1c, 0x07, 0x23, 0x9a, 0x69, 0x20, 0xcb, 0xf5, 0xe6, 0xfa, 0xe9, 0xdc, - 0xb0, 0x3f, 0x50, 0xd4, 0xe1, 0x31, 0x70, 0xc0, 0xc5, 0xb6, 0xf2, 0x70, 0x6c, 0x60, 0x92, 0x9b, - 0x3a, 0x24, 0x0f, 0xba, 0xd8, 0x7e, 0x28, 0xbc, 0xe1, 0xc0, 0x19, 0x4a, 0x6f, 0x5d, 0x35, 0x0d, - 0x5d, 0x75, 0xb1, 0xd3, 0x10, 0x3f, 0xa7, 0x73, 0xf6, 0xc3, 0x59, 0x70, 0x34, 0x60, 0xa2, 0xa8, - 0xba, 0xee, 0x20, 0x42, 0x7c, 0xcb, 0x79, 0xf8, 0x65, 0x3b, 0x7b, 0xb8, 0xae, 0x56, 0xcc, 0x9b, - 0x02, 0x9b, 0x10, 0xe4, 0x23, 0xc1, 0xda, 0x79, 0x7f, 0xe4, 0xe6, 0xf0, 0xdb, 0xf7, 0xd9, 0xbe, - 0x7f, 0xbd, 0xcf, 0xf6, 0x09, 0xab, 0x40, 0x68, 0xe7, 0x08, 0x0b, 0xf1, 0x79, 0x70, 0x34, 0x68, - 0x0c, 0xa1, 0x39, 0xdf, 0xa3, 0x23, 0x5a, 0xc3, 0x7a, 0xcf, 0x58, 0x2b, 0xb5, 0x52, 0x83, 0xf1, - 0x74, 0xd4, 0x5a, 0x6c, 0xb5, 0xa1, 0xd6, 0x64, 0xbf, 0x1d, 0xb5, 0xb8, 0x23, 0x11, 0xb5, 0x96, - 0x48, 0x32, 0x6a, 0x4d, 0x51, 0x13, 0xc6, 0xc1, 0x29, 0x0a, 0xb8, 0xb6, 0xe9, 0x60, 0xd7, 0x35, - 0x11, 0xed, 0x05, 0x41, 0xc6, 0xfe, 0x99, 0x63, 0x3d, 0xa1, 0x69, 0x96, 0x99, 0xc9, 0x82, 0x51, - 0x62, 0xaa, 0x64, 0x53, 0xa9, 0x20, 0x17, 0x39, 0xd4, 0xc2, 0x80, 0x0c, 0xe8, 0xd0, 0x03, 0x6f, - 0x04, 0xe6, 0xc0, 0xb7, 0x1a, 0x16, 0x28, 0xaa, 0x69, 0xe2, 0x17, 0xaa, 0xa5, 0x21, 0xca, 0x7d, - 0x40, 0x3e, 0x16, 0x2d, 0x9d, 0x0f, 0xa6, 0xe0, 0x8f, 0xc1, 0x98, 0x85, 0x5e, 0xba, 0x8a, 0x83, - 0x6c, 0x13, 0x59, 0x06, 0xd9, 0x54, 0x34, 0xd5, 0xd2, 0x3d, 0xb2, 0x88, 0xa6, 0xdb, 0x68, 0x8e, - 0x17, 0xfd, 0x7b, 0x44, 0x0c, 0xee, 0x11, 0x71, 0x2d, 0xb8, 0x47, 0xf2, 0xc3, 0x5e, 0x63, 0x7b, - 0xf7, 0xf7, 0x2c, 0x27, 0x9f, 0xf0, 0x50, 0xe4, 0x00, 0x64, 0x21, 0xc0, 0x10, 0x2e, 0x81, 0x0b, - 0x94, 0x92, 0x8c, 0xca, 0x06, 0x71, 0x91, 0x83, 0xf4, 0xa8, 0x64, 0x5e, 0xa8, 0x8e, 0x5e, 0x40, - 0x16, 0xae, 0x84, 0x35, 0xbb, 0x08, 0x2e, 0xa6, 0x5a, 0xcd, 0x22, 0x72, 0x02, 0x0c, 0xe9, 0x74, - 0x84, 0xb6, 0xc1, 0x11, 0x99, 0xfd, 0x12, 0x32, 0xac, 0xb1, 0xfb, 0xe5, 0x88, 0x74, 0x5a, 0x7e, - 0xc5, 0x42, 0x68, 0xe6, 0x35, 0x07, 0x4e, 0xef, 0xb2, 0x80, 0x21, 0x3f, 0x05, 0x87, 0xed, 0xc6, - 0xb9, 0xa0, 0xd1, 0xe6, 0x52, 0x75, 0x85, 0x18, 0x2c, 0xeb, 0xfe, 0x4d, 0x78, 0x42, 0x11, 0x1c, - 0x8a, 0x2d, 0x83, 0x63, 0x80, 0xe5, 0x6f, 0x21, 0x9e, 0xce, 0x05, 0x98, 0x01, 0x20, 0xe8, 0x26, - 0xc5, 0x02, 0x3d, 0xcc, 0x41, 0xb9, 0x61, 0x44, 0xb8, 0x0f, 0x24, 0xca, 0x66, 0xde, 0x34, 0x4b, - 0xaa, 0xe1, 0x90, 0x75, 0xd5, 0x5c, 0xc0, 0x96, 0x97, 0x72, 0xf9, 0x78, 0xf3, 0x2b, 0x16, 0x52, - 0xdc, 0x8a, 0xbf, 0xe7, 0xc0, 0x74, 0x7a, 0x38, 0x16, 0xaf, 0x2d, 0xf0, 0x4d, 0x5b, 0x35, 0x1c, - 0xa5, 0xa6, 0x9a, 0xde, 0xfd, 0x4f, 0xcb, 0x80, 0x85, 0x6c, 0x29, 0x5d, 0xc8, 0x54, 0xc3, 0x89, - 0x0c, 0x85, 0x65, 0x66, 0x45, 0x09, 0x70, 0xd8, 0x8e, 0x2d, 0x11, 0xfe, 0xcb, 0x81, 0x33, 0x1d, - 0x77, 0xc1, 0xa5, 0xdd, 0x6a, 0x33, 0x3f, 0xfe, 0x65, 0x3b, 0x7b, 0xd2, 0x6f, 0x05, 0xcd, 0x2b, - 0x5a, 0xdb, 0x9d, 0x87, 0xb3, 0x4b, 0x4b, 0x69, 0xc0, 0x69, 0x5e, 0xd1, 0xda, 0x5b, 0xe0, 0x1c, - 0x38, 0x18, 0xae, 0x7a, 0x8e, 0xea, 0xac, 0xc6, 0x26, 0xc4, 0xe8, 0xf5, 0x23, 0xfa, 0xaf, 0x1f, - 0xb1, 0x54, 0xdd, 0x30, 0x0d, 0x6d, 0x05, 0xd5, 0xe5, 0xd1, 0x60, 0xc7, 0x0a, 0xaa, 0x0b, 0xc7, - 0x01, 0xf4, 0x53, 0x57, 0x75, 0xd4, 0xa8, 0x70, 0x9e, 0x82, 0x63, 0xb1, 0x51, 0x76, 0x2c, 0x45, - 0x30, 0x64, 0xd3, 0x11, 0x76, 0xa9, 0x5d, 0x4c, 0x79, 0x16, 0xde, 0x16, 0x96, 0xb7, 0x0c, 0x40, - 0x58, 0x66, 0x85, 0x1c, 0xcb, 0x80, 0x55, 0xdb, 0x45, 0x7a, 0xd1, 0x0a, 0xdb, 0x63, 0x9a, 0x57, - 0xd7, 0x16, 0xab, 0xf1, 0x4e, 0x40, 0xe1, 0x53, 0xe7, 0x74, 0x2d, 0x1c, 0x55, 0x9a, 0x4f, 0x0a, - 0x05, 0xa5, 0x3f, 0x1e, 0x2d, 0x2a, 0xc5, 0x8f, 0x0e, 0x11, 0x61, 0x8b, 0x65, 0x74, 0xfc, 0x35, - 0x15, 0x1a, 0xbb, 0xa7, 0x92, 0x35, 0xcc, 0x7e, 0x05, 0xcd, 0x38, 0xf1, 0x7a, 0xe4, 0x52, 0x5f, - 0x8f, 0x82, 0x0a, 0x66, 0xba, 0x30, 0xc9, 0xb8, 0x5e, 0x02, 0x30, 0x4c, 0x8e, 0x20, 0x7c, 0x01, - 0xc1, 0x30, 0xfd, 0xfc, 0xd2, 0xd3, 0xe9, 0x35, 0x79, 0x31, 0xf9, 0xe2, 0x5d, 0xc0, 0x95, 0x8a, - 0x41, 0x88, 0x81, 0x2d, 0xb9, 0x81, 0xd1, 0xd7, 0xf6, 0x16, 0x10, 0x7e, 0xca, 0x81, 0x4b, 0xe9, - 0x3c, 0x61, 0x44, 0x4b, 0x60, 0xd0, 0x09, 0x1e, 0xd4, 0x23, 0xf9, 0xdb, 0x5e, 0xa2, 0xfd, 0x6d, - 0x3b, 0x7b, 0xae, 0x6c, 0xb8, 0x9b, 0xd5, 0x0d, 0x51, 0xc3, 0x15, 0xf6, 0xc4, 0x67, 0x7f, 0x2e, - 0x13, 0xfd, 0xb9, 0xe4, 0xd6, 0x6d, 0x44, 0xc4, 0x02, 0xd2, 0xfe, 0xf2, 0xc7, 0xcb, 0x80, 0x29, - 0x80, 0x02, 0xd2, 0x64, 0x8a, 0x24, 0xcc, 0x82, 0x49, 0xea, 0xc1, 0xaa, 0xa9, 0x23, 0xe2, 0x3e, - 0xb1, 0x34, 0x6c, 0x3d, 0x33, 0x9c, 0x0a, 0xd2, 0xd7, 0x89, 0x96, 0x22, 0x29, 0x7f, 0x19, 0x3c, - 0x39, 0x92, 0xf7, 0x33, 0xb7, 0x0d, 0x00, 0x6b, 0x44, 0x53, 0x08, 0xb2, 0x74, 0x25, 0x14, 0x53, - 0xac, 0xb4, 0xae, 0xa6, 0x2a, 0xad, 0x75, 0xa2, 0x3d, 0x46, 0x96, 0x1e, 0xdd, 0xa0, 0x7e, 0x91, - 0x1d, 0xad, 0x35, 0x8d, 0xe7, 0x7e, 0x71, 0x1a, 0x1c, 0xa0, 0x0e, 0xc1, 0x1d, 0x0e, 0x1c, 0x4f, - 0x92, 0x29, 0xf0, 0x6e, 0x2a, 0x8b, 0x6d, 0xb4, 0x11, 0x3f, 0xbf, 0x07, 0x04, 0x3f, 0x24, 0xc2, - 0xe2, 0xcf, 0x3e, 0xfd, 0xf3, 0xd7, 0xfd, 0x73, 0x70, 0xb6, 0xb3, 0xee, 0x0d, 0x53, 0x9b, 0xe9, - 0x20, 0xe9, 0x55, 0x70, 0x1a, 0x3f, 0x81, 0x9f, 0x38, 0xd6, 0xc0, 0xe2, 0xf5, 0x02, 0xe7, 0xba, - 0xf7, 0x30, 0x26, 0xa4, 0xf8, 0xbb, 0xbd, 0x03, 0x30, 0x86, 0x37, 0x28, 0xc3, 0x2b, 0x70, 0xa6, - 0x0b, 0x86, 0xbe, 0xc4, 0x82, 0xaf, 0xfb, 0xc1, 0xd8, 0x2e, 0xba, 0x89, 0xc0, 0xfb, 0x3d, 0x7a, - 0x96, 0x28, 0xd1, 0xf8, 0x07, 0xfb, 0x84, 0xc6, 0x48, 0xdf, 0xa3, 0xa4, 0xf3, 0xf0, 0x6e, 0xb7, - 0xa4, 0x3d, 0xa5, 0xec, 0xb8, 0x4a, 0xa8, 0x7e, 0xe0, 0xff, 0x39, 0x70, 0x32, 0x59, 0x86, 0x11, - 0xb8, 0xd2, 0xb3, 0xd3, 0xad, 0x7a, 0x8f, 0xbf, 0xbf, 0x3f, 0x60, 0x2c, 0x00, 0xcb, 0x34, 0x00, - 0xf3, 0x70, 0xae, 0x87, 0x00, 0x60, 0xbb, 0x81, 0xff, 0x7f, 0x82, 0x47, 0x7d, 0xa2, 0x3c, 0x82, - 0x4b, 0xe9, 0xbd, 0x6e, 0x27, 0xf4, 0xf8, 0xe5, 0x3d, 0xe3, 0x30, 0xe2, 0xf3, 0x94, 0xf8, 0x2d, - 0x78, 0x23, 0xc5, 0x87, 0xac, 0x00, 0x48, 0x89, 0x3d, 0x7c, 0x12, 0x28, 0x37, 0x5e, 0xc9, 0x3d, - 0x51, 0x4e, 0x10, 0x80, 0x3d, 0x51, 0x4e, 0xd2, 0x6f, 0xbd, 0x51, 0x8e, 0xdd, 0x97, 0xf0, 0x4f, - 0x1c, 0x7b, 0x96, 0xc5, 0xa4, 0x1b, 0xbc, 0x93, 0xde, 0xc5, 0x24, 0x45, 0xc8, 0xcf, 0xf5, 0xbc, - 0x9f, 0x51, 0xbb, 0x4e, 0xa9, 0xe5, 0xe0, 0x74, 0x67, 0x6a, 0x2e, 0x03, 0xf0, 0xbf, 0x75, 0xc1, - 0xdf, 0xf6, 0x83, 0xb3, 0x29, 0xb4, 0x18, 0x5c, 0x4d, 0xef, 0x62, 0x2a, 0x0d, 0xc8, 0x97, 0xf6, - 0x0f, 0x90, 0x05, 0x61, 0x85, 0x06, 0x61, 0x11, 0x2e, 0x74, 0x0e, 0x82, 0x13, 0x22, 0x46, 0x39, - 0xed, 0x50, 0x4c, 0xc5, 0xd7, 0x96, 0xf0, 0xdf, 0x2d, 0xda, 0x31, 0x2e, 0x89, 0x08, 0xec, 0xe2, - 0x56, 0xdd, 0x45, 0xa0, 0xf2, 0xf9, 0xbd, 0x40, 0x30, 0xd6, 0x79, 0xca, 0xfa, 0x36, 0xbc, 0xd9, - 0x99, 0x75, 0x20, 0x4d, 0x95, 0xe6, 0x0b, 0xec, 0x37, 0xfd, 0xec, 0xc3, 0x5f, 0x0a, 0x2d, 0x08, - 0xd7, 0xd2, 0x3b, 0x9d, 0x5e, 0xa9, 0xf2, 0x4f, 0xf6, 0x19, 0x95, 0x45, 0xe7, 0x16, 0x8d, 0xce, - 0x55, 0x78, 0xa5, 0xeb, 0xfe, 0x6e, 0xe8, 0xf0, 0x0f, 0x1c, 0x18, 0x6d, 0x90, 0x5b, 0xf0, 0x5a, - 0x17, 0xc7, 0xd5, 0x28, 0xdb, 0xf8, 0xeb, 0xdd, 0x6f, 0x64, 0xfe, 0x4f, 0x53, 0xff, 0x2f, 0xc0, - 0xa9, 0x14, 0xa7, 0xeb, 0x3b, 0xf9, 0x26, 0x28, 0xe8, 0xf6, 0xc2, 0xab, 0x9b, 0x82, 0x4e, 0xa5, - 0x05, 0xbb, 0x29, 0xe8, 0x74, 0x9a, 0x50, 0x98, 0xa5, 0xe4, 0xaf, 0xc1, 0xab, 0x9d, 0xc9, 0x63, - 0x0f, 0x44, 0x31, 0x2c, 0x25, 0xd2, 0x87, 0xf0, 0x77, 0xfd, 0xe0, 0x7c, 0x6a, 0x71, 0x06, 0x9f, - 0xf4, 0xfa, 0x82, 0x6c, 0xab, 0x2f, 0xf9, 0xf5, 0xfd, 0x86, 0xdd, 0xeb, 0xc3, 0x85, 0x28, 0x36, - 0x72, 0xa2, 0x30, 0xc1, 0x5f, 0xf5, 0x83, 0x6f, 0xa7, 0x11, 0x75, 0xb0, 0xb4, 0x87, 0xa7, 0x47, - 0xa2, 0x52, 0xe5, 0x1f, 0xed, 0x23, 0x62, 0xf7, 0xdd, 0x30, 0x0a, 0x4b, 0x08, 0xa5, 0x78, 0x1a, - 0x13, 0xfe, 0x8f, 0x63, 0x5f, 0x6f, 0x93, 0x44, 0x22, 0x5c, 0x4c, 0xef, 0x74, 0x1b, 0x91, 0xca, - 0x2f, 0xed, 0x15, 0xa6, 0xfb, 0x4b, 0x0f, 0x53, 0x1c, 0xa5, 0x1a, 0x01, 0x29, 0x35, 0xa2, 0x35, - 0xc8, 0xb3, 0xfc, 0xf7, 0x3f, 0xec, 0x64, 0xb8, 0x8f, 0x3b, 0x19, 0xee, 0x1f, 0x3b, 0x19, 0xee, - 0xdd, 0xe7, 0x4c, 0xdf, 0xc7, 0xcf, 0x99, 0xbe, 0xbf, 0x7e, 0xce, 0xf4, 0xfd, 0x60, 0xb6, 0x55, - 0xb3, 0x47, 0xf6, 0x2e, 0x87, 0xf6, 0x6a, 0xdf, 0x93, 0x5e, 0x36, 0x3d, 0x37, 0x3c, 0x39, 0xbf, - 0x31, 0x44, 0x3f, 0x2a, 0x5f, 0xf9, 0x2a, 0x00, 0x00, 0xff, 0xff, 0x2d, 0xeb, 0x2b, 0x80, 0x36, - 0x1d, 0x00, 0x00, + // 1834 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x6f, 0xdc, 0x5a, + 0x15, 0x8f, 0x93, 0x34, 0x24, 0x37, 0xaf, 0x7d, 0xe5, 0xb6, 0xbc, 0x97, 0x3a, 0xe9, 0x4c, 0x9e, + 0x1f, 0x3c, 0xd2, 0x2f, 0x3b, 0x99, 0xf2, 0x44, 0xbf, 0xd2, 0x34, 0x93, 0x49, 0xd2, 0x51, 0xda, + 0x66, 0xea, 0xa6, 0x01, 0x15, 0x84, 0xeb, 0xd8, 0xb7, 0x13, 0xab, 0x1e, 0x5f, 0xc7, 0xd7, 0x33, + 0xed, 0xa8, 0xaa, 0x44, 0x59, 0x40, 0x57, 0xa8, 0xe2, 0x63, 0xdf, 0x0d, 0x4b, 0x36, 0x08, 0xf1, + 0x2f, 0xd0, 0x1d, 0x85, 0x6e, 0x10, 0x8b, 0x80, 0x52, 0x16, 0x88, 0x15, 0xaa, 0x90, 0x58, 0x21, + 0x21, 0x5f, 0x5f, 0x7f, 0xcd, 0x38, 0x33, 0x9e, 0x49, 0x58, 0x25, 0x73, 0x7d, 0xef, 0xef, 0x9c, + 0xdf, 0xf1, 0x39, 0xe7, 0x9e, 0x9f, 0x81, 0x64, 0x58, 0x2e, 0x72, 0xb4, 0x6d, 0xd5, 0xb0, 0x14, + 0x82, 0xb4, 0xba, 0x63, 0xb8, 0x4d, 0x49, 0xd3, 0x1a, 0x92, 0xed, 0xe0, 0x86, 0xa1, 0x23, 0x47, + 0x6a, 0xcc, 0x49, 0x3b, 0x75, 0xe4, 0x34, 0x45, 0xdb, 0xc1, 0x2e, 0x86, 0x9f, 0xa7, 0x1c, 0x10, + 0x35, 0xad, 0x21, 0x06, 0x07, 0xc4, 0xc6, 0x1c, 0x3f, 0x55, 0xc5, 0xb8, 0x6a, 0x22, 0x49, 0xb5, + 0x0d, 0x49, 0xb5, 0x2c, 0xec, 0xaa, 0xae, 0x81, 0x2d, 0xe2, 0x43, 0xf0, 0x27, 0xab, 0xb8, 0x8a, + 0xe9, 0xbf, 0x92, 0xf7, 0x1f, 0x5b, 0xcd, 0xb3, 0x33, 0xf4, 0xd7, 0x56, 0xfd, 0x91, 0xe4, 0x1a, + 0x35, 0x44, 0x5c, 0xb5, 0x66, 0xb3, 0x0d, 0x85, 0x2c, 0xae, 0x86, 0x5e, 0xf8, 0x67, 0x66, 0xf7, + 0x3b, 0xd3, 0x98, 0x93, 0xc8, 0xb6, 0xea, 0x20, 0x5d, 0xd1, 0xb0, 0x45, 0xea, 0xb5, 0xf0, 0xc4, + 0x37, 0x3a, 0x9c, 0x78, 0x62, 0x38, 0x88, 0x6d, 0x9b, 0x72, 0x91, 0xa5, 0x23, 0xa7, 0x66, 0x58, + 0xae, 0xa4, 0x39, 0x4d, 0xdb, 0xc5, 0xd2, 0x63, 0xd4, 0x0c, 0x18, 0x9e, 0xd2, 0x30, 0xa9, 0x61, + 0xa2, 0xf8, 0x24, 0xfd, 0x1f, 0xfe, 0x23, 0xe1, 0x12, 0x98, 0xbc, 0xeb, 0x85, 0x73, 0x89, 0x99, + 0x5d, 0x45, 0x16, 0x22, 0x06, 0x91, 0xd1, 0x4e, 0x1d, 0x11, 0x17, 0x9e, 0x02, 0xa3, 0xbe, 0x6d, + 0x43, 0x9f, 0xe0, 0xa6, 0xb9, 0x99, 0x31, 0xf9, 0x2b, 0xf4, 0x77, 0x59, 0x17, 0x9e, 0x81, 0xa9, + 0xf4, 0x93, 0xc4, 0xc6, 0x16, 0x41, 0xf0, 0x7b, 0xe0, 0x68, 0xd5, 0x5f, 0x52, 0x88, 0xab, 0xba, + 0x88, 0x9e, 0x1f, 0x2f, 0xcc, 0x8a, 0xfb, 0xbd, 0xb1, 0xc6, 0x9c, 0xd8, 0x82, 0x75, 0xcf, 0x3b, + 0x57, 0x1c, 0x7e, 0xb3, 0x9b, 0x1f, 0x90, 0x3f, 0xaa, 0xc6, 0xd6, 0x84, 0x29, 0xc0, 0x27, 0x8c, + 0x2f, 0x79, 0x70, 0x81, 0xd7, 0x82, 0xda, 0x42, 0x2a, 0x78, 0xca, 0x3c, 0x2b, 0x82, 0x11, 0x6a, + 0x9e, 0x4c, 0x70, 0xd3, 0x43, 0x33, 0xe3, 0x85, 0xb3, 0x62, 0x86, 0x24, 0x12, 0x29, 0x88, 0xcc, + 0x4e, 0x0a, 0x67, 0xc0, 0x37, 0xdb, 0x4d, 0xdc, 0x73, 0x55, 0xc7, 0xad, 0x38, 0xd8, 0xc6, 0x44, + 0x35, 0x43, 0x6f, 0x5e, 0x72, 0x60, 0xa6, 0xfb, 0x5e, 0xe6, 0xdb, 0xf7, 0xc1, 0x98, 0x1d, 0x2c, + 0xb2, 0x88, 0x5d, 0xcf, 0xe6, 0x1e, 0x03, 0x5f, 0xd4, 0x75, 0xc3, 0xcb, 0xee, 0x08, 0x3a, 0x02, + 0x14, 0x66, 0xc0, 0x17, 0x69, 0x9e, 0x60, 0xbb, 0xcd, 0xe9, 0x1f, 0x73, 0xe9, 0x04, 0x13, 0x5b, + 0xc3, 0x37, 0xdd, 0xe6, 0xf3, 0x7c, 0x4f, 0x3e, 0xcb, 0xa8, 0x86, 0x1b, 0xaa, 0x99, 0xea, 0xf2, + 0x06, 0x38, 0x42, 0x4d, 0x77, 0x48, 0x45, 0x38, 0x09, 0xc6, 0x34, 0xd3, 0x40, 0x96, 0xeb, 0x3d, + 0x1b, 0xa4, 0xcf, 0x46, 0xfd, 0x85, 0xb2, 0x0e, 0x4f, 0x80, 0x23, 0x2e, 0xb6, 0x95, 0x3b, 0x13, + 0x43, 0xd3, 0xdc, 0xcc, 0x51, 0x79, 0xd8, 0xc5, 0xf6, 0x1d, 0xe1, 0x27, 0x1c, 0xf8, 0x8c, 0xd2, + 0xdb, 0x54, 0x4d, 0x43, 0x57, 0x5d, 0xec, 0xc4, 0xe2, 0xe7, 0x74, 0xcf, 0x7e, 0x38, 0x0f, 0x8e, + 0x07, 0x4c, 0x14, 0x55, 0xd7, 0x1d, 0x44, 0x88, 0x6f, 0xb9, 0x08, 0x3f, 0xec, 0xe6, 0x8f, 0x35, + 0xd5, 0x9a, 0x79, 0x45, 0x60, 0x0f, 0x04, 0xf9, 0xe3, 0x60, 0xef, 0xa2, 0xbf, 0x72, 0x65, 0xf4, + 0xe5, 0xeb, 0xfc, 0xc0, 0x3f, 0x5e, 0xe7, 0x07, 0x84, 0x75, 0x20, 0x74, 0x72, 0x84, 0x85, 0xf8, + 0x0c, 0x38, 0x1e, 0x34, 0x86, 0xd0, 0x9c, 0xef, 0xd1, 0xc7, 0x5a, 0x6c, 0xbf, 0x67, 0xac, 0x9d, + 0x5a, 0x25, 0x66, 0x3c, 0x1b, 0xb5, 0x36, 0x5b, 0x1d, 0xa8, 0xb5, 0xd8, 0xef, 0x44, 0x2d, 0xe9, + 0x48, 0x44, 0xad, 0x2d, 0x92, 0x8c, 0x5a, 0x4b, 0xd4, 0x84, 0x49, 0x70, 0x8a, 0x02, 0x6e, 0x6c, + 0x3b, 0xd8, 0x75, 0x4d, 0x44, 0x7b, 0x41, 0x90, 0xb1, 0x7f, 0xe4, 0x58, 0x4f, 0x68, 0x79, 0xca, + 0xcc, 0xe4, 0xc1, 0x38, 0x31, 0x55, 0xb2, 0xad, 0xd4, 0x90, 0x8b, 0x1c, 0x6a, 0x61, 0x48, 0x06, + 0x74, 0xe9, 0xb6, 0xb7, 0x02, 0x0b, 0xe0, 0x6b, 0xb1, 0x0d, 0x8a, 0x6a, 0x9a, 0xf8, 0x89, 0x6a, + 0x69, 0x88, 0x72, 0x1f, 0x92, 0x4f, 0x44, 0x5b, 0x17, 0x83, 0x47, 0xf0, 0x07, 0x60, 0xc2, 0x42, + 0x4f, 0x5d, 0xc5, 0x41, 0xb6, 0x89, 0x2c, 0x83, 0x6c, 0x2b, 0x9a, 0x6a, 0xe9, 0x1e, 0x59, 0x44, + 0xd3, 0x6d, 0xbc, 0xc0, 0x8b, 0xfe, 0x3d, 0x22, 0x06, 0xf7, 0x88, 0xb8, 0x11, 0xdc, 0x23, 0xc5, + 0x51, 0xaf, 0xb1, 0xbd, 0xfa, 0x6b, 0x9e, 0x93, 0x3f, 0xf1, 0x50, 0xe4, 0x00, 0x64, 0x29, 0xc0, + 0x10, 0xce, 0x83, 0xb3, 0x94, 0x92, 0x8c, 0xaa, 0x06, 0x71, 0x91, 0x83, 0xf4, 0xa8, 0x64, 0x9e, + 0xa8, 0x8e, 0x5e, 0x42, 0x16, 0xae, 0x85, 0x35, 0xbb, 0x0c, 0xce, 0x65, 0xda, 0xcd, 0x22, 0xf2, + 0x09, 0x18, 0xd1, 0xe9, 0x0a, 0x6d, 0x83, 0x63, 0x32, 0xfb, 0x25, 0xe4, 0x58, 0x63, 0xf7, 0xcb, + 0x11, 0xe9, 0xb4, 0xfc, 0xca, 0xa5, 0xd0, 0xcc, 0x0b, 0x0e, 0x9c, 0xde, 0x67, 0x03, 0x43, 0x7e, + 0x08, 0x8e, 0xd9, 0xf1, 0x67, 0x41, 0xa3, 0x2d, 0x64, 0xea, 0x0a, 0x09, 0x58, 0xd6, 0xfd, 0x5b, + 0xf0, 0x84, 0x32, 0x38, 0x9a, 0xd8, 0x06, 0x27, 0x00, 0xcb, 0xdf, 0x52, 0x32, 0x9d, 0x4b, 0x30, + 0x07, 0x40, 0xd0, 0x4d, 0xca, 0x25, 0xfa, 0x32, 0x87, 0xe5, 0xd8, 0x8a, 0x70, 0x0b, 0x48, 0x94, + 0xcd, 0xa2, 0x69, 0x56, 0x54, 0xc3, 0x21, 0x9b, 0xaa, 0xb9, 0x84, 0x2d, 0x2f, 0xe5, 0x8a, 0xc9, + 0xe6, 0x57, 0x2e, 0x65, 0xb8, 0x15, 0x7f, 0xc5, 0x81, 0xd9, 0xec, 0x70, 0x2c, 0x5e, 0x3b, 0xe0, + 0xab, 0xb6, 0x6a, 0x38, 0x4a, 0x43, 0x35, 0xbd, 0xfb, 0x9f, 0x96, 0x01, 0x0b, 0xd9, 0x4a, 0xb6, + 0x90, 0xa9, 0x86, 0x13, 0x19, 0x0a, 0xcb, 0xcc, 0x8a, 0x12, 0xe0, 0x98, 0x9d, 0xd8, 0x22, 0xfc, + 0x9b, 0x03, 0x9f, 0x75, 0x3d, 0x05, 0x57, 0xf6, 0xab, 0xcd, 0xe2, 0xe4, 0x87, 0xdd, 0xfc, 0xa7, + 0x7e, 0x2b, 0x68, 0xdd, 0xd1, 0xde, 0xee, 0x3c, 0x9c, 0x7d, 0x5a, 0x4a, 0x0c, 0xa7, 0x75, 0x47, + 0x7b, 0x6f, 0x81, 0x0b, 0xe0, 0xa3, 0x70, 0xd7, 0x63, 0xd4, 0x64, 0x35, 0x36, 0x25, 0x46, 0xd3, + 0x8f, 0xe8, 0x4f, 0x3f, 0x62, 0xa5, 0xbe, 0x65, 0x1a, 0xda, 0x1a, 0x6a, 0xca, 0xe3, 0xc1, 0x89, + 0x35, 0xd4, 0x14, 0x4e, 0x02, 0xe8, 0xa7, 0xae, 0xea, 0xa8, 0x51, 0xe1, 0x3c, 0x04, 0x27, 0x12, + 0xab, 0xec, 0xb5, 0x94, 0xc1, 0x88, 0x4d, 0x57, 0xd8, 0xa5, 0x76, 0x2e, 0xe3, 0xbb, 0xf0, 0x8e, + 0xb0, 0xbc, 0x65, 0x00, 0xc2, 0x2a, 0x2b, 0xe4, 0x44, 0x06, 0xac, 0xdb, 0x2e, 0xd2, 0xcb, 0x56, + 0xd8, 0x1e, 0xb3, 0x4c, 0x5d, 0x3b, 0xac, 0xc6, 0xbb, 0x01, 0x85, 0xa3, 0xce, 0xe9, 0x46, 0xb8, + 0xaa, 0xb4, 0xbe, 0x29, 0x14, 0x94, 0xfe, 0x64, 0xb4, 0xa9, 0x92, 0x7c, 0x75, 0x88, 0x08, 0x3b, + 0x2c, 0xa3, 0x93, 0xd3, 0x54, 0x68, 0xec, 0xa6, 0x4a, 0x36, 0x30, 0xfb, 0x15, 0x34, 0xe3, 0xd4, + 0xeb, 0x91, 0xcb, 0x7c, 0x3d, 0x0a, 0x2a, 0x98, 0xeb, 0xc1, 0x24, 0xe3, 0x7a, 0x1e, 0xc0, 0x30, + 0x39, 0x82, 0xf0, 0x05, 0x04, 0xc3, 0xf4, 0xf3, 0x4b, 0x4f, 0xa7, 0xd7, 0xe4, 0xb9, 0xf4, 0x8b, + 0x77, 0x09, 0xd7, 0x6a, 0x06, 0x21, 0x06, 0xb6, 0xe4, 0x18, 0xa3, 0xff, 0xdb, 0x2c, 0x20, 0xfc, + 0x90, 0x03, 0xe7, 0xb3, 0x79, 0xc2, 0x88, 0x56, 0xc0, 0xb0, 0x13, 0x0c, 0xd4, 0x63, 0xc5, 0x6b, + 0x5e, 0xa2, 0xfd, 0x65, 0x37, 0xff, 0x45, 0xd5, 0x70, 0xb7, 0xeb, 0x5b, 0xa2, 0x86, 0x6b, 0x6c, + 0xc4, 0x67, 0x7f, 0x2e, 0x10, 0xfd, 0xb1, 0xe4, 0x36, 0x6d, 0x44, 0xc4, 0x12, 0xd2, 0xfe, 0xf4, + 0xdb, 0x0b, 0x80, 0x29, 0x80, 0x12, 0xd2, 0x64, 0x8a, 0x24, 0xcc, 0x83, 0x69, 0xea, 0xc1, 0xba, + 0xa9, 0x23, 0xe2, 0xde, 0xb7, 0x34, 0x6c, 0x3d, 0x32, 0x9c, 0x1a, 0xd2, 0x37, 0x89, 0x96, 0x21, + 0x29, 0x7f, 0x1a, 0x8c, 0x1c, 0xe9, 0xe7, 0x99, 0xdb, 0x06, 0x80, 0x0d, 0xa2, 0x29, 0x04, 0x59, + 0xba, 0x12, 0x8a, 0x29, 0x56, 0x5a, 0x5f, 0x66, 0x2a, 0xad, 0x4d, 0xa2, 0xdd, 0x43, 0x96, 0x1e, + 0xdd, 0xa0, 0x7e, 0x91, 0x1d, 0x6f, 0xb4, 0xac, 0x17, 0x7e, 0x7f, 0x1a, 0x1c, 0xa1, 0x0e, 0xc1, + 0x3d, 0x0e, 0x9c, 0x4c, 0x93, 0x29, 0xf0, 0x46, 0x26, 0x8b, 0x1d, 0xb4, 0x11, 0xbf, 0x78, 0x00, + 0x04, 0x3f, 0x24, 0xc2, 0xf2, 0x8f, 0xde, 0xfd, 0xfd, 0xe7, 0x83, 0x0b, 0x70, 0xbe, 0xbb, 0xee, + 0x0d, 0x53, 0x9b, 0xe9, 0x20, 0xe9, 0x59, 0xf0, 0x36, 0x9e, 0xc3, 0x77, 0x1c, 0x6b, 0x60, 0xc9, + 0x7a, 0x81, 0x0b, 0xbd, 0x7b, 0x98, 0x10, 0x52, 0xfc, 0x8d, 0xfe, 0x01, 0x18, 0xc3, 0xcb, 0x94, + 0xe1, 0x45, 0x38, 0xd7, 0x03, 0x43, 0x5f, 0x62, 0xc1, 0x17, 0x83, 0x60, 0x62, 0x1f, 0xdd, 0x44, + 0xe0, 0xad, 0x3e, 0x3d, 0x4b, 0x95, 0x68, 0xfc, 0xed, 0x43, 0x42, 0x63, 0xa4, 0x6f, 0x52, 0xd2, + 0x45, 0x78, 0xa3, 0x57, 0xd2, 0x9e, 0x52, 0x76, 0x5c, 0x25, 0x54, 0x3f, 0xf0, 0xbf, 0x1c, 0xf8, + 0x34, 0x5d, 0x86, 0x11, 0xb8, 0xd6, 0xb7, 0xd3, 0xed, 0x7a, 0x8f, 0xbf, 0x75, 0x38, 0x60, 0x2c, + 0x00, 0xab, 0x34, 0x00, 0x8b, 0x70, 0xa1, 0x8f, 0x00, 0x60, 0x3b, 0xc6, 0xff, 0x5f, 0xc1, 0x50, + 0x9f, 0x2a, 0x8f, 0xe0, 0x4a, 0x76, 0xaf, 0x3b, 0x09, 0x3d, 0x7e, 0xf5, 0xc0, 0x38, 0x8c, 0xf8, + 0x22, 0x25, 0x7e, 0x15, 0x5e, 0xce, 0xf0, 0x21, 0x2b, 0x00, 0x52, 0x12, 0x83, 0x4f, 0x0a, 0xe5, + 0xf8, 0x95, 0xdc, 0x17, 0xe5, 0x14, 0x01, 0xd8, 0x17, 0xe5, 0x34, 0xfd, 0xd6, 0x1f, 0xe5, 0xc4, + 0x7d, 0x09, 0xff, 0xc0, 0xb1, 0xb1, 0x2c, 0x21, 0xdd, 0xe0, 0xf5, 0xec, 0x2e, 0xa6, 0x29, 0x42, + 0x7e, 0xa1, 0xef, 0xf3, 0x8c, 0xda, 0x25, 0x4a, 0xad, 0x00, 0x67, 0xbb, 0x53, 0x73, 0x19, 0x80, + 0xff, 0xad, 0x0b, 0xfe, 0x72, 0x10, 0x7c, 0x9e, 0x41, 0x8b, 0xc1, 0xf5, 0xec, 0x2e, 0x66, 0xd2, + 0x80, 0x7c, 0xe5, 0xf0, 0x00, 0x59, 0x10, 0xd6, 0x68, 0x10, 0x96, 0xe1, 0x52, 0xf7, 0x20, 0x38, + 0x21, 0x62, 0x94, 0xd3, 0x0e, 0xc5, 0x54, 0x7c, 0x6d, 0x09, 0xff, 0xd9, 0xa6, 0x1d, 0x93, 0x92, + 0x88, 0xc0, 0x1e, 0x6e, 0xd5, 0x7d, 0x04, 0x2a, 0x5f, 0x3c, 0x08, 0x04, 0x63, 0x5d, 0xa4, 0xac, + 0xaf, 0xc1, 0x2b, 0xdd, 0x59, 0x07, 0xd2, 0x54, 0x69, 0xbd, 0xc0, 0x7e, 0x31, 0xc8, 0x3e, 0xfc, + 0x65, 0xd0, 0x82, 0x70, 0x23, 0xbb, 0xd3, 0xd9, 0x95, 0x2a, 0x7f, 0xff, 0x90, 0x51, 0x59, 0x74, + 0xae, 0xd2, 0xe8, 0x7c, 0x09, 0x2f, 0xf6, 0xdc, 0xdf, 0x0d, 0x1d, 0xfe, 0x86, 0x03, 0xe3, 0x31, + 0xb9, 0x05, 0xbf, 0xdd, 0xc3, 0xeb, 0x8a, 0xcb, 0x36, 0xfe, 0x52, 0xef, 0x07, 0x99, 0xff, 0xb3, + 0xd4, 0xff, 0xb3, 0x70, 0x26, 0xc3, 0xdb, 0xf5, 0x9d, 0xfc, 0x59, 0x50, 0xd0, 0x9d, 0x85, 0x57, + 0x2f, 0x05, 0x9d, 0x49, 0x0b, 0xf6, 0x52, 0xd0, 0xd9, 0x34, 0x61, 0x2f, 0xd3, 0x09, 0xf6, 0x40, + 0x14, 0xc3, 0x52, 0x22, 0x7d, 0x18, 0x9f, 0x3b, 0x7f, 0x37, 0x08, 0xce, 0x64, 0xd6, 0x69, 0xf0, + 0x7e, 0xbf, 0xc3, 0x64, 0x47, 0xa9, 0xc9, 0x6f, 0x1e, 0x36, 0x2c, 0x0b, 0xd3, 0x03, 0x1a, 0xa6, + 0x0d, 0x28, 0xf7, 0x3c, 0xb9, 0x2a, 0x36, 0x72, 0xa2, 0x88, 0x49, 0xcf, 0x5a, 0xc5, 0xe1, 0x73, + 0xf8, 0xeb, 0x41, 0xf0, 0xf5, 0x2c, 0x92, 0x0f, 0x56, 0x0e, 0x30, 0x98, 0xa4, 0xea, 0x58, 0xfe, + 0xee, 0x21, 0x22, 0xb2, 0x48, 0x3d, 0xa4, 0x91, 0x7a, 0x00, 0xbf, 0xdb, 0x4b, 0xa4, 0x42, 0x28, + 0xc5, 0x53, 0xa0, 0xb1, 0xac, 0x4a, 0x8b, 0xd7, 0x7f, 0x38, 0xf6, 0xe5, 0x37, 0x4d, 0x60, 0xc2, + 0xe5, 0xec, 0x94, 0x3a, 0x08, 0x5c, 0x7e, 0xe5, 0xa0, 0x30, 0xbd, 0x5f, 0x98, 0x98, 0xe2, 0x28, + 0xf5, 0x08, 0x48, 0x69, 0x10, 0x2d, 0x16, 0x8c, 0xe2, 0x77, 0xde, 0xec, 0xe5, 0xb8, 0xb7, 0x7b, + 0x39, 0xee, 0x6f, 0x7b, 0x39, 0xee, 0xd5, 0xfb, 0xdc, 0xc0, 0xdb, 0xf7, 0xb9, 0x81, 0x3f, 0xbf, + 0xcf, 0x0d, 0x3c, 0x98, 0x6f, 0xd7, 0xfb, 0x91, 0xbd, 0x0b, 0xa1, 0xbd, 0xc6, 0xb7, 0xa4, 0xa7, + 0x2d, 0xa3, 0x4a, 0xd3, 0x46, 0x64, 0x6b, 0x84, 0x7e, 0x90, 0xbe, 0xf8, 0xbf, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x21, 0x1e, 0x15, 0x5a, 0x72, 0x1d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/ccv/provider/types/query.pb.gw.go b/x/ccv/provider/types/query.pb.gw.go index e44ddd00f5..2be0974fbf 100644 --- a/x/ccv/provider/types/query.pb.gw.go +++ b/x/ccv/provider/types/query.pb.gw.go @@ -321,19 +321,26 @@ func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Ma } -var ( - filter_Query_QueryConsumerChainOptedInValidators_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} -) - func request_Query_QueryConsumerChainOptedInValidators_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryConsumerChainOptedInValidatorsRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryConsumerChainOptedInValidators_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) } msg, err := client.QueryConsumerChainOptedInValidators(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -345,11 +352,22 @@ func local_request_Query_QueryConsumerChainOptedInValidators_0(ctx context.Conte var protoReq QueryConsumerChainOptedInValidatorsRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryConsumerChainOptedInValidators_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) } msg, err := server.QueryConsumerChainOptedInValidators(ctx, &protoReq) @@ -357,19 +375,26 @@ func local_request_Query_QueryConsumerChainOptedInValidators_0(ctx context.Conte } -var ( - filter_Query_QueryConsumerChainsValidatorHasToValidate_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} -) - func request_Query_QueryConsumerChainsValidatorHasToValidate_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryConsumerChainsValidatorHasToValidateRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["provider_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_address") } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryConsumerChainsValidatorHasToValidate_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + + protoReq.ProviderAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_address", err) } msg, err := client.QueryConsumerChainsValidatorHasToValidate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -381,11 +406,22 @@ func local_request_Query_QueryConsumerChainsValidatorHasToValidate_0(ctx context var protoReq QueryConsumerChainsValidatorHasToValidateRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["provider_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_address") } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryConsumerChainsValidatorHasToValidate_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + + protoReq.ProviderAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_address", err) } msg, err := server.QueryConsumerChainsValidatorHasToValidate(ctx, &protoReq) @@ -393,19 +429,37 @@ func local_request_Query_QueryConsumerChainsValidatorHasToValidate_0(ctx context } -var ( - filter_Query_QueryValidatorConsumerCommissionRate_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} -) - func request_Query_QueryValidatorConsumerCommissionRate_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryValidatorConsumerCommissionRateRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidatorConsumerCommissionRate_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) + } + + val, ok = pathParams["provider_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_address") + } + + protoReq.ProviderAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_address", err) } msg, err := client.QueryValidatorConsumerCommissionRate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -417,11 +471,33 @@ func local_request_Query_QueryValidatorConsumerCommissionRate_0(ctx context.Cont var protoReq QueryValidatorConsumerCommissionRateRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_QueryValidatorConsumerCommissionRate_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) + } + + val, ok = pathParams["provider_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_address") + } + + protoReq.ProviderAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_address", err) } msg, err := server.QueryValidatorConsumerCommissionRate(ctx, &protoReq) @@ -1201,11 +1277,11 @@ var ( pattern_Query_QueryParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryConsumerChainOptedInValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "opted_in_validators"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryConsumerChainOptedInValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "opted_in_validators", "chain_id"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryConsumerChainsValidatorHasToValidate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "consumer_chains_per_validator"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryConsumerChainsValidatorHasToValidate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "consumer_chains_per_validator", "provider_address"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_QueryValidatorConsumerCommissionRate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "consumer_commission_rate"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryValidatorConsumerCommissionRate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"interchain_security", "ccv", "provider", "consumer_commission_rate", "chain_id", "provider_address"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryOldestUnconfirmedVsc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "oldest_unconfirmed_vsc", "chain_id"}, "", runtime.AssumeColonVerbOpt(false))) ) diff --git a/x/ccv/provider/types/tx.pb.go b/x/ccv/provider/types/tx.pb.go index 7330433f5b..1fd4869548 100644 --- a/x/ccv/provider/types/tx.pb.go +++ b/x/ccv/provider/types/tx.pb.go @@ -276,9 +276,10 @@ type MsgOptIn struct { ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` // the validator address on the provider ProviderAddr string `protobuf:"bytes,2,opt,name=provider_addr,json=providerAddr,proto3" json:"provider_addr,omitempty" yaml:"address"` - // (optional) the consensus public key to use on the consumer in json string format corresponding to proto-any, - // for example `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}` - // we can set `consumer_key = ""` if we do not consider the `consumer_key` + // (optional) The consensus public key to use on the consumer in json string format corresponding to proto-any, + // for example `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}`. + // This field is optional and can remain empty (i.e., `consumer_key = ""`). A validator can always change the + // consumer public key at a later stage by issuing a `MsgAssignConsumerKey` message. ConsumerKey string `protobuf:"bytes,3,opt,name=consumer_key,json=consumerKey,proto3" json:"consumer_key,omitempty"` } From 97b1f742ac050513ade7cae3dd76517d3a1ca318 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 1 May 2024 15:33:15 +0200 Subject: [PATCH 16/16] fix merge issue and format --- x/ccv/provider/keeper/keeper.go | 7 +++++-- x/ccv/provider/keeper/partial_set_security_test.go | 1 + x/ccv/provider/keeper/proposal.go | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index c113c8a387..bc3664bfd7 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -22,6 +22,7 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cometbft/cometbft/libs/log" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" @@ -1269,8 +1270,10 @@ func (k Keeper) HasToValidate( bondedValidators := k.stakingKeeper.GetLastValidators(ctx) if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN) - k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN) + if err == nil { + k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + } } // if the validator is opted in and belongs to the validators of the next epoch, then if nothing changes diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 0258d3c1a9..afbd420e7f 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "bytes" + "math" "sort" "testing" diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index b13e85bc48..1d2bf19a4f 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -219,13 +219,13 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo k.DeleteUnbondingOpIndex(ctx, chainID, unbondingOpsIndex.VscId) } - k.DeleteTopN(ctx, chainID) + k.DeleteTopN(ctx, chainID) k.DeleteValidatorsPowerCap(ctx, chainID) k.DeleteValidatorSetCap(ctx, chainID) k.DeleteAllowlist(ctx, chainID) k.DeleteDenylist(ctx, chainID) - k.DeleteAllOptedIn(ctx, chainID) + k.DeleteAllOptedIn(ctx, chainID) k.DeleteConsumerValSet(ctx, chainID) k.Logger(ctx).Info("consumer chain removed from provider", "chainID", chainID)