Skip to content

Commit

Permalink
more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
insumity committed Feb 13, 2024
1 parent 6e07565 commit 71ac8e3
Show file tree
Hide file tree
Showing 11 changed files with 581 additions and 36 deletions.
39 changes: 39 additions & 0 deletions x/ccv/provider/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions x/ccv/provider/keeper/key_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion x/ccv/provider/keeper/key_assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions x/ccv/provider/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
193 changes: 193 additions & 0 deletions x/ccv/provider/keeper/partial_set_security.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
Loading

0 comments on commit 71ac8e3

Please sign in to comment.