-
Notifications
You must be signed in to change notification settings - Fork 134
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!: Allow validators outside the active set to validate on consumer chains #1878
Changes from 6 commits
0fcb57b
c0d8581
b88cfb5
2e5a91a
2cddff5
acdf06a
48f4085
5ccdb18
48a1aba
66139cc
c11e65d
d21cf9a
87742aa
fb1a8e5
b632aca
519a329
0285039
9e58c13
134d840
70a6bf1
1265836
affd10f
5ea92ea
617b515
9822bbf
b29b867
9b5e578
bfb83ab
a7de72a
faff1a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package keeper | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types" | ||
) | ||
|
||
// SetLastProviderConsensusValidator sets the given validator to be stored | ||
// as part of the last provider consensus validator set | ||
func (k Keeper) SetLastProviderConsensusValidator( | ||
ctx sdk.Context, | ||
validator types.ConsumerValidator, | ||
) { | ||
k.setValidator(ctx, []byte{types.LastProviderConsensusValsPrefix}, validator) | ||
} | ||
|
||
// SetLastProviderConsensusValSet resets the stored last validator set sent to the consensus engine on the provider | ||
// to the provided nextValidators. | ||
func (k Keeper) SetLastProviderConsensusValSet(ctx sdk.Context, nextValidators []types.ConsumerValidator) { | ||
k.setValSet(ctx, []byte{types.LastProviderConsensusValsPrefix}, nextValidators) | ||
} | ||
|
||
// DeleteLastProviderConsensusValidator removes the validator with `providerConsAddr` address | ||
// from the stored last provider consensus validator set | ||
func (k Keeper) DeleteLastProviderConsensusValidator( | ||
ctx sdk.Context, | ||
providerConsAddr types.ProviderConsAddress, | ||
) { | ||
k.deleteValidator(ctx, []byte{types.LastProviderConsensusValsPrefix}, providerConsAddr) | ||
} | ||
|
||
// DeleteLastProviderConsensusValSet deletes all the stored validators from the | ||
// last provider consensus validator set | ||
func (k Keeper) DeleteLastProviderConsensusValSet( | ||
ctx sdk.Context, | ||
) { | ||
k.deleteValSet(ctx, []byte{types.LastProviderConsensusValsPrefix}) | ||
} | ||
|
||
// GetLastProviderConsensusValSet returns the last stored | ||
// validator set sent to the consensus engine on the provider | ||
func (k Keeper) GetLastProviderConsensusValSet( | ||
ctx sdk.Context, | ||
) []types.ConsumerValidator { | ||
return k.getValSet(ctx, []byte{types.LastProviderConsensusValsPrefix}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package keeper_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/cometbft/cometbft/proto/tendermint/crypto" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please reorder the imports according to the Golang style guide. - "github.com/cometbft/cometbft/proto/tendermint/crypto"
+ "github.com/stretchr/testify/require"
+ "github.com/cometbft/cometbft/proto/tendermint/crypto"
Toolsgolangci-lint
|
||
"github.com/stretchr/testify/require" | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please reorder the imports according to the Golang style guide. - "github.com/stretchr/testify/require"
+ "github.com/cosmos/interchain-security/v4/testutil/keeper"
+ "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
+ "github.com/stretchr/testify/require"
Toolsgolangci-lint
|
||
testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" | ||
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types" | ||
) | ||
|
||
func TestSetLastProviderConsensusValidator(t *testing.T) { | ||
providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) | ||
defer ctrl.Finish() | ||
|
||
validator := types.ConsumerValidator{ | ||
ProviderConsAddr: []byte("providerConsAddr"), | ||
Power: 2, | ||
ConsumerPublicKey: &crypto.PublicKey{}, | ||
} | ||
|
||
providerKeeper.SetLastProviderConsensusValidator(ctx, validator) | ||
|
||
// Retrieve the stored validator | ||
storedValidator := providerKeeper.GetLastProviderConsensusValSet(ctx)[0] | ||
|
||
require.Equal(t, validator, storedValidator, "stored validator does not match") | ||
} | ||
|
||
func TestSetLastProviderConsensusValSet(t *testing.T) { | ||
providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) | ||
defer ctrl.Finish() | ||
|
||
validator1 := types.ConsumerValidator{ | ||
ProviderConsAddr: []byte("providerConsAddr1"), | ||
Power: 2, | ||
ConsumerPublicKey: &crypto.PublicKey{}, | ||
} | ||
|
||
validator2 := types.ConsumerValidator{ | ||
ProviderConsAddr: []byte("providerConsAddr2"), | ||
Power: 3, | ||
ConsumerPublicKey: &crypto.PublicKey{}, | ||
} | ||
|
||
nextValidators := []types.ConsumerValidator{validator1, validator2} | ||
|
||
providerKeeper.SetLastProviderConsensusValSet(ctx, nextValidators) | ||
|
||
// Retrieve the stored validator set | ||
storedValidators := providerKeeper.GetLastProviderConsensusValSet(ctx) | ||
require.Equal(t, nextValidators, storedValidators, "stored validator set does not match") | ||
} | ||
|
||
func TestDeleteLastProviderConsensusValidator(t *testing.T) { | ||
providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) | ||
defer ctrl.Finish() | ||
|
||
validator := types.ConsumerValidator{ | ||
ProviderConsAddr: []byte("providerConsAddr"), | ||
Power: 2, | ||
ConsumerPublicKey: &crypto.PublicKey{}, | ||
} | ||
|
||
providerKeeper.SetLastProviderConsensusValidator(ctx, validator) | ||
|
||
// Delete the stored validator | ||
providerKeeper.DeleteLastProviderConsensusValidator(ctx, types.NewProviderConsAddress(validator.ProviderConsAddr)) | ||
|
||
// Ensure the validator is deleted | ||
storedValidators := providerKeeper.GetLastProviderConsensusValSet(ctx) | ||
require.Empty(t, storedValidators, "validator set should be empty") | ||
} | ||
|
||
func TestDeleteLastProviderConsensusValSet(t *testing.T) { | ||
providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) | ||
defer ctrl.Finish() | ||
|
||
validator1 := types.ConsumerValidator{ | ||
ProviderConsAddr: []byte("providerConsAddr1"), | ||
Power: 2, | ||
ConsumerPublicKey: &crypto.PublicKey{}, | ||
} | ||
|
||
validator2 := types.ConsumerValidator{ | ||
ProviderConsAddr: []byte("providerConsAddr2"), | ||
Power: 3, | ||
ConsumerPublicKey: &crypto.PublicKey{}, | ||
} | ||
|
||
nextValidators := []types.ConsumerValidator{validator1, validator2} | ||
|
||
providerKeeper.SetLastProviderConsensusValSet(ctx, nextValidators) | ||
|
||
// Delete the stored validator set | ||
providerKeeper.DeleteLastProviderConsensusValSet(ctx) | ||
|
||
// Ensure the validator set is empty | ||
storedValidators := providerKeeper.GetLastProviderConsensusValSet(ctx) | ||
require.Empty(t, storedValidators, "validator set should be empty") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,8 @@ import ( | |
sdk "github.com/cosmos/cosmos-sdk/types" | ||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" | ||
|
||
abci "github.com/cometbft/cometbft/abci/types" | ||
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types" | ||
Comment on lines
+15
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure the It appears that the |
||
providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" | ||
ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" | ||
) | ||
|
@@ -256,6 +258,49 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { | |
k.IncrementValidatorSetUpdateId(ctx) | ||
} | ||
|
||
func (k Keeper) ProviderValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is quite important and is not called in any test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Also add docstring. |
||
// get the bonded validators from the staking module | ||
bondedValidators := k.stakingKeeper.GetLastValidators(ctx) | ||
|
||
// get the last validator set sent to consensus | ||
currentValidators := k.GetLastProviderConsensusValSet(ctx) | ||
|
||
MAX_CONSENSUS_VALIDATORS := 180 // TODO: make this a parameter | ||
|
||
nextValidators := []types.ConsumerValidator{} | ||
for _, val := range bondedValidators[:MAX_CONSENSUS_VALIDATORS] { | ||
// create the validator from the staking validator | ||
consAddr, err := val.GetConsAddr() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could potentially reuse |
||
if err != nil { | ||
k.Logger(ctx).Error("could not create consumer validator", | ||
"validator", val.GetOperator().String(), | ||
"error", err) | ||
continue | ||
} | ||
pubKey, err := val.TmConsPublicKey() | ||
if err != nil { | ||
k.Logger(ctx).Error("could not create consumer validator", | ||
"validator", val.GetOperator().String(), | ||
"error", err) | ||
continue | ||
} | ||
nextValidator := types.ConsumerValidator{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. General comment but maybe we should not be calling those There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, agreed. Didn't think of a good, unambigious way for this yet |
||
ProviderConsAddr: consAddr, | ||
ConsumerPublicKey: &pubKey, | ||
Power: k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()), | ||
} | ||
|
||
nextValidators = append(nextValidators, nextValidator) | ||
} | ||
|
||
// store the validator set we will send to consensus | ||
k.SetLastProviderConsensusValSet(ctx, nextValidators) | ||
|
||
valUpdates := DiffValidators(currentValidators, nextValidators) | ||
|
||
return valUpdates | ||
} | ||
|
||
// BeginBlockCIS contains the BeginBlock logic needed for the Consumer Initiated Slashing sub-protocol. | ||
func (k Keeper) BeginBlockCIS(ctx sdk.Context) { | ||
// Replenish slash meter if necessary. This ensures the meter value is replenished before handling any slash packets, | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,94 @@ | ||||||
package keeper | ||||||
|
||||||
import ( | ||||||
"fmt" | ||||||
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types" | ||||||
|
||||||
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types" | ||||||
) | ||||||
|
||||||
// getValidatorKey constructs the key to access a given validator, stored under a given prefix. | ||||||
func (k Keeper) getValidatorKey(prefix []byte, providerAddr types.ProviderConsAddress) []byte { | ||||||
return append(prefix, providerAddr.ToSdkConsAddr()...) | ||||||
} | ||||||
|
||||||
// setValidator stores the given `validator` in the validator set stored under the given prefix. | ||||||
func (k Keeper) setValidator( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All these methods should be tested. |
||||||
ctx sdk.Context, | ||||||
prefix []byte, | ||||||
validator types.ConsumerValidator, | ||||||
) { | ||||||
store := ctx.KVStore(k.storeKey) | ||||||
bz, err := validator.Marshal() | ||||||
if err != nil { | ||||||
panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err)) | ||||||
Check warning Code scanning / CodeQL Panic in BeginBock or EndBlock consensus methods Warning
Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace panics with proper error handling in the - panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err))
+ return fmt.Errorf("failed to marshal ConsumerValidator: %w", err)
- panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err))
+ return fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err) Also applies to: 89-89 Committable suggestion
Suggested change
ToolsGitHub Check: CodeQL
|
||||||
} | ||||||
|
||||||
store.Set(k.getValidatorKey(prefix, types.NewProviderConsAddress(validator.ProviderConsAddr)), bz) | ||||||
} | ||||||
|
||||||
// setValSet resets the validator set stored under the given prefix to the provided `nextValidators`. | ||||||
func (k Keeper) setValSet(ctx sdk.Context, prefix []byte, nextValidators []types.ConsumerValidator) { | ||||||
k.deleteValSet(ctx, prefix) | ||||||
for _, val := range nextValidators { | ||||||
k.setValidator(ctx, prefix, val) | ||||||
} | ||||||
} | ||||||
|
||||||
// deleteValidator removes validator with `providerAddr` address from the | ||||||
// validator set stored under the given prefix. | ||||||
func (k Keeper) deleteValidator( | ||||||
ctx sdk.Context, | ||||||
prefix []byte, | ||||||
providerConsAddr types.ProviderConsAddress, | ||||||
) { | ||||||
store := ctx.KVStore(k.storeKey) | ||||||
store.Delete(k.getValidatorKey(prefix, providerConsAddr)) | ||||||
} | ||||||
|
||||||
// deleteValSet deletes all the stored consumer validators under the given prefix. | ||||||
func (k Keeper) deleteValSet( | ||||||
ctx sdk.Context, | ||||||
prefix []byte, | ||||||
) { | ||||||
store := ctx.KVStore(k.storeKey) | ||||||
iterator := sdk.KVStorePrefixIterator(store, prefix) | ||||||
|
||||||
var keysToDel [][]byte | ||||||
defer iterator.Close() | ||||||
for ; iterator.Valid(); iterator.Next() { | ||||||
keysToDel = append(keysToDel, iterator.Key()) | ||||||
} | ||||||
for _, delKey := range keysToDel { | ||||||
store.Delete(delKey) | ||||||
} | ||||||
} | ||||||
|
||||||
// isValidator returns `true` if the validator with `providerAddr` exists | ||||||
// in the validator set stored under the given prefix. | ||||||
func (k Keeper) isValidator(ctx sdk.Context, prefix []byte, providerAddr types.ProviderConsAddress) bool { | ||||||
store := ctx.KVStore(k.storeKey) | ||||||
return store.Get(k.getValidatorKey(prefix, providerAddr)) != nil | ||||||
} | ||||||
|
||||||
// getValSet returns all the validators stored under the given prefix. | ||||||
func (k Keeper) getValSet( | ||||||
ctx sdk.Context, | ||||||
key []byte, | ||||||
) (validators []types.ConsumerValidator) { | ||||||
store := ctx.KVStore(k.storeKey) | ||||||
iterator := sdk.KVStorePrefixIterator(store, key) | ||||||
defer iterator.Close() | ||||||
|
||||||
for ; iterator.Valid(); iterator.Next() { | ||||||
iterator.Value() | ||||||
var validator types.ConsumerValidator | ||||||
if err := validator.Unmarshal(iterator.Value()); err != nil { | ||||||
panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)) | ||||||
Check warning Code scanning / CodeQL Panic in BeginBock or EndBlock consensus methods Warning
Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
|
||||||
} | ||||||
validators = append(validators, validator) | ||||||
} | ||||||
|
||||||
return validators | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tip
Codebase Verification
The search results indicate that the field
max_provider_consensus_validators
is defined in theprovider.proto
file but there is no evidence of its documentation or validation in the codebase.max_provider_consensus_validators
is properly documented.max_provider_consensus_validators
to ensure it is correctly configured and used.Analysis chain
Ensure the new field
max_provider_consensus_validators
is properly documented and validated.Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 407
Script:
Length of output: 162
Script:
Length of output: 162
Script:
Length of output: 244