From 71ac8e38862dd426b4cb796f3a732c01efda8e59 Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 13 Feb 2024 09:06:59 +0100 Subject: [PATCH 01/15] more tests --- x/ccv/provider/keeper/keeper.go | 39 +++ x/ccv/provider/keeper/key_assignment.go | 6 + x/ccv/provider/keeper/key_assignment_test.go | 2 +- x/ccv/provider/keeper/msg_server.go | 1 + x/ccv/provider/keeper/partial_set_security.go | 193 ++++++++++++ .../keeper/partial_set_security_test.go | 289 +++++++++++++++++- x/ccv/provider/keeper/proposal.go | 68 +++-- x/ccv/provider/keeper/relay.go | 12 +- x/ccv/provider/keeper/relay_test.go | 4 +- x/ccv/provider/keeper/throttle.go | 2 +- x/ccv/provider/types/errors.go | 1 + 11 files changed, 581 insertions(+), 36 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index ea052ec5fd..ea6562fef9 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1209,6 +1209,19 @@ func (k Keeper) DeleteOptedIn( store.Delete(types.OptedInKey(chainID, providerAddr)) } +func (k Keeper) DeleteAllOptedIn( + ctx sdk.Context, + chainID string) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.OptedInBytePrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + store.Delete(iterator.Key()) + } +} + func (k Keeper) IsOptedIn( ctx sdk.Context, chainID string, @@ -1254,6 +1267,19 @@ func (k Keeper) DeleteToBeOptedIn( store.Delete(types.ToBeOptedInKey(chainID, providerAddr)) } +func (k Keeper) DeleteAllToBeOptedIn( + ctx sdk.Context, + chainID string) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.ToBeOptedInBytePrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + store.Delete(iterator.Key()) + } +} + func (k Keeper) IsToBeOptedIn( ctx sdk.Context, chainID string, @@ -1298,6 +1324,19 @@ func (k Keeper) DeleteToBeOptedOut( store.Delete(types.ToBeOptedOutKey(chainID, providerAddr)) } +func (k Keeper) DeleteAllToBeOptedOut( + ctx sdk.Context, + chainID string) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.ToBeOptedOutBytePrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + store.Delete(iterator.Key()) + } +} + func (k Keeper) IsToBeOptedOut( ctx sdk.Context, chainID string, diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index fc864c6417..465157ce1e 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -554,6 +554,7 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( ctx sdk.Context, chainID string, valUpdates []abci.ValidatorUpdate, + considerReplacement func(address types.ProviderConsAddress) bool, ) (newUpdates []abci.ValidatorUpdate) { for _, valUpdate := range valUpdates { providerAddrTmp, err := ccvtypes.TMCryptoPublicKeyToConsAddr(valUpdate.PubKey) @@ -608,6 +609,11 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( // power in the pending key assignment. for _, replacement := range k.GetAllKeyAssignmentReplacements(ctx, chainID) { providerAddr := types.NewProviderConsAddress(replacement.ProviderAddr) + + // only consider updates for validators that are considered here ... + if !considerReplacement(providerAddr) { + return + } k.DeleteKeyAssignmentReplacement(ctx, chainID, providerAddr) newUpdates = append(newUpdates, abci.ValidatorUpdate{ PubKey: *replacement.PrevCKey, diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 4fab08c981..e532fb0108 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -828,7 +828,7 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // and increment the provider vscid. applyUpdatesAndIncrementVSCID := func(updates []abci.ValidatorUpdate) { providerValset.apply(updates) - updates = k.MustApplyKeyAssignmentToValUpdates(ctx, CHAINID, updates) + updates = k.MustApplyKeyAssignmentToValUpdates(ctx, CHAINID, updates, func(address types.ProviderConsAddress) bool { return true }) consumerValset.apply(updates) // Simulate the VSCID update in EndBlock k.IncrementValidatorSetUpdateId(ctx) diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index 4863cd0d66..eaee4dfb8f 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -143,6 +143,7 @@ func (k msgServer) OptIn(goCtx context.Context, msg *types.MsgOptIn) (*types.Msg if err != nil { return nil, err } + // FIXME: something is off here .. val to cons ... providerAddr := types.NewProviderConsAddress(valAddress) 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 e1bf9cc14f..ba6c371949 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -2,6 +2,8 @@ package keeper import ( errorsmod "cosmossdk.io/errors" + abci "github.com/cometbft/cometbft/abci/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" 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" @@ -20,6 +22,22 @@ func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types. "opting in to an unknown consumer chain, with id: %s", chainID) } + val, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.ToSdkConsAddr()) + if !found { + return errorsmod.Wrapf( + types.ErrNoValidatorProviderAddress, + "could not find validator with consensus address: %s", providerAddr.ToSdkConsAddr().Bytes()) + } else if !val.IsBonded() { + // FIXME: problematic ... + // Only active validators are allowed to opt in. Note that the fact that a validator is bonded when sending + // a `MsgOptIn` message does not guarantee that the validator would still be bonded when the validator actually + // opts in (e.g., at the end of a block or of an epoch). We recheck if validators are bonded in + // `GetToBeOptedInValidatorUpdates` before sending the partial set to a consumer chain. + return errorsmod.Wrapf( + types.ErrValidatorNotBonded, + "validator with consensus address: %s is not bonded", providerAddr.ToSdkConsAddr().Bytes()) + } + 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) @@ -67,3 +85,178 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types return nil } + +func (k Keeper) getValidatorsPublicKey(validator stakingtypes.Validator) (tmprotocrypto.PublicKey, error) { + consAddr, err := validator.GetConsAddr() + if err != nil { + return tmprotocrypto.PublicKey{}, err + } + return tmprotocrypto.PublicKey{ + Sum: &tmprotocrypto.PublicKey_Ed25519{ + Ed25519: consAddr.Bytes(), + }, + }, nil +} + +// GetToBeOptedInValidatorUpdates returns all the needed `ValidatorUpdate`s for validators that are to be opted in +// on consumer chain with `chainID` +func (k Keeper) GetToBeOptedInValidatorUpdates(ctx sdk.Context, chainID string) []abci.ValidatorUpdate { + var updates []abci.ValidatorUpdate + for _, val := range k.GetToBeOptedIn(ctx, chainID) { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ToSdkConsAddr()) + if !found { + // a validator was successfully set to be opted in, but we cannot find this validator anymore + k.Logger(ctx).Error("could not find validator with consensus address: %s", val.ToSdkConsAddr().Bytes()) + } + + // FIXME: bonded means in the active ... + if !validator.IsBonded() { + // a validator might have started unbonding or unbonded since it asked to be opted in + continue + } + + pubKey, err := k.getValidatorsPublicKey(validator) + if err != nil { + k.Logger(ctx).Error("could not find validator with consensus address: %s", val.ToSdkConsAddr().Bytes()) + continue + } + + updates = append(updates, abci.ValidatorUpdate{ + PubKey: pubKey, + Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), + }) + } + + return updates +} + +// GetToBeOptedOutValidatorUpdates returns all the needed `ValidatorUpdate`s for validators that are to be opted out +// of the consumer chain with `chainID` +func (k Keeper) GetToBeOptedOutValidatorUpdates(ctx sdk.Context, chainID string) []abci.ValidatorUpdate { + var updates []abci.ValidatorUpdate + for _, val := range k.GetToBeOptedOut(ctx, chainID) { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ToSdkConsAddr()) + if !found { + // a validator was successfully set to be opted in, but we cannot find this validator anymore + k.Logger(ctx).Error("could not find validator with consensus address: %s", val.ToSdkConsAddr().Bytes()) + continue + } + + pubKey, err := k.getValidatorsPublicKey(validator) + if err != nil { + continue + } + + updates = append(updates, abci.ValidatorUpdate{ + PubKey: pubKey, + Power: 0, + }) + } + return updates +} + +// ComputePartialSetValUpdates computes and returns the partial set `ValidatorUpdate`s for given chain with `chainID` +// and provided the `stakingUpdates` stemming from the staking module +func (k Keeper) ComputePartialSetValUpdates(ctx sdk.Context, chainID string, stakingUpdates []abci.ValidatorUpdate) []abci.ValidatorUpdate { + var partialSetUpdates []abci.ValidatorUpdate + + toBeOptedInValidatorUpdates := k.GetToBeOptedInValidatorUpdates(ctx, chainID) + toBeOptedOutValidatorUpdates := k.GetToBeOptedOutValidatorUpdates(ctx, chainID) + + partialSetUpdates = append(partialSetUpdates, toBeOptedInValidatorUpdates...) + partialSetUpdates = append(partialSetUpdates, toBeOptedOutValidatorUpdates...) + + // Create set that contains all the validators that are to be opted out so that we do not reintroduce opted-out + // validators when going through the already opted in validators. Opted out validators are already included + // in the partial set updates through `toBeOptedOutValidatorUpdates`. + isSetToBeOptedOut := make(map[string]bool) + for _, val := range toBeOptedOutValidatorUpdates { + isSetToBeOptedOut[val.PubKey.String()] = true + } + + // Create set that contains all the validators that stem from `stakingUpdates` changes so that we only send + // validator updates for validators that had a change in their voting power. + isStakingUpdate := make(map[string]bool) + for _, val := range stakingUpdates { + isStakingUpdate[val.PubKey.String()] = true + } + + for _, val := range k.GetOptedIn(ctx, chainID) { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) + if !found { + continue + } + pubKey, err := k.getValidatorsPublicKey(validator) + if err != nil { + continue + } + + if isSetToBeOptedOut[pubKey.String()] { + // do not create a `ValidatorUpdate` for validators that opt out + continue + } + + if !isStakingUpdate[pubKey.String()] { + // only send an update if an opted in validator had a staking update from the staking module and + // hence a voting power change + continue + } + + partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ + PubKey: pubKey, + Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), + }) + } + + return partialSetUpdates +} + +func (k Keeper) ResetPartialSet(ctx sdk.Context, chainID string) { + var optedOutOnes map[string]bool + for _, v := range k.GetToBeOptedIn(ctx, chainID) { + optedOutOnes[v.String()] = true + } + + var allOfThem []types.ProviderConsAddress + for _, v := range k.GetOptedIn(ctx, chainID) { + // FOXME: + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, v.ProviderAddr.ToSdkConsAddr()) + if !found { + // probably an error + continue + } + if !validator.IsBonded() { + continue + } + // only still bonded ones ... + if optedOutOnes[v.ProviderAddr.String()] { + // here you would need ot remove + continue + } + allOfThem = append(allOfThem, v.ProviderAddr) + } + + for _, v := range k.GetToBeOptedIn(ctx, chainID) { + // only still bonded ones ... + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, v.ToSdkConsAddr()) + if !found { + // probably an error + continue + } + if !validator.IsBonded() { + continue + } + allOfThem = append(allOfThem, types.NewProviderConsAddress(v.Address)) + } + + for _, v := range allOfThem { + if !k.IsOptedIn(ctx, chainID, v) { + k.SetOptedIn(ctx, chainID, v, uint64(ctx.BlockHeight())) + } else { + // leave the previous block height as is + } + } + + k.DeleteAllToBeOptedIn(ctx, chainID) + k.DeleteAllToBeOptedOut(ctx, chainID) +} diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 4831723bec..eab9feec04 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 ( + "fmt" + abci "github.com/cometbft/cometbft/abci/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" @@ -10,15 +13,35 @@ import ( ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "sort" "testing" ) func TestHandleOptIn(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() providerAddr := types.NewProviderConsAddress([]byte("providerAddr")) + // mock `GetValidatorByConsAddr` that returns a `Bonded` validator for the `providerAddr` address and + // an `Unbonding` validator for any other address + mocks.MockStakingKeeper.EXPECT(). + GetValidatorByConsAddr(gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx sdk.Context, addr sdk.ConsAddress) (stakingtypes.Validator, bool) { + if addr.Equals(providerAddr.Address) { + pkAny, _ := codectypes.NewAnyWithValue(ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey()) + return stakingtypes.Validator{ConsensusPubkey: pkAny, Status: stakingtypes.Bonded}, true + } else { + pkAny, _ := codectypes.NewAnyWithValue(ed25519.GenPrivKeyFromSecret([]byte{2}).PubKey()) + return stakingtypes.Validator{ConsensusPubkey: pkAny, Status: stakingtypes.Unbonding}, true + } + }).AnyTimes() + + // verify that a non-`Bonded` validator cannot opt in + unbondedProviderAddr := types.NewProviderConsAddress([]byte("unbondedProviderAddr")) + providerKeeper.SetProposedConsumerChain(ctx, "someChainID", 1) + require.Error(t, providerKeeper.HandleOptIn(ctx, "someChainID", unbondedProviderAddr, nil)) + // trying to opt in to a non-proposed and non-registered chain returns an error require.Error(t, providerKeeper.HandleOptIn(ctx, "unknownChainID", providerAddr, nil)) @@ -54,12 +77,12 @@ func TestHandleOptInWithConsumerKey(t *testing.T) { // Given `providerAddr`, `GetValidatorByConsAddr` returns a validator with the // exact same `ConsensusPubkey` pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) - return stakingtypes.Validator{ConsensusPubkey: pkAny}, true + return stakingtypes.Validator{ConsensusPubkey: pkAny, Status: stakingtypes.Bonded}, true } else { // for any other consensus address, we cannot find a validator return stakingtypes.Validator{}, false } - }).Times(2), + }).Times(3), } gomock.InOrder(calls...) @@ -109,3 +132,263 @@ func TestHandleOptOut(t *testing.T) { require.NoError(t, err) require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) } + +func TestGetToBeOptedInValidatorUpdates(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + type TestValidator struct { + validator stakingtypes.Validator + power int64 + expectedValUpdate abci.ValidatorUpdate + } + + chainID := "chainID" + + var testValidators []TestValidator + for i := int64(0); i < 10; i++ { + // generate a consensus public key for the provider + providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() + consAddr := sdk.ConsAddress(providerConsPubKey.Address()) + providerAddr := types.NewProviderConsAddress(consAddr) + + foo := providerAddr.Address.Bytes() + var providerValidatorAddr sdk.ValAddress + providerValidatorAddr = foo + + var status stakingtypes.BondStatus + if i%3 == 0 { + status = stakingtypes.Unbonded + } else if i%3 == 1 { + status = stakingtypes.Bonded + } else { + status = stakingtypes.Unbonding + } + + pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) + validator := stakingtypes.Validator{ + OperatorAddress: providerValidatorAddr.String(), + ConsensusPubkey: pkAny, + Status: status, + } + + consumerTMPublicKey := tmprotocrypto.PublicKey{ + Sum: &tmprotocrypto.PublicKey_Ed25519{ + Ed25519: consAddr.Bytes(), + }, + } + + expectedValUpdate := abci.ValidatorUpdate{PubKey: consumerTMPublicKey, Power: i} + + testValidators = append(testValidators, + TestValidator{validator: validator, power: i, expectedValUpdate: expectedValUpdate}) + + providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddr) + } + + for _, val := range testValidators { + consAddr, _ := val.validator.GetConsAddr() + mocks.MockStakingKeeper.EXPECT(). + GetValidatorByConsAddr(ctx, consAddr).Return(val.validator, true).AnyTimes() + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, val.validator.GetOperator()).Return(val.power).AnyTimes() + } + + var expectedValUpdates []abci.ValidatorUpdate + for _, val := range testValidators { + if !val.validator.IsBonded() { + continue + } + expectedValUpdates = append(expectedValUpdates, val.expectedValUpdate) + } + + actualValUpdates := providerKeeper.GetToBeOptedInValidatorUpdates(ctx, chainID) + + // sort before comparing + sort.Slice(expectedValUpdates, func(i int, j int) bool { + if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) < 0 { + return true + } else if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) == 0 { + return expectedValUpdates[i].Power < expectedValUpdates[j].Power + } + return false + }) + sort.Slice(actualValUpdates, func(i int, j int) bool { + if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) < 0 { + return true + } else if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) == 0 { + return actualValUpdates[i].Power < actualValUpdates[j].Power + } + return false + }) + + require.Equal(t, expectedValUpdates, actualValUpdates) +} + +func TestGetToBeOptedOutValidatorUpdates(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + type TestValidator struct { + validator stakingtypes.Validator + power int64 + expectedValUpdate abci.ValidatorUpdate + } + + chainID := "chainID" + + var testValidators []TestValidator + for i := int64(0); i < 10; i++ { + // generate a consensus public key for the provider + providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() + consAddr := sdk.ConsAddress(providerConsPubKey.Address()) + providerAddr := types.NewProviderConsAddress(consAddr) + + foo := providerAddr.Address.Bytes() + var providerValidatorAddr sdk.ValAddress + providerValidatorAddr = foo + + pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) + validator := stakingtypes.Validator{ + OperatorAddress: providerValidatorAddr.String(), + ConsensusPubkey: pkAny, + } + + consumerTMPublicKey := tmprotocrypto.PublicKey{ + Sum: &tmprotocrypto.PublicKey_Ed25519{ + Ed25519: consAddr.Bytes(), + }, + } + + expectedValUpdate := abci.ValidatorUpdate{PubKey: consumerTMPublicKey, Power: 0} + + testValidators = append(testValidators, + TestValidator{validator: validator, power: i, expectedValUpdate: expectedValUpdate}) + + providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddr) + } + + for _, val := range testValidators { + consAddr, _ := val.validator.GetConsAddr() + mocks.MockStakingKeeper.EXPECT(). + GetValidatorByConsAddr(ctx, consAddr).Return(val.validator, true).AnyTimes() + } + + var expectedValUpdates []abci.ValidatorUpdate + for _, val := range testValidators { + expectedValUpdates = append(expectedValUpdates, val.expectedValUpdate) + } + + actualValUpdates := providerKeeper.GetToBeOptedOutValidatorUpdates(ctx, chainID) + + // sort before comparing + sort.Slice(expectedValUpdates, func(i int, j int) bool { + if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) < 0 { + return true + } else if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) == 0 { + return expectedValUpdates[i].Power < expectedValUpdates[j].Power + } + return false + }) + sort.Slice(actualValUpdates, func(i int, j int) bool { + if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) < 0 { + return true + } else if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) == 0 { + return actualValUpdates[i].Power < actualValUpdates[j].Power + } + return false + }) + + require.Equal(t, expectedValUpdates, actualValUpdates) +} + +func createValidators(bondStatutes []stakingtypes.BondStatus, powers []int64) (validators []stakingtypes.Validator, + valUpdates []abci.ValidatorUpdate) { + if len(bondStatutes) != len(powers) { + return + } + + for i := 0; i < len(bondStatutes); i++ { + providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() + consAddr := sdk.ConsAddress(providerConsPubKey.Address()) + providerAddr := types.NewProviderConsAddress(consAddr) + + var providerValidatorAddr sdk.ValAddress + providerValidatorAddr = providerAddr.Address.Bytes() + + pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) + validator := stakingtypes.Validator{ + OperatorAddress: providerValidatorAddr.String(), + ConsensusPubkey: pkAny, + Status: bondStatutes[i], + } + validators = append(validators, validator) + + consumerTMPublicKey := tmprotocrypto.PublicKey{ + Sum: &tmprotocrypto.PublicKey_Ed25519{ + Ed25519: consAddr.Bytes(), + }, + } + + valUpdate := abci.ValidatorUpdate{PubKey: consumerTMPublicKey, Power: powers[i]} + valUpdates = append(valUpdates, valUpdate) + } + return +} + +func TestComputePartialValidatorUpdateSet(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + type TestValidator struct { + validator stakingtypes.Validator + power int64 + expectedValUpdate abci.ValidatorUpdate + } + + chainID := "chainID" + + // create 10 validator updates + //var updates []abci.ValidatorUpdate + powers := []int64{1, 2, 3, 4, 5, 6} + vals, updates := createValidators( + []stakingtypes.BondStatus{stakingtypes.Bonded, stakingtypes.Unbonding, stakingtypes.Bonded, stakingtypes.Unbonded, + stakingtypes.Unbonded, stakingtypes.Bonded}, powers) + + addr0, _ := vals[0].GetConsAddr() + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(addr0), uint64(1)) + addr1, _ := vals[1].GetConsAddr() + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(addr1), uint64(2)) + addr2, _ := vals[2].GetConsAddr() + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(addr2), uint64(3)) + addr3, _ := vals[3].GetConsAddr() + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(addr3), uint64(4)) + + // set to be opted out + providerKeeper.SetToBeOptedOut(ctx, chainID, types.NewProviderConsAddress(addr2)) + providerKeeper.SetToBeOptedOut(ctx, chainID, types.NewProviderConsAddress(addr3)) + + // make all the validators at once ... kane to stakingUpdates based on the publickeys of those validators and then get the result + addr4, _ := vals[4].GetConsAddr() + providerKeeper.SetToBeOptedIn(ctx, chainID, types.NewProviderConsAddress(addr4)) + addr5, _ := vals[5].GetConsAddr() + providerKeeper.SetToBeOptedIn(ctx, chainID, types.NewProviderConsAddress(addr5)) + + for i, val := range vals { + consAddr, _ := val.GetConsAddr() + mocks.MockStakingKeeper.EXPECT(). + GetValidatorByConsAddr(ctx, consAddr).Return(val, true).AnyTimes() + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, val.GetOperator()).Return(powers[i]).AnyTimes() + } + + actualValUpdates := providerKeeper.ComputePartialSetValUpdates(ctx, chainID, []abci.ValidatorUpdate{}) + + fmt.Println(updates) + fmt.Println(actualValUpdates) + //require.Equal(t, expectedValUpdates, actualValUpdates) +} + +func TestResetPartialSet(t *testing.T) { + // s +} diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 50dc69d080..c391d72ddd 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -255,31 +255,38 @@ func (k Keeper) MakeConsumerGenesis( return false }) - initialUpdates := []abci.ValidatorUpdate{} - for _, p := range lastPowers { - addr, err := sdk.ValAddressFromBech32(p.Address) - if err != nil { - return gen, nil, err - } - - val, found := k.stakingKeeper.GetValidator(ctx, addr) - if !found { - return gen, nil, errorsmod.Wrapf(stakingtypes.ErrNoValidatorFound, "error getting validator from LastValidatorPowers: %s", err) - } - - tmProtoPk, err := val.TmConsPublicKey() - if err != nil { - return gen, nil, err - } - - initialUpdates = append(initialUpdates, abci.ValidatorUpdate{ - PubKey: tmProtoPk, - Power: p.Power, - }) - } + // at this initial state, no validator is yet opted in ... but a validator is to be opted in + initialUpdates := k.ComputePartialSetValUpdates(ctx, chainID, []abci.ValidatorUpdate{}) + + // + //initialUpdates := []abci.ValidatorUpdate{} + //for _, p := range lastPowers { + // addr, err := sdk.ValAddressFromBech32(p.Address) + // if err != nil { + // return gen, nil, err + // } + // + // validator, found := k.stakingKeeper.GetValidator(ctx, addr) + // if !found { + // return gen, nil, errorsmod.Wrapf(stakingtypes.ErrNoValidatorFound, "error getting validator from LastValidatorPowers: %s", err) + // } + // + // tmProtoPk, err := validator.TmConsPublicKey() + // if err != nil { + // return gen, nil, err + // } + // + // initialUpdates = append(initialUpdates, abci.ValidatorUpdate{ + // PubKey: tmProtoPk, + // Power: p.Power, + // }) + //} // Apply key assignments to the initial valset. - initialUpdatesWithConsumerKeys := k.MustApplyKeyAssignmentToValUpdates(ctx, chainID, initialUpdates) + initialUpdatesWithConsumerKeys := k.MustApplyKeyAssignmentToValUpdates(ctx, chainID, initialUpdates, + func(address types.ProviderConsAddress) bool { return k.IsToBeOptedIn(ctx, chainID, address) }) + + k.ResetPartialSet(ctx, chainID) // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) @@ -360,6 +367,17 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { propsToExecute := k.GetConsumerAdditionPropsToExecute(ctx) for _, prop := range propsToExecute { + // Only set Top N at the moment a chain starts. If we were to do this earlier (e.g., during the proposal), + // then someone could create a bogus ConsumerAdditionProposal to override the Top N for a specific chain. + k.SetTopN(ctx, prop.ChainId, prop.Top_N) + + if !k.IsTopN(ctx, prop.ChainId) && len(k.GetToBeOptedIn(ctx, prop.ChainId)) == 0 { + // drop the proposal + ctx.Logger().Info("could not start Opt In consumer chain (%s) because no validator has opted in", + prop.ChainId) + continue + } + // create consumer client in a cached context to handle errors cachedCtx, writeFn, err := k.CreateConsumerClientInCachedCtx(ctx, prop) if err != nil { @@ -368,10 +386,6 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { continue } - // Only set Top N at the moment a chain starts. If we were to do this earlier (e.g., during the proposal), - // then someone could create a bogus ConsumerAdditionProposal to override the Top N for a specific chain. - k.SetTopN(ctx, prop.ChainId, prop.Top_N) - // The cached context is created with a new EventManager so we merge the event // into the original context ctx.EventManager().EmitEvents(cachedCtx.EventManager().Events()) diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 2c00d40441..f090d367fe 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -218,8 +218,16 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { valUpdates := k.stakingKeeper.GetValidatorUpdates(ctx) for _, chain := range k.GetAllConsumerChains(ctx) { + partialSetUpdates := k.ComputePartialSetValUpdates(ctx, chain.ChainId, valUpdates) + + // FIXME: staking updtes contain ... ARE only for active set ... // Apply the key assignment to the validator updates. - valUpdates := k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, valUpdates) + valUpdates := k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, partialSetUpdates, + func(address providertypes.ProviderConsAddress) bool { + return k.IsOptedIn(ctx, chain.ChainId, address) + }) + + k.ResetPartialSet(ctx, chain.ChainId) // check whether there are changes in the validator set; // note that this also entails unbonding operations @@ -498,7 +506,7 @@ func (k Keeper) EndBlockCCR(ctx sdk.Context) { } } -// getMappedInfractionHeight gets the infraction height mapped from val set ID for the given chain ID +// getMappedInfractionHeight gets the infraction height mapped from validator set ID for the given chain ID func (k Keeper) getMappedInfractionHeight(ctx sdk.Context, chainID string, valsetUpdateID uint64, ) (height uint64, found bool) { diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 02df262d53..f5955e63f0 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -385,7 +385,7 @@ func TestHandleSlashPacket(t *testing.T) { return testkeeper.GetMocksForHandleSlashPacket( ctx, mocks, providerConsAddr, // expected provider cons addr returned from GetProviderAddrFromConsumerAddr - stakingtypes.Validator{Jailed: false}, // staking keeper val to return + stakingtypes.Validator{Jailed: false}, // staking keeper validator to return true) // expectJailing = true }, 1, @@ -402,7 +402,7 @@ func TestHandleSlashPacket(t *testing.T) { return testkeeper.GetMocksForHandleSlashPacket( ctx, mocks, providerConsAddr, // expected provider cons addr returned from GetProviderAddrFromConsumerAddr - stakingtypes.Validator{Jailed: true}, // staking keeper val to return + stakingtypes.Validator{Jailed: true}, // staking keeper validator to return false) // expectJailing = false, validator is already jailed. }, 1, diff --git a/x/ccv/provider/keeper/throttle.go b/x/ccv/provider/keeper/throttle.go index b7e7fd5941..dc473ac506 100644 --- a/x/ccv/provider/keeper/throttle.go +++ b/x/ccv/provider/keeper/throttle.go @@ -17,7 +17,7 @@ import ( func (k Keeper) GetEffectiveValPower(ctx sdktypes.Context, valConsAddr providertypes.ProviderConsAddress, ) math.Int { - // Obtain staking module val object from the provider's consensus address. + // Obtain staking module validator object from the provider's consensus address. // Note: if validator is not found or unbonded, this will be handled appropriately in HandleSlashPacket val, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, valConsAddr.ToSdkConsAddr()) diff --git a/x/ccv/provider/types/errors.go b/x/ccv/provider/types/errors.go index 6c19a7b396..b1d55a0ea1 100644 --- a/x/ccv/provider/types/errors.go +++ b/x/ccv/provider/types/errors.go @@ -24,4 +24,5 @@ var ( ErrInvalidConsumerClient = errorsmod.Register(ModuleName, 16, "ccv channel is not built on correct client") ErrDuplicateConsumerChain = errorsmod.Register(ModuleName, 17, "consumer chain already exists") ErrConsumerChainNotFound = errorsmod.Register(ModuleName, 18, "consumer chain not found") + ErrValidatorNotBonded = errorsmod.Register(ModuleName, 19, "validator not bonded") ) From 61a40521377f90256ac490a9e5cd1151e73c27b4 Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 13 Feb 2024 14:08:29 +0100 Subject: [PATCH 02/15] interface changes --- x/ccv/provider/keeper/keeper.go | 10 +- x/ccv/provider/keeper/keeper_test.go | 9 +- x/ccv/provider/keeper/key_assignment.go | 4 +- x/ccv/provider/keeper/partial_set_security.go | 221 ++++++------ .../keeper/partial_set_security_test.go | 336 ++++++++++-------- x/ccv/provider/keeper/proposal.go | 7 +- x/ccv/provider/keeper/relay.go | 33 +- x/ccv/provider/types/errors.go | 1 - 8 files changed, 335 insertions(+), 286 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index ea6562fef9..aeb206a302 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1190,14 +1190,17 @@ func (k Keeper) SetOptedIn( chainID string, providerAddr types.ProviderConsAddress, blockHeight uint64, + power uint64, ) { store := ctx.KVStore(k.storeKey) - // validator is considered opted in blockHeightBytes := make([]byte, 8) binary.BigEndian.PutUint64(blockHeightBytes, blockHeight) - store.Set(types.OptedInKey(chainID, providerAddr), blockHeightBytes) + powerBytes := make([]byte, 8) + binary.BigEndian.PutUint64(powerBytes, power) + + store.Set(types.OptedInKey(chainID, providerAddr), append(blockHeightBytes, powerBytes...)) } func (k Keeper) DeleteOptedIn( @@ -1242,7 +1245,8 @@ func (k Keeper) GetOptedIn( for ; iterator.Valid(); iterator.Next() { optedInValidators = append(optedInValidators, OptedInValidator{ ProviderAddr: types.NewProviderConsAddress(iterator.Key()[len(key):]), - BlockHeight: binary.BigEndian.Uint64(iterator.Value()), + BlockHeight: binary.BigEndian.Uint64(iterator.Value()[0:8]), + Power: binary.BigEndian.Uint64(iterator.Value()[8:]), }) } diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index 6eb28675ba..a3605a31fe 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -670,20 +670,24 @@ func TestGetOptedIn(t *testing.T) { { ProviderAddr: types.NewProviderConsAddress([]byte("providerAddr1")), BlockHeight: 1, + Power: 2, }, { ProviderAddr: types.NewProviderConsAddress([]byte("providerAddr2")), BlockHeight: 2, + Power: 3, }, { ProviderAddr: types.NewProviderConsAddress([]byte("providerAddr3")), BlockHeight: 3, + Power: 4, }, } for _, expectedOptedInValidator := range expectedOptedInValidators { providerKeeper.SetOptedIn(ctx, "chainID", - expectedOptedInValidator.ProviderAddr, expectedOptedInValidator.BlockHeight) + expectedOptedInValidator.ProviderAddr, expectedOptedInValidator.BlockHeight, + expectedOptedInValidator.Power) } actualOptedInValidators := providerKeeper.GetOptedIn(ctx, "chainID") @@ -710,10 +714,11 @@ func TestOptedIn(t *testing.T) { optedInValidator := keeper.OptedInValidator{ ProviderAddr: types.NewProviderConsAddress([]byte("providerAddr")), BlockHeight: 1, + Power: 2, } require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator.ProviderAddr)) - providerKeeper.SetOptedIn(ctx, "chainID", optedInValidator.ProviderAddr, optedInValidator.BlockHeight) + providerKeeper.SetOptedIn(ctx, "chainID", optedInValidator.ProviderAddr, optedInValidator.BlockHeight, optedInValidator.Power) require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator.ProviderAddr)) providerKeeper.DeleteOptedIn(ctx, "chainID", optedInValidator.ProviderAddr) require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator.ProviderAddr)) diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index 465157ce1e..5b66fc7b86 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -554,7 +554,7 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( ctx sdk.Context, chainID string, valUpdates []abci.ValidatorUpdate, - considerReplacement func(address types.ProviderConsAddress) bool, + considerKeyReplacement func(address types.ProviderConsAddress) bool, ) (newUpdates []abci.ValidatorUpdate) { for _, valUpdate := range valUpdates { providerAddrTmp, err := ccvtypes.TMCryptoPublicKeyToConsAddr(valUpdate.PubKey) @@ -611,7 +611,7 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( providerAddr := types.NewProviderConsAddress(replacement.ProviderAddr) // only consider updates for validators that are considered here ... - if !considerReplacement(providerAddr) { + if !considerKeyReplacement(providerAddr) { return } k.DeleteKeyAssignmentReplacement(ctx, chainID, providerAddr) diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index ba6c371949..334afe8128 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -13,6 +13,8 @@ type OptedInValidator struct { ProviderAddr types.ProviderConsAddress // block height the validator opted in at BlockHeight uint64 + // power the validator had when it opted in + Power uint64 } func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, consumerKey *string) error { @@ -22,20 +24,11 @@ func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types. "opting in to an unknown consumer chain, with id: %s", chainID) } - val, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.ToSdkConsAddr()) + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.ToSdkConsAddr()) if !found { return errorsmod.Wrapf( types.ErrNoValidatorProviderAddress, "could not find validator with consensus address: %s", providerAddr.ToSdkConsAddr().Bytes()) - } else if !val.IsBonded() { - // FIXME: problematic ... - // Only active validators are allowed to opt in. Note that the fact that a validator is bonded when sending - // a `MsgOptIn` message does not guarantee that the validator would still be bonded when the validator actually - // opts in (e.g., at the end of a block or of an epoch). We recheck if validators are bonded in - // `GetToBeOptedInValidatorUpdates` before sending the partial set to a consumer chain. - return errorsmod.Wrapf( - types.ErrValidatorNotBonded, - "validator with consensus address: %s is not bonded", providerAddr.ToSdkConsAddr().Bytes()) } if k.IsToBeOptedOut(ctx, chainID, providerAddr) { @@ -52,11 +45,6 @@ func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types. return err } - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.Address) - if !found { - return stakingtypes.ErrNoValidatorFound - } - err = k.AssignConsumerKey(ctx, chainID, validator, consumerTMPublicKey) if err != nil { return err @@ -86,6 +74,7 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types return nil } +// getValidatorsPublicKey is a helper function that returns the public key of the validator func (k Keeper) getValidatorsPublicKey(validator stakingtypes.Validator) (tmprotocrypto.PublicKey, error) { consAddr, err := validator.GetConsAddr() if err != nil { @@ -98,163 +87,171 @@ func (k Keeper) getValidatorsPublicKey(validator stakingtypes.Validator) (tmprot }, nil } -// GetToBeOptedInValidatorUpdates returns all the needed `ValidatorUpdate`s for validators that are to be opted in -// on consumer chain with `chainID` -func (k Keeper) GetToBeOptedInValidatorUpdates(ctx sdk.Context, chainID string) []abci.ValidatorUpdate { - var updates []abci.ValidatorUpdate - for _, val := range k.GetToBeOptedIn(ctx, chainID) { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ToSdkConsAddr()) +// GetToBeOptedInValidators returns all the needed validators that are to be opted in +// on consumer chain with `chainID` and that are still bonded +func (k Keeper) GetToBeOptedInValidators(ctx sdk.Context, chainID string) []types.ProviderConsAddress { + var consAddresses []types.ProviderConsAddress + for _, addr := range k.GetToBeOptedIn(ctx, chainID) { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) if !found { // a validator was successfully set to be opted in, but we cannot find this validator anymore - k.Logger(ctx).Error("could not find validator with consensus address: %s", val.ToSdkConsAddr().Bytes()) + k.Logger(ctx).Error("could not find to-be-opted-in invalidator with consensus address: %s", addr.ToSdkConsAddr().Bytes()) + continue } - // FIXME: bonded means in the active ... if !validator.IsBonded() { - // a validator might have started unbonding or unbonded since it asked to be opted in + // only consider validators that are in the active set continue } + consAddresses = append(consAddresses, addr) + } - pubKey, err := k.getValidatorsPublicKey(validator) - if err != nil { - k.Logger(ctx).Error("could not find validator with consensus address: %s", val.ToSdkConsAddr().Bytes()) + return consAddresses +} + +// GetNextOptedInValidators returns all the addresses of validators that are going to be opted in +// next on the consumer chain with `chainID` and a validator update would need to be sent for them +func (k Keeper) GetNextOptedInValidators(ctx sdk.Context, chainID string) []types.ProviderConsAddress { + var consAddresses []types.ProviderConsAddress + + // Create set that contains all the validators that are to be opted out so that we do not reintroduce opted-out + // validators when going through the already opted in validators. + isSetToBeOptedOut := make(map[string]bool) + for _, addr := range k.GetToBeOptedOut(ctx, chainID) { + isSetToBeOptedOut[addr.ToSdkConsAddr().String()] = true + } + + for _, val := range k.GetOptedIn(ctx, chainID) { + // go through all the validators that are currently opted in + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) + if !found { + // a validator was opted in, but we cannot find this validator anymore + k.Logger(ctx).Error("could not find opted-in validator with consensus address: %s", + val.ProviderAddr.ToSdkConsAddr().Bytes()) continue } - updates = append(updates, abci.ValidatorUpdate{ - PubKey: pubKey, - Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), - }) + if isSetToBeOptedOut[val.ProviderAddr.ToSdkConsAddr().String()] { + continue + } + + if val.Power == uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) { + // Only send an update if an opted-in validator had a power change since the lat time it was sent. + // If an opted-in validator is not in the active set anymore, then its new power is 0 and hence we + // do not consider this validator in the next set of opted in validators. + continue + } + + consAddresses = append(consAddresses, val.ProviderAddr) } - return updates + // append all the to-be-opted-in validators + consAddresses = append(consAddresses, k.GetToBeOptedInValidators(ctx, chainID)...) + return consAddresses } -// GetToBeOptedOutValidatorUpdates returns all the needed `ValidatorUpdate`s for validators that are to be opted out -// of the consumer chain with `chainID` -func (k Keeper) GetToBeOptedOutValidatorUpdates(ctx sdk.Context, chainID string) []abci.ValidatorUpdate { - var updates []abci.ValidatorUpdate - for _, val := range k.GetToBeOptedOut(ctx, chainID) { +// ComputePartialSetValidatorUpdates returns the partial set of `ValidatorUpdate`s that the provider chain sends to the +// consumer chain. Receives `nextOptedIn` that corresponds to the validators that would be opted in next and +// `toBeOptedOut` that contains the validators that would be opted out. +func (k Keeper) ComputePartialSetValidatorUpdates(ctx sdk.Context, nextOptedIn []types.ProviderConsAddress, + toBeOptedOut []types.ProviderConsAddress) []abci.ValidatorUpdate { + var partialSetUpdates []abci.ValidatorUpdate + + for _, val := range nextOptedIn { validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ToSdkConsAddr()) if !found { - // a validator was successfully set to be opted in, but we cannot find this validator anymore - k.Logger(ctx).Error("could not find validator with consensus address: %s", val.ToSdkConsAddr().Bytes()) + // any validator in `nextOptedIn` should be found + k.Logger(ctx).Error("could not find validator with consensus address: %s", + val.ToSdkConsAddr().Bytes()) continue } pubKey, err := k.getValidatorsPublicKey(validator) if err != nil { + k.Logger(ctx).Error("could retrieve public key from validator with consensus address: %s", + val.ToSdkConsAddr().Bytes()) continue } - updates = append(updates, abci.ValidatorUpdate{ + // if a validator that was opted in, is not in the active set anymore, then `GetLastValidatorPower` returns 0 + partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ PubKey: pubKey, - Power: 0, + Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), }) } - return updates -} - -// ComputePartialSetValUpdates computes and returns the partial set `ValidatorUpdate`s for given chain with `chainID` -// and provided the `stakingUpdates` stemming from the staking module -func (k Keeper) ComputePartialSetValUpdates(ctx sdk.Context, chainID string, stakingUpdates []abci.ValidatorUpdate) []abci.ValidatorUpdate { - var partialSetUpdates []abci.ValidatorUpdate - - toBeOptedInValidatorUpdates := k.GetToBeOptedInValidatorUpdates(ctx, chainID) - toBeOptedOutValidatorUpdates := k.GetToBeOptedOutValidatorUpdates(ctx, chainID) - - partialSetUpdates = append(partialSetUpdates, toBeOptedInValidatorUpdates...) - partialSetUpdates = append(partialSetUpdates, toBeOptedOutValidatorUpdates...) - - // Create set that contains all the validators that are to be opted out so that we do not reintroduce opted-out - // validators when going through the already opted in validators. Opted out validators are already included - // in the partial set updates through `toBeOptedOutValidatorUpdates`. - isSetToBeOptedOut := make(map[string]bool) - for _, val := range toBeOptedOutValidatorUpdates { - isSetToBeOptedOut[val.PubKey.String()] = true - } - // Create set that contains all the validators that stem from `stakingUpdates` changes so that we only send - // validator updates for validators that had a change in their voting power. - isStakingUpdate := make(map[string]bool) - for _, val := range stakingUpdates { - isStakingUpdate[val.PubKey.String()] = true - } - - for _, val := range k.GetOptedIn(ctx, chainID) { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) + for _, addr := range toBeOptedOut { + // go through all the validators that are currently opted in + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) if !found { + // a validator was opted in, but we cannot find this validator anymore + k.Logger(ctx).Error("could not find opted-in validator with consensus address: %s", + addr.ToSdkConsAddr().Bytes()) continue } pubKey, err := k.getValidatorsPublicKey(validator) if err != nil { + k.Logger(ctx).Error("could retrieve public key from validator with consensus address: %s", + addr.ToSdkConsAddr().Bytes()) continue } - if isSetToBeOptedOut[pubKey.String()] { - // do not create a `ValidatorUpdate` for validators that opt out - continue - } - - if !isStakingUpdate[pubKey.String()] { - // only send an update if an opted in validator had a staking update from the staking module and - // hence a voting power change - continue - } - + // send power 0 so the validator is removed partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ PubKey: pubKey, - Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), + Power: 0, }) } return partialSetUpdates } -func (k Keeper) ResetPartialSet(ctx sdk.Context, chainID string) { - var optedOutOnes map[string]bool - for _, v := range k.GetToBeOptedIn(ctx, chainID) { - optedOutOnes[v.String()] = true +// ResetOptedInSet resets the opted-in validators with the newest set that was computed by +// `ComputePartialSetValidatorUpdates` and hence this method should only be called after +// `ComputePartialSetValidatorUpdates` has complete. Also, clears all the `ToBeOptedIn` and `ToBeOptedOut` sets. +func (k Keeper) ResetOptedInSet(ctx sdk.Context, chainID string) { + // Create set that contains all the validators that are to be opted out so that we do not consider opted-out + // validators when going through the already opted in validators. + isSetToBeOptedOut := make(map[string]bool) + for _, val := range k.GetToBeOptedOut(ctx, chainID) { + isSetToBeOptedOut[val.ToSdkConsAddr().String()] = true } - var allOfThem []types.ProviderConsAddress - for _, v := range k.GetOptedIn(ctx, chainID) { - // FOXME: - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, v.ProviderAddr.ToSdkConsAddr()) + optedInValidators := k.GetOptedIn(ctx, chainID) + k.DeleteAllOptedIn(ctx, chainID) + + for _, val := range optedInValidators { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) if !found { - // probably an error + // any validator in `nextOptedIn` should be found + k.Logger(ctx).Error("could not find validator with consensus address: %s", + val.ProviderAddr.ToSdkConsAddr().Bytes()) continue } - if !validator.IsBonded() { + + if isSetToBeOptedOut[val.ProviderAddr.ToSdkConsAddr().String()] { + // do not create a `ValidatorUpdate` for validators that opted out continue } - // only still bonded ones ... - if optedOutOnes[v.ProviderAddr.String()] { - // here you would need ot remove + + power := uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) + if power == 0 { + // validator has unbonded continue } - allOfThem = append(allOfThem, v.ProviderAddr) + + // we only update the power of the validator, the height the validator first opted in at remains the same + k.SetOptedIn(ctx, chainID, val.ProviderAddr, val.BlockHeight, power) } - for _, v := range k.GetToBeOptedIn(ctx, chainID) { - // only still bonded ones ... - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, v.ToSdkConsAddr()) + for _, addr := range k.GetToBeOptedInValidators(ctx, chainID) { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) if !found { - // probably an error - continue - } - if !validator.IsBonded() { continue } - allOfThem = append(allOfThem, types.NewProviderConsAddress(v.Address)) - } - for _, v := range allOfThem { - if !k.IsOptedIn(ctx, chainID, v) { - k.SetOptedIn(ctx, chainID, v, uint64(ctx.BlockHeight())) - } else { - // leave the previous block height as is - } + // this validator was not in the opted-in validators before, so set its height to be the current one + k.SetOptedIn(ctx, chainID, addr, uint64(ctx.BlockHeight()), uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()))) } k.DeleteAllToBeOptedIn(ctx, chainID) diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index eab9feec04..c5ff4c2b23 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "fmt" abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -9,11 +8,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "sort" + "strings" "testing" ) @@ -23,25 +24,13 @@ func TestHandleOptIn(t *testing.T) { providerAddr := types.NewProviderConsAddress([]byte("providerAddr")) - // mock `GetValidatorByConsAddr` that returns a `Bonded` validator for the `providerAddr` address and - // an `Unbonding` validator for any other address mocks.MockStakingKeeper.EXPECT(). GetValidatorByConsAddr(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx sdk.Context, addr sdk.ConsAddress) (stakingtypes.Validator, bool) { - if addr.Equals(providerAddr.Address) { - pkAny, _ := codectypes.NewAnyWithValue(ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey()) - return stakingtypes.Validator{ConsensusPubkey: pkAny, Status: stakingtypes.Bonded}, true - } else { - pkAny, _ := codectypes.NewAnyWithValue(ed25519.GenPrivKeyFromSecret([]byte{2}).PubKey()) - return stakingtypes.Validator{ConsensusPubkey: pkAny, Status: stakingtypes.Unbonding}, true - } + pkAny, _ := codectypes.NewAnyWithValue(ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey()) + return stakingtypes.Validator{ConsensusPubkey: pkAny}, true }).AnyTimes() - // verify that a non-`Bonded` validator cannot opt in - unbondedProviderAddr := types.NewProviderConsAddress([]byte("unbondedProviderAddr")) - providerKeeper.SetProposedConsumerChain(ctx, "someChainID", 1) - require.Error(t, providerKeeper.HandleOptIn(ctx, "someChainID", unbondedProviderAddr, nil)) - // trying to opt in to a non-proposed and non-registered chain returns an error require.Error(t, providerKeeper.HandleOptIn(ctx, "unknownChainID", providerAddr, nil)) @@ -55,7 +44,7 @@ func TestHandleOptIn(t *testing.T) { 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", providerAddr, 1) + providerKeeper.SetOptedIn(ctx, "chainID", providerAddr, 1, 2) providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, nil) require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) } @@ -82,7 +71,7 @@ func TestHandleOptInWithConsumerKey(t *testing.T) { // for any other consensus address, we cannot find a validator return stakingtypes.Validator{}, false } - }).Times(3), + }).Times(2), } gomock.InOrder(calls...) @@ -133,34 +122,30 @@ func TestHandleOptOut(t *testing.T) { require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) } -func TestGetToBeOptedInValidatorUpdates(t *testing.T) { +func TestGetToBeOptedInValidators(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - type TestValidator struct { - validator stakingtypes.Validator - power int64 - expectedValUpdate abci.ValidatorUpdate - } - chainID := "chainID" - var testValidators []TestValidator + var expectedAddresses []types.ProviderConsAddress + // set 10 validators as to-be-opted-in, but only 3 (= 10/3) of those are `Bonded` for i := int64(0); i < 10; i++ { // generate a consensus public key for the provider providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() consAddr := sdk.ConsAddress(providerConsPubKey.Address()) providerAddr := types.NewProviderConsAddress(consAddr) - foo := providerAddr.Address.Bytes() var providerValidatorAddr sdk.ValAddress - providerValidatorAddr = foo + providerValidatorAddr = providerAddr.Address.Bytes() var status stakingtypes.BondStatus if i%3 == 0 { status = stakingtypes.Unbonded } else if i%3 == 1 { status = stakingtypes.Bonded + // we only expect bonded validators to be returned by `GetToBeOptedInValidators` + expectedAddresses = append(expectedAddresses, providerAddr) } else { status = stakingtypes.Unbonding } @@ -172,116 +157,162 @@ func TestGetToBeOptedInValidatorUpdates(t *testing.T) { Status: status, } - consumerTMPublicKey := tmprotocrypto.PublicKey{ - Sum: &tmprotocrypto.PublicKey_Ed25519{ - Ed25519: consAddr.Bytes(), - }, - } - - expectedValUpdate := abci.ValidatorUpdate{PubKey: consumerTMPublicKey, Power: i} - - testValidators = append(testValidators, - TestValidator{validator: validator, power: i, expectedValUpdate: expectedValUpdate}) + mocks.MockStakingKeeper.EXPECT(). + GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddr) } - for _, val := range testValidators { - consAddr, _ := val.validator.GetConsAddr() + actualAddresses := providerKeeper.GetToBeOptedInValidators(ctx, chainID) + + // sort before comparing + sort.Slice(expectedAddresses, func(i int, j int) bool { + return strings.Compare(expectedAddresses[i].String(), expectedAddresses[j].String()) < 0 + }) + sort.Slice(actualAddresses, func(i int, j int) bool { + return strings.Compare(actualAddresses[i].String(), actualAddresses[j].String()) < 0 + }) + require.Equal(t, expectedAddresses, actualAddresses) +} + +func TestGetNextOptedInValidators(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // create 10 validators + var providerAddresses []types.ProviderConsAddress + for i := 0; i < 10; i++ { + // generate a consensus public key for the provider + providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() + consAddr := sdk.ConsAddress(providerConsPubKey.Address()) + providerAddr := types.NewProviderConsAddress(consAddr) + providerAddresses = append(providerAddresses, providerAddr) + + var providerValidatorAddr sdk.ValAddress + providerValidatorAddr = providerAddr.Address.Bytes() + + pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) + validator := stakingtypes.Validator{ + OperatorAddress: providerValidatorAddr.String(), + ConsensusPubkey: pkAny, + Status: stakingtypes.Bonded, + } + mocks.MockStakingKeeper.EXPECT(). - GetValidatorByConsAddr(ctx, consAddr).Return(val.validator, true).AnyTimes() + GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() + mocks.MockStakingKeeper.EXPECT(). - GetLastValidatorPower(ctx, val.validator.GetOperator()).Return(val.power).AnyTimes() + GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() } - var expectedValUpdates []abci.ValidatorUpdate - for _, val := range testValidators { - if !val.validator.IsBonded() { - continue - } - expectedValUpdates = append(expectedValUpdates, val.expectedValUpdate) + // first 3 validators (i.e., 0, 1, and 2) are already opted in but with the same power as they currently have + // and therefore should not be returned by `GetNextOptedInValidators` + for i := 0; i < 3; i++ { + providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, uint64(i)) + + } + + // validators 3, 4, 5 and 6 are already opted in but with a different voting power + // and therefore should be returned by `GetNextOptedInValidators` (unless opted out -- see below) + for i := 3; i < 7; i++ { + providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, uint64(i+1)) + } + + // validators 8 and 9 are to be opted in + for i := 8; i < 10; i++ { + providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddresses[i]) + } + + // first 5 validators are to be opted out and hence validators 3 and 4 won't be returned + // by `GetNextOptedInValidators` + for i := 0; i < 5; i++ { + providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddresses[i]) } - actualValUpdates := providerKeeper.GetToBeOptedInValidatorUpdates(ctx, chainID) + expectedAddresses := []types.ProviderConsAddress{ + providerAddresses[5], + providerAddresses[6], + providerAddresses[8], + providerAddresses[9], + } + actualAddresses := providerKeeper.GetNextOptedInValidators(ctx, chainID) // sort before comparing - sort.Slice(expectedValUpdates, func(i int, j int) bool { - if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) < 0 { - return true - } else if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) == 0 { - return expectedValUpdates[i].Power < expectedValUpdates[j].Power - } - return false + sort.Slice(expectedAddresses, func(i int, j int) bool { + return strings.Compare(expectedAddresses[i].String(), expectedAddresses[j].String()) < 0 }) - sort.Slice(actualValUpdates, func(i int, j int) bool { - if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) < 0 { - return true - } else if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) == 0 { - return actualValUpdates[i].Power < actualValUpdates[j].Power - } - return false + sort.Slice(actualAddresses, func(i int, j int) bool { + return strings.Compare(actualAddresses[i].String(), actualAddresses[j].String()) < 0 }) - - require.Equal(t, expectedValUpdates, actualValUpdates) + require.Equal(t, expectedAddresses, actualAddresses) } -func TestGetToBeOptedOutValidatorUpdates(t *testing.T) { +func TestComputePartialSetValidatorUpdates(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - type TestValidator struct { - validator stakingtypes.Validator - power int64 - expectedValUpdate abci.ValidatorUpdate - } - - chainID := "chainID" - - var testValidators []TestValidator - for i := int64(0); i < 10; i++ { + // create 10 validators + var providerAddresses []types.ProviderConsAddress + var pubKeys []tmprotocrypto.PublicKey + for i := 0; i < 10; i++ { // generate a consensus public key for the provider providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() consAddr := sdk.ConsAddress(providerConsPubKey.Address()) providerAddr := types.NewProviderConsAddress(consAddr) + providerAddresses = append(providerAddresses, providerAddr) - foo := providerAddr.Address.Bytes() var providerValidatorAddr sdk.ValAddress - providerValidatorAddr = foo + providerValidatorAddr = providerAddr.Address.Bytes() pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) validator := stakingtypes.Validator{ OperatorAddress: providerValidatorAddr.String(), ConsensusPubkey: pkAny, + Status: stakingtypes.Bonded, } - consumerTMPublicKey := tmprotocrypto.PublicKey{ + pubKey := tmprotocrypto.PublicKey{ Sum: &tmprotocrypto.PublicKey_Ed25519{ Ed25519: consAddr.Bytes(), }, } + pubKeys = append(pubKeys, pubKey) - expectedValUpdate := abci.ValidatorUpdate{PubKey: consumerTMPublicKey, Power: 0} + mocks.MockStakingKeeper.EXPECT(). + GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() - testValidators = append(testValidators, - TestValidator{validator: validator, power: i, expectedValUpdate: expectedValUpdate}) + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() + } - providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddr) + // first 6 validators to opt in + var nextOptedIn []types.ProviderConsAddress + for i := 0; i < 6; i++ { + nextOptedIn = append(nextOptedIn, providerAddresses[i]) } - for _, val := range testValidators { - consAddr, _ := val.validator.GetConsAddr() - mocks.MockStakingKeeper.EXPECT(). - GetValidatorByConsAddr(ctx, consAddr).Return(val.validator, true).AnyTimes() + var optedOut []types.ProviderConsAddress + for i := 6; i < 10; i++ { + optedOut = append(optedOut, providerAddresses[i]) } - var expectedValUpdates []abci.ValidatorUpdate - for _, val := range testValidators { - expectedValUpdates = append(expectedValUpdates, val.expectedValUpdate) + expectedValUpdates := []abci.ValidatorUpdate{ + {PubKey: pubKeys[0], Power: 0}, + {PubKey: pubKeys[1], Power: 1}, + {PubKey: pubKeys[2], Power: 2}, + {PubKey: pubKeys[3], Power: 3}, + {PubKey: pubKeys[4], Power: 4}, + {PubKey: pubKeys[5], Power: 5}, + {PubKey: pubKeys[6], Power: 0}, + {PubKey: pubKeys[7], Power: 0}, + {PubKey: pubKeys[8], Power: 0}, + {PubKey: pubKeys[9], Power: 0}, } - actualValUpdates := providerKeeper.GetToBeOptedOutValidatorUpdates(ctx, chainID) + actualValUpdates := providerKeeper.ComputePartialSetValidatorUpdates(ctx, nextOptedIn, optedOut) - // sort before comparing sort.Slice(expectedValUpdates, func(i int, j int) bool { if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) < 0 { return true @@ -300,18 +331,24 @@ func TestGetToBeOptedOutValidatorUpdates(t *testing.T) { }) require.Equal(t, expectedValUpdates, actualValUpdates) + } -func createValidators(bondStatutes []stakingtypes.BondStatus, powers []int64) (validators []stakingtypes.Validator, - valUpdates []abci.ValidatorUpdate) { - if len(bondStatutes) != len(powers) { - return - } +func TestResetOptedInSet(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" - for i := 0; i < len(bondStatutes); i++ { + // create 10 validators + var providerAddresses []types.ProviderConsAddress + var pubKeys []tmprotocrypto.PublicKey + for i := 0; i < 10; i++ { + // generate a consensus public key for the provider providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() consAddr := sdk.ConsAddress(providerConsPubKey.Address()) providerAddr := types.NewProviderConsAddress(consAddr) + providerAddresses = append(providerAddresses, providerAddr) var providerValidatorAddr sdk.ValAddress providerValidatorAddr = providerAddr.Address.Bytes() @@ -320,75 +357,70 @@ func createValidators(bondStatutes []stakingtypes.BondStatus, powers []int64) (v validator := stakingtypes.Validator{ OperatorAddress: providerValidatorAddr.String(), ConsensusPubkey: pkAny, - Status: bondStatutes[i], + Status: stakingtypes.Bonded, } - validators = append(validators, validator) - consumerTMPublicKey := tmprotocrypto.PublicKey{ + pubKey := tmprotocrypto.PublicKey{ Sum: &tmprotocrypto.PublicKey_Ed25519{ Ed25519: consAddr.Bytes(), }, } + pubKeys = append(pubKeys, pubKey) + + mocks.MockStakingKeeper.EXPECT(). + GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() - valUpdate := abci.ValidatorUpdate{PubKey: consumerTMPublicKey, Power: powers[i]} - valUpdates = append(valUpdates, valUpdate) + if i != 1 { + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i + 1)).AnyTimes() + } else { + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(0)).AnyTimes() + } } - return -} -func TestComputePartialValidatorUpdateSet(t *testing.T) { - providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() + // first 6 validators to opt in + var toBeOptedIn []types.ProviderConsAddress - type TestValidator struct { - validator stakingtypes.Validator - power int64 - expectedValUpdate abci.ValidatorUpdate - } + providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[0], 1, uint64(1)) - chainID := "chainID" + // this validator won't be added because it has a power of 0 .. .see above mock .. not here + providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[1], 1, uint64(3)) - // create 10 validator updates - //var updates []abci.ValidatorUpdate - powers := []int64{1, 2, 3, 4, 5, 6} - vals, updates := createValidators( - []stakingtypes.BondStatus{stakingtypes.Bonded, stakingtypes.Unbonding, stakingtypes.Bonded, stakingtypes.Unbonded, - stakingtypes.Unbonded, stakingtypes.Bonded}, powers) - - addr0, _ := vals[0].GetConsAddr() - providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(addr0), uint64(1)) - addr1, _ := vals[1].GetConsAddr() - providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(addr1), uint64(2)) - addr2, _ := vals[2].GetConsAddr() - providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(addr2), uint64(3)) - addr3, _ := vals[3].GetConsAddr() - providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(addr3), uint64(4)) - - // set to be opted out - providerKeeper.SetToBeOptedOut(ctx, chainID, types.NewProviderConsAddress(addr2)) - providerKeeper.SetToBeOptedOut(ctx, chainID, types.NewProviderConsAddress(addr3)) - - // make all the validators at once ... kane to stakingUpdates based on the publickeys of those validators and then get the result - addr4, _ := vals[4].GetConsAddr() - providerKeeper.SetToBeOptedIn(ctx, chainID, types.NewProviderConsAddress(addr4)) - addr5, _ := vals[5].GetConsAddr() - providerKeeper.SetToBeOptedIn(ctx, chainID, types.NewProviderConsAddress(addr5)) - - for i, val := range vals { - consAddr, _ := val.GetConsAddr() - mocks.MockStakingKeeper.EXPECT(). - GetValidatorByConsAddr(ctx, consAddr).Return(val, true).AnyTimes() - mocks.MockStakingKeeper.EXPECT(). - GetLastValidatorPower(ctx, val.GetOperator()).Return(powers[i]).AnyTimes() + for i := 2; i < 6; i++ { + providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddresses[i]) + toBeOptedIn = append(toBeOptedIn, providerAddresses[i]) } - actualValUpdates := providerKeeper.ComputePartialSetValUpdates(ctx, chainID, []abci.ValidatorUpdate{}) + var optedOut []types.ProviderConsAddress + for i := 6; i < 10; i++ { + providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddresses[i]) + optedOut = append(optedOut, providerAddresses[i]) + } - fmt.Println(updates) - fmt.Println(actualValUpdates) - //require.Equal(t, expectedValUpdates, actualValUpdates) -} + require.NotEmpty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) + require.NotEmpty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) + + providerKeeper.ResetOptedInSet(ctx, chainID) -func TestResetPartialSet(t *testing.T) { - // s + require.Empty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) + require.Empty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) + + actualOptedInValidators := providerKeeper.GetOptedIn(ctx, chainID) + expectedOptedInValidators := []keeper.OptedInValidator{ + {ProviderAddr: providerAddresses[0], BlockHeight: 1, Power: 1}, + {ProviderAddr: providerAddresses[2], BlockHeight: uint64(ctx.BlockHeight()), Power: 3}, + {ProviderAddr: providerAddresses[3], BlockHeight: uint64(ctx.BlockHeight()), Power: 4}, + {ProviderAddr: providerAddresses[4], BlockHeight: uint64(ctx.BlockHeight()), Power: 5}, + {ProviderAddr: providerAddresses[5], BlockHeight: uint64(ctx.BlockHeight()), Power: 6}, + } + + sort.Slice(expectedOptedInValidators, func(i int, j int) bool { + return strings.Compare(expectedOptedInValidators[i].ProviderAddr.String(), expectedOptedInValidators[j].ProviderAddr.String()) < 0 + }) + + sort.Slice(actualOptedInValidators, func(i int, j int) bool { + return strings.Compare(actualOptedInValidators[i].ProviderAddr.String(), actualOptedInValidators[j].ProviderAddr.String()) < 0 + }) + require.Equal(t, expectedOptedInValidators, actualOptedInValidators) } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index c391d72ddd..9c129043bb 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -16,7 +16,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" @@ -256,7 +255,9 @@ func (k Keeper) MakeConsumerGenesis( }) // at this initial state, no validator is yet opted in ... but a validator is to be opted in - initialUpdates := k.ComputePartialSetValUpdates(ctx, chainID, []abci.ValidatorUpdate{}) + initialUpdates := k.ComputePartialSetValidatorUpdates(ctx, + k.GetNextOptedInValidators(ctx, chainID), + k.GetToBeOptedOut(ctx, chainID)) // //initialUpdates := []abci.ValidatorUpdate{} @@ -286,7 +287,7 @@ func (k Keeper) MakeConsumerGenesis( initialUpdatesWithConsumerKeys := k.MustApplyKeyAssignmentToValUpdates(ctx, chainID, initialUpdates, func(address types.ProviderConsAddress) bool { return k.IsToBeOptedIn(ctx, chainID, address) }) - k.ResetPartialSet(ctx, chainID) + k.ResetOptedInSet(ctx, chainID) // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index f090d367fe..32e6d3d312 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + abci "github.com/cometbft/cometbft/abci/types" "strconv" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -212,22 +213,32 @@ func (k Keeper) SendVSCPacketsToChain(ctx sdk.Context, chainID, channelID string // QueueVSCPackets queues latest validator updates for every registered consumer chain func (k Keeper) QueueVSCPackets(ctx sdk.Context) { valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID - // Get the validator updates from the staking module. - // Note: GetValidatorUpdates panics if the updates provided by the x/staking module - // of cosmos-sdk is invalid. - valUpdates := k.stakingKeeper.GetValidatorUpdates(ctx) for _, chain := range k.GetAllConsumerChains(ctx) { - partialSetUpdates := k.ComputePartialSetValUpdates(ctx, chain.ChainId, valUpdates) + var valUpdates []abci.ValidatorUpdate + if k.IsTopN(ctx, chain.ChainId) { + valUpdates = k.ComputePartialSetValidatorUpdates(ctx, + k.GetNextOptedInValidators(ctx, chain.ChainId), + k.GetToBeOptedOut(ctx, chain.ChainId)) + } else { + valUpdates = k.stakingKeeper.GetValidatorUpdates(ctx) + } - // FIXME: staking updtes contain ... ARE only for active set ... - // Apply the key assignment to the validator updates. - valUpdates := k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, partialSetUpdates, - func(address providertypes.ProviderConsAddress) bool { + var considerKeyReplacement func(address providertypes.ProviderConsAddress) bool + if k.IsTopN(ctx, chain.ChainId) { + considerKeyReplacement = func(address providertypes.ProviderConsAddress) bool { return k.IsOptedIn(ctx, chain.ChainId, address) - }) + } + } else { + considerKeyReplacement = func(address providertypes.ProviderConsAddress) bool { + return true + } + } + + // Apply the key assignment to the validator updates. + valUpdates = k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, valUpdates, considerKeyReplacement) - k.ResetPartialSet(ctx, chain.ChainId) + k.ResetOptedInSet(ctx, chain.ChainId) // check whether there are changes in the validator set; // note that this also entails unbonding operations diff --git a/x/ccv/provider/types/errors.go b/x/ccv/provider/types/errors.go index b1d55a0ea1..6c19a7b396 100644 --- a/x/ccv/provider/types/errors.go +++ b/x/ccv/provider/types/errors.go @@ -24,5 +24,4 @@ var ( ErrInvalidConsumerClient = errorsmod.Register(ModuleName, 16, "ccv channel is not built on correct client") ErrDuplicateConsumerChain = errorsmod.Register(ModuleName, 17, "consumer chain already exists") ErrConsumerChainNotFound = errorsmod.Register(ModuleName, 18, "consumer chain not found") - ErrValidatorNotBonded = errorsmod.Register(ModuleName, 19, "validator not bonded") ) From 4bc488747654793bb8495ed31a9a7bfe9e73d055 Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 13 Feb 2024 14:31:03 +0100 Subject: [PATCH 03/15] small fixes --- x/ccv/provider/keeper/proposal.go | 2 +- x/ccv/provider/keeper/relay.go | 2 +- x/ccv/provider/keeper/relay_test.go | 4 ++-- x/ccv/provider/keeper/throttle.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 9c129043bb..12ae32dcfe 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -372,7 +372,7 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { // then someone could create a bogus ConsumerAdditionProposal to override the Top N for a specific chain. k.SetTopN(ctx, prop.ChainId, prop.Top_N) - if !k.IsTopN(ctx, prop.ChainId) && len(k.GetToBeOptedIn(ctx, prop.ChainId)) == 0 { + if k.IsOptIn(ctx, prop.ChainId) && len(k.GetToBeOptedIn(ctx, prop.ChainId)) == 0 { // drop the proposal ctx.Logger().Info("could not start Opt In consumer chain (%s) because no validator has opted in", prop.ChainId) diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 32e6d3d312..7872c02db8 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -517,7 +517,7 @@ func (k Keeper) EndBlockCCR(ctx sdk.Context) { } } -// getMappedInfractionHeight gets the infraction height mapped from validator set ID for the given chain ID +// getMappedInfractionHeight gets the infraction height mapped from val set ID for the given chain ID func (k Keeper) getMappedInfractionHeight(ctx sdk.Context, chainID string, valsetUpdateID uint64, ) (height uint64, found bool) { diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index f5955e63f0..02df262d53 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -385,7 +385,7 @@ func TestHandleSlashPacket(t *testing.T) { return testkeeper.GetMocksForHandleSlashPacket( ctx, mocks, providerConsAddr, // expected provider cons addr returned from GetProviderAddrFromConsumerAddr - stakingtypes.Validator{Jailed: false}, // staking keeper validator to return + stakingtypes.Validator{Jailed: false}, // staking keeper val to return true) // expectJailing = true }, 1, @@ -402,7 +402,7 @@ func TestHandleSlashPacket(t *testing.T) { return testkeeper.GetMocksForHandleSlashPacket( ctx, mocks, providerConsAddr, // expected provider cons addr returned from GetProviderAddrFromConsumerAddr - stakingtypes.Validator{Jailed: true}, // staking keeper validator to return + stakingtypes.Validator{Jailed: true}, // staking keeper val to return false) // expectJailing = false, validator is already jailed. }, 1, diff --git a/x/ccv/provider/keeper/throttle.go b/x/ccv/provider/keeper/throttle.go index dc473ac506..b7e7fd5941 100644 --- a/x/ccv/provider/keeper/throttle.go +++ b/x/ccv/provider/keeper/throttle.go @@ -17,7 +17,7 @@ import ( func (k Keeper) GetEffectiveValPower(ctx sdktypes.Context, valConsAddr providertypes.ProviderConsAddress, ) math.Int { - // Obtain staking module validator object from the provider's consensus address. + // Obtain staking module val object from the provider's consensus address. // Note: if validator is not found or unbonded, this will be handled appropriately in HandleSlashPacket val, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, valConsAddr.ToSdkConsAddr()) From f5c3af1310974a7601b7736d9c1377b17c20f595 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 14 Feb 2024 16:17:23 +0100 Subject: [PATCH 04/15] cleaning up --- x/ccv/provider/keeper/partial_set_security.go | 378 +++++++---- .../keeper/partial_set_security_test.go | 595 ++++++++++++------ x/ccv/provider/keeper/proposal.go | 75 +-- x/ccv/provider/keeper/relay.go | 35 +- 4 files changed, 729 insertions(+), 354 deletions(-) diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 334afe8128..6337777a31 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -7,6 +7,7 @@ import ( 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" + "sort" ) type OptedInValidator struct { @@ -87,171 +88,320 @@ func (k Keeper) getValidatorsPublicKey(validator stakingtypes.Validator) (tmprot }, nil } -// GetToBeOptedInValidators returns all the needed validators that are to be opted in -// on consumer chain with `chainID` and that are still bonded -func (k Keeper) GetToBeOptedInValidators(ctx sdk.Context, chainID string) []types.ProviderConsAddress { - var consAddresses []types.ProviderConsAddress - for _, addr := range k.GetToBeOptedIn(ctx, chainID) { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) - if !found { - // a validator was successfully set to be opted in, but we cannot find this validator anymore - k.Logger(ctx).Error("could not find to-be-opted-in invalidator with consensus address: %s", addr.ToSdkConsAddr().Bytes()) - continue - } +// getValidatorOperatorAddressAndPublicKey is a helper function that returns the public key of the validator +func (k Keeper) getValidatorOperatorAddressAndPublicKey(ctx sdk.Context, addr types.ProviderConsAddress) (sdk.ValAddress, tmprotocrypto.PublicKey, error) { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) + if !found { - if !validator.IsBonded() { - // only consider validators that are in the active set - continue - } - consAddresses = append(consAddresses, addr) + } + consAddr, err := validator.GetConsAddr() + if err != nil { + return sdk.ValAddress{}, tmprotocrypto.PublicKey{}, err } - return consAddresses + pubKey := tmprotocrypto.PublicKey{ + Sum: &tmprotocrypto.PublicKey_Ed25519{ + Ed25519: consAddr.Bytes(), + }, + } + return validator.GetOperator(), pubKey, nil } -// GetNextOptedInValidators returns all the addresses of validators that are going to be opted in -// next on the consumer chain with `chainID` and a validator update would need to be sent for them -func (k Keeper) GetNextOptedInValidators(ctx sdk.Context, chainID string) []types.ProviderConsAddress { - var consAddresses []types.ProviderConsAddress - - // Create set that contains all the validators that are to be opted out so that we do not reintroduce opted-out - // validators when going through the already opted in validators. - isSetToBeOptedOut := make(map[string]bool) - for _, addr := range k.GetToBeOptedOut(ctx, chainID) { - isSetToBeOptedOut[addr.ToSdkConsAddr().String()] = true +//func filterAddresses(addresses []types.ProviderConsAddress, predicate func(types.ProviderConsAddress) bool) []types.ProviderConsAddress { +// var filteredAddresses []types.ProviderConsAddress +// for _, addr := range addresses { +// if predicate(addr) { +// filteredAddresses = append(filteredAddresses, addr) +// } +// } +// return filteredAddresses +//} +// +//func filterOptedInValidators(addresses []OptedInValidator, predicate func(validator OptedInValidator) bool) []OptedInValidator { +// var filteredAddresses []OptedInValidator +// for _, addr := range addresses { +// if predicate(addr) { +// filteredAddresses = append(filteredAddresses, addr) +// } +// } +// return filteredAddresses +//} + +//// GetBondedToBeOptedInValidators returns all the bonded validators that are to be opted in +//// on consumer chain with `chainID` +//func (k Keeper) GetBondedToBeOptedInValidators(ctx sdk.Context, chainID string) []types.ProviderConsAddress { +// var consAddresses []types.ProviderConsAddress +// +// bondedToBeOptedInValidators := +// filterAddresses(k.GetToBeOptedIn(ctx, chainID), func(addr types.ProviderConsAddress) bool { +// validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) +// if !found { +// // a validator was successfully set to be opted in, but we cannot find this validator anymore +// k.Logger(ctx).Error("could not find to-be-opted-in invalidator with consensus address: %s", addr.ToSdkConsAddr().Bytes()) +// return false +// } +// +// if !validator.IsBonded() { +// // only consider validators that are in the active set +// return false +// } +// return true +// }) +// +// return bondedToBeOptedInValidators +// +// //for _, addr := range k.GetToBeOptedIn(ctx, chainID) { +// // validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) +// // if !found { +// // // a validator was successfully set to be opted in, but we cannot find this validator anymore +// // k.Logger(ctx).Error("could not find to-be-opted-in invalidator with consensus address: %s", addr.ToSdkConsAddr().Bytes()) +// // continue +// // } +// // +// // if !validator.IsBonded() { +// // // only consider validators that are in the active set +// // continue +// // } +// // consAddresses = append(consAddresses, addr) +// //} +// +// return consAddresses +//} + +//// GetOptedInValidatorsForValUpdates returns all the addresses of validators that are going to be opted in +//// next on the consumer chain with `chainID` and a validator update would need to be sent for them +//func (k Keeper) GetOptedInValidatorsForValUpdates(ctx sdk.Context, chainID string) []types.ProviderConsAddress { +// var consAddresses []types.ProviderConsAddress +// +// // Create set that contains all the validators that are to be opted out so that we do not reintroduce opted-out +// // validators when going through the already opted in validators. +// isSetToBeOptedOut := make(map[string]bool) +// for _, addr := range k.GetToBeOptedOut(ctx, chainID) { +// isSetToBeOptedOut[addr.ToSdkConsAddr().String()] = true +// } +// +// for _, val := range k.GetOptedIn(ctx, chainID) { +// // go through all the validators that are currently opted in +// validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) +// if !found { +// // a validator was opted in, but we cannot find this validator anymore +// k.Logger(ctx).Error("could not find opted-in validator with consensus address: %s", +// val.ProviderAddr.ToSdkConsAddr().Bytes()) +// continue +// } +// +// if isSetToBeOptedOut[val.ProviderAddr.ToSdkConsAddr().String()] { +// continue +// } +// +// if val.Power == uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) { +// // Only send an update if an opted-in validator had a power change since the lat time it was sent. +// // If an opted-in validator is not in the active set anymore, then its new power is 0 and hence we +// // do not consider this validator in the next set of opted in validators. +// continue +// } +// +// consAddresses = append(consAddresses, val.ProviderAddr) +// } +// +// // append all the to-be-opted-in validators +// consAddresses = append(consAddresses, k.GetBondedToBeOptedInValidators(ctx, chainID)...) +// return consAddresses +//} + +//// ComputePartialSetValidatorUpdates returns the partial set of `ValidatorUpdate`s that the provider chain sends to the +//// consumer chain. Receives `nextOptedIn` that corresponds to the validators that would be opted in next and +//// `toBeOptedOut` that contains the validators that would be opted out. +//func (k Keeper) ComputePartialSetValidatorUpdates(ctx sdk.Context, optedInValidatorsForValUpdates []types.ProviderConsAddress, +// toBeOptedOut []types.ProviderConsAddress) []abci.ValidatorUpdate { +// var partialSetUpdates []abci.ValidatorUpdate +// +// for _, val := range optedInValidatorsForValUpdates { +// validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ToSdkConsAddr()) +// if !found { +// // any validator in `optedInValidatorsForValUpdates` should be found +// k.Logger(ctx).Error("could not find validator with consensus address: %s", +// val.ToSdkConsAddr().Bytes()) +// continue +// } +// +// pubKey, err := k.getValidatorsPublicKey(validator) +// if err != nil { +// k.Logger(ctx).Error("could retrieve public key from validator with consensus address: %s", +// val.ToSdkConsAddr().Bytes()) +// continue +// } +// +// // if a validator that was opted in, is not in the active set anymore, then `GetLastValidatorPower` returns 0 +// partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ +// PubKey: pubKey, +// Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), +// }) +// } +// +// for _, addr := range toBeOptedOut { +// // go through all the validators that are currently opted in +// validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) +// if !found { +// // a validator was opted in, but we cannot find this validator anymore +// k.Logger(ctx).Error("could not find opted-in validator with consensus address: %s", +// addr.ToSdkConsAddr().Bytes()) +// continue +// } +// pubKey, err := k.getValidatorsPublicKey(validator) +// if err != nil { +// k.Logger(ctx).Error("could retrieve public key from validator with consensus address: %s", +// addr.ToSdkConsAddr().Bytes()) +// continue +// } +// +// // send power 0 so the validator is removed +// partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ +// PubKey: pubKey, +// Power: 0, +// }) +// } +// +// return partialSetUpdates +//} + +func (k Keeper) ComputeNextValidators(ctx sdk.Context, currentValidators []OptedInValidator, + validatorsToAdd []types.ProviderConsAddress, + validatorsToRemove []types.ProviderConsAddress, +) []OptedInValidator { + isRemoved := make(map[string]bool) + for _, val := range validatorsToRemove { + isRemoved[val.ToSdkConsAddr().String()] = true } - for _, val := range k.GetOptedIn(ctx, chainID) { - // go through all the validators that are currently opted in - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) - if !found { - // a validator was opted in, but we cannot find this validator anymore - k.Logger(ctx).Error("could not find opted-in validator with consensus address: %s", - val.ProviderAddr.ToSdkConsAddr().Bytes()) + var out []OptedInValidator + for _, val := range currentValidators { + if isRemoved[val.ProviderAddr.ToSdkConsAddr().String()] { + continue + } + valAddress, _, err := k.getValidatorOperatorAddressAndPublicKey(ctx, val.ProviderAddr) + if err != nil { + // FIXME continue } - if isSetToBeOptedOut[val.ProviderAddr.ToSdkConsAddr().String()] { + val.Power = uint64(k.stakingKeeper.GetLastValidatorPower(ctx, valAddress)) + if val.Power == 0 { continue } + out = append(out, val) + } - if val.Power == uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) { - // Only send an update if an opted-in validator had a power change since the lat time it was sent. - // If an opted-in validator is not in the active set anymore, then its new power is 0 and hence we - // do not consider this validator in the next set of opted in validators. + for _, addr := range validatorsToAdd { + valAddress, _, err := k.getValidatorOperatorAddressAndPublicKey(ctx, addr) + if err != nil { + // FIXME + continue + } + + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) + if !found { + continue + } + if !validator.IsBonded() { continue } + power := uint64(k.stakingKeeper.GetLastValidatorPower(ctx, valAddress)) - consAddresses = append(consAddresses, val.ProviderAddr) + out = append(out, OptedInValidator{ProviderAddr: addr, BlockHeight: uint64(ctx.BlockHeight()), Power: power}) } - // append all the to-be-opted-in validators - consAddresses = append(consAddresses, k.GetToBeOptedInValidators(ctx, chainID)...) - return consAddresses + return out } -// ComputePartialSetValidatorUpdates returns the partial set of `ValidatorUpdate`s that the provider chain sends to the -// consumer chain. Receives `nextOptedIn` that corresponds to the validators that would be opted in next and -// `toBeOptedOut` that contains the validators that would be opted out. -func (k Keeper) ComputePartialSetValidatorUpdates(ctx sdk.Context, nextOptedIn []types.ProviderConsAddress, - toBeOptedOut []types.ProviderConsAddress) []abci.ValidatorUpdate { - var partialSetUpdates []abci.ValidatorUpdate +func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, + currentValidators []OptedInValidator, + validatorsToAdd []types.ProviderConsAddress, + validatorsToRemove []types.ProviderConsAddress, +) []abci.ValidatorUpdate { + var m = make(map[string]abci.ValidatorUpdate) - for _, val := range nextOptedIn { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ToSdkConsAddr()) + for _, val := range currentValidators { + valAddress, pubKey, err := k.getValidatorOperatorAddressAndPublicKey(ctx, val.ProviderAddr) + if err != nil { + // FIXME ... + } + + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) if !found { - // any validator in `nextOptedIn` should be found - k.Logger(ctx).Error("could not find validator with consensus address: %s", - val.ToSdkConsAddr().Bytes()) continue } - pubKey, err := k.getValidatorsPublicKey(validator) - if err != nil { - k.Logger(ctx).Error("could retrieve public key from validator with consensus address: %s", - val.ToSdkConsAddr().Bytes()) + if val.Power == uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) { continue } - // if a validator that was opted in, is not in the active set anymore, then `GetLastValidatorPower` returns 0 - partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ + // if this validator has unbonded, the result would be 0 of power so we still good!! but then if you add, you readd + // no, because we check if bonded when we readd ... you cannot readd if already here + m[pubKey.String()] = abci.ValidatorUpdate{ PubKey: pubKey, - Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), - }) + Power: k.stakingKeeper.GetLastValidatorPower(ctx, valAddress), + } } - for _, addr := range toBeOptedOut { - // go through all the validators that are currently opted in + for _, addr := range validatorsToAdd { + valAddress, pubKey, err := k.getValidatorOperatorAddressAndPublicKey(ctx, addr) + if err != nil { + + } + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) if !found { - // a validator was opted in, but we cannot find this validator anymore - k.Logger(ctx).Error("could not find opted-in validator with consensus address: %s", - addr.ToSdkConsAddr().Bytes()) continue } - pubKey, err := k.getValidatorsPublicKey(validator) - if err != nil { - k.Logger(ctx).Error("could retrieve public key from validator with consensus address: %s", - addr.ToSdkConsAddr().Bytes()) + if !validator.IsBonded() { continue } - // send power 0 so the validator is removed - partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ + m[pubKey.String()] = abci.ValidatorUpdate{ PubKey: pubKey, - Power: 0, - }) - } - - return partialSetUpdates -} - -// ResetOptedInSet resets the opted-in validators with the newest set that was computed by -// `ComputePartialSetValidatorUpdates` and hence this method should only be called after -// `ComputePartialSetValidatorUpdates` has complete. Also, clears all the `ToBeOptedIn` and `ToBeOptedOut` sets. -func (k Keeper) ResetOptedInSet(ctx sdk.Context, chainID string) { - // Create set that contains all the validators that are to be opted out so that we do not consider opted-out - // validators when going through the already opted in validators. - isSetToBeOptedOut := make(map[string]bool) - for _, val := range k.GetToBeOptedOut(ctx, chainID) { - isSetToBeOptedOut[val.ToSdkConsAddr().String()] = true + Power: k.stakingKeeper.GetLastValidatorPower(ctx, valAddress), + } } - optedInValidators := k.GetOptedIn(ctx, chainID) - k.DeleteAllOptedIn(ctx, chainID) + for _, addr := range validatorsToRemove { + _, pubKey, err := k.getValidatorOperatorAddressAndPublicKey(ctx, addr) + if err != nil { - for _, val := range optedInValidators { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) - if !found { - // any validator in `nextOptedIn` should be found - k.Logger(ctx).Error("could not find validator with consensus address: %s", - val.ProviderAddr.ToSdkConsAddr().Bytes()) - continue } - if isSetToBeOptedOut[val.ProviderAddr.ToSdkConsAddr().String()] { - // do not create a `ValidatorUpdate` for validators that opted out - continue + m[pubKey.String()] = abci.ValidatorUpdate{ + PubKey: pubKey, + Power: 0, } - power := uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) - if power == 0 { - // validator has unbonded - continue - } + } - // we only update the power of the validator, the height the validator first opted in at remains the same - k.SetOptedIn(ctx, chainID, val.ProviderAddr, val.BlockHeight, power) + var out []abci.ValidatorUpdate + for _, update := range m { + out = append(out, update) } - for _, addr := range k.GetToBeOptedInValidators(ctx, chainID) { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) - if !found { - continue + // similar to `AccumulateChanges` + // The list of tendermint updates should hash the same across all consensus nodes + // that means it is necessary to sort for determinism. + sort.Slice(out, func(i, j int) bool { + if out[i].Power != out[j].Power { + return out[i].Power > out[j].Power } + return out[i].PubKey.String() > out[j].PubKey.String() + }) - // this validator was not in the opted-in validators before, so set its height to be the current one - k.SetOptedIn(ctx, chainID, addr, uint64(ctx.BlockHeight()), uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()))) + return out +} + +// ResetCurrentValidators resets the opted-in validators with the newest set that was computed by +// `ComputePartialSetValidatorUpdates` and hence this method should only be called after +// `ComputePartialSetValidatorUpdates` has complete. Also, clears all the `ToBeOptedIn` and `ToBeOptedOut` sets. +func (k Keeper) ResetCurrentValidators(ctx sdk.Context, chainID string, nextValidators []OptedInValidator) { + k.DeleteAllOptedIn(ctx, chainID) + for _, val := range nextValidators { + k.SetOptedIn(ctx, chainID, val.ProviderAddr, val.BlockHeight, val.Power) } k.DeleteAllToBeOptedIn(ctx, chainID) diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index c5ff4c2b23..3f798a9cfb 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -122,67 +122,316 @@ func TestHandleOptOut(t *testing.T) { require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) } -func TestGetToBeOptedInValidators(t *testing.T) { +//func TestGetToBeOptedInValidators(t *testing.T) { +// providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) +// defer ctrl.Finish() +// +// chainID := "chainID" +// +// var expectedAddresses []types.ProviderConsAddress +// // set 10 validators as to-be-opted-in, but only 3 (= 10/3) of those are `Bonded` +// for i := int64(0); i < 10; i++ { +// // generate a consensus public key for the provider +// providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() +// consAddr := sdk.ConsAddress(providerConsPubKey.Address()) +// providerAddr := types.NewProviderConsAddress(consAddr) +// +// var providerValidatorAddr sdk.ValAddress +// providerValidatorAddr = providerAddr.Address.Bytes() +// +// var status stakingtypes.BondStatus +// if i%3 == 0 { +// status = stakingtypes.Unbonded +// } else if i%3 == 1 { +// status = stakingtypes.Bonded +// // we only expect bonded validators to be returned by `GetBondedToBeOptedInValidators` +// expectedAddresses = append(expectedAddresses, providerAddr) +// } else { +// status = stakingtypes.Unbonding +// } +// +// pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) +// validator := stakingtypes.Validator{ +// OperatorAddress: providerValidatorAddr.String(), +// ConsensusPubkey: pkAny, +// Status: status, +// } +// +// mocks.MockStakingKeeper.EXPECT(). +// GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() +// +// providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddr) +// } +// +// actualAddresses := providerKeeper.GetBondedToBeOptedInValidators(ctx, chainID) +// +// // sort before comparing +// sort.Slice(expectedAddresses, func(i int, j int) bool { +// return strings.Compare(expectedAddresses[i].String(), expectedAddresses[j].String()) < 0 +// }) +// sort.Slice(actualAddresses, func(i int, j int) bool { +// return strings.Compare(actualAddresses[i].String(), actualAddresses[j].String()) < 0 +// }) +// require.Equal(t, expectedAddresses, actualAddresses) +//} +// +//func TestGetNextOptedInValidators(t *testing.T) { +// providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) +// defer ctrl.Finish() +// +// chainID := "chainID" +// +// // create 10 validators +// var providerAddresses []types.ProviderConsAddress +// for i := 0; i < 10; i++ { +// // generate a consensus public key for the provider +// providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() +// consAddr := sdk.ConsAddress(providerConsPubKey.Address()) +// providerAddr := types.NewProviderConsAddress(consAddr) +// providerAddresses = append(providerAddresses, providerAddr) +// +// var providerValidatorAddr sdk.ValAddress +// providerValidatorAddr = providerAddr.Address.Bytes() +// +// pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) +// validator := stakingtypes.Validator{ +// OperatorAddress: providerValidatorAddr.String(), +// ConsensusPubkey: pkAny, +// Status: stakingtypes.Bonded, +// } +// +// mocks.MockStakingKeeper.EXPECT(). +// GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() +// +// mocks.MockStakingKeeper.EXPECT(). +// GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() +// } +// +// // first 3 validators (i.e., 0, 1, and 2) are already opted in but with the same power as they currently have +// // and therefore should not be returned by `GetOptedInValidatorsForValUpdates` +// for i := 0; i < 3; i++ { +// providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, uint64(i)) +// +// } +// +// // validators 3, 4, 5 and 6 are already opted in but with a different voting power +// // and therefore should be returned by `GetOptedInValidatorsForValUpdates` (unless opted out -- see below) +// for i := 3; i < 7; i++ { +// providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, uint64(i+1)) +// } +// +// // validators 8 and 9 are to be opted in +// for i := 8; i < 10; i++ { +// providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddresses[i]) +// } +// +// // first 5 validators are to be opted out and hence validators 3 and 4 won't be returned +// // by `GetOptedInValidatorsForValUpdates` +// for i := 0; i < 5; i++ { +// providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddresses[i]) +// } +// +// expectedAddresses := []types.ProviderConsAddress{ +// providerAddresses[5], +// providerAddresses[6], +// providerAddresses[8], +// providerAddresses[9], +// } +// actualAddresses := providerKeeper.GetOptedInValidatorsForValUpdates(ctx, chainID) +// +// // sort before comparing +// sort.Slice(expectedAddresses, func(i int, j int) bool { +// return strings.Compare(expectedAddresses[i].String(), expectedAddresses[j].String()) < 0 +// }) +// sort.Slice(actualAddresses, func(i int, j int) bool { +// return strings.Compare(actualAddresses[i].String(), actualAddresses[j].String()) < 0 +// }) +// require.Equal(t, expectedAddresses, actualAddresses) +//} +// +//func TestComputePartialSetValidatorUpdates(t *testing.T) { +// providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) +// defer ctrl.Finish() +// +// // create 10 validators +// var providerAddresses []types.ProviderConsAddress +// var pubKeys []tmprotocrypto.PublicKey +// for i := 0; i < 10; i++ { +// // generate a consensus public key for the provider +// providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() +// consAddr := sdk.ConsAddress(providerConsPubKey.Address()) +// providerAddr := types.NewProviderConsAddress(consAddr) +// providerAddresses = append(providerAddresses, providerAddr) +// +// var providerValidatorAddr sdk.ValAddress +// providerValidatorAddr = providerAddr.Address.Bytes() +// +// pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) +// validator := stakingtypes.Validator{ +// OperatorAddress: providerValidatorAddr.String(), +// ConsensusPubkey: pkAny, +// Status: stakingtypes.Bonded, +// } +// +// pubKey := tmprotocrypto.PublicKey{ +// Sum: &tmprotocrypto.PublicKey_Ed25519{ +// Ed25519: consAddr.Bytes(), +// }, +// } +// pubKeys = append(pubKeys, pubKey) +// +// mocks.MockStakingKeeper.EXPECT(). +// GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() +// +// mocks.MockStakingKeeper.EXPECT(). +// GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() +// } +// +// // first 6 validators to opt in +// var nextOptedIn []types.ProviderConsAddress +// for i := 0; i < 6; i++ { +// nextOptedIn = append(nextOptedIn, providerAddresses[i]) +// } +// +// var optedOut []types.ProviderConsAddress +// for i := 6; i < 10; i++ { +// optedOut = append(optedOut, providerAddresses[i]) +// } +// +// expectedValUpdates := []abci.ValidatorUpdate{ +// {PubKey: pubKeys[0], Power: 0}, +// {PubKey: pubKeys[1], Power: 1}, +// {PubKey: pubKeys[2], Power: 2}, +// {PubKey: pubKeys[3], Power: 3}, +// {PubKey: pubKeys[4], Power: 4}, +// {PubKey: pubKeys[5], Power: 5}, +// {PubKey: pubKeys[6], Power: 0}, +// {PubKey: pubKeys[7], Power: 0}, +// {PubKey: pubKeys[8], Power: 0}, +// {PubKey: pubKeys[9], Power: 0}, +// } +// +// actualValUpdates := providerKeeper.ComputePartialSetValidatorUpdates(ctx, nextOptedIn, optedOut) +// +// sort.Slice(expectedValUpdates, func(i int, j int) bool { +// if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) < 0 { +// return true +// } else if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) == 0 { +// return expectedValUpdates[i].Power < expectedValUpdates[j].Power +// } +// return false +// }) +// sort.Slice(actualValUpdates, func(i int, j int) bool { +// if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) < 0 { +// return true +// } else if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) == 0 { +// return actualValUpdates[i].Power < actualValUpdates[j].Power +// } +// return false +// }) +// +// require.Equal(t, expectedValUpdates, actualValUpdates) +// +//} +// +//func TestResetOptedInSet(t *testing.T) { +// providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) +// defer ctrl.Finish() +// +// chainID := "chainID" +// +// // create 10 validators +// var providerAddresses []types.ProviderConsAddress +// var pubKeys []tmprotocrypto.PublicKey +// for i := 0; i < 10; i++ { +// // generate a consensus public key for the provider +// providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() +// consAddr := sdk.ConsAddress(providerConsPubKey.Address()) +// providerAddr := types.NewProviderConsAddress(consAddr) +// providerAddresses = append(providerAddresses, providerAddr) +// +// var providerValidatorAddr sdk.ValAddress +// providerValidatorAddr = providerAddr.Address.Bytes() +// +// pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) +// validator := stakingtypes.Validator{ +// OperatorAddress: providerValidatorAddr.String(), +// ConsensusPubkey: pkAny, +// Status: stakingtypes.Bonded, +// } +// +// pubKey := tmprotocrypto.PublicKey{ +// Sum: &tmprotocrypto.PublicKey_Ed25519{ +// Ed25519: consAddr.Bytes(), +// }, +// } +// pubKeys = append(pubKeys, pubKey) +// +// mocks.MockStakingKeeper.EXPECT(). +// GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() +// +// if i != 1 { +// mocks.MockStakingKeeper.EXPECT(). +// GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i + 1)).AnyTimes() +// } else { +// mocks.MockStakingKeeper.EXPECT(). +// GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(0)).AnyTimes() +// } +// } +// +// // first 6 validators to opt in +// var toBeOptedIn []types.ProviderConsAddress +// +// providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[0], 1, uint64(1)) +// +// // this validator won't be added because it has a power of 0 .. .see above mock .. not here +// providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[1], 1, uint64(3)) +// +// for i := 2; i < 6; i++ { +// providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddresses[i]) +// toBeOptedIn = append(toBeOptedIn, providerAddresses[i]) +// } +// +// var optedOut []types.ProviderConsAddress +// for i := 6; i < 10; i++ { +// providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddresses[i]) +// optedOut = append(optedOut, providerAddresses[i]) +// } +// +// require.NotEmpty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) +// require.NotEmpty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) +// +// providerKeeper.ResetOptedInSet(ctx, chainID) +// +// require.Empty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) +// require.Empty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) +// +// actualOptedInValidators := providerKeeper.GetOptedIn(ctx, chainID) +// expectedOptedInValidators := []keeper.OptedInValidator{ +// {ProviderAddr: providerAddresses[0], BlockHeight: 1, Power: 1}, +// {ProviderAddr: providerAddresses[2], BlockHeight: uint64(ctx.BlockHeight()), Power: 3}, +// {ProviderAddr: providerAddresses[3], BlockHeight: uint64(ctx.BlockHeight()), Power: 4}, +// {ProviderAddr: providerAddresses[4], BlockHeight: uint64(ctx.BlockHeight()), Power: 5}, +// {ProviderAddr: providerAddresses[5], BlockHeight: uint64(ctx.BlockHeight()), Power: 6}, +// } +// +// sort.Slice(expectedOptedInValidators, func(i int, j int) bool { +// return strings.Compare(expectedOptedInValidators[i].ProviderAddr.String(), expectedOptedInValidators[j].ProviderAddr.String()) < 0 +// }) +// +// sort.Slice(actualOptedInValidators, func(i int, j int) bool { +// return strings.Compare(actualOptedInValidators[i].ProviderAddr.String(), actualOptedInValidators[j].ProviderAddr.String()) < 0 +// }) +// require.Equal(t, expectedOptedInValidators, actualOptedInValidators) +//} + +func TestComputeValidatorUpdates(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - chainID := "chainID" - - var expectedAddresses []types.ProviderConsAddress - // set 10 validators as to-be-opted-in, but only 3 (= 10/3) of those are `Bonded` - for i := int64(0); i < 10; i++ { - // generate a consensus public key for the provider - providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() - consAddr := sdk.ConsAddress(providerConsPubKey.Address()) - providerAddr := types.NewProviderConsAddress(consAddr) - - var providerValidatorAddr sdk.ValAddress - providerValidatorAddr = providerAddr.Address.Bytes() - - var status stakingtypes.BondStatus - if i%3 == 0 { - status = stakingtypes.Unbonded - } else if i%3 == 1 { - status = stakingtypes.Bonded - // we only expect bonded validators to be returned by `GetToBeOptedInValidators` - expectedAddresses = append(expectedAddresses, providerAddr) - } else { - status = stakingtypes.Unbonding - } - - pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) - validator := stakingtypes.Validator{ - OperatorAddress: providerValidatorAddr.String(), - ConsensusPubkey: pkAny, - Status: status, - } - - mocks.MockStakingKeeper.EXPECT(). - GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() - - providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddr) - } - - actualAddresses := providerKeeper.GetToBeOptedInValidators(ctx, chainID) - - // sort before comparing - sort.Slice(expectedAddresses, func(i int, j int) bool { - return strings.Compare(expectedAddresses[i].String(), expectedAddresses[j].String()) < 0 - }) - sort.Slice(actualAddresses, func(i int, j int) bool { - return strings.Compare(actualAddresses[i].String(), actualAddresses[j].String()) < 0 - }) - require.Equal(t, expectedAddresses, actualAddresses) -} - -func TestGetNextOptedInValidators(t *testing.T) { - providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - chainID := "chainID" - - // create 10 validators + // create 10 validators, where the first 8 are bonded and the last 2 are unbonded var providerAddresses []types.ProviderConsAddress + var pubKeys []tmprotocrypto.PublicKey for i := 0; i < 10; i++ { // generate a consensus public key for the provider providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() @@ -194,83 +443,17 @@ func TestGetNextOptedInValidators(t *testing.T) { providerValidatorAddr = providerAddr.Address.Bytes() pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) - validator := stakingtypes.Validator{ - OperatorAddress: providerValidatorAddr.String(), - ConsensusPubkey: pkAny, - Status: stakingtypes.Bonded, - } - - mocks.MockStakingKeeper.EXPECT(). - GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() - - mocks.MockStakingKeeper.EXPECT(). - GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() - } - - // first 3 validators (i.e., 0, 1, and 2) are already opted in but with the same power as they currently have - // and therefore should not be returned by `GetNextOptedInValidators` - for i := 0; i < 3; i++ { - providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, uint64(i)) - - } - - // validators 3, 4, 5 and 6 are already opted in but with a different voting power - // and therefore should be returned by `GetNextOptedInValidators` (unless opted out -- see below) - for i := 3; i < 7; i++ { - providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, uint64(i+1)) - } - - // validators 8 and 9 are to be opted in - for i := 8; i < 10; i++ { - providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddresses[i]) - } - - // first 5 validators are to be opted out and hence validators 3 and 4 won't be returned - // by `GetNextOptedInValidators` - for i := 0; i < 5; i++ { - providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddresses[i]) - } - - expectedAddresses := []types.ProviderConsAddress{ - providerAddresses[5], - providerAddresses[6], - providerAddresses[8], - providerAddresses[9], - } - actualAddresses := providerKeeper.GetNextOptedInValidators(ctx, chainID) - - // sort before comparing - sort.Slice(expectedAddresses, func(i int, j int) bool { - return strings.Compare(expectedAddresses[i].String(), expectedAddresses[j].String()) < 0 - }) - sort.Slice(actualAddresses, func(i int, j int) bool { - return strings.Compare(actualAddresses[i].String(), actualAddresses[j].String()) < 0 - }) - require.Equal(t, expectedAddresses, actualAddresses) -} - -func TestComputePartialSetValidatorUpdates(t *testing.T) { - providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - // create 10 validators - var providerAddresses []types.ProviderConsAddress - var pubKeys []tmprotocrypto.PublicKey - for i := 0; i < 10; i++ { - // generate a consensus public key for the provider - providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() - consAddr := sdk.ConsAddress(providerConsPubKey.Address()) - providerAddr := types.NewProviderConsAddress(consAddr) - providerAddresses = append(providerAddresses, providerAddr) - var providerValidatorAddr sdk.ValAddress - providerValidatorAddr = providerAddr.Address.Bytes() + // all validators are bonded except the last 2 + status := stakingtypes.Bonded + if i >= 8 { + status = stakingtypes.Unbonded + } - pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) validator := stakingtypes.Validator{ OperatorAddress: providerValidatorAddr.String(), ConsensusPubkey: pkAny, - Status: stakingtypes.Bonded, + Status: status, } pubKey := tmprotocrypto.PublicKey{ @@ -287,31 +470,51 @@ func TestComputePartialSetValidatorUpdates(t *testing.T) { GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() } - // first 6 validators to opt in - var nextOptedIn []types.ProviderConsAddress + // set first 6 validators as currently opted in where validators 1, 2, and 3 have a + // different power now than the power they had when they opted in + var currentValidators []keeper.OptedInValidator for i := 0; i < 6; i++ { - nextOptedIn = append(nextOptedIn, providerAddresses[i]) + if i > 0 && i < 4 { + currentValidators = append(currentValidators, + keeper.OptedInValidator{ProviderAddr: providerAddresses[i], + BlockHeight: 1, + Power: uint64(i + 1)}) + } else { + currentValidators = append(currentValidators, + keeper.OptedInValidator{ProviderAddr: providerAddresses[i], + BlockHeight: 1, + Power: uint64(i)}) + } } - var optedOut []types.ProviderConsAddress + // remove the first 2 validators + var validatorsToRemove []types.ProviderConsAddress + for i := 0; i < 2; i++ { + validatorsToRemove = append(validatorsToRemove, providerAddresses[i]) + } + + // add the last 4 validators + var validatorsToAdd []types.ProviderConsAddress for i := 6; i < 10; i++ { - optedOut = append(optedOut, providerAddresses[i]) + validatorsToAdd = append(validatorsToAdd, providerAddresses[i]) } expectedValUpdates := []abci.ValidatorUpdate{ + // validators 0 and 1 are removed and hence have a power of 0 {PubKey: pubKeys[0], Power: 0}, - {PubKey: pubKeys[1], Power: 1}, + {PubKey: pubKeys[1], Power: 0}, + // validators 2 and 3 had a different power before and hence are also added {PubKey: pubKeys[2], Power: 2}, {PubKey: pubKeys[3], Power: 3}, - {PubKey: pubKeys[4], Power: 4}, - {PubKey: pubKeys[5], Power: 5}, - {PubKey: pubKeys[6], Power: 0}, - {PubKey: pubKeys[7], Power: 0}, - {PubKey: pubKeys[8], Power: 0}, - {PubKey: pubKeys[9], Power: 0}, + // validators 4, 5 are not added because their voting power did not change + // validators 6 and 7 are bonded and are added + {PubKey: pubKeys[6], Power: 6}, + {PubKey: pubKeys[7], Power: 7}, + // validators 8 and 9 are unbonded, so they will not end up being added } - actualValUpdates := providerKeeper.ComputePartialSetValidatorUpdates(ctx, nextOptedIn, optedOut) + actualValUpdates := providerKeeper.ComputeValidatorUpdates(ctx, + currentValidators, validatorsToAdd, validatorsToRemove) sort.Slice(expectedValUpdates, func(i int, j int) bool { if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) < 0 { @@ -334,13 +537,14 @@ func TestComputePartialSetValidatorUpdates(t *testing.T) { } -func TestResetOptedInSet(t *testing.T) { +func TestComputeNextValidators(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - chainID := "chainID" + // change the block height, so we can verify that newly added validators have the right height set + ctx = ctx.WithBlockHeight(1000) - // create 10 validators + // create 10 validators, where the first 8 are bonded and the last 2 are unbonded var providerAddresses []types.ProviderConsAddress var pubKeys []tmprotocrypto.PublicKey for i := 0; i < 10; i++ { @@ -354,10 +558,17 @@ func TestResetOptedInSet(t *testing.T) { providerValidatorAddr = providerAddr.Address.Bytes() pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) + + // all validators are bonded except the last 2 + status := stakingtypes.Bonded + if i >= 8 { + status = stakingtypes.Unbonded + } + validator := stakingtypes.Validator{ OperatorAddress: providerValidatorAddr.String(), ConsensusPubkey: pkAny, - Status: stakingtypes.Bonded, + Status: status, } pubKey := tmprotocrypto.PublicKey{ @@ -370,57 +581,71 @@ func TestResetOptedInSet(t *testing.T) { mocks.MockStakingKeeper.EXPECT(). GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() - if i != 1 { - mocks.MockStakingKeeper.EXPECT(). - GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i + 1)).AnyTimes() + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() + + } + + // set first 6 validators as currently opted in where validators 1, 2, and 3 have a + // different power now than the power they had when they opted in + var currentValidators []keeper.OptedInValidator + for i := 0; i < 6; i++ { + if i > 0 && i < 4 { + currentValidators = append(currentValidators, + keeper.OptedInValidator{ProviderAddr: providerAddresses[i], + BlockHeight: 1, + Power: uint64(i + 1)}) } else { - mocks.MockStakingKeeper.EXPECT(). - GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(0)).AnyTimes() + currentValidators = append(currentValidators, + keeper.OptedInValidator{ProviderAddr: providerAddresses[i], + BlockHeight: 1, + Power: uint64(i)}) } } - // first 6 validators to opt in - var toBeOptedIn []types.ProviderConsAddress - - providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[0], 1, uint64(1)) - - // this validator won't be added because it has a power of 0 .. .see above mock .. not here - providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[1], 1, uint64(3)) - - for i := 2; i < 6; i++ { - providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddresses[i]) - toBeOptedIn = append(toBeOptedIn, providerAddresses[i]) + // remove the first 2 validators + var validatorsToRemove []types.ProviderConsAddress + for i := 0; i < 2; i++ { + validatorsToRemove = append(validatorsToRemove, providerAddresses[i]) } - var optedOut []types.ProviderConsAddress + // add the last 4 validators + var validatorsToAdd []types.ProviderConsAddress for i := 6; i < 10; i++ { - providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddresses[i]) - optedOut = append(optedOut, providerAddresses[i]) + validatorsToAdd = append(validatorsToAdd, providerAddresses[i]) } - require.NotEmpty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) - require.NotEmpty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) - - providerKeeper.ResetOptedInSet(ctx, chainID) - - require.Empty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) - require.Empty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) - - actualOptedInValidators := providerKeeper.GetOptedIn(ctx, chainID) - expectedOptedInValidators := []keeper.OptedInValidator{ - {ProviderAddr: providerAddresses[0], BlockHeight: 1, Power: 1}, - {ProviderAddr: providerAddresses[2], BlockHeight: uint64(ctx.BlockHeight()), Power: 3}, - {ProviderAddr: providerAddresses[3], BlockHeight: uint64(ctx.BlockHeight()), Power: 4}, - {ProviderAddr: providerAddresses[4], BlockHeight: uint64(ctx.BlockHeight()), Power: 5}, - {ProviderAddr: providerAddresses[5], BlockHeight: uint64(ctx.BlockHeight()), Power: 6}, + expectedValidators := []keeper.OptedInValidator{ + {ProviderAddr: providerAddresses[2], BlockHeight: uint64(1), Power: uint64(2)}, + {ProviderAddr: providerAddresses[3], BlockHeight: uint64(1), Power: uint64(3)}, + {ProviderAddr: providerAddresses[4], BlockHeight: uint64(1), Power: uint64(4)}, + {ProviderAddr: providerAddresses[5], BlockHeight: uint64(1), Power: uint64(5)}, + {ProviderAddr: providerAddresses[6], BlockHeight: uint64(1000), Power: uint64(6)}, + {ProviderAddr: providerAddresses[7], BlockHeight: uint64(1000), Power: uint64(7)}, } - sort.Slice(expectedOptedInValidators, func(i int, j int) bool { - return strings.Compare(expectedOptedInValidators[i].ProviderAddr.String(), expectedOptedInValidators[j].ProviderAddr.String()) < 0 + actualValidators := providerKeeper.ComputeNextValidators(ctx, + currentValidators, validatorsToAdd, validatorsToRemove) + + sort.Slice(actualValidators, func(i int, j int) bool { + if strings.Compare(actualValidators[i].ProviderAddr.String(), actualValidators[j].ProviderAddr.String()) == 0 { + if actualValidators[i].BlockHeight == actualValidators[j].BlockHeight { + return actualValidators[i].Power < actualValidators[j].Power + } + return actualValidators[i].BlockHeight < actualValidators[j].BlockHeight + } + return strings.Compare(actualValidators[i].ProviderAddr.String(), actualValidators[j].ProviderAddr.String()) < 0 }) - sort.Slice(actualOptedInValidators, func(i int, j int) bool { - return strings.Compare(actualOptedInValidators[i].ProviderAddr.String(), actualOptedInValidators[j].ProviderAddr.String()) < 0 + sort.Slice(expectedValidators, func(i int, j int) bool { + if strings.Compare(expectedValidators[i].ProviderAddr.String(), expectedValidators[j].ProviderAddr.String()) == 0 { + if expectedValidators[i].BlockHeight == expectedValidators[j].BlockHeight { + return expectedValidators[i].Power < expectedValidators[j].Power + } + return expectedValidators[i].BlockHeight < expectedValidators[j].BlockHeight + } + return strings.Compare(expectedValidators[i].ProviderAddr.String(), expectedValidators[j].ProviderAddr.String()) < 0 }) - require.Equal(t, expectedOptedInValidators, actualOptedInValidators) + + require.Equal(t, expectedValidators, actualValidators) } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 12ae32dcfe..78298e54cc 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + abci "github.com/cometbft/cometbft/abci/types" "strconv" "time" @@ -254,40 +255,43 @@ func (k Keeper) MakeConsumerGenesis( return false }) - // at this initial state, no validator is yet opted in ... but a validator is to be opted in - initialUpdates := k.ComputePartialSetValidatorUpdates(ctx, - k.GetNextOptedInValidators(ctx, chainID), - k.GetToBeOptedOut(ctx, chainID)) - - // - //initialUpdates := []abci.ValidatorUpdate{} - //for _, p := range lastPowers { - // addr, err := sdk.ValAddressFromBech32(p.Address) - // if err != nil { - // return gen, nil, err - // } - // - // validator, found := k.stakingKeeper.GetValidator(ctx, addr) - // if !found { - // return gen, nil, errorsmod.Wrapf(stakingtypes.ErrNoValidatorFound, "error getting validator from LastValidatorPowers: %s", err) - // } + //currentValidators := k.GetOptedIn(ctx, chainID) + //validatorsToAdd := k.GetToBeOptedIn(ctx, chainID) + //validatorsToRemove := k.GetToBeOptedOut(ctx, chainID) // - // tmProtoPk, err := validator.TmConsPublicKey() - // if err != nil { - // return gen, nil, err - // } - // - // initialUpdates = append(initialUpdates, abci.ValidatorUpdate{ - // PubKey: tmProtoPk, - // Power: p.Power, - // }) - //} + //initialUpdates := k.ComputeValidatorUpdates(ctx, currentValidators, validatorsToAdd, validatorsToRemove) + + initialUpdates := []abci.ValidatorUpdate{} + for _, p := range lastPowers { + addr, err := sdk.ValAddressFromBech32(p.Address) + if err != nil { + return gen, nil, err + } + + validator, found := k.stakingKeeper.GetValidator(ctx, addr) + if !found { + return gen, nil, errorsmod.Wrapf(stakingtypes.ErrNoValidatorFound, "error getting validator from LastValidatorPowers: %s", err) + } + + tmProtoPk, err := validator.TmConsPublicKey() + if err != nil { + return gen, nil, err + } + + initialUpdates = append(initialUpdates, abci.ValidatorUpdate{ + PubKey: tmProtoPk, + Power: p.Power, + }) + } // Apply key assignments to the initial valset. initialUpdatesWithConsumerKeys := k.MustApplyKeyAssignmentToValUpdates(ctx, chainID, initialUpdates, - func(address types.ProviderConsAddress) bool { return k.IsToBeOptedIn(ctx, chainID, address) }) + func(address types.ProviderConsAddress) bool { + return true + }) - k.ResetOptedInSet(ctx, chainID) + //nextValidators := k.ComputeNextValidators(ctx, currentValidators, validatorsToAdd, validatorsToRemove) + //k.ResetCurrentValidators(ctx, chainID, nextValidators) // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) @@ -372,12 +376,13 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { // then someone could create a bogus ConsumerAdditionProposal to override the Top N for a specific chain. k.SetTopN(ctx, prop.ChainId, prop.Top_N) - if k.IsOptIn(ctx, prop.ChainId) && len(k.GetToBeOptedIn(ctx, prop.ChainId)) == 0 { - // drop the proposal - ctx.Logger().Info("could not start Opt In consumer chain (%s) because no validator has opted in", - prop.ChainId) - continue - } + // FIXME(PSS) + //if k.IsOptIn(ctx, prop.ChainId) && len(k.GetToBeOptedIn(ctx, prop.ChainId)) == 0 { + // // drop the proposal + // ctx.Logger().Info("could not start Opt In consumer chain (%s) because no validator has opted in", + // prop.ChainId) + // continue + //} // create consumer client in a cached context to handle errors cachedCtx, writeFn, err := k.CreateConsumerClientInCachedCtx(ctx, prop) diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 7872c02db8..6f5c81a7ea 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -2,7 +2,6 @@ package keeper import ( "fmt" - abci "github.com/cometbft/cometbft/abci/types" "strconv" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -213,32 +212,28 @@ func (k Keeper) SendVSCPacketsToChain(ctx sdk.Context, chainID, channelID string // QueueVSCPackets queues latest validator updates for every registered consumer chain func (k Keeper) QueueVSCPackets(ctx sdk.Context) { valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID + // Get the validator updates from the staking module. + // Note: GetValidatorUpdates panics if the updates provided by the x/staking module + // of cosmos-sdk is invalid. + valUpdates := k.stakingKeeper.GetValidatorUpdates(ctx) for _, chain := range k.GetAllConsumerChains(ctx) { - var valUpdates []abci.ValidatorUpdate - if k.IsTopN(ctx, chain.ChainId) { - valUpdates = k.ComputePartialSetValidatorUpdates(ctx, - k.GetNextOptedInValidators(ctx, chain.ChainId), - k.GetToBeOptedOut(ctx, chain.ChainId)) - } else { - valUpdates = k.stakingKeeper.GetValidatorUpdates(ctx) - } + currentValidators := k.GetOptedIn(ctx, chain.ChainId) + validatorsToAdd := k.GetToBeOptedIn(ctx, chain.ChainId) + validatorsToRemove := k.GetToBeOptedOut(ctx, chain.ChainId) - var considerKeyReplacement func(address providertypes.ProviderConsAddress) bool - if k.IsTopN(ctx, chain.ChainId) { - considerKeyReplacement = func(address providertypes.ProviderConsAddress) bool { - return k.IsOptedIn(ctx, chain.ChainId, address) - } - } else { - considerKeyReplacement = func(address providertypes.ProviderConsAddress) bool { - return true - } - } + // FIXME(PSS) + //valUpdates := k.ComputeValidatorUpdates(ctx, currentValidators, validatorsToAdd, validatorsToRemove) // Apply the key assignment to the validator updates. + considerKeyReplacement := func(address providertypes.ProviderConsAddress) bool { + return true + //return k.IsOptedIn(ctx, chain.ChainId, address) + } valUpdates = k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, valUpdates, considerKeyReplacement) - k.ResetOptedInSet(ctx, chain.ChainId) + nextValidators := k.ComputeNextValidators(ctx, currentValidators, validatorsToAdd, validatorsToRemove) + k.ResetCurrentValidators(ctx, chain.ChainId, nextValidators) // check whether there are changes in the validator set; // note that this also entails unbonding operations From de1ce679cec3605ed51c4c2fc0622192e9de083e Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 14 Feb 2024 16:37:42 +0100 Subject: [PATCH 05/15] more cleaning up --- x/ccv/provider/keeper/key_assignment.go | 9 +- x/ccv/provider/keeper/msg_server.go | 37 ++- x/ccv/provider/keeper/partial_set_security.go | 228 ++----------- .../keeper/partial_set_security_test.go | 303 ------------------ x/ccv/provider/keeper/proposal.go | 4 +- 5 files changed, 67 insertions(+), 514 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index 5b66fc7b86..92e79b9653 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -547,8 +547,9 @@ func (k Keeper) AssignConsumerKey( return nil } -// MustApplyKeyAssignmentToValUpdates applies the key assignment to the validator updates -// received from the staking module. +// MustApplyKeyAssignmentToValUpdates applies the key assignment to the validator updates received from the +// staking module. For validators that do not have a validator update in `valUpdates`, the method also considers +// key-assignment replacements when the `considerKeyReplacement` predicate evaluates to `true` for this validator. // The method panics if the key-assignment state is corrupted. func (k Keeper) MustApplyKeyAssignmentToValUpdates( ctx sdk.Context, @@ -610,9 +611,9 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( for _, replacement := range k.GetAllKeyAssignmentReplacements(ctx, chainID) { providerAddr := types.NewProviderConsAddress(replacement.ProviderAddr) - // only consider updates for validators that are considered here ... + // filter out key-assignment replacements if !considerKeyReplacement(providerAddr) { - return + continue } k.DeleteKeyAssignmentReplacement(ctx, chainID, providerAddr) newUpdates = append(newUpdates, abci.ValidatorUpdate{ diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index eaee4dfb8f..4824f9cf8a 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -139,20 +139,30 @@ 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) + providerValidatorAddr, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + return nil, err + } + + // validator must already be registered + validator, found := k.stakingKeeper.GetValidator(ctx, providerValidatorAddr) + if !found { + return nil, stakingtypes.ErrNoValidatorFound + } + + consAddress, err := validator.GetConsAddr() if err != nil { return nil, err } - // FIXME: something is off here .. val to cons ... - providerAddr := types.NewProviderConsAddress(valAddress) + providerConsAddr := types.NewProviderConsAddress(consAddress) if err != nil { return nil, err } 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 +183,27 @@ 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) + providerValidatorAddr, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + return nil, err + } + + // validator must already be registered + validator, found := k.stakingKeeper.GetValidator(ctx, providerValidatorAddr) + if !found { + return nil, stakingtypes.ErrNoValidatorFound + } + + consAddress, err := validator.GetConsAddr() if err != nil { return nil, err } - providerAddr := types.NewProviderConsAddress(valAddress) + providerConsAddr := types.NewProviderConsAddress(consAddress) if err != nil { return nil, err } - 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 6337777a31..87fd23f7fd 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -75,25 +75,15 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types return nil } -// getValidatorsPublicKey is a helper function that returns the public key of the validator -func (k Keeper) getValidatorsPublicKey(validator stakingtypes.Validator) (tmprotocrypto.PublicKey, error) { - consAddr, err := validator.GetConsAddr() - if err != nil { - return tmprotocrypto.PublicKey{}, err - } - return tmprotocrypto.PublicKey{ - Sum: &tmprotocrypto.PublicKey_Ed25519{ - Ed25519: consAddr.Bytes(), - }, - }, nil -} - -// getValidatorOperatorAddressAndPublicKey is a helper function that returns the public key of the validator -func (k Keeper) getValidatorOperatorAddressAndPublicKey(ctx sdk.Context, addr types.ProviderConsAddress) (sdk.ValAddress, tmprotocrypto.PublicKey, error) { +// getValAddressAndPublicKey is a helper function that returns the `ValAddress` and the public key of +// the corresponding validator +func (k Keeper) getValAddressAndPublicKey(ctx sdk.Context, addr types.ProviderConsAddress, +) (sdk.ValAddress, tmprotocrypto.PublicKey, error) { validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) if !found { - + return sdk.ValAddress{}, tmprotocrypto.PublicKey{}, stakingtypes.ErrNoValidatorFound } + consAddr, err := validator.GetConsAddr() if err != nil { return sdk.ValAddress{}, tmprotocrypto.PublicKey{}, err @@ -107,170 +97,15 @@ func (k Keeper) getValidatorOperatorAddressAndPublicKey(ctx sdk.Context, addr ty return validator.GetOperator(), pubKey, nil } -//func filterAddresses(addresses []types.ProviderConsAddress, predicate func(types.ProviderConsAddress) bool) []types.ProviderConsAddress { -// var filteredAddresses []types.ProviderConsAddress -// for _, addr := range addresses { -// if predicate(addr) { -// filteredAddresses = append(filteredAddresses, addr) -// } -// } -// return filteredAddresses -//} -// -//func filterOptedInValidators(addresses []OptedInValidator, predicate func(validator OptedInValidator) bool) []OptedInValidator { -// var filteredAddresses []OptedInValidator -// for _, addr := range addresses { -// if predicate(addr) { -// filteredAddresses = append(filteredAddresses, addr) -// } -// } -// return filteredAddresses -//} - -//// GetBondedToBeOptedInValidators returns all the bonded validators that are to be opted in -//// on consumer chain with `chainID` -//func (k Keeper) GetBondedToBeOptedInValidators(ctx sdk.Context, chainID string) []types.ProviderConsAddress { -// var consAddresses []types.ProviderConsAddress -// -// bondedToBeOptedInValidators := -// filterAddresses(k.GetToBeOptedIn(ctx, chainID), func(addr types.ProviderConsAddress) bool { -// validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) -// if !found { -// // a validator was successfully set to be opted in, but we cannot find this validator anymore -// k.Logger(ctx).Error("could not find to-be-opted-in invalidator with consensus address: %s", addr.ToSdkConsAddr().Bytes()) -// return false -// } -// -// if !validator.IsBonded() { -// // only consider validators that are in the active set -// return false -// } -// return true -// }) -// -// return bondedToBeOptedInValidators -// -// //for _, addr := range k.GetToBeOptedIn(ctx, chainID) { -// // validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) -// // if !found { -// // // a validator was successfully set to be opted in, but we cannot find this validator anymore -// // k.Logger(ctx).Error("could not find to-be-opted-in invalidator with consensus address: %s", addr.ToSdkConsAddr().Bytes()) -// // continue -// // } -// // -// // if !validator.IsBonded() { -// // // only consider validators that are in the active set -// // continue -// // } -// // consAddresses = append(consAddresses, addr) -// //} -// -// return consAddresses -//} - -//// GetOptedInValidatorsForValUpdates returns all the addresses of validators that are going to be opted in -//// next on the consumer chain with `chainID` and a validator update would need to be sent for them -//func (k Keeper) GetOptedInValidatorsForValUpdates(ctx sdk.Context, chainID string) []types.ProviderConsAddress { -// var consAddresses []types.ProviderConsAddress -// -// // Create set that contains all the validators that are to be opted out so that we do not reintroduce opted-out -// // validators when going through the already opted in validators. -// isSetToBeOptedOut := make(map[string]bool) -// for _, addr := range k.GetToBeOptedOut(ctx, chainID) { -// isSetToBeOptedOut[addr.ToSdkConsAddr().String()] = true -// } -// -// for _, val := range k.GetOptedIn(ctx, chainID) { -// // go through all the validators that are currently opted in -// validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) -// if !found { -// // a validator was opted in, but we cannot find this validator anymore -// k.Logger(ctx).Error("could not find opted-in validator with consensus address: %s", -// val.ProviderAddr.ToSdkConsAddr().Bytes()) -// continue -// } -// -// if isSetToBeOptedOut[val.ProviderAddr.ToSdkConsAddr().String()] { -// continue -// } -// -// if val.Power == uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) { -// // Only send an update if an opted-in validator had a power change since the lat time it was sent. -// // If an opted-in validator is not in the active set anymore, then its new power is 0 and hence we -// // do not consider this validator in the next set of opted in validators. -// continue -// } -// -// consAddresses = append(consAddresses, val.ProviderAddr) -// } -// -// // append all the to-be-opted-in validators -// consAddresses = append(consAddresses, k.GetBondedToBeOptedInValidators(ctx, chainID)...) -// return consAddresses -//} - -//// ComputePartialSetValidatorUpdates returns the partial set of `ValidatorUpdate`s that the provider chain sends to the -//// consumer chain. Receives `nextOptedIn` that corresponds to the validators that would be opted in next and -//// `toBeOptedOut` that contains the validators that would be opted out. -//func (k Keeper) ComputePartialSetValidatorUpdates(ctx sdk.Context, optedInValidatorsForValUpdates []types.ProviderConsAddress, -// toBeOptedOut []types.ProviderConsAddress) []abci.ValidatorUpdate { -// var partialSetUpdates []abci.ValidatorUpdate -// -// for _, val := range optedInValidatorsForValUpdates { -// validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ToSdkConsAddr()) -// if !found { -// // any validator in `optedInValidatorsForValUpdates` should be found -// k.Logger(ctx).Error("could not find validator with consensus address: %s", -// val.ToSdkConsAddr().Bytes()) -// continue -// } -// -// pubKey, err := k.getValidatorsPublicKey(validator) -// if err != nil { -// k.Logger(ctx).Error("could retrieve public key from validator with consensus address: %s", -// val.ToSdkConsAddr().Bytes()) -// continue -// } -// -// // if a validator that was opted in, is not in the active set anymore, then `GetLastValidatorPower` returns 0 -// partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ -// PubKey: pubKey, -// Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), -// }) -// } -// -// for _, addr := range toBeOptedOut { -// // go through all the validators that are currently opted in -// validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) -// if !found { -// // a validator was opted in, but we cannot find this validator anymore -// k.Logger(ctx).Error("could not find opted-in validator with consensus address: %s", -// addr.ToSdkConsAddr().Bytes()) -// continue -// } -// pubKey, err := k.getValidatorsPublicKey(validator) -// if err != nil { -// k.Logger(ctx).Error("could retrieve public key from validator with consensus address: %s", -// addr.ToSdkConsAddr().Bytes()) -// continue -// } -// -// // send power 0 so the validator is removed -// partialSetUpdates = append(partialSetUpdates, abci.ValidatorUpdate{ -// PubKey: pubKey, -// Power: 0, -// }) -// } -// -// return partialSetUpdates -//} - -func (k Keeper) ComputeNextValidators(ctx sdk.Context, currentValidators []OptedInValidator, - validatorsToAdd []types.ProviderConsAddress, - validatorsToRemove []types.ProviderConsAddress, +// ComputeNextValidators computes the next validator set that is responsible for validating on a consumer chain. +// The returned opted-in validators by `ComputeNextValidators` constitute the next `currentValidators`. +func (k Keeper) ComputeNextValidators(ctx sdk.Context, + currentValidators []OptedInValidator, + validatorAddressesToAdd []types.ProviderConsAddress, + validatorAddressesToRemove []types.ProviderConsAddress, ) []OptedInValidator { isRemoved := make(map[string]bool) - for _, val := range validatorsToRemove { + for _, val := range validatorAddressesToRemove { isRemoved[val.ToSdkConsAddr().String()] = true } @@ -279,9 +114,8 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, currentValidators []Opted if isRemoved[val.ProviderAddr.ToSdkConsAddr().String()] { continue } - valAddress, _, err := k.getValidatorOperatorAddressAndPublicKey(ctx, val.ProviderAddr) + valAddress, _, err := k.getValAddressAndPublicKey(ctx, val.ProviderAddr) if err != nil { - // FIXME continue } @@ -292,10 +126,9 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, currentValidators []Opted out = append(out, val) } - for _, addr := range validatorsToAdd { - valAddress, _, err := k.getValidatorOperatorAddressAndPublicKey(ctx, addr) + for _, addr := range validatorAddressesToAdd { + valAddress, _, err := k.getValAddressAndPublicKey(ctx, addr) if err != nil { - // FIXME continue } @@ -314,17 +147,19 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, currentValidators []Opted return out } +// ComputeValidatorUpdates computes the validator updates needed to be sent to the consumer chain to capture +// the newly opted-in and opted-out validators, as well as validators that unbonded. func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, currentValidators []OptedInValidator, - validatorsToAdd []types.ProviderConsAddress, - validatorsToRemove []types.ProviderConsAddress, + validatorAddressesToAdd []types.ProviderConsAddress, + validatorAddressesToRemove []types.ProviderConsAddress, ) []abci.ValidatorUpdate { var m = make(map[string]abci.ValidatorUpdate) for _, val := range currentValidators { - valAddress, pubKey, err := k.getValidatorOperatorAddressAndPublicKey(ctx, val.ProviderAddr) + valAddress, pubKey, err := k.getValAddressAndPublicKey(ctx, val.ProviderAddr) if err != nil { - // FIXME ... + continue } validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) @@ -336,24 +171,25 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, continue } - // if this validator has unbonded, the result would be 0 of power so we still good!! but then if you add, you readd - // no, because we check if bonded when we readd ... you cannot readd if already here + // if `val` has unbonded, its `GetLastValidatorPower` power returns 0. m[pubKey.String()] = abci.ValidatorUpdate{ PubKey: pubKey, Power: k.stakingKeeper.GetLastValidatorPower(ctx, valAddress), } } - for _, addr := range validatorsToAdd { - valAddress, pubKey, err := k.getValidatorOperatorAddressAndPublicKey(ctx, addr) + for _, addr := range validatorAddressesToAdd { + valAddress, pubKey, err := k.getValAddressAndPublicKey(ctx, addr) if err != nil { - + continue } validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) if !found { continue } + + // if a validator is in the active set, we do not add it if !validator.IsBonded() { continue } @@ -364,8 +200,8 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, } } - for _, addr := range validatorsToRemove { - _, pubKey, err := k.getValidatorOperatorAddressAndPublicKey(ctx, addr) + for _, addr := range validatorAddressesToRemove { + _, pubKey, err := k.getValAddressAndPublicKey(ctx, addr) if err != nil { } @@ -382,9 +218,7 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, out = append(out, update) } - // similar to `AccumulateChanges` - // The list of tendermint updates should hash the same across all consensus nodes - // that means it is necessary to sort for determinism. + // Similarly to `AccumulateChanges`, we sort validators for determinism. sort.Slice(out, func(i, j int) bool { if out[i].Power != out[j].Power { return out[i].Power > out[j].Power diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 3f798a9cfb..e02ae0a29b 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -122,309 +122,6 @@ func TestHandleOptOut(t *testing.T) { require.False(t, providerKeeper.IsToBeOptedOut(ctx, "chainID", providerAddr)) } -//func TestGetToBeOptedInValidators(t *testing.T) { -// providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) -// defer ctrl.Finish() -// -// chainID := "chainID" -// -// var expectedAddresses []types.ProviderConsAddress -// // set 10 validators as to-be-opted-in, but only 3 (= 10/3) of those are `Bonded` -// for i := int64(0); i < 10; i++ { -// // generate a consensus public key for the provider -// providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() -// consAddr := sdk.ConsAddress(providerConsPubKey.Address()) -// providerAddr := types.NewProviderConsAddress(consAddr) -// -// var providerValidatorAddr sdk.ValAddress -// providerValidatorAddr = providerAddr.Address.Bytes() -// -// var status stakingtypes.BondStatus -// if i%3 == 0 { -// status = stakingtypes.Unbonded -// } else if i%3 == 1 { -// status = stakingtypes.Bonded -// // we only expect bonded validators to be returned by `GetBondedToBeOptedInValidators` -// expectedAddresses = append(expectedAddresses, providerAddr) -// } else { -// status = stakingtypes.Unbonding -// } -// -// pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) -// validator := stakingtypes.Validator{ -// OperatorAddress: providerValidatorAddr.String(), -// ConsensusPubkey: pkAny, -// Status: status, -// } -// -// mocks.MockStakingKeeper.EXPECT(). -// GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() -// -// providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddr) -// } -// -// actualAddresses := providerKeeper.GetBondedToBeOptedInValidators(ctx, chainID) -// -// // sort before comparing -// sort.Slice(expectedAddresses, func(i int, j int) bool { -// return strings.Compare(expectedAddresses[i].String(), expectedAddresses[j].String()) < 0 -// }) -// sort.Slice(actualAddresses, func(i int, j int) bool { -// return strings.Compare(actualAddresses[i].String(), actualAddresses[j].String()) < 0 -// }) -// require.Equal(t, expectedAddresses, actualAddresses) -//} -// -//func TestGetNextOptedInValidators(t *testing.T) { -// providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) -// defer ctrl.Finish() -// -// chainID := "chainID" -// -// // create 10 validators -// var providerAddresses []types.ProviderConsAddress -// for i := 0; i < 10; i++ { -// // generate a consensus public key for the provider -// providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() -// consAddr := sdk.ConsAddress(providerConsPubKey.Address()) -// providerAddr := types.NewProviderConsAddress(consAddr) -// providerAddresses = append(providerAddresses, providerAddr) -// -// var providerValidatorAddr sdk.ValAddress -// providerValidatorAddr = providerAddr.Address.Bytes() -// -// pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) -// validator := stakingtypes.Validator{ -// OperatorAddress: providerValidatorAddr.String(), -// ConsensusPubkey: pkAny, -// Status: stakingtypes.Bonded, -// } -// -// mocks.MockStakingKeeper.EXPECT(). -// GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() -// -// mocks.MockStakingKeeper.EXPECT(). -// GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() -// } -// -// // first 3 validators (i.e., 0, 1, and 2) are already opted in but with the same power as they currently have -// // and therefore should not be returned by `GetOptedInValidatorsForValUpdates` -// for i := 0; i < 3; i++ { -// providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, uint64(i)) -// -// } -// -// // validators 3, 4, 5 and 6 are already opted in but with a different voting power -// // and therefore should be returned by `GetOptedInValidatorsForValUpdates` (unless opted out -- see below) -// for i := 3; i < 7; i++ { -// providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, uint64(i+1)) -// } -// -// // validators 8 and 9 are to be opted in -// for i := 8; i < 10; i++ { -// providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddresses[i]) -// } -// -// // first 5 validators are to be opted out and hence validators 3 and 4 won't be returned -// // by `GetOptedInValidatorsForValUpdates` -// for i := 0; i < 5; i++ { -// providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddresses[i]) -// } -// -// expectedAddresses := []types.ProviderConsAddress{ -// providerAddresses[5], -// providerAddresses[6], -// providerAddresses[8], -// providerAddresses[9], -// } -// actualAddresses := providerKeeper.GetOptedInValidatorsForValUpdates(ctx, chainID) -// -// // sort before comparing -// sort.Slice(expectedAddresses, func(i int, j int) bool { -// return strings.Compare(expectedAddresses[i].String(), expectedAddresses[j].String()) < 0 -// }) -// sort.Slice(actualAddresses, func(i int, j int) bool { -// return strings.Compare(actualAddresses[i].String(), actualAddresses[j].String()) < 0 -// }) -// require.Equal(t, expectedAddresses, actualAddresses) -//} -// -//func TestComputePartialSetValidatorUpdates(t *testing.T) { -// providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) -// defer ctrl.Finish() -// -// // create 10 validators -// var providerAddresses []types.ProviderConsAddress -// var pubKeys []tmprotocrypto.PublicKey -// for i := 0; i < 10; i++ { -// // generate a consensus public key for the provider -// providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() -// consAddr := sdk.ConsAddress(providerConsPubKey.Address()) -// providerAddr := types.NewProviderConsAddress(consAddr) -// providerAddresses = append(providerAddresses, providerAddr) -// -// var providerValidatorAddr sdk.ValAddress -// providerValidatorAddr = providerAddr.Address.Bytes() -// -// pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) -// validator := stakingtypes.Validator{ -// OperatorAddress: providerValidatorAddr.String(), -// ConsensusPubkey: pkAny, -// Status: stakingtypes.Bonded, -// } -// -// pubKey := tmprotocrypto.PublicKey{ -// Sum: &tmprotocrypto.PublicKey_Ed25519{ -// Ed25519: consAddr.Bytes(), -// }, -// } -// pubKeys = append(pubKeys, pubKey) -// -// mocks.MockStakingKeeper.EXPECT(). -// GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() -// -// mocks.MockStakingKeeper.EXPECT(). -// GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() -// } -// -// // first 6 validators to opt in -// var nextOptedIn []types.ProviderConsAddress -// for i := 0; i < 6; i++ { -// nextOptedIn = append(nextOptedIn, providerAddresses[i]) -// } -// -// var optedOut []types.ProviderConsAddress -// for i := 6; i < 10; i++ { -// optedOut = append(optedOut, providerAddresses[i]) -// } -// -// expectedValUpdates := []abci.ValidatorUpdate{ -// {PubKey: pubKeys[0], Power: 0}, -// {PubKey: pubKeys[1], Power: 1}, -// {PubKey: pubKeys[2], Power: 2}, -// {PubKey: pubKeys[3], Power: 3}, -// {PubKey: pubKeys[4], Power: 4}, -// {PubKey: pubKeys[5], Power: 5}, -// {PubKey: pubKeys[6], Power: 0}, -// {PubKey: pubKeys[7], Power: 0}, -// {PubKey: pubKeys[8], Power: 0}, -// {PubKey: pubKeys[9], Power: 0}, -// } -// -// actualValUpdates := providerKeeper.ComputePartialSetValidatorUpdates(ctx, nextOptedIn, optedOut) -// -// sort.Slice(expectedValUpdates, func(i int, j int) bool { -// if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) < 0 { -// return true -// } else if expectedValUpdates[i].PubKey.Compare(expectedValUpdates[j].PubKey) == 0 { -// return expectedValUpdates[i].Power < expectedValUpdates[j].Power -// } -// return false -// }) -// sort.Slice(actualValUpdates, func(i int, j int) bool { -// if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) < 0 { -// return true -// } else if actualValUpdates[i].PubKey.Compare(actualValUpdates[j].PubKey) == 0 { -// return actualValUpdates[i].Power < actualValUpdates[j].Power -// } -// return false -// }) -// -// require.Equal(t, expectedValUpdates, actualValUpdates) -// -//} -// -//func TestResetOptedInSet(t *testing.T) { -// providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) -// defer ctrl.Finish() -// -// chainID := "chainID" -// -// // create 10 validators -// var providerAddresses []types.ProviderConsAddress -// var pubKeys []tmprotocrypto.PublicKey -// for i := 0; i < 10; i++ { -// // generate a consensus public key for the provider -// providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() -// consAddr := sdk.ConsAddress(providerConsPubKey.Address()) -// providerAddr := types.NewProviderConsAddress(consAddr) -// providerAddresses = append(providerAddresses, providerAddr) -// -// var providerValidatorAddr sdk.ValAddress -// providerValidatorAddr = providerAddr.Address.Bytes() -// -// pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) -// validator := stakingtypes.Validator{ -// OperatorAddress: providerValidatorAddr.String(), -// ConsensusPubkey: pkAny, -// Status: stakingtypes.Bonded, -// } -// -// pubKey := tmprotocrypto.PublicKey{ -// Sum: &tmprotocrypto.PublicKey_Ed25519{ -// Ed25519: consAddr.Bytes(), -// }, -// } -// pubKeys = append(pubKeys, pubKey) -// -// mocks.MockStakingKeeper.EXPECT(). -// GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() -// -// if i != 1 { -// mocks.MockStakingKeeper.EXPECT(). -// GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i + 1)).AnyTimes() -// } else { -// mocks.MockStakingKeeper.EXPECT(). -// GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(0)).AnyTimes() -// } -// } -// -// // first 6 validators to opt in -// var toBeOptedIn []types.ProviderConsAddress -// -// providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[0], 1, uint64(1)) -// -// // this validator won't be added because it has a power of 0 .. .see above mock .. not here -// providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[1], 1, uint64(3)) -// -// for i := 2; i < 6; i++ { -// providerKeeper.SetToBeOptedIn(ctx, chainID, providerAddresses[i]) -// toBeOptedIn = append(toBeOptedIn, providerAddresses[i]) -// } -// -// var optedOut []types.ProviderConsAddress -// for i := 6; i < 10; i++ { -// providerKeeper.SetToBeOptedOut(ctx, chainID, providerAddresses[i]) -// optedOut = append(optedOut, providerAddresses[i]) -// } -// -// require.NotEmpty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) -// require.NotEmpty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) -// -// providerKeeper.ResetOptedInSet(ctx, chainID) -// -// require.Empty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) -// require.Empty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) -// -// actualOptedInValidators := providerKeeper.GetOptedIn(ctx, chainID) -// expectedOptedInValidators := []keeper.OptedInValidator{ -// {ProviderAddr: providerAddresses[0], BlockHeight: 1, Power: 1}, -// {ProviderAddr: providerAddresses[2], BlockHeight: uint64(ctx.BlockHeight()), Power: 3}, -// {ProviderAddr: providerAddresses[3], BlockHeight: uint64(ctx.BlockHeight()), Power: 4}, -// {ProviderAddr: providerAddresses[4], BlockHeight: uint64(ctx.BlockHeight()), Power: 5}, -// {ProviderAddr: providerAddresses[5], BlockHeight: uint64(ctx.BlockHeight()), Power: 6}, -// } -// -// sort.Slice(expectedOptedInValidators, func(i int, j int) bool { -// return strings.Compare(expectedOptedInValidators[i].ProviderAddr.String(), expectedOptedInValidators[j].ProviderAddr.String()) < 0 -// }) -// -// sort.Slice(actualOptedInValidators, func(i int, j int) bool { -// return strings.Compare(actualOptedInValidators[i].ProviderAddr.String(), actualOptedInValidators[j].ProviderAddr.String()) < 0 -// }) -// require.Equal(t, expectedOptedInValidators, actualOptedInValidators) -//} - func TestComputeValidatorUpdates(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 78298e54cc..9ad5ea514d 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -268,12 +268,12 @@ func (k Keeper) MakeConsumerGenesis( return gen, nil, err } - validator, found := k.stakingKeeper.GetValidator(ctx, addr) + val, found := k.stakingKeeper.GetValidator(ctx, addr) if !found { return gen, nil, errorsmod.Wrapf(stakingtypes.ErrNoValidatorFound, "error getting validator from LastValidatorPowers: %s", err) } - tmProtoPk, err := validator.TmConsPublicKey() + tmProtoPk, err := val.TmConsPublicKey() if err != nil { return gen, nil, err } From 8710bc296e7f08dcf5ab1686be389b9b85817b96 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 14 Feb 2024 16:57:15 +0100 Subject: [PATCH 06/15] nit changes --- x/ccv/provider/keeper/partial_set_security.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 87fd23f7fd..8f8911a53f 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -167,6 +167,7 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, continue } + // if the voting power did not change since the last time the validator opted in, do not create an update if val.Power == uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) { continue } @@ -203,14 +204,13 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, for _, addr := range validatorAddressesToRemove { _, pubKey, err := k.getValAddressAndPublicKey(ctx, addr) if err != nil { - + continue } m[pubKey.String()] = abci.ValidatorUpdate{ PubKey: pubKey, Power: 0, } - } var out []abci.ValidatorUpdate From a3e3d416cc73c3da9c3891138a6d9de74046f3e1 Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 15 Feb 2024 10:41:34 +0100 Subject: [PATCH 07/15] one more test --- x/ccv/provider/keeper/key_assignment.go | 1 + x/ccv/provider/keeper/partial_set_security.go | 109 +++++++++--------- .../keeper/partial_set_security_test.go | 66 ++++++++++- x/ccv/provider/keeper/proposal.go | 9 +- x/ccv/provider/keeper/relay.go | 26 ++--- 5 files changed, 138 insertions(+), 73 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index 92e79b9653..26da2f7f69 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -615,6 +615,7 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( if !considerKeyReplacement(providerAddr) { continue } + k.DeleteKeyAssignmentReplacement(ctx, chainID, providerAddr) newUpdates = append(newUpdates, abci.ValidatorUpdate{ PubKey: *replacement.PrevCKey, diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 8f8911a53f..d56f82ec30 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -97,56 +97,6 @@ func (k Keeper) getValAddressAndPublicKey(ctx sdk.Context, addr types.ProviderCo return validator.GetOperator(), pubKey, nil } -// ComputeNextValidators computes the next validator set that is responsible for validating on a consumer chain. -// The returned opted-in validators by `ComputeNextValidators` constitute the next `currentValidators`. -func (k Keeper) ComputeNextValidators(ctx sdk.Context, - currentValidators []OptedInValidator, - validatorAddressesToAdd []types.ProviderConsAddress, - validatorAddressesToRemove []types.ProviderConsAddress, -) []OptedInValidator { - isRemoved := make(map[string]bool) - for _, val := range validatorAddressesToRemove { - isRemoved[val.ToSdkConsAddr().String()] = true - } - - var out []OptedInValidator - for _, val := range currentValidators { - if isRemoved[val.ProviderAddr.ToSdkConsAddr().String()] { - continue - } - valAddress, _, err := k.getValAddressAndPublicKey(ctx, val.ProviderAddr) - if err != nil { - continue - } - - val.Power = uint64(k.stakingKeeper.GetLastValidatorPower(ctx, valAddress)) - if val.Power == 0 { - continue - } - out = append(out, val) - } - - for _, addr := range validatorAddressesToAdd { - valAddress, _, err := k.getValAddressAndPublicKey(ctx, addr) - if err != nil { - continue - } - - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) - if !found { - continue - } - if !validator.IsBonded() { - continue - } - power := uint64(k.stakingKeeper.GetLastValidatorPower(ctx, valAddress)) - - out = append(out, OptedInValidator{ProviderAddr: addr, BlockHeight: uint64(ctx.BlockHeight()), Power: power}) - } - - return out -} - // ComputeValidatorUpdates computes the validator updates needed to be sent to the consumer chain to capture // the newly opted-in and opted-out validators, as well as validators that unbonded. func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, @@ -172,7 +122,7 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, continue } - // if `val` has unbonded, its `GetLastValidatorPower` power returns 0. + // if `val` has unbonded, its `GetLastValidatorPower` power returns 0 m[pubKey.String()] = abci.ValidatorUpdate{ PubKey: pubKey, Power: k.stakingKeeper.GetLastValidatorPower(ctx, valAddress), @@ -218,7 +168,7 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, out = append(out, update) } - // Similarly to `AccumulateChanges`, we sort validators for determinism. + // similarly to `AccumulateChanges`, we sort validators for determinism sort.Slice(out, func(i, j int) bool { if out[i].Power != out[j].Power { return out[i].Power > out[j].Power @@ -229,9 +179,60 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, return out } +// ComputeNextValidators computes the next validator set that is responsible for validating on a consumer chain. +// The returned opted-in validators correspond to the next `currentValidators`. +func (k Keeper) ComputeNextValidators(ctx sdk.Context, + currentValidators []OptedInValidator, + validatorAddressesToAdd []types.ProviderConsAddress, + validatorAddressesToRemove []types.ProviderConsAddress, +) []OptedInValidator { + isRemoved := make(map[string]bool) + for _, val := range validatorAddressesToRemove { + isRemoved[val.ToSdkConsAddr().String()] = true + } + + var out []OptedInValidator + for _, val := range currentValidators { + if isRemoved[val.ProviderAddr.ToSdkConsAddr().String()] { + continue + } + valAddress, _, err := k.getValAddressAndPublicKey(ctx, val.ProviderAddr) + if err != nil { + continue + } + + val.Power = uint64(k.stakingKeeper.GetLastValidatorPower(ctx, valAddress)) + if val.Power == 0 { + continue + } + out = append(out, val) + } + + for _, addr := range validatorAddressesToAdd { + valAddress, _, err := k.getValAddressAndPublicKey(ctx, addr) + if err != nil { + continue + } + + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) + if !found { + continue + } + if !validator.IsBonded() { + continue + } + + power := uint64(k.stakingKeeper.GetLastValidatorPower(ctx, valAddress)) + // validator just opted in and hence sets `BlockHeight` as the current height + out = append(out, OptedInValidator{ProviderAddr: addr, BlockHeight: uint64(ctx.BlockHeight()), Power: power}) + } + + return out +} + // ResetCurrentValidators resets the opted-in validators with the newest set that was computed by -// `ComputePartialSetValidatorUpdates` and hence this method should only be called after -// `ComputePartialSetValidatorUpdates` has complete. Also, clears all the `ToBeOptedIn` and `ToBeOptedOut` sets. +// `ComputePartialSetValidatorUpdates` and hence this method should only be called after +// `ComputePartialSetValidatorUpdates` has complete. Method also clears all the `ToBeOptedIn` and `ToBeOptedOut` states. func (k Keeper) ResetCurrentValidators(ctx sdk.Context, chainID string, nextValidators []OptedInValidator) { k.DeleteAllOptedIn(ctx, chainID) for _, val := range nextValidators { diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index e02ae0a29b..3173250fd5 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "fmt" abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -200,14 +201,14 @@ func TestComputeValidatorUpdates(t *testing.T) { // validators 0 and 1 are removed and hence have a power of 0 {PubKey: pubKeys[0], Power: 0}, {PubKey: pubKeys[1], Power: 0}, - // validators 2 and 3 had a different power before and hence are also added + // validators 2 and 3 had a different power before and hence are also added with their latest power {PubKey: pubKeys[2], Power: 2}, {PubKey: pubKeys[3], Power: 3}, // validators 4, 5 are not added because their voting power did not change // validators 6 and 7 are bonded and are added {PubKey: pubKeys[6], Power: 6}, {PubKey: pubKeys[7], Power: 7}, - // validators 8 and 9 are unbonded, so they will not end up being added + // validators 8 and 9 are unbonded, so they are not added } actualValUpdates := providerKeeper.ComputeValidatorUpdates(ctx, @@ -313,12 +314,16 @@ func TestComputeNextValidators(t *testing.T) { } expectedValidators := []keeper.OptedInValidator{ + // validators 0 and 1 are removed + // validators 2 to 5 are still opted in and hence remain {ProviderAddr: providerAddresses[2], BlockHeight: uint64(1), Power: uint64(2)}, {ProviderAddr: providerAddresses[3], BlockHeight: uint64(1), Power: uint64(3)}, {ProviderAddr: providerAddresses[4], BlockHeight: uint64(1), Power: uint64(4)}, {ProviderAddr: providerAddresses[5], BlockHeight: uint64(1), Power: uint64(5)}, + // validators 6 and 7 are now opted in and hence have the latest `BlockHeight` {ProviderAddr: providerAddresses[6], BlockHeight: uint64(1000), Power: uint64(6)}, {ProviderAddr: providerAddresses[7], BlockHeight: uint64(1000), Power: uint64(7)}, + // validators 8 and 9 are unbonded, so they are not added } actualValidators := providerKeeper.ComputeNextValidators(ctx, @@ -346,3 +351,60 @@ func TestComputeNextValidators(t *testing.T) { require.Equal(t, expectedValidators, actualValidators) } + +func TestResetCurrentValidators(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // create 10 provider addresses + providerAddresses := make([]types.ProviderConsAddress, 10) + for i := 0; i < 10; i++ { + providerAddresses[i] = types.NewProviderConsAddress([]byte(fmt.Sprintf("providerAddr%d", i))) + } + + // opt in the first 5 validators + for i := 0; i < 5; i++ { + providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, 1) + } + require.NotEmpty(t, providerKeeper.GetOptedIn(ctx, chainID)) + + // set the remaining 5 validators as opted in + nextValidators := []keeper.OptedInValidator{ + {ProviderAddr: providerAddresses[6], BlockHeight: uint64(6), Power: uint64(6)}, + {ProviderAddr: providerAddresses[7], BlockHeight: uint64(7), Power: uint64(7)}, + {ProviderAddr: providerAddresses[8], BlockHeight: uint64(8), Power: uint64(8)}, + {ProviderAddr: providerAddresses[9], BlockHeight: uint64(9), Power: uint64(9)}, + } + + providerKeeper.ResetCurrentValidators(ctx, chainID, nextValidators) + + require.Empty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) + require.Empty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) + + // verify that the currently opted in validators (`actualValidators`) are the ones that were in `nextValidators` + actualValidators := providerKeeper.GetOptedIn(ctx, chainID) + + sort.Slice(actualValidators, func(i int, j int) bool { + if strings.Compare(actualValidators[i].ProviderAddr.String(), actualValidators[j].ProviderAddr.String()) == 0 { + if actualValidators[i].BlockHeight == actualValidators[j].BlockHeight { + return actualValidators[i].Power < actualValidators[j].Power + } + return actualValidators[i].BlockHeight < actualValidators[j].BlockHeight + } + return strings.Compare(actualValidators[i].ProviderAddr.String(), actualValidators[j].ProviderAddr.String()) < 0 + }) + + sort.Slice(nextValidators, func(i int, j int) bool { + if strings.Compare(nextValidators[i].ProviderAddr.String(), nextValidators[j].ProviderAddr.String()) == 0 { + if nextValidators[i].BlockHeight == nextValidators[j].BlockHeight { + return nextValidators[i].Power < nextValidators[j].Power + } + return nextValidators[i].BlockHeight < nextValidators[j].BlockHeight + } + return strings.Compare(nextValidators[i].ProviderAddr.String(), nextValidators[j].ProviderAddr.String()) < 0 + }) + + require.Equal(t, nextValidators, actualValidators) +} diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 9ad5ea514d..c63076284b 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -255,11 +255,11 @@ func (k Keeper) MakeConsumerGenesis( return false }) - //currentValidators := k.GetOptedIn(ctx, chainID) - //validatorsToAdd := k.GetToBeOptedIn(ctx, chainID) - //validatorsToRemove := k.GetToBeOptedOut(ctx, chainID) + // currentValidators := k.GetOptedIn(ctx, chainID) + // validatorsToAdd := k.GetToBeOptedIn(ctx, chainID) + // validatorsToRemove := k.GetToBeOptedOut(ctx, chainID) // - //initialUpdates := k.ComputeValidatorUpdates(ctx, currentValidators, validatorsToAdd, validatorsToRemove) + // initialUpdates := k.ComputeValidatorUpdates(ctx, currentValidators, validatorsToAdd, validatorsToRemove) initialUpdates := []abci.ValidatorUpdate{} for _, p := range lastPowers { @@ -287,6 +287,7 @@ func (k Keeper) MakeConsumerGenesis( // Apply key assignments to the initial valset. initialUpdatesWithConsumerKeys := k.MustApplyKeyAssignmentToValUpdates(ctx, chainID, initialUpdates, func(address types.ProviderConsAddress) bool { + // return k.IsOptedIn(ctx, chain.ChainId, address) return true }) diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 6f5c81a7ea..c0854abbca 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -218,22 +218,22 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { valUpdates := k.stakingKeeper.GetValidatorUpdates(ctx) for _, chain := range k.GetAllConsumerChains(ctx) { - currentValidators := k.GetOptedIn(ctx, chain.ChainId) - validatorsToAdd := k.GetToBeOptedIn(ctx, chain.ChainId) - validatorsToRemove := k.GetToBeOptedOut(ctx, chain.ChainId) - // FIXME(PSS) - //valUpdates := k.ComputeValidatorUpdates(ctx, currentValidators, validatorsToAdd, validatorsToRemove) + // currentValidators := k.GetOptedIn(ctx, chain.ChainId) + // validatorsToAdd := k.GetToBeOptedIn(ctx, chain.ChainId) + // validatorsToRemove := k.GetToBeOptedOut(ctx, chain.ChainId) - // Apply the key assignment to the validator updates. - considerKeyReplacement := func(address providertypes.ProviderConsAddress) bool { - return true - //return k.IsOptedIn(ctx, chain.ChainId, address) - } - valUpdates = k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, valUpdates, considerKeyReplacement) + // valUpdates := k.ComputeValidatorUpdates(ctx, currentValidators, validatorsToAdd, validatorsToRemove) - nextValidators := k.ComputeNextValidators(ctx, currentValidators, validatorsToAdd, validatorsToRemove) - k.ResetCurrentValidators(ctx, chain.ChainId, nextValidators) + // Apply the key assignment to the validator updates. + valUpdates = k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, valUpdates, + func(address providertypes.ProviderConsAddress) bool { + // return k.IsOptedIn(ctx, chain.ChainId, address) + return true + }) + + // nextValidators := k.ComputeNextValidators(ctx, currentValidators, validatorsToAdd, validatorsToRemove) + // k.ResetCurrentValidators(ctx, chain.ChainId, nextValidators) // check whether there are changes in the validator set; // note that this also entails unbonding operations From 6fa64fae9b329b4408aa59e31e8e6aa54443a85e Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 15 Feb 2024 10:44:04 +0100 Subject: [PATCH 08/15] nit changes --- x/ccv/provider/keeper/key_assignment_test.go | 5 ++++- x/ccv/provider/keeper/proposal.go | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index e532fb0108..cb8f245941 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -828,7 +828,10 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // and increment the provider vscid. applyUpdatesAndIncrementVSCID := func(updates []abci.ValidatorUpdate) { providerValset.apply(updates) - updates = k.MustApplyKeyAssignmentToValUpdates(ctx, CHAINID, updates, func(address types.ProviderConsAddress) bool { return true }) + updates = k.MustApplyKeyAssignmentToValUpdates(ctx, CHAINID, updates, + func(address types.ProviderConsAddress) bool { + return true + }) consumerValset.apply(updates) // Simulate the VSCID update in EndBlock k.IncrementValidatorSetUpdateId(ctx) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index c63076284b..0e54a5ca21 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -291,8 +291,8 @@ func (k Keeper) MakeConsumerGenesis( return true }) - //nextValidators := k.ComputeNextValidators(ctx, currentValidators, validatorsToAdd, validatorsToRemove) - //k.ResetCurrentValidators(ctx, chainID, nextValidators) + // nextValidators := k.ComputeNextValidators(ctx, currentValidators, validatorsToAdd, validatorsToRemove) + // k.ResetCurrentValidators(ctx, chainID, nextValidators) // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) From 20f9625b2b2d6f1452128f1d74b90ae94db39fbc Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 15 Feb 2024 11:45:44 +0100 Subject: [PATCH 09/15] nit changes --- x/ccv/provider/keeper/key_assignment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index 26da2f7f69..f397eb5f38 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -611,8 +611,8 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( for _, replacement := range k.GetAllKeyAssignmentReplacements(ctx, chainID) { providerAddr := types.NewProviderConsAddress(replacement.ProviderAddr) - // filter out key-assignment replacements if !considerKeyReplacement(providerAddr) { + // filter out key-assignment replacements continue } From 7a2b2d9def56d77252397fd01d4d354d43a8ad1a Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 15 Feb 2024 11:54:49 +0100 Subject: [PATCH 10/15] small typo --- x/ccv/provider/keeper/partial_set_security.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index d56f82ec30..dd5bfea871 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -232,7 +232,7 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, // ResetCurrentValidators resets the opted-in validators with the newest set that was computed by // `ComputePartialSetValidatorUpdates` and hence this method should only be called after -// `ComputePartialSetValidatorUpdates` has complete. Method also clears all the `ToBeOptedIn` and `ToBeOptedOut` states. +// `ComputePartialSetValidatorUpdates` has completed. Method also clears all the `ToBeOptedIn` and `ToBeOptedOut` states. func (k Keeper) ResetCurrentValidators(ctx sdk.Context, chainID string, nextValidators []OptedInValidator) { k.DeleteAllOptedIn(ctx, chainID) for _, val := range nextValidators { From 000760a8fcea67ff78f2b43aeaa41951080cbede Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 16 Feb 2024 08:52:30 +0100 Subject: [PATCH 11/15] Update x/ccv/provider/keeper/partial_set_security.go Co-authored-by: Marius Poke --- x/ccv/provider/keeper/partial_set_security.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index dd5bfea871..970c4588ed 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -188,7 +188,7 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, ) []OptedInValidator { isRemoved := make(map[string]bool) for _, val := range validatorAddressesToRemove { - isRemoved[val.ToSdkConsAddr().String()] = true + isRemoved[val.String()] = true } var out []OptedInValidator From 272b87bc9f4a226a0132a52276cf6de7fbae2587 Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 16 Feb 2024 08:52:37 +0100 Subject: [PATCH 12/15] Update x/ccv/provider/keeper/partial_set_security.go Co-authored-by: Marius Poke --- x/ccv/provider/keeper/partial_set_security.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 970c4588ed..2397b6eccb 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -193,7 +193,7 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, var out []OptedInValidator for _, val := range currentValidators { - if isRemoved[val.ProviderAddr.ToSdkConsAddr().String()] { + if isRemoved[val.ProviderAddr.String()] { continue } valAddress, _, err := k.getValAddressAndPublicKey(ctx, val.ProviderAddr) From 2bff04a52d5dcba5c4f4ef3abdf9ab254655cc07 Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 16 Feb 2024 08:53:45 +0100 Subject: [PATCH 13/15] rebase --- x/ccv/provider/keeper/keeper.go | 24 ++++-- x/ccv/provider/keeper/partial_set_security.go | 75 +++++++------------ .../keeper/partial_set_security_test.go | 6 +- x/ccv/provider/keeper/proposal.go | 11 +-- 4 files changed, 52 insertions(+), 64 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index aeb206a302..ddb1d9fd25 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1218,10 +1218,14 @@ func (k Keeper) DeleteAllOptedIn( store := ctx.KVStore(k.storeKey) key := types.ChainIdWithLenKey(types.OptedInBytePrefix, chainID) iterator := sdk.KVStorePrefixIterator(store, key) - defer iterator.Close() + var keysToDel [][]byte + defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - store.Delete(iterator.Key()) + keysToDel = append(keysToDel, iterator.Key()) + } + for _, delKey := range keysToDel { + store.Delete(delKey) } } @@ -1277,10 +1281,14 @@ func (k Keeper) DeleteAllToBeOptedIn( store := ctx.KVStore(k.storeKey) key := types.ChainIdWithLenKey(types.ToBeOptedInBytePrefix, chainID) iterator := sdk.KVStorePrefixIterator(store, key) - defer iterator.Close() + var keysToDel [][]byte + defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - store.Delete(iterator.Key()) + keysToDel = append(keysToDel, iterator.Key()) + } + for _, delKey := range keysToDel { + store.Delete(delKey) } } @@ -1334,10 +1342,14 @@ func (k Keeper) DeleteAllToBeOptedOut( store := ctx.KVStore(k.storeKey) key := types.ChainIdWithLenKey(types.ToBeOptedOutBytePrefix, chainID) iterator := sdk.KVStorePrefixIterator(store, key) - defer iterator.Close() + var keysToDel [][]byte + defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - store.Delete(iterator.Key()) + keysToDel = append(keysToDel, iterator.Key()) + } + for _, delKey := range keysToDel { + store.Delete(delKey) } } diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 2397b6eccb..deb09fde18 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -3,9 +3,7 @@ package keeper import ( errorsmod "cosmossdk.io/errors" abci "github.com/cometbft/cometbft/abci/types" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" 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" "sort" ) @@ -75,28 +73,6 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types return nil } -// getValAddressAndPublicKey is a helper function that returns the `ValAddress` and the public key of -// the corresponding validator -func (k Keeper) getValAddressAndPublicKey(ctx sdk.Context, addr types.ProviderConsAddress, -) (sdk.ValAddress, tmprotocrypto.PublicKey, error) { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) - if !found { - return sdk.ValAddress{}, tmprotocrypto.PublicKey{}, stakingtypes.ErrNoValidatorFound - } - - consAddr, err := validator.GetConsAddr() - if err != nil { - return sdk.ValAddress{}, tmprotocrypto.PublicKey{}, err - } - - pubKey := tmprotocrypto.PublicKey{ - Sum: &tmprotocrypto.PublicKey_Ed25519{ - Ed25519: consAddr.Bytes(), - }, - } - return validator.GetOperator(), pubKey, nil -} - // ComputeValidatorUpdates computes the validator updates needed to be sent to the consumer chain to capture // the newly opted-in and opted-out validators, as well as validators that unbonded. func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, @@ -107,52 +83,58 @@ func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, var m = make(map[string]abci.ValidatorUpdate) for _, val := range currentValidators { - valAddress, pubKey, err := k.getValAddressAndPublicKey(ctx, val.ProviderAddr) - if err != nil { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) + if !found { continue } - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) - if !found { + pubKey, err := validator.TmConsPublicKey() + if err != nil { continue } // if the voting power did not change since the last time the validator opted in, do not create an update - if val.Power == uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) { + currentValPower := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) + if val.Power == uint64(currentValPower) { continue } // if `val` has unbonded, its `GetLastValidatorPower` power returns 0 m[pubKey.String()] = abci.ValidatorUpdate{ PubKey: pubKey, - Power: k.stakingKeeper.GetLastValidatorPower(ctx, valAddress), + Power: currentValPower, } } for _, addr := range validatorAddressesToAdd { - valAddress, pubKey, err := k.getValAddressAndPublicKey(ctx, addr) - if err != nil { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) + if !found { continue } - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) - if !found { + pubKey, err := validator.TmConsPublicKey() + if err != nil { continue } - // if a validator is in the active set, we do not add it + // if a validator is not in the active set, we do not add it if !validator.IsBonded() { continue } m[pubKey.String()] = abci.ValidatorUpdate{ PubKey: pubKey, - Power: k.stakingKeeper.GetLastValidatorPower(ctx, valAddress), + Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), } } for _, addr := range validatorAddressesToRemove { - _, pubKey, err := k.getValAddressAndPublicKey(ctx, addr) + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) + if !found { + continue + } + + pubKey, err := validator.TmConsPublicKey() if err != nil { continue } @@ -196,12 +178,13 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, if isRemoved[val.ProviderAddr.String()] { continue } - valAddress, _, err := k.getValAddressAndPublicKey(ctx, val.ProviderAddr) - if err != nil { + + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) + if !found { continue } - val.Power = uint64(k.stakingKeeper.GetLastValidatorPower(ctx, valAddress)) + val.Power = uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) if val.Power == 0 { continue } @@ -209,20 +192,16 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, } for _, addr := range validatorAddressesToAdd { - valAddress, _, err := k.getValAddressAndPublicKey(ctx, addr) - if err != nil { - continue - } - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) if !found { continue } + if !validator.IsBonded() { continue } - power := uint64(k.stakingKeeper.GetLastValidatorPower(ctx, valAddress)) + power := uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) // validator just opted in and hence sets `BlockHeight` as the current height out = append(out, OptedInValidator{ProviderAddr: addr, BlockHeight: uint64(ctx.BlockHeight()), Power: power}) } @@ -231,8 +210,8 @@ func (k Keeper) ComputeNextValidators(ctx sdk.Context, } // ResetCurrentValidators resets the opted-in validators with the newest set that was computed by -// `ComputePartialSetValidatorUpdates` and hence this method should only be called after -// `ComputePartialSetValidatorUpdates` has completed. Method also clears all the `ToBeOptedIn` and `ToBeOptedOut` states. +// `ComputeNextValidators` and hence this method should only be called after +// `ComputeNextValidators` has completed. Method also clears all the `ToBeOptedIn` and `ToBeOptedOut` states. func (k Keeper) ResetCurrentValidators(ctx sdk.Context, chainID string, nextValidators []OptedInValidator) { k.DeleteAllOptedIn(ctx, chainID) for _, val := range nextValidators { diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 3173250fd5..177d195ae9 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -154,11 +154,7 @@ func TestComputeValidatorUpdates(t *testing.T) { Status: status, } - pubKey := tmprotocrypto.PublicKey{ - Sum: &tmprotocrypto.PublicKey_Ed25519{ - Ed25519: consAddr.Bytes(), - }, - } + pubKey, _ := validator.TmConsPublicKey() pubKeys = append(pubKeys, pubKey) mocks.MockStakingKeeper.EXPECT(). diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 0e54a5ca21..0ee895188f 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -373,12 +373,13 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { propsToExecute := k.GetConsumerAdditionPropsToExecute(ctx) for _, prop := range propsToExecute { - // Only set Top N at the moment a chain starts. If we were to do this earlier (e.g., during the proposal), - // then someone could create a bogus ConsumerAdditionProposal to override the Top N for a specific chain. - k.SetTopN(ctx, prop.ChainId, prop.Top_N) + cachedCtx, writeFn := ctx.CacheContext() + + // Set in a cached context. It would only be written if the consumer client creation is successful. + k.SetTopN(cachedCtx, prop.ChainId, prop.Top_N) // FIXME(PSS) - //if k.IsOptIn(ctx, prop.ChainId) && len(k.GetToBeOptedIn(ctx, prop.ChainId)) == 0 { + //if k.IsOptIn(cachedCtx, prop.ChainId) && len(k.GetToBeOptedIn(cachedCtx, prop.ChainId)) == 0 { // // drop the proposal // ctx.Logger().Info("could not start Opt In consumer chain (%s) because no validator has opted in", // prop.ChainId) @@ -386,7 +387,7 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { //} // create consumer client in a cached context to handle errors - cachedCtx, writeFn, err := k.CreateConsumerClientInCachedCtx(ctx, prop) + err := k.CreateConsumerClient(cachedCtx, &prop) if err != nil { // drop the proposal ctx.Logger().Info("consumer client could not be created: %w", err) From 5348f837a741c7ca1c559a28e033a0ffb6ce313b Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 16 Feb 2024 10:17:27 +0100 Subject: [PATCH 14/15] add godocs --- x/ccv/provider/keeper/keeper.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index ddb1d9fd25..f88b6f5ef1 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1185,6 +1185,7 @@ func (k Keeper) IsOptIn(ctx sdk.Context, chainID string) bool { return !found || topN == 0 } +// SetOptedIn sets validator `providerAddr` as opted in with the given `blockHeight` and `power` func (k Keeper) SetOptedIn( ctx sdk.Context, chainID string, @@ -1203,6 +1204,7 @@ func (k Keeper) SetOptedIn( store.Set(types.OptedInKey(chainID, providerAddr), append(blockHeightBytes, powerBytes...)) } +// DeleteOptedIn deletes opted-in validator `providerAddr` func (k Keeper) DeleteOptedIn( ctx sdk.Context, chainID string, @@ -1212,6 +1214,7 @@ func (k Keeper) DeleteOptedIn( store.Delete(types.OptedInKey(chainID, providerAddr)) } +// DeleteAllOptedIn deletes all opted-in validators func (k Keeper) DeleteAllOptedIn( ctx sdk.Context, chainID string) { @@ -1229,6 +1232,7 @@ func (k Keeper) DeleteAllOptedIn( } } +// IsOptedIn returns `true` if the validator with `providerAddr` is opted in and `false` otherwise func (k Keeper) IsOptedIn( ctx sdk.Context, chainID string, @@ -1238,6 +1242,7 @@ func (k Keeper) IsOptedIn( return store.Get(types.OptedInKey(chainID, providerAddr)) != nil } +// GetOptedIn returns all the opted-in validators on chain `chainID` func (k Keeper) GetOptedIn( ctx sdk.Context, chainID string) (optedInValidators []OptedInValidator) { @@ -1257,6 +1262,7 @@ func (k Keeper) GetOptedIn( return optedInValidators } +// SetToBeOptedIn sets validator `providerAddr` as to be opted in func (k Keeper) SetToBeOptedIn( ctx sdk.Context, chainID string, @@ -1266,6 +1272,7 @@ func (k Keeper) SetToBeOptedIn( store.Set(types.ToBeOptedInKey(chainID, providerAddr), []byte{}) } +// DeleteToBeOptedIn deletes to-be-opted-in validator `providerAddr` func (k Keeper) DeleteToBeOptedIn( ctx sdk.Context, chainID string, @@ -1275,6 +1282,7 @@ func (k Keeper) DeleteToBeOptedIn( store.Delete(types.ToBeOptedInKey(chainID, providerAddr)) } +// DeleteAllToBeOptedIn deletes all to-be-opted-in validators func (k Keeper) DeleteAllToBeOptedIn( ctx sdk.Context, chainID string) { @@ -1292,6 +1300,7 @@ func (k Keeper) DeleteAllToBeOptedIn( } } +// IsToBeOptedIn returns `true` if the validator with `providerAddr` is to be opted in and `false` otherwise func (k Keeper) IsToBeOptedIn( ctx sdk.Context, chainID string, @@ -1301,6 +1310,7 @@ func (k Keeper) IsToBeOptedIn( return store.Get(types.ToBeOptedInKey(chainID, providerAddr)) != nil } +// GetToBeOptedIn returns all the to-be-opted-in validators on chain `chainID` func (k Keeper) GetToBeOptedIn( ctx sdk.Context, chainID string) (addresses []types.ProviderConsAddress) { @@ -1318,6 +1328,7 @@ func (k Keeper) GetToBeOptedIn( return addresses } +// SetToBeOptedOut sets validator `providerAddr` as to be opted out func (k Keeper) SetToBeOptedOut( ctx sdk.Context, chainID string, @@ -1327,6 +1338,7 @@ func (k Keeper) SetToBeOptedOut( store.Set(types.ToBeOptedOutKey(chainID, providerAddr), []byte{}) } +// DeleteToBeOptedOut deletes to-be-opted-out validator `providerAddr` func (k Keeper) DeleteToBeOptedOut( ctx sdk.Context, chainID string, @@ -1336,6 +1348,7 @@ func (k Keeper) DeleteToBeOptedOut( store.Delete(types.ToBeOptedOutKey(chainID, providerAddr)) } +// DeleteAllToBeOptedOut deletes all to-be-opted-out validators func (k Keeper) DeleteAllToBeOptedOut( ctx sdk.Context, chainID string) { @@ -1353,6 +1366,7 @@ func (k Keeper) DeleteAllToBeOptedOut( } } +// IsToBeOptedOut returns `true` if the validator with `providerAddr` is to be opted out and `false` otherwise func (k Keeper) IsToBeOptedOut( ctx sdk.Context, chainID string, @@ -1362,6 +1376,7 @@ func (k Keeper) IsToBeOptedOut( return store.Get(types.ToBeOptedOutKey(chainID, providerAddr)) != nil } +// GetToBeOptedOut returns all the to-be-opted-out validators on chain `chainID` func (k Keeper) GetToBeOptedOut( ctx sdk.Context, chainID string) (addresses []types.ProviderConsAddress) { From 71445041264c8afac7c04d87258f56272529b698 Mon Sep 17 00:00:00 2001 From: insumity Date: Mon, 19 Feb 2024 12:29:31 +0100 Subject: [PATCH 15/15] revamp --- .../ccv/provider/v1/provider.proto | 8 + x/ccv/provider/keeper/keeper.go | 45 +- x/ccv/provider/keeper/keeper_test.go | 55 +- x/ccv/provider/keeper/key_assignment.go | 15 +- x/ccv/provider/keeper/partial_set_security.go | 208 ++++--- .../keeper/partial_set_security_test.go | 126 +++-- x/ccv/provider/keeper/proposal.go | 8 +- x/ccv/provider/keeper/relay.go | 9 +- x/ccv/provider/types/keys.go | 4 +- x/ccv/provider/types/provider.pb.go | 509 ++++++++++++++---- 10 files changed, 654 insertions(+), 333 deletions(-) diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 38be02864f..4cc5438d7d 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -300,3 +300,11 @@ message ConsumerAddrsToPrune { uint64 vsc_id = 2; AddressList consumer_addrs = 3; } + + +message OptedInValidator { + bytes provider_addr = 1; + int64 block_height = 2; + int64 power = 3; + bytes public_key = 4; +} diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index f88b6f5ef1..85ae61b6c5 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1189,19 +1189,15 @@ func (k Keeper) IsOptIn(ctx sdk.Context, chainID string) bool { func (k Keeper) SetOptedIn( ctx sdk.Context, chainID string, - providerAddr types.ProviderConsAddress, - blockHeight uint64, - power uint64, + validator types.OptedInValidator, ) { store := ctx.KVStore(k.storeKey) + bz, err := validator.Marshal() + if err != nil { + panic(fmt.Errorf("failed to marshal OptedInValidator: %w", err)) + } - blockHeightBytes := make([]byte, 8) - binary.BigEndian.PutUint64(blockHeightBytes, blockHeight) - - powerBytes := make([]byte, 8) - binary.BigEndian.PutUint64(powerBytes, power) - - store.Set(types.OptedInKey(chainID, providerAddr), append(blockHeightBytes, powerBytes...)) + store.Set(types.OptedInKey(chainID, validator.ProviderAddr), bz) } // DeleteOptedIn deletes opted-in validator `providerAddr` @@ -1211,7 +1207,7 @@ func (k Keeper) DeleteOptedIn( providerAddr types.ProviderConsAddress, ) { store := ctx.KVStore(k.storeKey) - store.Delete(types.OptedInKey(chainID, providerAddr)) + store.Delete(types.OptedInKey(chainID, providerAddr.ToSdkConsAddr())) } // DeleteAllOptedIn deletes all opted-in validators @@ -1239,24 +1235,25 @@ func (k Keeper) IsOptedIn( providerAddr types.ProviderConsAddress, ) bool { store := ctx.KVStore(k.storeKey) - return store.Get(types.OptedInKey(chainID, providerAddr)) != nil + return store.Get(types.OptedInKey(chainID, providerAddr.ToSdkConsAddr())) != nil } -// GetOptedIn returns all the opted-in validators on chain `chainID` -func (k Keeper) GetOptedIn( +// GetAllOptedIn returns all the opted-in validators on chain `chainID` +func (k Keeper) GetAllOptedIn( ctx sdk.Context, - chainID string) (optedInValidators []OptedInValidator) { + chainID string) (optedInValidators []types.OptedInValidator) { store := ctx.KVStore(k.storeKey) key := types.ChainIdWithLenKey(types.OptedInBytePrefix, chainID) iterator := sdk.KVStorePrefixIterator(store, key) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - optedInValidators = append(optedInValidators, OptedInValidator{ - ProviderAddr: types.NewProviderConsAddress(iterator.Key()[len(key):]), - BlockHeight: binary.BigEndian.Uint64(iterator.Value()[0:8]), - Power: binary.BigEndian.Uint64(iterator.Value()[8:]), - }) + 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) } return optedInValidators @@ -1310,8 +1307,8 @@ func (k Keeper) IsToBeOptedIn( return store.Get(types.ToBeOptedInKey(chainID, providerAddr)) != nil } -// GetToBeOptedIn returns all the to-be-opted-in validators on chain `chainID` -func (k Keeper) GetToBeOptedIn( +// GetAllToBeOptedIn returns all the to-be-opted-in validators on chain `chainID` +func (k Keeper) GetAllToBeOptedIn( ctx sdk.Context, chainID string) (addresses []types.ProviderConsAddress) { @@ -1376,8 +1373,8 @@ func (k Keeper) IsToBeOptedOut( return store.Get(types.ToBeOptedOutKey(chainID, providerAddr)) != nil } -// GetToBeOptedOut returns all the to-be-opted-out validators on chain `chainID` -func (k Keeper) GetToBeOptedOut( +// GetAllToBeOptedOut returns all the to-be-opted-out validators on chain `chainID` +func (k Keeper) GetAllToBeOptedOut( ctx sdk.Context, chainID string) (addresses []types.ProviderConsAddress) { diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index a3605a31fe..fc1aa42a21 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -3,7 +3,6 @@ package keeper_test import ( "bytes" "fmt" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" "sort" "testing" "time" @@ -662,43 +661,49 @@ func TestTopN(t *testing.T) { } } -func TestGetOptedIn(t *testing.T) { +func TestGetAllOptedIn(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - expectedOptedInValidators := []keeper.OptedInValidator{ + expectedOptedInValidators := []types.OptedInValidator{ { - ProviderAddr: types.NewProviderConsAddress([]byte("providerAddr1")), + ProviderAddr: []byte("providerAddr1"), BlockHeight: 1, Power: 2, + PublicKey: []byte{3}, }, { - ProviderAddr: types.NewProviderConsAddress([]byte("providerAddr2")), + ProviderAddr: []byte("providerAddr2"), BlockHeight: 2, Power: 3, + PublicKey: []byte{4}, }, { - ProviderAddr: types.NewProviderConsAddress([]byte("providerAddr3")), + ProviderAddr: []byte("providerAddr3"), BlockHeight: 3, Power: 4, + PublicKey: []byte{5}, }, } for _, expectedOptedInValidator := range expectedOptedInValidators { providerKeeper.SetOptedIn(ctx, "chainID", - expectedOptedInValidator.ProviderAddr, expectedOptedInValidator.BlockHeight, - expectedOptedInValidator.Power) + types.OptedInValidator{ + ProviderAddr: expectedOptedInValidator.ProviderAddr, + BlockHeight: expectedOptedInValidator.BlockHeight, + Power: expectedOptedInValidator.Power, + PublicKey: expectedOptedInValidator.PublicKey}) } - actualOptedInValidators := providerKeeper.GetOptedIn(ctx, "chainID") + actualOptedInValidators := providerKeeper.GetAllOptedIn(ctx, "chainID") // sort validators first to be able to compare - sortOptedInValidators := func(optedInValidators []keeper.OptedInValidator) { + sortOptedInValidators := func(optedInValidators []types.OptedInValidator) { sort.Slice(optedInValidators, func(i int, j int) bool { a := optedInValidators[i] b := optedInValidators[j] return a.BlockHeight < b.BlockHeight || - (a.BlockHeight == b.BlockHeight && bytes.Compare(a.ProviderAddr.ToSdkConsAddr(), b.ProviderAddr.ToSdkConsAddr()) < 0) + (a.BlockHeight == b.BlockHeight && bytes.Compare(a.ProviderAddr, b.ProviderAddr) < 0) }) } sortOptedInValidators(expectedOptedInValidators) @@ -711,20 +716,20 @@ func TestOptedIn(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - optedInValidator := keeper.OptedInValidator{ - ProviderAddr: types.NewProviderConsAddress([]byte("providerAddr")), - BlockHeight: 1, - Power: 2, + optedInValidator := types.OptedInValidator{ProviderAddr: []byte("providerAddr"), + BlockHeight: 1, + Power: 2, + PublicKey: []byte{3}, } - require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator.ProviderAddr)) - providerKeeper.SetOptedIn(ctx, "chainID", optedInValidator.ProviderAddr, optedInValidator.BlockHeight, optedInValidator.Power) - require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator.ProviderAddr)) - providerKeeper.DeleteOptedIn(ctx, "chainID", optedInValidator.ProviderAddr) - require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator.ProviderAddr)) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", types.NewProviderConsAddress(optedInValidator.ProviderAddr))) + 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 TestGetToBeOptedIn(t *testing.T) { +func TestGetAllToBeOptedIn(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() @@ -737,7 +742,7 @@ func TestGetToBeOptedIn(t *testing.T) { providerKeeper.SetToBeOptedIn(ctx, "chainID", addr) } - actualAddresses := providerKeeper.GetToBeOptedIn(ctx, "chainID") + actualAddresses := providerKeeper.GetAllToBeOptedIn(ctx, "chainID") // sort addresses first to be able to compare sortAddresses := func(addresses []types.ProviderConsAddress) { @@ -771,7 +776,7 @@ func TestBeOptedIn(t *testing.T) { providerKeeper.SetToBeOptedIn(ctx, "chainID", addr) } - actualAddresses := providerKeeper.GetToBeOptedIn(ctx, "chainID") + actualAddresses := providerKeeper.GetAllToBeOptedIn(ctx, "chainID") // sort addresses first to be able to compare sortAddresses := func(addresses []types.ProviderConsAddress) { @@ -806,7 +811,7 @@ func TestToBeOptedIn(t *testing.T) { require.False(t, providerKeeper.IsToBeOptedIn(ctx, "chainID", providerAddr)) } -func TestGetToBeOptedOut(t *testing.T) { +func TestGetAllToBeOptedOut(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() @@ -819,7 +824,7 @@ func TestGetToBeOptedOut(t *testing.T) { providerKeeper.SetToBeOptedOut(ctx, "chainID", addr) } - actualAddresses := providerKeeper.GetToBeOptedOut(ctx, "chainID") + actualAddresses := providerKeeper.GetAllToBeOptedOut(ctx, "chainID") // sort addresses first to be able to compare sortAddresses := func(addresses []types.ProviderConsAddress) { diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index f397eb5f38..ee6eabe538 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -502,7 +502,6 @@ func (k Keeper) AssignConsumerKey( oldConsumerKey = providerKey } - // check whether the validator is valid, i.e., its power is positive power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) if 0 < power { // to enable multiple calls of AssignConsumerKey in the same block by the same validator @@ -547,15 +546,13 @@ func (k Keeper) AssignConsumerKey( return nil } -// MustApplyKeyAssignmentToValUpdates applies the key assignment to the validator updates received from the -// staking module. For validators that do not have a validator update in `valUpdates`, the method also considers -// key-assignment replacements when the `considerKeyReplacement` predicate evaluates to `true` for this validator. -// The method panics if the key-assignment state is corrupted. +// MustApplyKeyAssignmentToValUpdates applies the key assignment to the validator updates and the key-assignment +// replacements provided. The method panics if the key-assignment state is corrupted. func (k Keeper) MustApplyKeyAssignmentToValUpdates( ctx sdk.Context, chainID string, valUpdates []abci.ValidatorUpdate, - considerKeyReplacement func(address types.ProviderConsAddress) bool, + isOptedIn func(address types.ProviderConsAddress) bool, ) (newUpdates []abci.ValidatorUpdate) { for _, valUpdate := range valUpdates { providerAddrTmp, err := ccvtypes.TMCryptoPublicKeyToConsAddr(valUpdate.PubKey) @@ -570,10 +567,13 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( // - and setting the new consumer key's power to the power in the update prevConsumerKey, _, found := k.GetKeyAssignmentReplacement(ctx, chainID, providerAddr) if found { + //if isOptedIn(providerAddr) && !isToBeOptedOut(providerAddr) { + // only generate a removal (i.e., power 0) if the validator was previously opted in newUpdates = append(newUpdates, abci.ValidatorUpdate{ PubKey: prevConsumerKey, Power: 0, }) + //} newConsumerKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) if !found { @@ -611,8 +611,7 @@ func (k Keeper) MustApplyKeyAssignmentToValUpdates( for _, replacement := range k.GetAllKeyAssignmentReplacements(ctx, chainID) { providerAddr := types.NewProviderConsAddress(replacement.ProviderAddr) - if !considerKeyReplacement(providerAddr) { - // filter out key-assignment replacements + if !isOptedIn(providerAddr) { continue } diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index deb09fde18..a8acaf3d57 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -2,20 +2,13 @@ package keeper import ( errorsmod "cosmossdk.io/errors" + "fmt" abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/proto/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - "sort" ) -type OptedInValidator struct { - ProviderAddr types.ProviderConsAddress - // block height the validator opted in at - BlockHeight uint64 - // power the validator had when it opted in - Power uint64 -} - func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, consumerKey *string) error { if !k.IsConsumerProposedOrRegistered(ctx, chainID) { return errorsmod.Wrapf( @@ -73,149 +66,152 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types return nil } -// ComputeValidatorUpdates computes the validator updates needed to be sent to the consumer chain to capture -// the newly opted-in and opted-out validators, as well as validators that unbonded. -func (k Keeper) ComputeValidatorUpdates(ctx sdk.Context, - currentValidators []OptedInValidator, +// ComputeValidatorUpdatesAndNextValidators returns the validator updates needed to be sent to the consumer chain to +// capture the newly opted-in and opted-out validators, as well as validators that unbonded. It also computes the +// next validator set that is responsible for validating on a consumer chain. +func (k Keeper) ComputeValidatorUpdatesAndNextValidators(ctx sdk.Context, + chainID string, + currentValidators []types.OptedInValidator, validatorAddressesToAdd []types.ProviderConsAddress, validatorAddressesToRemove []types.ProviderConsAddress, -) []abci.ValidatorUpdate { - var m = make(map[string]abci.ValidatorUpdate) +) (updates []abci.ValidatorUpdate, nextValidators []types.OptedInValidator) { + // store in a map all the validators that are to be removed, so we can filter out those + // validators when we go through `currentValidators` + isRemoved := make(map[string]bool) + for _, addr := range validatorAddressesToRemove { + isRemoved[addr.String()] = true + } + // go through all opted-in validators and look in the following order: + // - if the validator has opted out, generate a 0-power update + // - if the validator is not bonded anymore, generate a 0-power update + // - if the validator has changed its consumer key since last epoch, generate a 0-power update for the old key + // and generate an update with the new power and the new key + // - if the validator has not changed its consumer key but has changed its voting power since last epoch, + // generate an update with the new power + // - if validator has not changed its consumer key or its voting power since, then do not generate an update for _, val := range currentValidators { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) - if !found { - continue - } - - pubKey, err := validator.TmConsPublicKey() + // `currentPublicKey` is the currently used key by validator `val` when validating on the consumer chain + var currentPublicKey crypto.PublicKey + err := currentPublicKey.Unmarshal(val.PublicKey) if err != nil { - continue + // this should never happen and is not recoverable because without the public key + // we cannot generate a validator update to remove this validator + panic(fmt.Errorf("could not unmarshall public key (%s): %w", val.PublicKey, err)) } - // if the voting power did not change since the last time the validator opted in, do not create an update - currentValPower := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) - if val.Power == uint64(currentValPower) { + providerAddr := types.NewProviderConsAddress(val.ProviderAddr) + if isRemoved[providerAddr.String()] { + // if the validator is removed, then generate a 0-power update to remove + // validator `val` from the consumer chain + updates = append(updates, abci.ValidatorUpdate{PubKey: currentPublicKey, Power: 0}) continue } - // if `val` has unbonded, its `GetLastValidatorPower` power returns 0 - m[pubKey.String()] = abci.ValidatorUpdate{ - PubKey: pubKey, - Power: currentValPower, - } - } - - for _, addr := range validatorAddressesToAdd { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr) if !found { + // This should never happen because when `val` was added as an opt-in validator it was bonded + // and for it not to be found it means that it fully unbonded after an unbonding period. Assuming + // an epoch is smaller than the unbonding period, we would have already removed this validator from + // an opted-in validator. In any case, we can still recover in this case by sending a 0-power update. + k.Logger(ctx).Error("validator with consensus address (%s) could not be found", val.ProviderAddr) + updates = append(updates, abci.ValidatorUpdate{PubKey: currentPublicKey, Power: 0}) continue } - pubKey, err := validator.TmConsPublicKey() - if err != nil { + nextPower := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) + if nextPower == 0 { + // if the validator is not bonded anymore, then send an update with 0 power to remove validator + updates = append(updates, abci.ValidatorUpdate{PubKey: currentPublicKey, Power: 0}) continue } - // if a validator is not in the active set, we do not add it - if !validator.IsBonded() { - continue - } - - m[pubKey.String()] = abci.ValidatorUpdate{ - PubKey: pubKey, - Power: k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()), - } - } - - for _, addr := range validatorAddressesToRemove { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) + // get the consumer key the validator intends to use to validate on the consumer chain + nextPublicKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(val.ProviderAddr)) if !found { - continue - } - - pubKey, err := validator.TmConsPublicKey() - if err != nil { - continue + // if not found, then the current consumer key is the same + // as the one the validator used when it first opted in + nextPublicKey = currentPublicKey } - m[pubKey.String()] = abci.ValidatorUpdate{ - PubKey: pubKey, - Power: 0, + if !nextPublicKey.Equal(currentPublicKey) { + // if the validator has changed its consumer key since last it opted in, then generate a 0-power update + // for the old consumer key, and an update with the new key with the new power + updates = append(updates, abci.ValidatorUpdate{PubKey: currentPublicKey, Power: 0}) + updates = append(updates, abci.ValidatorUpdate{PubKey: nextPublicKey, Power: nextPower}) + } else if val.Power != nextPower { + // otherwise, only send an update if the power has changed since the last epoch + updates = append(updates, abci.ValidatorUpdate{PubKey: nextPublicKey, Power: nextPower}) } - } - var out []abci.ValidatorUpdate - for _, update := range m { - out = append(out, update) - } - - // similarly to `AccumulateChanges`, we sort validators for determinism - sort.Slice(out, func(i, j int) bool { - if out[i].Power != out[j].Power { - return out[i].Power > out[j].Power + // update validator with the new power the validator has + // at the end of this epoch and with the new key + val.Power = nextPower + val.PublicKey, err = nextPublicKey.Marshal() + if err != nil { + // this should never happen and would lead to `panic` later on when we try to `Unmarshal` the key + panic(fmt.Errorf("could not marshal public key (%s): %w", nextPublicKey, err)) } - return out[i].PubKey.String() > out[j].PubKey.String() - }) - return out -} - -// ComputeNextValidators computes the next validator set that is responsible for validating on a consumer chain. -// The returned opted-in validators correspond to the next `currentValidators`. -func (k Keeper) ComputeNextValidators(ctx sdk.Context, - currentValidators []OptedInValidator, - validatorAddressesToAdd []types.ProviderConsAddress, - validatorAddressesToRemove []types.ProviderConsAddress, -) []OptedInValidator { - isRemoved := make(map[string]bool) - for _, val := range validatorAddressesToRemove { - isRemoved[val.String()] = true + // validator `val` was not removed, so it would still be part of the next opted-in validators + nextValidators = append(nextValidators, val) } - var out []OptedInValidator - for _, val := range currentValidators { - if isRemoved[val.ProviderAddr.String()] { - continue - } - - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, val.ProviderAddr.ToSdkConsAddr()) + // go through all to-be-opted-in validators and look in the following order: + // - if the validator is not in the active set, do not generate an update for it + for _, addr := range validatorAddressesToAdd { + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) if !found { + k.Logger(ctx).Info("validator (%s) that was to-be-opted-in cannot be found anymore", addr) continue } - val.Power = uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) - if val.Power == 0 { + if !validator.IsBonded() { + // if the validator is not bonded anymore and hence not in the active set we do not add it continue } - out = append(out, val) - } - for _, addr := range validatorAddressesToAdd { - validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, addr.ToSdkConsAddr()) + // for a validator that just opts in, we can immediately use the next key + nextPublicKey, found := k.GetValidatorConsumerPubKey(ctx, chainID, addr) if !found { - continue + var err error + nextPublicKey, err = validator.TmConsPublicKey() + if err != nil { + k.Logger(ctx).Error("could not retrieve public key from validator (%s)", addr) + continue + } } - if !validator.IsBonded() { - continue - } + // for a validator that just opts in, we can immediately use the next power + nextPower := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) + updates = append(updates, abci.ValidatorUpdate{PubKey: nextPublicKey, Power: nextPower}) - power := uint64(k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator())) - // validator just opted in and hence sets `BlockHeight` as the current height - out = append(out, OptedInValidator{ProviderAddr: addr, BlockHeight: uint64(ctx.BlockHeight()), Power: power}) + nextPublicKeyBytes, err := nextPublicKey.Marshal() + if err != nil { + // this should never happen and would lead to `panic` later on when we try to `Unmarshal` the key + panic(fmt.Errorf("could not marshal public key (%s): %w", nextPublicKey, err)) + } + + nextValidators = append(nextValidators, + types.OptedInValidator{ + ProviderAddr: addr.ToSdkConsAddr(), + BlockHeight: ctx.BlockHeight(), + // validator that just opted-in would be using next power and next key for the upcoming epoch + Power: nextPower, + PublicKey: nextPublicKeyBytes, + }) } - return out + return updates, nextValidators } // ResetCurrentValidators resets the opted-in validators with the newest set that was computed by // `ComputeNextValidators` and hence this method should only be called after // `ComputeNextValidators` has completed. Method also clears all the `ToBeOptedIn` and `ToBeOptedOut` states. -func (k Keeper) ResetCurrentValidators(ctx sdk.Context, chainID string, nextValidators []OptedInValidator) { +func (k Keeper) ResetCurrentValidators(ctx sdk.Context, chainID string, nextValidators []types.OptedInValidator) { k.DeleteAllOptedIn(ctx, chainID) for _, val := range nextValidators { - k.SetOptedIn(ctx, chainID, val.ProviderAddr, val.BlockHeight, val.Power) + k.SetOptedIn(ctx, chainID, val) } k.DeleteAllToBeOptedIn(ctx, chainID) diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 177d195ae9..0a69916922 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "bytes" "fmt" abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" @@ -9,13 +10,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "sort" - "strings" "testing" ) @@ -45,7 +44,9 @@ func TestHandleOptIn(t *testing.T) { 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", providerAddr, 1, 2) + + 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)) } @@ -127,8 +128,11 @@ func TestComputeValidatorUpdates(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + chainID := "chainID" + // create 10 validators, where the first 8 are bonded and the last 2 are unbonded var providerAddresses []types.ProviderConsAddress + var pubKeysBytes [][]byte var pubKeys []tmprotocrypto.PublicKey for i := 0; i < 10; i++ { // generate a consensus public key for the provider @@ -155,6 +159,8 @@ func TestComputeValidatorUpdates(t *testing.T) { } pubKey, _ := validator.TmConsPublicKey() + pubKeyBytes, _ := pubKey.Marshal() + pubKeysBytes = append(pubKeysBytes, pubKeyBytes) pubKeys = append(pubKeys, pubKey) mocks.MockStakingKeeper.EXPECT(). @@ -162,22 +168,28 @@ func TestComputeValidatorUpdates(t *testing.T) { mocks.MockStakingKeeper.EXPECT(). GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() + + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, providerAddresses[i], pubKey) } // set first 6 validators as currently opted in where validators 1, 2, and 3 have a // different power now than the power they had when they opted in - var currentValidators []keeper.OptedInValidator - for i := 0; i < 6; i++ { + var currentValidators []types.OptedInValidator + for i := byte(0); i < 6; i++ { if i > 0 && i < 4 { currentValidators = append(currentValidators, - keeper.OptedInValidator{ProviderAddr: providerAddresses[i], + types.OptedInValidator{ProviderAddr: providerAddresses[i].ToSdkConsAddr(), BlockHeight: 1, - Power: uint64(i + 1)}) + Power: int64(i + 1), + PublicKey: pubKeysBytes[i], + }) } else { currentValidators = append(currentValidators, - keeper.OptedInValidator{ProviderAddr: providerAddresses[i], + types.OptedInValidator{ProviderAddr: providerAddresses[i].ToSdkConsAddr(), BlockHeight: 1, - Power: uint64(i)}) + Power: int64(i), + PublicKey: pubKeysBytes[i], + }) } } @@ -207,7 +219,7 @@ func TestComputeValidatorUpdates(t *testing.T) { // validators 8 and 9 are unbonded, so they are not added } - actualValUpdates := providerKeeper.ComputeValidatorUpdates(ctx, + actualValUpdates, _ := providerKeeper.ComputeValidatorUpdatesAndNextValidators(ctx, chainID, currentValidators, validatorsToAdd, validatorsToRemove) sort.Slice(expectedValUpdates, func(i int, j int) bool { @@ -235,12 +247,14 @@ func TestComputeNextValidators(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + chainID := "chainID" + // change the block height, so we can verify that newly added validators have the right height set ctx = ctx.WithBlockHeight(1000) // create 10 validators, where the first 8 are bonded and the last 2 are unbonded var providerAddresses []types.ProviderConsAddress - var pubKeys []tmprotocrypto.PublicKey + var pubKeys [][]byte for i := 0; i < 10; i++ { // generate a consensus public key for the provider providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{uint8(i)}).PubKey() @@ -270,7 +284,8 @@ func TestComputeNextValidators(t *testing.T) { Ed25519: consAddr.Bytes(), }, } - pubKeys = append(pubKeys, pubKey) + pubKeyBytes, _ := pubKey.Marshal() + pubKeys = append(pubKeys, pubKeyBytes) mocks.MockStakingKeeper.EXPECT(). GetValidatorByConsAddr(ctx, consAddr).Return(validator, true).AnyTimes() @@ -278,22 +293,27 @@ func TestComputeNextValidators(t *testing.T) { mocks.MockStakingKeeper.EXPECT(). GetLastValidatorPower(ctx, providerValidatorAddr).Return(int64(i)).AnyTimes() + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, providerAddresses[i], pubKey) } // set first 6 validators as currently opted in where validators 1, 2, and 3 have a // different power now than the power they had when they opted in - var currentValidators []keeper.OptedInValidator + var currentValidators []types.OptedInValidator for i := 0; i < 6; i++ { if i > 0 && i < 4 { currentValidators = append(currentValidators, - keeper.OptedInValidator{ProviderAddr: providerAddresses[i], + types.OptedInValidator{ProviderAddr: providerAddresses[i].ToSdkConsAddr(), BlockHeight: 1, - Power: uint64(i + 1)}) + Power: int64(i + 1), + PublicKey: pubKeys[i], + }) } else { currentValidators = append(currentValidators, - keeper.OptedInValidator{ProviderAddr: providerAddresses[i], + types.OptedInValidator{ProviderAddr: providerAddresses[i].ToSdkConsAddr(), BlockHeight: 1, - Power: uint64(i)}) + Power: int64(i), + PublicKey: pubKeys[i], + }) } } @@ -309,40 +329,40 @@ func TestComputeNextValidators(t *testing.T) { validatorsToAdd = append(validatorsToAdd, providerAddresses[i]) } - expectedValidators := []keeper.OptedInValidator{ + expectedValidators := []types.OptedInValidator{ // validators 0 and 1 are removed // validators 2 to 5 are still opted in and hence remain - {ProviderAddr: providerAddresses[2], BlockHeight: uint64(1), Power: uint64(2)}, - {ProviderAddr: providerAddresses[3], BlockHeight: uint64(1), Power: uint64(3)}, - {ProviderAddr: providerAddresses[4], BlockHeight: uint64(1), Power: uint64(4)}, - {ProviderAddr: providerAddresses[5], BlockHeight: uint64(1), Power: uint64(5)}, + {ProviderAddr: providerAddresses[2].ToSdkConsAddr(), BlockHeight: int64(1), Power: int64(2), PublicKey: pubKeys[2]}, + {ProviderAddr: providerAddresses[3].ToSdkConsAddr(), BlockHeight: int64(1), Power: int64(3), PublicKey: pubKeys[3]}, + {ProviderAddr: providerAddresses[4].ToSdkConsAddr(), BlockHeight: int64(1), Power: int64(4), PublicKey: pubKeys[4]}, + {ProviderAddr: providerAddresses[5].ToSdkConsAddr(), BlockHeight: int64(1), Power: int64(5), PublicKey: pubKeys[5]}, // validators 6 and 7 are now opted in and hence have the latest `BlockHeight` - {ProviderAddr: providerAddresses[6], BlockHeight: uint64(1000), Power: uint64(6)}, - {ProviderAddr: providerAddresses[7], BlockHeight: uint64(1000), Power: uint64(7)}, + {ProviderAddr: providerAddresses[6].ToSdkConsAddr(), BlockHeight: int64(1000), Power: int64(6), PublicKey: pubKeys[6]}, + {ProviderAddr: providerAddresses[7].ToSdkConsAddr(), BlockHeight: int64(1000), Power: int64(7), PublicKey: pubKeys[7]}, // validators 8 and 9 are unbonded, so they are not added } - actualValidators := providerKeeper.ComputeNextValidators(ctx, + _, actualValidators := providerKeeper.ComputeValidatorUpdatesAndNextValidators(ctx, chainID, currentValidators, validatorsToAdd, validatorsToRemove) sort.Slice(actualValidators, func(i int, j int) bool { - if strings.Compare(actualValidators[i].ProviderAddr.String(), actualValidators[j].ProviderAddr.String()) == 0 { + if bytes.Compare(actualValidators[i].ProviderAddr, actualValidators[j].ProviderAddr) == 0 { if actualValidators[i].BlockHeight == actualValidators[j].BlockHeight { return actualValidators[i].Power < actualValidators[j].Power } return actualValidators[i].BlockHeight < actualValidators[j].BlockHeight } - return strings.Compare(actualValidators[i].ProviderAddr.String(), actualValidators[j].ProviderAddr.String()) < 0 + return bytes.Compare(actualValidators[i].ProviderAddr, actualValidators[j].ProviderAddr) < 0 }) sort.Slice(expectedValidators, func(i int, j int) bool { - if strings.Compare(expectedValidators[i].ProviderAddr.String(), expectedValidators[j].ProviderAddr.String()) == 0 { + if bytes.Compare(expectedValidators[i].ProviderAddr, expectedValidators[j].ProviderAddr) == 0 { if expectedValidators[i].BlockHeight == expectedValidators[j].BlockHeight { return expectedValidators[i].Power < expectedValidators[j].Power } return expectedValidators[i].BlockHeight < expectedValidators[j].BlockHeight } - return strings.Compare(expectedValidators[i].ProviderAddr.String(), expectedValidators[j].ProviderAddr.String()) < 0 + return bytes.Compare(expectedValidators[i].ProviderAddr, expectedValidators[j].ProviderAddr) < 0 }) require.Equal(t, expectedValidators, actualValidators) @@ -354,52 +374,50 @@ func TestResetCurrentValidators(t *testing.T) { chainID := "chainID" - // create 10 provider addresses - providerAddresses := make([]types.ProviderConsAddress, 10) - for i := 0; i < 10; i++ { - providerAddresses[i] = types.NewProviderConsAddress([]byte(fmt.Sprintf("providerAddr%d", i))) - } - - // opt in the first 5 validators - for i := 0; i < 5; i++ { - providerKeeper.SetOptedIn(ctx, chainID, providerAddresses[i], 1, 1) + // consider that 5 validators are opted in + for i := byte(0); i < 5; i++ { + optedInValidator := types.OptedInValidator{ + ProviderAddr: []byte(fmt.Sprintf("providerAddr%d", i)), + BlockHeight: int64(i), + Power: int64(i), + PublicKey: []byte{i}} + providerKeeper.SetOptedIn(ctx, chainID, optedInValidator) } - require.NotEmpty(t, providerKeeper.GetOptedIn(ctx, chainID)) - - // set the remaining 5 validators as opted in - nextValidators := []keeper.OptedInValidator{ - {ProviderAddr: providerAddresses[6], BlockHeight: uint64(6), Power: uint64(6)}, - {ProviderAddr: providerAddresses[7], BlockHeight: uint64(7), Power: uint64(7)}, - {ProviderAddr: providerAddresses[8], BlockHeight: uint64(8), Power: uint64(8)}, - {ProviderAddr: providerAddresses[9], BlockHeight: uint64(9), Power: uint64(9)}, + require.NotEmpty(t, providerKeeper.GetAllOptedIn(ctx, chainID)) + + // consider another 5 validators that correspond to the next opted-in validators + nextValidators := []types.OptedInValidator{ + {ProviderAddr: []byte(fmt.Sprintf("providerAddr6")), BlockHeight: 6, Power: 6}, + {ProviderAddr: []byte(fmt.Sprintf("providerAddr7")), BlockHeight: 7, Power: 7}, + {ProviderAddr: []byte(fmt.Sprintf("providerAddr8")), BlockHeight: 8, Power: 8}, + {ProviderAddr: []byte(fmt.Sprintf("providerAddr9")), BlockHeight: 9, Power: 9}, } - providerKeeper.ResetCurrentValidators(ctx, chainID, nextValidators) - require.Empty(t, providerKeeper.GetToBeOptedIn(ctx, chainID)) - require.Empty(t, providerKeeper.GetToBeOptedOut(ctx, chainID)) + require.Empty(t, providerKeeper.GetAllToBeOptedIn(ctx, chainID)) + require.Empty(t, providerKeeper.GetAllToBeOptedOut(ctx, chainID)) // verify that the currently opted in validators (`actualValidators`) are the ones that were in `nextValidators` - actualValidators := providerKeeper.GetOptedIn(ctx, chainID) + actualValidators := providerKeeper.GetAllOptedIn(ctx, chainID) sort.Slice(actualValidators, func(i int, j int) bool { - if strings.Compare(actualValidators[i].ProviderAddr.String(), actualValidators[j].ProviderAddr.String()) == 0 { + if bytes.Compare(actualValidators[i].ProviderAddr, actualValidators[j].ProviderAddr) == 0 { if actualValidators[i].BlockHeight == actualValidators[j].BlockHeight { return actualValidators[i].Power < actualValidators[j].Power } return actualValidators[i].BlockHeight < actualValidators[j].BlockHeight } - return strings.Compare(actualValidators[i].ProviderAddr.String(), actualValidators[j].ProviderAddr.String()) < 0 + return bytes.Compare(actualValidators[i].ProviderAddr, actualValidators[j].ProviderAddr) < 0 }) sort.Slice(nextValidators, func(i int, j int) bool { - if strings.Compare(nextValidators[i].ProviderAddr.String(), nextValidators[j].ProviderAddr.String()) == 0 { + if bytes.Compare(nextValidators[i].ProviderAddr, nextValidators[j].ProviderAddr) == 0 { if nextValidators[i].BlockHeight == nextValidators[j].BlockHeight { return nextValidators[i].Power < nextValidators[j].Power } return nextValidators[i].BlockHeight < nextValidators[j].BlockHeight } - return strings.Compare(nextValidators[i].ProviderAddr.String(), nextValidators[j].ProviderAddr.String()) < 0 + return bytes.Compare(nextValidators[i].ProviderAddr, nextValidators[j].ProviderAddr) < 0 }) require.Equal(t, nextValidators, actualValidators) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 0ee895188f..b75b87a6b8 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -255,9 +255,9 @@ func (k Keeper) MakeConsumerGenesis( return false }) - // currentValidators := k.GetOptedIn(ctx, chainID) - // validatorsToAdd := k.GetToBeOptedIn(ctx, chainID) - // validatorsToRemove := k.GetToBeOptedOut(ctx, chainID) + // currentValidators := k.GetAllOptedIn(ctx, chainID) + // validatorsToAdd := k.GetAllToBeOptedIn(ctx, chainID) + // validatorsToRemove := k.GetAllToBeOptedOut(ctx, chainID) // // initialUpdates := k.ComputeValidatorUpdates(ctx, currentValidators, validatorsToAdd, validatorsToRemove) @@ -379,7 +379,7 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { k.SetTopN(cachedCtx, prop.ChainId, prop.Top_N) // FIXME(PSS) - //if k.IsOptIn(cachedCtx, prop.ChainId) && len(k.GetToBeOptedIn(cachedCtx, prop.ChainId)) == 0 { + //if k.IsOptIn(cachedCtx, prop.ChainId) && len(k.GetAllToBeOptedIn(cachedCtx, prop.ChainId)) == 0 { // // drop the proposal // ctx.Logger().Info("could not start Opt In consumer chain (%s) because no validator has opted in", // prop.ChainId) diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index c0854abbca..e711d73983 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -219,17 +219,16 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { for _, chain := range k.GetAllConsumerChains(ctx) { // FIXME(PSS) - // currentValidators := k.GetOptedIn(ctx, chain.ChainId) - // validatorsToAdd := k.GetToBeOptedIn(ctx, chain.ChainId) - // validatorsToRemove := k.GetToBeOptedOut(ctx, chain.ChainId) + // currentValidators := k.GetAllOptedIn(ctx, chain.ChainId) + // validatorsToAdd := k.GetAllToBeOptedIn(ctx, chain.ChainId) + // validatorsToRemove := k.GetAllToBeOptedOut(ctx, chain.ChainId) // valUpdates := k.ComputeValidatorUpdates(ctx, currentValidators, validatorsToAdd, validatorsToRemove) // Apply the key assignment to the validator updates. valUpdates = k.MustApplyKeyAssignmentToValUpdates(ctx, chain.ChainId, valUpdates, func(address providertypes.ProviderConsAddress) bool { - // return k.IsOptedIn(ctx, chain.ChainId, address) - return true + return k.IsOptedIn(ctx, chain.ChainId, address) }) // nextValidators := k.ComputeNextValidators(ctx, currentValidators, validatorsToAdd, validatorsToRemove) diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 20824454cb..a056268ca2 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -538,9 +538,9 @@ func TopNKey(chainID string) []byte { } // OptedInKey returns the key of consumer chain `chainID` and validator with `providerAddr` -func OptedInKey(chainID string, providerAddr ProviderConsAddress) []byte { +func OptedInKey(chainID string, providerAddr []byte) []byte { prefix := ChainIdWithLenKey(OptedInBytePrefix, chainID) - return append(prefix, providerAddr.ToSdkConsAddr().Bytes()...) + return append(prefix, providerAddr...) } // ToBeOptedInKey returns the key of consumer chain `chainID` and validator with `providerAddr` diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index fd9d63bc7e..46eff7fb0a 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -1390,6 +1390,74 @@ func (m *ConsumerAddrsToPrune) GetConsumerAddrs() *AddressList { return nil } +type OptedInValidator struct { + ProviderAddr []byte `protobuf:"bytes,1,opt,name=provider_addr,json=providerAddr,proto3" json:"provider_addr,omitempty"` + BlockHeight int64 `protobuf:"varint,2,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + Power int64 `protobuf:"varint,3,opt,name=power,proto3" json:"power,omitempty"` + 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{22} +} +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") @@ -1413,6 +1481,7 @@ func init() { proto.RegisterType((*ValidatorConsumerPubKey)(nil), "interchain_security.ccv.provider.v1.ValidatorConsumerPubKey") proto.RegisterType((*ValidatorByConsumerAddr)(nil), "interchain_security.ccv.provider.v1.ValidatorByConsumerAddr") proto.RegisterType((*ConsumerAddrsToPrune)(nil), "interchain_security.ccv.provider.v1.ConsumerAddrsToPrune") + proto.RegisterType((*OptedInValidator)(nil), "interchain_security.ccv.provider.v1.OptedInValidator") } func init() { @@ -1420,114 +1489,118 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1712 bytes of a gzipped FileDescriptorProto + // 1761 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4f, 0x73, 0xdb, 0xc6, - 0x15, 0x17, 0x44, 0x4a, 0x16, 0x1f, 0xf5, 0xcf, 0x90, 0x12, 0x43, 0xae, 0x4a, 0xd1, 0x48, 0x93, + 0x15, 0x17, 0x48, 0x4a, 0x16, 0x1f, 0xf5, 0xcf, 0x90, 0x12, 0x43, 0xae, 0x4a, 0xd1, 0x48, 0x93, 0xaa, 0x93, 0x09, 0x58, 0x29, 0xed, 0x4c, 0xc6, 0xd3, 0x4c, 0x46, 0xa2, 0x9c, 0x58, 0x56, 0x63, 0x2b, 0x90, 0x2a, 0x4f, 0xdb, 0x03, 0x66, 0xb9, 0x58, 0x93, 0x3b, 0x02, 0xb1, 0xf0, 0xee, 0x02, - 0x0e, 0x2f, 0x3d, 0xf7, 0x98, 0xde, 0x32, 0xbd, 0x34, 0xed, 0x17, 0xe8, 0xb9, 0xdf, 0x20, 0xc7, - 0x1c, 0x7b, 0x4a, 0x3b, 0xf2, 0xb1, 0xd7, 0x7e, 0x80, 0xce, 0x2e, 0xfe, 0x92, 0x12, 0x5d, 0x7a, - 0xdc, 0xde, 0x80, 0xb7, 0xef, 0xfd, 0xde, 0xdb, 0xf7, 0xe7, 0xf7, 0x40, 0xc2, 0x3e, 0x0d, 0x25, - 0xe1, 0x78, 0x80, 0x68, 0xe8, 0x09, 0x82, 0x63, 0x4e, 0xe5, 0xa8, 0x83, 0x71, 0xd2, 0x89, 0x38, - 0x4b, 0xa8, 0x4f, 0x78, 0x27, 0xd9, 0x2b, 0x9e, 0x9d, 0x88, 0x33, 0xc9, 0xcc, 0x77, 0x6e, 0xb0, - 0x71, 0x30, 0x4e, 0x9c, 0x42, 0x2f, 0xd9, 0xbb, 0xfb, 0xee, 0x34, 0xe0, 0x64, 0xaf, 0xf3, 0x82, - 0x72, 0x92, 0x62, 0xdd, 0xdd, 0xec, 0xb3, 0x3e, 0xd3, 0x8f, 0x1d, 0xf5, 0x94, 0x49, 0x77, 0xfa, - 0x8c, 0xf5, 0x03, 0xd2, 0xd1, 0x6f, 0xbd, 0xf8, 0x59, 0x47, 0xd2, 0x21, 0x11, 0x12, 0x0d, 0xa3, - 0x4c, 0xa1, 0x35, 0xa9, 0xe0, 0xc7, 0x1c, 0x49, 0xca, 0xc2, 0x1c, 0x80, 0xf6, 0x70, 0x07, 0x33, - 0x4e, 0x3a, 0x38, 0xa0, 0x24, 0x94, 0xca, 0x6b, 0xfa, 0x94, 0x29, 0x74, 0x94, 0x42, 0x40, 0xfb, - 0x03, 0x99, 0x8a, 0x45, 0x47, 0x92, 0xd0, 0x27, 0x7c, 0x48, 0x53, 0xe5, 0xf2, 0x2d, 0x33, 0xd8, - 0xae, 0x9c, 0x63, 0x3e, 0x8a, 0x24, 0xeb, 0x5c, 0x92, 0x91, 0xc8, 0x4e, 0xdf, 0xc3, 0x4c, 0x0c, - 0x99, 0xe8, 0x10, 0x75, 0xff, 0x10, 0x93, 0x4e, 0xb2, 0xd7, 0x23, 0x12, 0xed, 0x15, 0x82, 0x3c, - 0xee, 0x4c, 0xaf, 0x87, 0x44, 0xa9, 0x83, 0x19, 0xcd, 0xe2, 0xb6, 0xff, 0xbd, 0x08, 0x56, 0x97, - 0x85, 0x22, 0x1e, 0x12, 0x7e, 0xe0, 0xfb, 0x54, 0x5d, 0xe9, 0x94, 0xb3, 0x88, 0x09, 0x14, 0x98, - 0x9b, 0xb0, 0x20, 0xa9, 0x0c, 0x88, 0x65, 0xb4, 0x8d, 0xdd, 0x86, 0x9b, 0xbe, 0x98, 0x6d, 0x68, - 0xfa, 0x44, 0x60, 0x4e, 0x23, 0xa5, 0x6c, 0xcd, 0xeb, 0xb3, 0xaa, 0xc8, 0xdc, 0x82, 0xa5, 0xb4, - 0x0e, 0xd4, 0xb7, 0x6a, 0xfa, 0xf8, 0x96, 0x7e, 0x3f, 0xf6, 0xcd, 0xcf, 0x60, 0x95, 0x86, 0x54, - 0x52, 0x14, 0x78, 0x03, 0xa2, 0xb2, 0x61, 0xd5, 0xdb, 0xc6, 0x6e, 0x73, 0xff, 0xae, 0x43, 0x7b, - 0xd8, 0x51, 0x09, 0x74, 0xb2, 0xb4, 0x25, 0x7b, 0xce, 0x43, 0xad, 0x71, 0x58, 0xff, 0xf6, 0xfb, - 0x9d, 0x39, 0x77, 0x25, 0xb3, 0x4b, 0x85, 0xe6, 0x3d, 0x58, 0xee, 0x93, 0x90, 0x08, 0x2a, 0xbc, - 0x01, 0x12, 0x03, 0x6b, 0xa1, 0x6d, 0xec, 0x2e, 0xbb, 0xcd, 0x4c, 0xf6, 0x10, 0x89, 0x81, 0xb9, - 0x03, 0xcd, 0x1e, 0x0d, 0x11, 0x1f, 0xa5, 0x1a, 0x8b, 0x5a, 0x03, 0x52, 0x91, 0x56, 0xe8, 0x02, - 0x88, 0x08, 0xbd, 0x08, 0x3d, 0x55, 0x6d, 0xeb, 0x56, 0x16, 0x48, 0x5a, 0x69, 0x27, 0xaf, 0xb4, - 0x73, 0x9e, 0xb7, 0xc2, 0xe1, 0x92, 0x0a, 0xe4, 0xab, 0x7f, 0xec, 0x18, 0x6e, 0x43, 0xdb, 0xa9, - 0x13, 0xf3, 0x31, 0xac, 0xc7, 0x61, 0x8f, 0x85, 0x3e, 0x0d, 0xfb, 0x5e, 0x44, 0x38, 0x65, 0xbe, - 0xb5, 0xa4, 0xa1, 0xb6, 0xae, 0x41, 0x1d, 0x65, 0x4d, 0x93, 0x22, 0x7d, 0xad, 0x90, 0xd6, 0x0a, - 0xe3, 0x53, 0x6d, 0x6b, 0x7e, 0x01, 0x26, 0xc6, 0x89, 0x0e, 0x89, 0xc5, 0x32, 0x47, 0x6c, 0xcc, - 0x8e, 0xb8, 0x8e, 0x71, 0x72, 0x9e, 0x5a, 0x67, 0x90, 0xbf, 0x85, 0x3b, 0x92, 0xa3, 0x50, 0x3c, - 0x23, 0x7c, 0x12, 0x17, 0x66, 0xc7, 0x7d, 0x2b, 0xc7, 0x18, 0x07, 0x7f, 0x08, 0x6d, 0x9c, 0x35, - 0x90, 0xc7, 0x89, 0x4f, 0x85, 0xe4, 0xb4, 0x17, 0x2b, 0x5b, 0xef, 0x19, 0x47, 0x58, 0xf7, 0x48, - 0x53, 0x37, 0x41, 0x2b, 0xd7, 0x73, 0xc7, 0xd4, 0x3e, 0xcd, 0xb4, 0xcc, 0x27, 0xf0, 0xa3, 0x5e, - 0xc0, 0xf0, 0xa5, 0x50, 0xc1, 0x79, 0x63, 0x48, 0xda, 0xf5, 0x90, 0x0a, 0xa1, 0xd0, 0x96, 0xdb, - 0xc6, 0x6e, 0xcd, 0xbd, 0x97, 0xea, 0x9e, 0x12, 0x7e, 0x54, 0xd1, 0x3c, 0xaf, 0x28, 0x9a, 0x1f, - 0x80, 0x39, 0xa0, 0x42, 0x32, 0x4e, 0x31, 0x0a, 0x3c, 0x12, 0x4a, 0x4e, 0x89, 0xb0, 0x56, 0xb4, - 0xf9, 0xed, 0xf2, 0xe4, 0x41, 0x7a, 0x60, 0x3e, 0x82, 0x7b, 0x53, 0x9d, 0x7a, 0x78, 0x80, 0xc2, - 0x90, 0x04, 0xd6, 0xaa, 0xbe, 0xca, 0x8e, 0x3f, 0xc5, 0x67, 0x37, 0x55, 0x33, 0x37, 0x60, 0x41, - 0xb2, 0xc8, 0x7b, 0x6c, 0xad, 0xb5, 0x8d, 0xdd, 0x15, 0xb7, 0x2e, 0x59, 0xf4, 0xf8, 0xfe, 0xd2, - 0xef, 0xbf, 0xd9, 0x99, 0xfb, 0xfa, 0x9b, 0x9d, 0x39, 0xfb, 0xaf, 0x06, 0xdc, 0xe9, 0x16, 0xd9, - 0x18, 0xb2, 0x04, 0x05, 0xff, 0xcf, 0xa9, 0x3b, 0x80, 0x86, 0x50, 0xe1, 0xe8, 0x3e, 0xaf, 0xbf, - 0x46, 0x9f, 0x2f, 0x29, 0x33, 0x75, 0x60, 0xff, 0xc9, 0x80, 0xcd, 0x07, 0xcf, 0x63, 0x9a, 0x30, - 0x8c, 0xfe, 0x27, 0x24, 0x71, 0x02, 0x2b, 0xa4, 0x82, 0x27, 0xac, 0x5a, 0xbb, 0xb6, 0xdb, 0xdc, - 0x7f, 0xd7, 0x49, 0x19, 0xcb, 0x29, 0x88, 0x2c, 0x63, 0x2d, 0xa7, 0xea, 0xdd, 0x1d, 0xb7, 0xbd, - 0x3f, 0x6f, 0x19, 0xf6, 0x5f, 0x0c, 0xb8, 0xab, 0xd2, 0xdf, 0x27, 0x2e, 0x79, 0x81, 0xb8, 0x7f, - 0x44, 0x42, 0x36, 0x14, 0x6f, 0x1c, 0xa7, 0x0d, 0x2b, 0xbe, 0x46, 0xf2, 0x24, 0xf3, 0x90, 0xef, - 0xeb, 0x38, 0xb5, 0x8e, 0x12, 0x9e, 0xb3, 0x03, 0xdf, 0x37, 0x77, 0x61, 0xbd, 0xd4, 0xe1, 0xaa, - 0x9e, 0x2a, 0xcd, 0x4a, 0x6d, 0x35, 0x57, 0xd3, 0x55, 0x26, 0xf6, 0xbf, 0x0c, 0x58, 0xff, 0x2c, - 0x60, 0x3d, 0x14, 0x9c, 0x05, 0x48, 0x0c, 0x54, 0xeb, 0x8d, 0x54, 0x79, 0x38, 0xc9, 0x66, 0x5e, - 0x87, 0x37, 0x73, 0x79, 0x94, 0x99, 0x66, 0xa1, 0x4f, 0xe0, 0x76, 0x31, 0x85, 0x45, 0x17, 0xe8, - 0xdb, 0x1c, 0x6e, 0x5c, 0x7d, 0xbf, 0xb3, 0x96, 0x37, 0x5b, 0x57, 0x77, 0xc4, 0x91, 0xbb, 0x86, - 0xc7, 0x04, 0xbe, 0xd9, 0x82, 0x26, 0xed, 0x61, 0x4f, 0x90, 0xe7, 0x5e, 0x18, 0x0f, 0x75, 0x03, - 0xd5, 0xdd, 0x06, 0xed, 0xe1, 0x33, 0xf2, 0xfc, 0x71, 0x3c, 0x34, 0x3f, 0x84, 0xb7, 0xf3, 0x6d, - 0xeb, 0x25, 0x28, 0xf0, 0x94, 0xbd, 0x4a, 0x07, 0xd7, 0xfd, 0xb4, 0xec, 0x6e, 0xe4, 0xa7, 0x17, - 0x28, 0x50, 0xce, 0x0e, 0x7c, 0x9f, 0xdb, 0x7f, 0x5b, 0x80, 0xc5, 0x53, 0xc4, 0xd1, 0x50, 0x98, - 0xe7, 0xb0, 0x26, 0xc9, 0x30, 0x0a, 0x90, 0x24, 0x5e, 0xca, 0xf0, 0xd9, 0x4d, 0xdf, 0xd7, 0xcc, - 0x5f, 0xdd, 0x8c, 0x4e, 0x65, 0x17, 0x26, 0x7b, 0x4e, 0x57, 0x4b, 0xcf, 0x24, 0x92, 0xc4, 0x5d, - 0xcd, 0x31, 0x52, 0xa1, 0xf9, 0x11, 0x58, 0x92, 0xc7, 0x42, 0x96, 0xdc, 0x5b, 0x92, 0x4e, 0x5a, - 0xcb, 0xb7, 0xf3, 0xf3, 0x94, 0xae, 0x0a, 0xb2, 0xb9, 0x99, 0x66, 0x6b, 0x6f, 0x42, 0xb3, 0x67, - 0xb0, 0xa1, 0x76, 0xd4, 0x24, 0x66, 0x7d, 0x76, 0xcc, 0xdb, 0xca, 0x7e, 0x1c, 0xf4, 0x0b, 0x30, - 0x13, 0x81, 0x27, 0x31, 0x17, 0x5e, 0x23, 0xce, 0x44, 0xe0, 0x71, 0x48, 0x1f, 0xb6, 0x85, 0x6a, - 0x3e, 0x6f, 0x48, 0xa4, 0x26, 0xed, 0x28, 0x20, 0x21, 0x15, 0x83, 0x1c, 0x7c, 0x71, 0x76, 0xf0, - 0x2d, 0x0d, 0xf4, 0xb9, 0xc2, 0x71, 0x73, 0x98, 0xcc, 0x4b, 0x17, 0x5a, 0x37, 0x7b, 0x29, 0x0a, - 0x74, 0x4b, 0x17, 0xe8, 0x07, 0x37, 0x40, 0x14, 0x55, 0x12, 0xf0, 0x5e, 0x65, 0xb9, 0xa8, 0xa9, - 0xf6, 0xf4, 0x40, 0x79, 0x9c, 0xf4, 0x15, 0x03, 0xa3, 0x74, 0xcf, 0x10, 0x52, 0x2c, 0xc8, 0x8c, - 0x3d, 0xd4, 0xf7, 0x4e, 0xc1, 0x1c, 0x5d, 0x46, 0xc3, 0xec, 0x2b, 0xc2, 0x2e, 0x77, 0x50, 0xc1, - 0x11, 0x6e, 0x05, 0xeb, 0x53, 0x42, 0x1e, 0xd5, 0x97, 0x96, 0xd6, 0x1b, 0xf6, 0x4f, 0xa0, 0xa1, - 0x47, 0xf4, 0x00, 0x5f, 0x0a, 0x73, 0x1b, 0x1a, 0xaa, 0xd7, 0x89, 0x10, 0x44, 0x58, 0x86, 0x9e, - 0xec, 0x52, 0x60, 0x4b, 0xd8, 0x9a, 0xf6, 0x0d, 0x25, 0xcc, 0xa7, 0x70, 0x2b, 0x22, 0x7a, 0xc1, - 0x6b, 0xc3, 0xe6, 0xfe, 0xc7, 0xce, 0x0c, 0x9f, 0xb3, 0xce, 0x34, 0x40, 0x37, 0x47, 0xb3, 0x79, - 0xf9, 0xe5, 0x36, 0xb1, 0x42, 0x84, 0x79, 0x31, 0xe9, 0xf4, 0x17, 0xaf, 0xe5, 0x74, 0x02, 0xaf, - 0xf4, 0xf9, 0x3e, 0x34, 0x0f, 0xd2, 0x6b, 0xff, 0x92, 0x0a, 0x79, 0x3d, 0x2d, 0xcb, 0xd5, 0xb4, - 0x3c, 0x82, 0xd5, 0x6c, 0x1d, 0x9e, 0x33, 0x4d, 0x33, 0xe6, 0x0f, 0x01, 0xb2, 0x3d, 0xaa, 0xe8, - 0x29, 0x25, 0xe2, 0x46, 0x26, 0x39, 0xf6, 0xc7, 0x36, 0xd8, 0xfc, 0xd8, 0x06, 0xb3, 0x5d, 0x58, - 0xbb, 0x10, 0xf8, 0x57, 0xf9, 0xb7, 0xd2, 0x93, 0x48, 0x98, 0x6f, 0xc1, 0xa2, 0x9a, 0x8c, 0x0c, - 0xa8, 0xee, 0x2e, 0x24, 0x02, 0x1f, 0x6b, 0x2e, 0x2e, 0xbf, 0xc7, 0x58, 0xe4, 0x51, 0x5f, 0x58, - 0xf3, 0xed, 0xda, 0x6e, 0xdd, 0x5d, 0x8d, 0x4b, 0xf3, 0x63, 0x5f, 0xd8, 0xbf, 0x86, 0x66, 0x05, - 0xd0, 0x5c, 0x85, 0xf9, 0x02, 0x6b, 0x9e, 0xfa, 0xe6, 0x7d, 0xd8, 0x2a, 0x81, 0xc6, 0xc9, 0x35, - 0x45, 0x6c, 0xb8, 0x77, 0x0a, 0x85, 0x31, 0x7e, 0x15, 0xf6, 0x13, 0xd8, 0x3c, 0x2e, 0x47, 0xb9, - 0xa0, 0xee, 0xb1, 0x1b, 0x1a, 0xe3, 0x3b, 0x7a, 0x1b, 0x1a, 0xc5, 0x8f, 0x0e, 0x7d, 0xfb, 0xba, - 0x5b, 0x0a, 0xec, 0x21, 0xac, 0x5f, 0x08, 0x7c, 0x46, 0x42, 0xbf, 0x04, 0x9b, 0x92, 0x80, 0xc3, - 0x49, 0xa0, 0x99, 0x3f, 0x6a, 0x4b, 0x77, 0x0c, 0xb6, 0x2e, 0x50, 0x40, 0x7d, 0x24, 0x19, 0x3f, - 0x23, 0x32, 0x5d, 0xab, 0xa7, 0x08, 0x5f, 0x12, 0x29, 0x4c, 0x17, 0xea, 0x01, 0x15, 0x32, 0xeb, - 0xac, 0x8f, 0xa6, 0x76, 0x56, 0xb2, 0xe7, 0x4c, 0x03, 0x39, 0x42, 0x12, 0x65, 0x13, 0xa9, 0xb1, - 0xec, 0x1f, 0xc3, 0xc6, 0xe7, 0x48, 0xc6, 0x9c, 0xf8, 0x63, 0x35, 0x5e, 0x87, 0x9a, 0xaa, 0x9f, - 0xa1, 0xeb, 0xa7, 0x1e, 0xd5, 0x96, 0xb7, 0x1e, 0x7c, 0x19, 0x31, 0x2e, 0x89, 0x7f, 0x2d, 0x23, - 0xaf, 0x48, 0xef, 0x25, 0x6c, 0xa8, 0x64, 0x09, 0x12, 0xfa, 0x5e, 0x71, 0xcf, 0xb4, 0x8e, 0xcd, - 0xfd, 0x9f, 0xcf, 0x34, 0x1d, 0x93, 0xee, 0xb2, 0x0b, 0xdc, 0x4e, 0x26, 0xe4, 0xc2, 0xfe, 0x83, - 0x01, 0xd6, 0x09, 0x19, 0x1d, 0x08, 0x41, 0xfb, 0xe1, 0x90, 0x84, 0x52, 0x31, 0x1b, 0xc2, 0x44, - 0x3d, 0x9a, 0xef, 0xc0, 0x4a, 0xb1, 0x49, 0xf5, 0x02, 0x35, 0xf4, 0x02, 0x5d, 0xce, 0x85, 0x6a, - 0xc0, 0xcc, 0xfb, 0x00, 0x11, 0x27, 0x89, 0x87, 0xbd, 0x4b, 0x32, 0xca, 0xaa, 0xb8, 0x5d, 0x5d, - 0x8c, 0xe9, 0x4f, 0x42, 0xe7, 0x34, 0xee, 0x05, 0x14, 0x9f, 0x90, 0x91, 0xbb, 0xa4, 0xf4, 0xbb, - 0x27, 0x64, 0xa4, 0xbe, 0x74, 0x22, 0xf6, 0x82, 0x70, 0xbd, 0xcd, 0x6a, 0x6e, 0xfa, 0x62, 0xff, - 0xd1, 0x80, 0x3b, 0x45, 0x39, 0xf2, 0x76, 0x3d, 0x8d, 0x7b, 0xca, 0xe2, 0x15, 0x79, 0xbb, 0x16, - 0xed, 0xfc, 0x0d, 0xd1, 0x7e, 0x02, 0xcb, 0xc5, 0x80, 0xa8, 0x78, 0x6b, 0x33, 0xc4, 0xdb, 0xcc, - 0x2d, 0x4e, 0xc8, 0xc8, 0xfe, 0x5d, 0x25, 0xb6, 0xc3, 0x51, 0x85, 0xfb, 0xf8, 0x7f, 0x89, 0xad, - 0x70, 0x5b, 0x8d, 0x0d, 0x57, 0xed, 0xaf, 0x5d, 0xa0, 0x76, 0xfd, 0x02, 0xf6, 0x9f, 0x0d, 0xd8, - 0xac, 0x7a, 0x15, 0xe7, 0xec, 0x94, 0xc7, 0x21, 0x79, 0x95, 0xf7, 0x72, 0xfc, 0xe6, 0xab, 0xe3, - 0xf7, 0x14, 0x56, 0xc7, 0x82, 0x12, 0x59, 0x36, 0x7e, 0x3a, 0x53, 0x8f, 0x55, 0xd8, 0xd5, 0x5d, - 0xa9, 0xde, 0x43, 0x1c, 0x3e, 0xfd, 0xf6, 0xaa, 0x65, 0x7c, 0x77, 0xd5, 0x32, 0xfe, 0x79, 0xd5, - 0x32, 0xbe, 0x7a, 0xd9, 0x9a, 0xfb, 0xee, 0x65, 0x6b, 0xee, 0xef, 0x2f, 0x5b, 0x73, 0xbf, 0xf9, - 0xb8, 0x4f, 0xe5, 0x20, 0xee, 0x39, 0x98, 0x0d, 0x3b, 0xd9, 0xef, 0xfd, 0xd2, 0xd7, 0x07, 0xc5, - 0x9f, 0x21, 0xc9, 0xcf, 0x3a, 0x5f, 0x8e, 0xff, 0xd5, 0x22, 0x47, 0x11, 0x11, 0xbd, 0x45, 0xcd, - 0x0a, 0x1f, 0xfe, 0x27, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x99, 0xdf, 0x57, 0x9b, 0x11, 0x00, 0x00, + 0x0e, 0x2f, 0x3d, 0xf7, 0xd6, 0xf4, 0x96, 0xe9, 0xa5, 0x69, 0xbf, 0x40, 0xcf, 0xfd, 0x06, 0x39, + 0xe6, 0xd8, 0x53, 0xda, 0x91, 0x8f, 0xbd, 0xf6, 0x03, 0x74, 0x76, 0xf1, 0x97, 0x94, 0xe4, 0xd2, + 0x93, 0xe6, 0x06, 0xbc, 0x7d, 0xef, 0xf7, 0xde, 0xbe, 0xf7, 0xf6, 0xf7, 0x16, 0x80, 0x3d, 0x1a, + 0x4a, 0xc2, 0xf1, 0x10, 0xd1, 0xd0, 0x13, 0x04, 0xc7, 0x9c, 0xca, 0x71, 0x17, 0xe3, 0xa4, 0x1b, + 0x71, 0x96, 0x50, 0x9f, 0xf0, 0x6e, 0xb2, 0x5b, 0x3c, 0x3b, 0x11, 0x67, 0x92, 0x99, 0x6f, 0x5d, + 0x63, 0xe3, 0x60, 0x9c, 0x38, 0x85, 0x5e, 0xb2, 0x7b, 0xf7, 0xed, 0x9b, 0x80, 0x93, 0xdd, 0xee, + 0x0b, 0xca, 0x49, 0x8a, 0x75, 0x77, 0x63, 0xc0, 0x06, 0x4c, 0x3f, 0x76, 0xd5, 0x53, 0x26, 0xdd, + 0x1e, 0x30, 0x36, 0x08, 0x48, 0x57, 0xbf, 0xf5, 0xe3, 0x67, 0x5d, 0x49, 0x47, 0x44, 0x48, 0x34, + 0x8a, 0x32, 0x85, 0xf6, 0xb4, 0x82, 0x1f, 0x73, 0x24, 0x29, 0x0b, 0x73, 0x00, 0xda, 0xc7, 0x5d, + 0xcc, 0x38, 0xe9, 0xe2, 0x80, 0x92, 0x50, 0x2a, 0xaf, 0xe9, 0x53, 0xa6, 0xd0, 0x55, 0x0a, 0x01, + 0x1d, 0x0c, 0x65, 0x2a, 0x16, 0x5d, 0x49, 0x42, 0x9f, 0xf0, 0x11, 0x4d, 0x95, 0xcb, 0xb7, 0xcc, + 0x60, 0xab, 0xb2, 0x8e, 0xf9, 0x38, 0x92, 0xac, 0x7b, 0x41, 0xc6, 0x22, 0x5b, 0x7d, 0x07, 0x33, + 0x31, 0x62, 0xa2, 0x4b, 0xd4, 0xfe, 0x43, 0x4c, 0xba, 0xc9, 0x6e, 0x9f, 0x48, 0xb4, 0x5b, 0x08, + 0xf2, 0xb8, 0x33, 0xbd, 0x3e, 0x12, 0xa5, 0x0e, 0x66, 0x34, 0x8b, 0xdb, 0xfe, 0xcf, 0x02, 0x58, + 0x3d, 0x16, 0x8a, 0x78, 0x44, 0xf8, 0xbe, 0xef, 0x53, 0xb5, 0xa5, 0x13, 0xce, 0x22, 0x26, 0x50, + 0x60, 0x6e, 0xc0, 0xbc, 0xa4, 0x32, 0x20, 0x96, 0xd1, 0x31, 0x76, 0x9a, 0x6e, 0xfa, 0x62, 0x76, + 0xa0, 0xe5, 0x13, 0x81, 0x39, 0x8d, 0x94, 0xb2, 0x55, 0xd3, 0x6b, 0x55, 0x91, 0xb9, 0x09, 0x8b, + 0x69, 0x1d, 0xa8, 0x6f, 0xd5, 0xf5, 0xf2, 0x2d, 0xfd, 0x7e, 0xe4, 0x9b, 0x9f, 0xc0, 0x0a, 0x0d, + 0xa9, 0xa4, 0x28, 0xf0, 0x86, 0x44, 0x65, 0xc3, 0x6a, 0x74, 0x8c, 0x9d, 0xd6, 0xde, 0x5d, 0x87, + 0xf6, 0xb1, 0xa3, 0x12, 0xe8, 0x64, 0x69, 0x4b, 0x76, 0x9d, 0x87, 0x5a, 0xe3, 0xa0, 0xf1, 0xf5, + 0xb7, 0xdb, 0x73, 0xee, 0x72, 0x66, 0x97, 0x0a, 0xcd, 0x7b, 0xb0, 0x34, 0x20, 0x21, 0x11, 0x54, + 0x78, 0x43, 0x24, 0x86, 0xd6, 0x7c, 0xc7, 0xd8, 0x59, 0x72, 0x5b, 0x99, 0xec, 0x21, 0x12, 0x43, + 0x73, 0x1b, 0x5a, 0x7d, 0x1a, 0x22, 0x3e, 0x4e, 0x35, 0x16, 0xb4, 0x06, 0xa4, 0x22, 0xad, 0xd0, + 0x03, 0x10, 0x11, 0x7a, 0x11, 0x7a, 0xaa, 0xda, 0xd6, 0xad, 0x2c, 0x90, 0xb4, 0xd2, 0x4e, 0x5e, + 0x69, 0xe7, 0x2c, 0x6f, 0x85, 0x83, 0x45, 0x15, 0xc8, 0x17, 0xff, 0xdc, 0x36, 0xdc, 0xa6, 0xb6, + 0x53, 0x2b, 0xe6, 0x63, 0x58, 0x8b, 0xc3, 0x3e, 0x0b, 0x7d, 0x1a, 0x0e, 0xbc, 0x88, 0x70, 0xca, + 0x7c, 0x6b, 0x51, 0x43, 0x6d, 0x5e, 0x81, 0x3a, 0xcc, 0x9a, 0x26, 0x45, 0xfa, 0x52, 0x21, 0xad, + 0x16, 0xc6, 0x27, 0xda, 0xd6, 0xfc, 0x0c, 0x4c, 0x8c, 0x13, 0x1d, 0x12, 0x8b, 0x65, 0x8e, 0xd8, + 0x9c, 0x1d, 0x71, 0x0d, 0xe3, 0xe4, 0x2c, 0xb5, 0xce, 0x20, 0x7f, 0x0b, 0x77, 0x24, 0x47, 0xa1, + 0x78, 0x46, 0xf8, 0x34, 0x2e, 0xcc, 0x8e, 0xfb, 0x46, 0x8e, 0x31, 0x09, 0xfe, 0x10, 0x3a, 0x38, + 0x6b, 0x20, 0x8f, 0x13, 0x9f, 0x0a, 0xc9, 0x69, 0x3f, 0x56, 0xb6, 0xde, 0x33, 0x8e, 0xb0, 0xee, + 0x91, 0x96, 0x6e, 0x82, 0x76, 0xae, 0xe7, 0x4e, 0xa8, 0x7d, 0x9c, 0x69, 0x99, 0x4f, 0xe0, 0x47, + 0xfd, 0x80, 0xe1, 0x0b, 0xa1, 0x82, 0xf3, 0x26, 0x90, 0xb4, 0xeb, 0x11, 0x15, 0x42, 0xa1, 0x2d, + 0x75, 0x8c, 0x9d, 0xba, 0x7b, 0x2f, 0xd5, 0x3d, 0x21, 0xfc, 0xb0, 0xa2, 0x79, 0x56, 0x51, 0x34, + 0xdf, 0x03, 0x73, 0x48, 0x85, 0x64, 0x9c, 0x62, 0x14, 0x78, 0x24, 0x94, 0x9c, 0x12, 0x61, 0x2d, + 0x6b, 0xf3, 0xdb, 0xe5, 0xca, 0x83, 0x74, 0xc1, 0x7c, 0x04, 0xf7, 0x6e, 0x74, 0xea, 0xe1, 0x21, + 0x0a, 0x43, 0x12, 0x58, 0x2b, 0x7a, 0x2b, 0xdb, 0xfe, 0x0d, 0x3e, 0x7b, 0xa9, 0x9a, 0xb9, 0x0e, + 0xf3, 0x92, 0x45, 0xde, 0x63, 0x6b, 0xb5, 0x63, 0xec, 0x2c, 0xbb, 0x0d, 0xc9, 0xa2, 0xc7, 0xf7, + 0x17, 0x7f, 0xff, 0xd5, 0xf6, 0xdc, 0x97, 0x5f, 0x6d, 0xcf, 0xd9, 0x7f, 0x33, 0xe0, 0x4e, 0xaf, + 0xc8, 0xc6, 0x88, 0x25, 0x28, 0xf8, 0x3e, 0x4f, 0xdd, 0x3e, 0x34, 0x85, 0x0a, 0x47, 0xf7, 0x79, + 0xe3, 0x35, 0xfa, 0x7c, 0x51, 0x99, 0xa9, 0x05, 0xfb, 0xcf, 0x06, 0x6c, 0x3c, 0x78, 0x1e, 0xd3, + 0x84, 0x61, 0xf4, 0x7f, 0x21, 0x89, 0x63, 0x58, 0x26, 0x15, 0x3c, 0x61, 0xd5, 0x3b, 0xf5, 0x9d, + 0xd6, 0xde, 0xdb, 0x4e, 0xca, 0x58, 0x4e, 0x41, 0x64, 0x19, 0x6b, 0x39, 0x55, 0xef, 0xee, 0xa4, + 0xed, 0xfd, 0x9a, 0x65, 0xd8, 0x7f, 0x35, 0xe0, 0xae, 0x4a, 0xff, 0x80, 0xb8, 0xe4, 0x05, 0xe2, + 0xfe, 0x21, 0x09, 0xd9, 0x48, 0x7c, 0xe7, 0x38, 0x6d, 0x58, 0xf6, 0x35, 0x92, 0x27, 0x99, 0x87, + 0x7c, 0x5f, 0xc7, 0xa9, 0x75, 0x94, 0xf0, 0x8c, 0xed, 0xfb, 0xbe, 0xb9, 0x03, 0x6b, 0xa5, 0x0e, + 0x57, 0xf5, 0x54, 0x69, 0x56, 0x6a, 0x2b, 0xb9, 0x9a, 0xae, 0x32, 0xb1, 0xff, 0x6d, 0xc0, 0xda, + 0x27, 0x01, 0xeb, 0xa3, 0xe0, 0x34, 0x40, 0x62, 0xa8, 0x5a, 0x6f, 0xac, 0xca, 0xc3, 0x49, 0x76, + 0xe6, 0x75, 0x78, 0x33, 0x97, 0x47, 0x99, 0x69, 0x16, 0xfa, 0x08, 0x6e, 0x17, 0xa7, 0xb0, 0xe8, + 0x02, 0xbd, 0x9b, 0x83, 0xf5, 0xcb, 0x6f, 0xb7, 0x57, 0xf3, 0x66, 0xeb, 0xe9, 0x8e, 0x38, 0x74, + 0x57, 0xf1, 0x84, 0xc0, 0x37, 0xdb, 0xd0, 0xa2, 0x7d, 0xec, 0x09, 0xf2, 0xdc, 0x0b, 0xe3, 0x91, + 0x6e, 0xa0, 0x86, 0xdb, 0xa4, 0x7d, 0x7c, 0x4a, 0x9e, 0x3f, 0x8e, 0x47, 0xe6, 0xfb, 0xf0, 0x66, + 0x3e, 0x6d, 0xbd, 0x04, 0x05, 0x9e, 0xb2, 0x57, 0xe9, 0xe0, 0xba, 0x9f, 0x96, 0xdc, 0xf5, 0x7c, + 0xf5, 0x1c, 0x05, 0xca, 0xd9, 0xbe, 0xef, 0x73, 0xfb, 0xef, 0xf3, 0xb0, 0x70, 0x82, 0x38, 0x1a, + 0x09, 0xf3, 0x0c, 0x56, 0x25, 0x19, 0x45, 0x01, 0x92, 0xc4, 0x4b, 0x19, 0x3e, 0xdb, 0xe9, 0xbb, + 0x9a, 0xf9, 0xab, 0x93, 0xd1, 0xa9, 0xcc, 0xc2, 0x64, 0xd7, 0xe9, 0x69, 0xe9, 0xa9, 0x44, 0x92, + 0xb8, 0x2b, 0x39, 0x46, 0x2a, 0x34, 0x3f, 0x00, 0x4b, 0xf2, 0x58, 0xc8, 0x92, 0x7b, 0x4b, 0xd2, + 0x49, 0x6b, 0xf9, 0x66, 0xbe, 0x9e, 0xd2, 0x55, 0x41, 0x36, 0xd7, 0xd3, 0x6c, 0xfd, 0xbb, 0xd0, + 0xec, 0x29, 0xac, 0xab, 0x19, 0x35, 0x8d, 0xd9, 0x98, 0x1d, 0xf3, 0xb6, 0xb2, 0x9f, 0x04, 0xfd, + 0x0c, 0xcc, 0x44, 0xe0, 0x69, 0xcc, 0xf9, 0xd7, 0x88, 0x33, 0x11, 0x78, 0x12, 0xd2, 0x87, 0x2d, + 0xa1, 0x9a, 0xcf, 0x1b, 0x11, 0xa9, 0x49, 0x3b, 0x0a, 0x48, 0x48, 0xc5, 0x30, 0x07, 0x5f, 0x98, + 0x1d, 0x7c, 0x53, 0x03, 0x7d, 0xaa, 0x70, 0xdc, 0x1c, 0x26, 0xf3, 0xd2, 0x83, 0xf6, 0xf5, 0x5e, + 0x8a, 0x02, 0xdd, 0xd2, 0x05, 0xfa, 0xc1, 0x35, 0x10, 0x45, 0x95, 0x04, 0xbc, 0x53, 0x19, 0x2e, + 0xea, 0x54, 0x7b, 0xfa, 0x40, 0x79, 0x9c, 0x0c, 0x14, 0x03, 0xa3, 0x74, 0xce, 0x10, 0x52, 0x0c, + 0xc8, 0x8c, 0x3d, 0xd4, 0x7d, 0xa7, 0x60, 0x8e, 0x1e, 0xa3, 0x61, 0x76, 0x8b, 0xb0, 0xcb, 0x19, + 0x54, 0x70, 0x84, 0x5b, 0xc1, 0xfa, 0x98, 0x90, 0x47, 0x8d, 0xc5, 0xc5, 0xb5, 0xa6, 0xfd, 0x13, + 0x68, 0xea, 0x23, 0xba, 0x8f, 0x2f, 0x84, 0xb9, 0x05, 0x4d, 0xd5, 0xeb, 0x44, 0x08, 0x22, 0x2c, + 0x43, 0x9f, 0xec, 0x52, 0x60, 0x4b, 0xd8, 0xbc, 0xe9, 0x0e, 0x25, 0xcc, 0xa7, 0x70, 0x2b, 0x22, + 0x7a, 0xc0, 0x6b, 0xc3, 0xd6, 0xde, 0x87, 0xce, 0x0c, 0xd7, 0x59, 0xe7, 0x26, 0x40, 0x37, 0x47, + 0xb3, 0x79, 0x79, 0x73, 0x9b, 0x1a, 0x21, 0xc2, 0x3c, 0x9f, 0x76, 0xfa, 0x8b, 0xd7, 0x72, 0x3a, + 0x85, 0x57, 0xfa, 0x7c, 0x17, 0x5a, 0xfb, 0xe9, 0xb6, 0x7f, 0x49, 0x85, 0xbc, 0x9a, 0x96, 0xa5, + 0x6a, 0x5a, 0x1e, 0xc1, 0x4a, 0x36, 0x0e, 0xcf, 0x98, 0xa6, 0x19, 0xf3, 0x87, 0x00, 0xd9, 0x1c, + 0x55, 0xf4, 0x94, 0x12, 0x71, 0x33, 0x93, 0x1c, 0xf9, 0x13, 0x13, 0xac, 0x36, 0x31, 0xc1, 0x6c, + 0x17, 0x56, 0xcf, 0x05, 0xfe, 0x55, 0x7e, 0x57, 0x7a, 0x12, 0x09, 0xf3, 0x0d, 0x58, 0x50, 0x27, + 0x23, 0x03, 0x6a, 0xb8, 0xf3, 0x89, 0xc0, 0x47, 0x9a, 0x8b, 0xcb, 0xfb, 0x18, 0x8b, 0x3c, 0xea, + 0x0b, 0xab, 0xd6, 0xa9, 0xef, 0x34, 0xdc, 0x95, 0xb8, 0x34, 0x3f, 0xf2, 0x85, 0xfd, 0x6b, 0x68, + 0x55, 0x00, 0xcd, 0x15, 0xa8, 0x15, 0x58, 0x35, 0xea, 0x9b, 0xf7, 0x61, 0xb3, 0x04, 0x9a, 0x24, + 0xd7, 0x14, 0xb1, 0xe9, 0xde, 0x29, 0x14, 0x26, 0xf8, 0x55, 0xd8, 0x4f, 0x60, 0xe3, 0xa8, 0x3c, + 0xca, 0x05, 0x75, 0x4f, 0xec, 0xd0, 0x98, 0x9c, 0xd1, 0x5b, 0xd0, 0x2c, 0x3e, 0x3a, 0xf4, 0xee, + 0x1b, 0x6e, 0x29, 0xb0, 0x47, 0xb0, 0x76, 0x2e, 0xf0, 0x29, 0x09, 0xfd, 0x12, 0xec, 0x86, 0x04, + 0x1c, 0x4c, 0x03, 0xcd, 0x7c, 0xa9, 0x2d, 0xdd, 0x31, 0xd8, 0x3c, 0x47, 0x01, 0xf5, 0x91, 0x64, + 0xfc, 0x94, 0xc8, 0x74, 0xac, 0x9e, 0x20, 0x7c, 0x41, 0xa4, 0x30, 0x5d, 0x68, 0x04, 0x54, 0xc8, + 0xac, 0xb3, 0x3e, 0xb8, 0xb1, 0xb3, 0x92, 0x5d, 0xe7, 0x26, 0x90, 0x43, 0x24, 0x51, 0x76, 0x22, + 0x35, 0x96, 0xfd, 0x63, 0x58, 0xff, 0x14, 0xc9, 0x98, 0x13, 0x7f, 0xa2, 0xc6, 0x6b, 0x50, 0x57, + 0xf5, 0x33, 0x74, 0xfd, 0xd4, 0xa3, 0x9a, 0xf2, 0xd6, 0x83, 0xcf, 0x23, 0xc6, 0x25, 0xf1, 0xaf, + 0x64, 0xe4, 0x15, 0xe9, 0xbd, 0x80, 0x75, 0x95, 0x2c, 0x41, 0x42, 0xdf, 0x2b, 0xf6, 0x99, 0xd6, + 0xb1, 0xb5, 0xf7, 0xf3, 0x99, 0x4e, 0xc7, 0xb4, 0xbb, 0x6c, 0x03, 0xb7, 0x93, 0x29, 0xb9, 0xb0, + 0xff, 0x68, 0x80, 0x75, 0x4c, 0xc6, 0xfb, 0x42, 0xd0, 0x41, 0x38, 0x22, 0xa1, 0x54, 0xcc, 0x86, + 0x30, 0x51, 0x8f, 0xe6, 0x5b, 0xb0, 0x5c, 0x4c, 0x52, 0x3d, 0x40, 0x0d, 0x3d, 0x40, 0x97, 0x72, + 0xa1, 0x3a, 0x60, 0xe6, 0x7d, 0x80, 0x88, 0x93, 0xc4, 0xc3, 0xde, 0x05, 0x19, 0x67, 0x55, 0xdc, + 0xaa, 0x0e, 0xc6, 0xf4, 0x93, 0xd0, 0x39, 0x89, 0xfb, 0x01, 0xc5, 0xc7, 0x64, 0xec, 0x2e, 0x2a, + 0xfd, 0xde, 0x31, 0x19, 0xab, 0x9b, 0x4e, 0xc4, 0x5e, 0x10, 0xae, 0xa7, 0x59, 0xdd, 0x4d, 0x5f, + 0xec, 0x3f, 0x19, 0x70, 0xa7, 0x28, 0x47, 0xde, 0xae, 0x27, 0x71, 0x5f, 0x59, 0xbc, 0x22, 0x6f, + 0x57, 0xa2, 0xad, 0x5d, 0x13, 0xed, 0x47, 0xb0, 0x54, 0x1c, 0x10, 0x15, 0x6f, 0x7d, 0x86, 0x78, + 0x5b, 0xb9, 0xc5, 0x31, 0x19, 0xdb, 0xbf, 0xab, 0xc4, 0x76, 0x30, 0xae, 0x70, 0x1f, 0xff, 0x1f, + 0xb1, 0x15, 0x6e, 0xab, 0xb1, 0xe1, 0xaa, 0xfd, 0x95, 0x0d, 0xd4, 0xaf, 0x6e, 0xc0, 0xfe, 0x8b, + 0x01, 0x1b, 0x55, 0xaf, 0xe2, 0x8c, 0x9d, 0xf0, 0x38, 0x24, 0xaf, 0xf2, 0x5e, 0x1e, 0xbf, 0x5a, + 0xf5, 0xf8, 0x3d, 0x85, 0x95, 0x89, 0xa0, 0x44, 0x96, 0x8d, 0x9f, 0xce, 0xd4, 0x63, 0x15, 0x76, + 0x75, 0x97, 0xab, 0xfb, 0x10, 0xf6, 0x1f, 0x0c, 0x58, 0x7b, 0x12, 0x49, 0xe2, 0x1f, 0x85, 0x45, + 0xae, 0x66, 0x6b, 0xa6, 0x7b, 0xb0, 0xa4, 0x3f, 0x96, 0xf2, 0x4f, 0xee, 0x9a, 0xee, 0x8b, 0x96, + 0x96, 0x65, 0x9f, 0xd3, 0xd7, 0xf6, 0x8c, 0xe2, 0xeb, 0x48, 0x17, 0x4c, 0x57, 0x35, 0xbd, 0xe8, + 0x35, 0xa3, 0xbc, 0x84, 0x07, 0x4f, 0xbf, 0xbe, 0x6c, 0x1b, 0xdf, 0x5c, 0xb6, 0x8d, 0x7f, 0x5d, + 0xb6, 0x8d, 0x2f, 0x5e, 0xb6, 0xe7, 0xbe, 0x79, 0xd9, 0x9e, 0xfb, 0xc7, 0xcb, 0xf6, 0xdc, 0x6f, + 0x3e, 0x1c, 0x50, 0x39, 0x8c, 0xfb, 0x0e, 0x66, 0xa3, 0x6e, 0xf6, 0x07, 0xa2, 0xdc, 0xfd, 0x7b, + 0xc5, 0xef, 0x99, 0xe4, 0x67, 0xdd, 0xcf, 0x27, 0x7f, 0xfe, 0xc8, 0x71, 0x44, 0x44, 0x7f, 0x41, + 0xf3, 0xd4, 0xfb, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x04, 0x1f, 0xf3, 0x49, 0x2d, 0x12, 0x00, + 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -2596,6 +2669,53 @@ func (m *ConsumerAddrsToPrune) MarshalToSizedBuffer(dAtA []byte) (int, error) { 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 @@ -3067,6 +3187,29 @@ func (m *ConsumerAddrsToPrune) 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 } @@ -6360,6 +6503,162 @@ func (m *ConsumerAddrsToPrune) 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