From 3ca33ecc9d3aec2533104e3b4b1ae0e2dd9be032 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Wed, 20 Mar 2024 10:09:54 +0100 Subject: [PATCH 1/5] feat!: Add slashing logic for PSS (#1710) * add check for consumer validators in downtime logic * fix UT * try to fix weird errors in gh worfklow * fix silly merge bug * nits --- x/ccv/provider/keeper/keeper_test.go | 1 - x/ccv/provider/keeper/relay.go | 8 ++++ x/ccv/provider/keeper/relay_test.go | 65 +++++++++++++++++++--------- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index feb87490c6..90faec1c85 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -9,7 +9,6 @@ import ( ibctesting "github.com/cosmos/ibc-go/v7/testing" "github.com/stretchr/testify/require" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 939f6d3995..1644ce263a 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -391,6 +391,14 @@ func (k Keeper) HandleSlashPacket(ctx sdk.Context, chainID string, data ccv.Slas "infractionType", data.Infraction, ) + // Check that the validator belongs to the consumer chain valset + if !k.IsConsumerValidator(ctx, chainID, providerConsAddr) { + k.Logger(ctx).Error("cannot jail validator %s that does not belong to consumer %s valset", + providerConsAddr.String(), chainID) + // drop packet + return + } + // Obtain validator from staking keeper validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerConsAddr.ToSdkConsAddr()) diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 2c5a24cab8..bb5a5227c6 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -22,6 +22,7 @@ import ( cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/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" providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) @@ -136,6 +137,9 @@ func TestOnRecvDowntimeSlashPacket(t *testing.T) { // Now set slash meter to positive value and assert slash packet handled result is returned providerKeeper.SetSlashMeter(ctx, math.NewInt(5)) + // Set the consumer validator + providerKeeper.SetConsumerValidator(ctx, "chain-1", types.ConsumerValidator{ProviderConsAddr: packetData.Validator.Address}) + // Mock call to GetEffectiveValPower, so that it returns 2. providerAddr := providertypes.NewProviderConsAddress(packetData.Validator.Address) calls := []*gomock.Call{ @@ -289,8 +293,11 @@ func TestValidateSlashPacket(t *testing.T) { func TestHandleSlashPacket(t *testing.T) { chainId := "consumer-id" validVscID := uint64(234) + providerConsAddr := cryptotestutil.NewCryptoIdentityFromIntSeed(7842334).ProviderConsAddress() consumerConsAddr := cryptotestutil.NewCryptoIdentityFromIntSeed(784987634).ConsumerConsAddress() + // this "dummy" consensus address won't be stored on the provider states + dummyConsAddr := cryptotestutil.NewCryptoIdentityFromIntSeed(784987639).ConsumerConsAddress() testCases := []struct { name string @@ -299,6 +306,20 @@ func TestHandleSlashPacket(t *testing.T) { expectedCalls func(sdk.Context, testkeeper.MockedKeepers, ccv.SlashPacketData) []*gomock.Call expectedSlashAcksLen int }{ + { + "validator isn't a consumer validator", + ccv.SlashPacketData{ + Validator: abci.Validator{Address: dummyConsAddr.ToSdkConsAddr()}, + ValsetUpdateId: validVscID, + Infraction: stakingtypes.Infraction_INFRACTION_DOWNTIME, + }, + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, + expectedPacketData ccv.SlashPacketData, + ) []*gomock.Call { + return []*gomock.Call{} + }, + 0, + }, { "unfound validator", ccv.SlashPacketData{ @@ -403,34 +424,36 @@ func TestHandleSlashPacket(t *testing.T) { } for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx( + t, testkeeper.NewInMemKeeperParams(t)) - providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx( - t, testkeeper.NewInMemKeeperParams(t)) - - // Setup expected mock calls - gomock.InOrder(tc.expectedCalls(ctx, mocks, tc.packetData)...) + // Setup expected mock calls + gomock.InOrder(tc.expectedCalls(ctx, mocks, tc.packetData)...) - // Setup init chain height and a single valid valset update ID to block height mapping. - providerKeeper.SetInitChainHeight(ctx, chainId, 5) - providerKeeper.SetValsetUpdateBlockHeight(ctx, validVscID, 99) + // Setup init chain height and a single valid valset update ID to block height mapping. + providerKeeper.SetInitChainHeight(ctx, chainId, 5) + providerKeeper.SetValsetUpdateBlockHeight(ctx, validVscID, 99) - // Setup consumer address to provider address mapping. - require.NotEmpty(t, tc.packetData.Validator.Address) - providerKeeper.SetValidatorByConsumerAddr(ctx, chainId, consumerConsAddr, providerConsAddr) + // Setup consumer address to provider address mapping. + require.NotEmpty(t, tc.packetData.Validator.Address) + providerKeeper.SetValidatorByConsumerAddr(ctx, chainId, consumerConsAddr, providerConsAddr) + providerKeeper.SetConsumerValidator(ctx, chainId, types.ConsumerValidator{ProviderConsAddr: providerConsAddr.Address.Bytes()}) - // Execute method and assert expected mock calls. - providerKeeper.HandleSlashPacket(ctx, chainId, tc.packetData) + // Execute method and assert expected mock calls. + providerKeeper.HandleSlashPacket(ctx, chainId, tc.packetData) - require.Equal(t, tc.expectedSlashAcksLen, len(providerKeeper.GetSlashAcks(ctx, chainId))) + require.Equal(t, tc.expectedSlashAcksLen, len(providerKeeper.GetSlashAcks(ctx, chainId))) - if tc.expectedSlashAcksLen == 1 { - // must match the consumer address - require.Equal(t, consumerConsAddr.String(), providerKeeper.GetSlashAcks(ctx, chainId)[0]) - require.NotEqual(t, providerConsAddr.String(), providerKeeper.GetSlashAcks(ctx, chainId)[0]) - require.NotEqual(t, providerConsAddr.String(), consumerConsAddr.String()) - } + if tc.expectedSlashAcksLen == 1 { + // must match the consumer address + require.Equal(t, consumerConsAddr.String(), providerKeeper.GetSlashAcks(ctx, chainId)[0]) + require.NotEqual(t, providerConsAddr.String(), providerKeeper.GetSlashAcks(ctx, chainId)[0]) + require.NotEqual(t, providerConsAddr.String(), consumerConsAddr.String()) + } - ctrl.Finish() + ctrl.Finish() + }) } } From 79abc145c21d55b0a1ce6063779e5a09d4019e5b Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 21 Mar 2024 14:39:21 +0100 Subject: [PATCH 2/5] ci: do not scan the tests for security issues (#1717) init commit --- .github/workflows/gosec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gosec.yml b/.github/workflows/gosec.yml index 3955b92323..90f1379eb1 100644 --- a/.github/workflows/gosec.yml +++ b/.github/workflows/gosec.yml @@ -28,4 +28,4 @@ jobs: - name: Run Gosec Security Scanner uses: securego/gosec@master with: - args: -exclude-dir=legacy_ibc_testing ./... -exclude-generated ./... + args: -exclude-dir=tests ./... -exclude-generated ./... From b25114105ceb728c20bb06eb341d67ee8955adcf Mon Sep 17 00:00:00 2001 From: insumity Date: Mon, 25 Mar 2024 18:00:10 +0100 Subject: [PATCH 3/5] feat!: compute partial sets (#1702) * init commit * nit change * cleaning up * clean up * fix distribution test * Update x/ccv/provider/keeper/hooks.go Co-authored-by: Simon Noetzlin * took into Simon's comments * took into rest of the comments * nit change * return an error if validator cannot opt out from a Top N chain * removed automatic opt-in for validators that vote Yes on proposals * tiny fix for E2E tests * nit change to remove unecessary else * fixed topN == 0 issue --------- Co-authored-by: Simon Noetzlin --- .../ccv/provider/v1/provider.proto | 13 - tests/e2e/actions.go | 1 + tests/mbt/driver/setup.go | 3 +- testutil/keeper/mocks.go | 29 - x/ccv/provider/keeper/hooks.go | 56 -- x/ccv/provider/keeper/hooks_test.go | 89 --- x/ccv/provider/keeper/keeper.go | 121 +--- x/ccv/provider/keeper/keeper_test.go | 192 +------ x/ccv/provider/keeper/key_assignment_test.go | 5 +- x/ccv/provider/keeper/msg_server.go | 30 +- x/ccv/provider/keeper/partial_set_security.go | 112 +++- .../keeper/partial_set_security_test.go | 213 ++++++- x/ccv/provider/keeper/proposal.go | 11 +- x/ccv/provider/keeper/relay.go | 12 +- x/ccv/provider/keeper/relay_test.go | 22 +- x/ccv/provider/keeper/validator_set_update.go | 124 ++-- .../keeper/validator_set_update_test.go | 223 ++++++-- x/ccv/provider/types/errors.go | 1 + x/ccv/provider/types/keys.go | 32 +- x/ccv/provider/types/keys_test.go | 4 +- x/ccv/provider/types/provider.pb.go | 538 ++++-------------- x/ccv/types/expected_keepers.go | 2 - 22 files changed, 727 insertions(+), 1106 deletions(-) diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 8c70c9e428..1b5f7a515f 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -326,16 +326,3 @@ message ConsumerRewardsAllocation { (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins" ]; } - -// OptedInValidator is used to store a opted-in validator -// to a consumer chain with the following mapping: (chainID, providerAddr) -> optedInValidator -message OptedInValidator { - // validator address - bytes provider_addr = 1; - // block height at which the validator opted-in - int64 block_height = 2; - // validator voting power at the block it opted-in - int64 power = 3; - // public key used by the validator on the consumer - bytes public_key = 4; -} \ No newline at end of file diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 5fea89ec12..e49c37ae71 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -278,6 +278,7 @@ func (tr TestConfig) submitConsumerAdditionProposal( UnbondingPeriod: params.UnbondingPeriod, Deposit: fmt.Sprint(action.Deposit) + `stake`, DistributionTransmissionChannel: action.DistributionChannel, + TopN: 100, } bz, err := json.Marshal(prop) diff --git a/tests/mbt/driver/setup.go b/tests/mbt/driver/setup.go index 6408f0e873..deeae01f6b 100644 --- a/tests/mbt/driver/setup.go +++ b/tests/mbt/driver/setup.go @@ -373,7 +373,8 @@ func (s *Driver) ConfigureNewPath(consumerChain, providerChain *ibctesting.TestC stakingValidators = append(stakingValidators, v) } - nextValidators := s.providerKeeper().ComputeNextEpochConsumerValSet(s.providerCtx(), string(consumerChainId), stakingValidators) + considerAll := func(validator stakingtypes.Validator) bool { return true } + nextValidators := s.providerKeeper().ComputeNextEpochConsumerValSet(s.providerCtx(), string(consumerChainId), stakingValidators, considerAll) s.providerKeeper().SetConsumerValSet(s.providerCtx(), string(consumerChainId), nextValidators) err = s.providerKeeper().SetConsumerGenesis( diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index 78c0fbedc9..e588f51e30 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -1270,32 +1270,3 @@ func (mr *MockGovKeeperMockRecorder) GetProposal(ctx, proposalID interface{}) *g mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProposal", reflect.TypeOf((*MockGovKeeper)(nil).GetProposal), ctx, proposalID) } - -// GetProposals mocks base method. -func (m *MockGovKeeper) GetProposals(ctx types0.Context) v1.Proposals { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetProposals", ctx) - ret0, _ := ret[0].(v1.Proposals) - return ret0 -} - -// GetProposals indicates an expected call of GetProposals. -func (mr *MockGovKeeperMockRecorder) GetProposals(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProposals", reflect.TypeOf((*MockGovKeeper)(nil).GetProposals), ctx) -} - -// GetVote mocks base method. -func (m *MockGovKeeper) GetVote(ctx types0.Context, proposalID uint64, voterAddr types0.AccAddress) (v1.Vote, bool) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVote", ctx, proposalID, voterAddr) - ret0, _ := ret[0].(v1.Vote) - ret1, _ := ret[1].(bool) - return ret0, ret1 -} - -// GetVote indicates an expected call of GetVote. -func (mr *MockGovKeeperMockRecorder) GetVote(ctx, proposalID, voterAddr interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVote", reflect.TypeOf((*MockGovKeeper)(nil).GetVote), ctx, proposalID, voterAddr) -} diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 13cf0cc382..88590a9875 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -3,8 +3,6 @@ package keeper import ( "fmt" - "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" sdkgov "github.com/cosmos/cosmos-sdk/x/gov/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" @@ -177,61 +175,7 @@ func (h Hooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64 func (h Hooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) { } -// AfterProposalVote opts in validators that vote YES (with 100% weight) on a `ConsumerAdditionProposal`. If a -// validator votes multiple times, only the last vote would be considered on whether the validator is opted in or not. func (h Hooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { - validator, found := h.k.stakingKeeper.GetValidator(ctx, voterAddr.Bytes()) - if !found { - return - } - - consAddr, err := validator.GetConsAddr() - if err != nil { - h.k.Logger(ctx).Error("could not extract validator's consensus address", - "error", err.Error(), - "validator acc addr", voterAddr, - ) - return - } - - chainID, found := h.k.GetProposedConsumerChain(ctx, proposalID) - if !found { - return - } - - vote, found := h.k.govKeeper.GetVote(ctx, proposalID, voterAddr) - if !found { - h.k.Logger(ctx).Error("could not find vote for validator", - "validator acc addr", voterAddr, - "proposalID", proposalID, - ) - return - } - - if len(vote.Options) == 1 && vote.Options[0].Option == v1.VoteOption_VOTE_OPTION_YES { - // only consider votes that vote YES with 100% weight - weight, err := sdk.NewDecFromStr(vote.Options[0].Weight) - if err != nil { - h.k.Logger(ctx).Error("could not extract decimal value from vote's weight", - "vote", vote, - "error", err.Error(), - ) - return - } - if !weight.Equal(math.LegacyNewDec(1)) { - h.k.Logger(ctx).Error("single vote does not have a weight of 1", - "vote", vote, - ) - return - } - - // in the validator is already to-be-opted in, the `SetToBeOptedIn` is a no-op - h.k.SetToBeOptedIn(ctx, chainID, providertypes.NewProviderConsAddress(consAddr)) - } else { - // if vote is not a YES vote with 100% weight, we delete the validator from to-be-opted in - // if the validator is not to-be-opted in, the `DeleteToBeOptedIn` is a no-op - h.k.DeleteToBeOptedIn(ctx, chainID, providertypes.NewProviderConsAddress(consAddr)) - } } func (h Hooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) { diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index c2c8df36e2..a9cf69b350 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -9,19 +9,14 @@ import ( "cosmossdk.io/math" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/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" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) func TestValidatorConsensusKeyInUse(t *testing.T) { @@ -229,87 +224,3 @@ func TestGetConsumerAdditionLegacyPropFromProp(t *testing.T) { }) } } - -func TestAfterProposalVoteWithYesVote(t *testing.T) { - k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey() - pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) - providerAddr := types.NewProviderConsAddress(providerConsPubKey.Address().Bytes()) - - options := []*v1.WeightedVoteOption{{Option: v1.OptionYes, Weight: "1"}} - k.SetProposedConsumerChain(ctx, "chainID", 1) - - gomock.InOrder( - mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return( - stakingtypes.Validator{ConsensusPubkey: pkAny}, true), - mocks.MockGovKeeper.EXPECT().GetVote(ctx, gomock.Any(), gomock.Any()).Return( - v1.Vote{ProposalId: 1, Voter: "voter", Options: options}, true, - ), - ) - - require.False(t, k.IsToBeOptedIn(ctx, "chainID", providerAddr)) - k.Hooks().AfterProposalVote(ctx, 1, sdk.AccAddress{}) - require.True(t, k.IsToBeOptedIn(ctx, "chainID", providerAddr)) -} - -func TestAfterProposalVoteWithNoVote(t *testing.T) { - testCases := []struct { - name string - options []*v1.WeightedVoteOption - setup func(sdk.Context, []*v1.WeightedVoteOption, testkeeper.MockedKeepers, *codectypes.Any) - }{ - { - "Weighted vote with 100% NO", - []*v1.WeightedVoteOption{{Option: v1.OptionNo, Weight: "1"}}, - func(ctx sdk.Context, options []*v1.WeightedVoteOption, - mocks testkeeper.MockedKeepers, pubKey *codectypes.Any, - ) { - gomock.InOrder( - mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return( - stakingtypes.Validator{ConsensusPubkey: pubKey}, true), - mocks.MockGovKeeper.EXPECT().GetVote(ctx, gomock.Any(), gomock.Any()).Return( - v1.Vote{ProposalId: 1, Voter: "voter", Options: options}, true, - ), - ) - }, - }, - { - "Weighted vote with 99.9% YES and 0.1% NO", - []*v1.WeightedVoteOption{{Option: v1.OptionYes, Weight: "0.999"}, {Option: v1.OptionNo, Weight: "0.001"}}, - func(ctx sdk.Context, options []*v1.WeightedVoteOption, - mocks testkeeper.MockedKeepers, pubKey *codectypes.Any, - ) { - gomock.InOrder( - mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, gomock.Any()).Return( - stakingtypes.Validator{ConsensusPubkey: pubKey}, true), - mocks.MockGovKeeper.EXPECT().GetVote(ctx, gomock.Any(), gomock.Any()).Return( - v1.Vote{ProposalId: 1, Voter: "voter", Options: options}, true, - ), - ) - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey() - pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) - providerAddr := types.NewProviderConsAddress(providerConsPubKey.Address().Bytes()) - - k.SetProposedConsumerChain(ctx, "chainID", 1) - - tc.setup(ctx, tc.options, mocks, pkAny) - - // set the validator to-be-opted in first to assert that a NO vote removes the validator from to-be-opted in - k.SetToBeOptedIn(ctx, "chainID", providerAddr) - require.True(t, k.IsToBeOptedIn(ctx, "chainID", providerAddr)) - k.Hooks().AfterProposalVote(ctx, 1, sdk.AccAddress{}) - require.False(t, k.IsToBeOptedIn(ctx, "chainID", providerAddr)) - }) - } -} diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 2ec0a13603..7e79bf4f6c 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1174,13 +1174,13 @@ func (k Keeper) GetTopN( return binary.BigEndian.Uint32(buf), true } -// IsTopN returns true if chain with `chainID` is a Top N chain (i.e., enforces at least one validator to validate chain `chainID`) +// IsTopN returns true if chain with `chainID` is a Top-N chain (i.e., enforces at least one validator to validate chain `chainID`) func (k Keeper) IsTopN(ctx sdk.Context, chainID string) bool { topN, found := k.GetTopN(ctx, chainID) return found && topN > 0 } -// IsOptIn returns true if chain with `chainID` is an Opt In chain (i.e., no validator is forced to validate chain `chainID`) +// IsOptIn returns true if chain with `chainID` is an Opt-In chain (i.e., no validator is forced to validate chain `chainID`) func (k Keeper) IsOptIn(ctx sdk.Context, chainID string) bool { topN, found := k.GetTopN(ctx, chainID) return !found || topN == 0 @@ -1189,15 +1189,10 @@ func (k Keeper) IsOptIn(ctx sdk.Context, chainID string) bool { func (k Keeper) SetOptedIn( ctx sdk.Context, chainID string, - validator types.OptedInValidator, + providerConsAddress types.ProviderConsAddress, ) { store := ctx.KVStore(k.storeKey) - bz, err := validator.Marshal() - if err != nil { - panic(fmt.Errorf("failed to marshal OptedInValidator: %w", err)) - } - - store.Set(types.OptedInKey(chainID, validator.ProviderAddr), bz) + store.Set(types.OptedInKey(chainID, providerConsAddress), []byte{}) } func (k Keeper) DeleteOptedIn( @@ -1206,7 +1201,7 @@ func (k Keeper) DeleteOptedIn( providerAddr types.ProviderConsAddress, ) { store := ctx.KVStore(k.storeKey) - store.Delete(types.OptedInKey(chainID, providerAddr.ToSdkConsAddr())) + store.Delete(types.OptedInKey(chainID, providerAddr)) } func (k Keeper) IsOptedIn( @@ -1215,38 +1210,23 @@ func (k Keeper) IsOptedIn( providerAddr types.ProviderConsAddress, ) bool { store := ctx.KVStore(k.storeKey) - return store.Get(types.OptedInKey(chainID, providerAddr.ToSdkConsAddr())) != nil + return store.Get(types.OptedInKey(chainID, providerAddr)) != nil } // GetAllOptedIn returns all the opted-in validators on chain `chainID` func (k Keeper) GetAllOptedIn( ctx sdk.Context, - chainID string, -) (optedInValidators []types.OptedInValidator) { + chainID string) (providerConsAddresses []types.ProviderConsAddress) { store := ctx.KVStore(k.storeKey) key := types.ChainIdWithLenKey(types.OptedInBytePrefix, chainID) iterator := sdk.KVStorePrefixIterator(store, key) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - iterator.Value() - var optedInValidator types.OptedInValidator - if err := optedInValidator.Unmarshal(iterator.Value()); err != nil { - panic(fmt.Errorf("failed to unmarshal OptedInValidator: %w", err)) - } - optedInValidators = append(optedInValidators, optedInValidator) + providerConsAddresses = append(providerConsAddresses, types.NewProviderConsAddress(iterator.Key()[len(key):])) } - return optedInValidators -} - -func (k Keeper) SetToBeOptedIn( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) { - store := ctx.KVStore(k.storeKey) - store.Set(types.ToBeOptedInKey(chainID, providerAddr), []byte{}) + return providerConsAddresses } // SetConsumerCommissionRate sets a per-consumer chain commission rate @@ -1316,85 +1296,4 @@ func (k Keeper) DeleteConsumerCommissionRate( ) { store := ctx.KVStore(k.storeKey) store.Delete(types.ConsumerCommissionRateKey(chainID, providerAddr)) -} - -func (k Keeper) DeleteToBeOptedIn( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.ToBeOptedInKey(chainID, providerAddr)) -} - -func (k Keeper) IsToBeOptedIn( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) bool { - store := ctx.KVStore(k.storeKey) - return store.Get(types.ToBeOptedInKey(chainID, providerAddr)) != nil -} - -// GetAllToBeOptedIn returns all the to-be-opted-in validators on chain `chainID` -func (k Keeper) GetAllToBeOptedIn( - ctx sdk.Context, - chainID string, -) (addresses []types.ProviderConsAddress) { - store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.ToBeOptedInBytePrefix, chainID) - iterator := sdk.KVStorePrefixIterator(store, key) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - providerAddr := types.NewProviderConsAddress(iterator.Key()[len(key):]) - addresses = append(addresses, providerAddr) - } - - return addresses -} - -func (k Keeper) SetToBeOptedOut( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) { - store := ctx.KVStore(k.storeKey) - store.Set(types.ToBeOptedOutKey(chainID, providerAddr), []byte{}) -} - -func (k Keeper) DeleteToBeOptedOut( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.ToBeOptedOutKey(chainID, providerAddr)) -} - -func (k Keeper) IsToBeOptedOut( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) bool { - store := ctx.KVStore(k.storeKey) - return store.Get(types.ToBeOptedOutKey(chainID, providerAddr)) != nil -} - -// GetAllToBeOptedOut returns all the to-be-opted-out validators on chain `chainID` -func (k Keeper) GetAllToBeOptedOut( - ctx sdk.Context, - chainID string, -) (addresses []types.ProviderConsAddress) { - store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.ToBeOptedOutBytePrefix, chainID) - iterator := sdk.KVStorePrefixIterator(store, key) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - providerAddr := types.NewProviderConsAddress(iterator.Key()[len(key):]) - addresses = append(addresses, providerAddr) - } - - return addresses -} +} \ No newline at end of file diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index 90faec1c85..640b2ac70d 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -665,46 +665,22 @@ func TestGetAllOptedIn(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - expectedOptedInValidators := []types.OptedInValidator{ - { - ProviderAddr: []byte("providerAddr1"), - BlockHeight: 1, - Power: 2, - PublicKey: []byte{3}, - }, - { - ProviderAddr: []byte("providerAddr2"), - BlockHeight: 2, - Power: 3, - PublicKey: []byte{4}, - }, - { - ProviderAddr: []byte("providerAddr3"), - BlockHeight: 3, - Power: 4, - PublicKey: []byte{5}, - }, - } + expectedOptedInValidators := []types.ProviderConsAddress{ + types.NewProviderConsAddress([]byte("providerAddr1")), + types.NewProviderConsAddress([]byte("providerAddr2")), + types.NewProviderConsAddress([]byte("providerAddr3"))} for _, expectedOptedInValidator := range expectedOptedInValidators { - providerKeeper.SetOptedIn(ctx, "chainID", - types.OptedInValidator{ - ProviderAddr: expectedOptedInValidator.ProviderAddr, - BlockHeight: expectedOptedInValidator.BlockHeight, - Power: expectedOptedInValidator.Power, - PublicKey: expectedOptedInValidator.PublicKey, - }) + providerKeeper.SetOptedIn(ctx, "chainID", expectedOptedInValidator) + } actualOptedInValidators := providerKeeper.GetAllOptedIn(ctx, "chainID") // sort validators first to be able to compare - sortOptedInValidators := func(optedInValidators []types.OptedInValidator) { - sort.Slice(optedInValidators, func(i, j int) bool { - a := optedInValidators[i] - b := optedInValidators[j] - return a.BlockHeight < b.BlockHeight || - (a.BlockHeight == b.BlockHeight && bytes.Compare(a.ProviderAddr, b.ProviderAddr) < 0) + sortOptedInValidators := func(addressess []types.ProviderConsAddress) { + sort.Slice(addressess, func(i int, j int) bool { + return bytes.Compare(addressess[i].ToSdkConsAddr(), addressess[j].ToSdkConsAddr()) < 0 }) } sortOptedInValidators(expectedOptedInValidators) @@ -717,154 +693,16 @@ func TestOptedIn(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - optedInValidator := types.OptedInValidator{ - ProviderAddr: []byte("providerAddr"), - BlockHeight: 1, - Power: 2, - PublicKey: []byte{3}, - } + optedInValidator := types.NewProviderConsAddress([]byte("providerAddr")) - require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", types.NewProviderConsAddress(optedInValidator.ProviderAddr))) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator)) providerKeeper.SetOptedIn(ctx, "chainID", optedInValidator) - require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", types.NewProviderConsAddress(optedInValidator.ProviderAddr))) - providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(optedInValidator.ProviderAddr)) - require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", types.NewProviderConsAddress(optedInValidator.ProviderAddr))) -} - -func TestGetAllToBeOptedIn(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - expectedAddresses := []types.ProviderConsAddress{ - types.NewProviderConsAddress([]byte("providerAddr1")), - types.NewProviderConsAddress([]byte("providerAddr2")), - types.NewProviderConsAddress([]byte("providerAddr3")), - } - - for _, addr := range expectedAddresses { - providerKeeper.SetToBeOptedIn(ctx, "chainID", addr) - } - - actualAddresses := providerKeeper.GetAllToBeOptedIn(ctx, "chainID") - - // sort addresses first to be able to compare - sortAddresses := func(addresses []types.ProviderConsAddress) { - sort.Slice(addresses, func(i, j int) bool { - a := addresses[i] - b := addresses[j] - return bytes.Compare(a.Address.Bytes(), b.Address.Bytes()) < 0 - }) - } - sortAddresses(expectedAddresses) - sortAddresses(actualAddresses) - require.Equal(t, expectedAddresses, actualAddresses) - - for _, addr := range expectedAddresses { - require.True(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", addr)) - providerKeeper.DeleteToBeOptedIn(ctx, "chainID", addr) - require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", addr)) - } -} - -func TestBeOptedIn(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - expectedAddresses := []types.ProviderConsAddress{ - types.NewProviderConsAddress([]byte("providerAddr1")), - types.NewProviderConsAddress([]byte("providerAddr2")), - types.NewProviderConsAddress([]byte("providerAddr3")), - } - - for _, addr := range expectedAddresses { - providerKeeper.SetToBeOptedIn(ctx, "chainID", addr) - } - - actualAddresses := providerKeeper.GetAllToBeOptedIn(ctx, "chainID") - - // sort addresses first to be able to compare - sortAddresses := func(addresses []types.ProviderConsAddress) { - sort.Slice(addresses, func(i, j int) bool { - a := addresses[i] - b := addresses[j] - return bytes.Compare(a.Address.Bytes(), b.Address.Bytes()) < 0 - }) - } - sortAddresses(expectedAddresses) - sortAddresses(actualAddresses) - require.Equal(t, expectedAddresses, actualAddresses) - - for _, addr := range expectedAddresses { - require.True(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", addr)) - providerKeeper.DeleteToBeOptedIn(ctx, "chainID", addr) - require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", addr)) - } -} - -// TestToBeOptedIn tests the `SetToBeOptedIn`, `IsToBeOptedIn`, and `DeleteToBeOptedIn` methods -func TestToBeOptedIn(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - providerAddr := types.NewProviderConsAddress([]byte("providerAddr1")) - - require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) - providerKeeper.SetToBeOptedIn(ctx, "chainID", providerAddr) - require.True(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) - providerKeeper.DeleteToBeOptedIn(ctx, "chainID", providerAddr) - require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) -} - -func TestGetAllToBeOptedOut(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - expectedAddresses := []types.ProviderConsAddress{ - types.NewProviderConsAddress([]byte("providerAddr1")), - types.NewProviderConsAddress([]byte("providerAddr2")), - types.NewProviderConsAddress([]byte("providerAddr3")), - } - - for _, addr := range expectedAddresses { - providerKeeper.SetToBeOptedOut(ctx, "chainID", addr) - } - - actualAddresses := providerKeeper.GetAllToBeOptedOut(ctx, "chainID") - - // sort addresses first to be able to compare - sortAddresses := func(addresses []types.ProviderConsAddress) { - sort.Slice(addresses, func(i, j int) bool { - a := addresses[i] - b := addresses[j] - return bytes.Compare(a.Address.Bytes(), b.Address.Bytes()) < 0 - }) - } - sortAddresses(expectedAddresses) - sortAddresses(actualAddresses) - require.Equal(t, expectedAddresses, actualAddresses) - - for _, addr := range expectedAddresses { - require.True(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", addr)) - providerKeeper.DeleteToBeOptedOut(ctx, "chainID", addr) - require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", addr)) - } -} - -// TestToBeOptedOut tests the `SetToBeOptedOut`, `IsToBeOptedOut`, and `DeleteToBeOptedOut` methods -func TestToBeOptedOut(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - providerAddr := types.NewProviderConsAddress([]byte("providerAddr1")) - - require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) - providerKeeper.SetToBeOptedOut(ctx, "chainID", providerAddr) - require.True(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) - providerKeeper.DeleteToBeOptedOut(ctx, "chainID", providerAddr) - require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator)) + providerKeeper.DeleteOptedIn(ctx, "chainID", optedInValidator) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator)) } -// TestToBeOptedOut tests the `SetConsumerCommissionRate`, `GetConsumerCommissionRate`, and `DeleteConsumerCommissionRate` methods +// TestConsumerCommissionRate tests the `SetConsumerCommissionRate`, `GetConsumerCommissionRate`, and `DeleteConsumerCommissionRate` methods func TestConsumerCommissionRate(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 53a428d0dc..1ea4080d2f 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -766,7 +766,10 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { }) } - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, CHAINID, bondedValidators) + nextValidators := k.ComputeNextEpochConsumerValSet(ctx, CHAINID, bondedValidators, + func(validator stakingtypes.Validator) bool { + return true + }) updates = providerkeeper.DiffValidators(k.GetConsumerValSet(ctx, CHAINID), nextValidators) k.SetConsumerValSet(ctx, CHAINID, nextValidators) diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index a595b5fbc2..7bbf270ef9 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -140,19 +140,27 @@ func (k msgServer) SubmitConsumerDoubleVoting(goCtx context.Context, msg *types. func (k msgServer) OptIn(goCtx context.Context, msg *types.MsgOptIn) (*types.MsgOptInResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - valAddress, err := sdk.ConsAddressFromBech32(msg.ProviderAddr) + valAddress, err := sdk.ValAddressFromBech32(msg.ProviderAddr) if err != nil { return nil, err } - providerAddr := types.NewProviderConsAddress(valAddress) + + // validator must already be registered + validator, found := k.stakingKeeper.GetValidator(ctx, valAddress) + if !found { + return nil, stakingtypes.ErrNoValidatorFound + } + + consAddrTmp, err := validator.GetConsAddr() if err != nil { return nil, err } + providerConsAddr := types.NewProviderConsAddress(consAddrTmp) if msg.ConsumerKey != "" { - err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerAddr, &msg.ConsumerKey) + err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerConsAddr, &msg.ConsumerKey) } else { - err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerAddr, nil) + err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerConsAddr, nil) } if err != nil { @@ -173,16 +181,24 @@ func (k msgServer) OptIn(goCtx context.Context, msg *types.MsgOptIn) (*types.Msg func (k msgServer) OptOut(goCtx context.Context, msg *types.MsgOptOut) (*types.MsgOptOutResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - valAddress, err := sdk.ConsAddressFromBech32(msg.ProviderAddr) + valAddress, err := sdk.ValAddressFromBech32(msg.ProviderAddr) if err != nil { return nil, err } - providerAddr := types.NewProviderConsAddress(valAddress) + + // validator must already be registered + validator, found := k.stakingKeeper.GetValidator(ctx, valAddress) + if !found { + return nil, stakingtypes.ErrNoValidatorFound + } + + consAddrTmp, err := validator.GetConsAddr() if err != nil { return nil, err } + providerConsAddr := types.NewProviderConsAddress(consAddrTmp) - err = k.Keeper.HandleOptOut(ctx, msg.ChainId, providerAddr) + err = k.Keeper.HandleOptOut(ctx, msg.ChainId, providerConsAddr) if err != nil { return nil, err } diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 4034687d21..c34885e117 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -2,13 +2,15 @@ package keeper import ( errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "math" + "sort" ) +// HandleOptIn prepares validator `providerAddr` to opt in to `chainID` with an optional `consumerKey` consumer public key. +// Note that the validator only opts in at the end of an epoch. func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, consumerKey *string) error { if !k.IsConsumerProposedOrRegistered(ctx, chainID) { return errorsmod.Wrapf( @@ -16,13 +18,7 @@ func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types. "opting in to an unknown consumer chain, with id: %s", chainID) } - if k.IsToBeOptedOut(ctx, chainID, providerAddr) { - // a validator to be opted in cancels out with a validator to be opted out - k.DeleteToBeOptedOut(ctx, chainID, providerAddr) - } else if !k.IsToBeOptedIn(ctx, chainID, providerAddr) && !k.IsOptedIn(ctx, chainID, providerAddr) { - // a validator can only be set for opt in if it is not opted in and not already set for opt in - k.SetToBeOptedIn(ctx, chainID, providerAddr) - } + k.SetOptedIn(ctx, chainID, providerAddr) if consumerKey != nil { consumerTMPublicKey, err := k.ParseConsumerKey(*consumerKey) @@ -44,6 +40,8 @@ func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types. return nil } +// HandleOptOut prepares validator `providerAddr` to opt out from running `chainID`. +// Note that the validator only opts out at the end of an epoch. func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) error { if _, found := k.GetConsumerClientId(ctx, chainID); !found { // A validator can only opt out from a running chain. We check this by checking the consumer client id, because @@ -53,13 +51,97 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types "opting out of an unknown or not running consumer chain, with id: %s", chainID) } - if k.IsToBeOptedIn(ctx, chainID, providerAddr) { - // a validator to be opted out cancels out a validator to be opted in - k.DeleteToBeOptedIn(ctx, chainID, providerAddr) - } else if !k.IsToBeOptedOut(ctx, chainID, providerAddr) && k.IsOptedIn(ctx, chainID, providerAddr) { - // a validator can only be set for opt out if it is opted in and not already set for opt out - k.SetToBeOptedOut(ctx, chainID, providerAddr) + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { + // a validator cannot opt out from a Top N chain if the validator is in the Top N validators + validator, validatorFound := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.ToSdkConsAddr()) + if !validatorFound { + return errorsmod.Wrapf( + stakingtypes.ErrNoValidatorFound, + "validator with consensus address %s could not be found", providerAddr.ToSdkConsAddr()) + } + power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) + minPowerToOptIn := k.ComputeMinPowerToOptIn(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx), topN) + + if power >= minPowerToOptIn { + return errorsmod.Wrapf( + types.ErrCannotOptOutFromTopN, + "validator with power (%d) cannot opt out from Top N chain because all validators"+ + "with at least %d power have to validate", power, minPowerToOptIn) + } } + k.DeleteOptedIn(ctx, chainID, providerAddr) return nil } + +// OptInTopNValidators opts in to `chainID` all the `bondedValidators` that have at least `minPowerToOptIn` power +func (k Keeper) OptInTopNValidators(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator, minPowerToOptIn int64) { + for _, val := range bondedValidators { + power := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) + if power >= minPowerToOptIn { + consAddr, err := val.GetConsAddr() + if err != nil { + k.Logger(ctx).Error("could not retrieve validators consensus address", + "validator", val, + "error", err) + continue + } + + // if validator already exists it gets overwritten + k.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(consAddr)) + } // else validators that do not belong to the top N validators but were opted in, remain opted in + } +} + +// ComputeMinPowerToOptIn returns the minimum power needed for a validator (from the bonded validators) +// to belong to the `topN` validators. `chainID` is only used for logging purposes. +func (k Keeper) ComputeMinPowerToOptIn(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator, topN uint32) int64 { + if topN == 0 { + // This should never happen but because `ComputeMinPowerToOptIn` is called during an `EndBlock` we do want + // to `panic` here. Instead, we log an error and return the maximum possible `int64`. + k.Logger(ctx).Error("trying to compute minimum power to opt in for a non-Top-N chain", + "chainID", chainID) + return math.MaxInt64 + } + + totalPower := sdk.ZeroDec() + var powers []int64 + + for _, val := range bondedValidators { + power := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) + powers = append(powers, power) + totalPower = totalPower.Add(sdk.NewDecFromInt(sdk.NewInt(power))) + } + + // sort by powers descending + sort.Slice(powers, func(i, j int) bool { + return powers[i] > powers[j] + }) + + topNThreshold := sdk.NewDecFromInt(sdk.NewInt(int64(topN))).QuoInt64(int64(100)) + powerSum := sdk.ZeroDec() + for _, power := range powers { + powerSum = powerSum.Add(sdk.NewDecFromInt(sdk.NewInt(power))) + if powerSum.Quo(totalPower).GTE(topNThreshold) { + return power + } + } + + // We should never reach this point because the topN can be up to 1.0 (100%) and in the above `for` loop we + // perform an equal comparison as well (`GTE`). In any case, we do not have to panic here because we can return 0 + // as the smallest possible power. + k.Logger(ctx).Error("should never reach this point", + "topN", topN, + "totalPower", totalPower, + "powerSum", powerSum) + 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 + } + return k.IsOptedIn(ctx, chainID, types.NewProviderConsAddress(consAddr)) +} diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 651bfc311d..303bdd633f 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -1,6 +1,9 @@ package keeper_test import ( + "bytes" + "math" + "sort" "testing" "github.com/golang/mock/gomock" @@ -25,21 +28,10 @@ func TestHandleOptIn(t *testing.T) { // trying to opt in to a non-proposed and non-registered chain returns an error require.Error(t, providerKeeper.HandleOptIn(ctx, "unknownChainID", providerAddr, nil)) - // if validator (`providerAddr`) is to be opted out, then we cancel that the validator is about - // to be opted out and do not consider the validator to opt in - providerKeeper.SetToBeOptedOut(ctx, "chainID", providerAddr) providerKeeper.SetProposedConsumerChain(ctx, "chainID", 1) - require.True(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, nil) - require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) - require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) - - // if validator (`providerAddr`) is already opted in, then the validator cannot be opted in - - providerKeeper.SetOptedIn(ctx, "chainID", - types.OptedInValidator{ProviderAddr: providerAddr.ToSdkConsAddr(), BlockHeight: 1, Power: 1, PublicKey: []byte{1}}) - providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, nil) - require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) } func TestHandleOptInWithConsumerKey(t *testing.T) { @@ -98,21 +90,64 @@ func TestHandleOptOut(t *testing.T) { // trying to opt out from a not running chain returns an error require.Error(t, providerKeeper.HandleOptOut(ctx, "unknownChainID", providerAddr)) - // if validator (`providerAddr`) is to be opted in, then we cancel that the validator is about - // to be opted out and do not consider the validator to opt out - providerKeeper.SetToBeOptedIn(ctx, "chainID", providerAddr) + // set a consumer client so that the chain is considered running providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") - require.True(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) - err := providerKeeper.HandleOptOut(ctx, "chainID", providerAddr) - require.NoError(t, err) - require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) - require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) - // if validator (`providerAddr`) is not opted in, then the validator cannot be opted out - providerKeeper.DeleteOptedIn(ctx, "chainID", providerAddr) - err = providerKeeper.HandleOptOut(ctx, "chainID", providerAddr) - require.NoError(t, err) - require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) + // if validator (`providerAddr`) is already opted in, then an opt-out would remove this validator + providerKeeper.SetOptedIn(ctx, "chainID", providerAddr) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) + providerKeeper.HandleOptOut(ctx, "chainID", providerAddr) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) +} + +func TestHandleOptOutFromTopNChain(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // set a consumer client so that the chain is considered running + providerKeeper.SetConsumerClientId(ctx, chainID, "clientID") + + // set the chain as Top 50 and create 4 validators with 10%, 20%, 30%, and 40% of the total voting power + // respectively + providerKeeper.SetTopN(ctx, "chainID", 50) + valA := createStakingValidator(ctx, mocks, 1, 1) // 10% of the total voting power (can opt out) + valAConsAddr, _ := valA.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(valA, true).AnyTimes() + valB := createStakingValidator(ctx, mocks, 2, 2) // 20% of the total voting power (can opt out) + valBConsAddr, _ := valB.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(valB, true).AnyTimes() + valC := createStakingValidator(ctx, mocks, 3, 3) // 30% of the total voting power (cannot opt out) + valCConsAddr, _ := valC.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valCConsAddr).Return(valC, true).AnyTimes() + valD := createStakingValidator(ctx, mocks, 4, 4) // 40% of the total voting power (cannot opt out) + valDConsAddr, _ := valD.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, true).AnyTimes() + + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB, valC, valD}).AnyTimes() + + // opt in all validators + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valBConsAddr)) + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valCConsAddr)) + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valDConsAddr)) + + // validators A and B can opt out because they belong the bottom 30% of validators + require.NoError(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(valAConsAddr))) + require.NoError(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(valBConsAddr))) + + // validators C and D cannot opt out because C has 30% of the voting power and D has 40% of the voting power + // and hence both are needed to keep validating a Top 50 chain + require.Error(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(valCConsAddr))) + require.Error(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(valDConsAddr))) + + // opting out a validator that cannot be found from a Top N chain should also return an error + notFoundValidator := createStakingValidator(ctx, mocks, 5, 5) + notFoundValidatorConsAddr, _ := notFoundValidator.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, notFoundValidatorConsAddr). + Return(stakingtypes.Validator{}, false) + require.Error(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(notFoundValidatorConsAddr))) } func TestHandleSetConsumerCommissionRate(t *testing.T) { @@ -139,3 +174,129 @@ func TestHandleSetConsumerCommissionRate(t *testing.T) { require.Equal(t, sdk.OneDec(), cr) require.True(t, found) } + +func TestOptInTopNValidators(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // create 4 validators with powers 1, 2, 3, and 1 respectively + valA := createStakingValidator(ctx, mocks, 1, 1) + valAConsAddr, _ := valA.GetConsAddr() + valB := createStakingValidator(ctx, mocks, 2, 2) + valBConsAddr, _ := valB.GetConsAddr() + valC := createStakingValidator(ctx, mocks, 3, 3) + valCConsAddr, _ := valC.GetConsAddr() + valD := createStakingValidator(ctx, mocks, 4, 1) + valDConsAddr, _ := valD.GetConsAddr() + + // Start Test 1: opt in all validators with power >= 0 + providerKeeper.OptInTopNValidators(ctx, "chainID", []stakingtypes.Validator{valA, valB, valC, valD}, 0) + expectedOptedInValidators := []types.ProviderConsAddress{ + types.NewProviderConsAddress(valAConsAddr), + types.NewProviderConsAddress(valBConsAddr), + types.NewProviderConsAddress(valCConsAddr), + types.NewProviderConsAddress(valDConsAddr)} + actualOptedInValidators := providerKeeper.GetAllOptedIn(ctx, "chainID") + + // sort validators first to be able to compare + sortUpdates := func(addresses []types.ProviderConsAddress) { + sort.Slice(addresses, func(i, j int) bool { + return bytes.Compare(addresses[i].ToSdkConsAddr(), addresses[j].ToSdkConsAddr()) < 0 + }) + } + + sortUpdates(expectedOptedInValidators) + sortUpdates(actualOptedInValidators) + require.Equal(t, expectedOptedInValidators, actualOptedInValidators) + + // reset state for the upcoming checks + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valBConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valCConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valDConsAddr)) + + // Start Test 2: opt in all validators with power >= 1 + // We expect the same `expectedOptedInValidators` as when we opted in all validators with power >= 0 because the + // validators with the smallest power have power == 1 + providerKeeper.OptInTopNValidators(ctx, "chainID", []stakingtypes.Validator{valA, valB, valC, valD}, 0) + actualOptedInValidators = providerKeeper.GetAllOptedIn(ctx, "chainID") + sortUpdates(actualOptedInValidators) + require.Equal(t, expectedOptedInValidators, actualOptedInValidators) + + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valBConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valCConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valDConsAddr)) + + // Start Test 3: opt in all validators with power >= 2 and hence we do not expect to opt in validator A + providerKeeper.OptInTopNValidators(ctx, "chainID", []stakingtypes.Validator{valA, valB, valC, valD}, 2) + expectedOptedInValidators = []types.ProviderConsAddress{ + types.NewProviderConsAddress(valBConsAddr), + types.NewProviderConsAddress(valCConsAddr)} + actualOptedInValidators = providerKeeper.GetAllOptedIn(ctx, "chainID") + + // sort validators first to be able to compare + sortUpdates(expectedOptedInValidators) + sortUpdates(actualOptedInValidators) + require.Equal(t, expectedOptedInValidators, actualOptedInValidators) + + // reset state for the upcoming checks + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valBConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valCConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valDConsAddr)) + + // Start Test 4: opt in all validators with power >= 4 and hence we do not expect any opted-in validators + providerKeeper.OptInTopNValidators(ctx, "chainID", []stakingtypes.Validator{valA, valB, valC, valD}, 4) + require.Empty(t, providerKeeper.GetAllOptedIn(ctx, "chainID")) +} + +func TestComputeMinPowerToOptIn(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // create 5 validators with powers 1, 3, 5, 6, 10 (not in that order) with total power of 25 (= 1 + 3 + 5 + 6 + 10) + // such that: + // validator power => cumulative share + // 10 => 40% + // 6 => 64% + // 5 => 84% + // 3 => 96% + // 1 => 100% + + bondedValidators := []stakingtypes.Validator{ + createStakingValidator(ctx, mocks, 1, 5), + createStakingValidator(ctx, mocks, 2, 10), + createStakingValidator(ctx, mocks, 3, 3), + createStakingValidator(ctx, mocks, 4, 1), + createStakingValidator(ctx, mocks, 5, 6), + } + + require.Equal(t, int64(1), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 100)) + require.Equal(t, int64(1), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 97)) + require.Equal(t, int64(3), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 96)) + require.Equal(t, int64(3), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 85)) + require.Equal(t, int64(5), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 84)) + require.Equal(t, int64(5), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 65)) + require.Equal(t, int64(6), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 64)) + require.Equal(t, int64(6), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 41)) + require.Equal(t, int64(10), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 40)) + require.Equal(t, int64(10), providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 1)) + + // exceptional case when we erroneously call with `topN == 0` + 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) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validator := createStakingValidator(ctx, mocks, 0, 1) + consAddr, _ := validator.GetConsAddr() + + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", types.NewProviderConsAddress(consAddr))) + providerKeeper.SetOptedIn(ctx, "chainID", types.NewProviderConsAddress(consAddr)) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", types.NewProviderConsAddress(consAddr))) + +} diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index e8d50133ba..360ff8f9d2 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -289,7 +289,16 @@ func (k Keeper) MakeConsumerGenesis( bondedValidators = append(bondedValidators, val) } - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chainID, bondedValidators) + if prop.Top_N > 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) + } + + nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chainID, bondedValidators, + func(validator stakingtypes.Validator) bool { + return k.ShouldConsiderOnlyOptIn(ctx, chainID, validator) + }) k.SetConsumerValSet(ctx, chainID, nextValidators) // get the initial updates with the latest set consumer public keys diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 1644ce263a..1ef8547243 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -222,7 +222,17 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { for _, chain := range k.GetAllConsumerChains(ctx) { currentValidators := k.GetConsumerValSet(ctx, chain.ChainId) - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chain.ChainId, bondedValidators) + + if topN, found := k.GetTopN(ctx, chain.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, chain.ChainId, bondedValidators, topN) + 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) + }) 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 bb5a5227c6..594783a9a1 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -682,6 +682,10 @@ func TestEndBlockVSU(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + chainID := "chainID" + + providerKeeper.SetTopN(ctx, chainID, 100) + // 10 blocks constitute an epoch params := providertypes.DefaultParams() params.BlocksPerEpoch = 10 @@ -689,34 +693,38 @@ func TestEndBlockVSU(t *testing.T) { // create 4 sample lastValidators var lastValidators []stakingtypes.Validator + var valAddresses []sdk.ValAddress for i := 0; i < 4; i++ { - lastValidators = append(lastValidators, crypto.NewCryptoIdentityFromIntSeed(i).SDKStakingValidator()) + validator := crypto.NewCryptoIdentityFromIntSeed(i).SDKStakingValidator() + lastValidators = append(lastValidators, validator) + valAddresses = append(valAddresses, validator.GetOperator()) + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), validator.GetOperator()).Return(int64(i + 1)).AnyTimes() } + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return(lastValidators).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(2)).AnyTimes() // set a sample client for a consumer chain so that `GetAllConsumerChains` in `QueueVSCPackets` iterates at least once - providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + providerKeeper.SetConsumerClientId(ctx, chainID, "clientID") // with block height of 1 we do not expect any queueing of VSC packets ctx = ctx.WithBlockHeight(1) providerKeeper.EndBlockVSU(ctx) - require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) // with block height of 5 we do not expect any queueing of VSC packets ctx = ctx.WithBlockHeight(5) providerKeeper.EndBlockVSU(ctx) - require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) // with block height of 10 we expect the queueing of one VSC packet ctx = ctx.WithBlockHeight(10) providerKeeper.EndBlockVSU(ctx) - require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) // With block height of 15 we expect no additional queueing of a VSC packet. // Note that the pending VSC packet is still there because `SendVSCPackets` does not send the packet. We // need to mock channels, etc. for this to work, and it's out of scope for this test. ctx = ctx.WithBlockHeight(15) providerKeeper.EndBlockVSU(ctx) - require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) } diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index adaee853e8..b9dc741d1b 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -26,6 +26,15 @@ func (k Keeper) SetConsumerValidator( store.Set(types.ConsumerValidatorKey(chainID, validator.ProviderConsAddr), bz) } +// SetConsumerValSet resets the current consumer validators with the `nextValidators` computed by +// `ComputeNextEpochConsumerValSet` and hence this method should only be called after `ComputeNextEpochConsumerValSet` has completed. +func (k Keeper) SetConsumerValSet(ctx sdk.Context, chainID string, nextValidators []types.ConsumerValidator) { + k.DeleteConsumerValSet(ctx, chainID) + for _, val := range nextValidators { + k.SetConsumerValidator(ctx, chainID, val) + } +} + // DeleteConsumerValidator removes consumer validator with `providerAddr` address func (k Keeper) DeleteConsumerValidator( ctx sdk.Context, @@ -37,10 +46,7 @@ func (k Keeper) DeleteConsumerValidator( } // DeleteConsumerValSet deletes all the stored consumer validators for chain `chainID` -func (k Keeper) DeleteConsumerValSet( - ctx sdk.Context, - chainID string, -) { +func (k Keeper) DeleteConsumerValSet(ctx sdk.Context, chainID string) { store := ctx.KVStore(k.storeKey) key := types.ChainIdWithLenKey(types.ConsumerValidatorBytePrefix, chainID) iterator := sdk.KVStorePrefixIterator(store, key) @@ -57,20 +63,13 @@ func (k Keeper) DeleteConsumerValSet( // IsConsumerValidator returns `true` if the consumer validator with `providerAddr` exists for chain `chainID` // and `false` otherwise -func (k Keeper) IsConsumerValidator( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) bool { +func (k Keeper) IsConsumerValidator(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) bool { store := ctx.KVStore(k.storeKey) return store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) != nil } // GetConsumerValSet returns all the consumer validators for chain `chainID` -func (k Keeper) GetConsumerValSet( - ctx sdk.Context, - chainID string, -) (validators []types.ConsumerValidator) { +func (k Keeper) GetConsumerValSet(ctx sdk.Context, chainID string) (validators []types.ConsumerValidator) { store := ctx.KVStore(k.storeKey) key := types.ChainIdWithLenKey(types.ConsumerValidatorBytePrefix, chainID) iterator := sdk.KVStorePrefixIterator(store, key) @@ -88,51 +87,6 @@ func (k Keeper) GetConsumerValSet( return validators } -// ComputeNextEpochConsumerValSet returns the next validator set that is responsible for validating consumer -// chain `chainID`, based on the bonded validators. -func (k Keeper) ComputeNextEpochConsumerValSet( - ctx sdk.Context, - chainID string, - bondedValidators []stakingtypes.Validator, -) []types.ConsumerValidator { - var nextValidators []types.ConsumerValidator - for _, val := range bondedValidators { - // get next voting power and the next consumer public key - nextPower := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) - consAddr, err := val.GetConsAddr() - if err != nil { - // this should never happen but is recoverable if we exclude this validator from the `nextValidators` - k.Logger(ctx).Error("could not get consensus address of validator", - "validator", val.GetOperator().String(), - "error", err) - continue - } - nextConsumerPublicKey, foundConsumerPublicKey := k.GetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(consAddr)) - if !foundConsumerPublicKey { - // if no consumer key assigned then use the validator's key itself - k.Logger(ctx).Info("could not retrieve public key for validator on consumer chain because"+ - " the validator did not assign a new consumer key", - "validator", val.GetOperator().String(), - "chainID", chainID) - nextConsumerPublicKey, err = val.TmConsPublicKey() - if err != nil { - // this should never happen and might not be recoverable because without the public key - // we cannot generate a validator update - panic(fmt.Errorf("could not retrieve validator's (%+v) public key: %w", val, err)) - } - } - - nextValidator := types.ConsumerValidator{ - ProviderConsAddr: consAddr, - Power: nextPower, - ConsumerPublicKey: &nextConsumerPublicKey, - } - nextValidators = append(nextValidators, nextValidator) - } - - return nextValidators -} - // DiffValidators compares the current and the next epoch's consumer validators and returns the `ValidatorUpdate` diff // needed by CometBFT to update the validator set on a chain. func DiffValidators( @@ -174,11 +128,53 @@ func DiffValidators( return updates } -// SetConsumerValSet resets the current consumer validators with the `nextValidators` computed by -// `ComputeNextEpochConsumerValSet` and hence this method should only be called after `ComputeNextEpochConsumerValSet` has completed. -func (k Keeper) SetConsumerValSet(ctx sdk.Context, chainID string, nextValidators []types.ConsumerValidator) { - k.DeleteConsumerValSet(ctx, chainID) - for _, val := range nextValidators { - k.SetConsumerValidator(ctx, chainID, val) +// CreateConsumerValidator creates a consumer validator for `chainID` from the given staking `validator` +func (k Keeper) CreateConsumerValidator(ctx sdk.Context, chainID string, validator stakingtypes.Validator) (types.ConsumerValidator, error) { + power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) + consAddr, err := validator.GetConsAddr() + if err != nil { + return types.ConsumerValidator{}, fmt.Errorf("could not retrieve validator's (%+v) consensus address: %w", + validator, err) + } + + consumerPublicKey, foundConsumerPublicKey := k.GetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(consAddr)) + if !foundConsumerPublicKey { + consumerPublicKey, err = validator.TmConsPublicKey() + if err != nil { + return types.ConsumerValidator{}, fmt.Errorf("could not retrieve validator's (%+v) public key: %w", validator, err) + } + } + + return types.ConsumerValidator{ + ProviderConsAddr: consAddr, + Power: power, + ConsumerPublicKey: &consumerPublicKey, + }, 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( + ctx sdk.Context, + chainID string, + bondedValidators []stakingtypes.Validator, + shouldConsider func(validator stakingtypes.Validator) bool, +) []types.ConsumerValidator { + var nextValidators []types.ConsumerValidator + for _, val := range bondedValidators { + if shouldConsider(val) { + 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 + k.Logger(ctx).Error("could not create consumer validator", + "validator", val.GetOperator().String(), + "error", err) + continue + } + + nextValidators = append(nextValidators, nextValidator) + } } + + return nextValidators } diff --git a/x/ccv/provider/keeper/validator_set_update_test.go b/x/ccv/provider/keeper/validator_set_update_test.go index dce307a135..1de54fbbde 100644 --- a/x/ccv/provider/keeper/validator_set_update_test.go +++ b/x/ccv/provider/keeper/validator_set_update_test.go @@ -110,65 +110,25 @@ func createConsumerValidator(index int, power int64, seed int) (types.ConsumerVa }, publicKey } -func TestComputeNextEpochConsumerValSet(t *testing.T) { - providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - chainID := "chainID" - - // helper function to generate a validator with the given power and with a provider address based on index - createStakingValidator := func(ctx sdk.Context, mocks testkeeper.MockedKeepers, index int, power int64) stakingtypes.Validator { - providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{byte(index)}).PubKey() - consAddr := sdk.ConsAddress(providerConsPubKey.Address()) - providerAddr := types.NewProviderConsAddress(consAddr) - pk, _ := cryptocodec.FromTmPubKeyInterface(providerConsPubKey) - pkAny, _ := codectypes.NewAnyWithValue(pk) - - var providerValidatorAddr sdk.ValAddress - providerValidatorAddr = providerAddr.Address.Bytes() - - mocks.MockStakingKeeper.EXPECT(). - GetLastValidatorPower(ctx, providerValidatorAddr).Return(power).AnyTimes() - - return stakingtypes.Validator{ - OperatorAddress: providerValidatorAddr.String(), - ConsensusPubkey: pkAny, - } +// createStakingValidator helper function to generate a validator with the given power and with a provider address based on index +func createStakingValidator(ctx sdk.Context, mocks testkeeper.MockedKeepers, index int, power int64) stakingtypes.Validator { + providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{byte(index)}).PubKey() + consAddr := sdk.ConsAddress(providerConsPubKey.Address()) + providerAddr := types.NewProviderConsAddress(consAddr) + pk, _ := cryptocodec.FromTmPubKeyInterface(providerConsPubKey) + pkAny, _ := codectypes.NewAnyWithValue(pk) + + var providerValidatorAddr sdk.ValAddress + providerValidatorAddr = providerAddr.Address.Bytes() + + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, providerValidatorAddr).Return(power).AnyTimes() + + return stakingtypes.Validator{ + OperatorAddress: providerValidatorAddr.String(), + ConsensusPubkey: pkAny, } - - // no consumer validators returned if we have no bonded validators - require.Empty(t, providerKeeper.ComputeNextEpochConsumerValSet(ctx, chainID, []stakingtypes.Validator{})) - - var expectedValidators []types.ConsumerValidator - - // create a staking validator A that has not set a consumer public key - valA := createStakingValidator(ctx, mocks, 1, 1) - // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain - valAConsAddr, _ := valA.GetConsAddr() - valAPublicKey, _ := valA.TmConsPublicKey() - expectedValidators = append(expectedValidators, types.ConsumerValidator{ - ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), - Power: 1, - ConsumerPublicKey: &valAPublicKey, - }) - - // create a staking validator B that has set a consumer public key - valB := createStakingValidator(ctx, mocks, 2, 2) - // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` - valBConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() - valBConsAddr, _ := valB.GetConsAddr() - providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(valBConsAddr), valBConsumerKey) - expectedValidators = append(expectedValidators, types.ConsumerValidator{ - ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), - Power: 2, - ConsumerPublicKey: &valBConsumerKey, - }) - - bondedValidators := []stakingtypes.Validator{valA, valB} - actualValidators := providerKeeper.ComputeNextEpochConsumerValSet(ctx, "chainID", bondedValidators) - require.Equal(t, expectedValidators, actualValidators) } - func TestDiff(t *testing.T) { // In what follows we create 6 validators: A, B, C, D, E, and F where currentValidators = {A, B, C, D, E} // and nextValidators = {B, C, D, E, F}. For the validators {B, C, D, E} in the intersection we have: @@ -356,3 +316,152 @@ func TestSetConsumerValSet(t *testing.T) { sortValidators(nextCurrentValidators) require.Equal(t, nextValidators, nextCurrentValidators) } + +func TestComputeNextEpochConsumerValSetConsiderAll(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + 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)) + + var expectedValidators []types.ConsumerValidator + + // create a staking validator A that has not set a consumer public key + valA := createStakingValidator(ctx, mocks, 1, 1) + // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain + valAConsAddr, _ := valA.GetConsAddr() + valAPublicKey, _ := valA.TmConsPublicKey() + expectedValidators = append(expectedValidators, types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), + Power: 1, + ConsumerPublicKey: &valAPublicKey, + }) + + // create a staking validator B that has set a consumer public key + valB := createStakingValidator(ctx, mocks, 2, 2) + // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` + valBConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() + valBConsAddr, _ := valB.GetConsAddr() + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(valBConsAddr), valBConsumerKey) + expectedValidators = append(expectedValidators, types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), + Power: 2, + ConsumerPublicKey: &valBConsumerKey, + }) + + bondedValidators := []stakingtypes.Validator{valA, valB} + actualValidators := providerKeeper.ComputeNextEpochConsumerValSet(ctx, chainID, bondedValidators, considerAll) + require.Equal(t, expectedValidators, actualValidators) +} + +func TestComputeNextEpochConsumerValSetConsiderOnlyOptIn(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + 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) + })) + + var expectedValidators []types.ConsumerValidator + + // create a staking validator A that has not set a consumer public key + valA := createStakingValidator(ctx, mocks, 1, 1) + // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain + valAConsAddr, _ := valA.GetConsAddr() + valAPublicKey, _ := valA.TmConsPublicKey() + expectedValAConsumerValidator := types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), + Power: 1, + ConsumerPublicKey: &valAPublicKey} + expectedValidators = append(expectedValidators, expectedValAConsumerValidator) + + // create a staking validator B that has set a consumer public key + valB := createStakingValidator(ctx, mocks, 2, 2) + // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` + valBConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() + valBConsAddr, _ := valB.GetConsAddr() + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(valBConsAddr), valBConsumerKey) + expectedValBConsumerValidator := types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), + Power: 2, + ConsumerPublicKey: &valBConsumerKey, + } + expectedValidators = append(expectedValidators, expectedValBConsumerValidator) + + // opt in validators A and B with 0 power and no consumer public keys + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valBConsAddr)) + + // 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) + }) + + // sort validators first to be able to compare + sortValidators := func(validators []types.ConsumerValidator) { + sort.Slice(validators, func(i, j int) bool { + return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 + }) + } + + sortValidators(actualValidators) + sortValidators(expectedValidators) + require.Equal(t, expectedValidators, actualValidators) + + // 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) + }) + + sortValidators(actualValidators) + sortValidators(expectedValidators) + require.Equal(t, expectedValidators, actualValidators) +} + +func TestCreateConsumerValidator(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // create a validator which has set a consumer public key + valA := createStakingValidator(ctx, mocks, 0, 1) + valAConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() + valAConsAddr, _ := valA.GetConsAddr() + valAProviderConsAddr := types.NewProviderConsAddress(valAConsAddr) + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, valAProviderConsAddr, valAConsumerKey) + actualConsumerValidatorA, err := providerKeeper.CreateConsumerValidator(ctx, chainID, valA) + expectedConsumerValidatorA := types.ConsumerValidator{ + ProviderConsAddr: valAProviderConsAddr.ToSdkConsAddr(), + Power: 1, + ConsumerPublicKey: &valAConsumerKey, + } + require.Equal(t, expectedConsumerValidatorA, actualConsumerValidatorA) + require.NoError(t, err) + + // create a validator which has not set a consumer public key + valB := createStakingValidator(ctx, mocks, 1, 2) + valBConsAddr, _ := valB.GetConsAddr() + valBProviderConsAddr := types.NewProviderConsAddress(valBConsAddr) + valBPublicKey, _ := valB.TmConsPublicKey() + actualConsumerValidatorB, err := providerKeeper.CreateConsumerValidator(ctx, chainID, valB) + expectedConsumerValidatorB := types.ConsumerValidator{ + ProviderConsAddr: valBProviderConsAddr.ToSdkConsAddr(), + Power: 2, + ConsumerPublicKey: &valBPublicKey, + } + require.Equal(t, expectedConsumerValidatorB, actualConsumerValidatorB) + require.NoError(t, err) +} diff --git a/x/ccv/provider/types/errors.go b/x/ccv/provider/types/errors.go index 271ea90329..1e7dd7dd3d 100644 --- a/x/ccv/provider/types/errors.go +++ b/x/ccv/provider/types/errors.go @@ -25,4 +25,5 @@ var ( ErrDuplicateConsumerChain = errorsmod.Register(ModuleName, 17, "consumer chain already exists") ErrConsumerChainNotFound = errorsmod.Register(ModuleName, 18, "consumer chain not found") ErrInvalidConsumerCommissionRate = errorsmod.Register(ModuleName, 19, "consumer commission rate is invalid") + ErrCannotOptOutFromTopN = errorsmod.Register(ModuleName, 20, "cannot opt out from a Top N chain") ) diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 998608ef18..1e2516ba07 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -147,24 +147,18 @@ const ( // ProposedConsumerChainByteKey is the byte prefix storing the consumer chainId in consumerAddition gov proposal submitted before voting finishes ProposedConsumerChainByteKey - // ConsumerValidatorBytePrefix is the byte prefix used when storing for each consumer chain all the consumer validators in this epoch + // OptedInBytePrefix is the byte prefix used when storing for each consumer chain the validators that + // are opted in but not necessarily the ones that are validating. + OptedInBytePrefix + + // ConsumerValidatorBytePrefix is the byte prefix used when storing for each consumer chain all the consumer + // validators in this epoch that are validating the consumer chain ConsumerValidatorBytePrefix // TopNBytePrefix is the byte prefix storing the mapping from a consumer chain to the N value of this chain, // that corresponds to the N% of the top validators that have to validate this consumer chain TopNBytePrefix - // OptedInBytePrefix is the byte prefix used when storing for each consumer chain all the opted in validators - OptedInBytePrefix - - // ToBeOptedInBytePrefix is the byte prefix used when storing for each consumer chain the validators that - // are about to be opted in - ToBeOptedInBytePrefix - - // ToBeOptedOutBytePrefix is the byte prefix used when storing for each consumer chain the validators that - // are about to be opted out - ToBeOptedOutBytePrefix - // ConsumerRewardsAllocationBytePrefix is the byte prefix used when storing for each consumer the rewards // it allocated to the consumer rewards pool ConsumerRewardsAllocationBytePrefix @@ -551,20 +545,8 @@ func TopNKey(chainID string) []byte { } // OptedInKey returns the key of consumer chain `chainID` and validator with `providerAddr` -func OptedInKey(chainID string, providerAddr []byte) []byte { +func OptedInKey(chainID string, providerAddr ProviderConsAddress) []byte { prefix := ChainIdWithLenKey(OptedInBytePrefix, chainID) - return append(prefix, providerAddr...) -} - -// ToBeOptedInKey returns the key of consumer chain `chainID` and validator with `providerAddr` -func ToBeOptedInKey(chainID string, providerAddr ProviderConsAddress) []byte { - prefix := ChainIdWithLenKey(ToBeOptedInBytePrefix, chainID) - return append(prefix, providerAddr.ToSdkConsAddr().Bytes()...) -} - -// ToBeOptedOutKey returns the key of consumer chain `chainID` and validator with `providerAddr` -func ToBeOptedOutKey(chainID string, providerAddr ProviderConsAddress) []byte { - prefix := ChainIdWithLenKey(ToBeOptedOutBytePrefix, chainID) return append(prefix, providerAddr.ToSdkConsAddr().Bytes()...) } diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index f24c6ec476..d37482e50e 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -56,11 +56,9 @@ func getAllKeyPrefixes() []byte { providertypes.VSCMaturedHandledThisBlockBytePrefix, providertypes.EquivocationEvidenceMinHeightBytePrefix, providertypes.ProposedConsumerChainByteKey, + providertypes.OptedInBytePrefix, providertypes.ConsumerValidatorBytePrefix, providertypes.TopNBytePrefix, - providertypes.OptedInBytePrefix, - providertypes.ToBeOptedInBytePrefix, - providertypes.ToBeOptedOutBytePrefix, providertypes.ConsumerRewardsAllocationBytePrefix, providertypes.ConsumerCommissionRatePrefix, } diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index c2771de119..dee8a9520a 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -1513,80 +1513,6 @@ func (m *ConsumerRewardsAllocation) GetRewards() github_com_cosmos_cosmos_sdk_ty return nil } -// OptedInValidator is used to store a opted-in validator -// to a consumer chain with the following mapping: (chainID, providerAddr) -> optedInValidator -type OptedInValidator struct { - // validator address - ProviderAddr []byte `protobuf:"bytes,1,opt,name=provider_addr,json=providerAddr,proto3" json:"provider_addr,omitempty"` - // block height at which the validator opted-in - BlockHeight int64 `protobuf:"varint,2,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` - // validator voting power at the block it opted-in - Power int64 `protobuf:"varint,3,opt,name=power,proto3" json:"power,omitempty"` - // public key used by the validator on the consumer - PublicKey []byte `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` -} - -func (m *OptedInValidator) Reset() { *m = OptedInValidator{} } -func (m *OptedInValidator) String() string { return proto.CompactTextString(m) } -func (*OptedInValidator) ProtoMessage() {} -func (*OptedInValidator) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{24} -} -func (m *OptedInValidator) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *OptedInValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_OptedInValidator.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *OptedInValidator) XXX_Merge(src proto.Message) { - xxx_messageInfo_OptedInValidator.Merge(m, src) -} -func (m *OptedInValidator) XXX_Size() int { - return m.Size() -} -func (m *OptedInValidator) XXX_DiscardUnknown() { - xxx_messageInfo_OptedInValidator.DiscardUnknown(m) -} - -var xxx_messageInfo_OptedInValidator proto.InternalMessageInfo - -func (m *OptedInValidator) GetProviderAddr() []byte { - if m != nil { - return m.ProviderAddr - } - return nil -} - -func (m *OptedInValidator) GetBlockHeight() int64 { - if m != nil { - return m.BlockHeight - } - return 0 -} - -func (m *OptedInValidator) GetPower() int64 { - if m != nil { - return m.Power - } - return 0 -} - -func (m *OptedInValidator) GetPublicKey() []byte { - if m != nil { - return m.PublicKey - } - return nil -} - func init() { proto.RegisterType((*ConsumerAdditionProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerAdditionProposal") proto.RegisterType((*ConsumerRemovalProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerRemovalProposal") @@ -1612,7 +1538,6 @@ func init() { proto.RegisterType((*ConsumerAddrsToPrune)(nil), "interchain_security.ccv.provider.v1.ConsumerAddrsToPrune") proto.RegisterType((*ConsumerValidator)(nil), "interchain_security.ccv.provider.v1.ConsumerValidator") proto.RegisterType((*ConsumerRewardsAllocation)(nil), "interchain_security.ccv.provider.v1.ConsumerRewardsAllocation") - proto.RegisterType((*OptedInValidator)(nil), "interchain_security.ccv.provider.v1.OptedInValidator") } func init() { @@ -1620,126 +1545,123 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1892 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x73, 0x1c, 0x47, - 0x15, 0xd7, 0x68, 0x57, 0x1f, 0xfb, 0x56, 0x9f, 0x23, 0x25, 0x1e, 0x19, 0xb1, 0x92, 0x27, 0x24, - 0x08, 0x82, 0x67, 0x90, 0x02, 0x55, 0x2e, 0x17, 0xa9, 0x94, 0xb4, 0x72, 0x62, 0x59, 0x89, 0xad, - 0x8c, 0x84, 0x5c, 0xc0, 0x61, 0xaa, 0xb7, 0xa7, 0xbd, 0xdb, 0xa5, 0xd9, 0xe9, 0x71, 0x77, 0xef, - 0x38, 0x7b, 0xe1, 0xcc, 0x85, 0x22, 0xdc, 0x52, 0x5c, 0x08, 0x54, 0x51, 0x45, 0x71, 0x81, 0x3f, - 0x23, 0xc7, 0x1c, 0x39, 0x25, 0x94, 0x7d, 0xe0, 0xc0, 0x95, 0x3f, 0x80, 0xea, 0x9e, 0xcf, 0x5d, - 0x49, 0x66, 0x5d, 0x81, 0x8b, 0x34, 0xf3, 0xfa, 0xbd, 0xdf, 0x7b, 0xfd, 0xbe, 0x77, 0x60, 0x8f, - 0x46, 0x92, 0x70, 0xdc, 0x43, 0x34, 0xf2, 0x05, 0xc1, 0x03, 0x4e, 0xe5, 0xd0, 0xc5, 0x38, 0x71, - 0x63, 0xce, 0x12, 0x1a, 0x10, 0xee, 0x26, 0xbb, 0xc5, 0xb3, 0x13, 0x73, 0x26, 0x99, 0xf9, 0xc6, - 0x15, 0x32, 0x0e, 0xc6, 0x89, 0x53, 0xf0, 0x25, 0xbb, 0x37, 0xdf, 0xbc, 0x0e, 0x38, 0xd9, 0x75, - 0x9f, 0x51, 0x4e, 0x52, 0xac, 0x9b, 0xeb, 0x5d, 0xd6, 0x65, 0xfa, 0xd1, 0x55, 0x4f, 0x19, 0x75, - 0xab, 0xcb, 0x58, 0x37, 0x24, 0xae, 0x7e, 0xeb, 0x0c, 0x9e, 0xb8, 0x92, 0xf6, 0x89, 0x90, 0xa8, - 0x1f, 0x67, 0x0c, 0xad, 0x71, 0x86, 0x60, 0xc0, 0x91, 0xa4, 0x2c, 0xca, 0x01, 0x68, 0x07, 0xbb, - 0x98, 0x71, 0xe2, 0xe2, 0x90, 0x92, 0x48, 0x2a, 0xad, 0xe9, 0x53, 0xc6, 0xe0, 0x2a, 0x86, 0x90, - 0x76, 0x7b, 0x32, 0x25, 0x0b, 0x57, 0x92, 0x28, 0x20, 0xbc, 0x4f, 0x53, 0xe6, 0xf2, 0x2d, 0x13, - 0xd8, 0xac, 0x9c, 0x63, 0x3e, 0x8c, 0x25, 0x73, 0x2f, 0xc8, 0x50, 0x64, 0xa7, 0x6f, 0x61, 0x26, - 0xfa, 0x4c, 0xb8, 0x44, 0xdd, 0x3f, 0xc2, 0xc4, 0x4d, 0x76, 0x3b, 0x44, 0xa2, 0xdd, 0x82, 0x90, - 0xdb, 0x9d, 0xf1, 0x75, 0x90, 0x28, 0x79, 0x30, 0xa3, 0xb9, 0xdd, 0xab, 0xa8, 0x4f, 0x23, 0xe6, - 0xea, 0xbf, 0x29, 0xc9, 0xfe, 0xf7, 0x2c, 0x58, 0x6d, 0x16, 0x89, 0x41, 0x9f, 0xf0, 0xfd, 0x20, - 0xa0, 0xea, 0x96, 0x27, 0x9c, 0xc5, 0x4c, 0xa0, 0xd0, 0x5c, 0x87, 0x19, 0x49, 0x65, 0x48, 0x2c, - 0x63, 0xdb, 0xd8, 0x69, 0x78, 0xe9, 0x8b, 0xb9, 0x0d, 0xcd, 0x80, 0x08, 0xcc, 0x69, 0xac, 0x98, - 0xad, 0x69, 0x7d, 0x56, 0x25, 0x99, 0x1b, 0x30, 0x9f, 0x86, 0x86, 0x06, 0x56, 0x4d, 0x1f, 0xcf, - 0xe9, 0xf7, 0xa3, 0xc0, 0xfc, 0x00, 0x96, 0x68, 0x44, 0x25, 0x45, 0xa1, 0xdf, 0x23, 0xca, 0x41, - 0x56, 0x7d, 0xdb, 0xd8, 0x69, 0xee, 0xdd, 0x74, 0x68, 0x07, 0x3b, 0xca, 0xa7, 0x4e, 0xe6, 0xc9, - 0x64, 0xd7, 0xb9, 0xaf, 0x39, 0x0e, 0xea, 0x5f, 0x7c, 0xb5, 0x35, 0xe5, 0x2d, 0x66, 0x72, 0x29, - 0xd1, 0xbc, 0x05, 0x0b, 0x5d, 0x12, 0x11, 0x41, 0x85, 0xdf, 0x43, 0xa2, 0x67, 0xcd, 0x6c, 0x1b, - 0x3b, 0x0b, 0x5e, 0x33, 0xa3, 0xdd, 0x47, 0xa2, 0x67, 0x6e, 0x41, 0xb3, 0x43, 0x23, 0xc4, 0x87, - 0x29, 0xc7, 0xac, 0xe6, 0x80, 0x94, 0xa4, 0x19, 0xda, 0x00, 0x22, 0x46, 0xcf, 0x22, 0x5f, 0x25, - 0x80, 0x35, 0x97, 0x19, 0x92, 0x06, 0xdf, 0xc9, 0x83, 0xef, 0x9c, 0xe5, 0xd9, 0x71, 0x30, 0xaf, - 0x0c, 0xf9, 0xf4, 0xeb, 0x2d, 0xc3, 0x6b, 0x68, 0x39, 0x75, 0x62, 0x3e, 0x84, 0x95, 0x41, 0xd4, - 0x61, 0x51, 0x40, 0xa3, 0xae, 0x1f, 0x13, 0x4e, 0x59, 0x60, 0xcd, 0x6b, 0xa8, 0x8d, 0x4b, 0x50, - 0x87, 0x59, 0x1e, 0xa5, 0x48, 0x9f, 0x29, 0xa4, 0xe5, 0x42, 0xf8, 0x44, 0xcb, 0x9a, 0x1f, 0x83, - 0x89, 0x71, 0xa2, 0x4d, 0x62, 0x03, 0x99, 0x23, 0x36, 0x26, 0x47, 0x5c, 0xc1, 0x38, 0x39, 0x4b, - 0xa5, 0x33, 0xc8, 0x5f, 0xc0, 0x0d, 0xc9, 0x51, 0x24, 0x9e, 0x10, 0x3e, 0x8e, 0x0b, 0x93, 0xe3, - 0xbe, 0x96, 0x63, 0x8c, 0x82, 0xdf, 0x87, 0x6d, 0x9c, 0x25, 0x90, 0xcf, 0x49, 0x40, 0x85, 0xe4, - 0xb4, 0x33, 0x50, 0xb2, 0xfe, 0x13, 0x8e, 0xb0, 0xce, 0x91, 0xa6, 0x4e, 0x82, 0x56, 0xce, 0xe7, - 0x8d, 0xb0, 0xbd, 0x9f, 0x71, 0x99, 0x8f, 0xe0, 0x3b, 0x9d, 0x90, 0xe1, 0x0b, 0xa1, 0x8c, 0xf3, - 0x47, 0x90, 0xb4, 0xea, 0x3e, 0x15, 0x42, 0xa1, 0x2d, 0x6c, 0x1b, 0x3b, 0x35, 0xef, 0x56, 0xca, - 0x7b, 0x42, 0xf8, 0x61, 0x85, 0xf3, 0xac, 0xc2, 0x68, 0xde, 0x06, 0xb3, 0x47, 0x85, 0x64, 0x9c, - 0x62, 0x14, 0xfa, 0x24, 0x92, 0x9c, 0x12, 0x61, 0x2d, 0x6a, 0xf1, 0xd5, 0xf2, 0xe4, 0x5e, 0x7a, - 0x60, 0x3e, 0x80, 0x5b, 0xd7, 0x2a, 0xf5, 0x71, 0x0f, 0x45, 0x11, 0x09, 0xad, 0x25, 0x7d, 0x95, - 0xad, 0xe0, 0x1a, 0x9d, 0xed, 0x94, 0xcd, 0x5c, 0x83, 0x19, 0xc9, 0x62, 0xff, 0xa1, 0xb5, 0xbc, - 0x6d, 0xec, 0x2c, 0x7a, 0x75, 0xc9, 0xe2, 0x87, 0x77, 0xe7, 0x7f, 0xf5, 0xf9, 0xd6, 0xd4, 0x67, - 0x9f, 0x6f, 0x4d, 0xd9, 0x7f, 0x35, 0xe0, 0x46, 0xbb, 0xf0, 0x46, 0x9f, 0x25, 0x28, 0xfc, 0x7f, - 0x56, 0xdd, 0x3e, 0x34, 0x84, 0x32, 0x47, 0xe7, 0x79, 0xfd, 0x15, 0xf2, 0x7c, 0x5e, 0x89, 0xa9, - 0x03, 0xfb, 0xf7, 0x06, 0xac, 0xdf, 0x7b, 0x3a, 0xa0, 0x09, 0xc3, 0xe8, 0x7f, 0xd2, 0x24, 0x8e, - 0x61, 0x91, 0x54, 0xf0, 0x84, 0x55, 0xdb, 0xae, 0xed, 0x34, 0xf7, 0xde, 0x74, 0xd2, 0x26, 0xe6, - 0x14, 0xbd, 0x2d, 0x6b, 0x64, 0x4e, 0x55, 0xbb, 0x37, 0x2a, 0x7b, 0x77, 0xda, 0x32, 0xec, 0x3f, - 0x1a, 0x70, 0x53, 0xb9, 0xbf, 0x4b, 0x3c, 0xf2, 0x0c, 0xf1, 0xe0, 0x90, 0x44, 0xac, 0x2f, 0xbe, - 0xb1, 0x9d, 0x36, 0x2c, 0x06, 0x1a, 0xc9, 0x97, 0xcc, 0x47, 0x41, 0xa0, 0xed, 0xd4, 0x3c, 0x8a, - 0x78, 0xc6, 0xf6, 0x83, 0xc0, 0xdc, 0x81, 0x95, 0x92, 0x87, 0xab, 0x78, 0x2a, 0x37, 0x2b, 0xb6, - 0xa5, 0x9c, 0x4d, 0x47, 0x99, 0xd8, 0xff, 0x32, 0x60, 0xe5, 0x83, 0x90, 0x75, 0x50, 0x78, 0x1a, - 0x22, 0xd1, 0x53, 0xa9, 0x37, 0x54, 0xe1, 0xe1, 0x24, 0xab, 0x79, 0x6d, 0xde, 0xc4, 0xe1, 0x51, - 0x62, 0xba, 0x0b, 0xbd, 0x07, 0xab, 0x45, 0x15, 0x16, 0x59, 0xa0, 0x6f, 0x73, 0xb0, 0xf6, 0xfc, - 0xab, 0xad, 0xe5, 0x3c, 0xd9, 0xda, 0x3a, 0x23, 0x0e, 0xbd, 0x65, 0x3c, 0x42, 0x08, 0xcc, 0x16, - 0x34, 0x69, 0x07, 0xfb, 0x82, 0x3c, 0xf5, 0xa3, 0x41, 0x5f, 0x27, 0x50, 0xdd, 0x6b, 0xd0, 0x0e, - 0x3e, 0x25, 0x4f, 0x1f, 0x0e, 0xfa, 0xe6, 0x3b, 0xf0, 0x7a, 0x3e, 0x80, 0xfd, 0x04, 0x85, 0xbe, - 0x92, 0x57, 0xee, 0xe0, 0x3a, 0x9f, 0x16, 0xbc, 0xb5, 0xfc, 0xf4, 0x1c, 0x85, 0x4a, 0xd9, 0x7e, - 0x10, 0x70, 0xfb, 0xc5, 0x0c, 0xcc, 0x9e, 0x20, 0x8e, 0xfa, 0xc2, 0x3c, 0x83, 0x65, 0x49, 0xfa, - 0x71, 0x88, 0x24, 0xf1, 0xd3, 0x0e, 0x9f, 0xdd, 0xf4, 0x6d, 0xdd, 0xf9, 0xab, 0xc3, 0xd2, 0xa9, - 0x8c, 0xc7, 0x64, 0xd7, 0x69, 0x6b, 0xea, 0xa9, 0x44, 0x92, 0x78, 0x4b, 0x39, 0x46, 0x4a, 0x34, - 0xef, 0x80, 0x25, 0xf9, 0x40, 0xc8, 0xb2, 0xf7, 0x96, 0x4d, 0x27, 0x8d, 0xe5, 0xeb, 0xf9, 0x79, - 0xda, 0xae, 0x8a, 0x66, 0x73, 0x75, 0x9b, 0xad, 0x7d, 0x93, 0x36, 0x7b, 0x0a, 0x6b, 0x6a, 0x46, - 0x8d, 0x63, 0xd6, 0x27, 0xc7, 0x5c, 0x55, 0xf2, 0xa3, 0xa0, 0x1f, 0x83, 0x99, 0x08, 0x3c, 0x8e, - 0x39, 0xf3, 0x0a, 0x76, 0x26, 0x02, 0x8f, 0x42, 0x06, 0xb0, 0x29, 0x54, 0xf2, 0xf9, 0x7d, 0x22, - 0x75, 0xd3, 0x8e, 0x43, 0x12, 0x51, 0xd1, 0xcb, 0xc1, 0x67, 0x27, 0x07, 0xdf, 0xd0, 0x40, 0x1f, - 0x29, 0x1c, 0x2f, 0x87, 0xc9, 0xb4, 0xb4, 0xa1, 0x75, 0xb5, 0x96, 0x22, 0x40, 0x73, 0x3a, 0x40, - 0xdf, 0xba, 0x02, 0xa2, 0x88, 0x92, 0x80, 0xb7, 0x2a, 0xc3, 0x45, 0x55, 0xb5, 0xaf, 0x0b, 0xca, - 0xe7, 0xa4, 0xab, 0x3a, 0x30, 0x4a, 0xe7, 0x0c, 0x21, 0xc5, 0x80, 0xcc, 0xba, 0x87, 0x5a, 0x81, - 0x8a, 0xce, 0xd1, 0x66, 0x34, 0xca, 0xb6, 0x08, 0xbb, 0x9c, 0x41, 0x45, 0x8f, 0xf0, 0x2a, 0x58, - 0xef, 0x13, 0xa2, 0xaa, 0xb9, 0x32, 0x87, 0x48, 0xcc, 0x70, 0x4f, 0xcf, 0xc9, 0x9a, 0xb7, 0x54, - 0xcc, 0x9c, 0x7b, 0x8a, 0xfa, 0xa0, 0x3e, 0x3f, 0xbf, 0xd2, 0xb0, 0xbf, 0x07, 0x0d, 0x5d, 0xcc, - 0xfb, 0xf8, 0x42, 0x98, 0x9b, 0xd0, 0x50, 0x55, 0x41, 0x84, 0x20, 0xc2, 0x32, 0x74, 0x0f, 0x28, - 0x09, 0xb6, 0x84, 0x8d, 0xeb, 0xb6, 0x2d, 0x61, 0x3e, 0x86, 0xb9, 0x98, 0xe8, 0x55, 0x40, 0x0b, - 0x36, 0xf7, 0xde, 0x75, 0x26, 0xd8, 0x85, 0x9d, 0xeb, 0x00, 0xbd, 0x1c, 0xcd, 0xe6, 0xe5, 0x8e, - 0x37, 0x36, 0x6c, 0x84, 0x79, 0x3e, 0xae, 0xf4, 0x27, 0xaf, 0xa4, 0x74, 0x0c, 0xaf, 0xd4, 0xf9, - 0x36, 0x34, 0xf7, 0xd3, 0x6b, 0x7f, 0x48, 0x85, 0xbc, 0xec, 0x96, 0x85, 0xaa, 0x5b, 0x1e, 0xc0, - 0x52, 0x36, 0x38, 0xcf, 0x98, 0x6e, 0x48, 0xe6, 0xb7, 0x01, 0xb2, 0x89, 0xab, 0x1a, 0x59, 0xda, - 0xb2, 0x1b, 0x19, 0xe5, 0x28, 0x18, 0x99, 0x75, 0xd3, 0x23, 0xb3, 0xce, 0xf6, 0x60, 0xf9, 0x5c, - 0xe0, 0x9f, 0xe6, 0x5b, 0xd5, 0xa3, 0x58, 0x98, 0xaf, 0xc1, 0xac, 0xaa, 0xa1, 0x0c, 0xa8, 0xee, - 0xcd, 0x24, 0x02, 0x1f, 0xe9, 0xae, 0x5d, 0x6e, 0x6e, 0x2c, 0xf6, 0x69, 0x20, 0xac, 0xe9, 0xed, - 0xda, 0x4e, 0xdd, 0x5b, 0x1a, 0x94, 0xe2, 0x47, 0x81, 0xb0, 0x7f, 0x06, 0xcd, 0x0a, 0xa0, 0xb9, - 0x04, 0xd3, 0x05, 0xd6, 0x34, 0x0d, 0xcc, 0xbb, 0xb0, 0x51, 0x02, 0x8d, 0xb6, 0xe1, 0x14, 0xb1, - 0xe1, 0xdd, 0x28, 0x18, 0x46, 0x3a, 0xb1, 0xb0, 0x1f, 0xc1, 0xfa, 0x51, 0x59, 0xf4, 0x45, 0x93, - 0x1f, 0xb9, 0xa1, 0x31, 0x3a, 0xcd, 0x37, 0xa1, 0x51, 0xfc, 0x62, 0xd1, 0xb7, 0xaf, 0x7b, 0x25, - 0xc1, 0xee, 0xc3, 0xca, 0xb9, 0xc0, 0xa7, 0x24, 0x0a, 0x4a, 0xb0, 0x6b, 0x1c, 0x70, 0x30, 0x0e, - 0x34, 0xf1, 0xfa, 0x5b, 0xaa, 0x63, 0xb0, 0x71, 0x8e, 0x42, 0x1a, 0x20, 0xc9, 0xf8, 0x29, 0x91, - 0xe9, 0x00, 0x3e, 0x41, 0xf8, 0x82, 0x48, 0x61, 0x7a, 0x50, 0x0f, 0xa9, 0x90, 0x59, 0x66, 0xdd, - 0xb9, 0x36, 0xb3, 0x92, 0x5d, 0xe7, 0x3a, 0x90, 0x43, 0x24, 0x51, 0x56, 0xbb, 0x1a, 0xcb, 0xfe, - 0x2e, 0xac, 0x7d, 0x84, 0xe4, 0x80, 0x93, 0x60, 0x24, 0xc6, 0x2b, 0x50, 0x53, 0xf1, 0x33, 0x74, - 0xfc, 0xd4, 0xa3, 0xda, 0x07, 0xac, 0x7b, 0x9f, 0xc4, 0x8c, 0x4b, 0x12, 0x5c, 0xf2, 0xc8, 0x4b, - 0xdc, 0x7b, 0x01, 0x6b, 0xca, 0x59, 0x82, 0x44, 0x81, 0x5f, 0xdc, 0x33, 0x8d, 0x63, 0x73, 0xef, - 0xc7, 0x13, 0x55, 0xc7, 0xb8, 0xba, 0xec, 0x02, 0xab, 0xc9, 0x18, 0x5d, 0xd8, 0xbf, 0x35, 0xc0, - 0x3a, 0x26, 0xc3, 0x7d, 0x21, 0x68, 0x37, 0xea, 0x93, 0x48, 0xaa, 0x1e, 0x88, 0x30, 0x51, 0x8f, - 0xe6, 0x1b, 0xb0, 0x58, 0xcc, 0x5c, 0x3d, 0x6a, 0x0d, 0x3d, 0x6a, 0x17, 0x72, 0xa2, 0x2a, 0x30, - 0xf3, 0x2e, 0x40, 0xcc, 0x49, 0xe2, 0x63, 0xff, 0x82, 0x0c, 0xb3, 0x28, 0x6e, 0x56, 0x47, 0x68, - 0xfa, 0x7b, 0xd2, 0x39, 0x19, 0x74, 0x42, 0x8a, 0x8f, 0xc9, 0xd0, 0x9b, 0x57, 0xfc, 0xed, 0x63, - 0x32, 0x54, 0x3b, 0x51, 0xcc, 0x9e, 0x11, 0xae, 0xe7, 0x5e, 0xcd, 0x4b, 0x5f, 0xec, 0xdf, 0x19, - 0x70, 0xa3, 0x08, 0x47, 0x9e, 0xae, 0x27, 0x83, 0x8e, 0x92, 0x78, 0x89, 0xdf, 0x2e, 0x59, 0x3b, - 0x7d, 0x85, 0xb5, 0xef, 0xc1, 0x42, 0x51, 0x20, 0xca, 0xde, 0xda, 0x04, 0xf6, 0x36, 0x73, 0x89, - 0x63, 0x32, 0xb4, 0x7f, 0x59, 0xb1, 0xed, 0x60, 0x58, 0xe9, 0x7d, 0xfc, 0xbf, 0xd8, 0x56, 0xa8, - 0xad, 0xda, 0x86, 0xab, 0xf2, 0x97, 0x2e, 0x50, 0xbb, 0x7c, 0x01, 0xfb, 0x0f, 0x06, 0xac, 0x57, - 0xb5, 0x8a, 0x33, 0x76, 0xc2, 0x07, 0x11, 0x79, 0x99, 0xf6, 0xb2, 0xfc, 0xa6, 0xab, 0xe5, 0xf7, - 0x18, 0x96, 0x46, 0x8c, 0x12, 0x99, 0x37, 0x7e, 0x38, 0x51, 0x8e, 0x55, 0xba, 0xab, 0xb7, 0x58, - 0xbd, 0x87, 0xb0, 0xff, 0x64, 0xc0, 0x6a, 0x6e, 0x63, 0xe1, 0x2c, 0xf3, 0x07, 0x60, 0x16, 0xd7, - 0x2b, 0xb7, 0xb7, 0x34, 0xa5, 0x56, 0xf2, 0x93, 0x7c, 0x75, 0x2b, 0x53, 0x63, 0xba, 0x92, 0x1a, - 0xe6, 0x87, 0xb0, 0x56, 0x98, 0x1c, 0xeb, 0x00, 0x4d, 0x1c, 0xc5, 0x62, 0x3f, 0x2d, 0x48, 0xf6, - 0xaf, 0x8d, 0x72, 0x1c, 0xa6, 0xf3, 0x58, 0xec, 0x87, 0x61, 0xb6, 0xd4, 0x9b, 0x31, 0xcc, 0xa5, - 0x23, 0x5f, 0x64, 0xfd, 0x63, 0xf3, 0xca, 0xe1, 0x7e, 0x48, 0xb0, 0x9e, 0xef, 0x77, 0x54, 0x89, - 0xfd, 0xe5, 0xeb, 0xad, 0xb7, 0xbb, 0x54, 0xf6, 0x06, 0x1d, 0x07, 0xb3, 0xbe, 0x9b, 0x7d, 0x0f, - 0x49, 0xff, 0xdd, 0x16, 0xc1, 0x85, 0x2b, 0x87, 0x31, 0x11, 0xb9, 0x8c, 0xf8, 0xf3, 0x3f, 0xff, - 0xf6, 0x7d, 0xc3, 0xcb, 0xd5, 0xd8, 0xbf, 0x31, 0x60, 0xe5, 0x51, 0x2c, 0x49, 0x70, 0x14, 0x95, - 0x6e, 0x9b, 0xa8, 0x08, 0x6f, 0xc1, 0x82, 0x5e, 0x0d, 0xf2, 0x8f, 0x1a, 0xa9, 0xd3, 0x9a, 0x9a, - 0x96, 0x7d, 0xb0, 0xb8, 0xb2, 0xd6, 0xd4, 0x9c, 0xab, 0xf8, 0x31, 0x5d, 0xa5, 0x1b, 0x71, 0xee, - 0xa1, 0x83, 0xc7, 0x5f, 0x3c, 0x6f, 0x19, 0x5f, 0x3e, 0x6f, 0x19, 0xff, 0x78, 0xde, 0x32, 0x3e, - 0x7d, 0xd1, 0x9a, 0xfa, 0xf2, 0x45, 0x6b, 0xea, 0xef, 0x2f, 0x5a, 0x53, 0x3f, 0x7f, 0xf7, 0xf2, - 0x35, 0xcb, 0xac, 0xb9, 0x5d, 0x7c, 0x13, 0x4b, 0x7e, 0xe4, 0x7e, 0x32, 0xfa, 0xc5, 0x4d, 0x7b, - 0xa0, 0x33, 0xab, 0xfb, 0xfb, 0x3b, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x12, 0x76, 0xb8, - 0xa2, 0x13, 0x00, 0x00, + // 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, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -2897,53 +2819,6 @@ func (m *ConsumerRewardsAllocation) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } -func (m *OptedInValidator) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *OptedInValidator) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *OptedInValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.PublicKey) > 0 { - i -= len(m.PublicKey) - copy(dAtA[i:], m.PublicKey) - i = encodeVarintProvider(dAtA, i, uint64(len(m.PublicKey))) - i-- - dAtA[i] = 0x22 - } - if m.Power != 0 { - i = encodeVarintProvider(dAtA, i, uint64(m.Power)) - i-- - dAtA[i] = 0x18 - } - if m.BlockHeight != 0 { - i = encodeVarintProvider(dAtA, i, uint64(m.BlockHeight)) - i-- - dAtA[i] = 0x10 - } - if len(m.ProviderAddr) > 0 { - i -= len(m.ProviderAddr) - copy(dAtA[i:], m.ProviderAddr) - i = encodeVarintProvider(dAtA, i, uint64(len(m.ProviderAddr))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - func encodeVarintProvider(dAtA []byte, offset int, v uint64) int { offset -= sovProvider(v) base := offset @@ -3453,29 +3328,6 @@ func (m *ConsumerRewardsAllocation) Size() (n int) { return n } -func (m *OptedInValidator) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ProviderAddr) - if l > 0 { - n += 1 + l + sovProvider(uint64(l)) - } - if m.BlockHeight != 0 { - n += 1 + sovProvider(uint64(m.BlockHeight)) - } - if m.Power != 0 { - n += 1 + sovProvider(uint64(m.Power)) - } - l = len(m.PublicKey) - if l > 0 { - n += 1 + l + sovProvider(uint64(l)) - } - return n -} - func sovProvider(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -7011,162 +6863,6 @@ func (m *ConsumerRewardsAllocation) Unmarshal(dAtA []byte) error { } return nil } -func (m *OptedInValidator) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: OptedInValidator: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: OptedInValidator: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddr", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthProvider - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthProvider - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ProviderAddr = append(m.ProviderAddr[:0], dAtA[iNdEx:postIndex]...) - if m.ProviderAddr == nil { - m.ProviderAddr = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) - } - m.BlockHeight = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.BlockHeight |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Power", wireType) - } - m.Power = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Power |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthProvider - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthProvider - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.PublicKey = append(m.PublicKey[:0], dAtA[iNdEx:postIndex]...) - if m.PublicKey == nil { - m.PublicKey = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipProvider(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProvider - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func skipProvider(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index aaff34d878..73566926ec 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -156,6 +156,4 @@ type ScopedKeeper interface { type GovKeeper interface { GetProposal(ctx sdk.Context, proposalID uint64) (v1.Proposal, bool) - GetProposals(ctx sdk.Context) (proposals v1.Proposals) - GetVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) (vote v1.Vote, found bool) } From 72f5f66b9acac46691daaf1640b2dbc7467191be Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 26 Mar 2024 10:14:48 +0100 Subject: [PATCH 4/5] feat!: update PSS cli (#1708) finalize PSS CLI cmds --- x/ccv/provider/client/cli/tx.go | 45 +++++++++++++++++++++++++++++++++ x/ccv/provider/handler.go | 10 ++++++-- x/ccv/provider/types/msg.go | 9 +++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index be77ecf6d8..87fea113d1 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -288,3 +288,48 @@ func NewOptOutCmd() *cobra.Command { return cmd } + +func NewSetConsumerCommissionRate() *cobra.Command { + cmd := &cobra.Command{ + Use: "set-consumer-commission-rate [consumer-chain-id] [commission-rate]", + Short: "set a per-consumer chain commission", + Long: strings.TrimSpace( + fmt.Sprintf(`Note that the "commission-rate" argument is a fraction and should be in the range [0,1]. + Example: + %s set-consumer-commission-rate consumer-1 0.5 --from node0 --home ../node0`, + version.AppName), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf, err := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if err != nil { + return err + } + txf = txf.WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) + + providerValAddr := clientCtx.GetFromAddress() + + commission, err := sdk.NewDecFromStr(args[1]) + if err != nil { + return err + } + msg := types.NewMsgSetConsumerCommissionRate(args[0], commission, sdk.ValAddress(providerValAddr)) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + _ = cmd.MarkFlagRequired(flags.FlagFrom) + + return cmd +} diff --git a/x/ccv/provider/handler.go b/x/ccv/provider/handler.go index cf176a86c1..d361ec34f7 100644 --- a/x/ccv/provider/handler.go +++ b/x/ccv/provider/handler.go @@ -23,8 +23,14 @@ func NewHandler(k *keeper.Keeper) sdk.Handler { case *types.MsgSubmitConsumerMisbehaviour: res, err := msgServer.SubmitConsumerMisbehaviour(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) - case *types.MsgSubmitConsumerDoubleVoting: - res, err := msgServer.SubmitConsumerDoubleVoting(sdk.WrapSDKContext(ctx), msg) + case *types.MsgOptIn: + res, err := msgServer.OptIn(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgOptOut: + res, err := msgServer.OptOut(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgSetConsumerCommissionRate: + res, err := msgServer.SetConsumerCommissionRate(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) default: return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index 1a8d6fa145..70f090a971 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -314,6 +314,15 @@ func (msg MsgOptOut) ValidateBasic() error { return nil } +// NewMsgSetConsumerCommissionRate creates a new MsgSetConsumerCommissionRate msg instance. +func NewMsgSetConsumerCommissionRate(chainID string, commission sdk.Dec, providerValidatorAddress sdk.ValAddress) *MsgSetConsumerCommissionRate { + return &MsgSetConsumerCommissionRate{ + ChainId: chainID, + Rate: commission, + ProviderAddr: providerValidatorAddress.String(), + } +} + // Type implements the sdk.Msg interface. func (msg MsgOptOut) Type() string { return TypeMsgOptOut From 8ed60ee83defe646e4638787a9bd2c6dd98064d8 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Tue, 26 Mar 2024 10:53:36 +0100 Subject: [PATCH 5/5] Rename and add comission rate command to commands --- x/ccv/provider/client/cli/tx.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index 87fea113d1..876aaa9b8b 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -36,6 +36,7 @@ func GetTxCmd() *cobra.Command { cmd.AddCommand(NewSubmitConsumerDoubleVotingCmd()) cmd.AddCommand(NewOptInCmd()) cmd.AddCommand(NewOptOutCmd()) + cmd.AddCommand(NewSetConsumerCommissionRateCmd()) return cmd } @@ -289,7 +290,7 @@ func NewOptOutCmd() *cobra.Command { return cmd } -func NewSetConsumerCommissionRate() *cobra.Command { +func NewSetConsumerCommissionRateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "set-consumer-commission-rate [consumer-chain-id] [commission-rate]", Short: "set a per-consumer chain commission",