Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: only allow chains with at least one active validator to launch #2399

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion x/ccv/provider/keeper/consumer_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,33 @@ func (k Keeper) ConsumeIdsFromTimeQueue(
return result, nil
}

// HasActiveConsumerValidator checks whether at least one active validator is opted in to chain with `consumerId`
func (k Keeper) HasActiveConsumerValidator(ctx sdk.Context, consumerId string, activeValidators []stakingtypes.Validator) (bool, error) {
currentValidatorSet, err := k.GetConsumerValSet(ctx, consumerId)
if err != nil {
return false, err
}

isActiveValidator := make(map[string]bool)
for _, val := range activeValidators {
consAddr, err := val.GetConsAddr()
if err != nil {
return false, fmt.Errorf("creating consumer genesis state, consumerId(%s): %w", consumerId, err)
}
providerConsAddr := types.NewProviderConsAddress(consAddr)
isActiveValidator[providerConsAddr.String()] = true
}

for _, val := range currentValidatorSet {
providerConsAddr := types.NewProviderConsAddress(val.ProviderConsAddr)
if isActiveValidator[providerConsAddr.String()] {
return true, nil
}
}

return false, nil
}

// LaunchConsumer launches the chain with the provided consumer id by creating the consumer client and the respective
// consumer genesis file
//
Expand All @@ -205,8 +232,17 @@ func (k Keeper) LaunchConsumer(
if err != nil {
return fmt.Errorf("computing consumer next validator set, consumerId(%s): %w", consumerId, err)
}

if len(initialValUpdates) == 0 {
return fmt.Errorf("cannot launch consumer with no validator opted in, consumerId(%s)", consumerId)
return fmt.Errorf("cannot launch consumer with no consumer validator, consumerId(%s)", consumerId)
}

hasActiveConsumerValidator, err := k.HasActiveConsumerValidator(ctx, consumerId, activeValidators)
if err != nil {
return fmt.Errorf("cannot check if chain has an active consumer validator, consumerId(%s): %w", consumerId, err)
}
if !hasActiveConsumerValidator {
return fmt.Errorf("cannot launch consumer with no active consumer validator, consumerId(%s)", consumerId)
}

// create consumer genesis
Expand Down
61 changes: 61 additions & 0 deletions x/ccv/provider/keeper/consumer_lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,67 @@ func TestConsumeIdsFromTimeQueue(t *testing.T) {
}
}

func TestHasActiveValidatorOptedIn(t *testing.T) {
keeperParams := testkeeper.NewInMemKeeperParams(t)
providerKeeper, ctx, _, mocks := testkeeper.GetProviderKeeperAndCtx(t, keeperParams)

// set 5 bonded validators with powers 5, 4, 3, 2, and 1
NumberOfBondedValidators := 5
var bondedValidators []stakingtypes.Validator
for i := 0; i < NumberOfBondedValidators; i++ {
power := int64(NumberOfBondedValidators - i)
bondedValidators = append(bondedValidators, createStakingValidator(ctx, mocks, power, i))
}
mocks.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower(gomock.Any()).Return(bondedValidators, nil).AnyTimes()

// get the consensus addresses of the previously-set bonded validators
var consensusAddresses [][]byte
for i := 0; i < NumberOfBondedValidators; i++ {
consAddr, _ := bondedValidators[i].GetConsAddr()
consensusAddresses = append(consensusAddresses, consAddr)
}

// Set the maximum number of provider consensus active validators (i.e., active validators) to 3. As a result
// `bondedValidators[0]` (with power of 5), `bondedValidators[1]` (with power of 4), `bondedValidators[2]` (with power of 3)
// are the active validators, and `bondedValidators[3]` (with power of 2) and `bondedValidators[4]` (with power of 1)
// are non-active validators.
maxProviderConsensusValidators := int64(3)
params := providerKeeper.GetParams(ctx)
params.MaxProviderConsensusValidators = maxProviderConsensusValidators
providerKeeper.SetParams(ctx, params)

activeValidators, _ := providerKeeper.GetLastProviderConsensusActiveValidators(ctx)

consumerId := "0"

// consumer chain has only non-active validators
err := providerKeeper.SetConsumerValSet(ctx, consumerId, []providertypes.ConsensusValidator{
{ProviderConsAddr: consensusAddresses[3]},
{ProviderConsAddr: consensusAddresses[4]}})
require.NoError(t, err)
hasActiveValidatorOptedIn, err := providerKeeper.HasActiveConsumerValidator(ctx, consumerId, activeValidators)
require.NoError(t, err)
require.False(t, hasActiveValidatorOptedIn)

// consumer chain has one active validator
err = providerKeeper.SetConsumerValSet(ctx, consumerId, []providertypes.ConsensusValidator{
{ProviderConsAddr: consensusAddresses[2]}})
require.NoError(t, err)
hasActiveValidatorOptedIn, err = providerKeeper.HasActiveConsumerValidator(ctx, consumerId, activeValidators)
require.NoError(t, err)
require.True(t, hasActiveValidatorOptedIn)

// consumer chain has one active and two non-active validators
err = providerKeeper.SetConsumerValSet(ctx, consumerId, []providertypes.ConsensusValidator{
{ProviderConsAddr: consensusAddresses[3]},
{ProviderConsAddr: consensusAddresses[4]},
{ProviderConsAddr: consensusAddresses[1]}})
require.NoError(t, err)
hasActiveValidatorOptedIn, err = providerKeeper.HasActiveConsumerValidator(ctx, consumerId, activeValidators)
require.NoError(t, err)
require.True(t, hasActiveValidatorOptedIn)
}

func TestCreateConsumerClient(t *testing.T) {
type testCase struct {
description string
Expand Down
Loading