-
Notifications
You must be signed in to change notification settings - Fork 102
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
base: feat/non-active-validators
Are you sure you want to change the base?
feat!: Allow validators outside the active set to validate on consumer chains #1878
Conversation
This reverts commit 2e5a91a.
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
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
// Important: must be called before EndBlockVSU, because | ||
// EndBlockVSU needs to know the updated provider validator set | ||
// to compute the minimum power in the top N | ||
providerUpdates := am.keeper.ProviderValidatorUpdates(ctx) |
Check warning
Code scanning / CodeQL
Panic in BeginBock or EndBlock consensus methods Warning
path flow from Begin/EndBlock to a panic call
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.
This function should be part of EndBlockVSU. Just return providerUpdates from EndBlockVSU.
@@ -498,7 +504,7 @@ func New( | |||
// NOTE: Any module instantiated in the module manager that is later modified | |||
// must be passed by reference here. | |||
app.MM = module.NewManager( | |||
genutil.NewAppModule( | |||
wrapped_genutil.NewAppModule( |
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.
Genutil and staking need to be wrapped, because they send validator updates to CometBFT and we need to filter those.
Note Currently processing new changes in this PR. This may take a few minutes, please wait... Files ignored due to path filters (1)
Files selected for processing (38)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
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.
Great work @p-offtermatt. The general logic LGTM. See my comment below.
Things missing:
- migration (e.g., the
LastProviderConsensusValSet
needs to be initialized) - docs on integration (i.e., how to wire this in app.go, especially given the wrapped modules)
- the provider module needs to implement the staking keeper interface, similarly to the consumer module (some of the modules that use the staking keeper need to use the provider keeper instead, e.g., mint, gov, distribution, IBC, ... )
- finalized ADR
@@ -82,6 +88,7 @@ func NewParams( | |||
SlashMeterReplenishFraction: slashMeterReplenishFraction, | |||
ConsumerRewardDenomRegistrationFee: consumerRewardDenomRegistrationFee, | |||
BlocksPerEpoch: blocksPerEpoch, | |||
MaxProviderConsensusValidators: maxProviderConsensusValidators, |
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.
This param needs to be validated in the Validate() below.
// Important: must be called before EndBlockVSU, because | ||
// EndBlockVSU needs to know the updated provider validator set | ||
// to compute the minimum power in the top N | ||
providerUpdates := am.keeper.ProviderValidatorUpdates(ctx) |
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.
This function should be part of EndBlockVSU. Just return providerUpdates from EndBlockVSU.
wrapped_genutil "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_genutil" | ||
wrapped_staking "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_staking" |
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.
We need to find better naming for these two modules.
@@ -129,7 +134,8 @@ var ( | |||
genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), | |||
bank.AppModuleBasic{}, | |||
capability.AppModuleBasic{}, | |||
staking.AppModuleBasic{}, | |||
// staking.AppModuleBasic{}, |
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.
Remove commented code.
@@ -513,7 +519,8 @@ func New( | |||
mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), | |||
slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), | |||
distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), | |||
staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), | |||
// staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), |
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.
ditto: remove commented code
@@ -217,17 +219,18 @@ func (k Keeper) SendVSCPacketsToChain(ctx sdk.Context, chainID, channelID string | |||
func (k Keeper) QueueVSCPackets(ctx sdk.Context) { | |||
valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID | |||
|
|||
// get the bonded validators from the staking module |
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.
This docstring is still relevant. Explain better why bondedValidators
is needed and why consensusValidators
is needed (i.e., for TopN chains).
totalPower := k.stakingKeeper.GetLastTotalPower(ctx) | ||
|
||
// Get total total power of the last consensus validator set on the provider | ||
totalPower := k.GetLastTotalProviderConsensusPower(ctx) |
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.
Note that this changes the behavior. stakingKeeper.GetLastTotalPower
gets the total power from the store, while GetLastTotalProviderConsensusPower
iterates over 180 keys and recomputes the power every time.
ctx sdk.Context, | ||
chainID string, | ||
validator types.ConsumerValidator, | ||
) { | ||
store := ctx.KVStore(k.storeKey) | ||
bz, err := validator.Marshal() | ||
if err != nil { | ||
panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err)) | ||
} | ||
|
||
store.Set(types.ConsumerValidatorKey(chainID, validator.ProviderConsAddr), bz) |
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.
Nice :)
|
||
// InitGenesis delegates the InitGenesis call to the underlying x/genutil module, | ||
// however, it returns no validator updates as validator updates will be provided by the provider module. | ||
func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { |
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.
Why is this needed? We don't have a wrapped genutil in the democracy module on the consumer. How is this different?
} | ||
} | ||
|
||
k.SetLastProviderConsensusValSet(ctx, reducedValSet) |
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.
This should also be exported in ExportGenesis
State: State{ | ||
ChainID("provi"): ChainState{ | ||
ValPowers: &map[ValidatorID]uint{ | ||
ValidatorID("alice"): 0, // alice is still not in the active set, and should now be jailed too |
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.
What do you mean by and should now be jailed too
?
alice
is not in the active set on the provider, so what is this testing? Even if alice
is to be jailed on the provider, can we actually test with ValPowers
here?
Update: I just saw the comment below
// we need to double-check that alice is actually jailed, so we get bob jailed, too, which usually would mean alice gets into power
so maybe that is something you can talk about here as well.
}, | ||
}, | ||
}, | ||
|
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.
nit: remove new line
State: State{ | ||
ChainID("provi"): ChainState{ | ||
ValPowers: &map[ValidatorID]uint{ | ||
ValidatorID("alice"): 100, // alice is back as an active consensus validator |
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.
I guess alice
did not get slashed due to DowntimeSlashAction
because DowntimeSlashAction
was on the consu
chain, right?
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.
Downtime on consumer chains does not slash, just jail
I should comment on the distinction :)
}, | ||
}, | ||
}, | ||
// Οpt in "alice" and "bob" so the chain is not empty when it is about to start. Note, that "alice" and "bob" use |
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.
This is a setup function, so why not opt in all 3 validators at once? What's the benefit of opting in bob
after the chain starts?
@@ -298,3 +299,21 @@ func GetNewCrossChainValidator(t *testing.T) consumertypes.CrossChainValidator { | |||
require.NoError(t, err) | |||
return validator | |||
} | |||
|
|||
// CreateStakingValidator helper function to generate a validator with the given power and with a provider address based on index |
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.
The exact same function exists here. Maybe clean up so that only one function exists (the one you have here).
} | ||
for _, val := range bondedValidators[:maxValidators] { | ||
// create the validator from the staking validator | ||
consAddr, err := val.GetConsAddr() |
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.
You could potentially reuse CreateConsumerValidator
with an empty chainID
.
"error", err) | ||
continue | ||
} | ||
nextValidator := types.ConsumerValidator{ |
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.
General comment but maybe we should not be calling those **Consumer**
validators anymore because they're also provider 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.
Yeah, agreed. Didn't think of a good, unambigious way for this yet
@@ -262,11 +262,14 @@ func (k Keeper) MakeConsumerGenesis( | |||
// get the bonded validators from the staking module | |||
bondedValidators := k.stakingKeeper.GetLastValidators(ctx) | |||
|
|||
// get the consensus validators (to compute the minimal power in the top N) | |||
consensusValidators := k.GetLastProviderConsensusValSet(ctx) |
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.
Why do we need both consensus and bonded validators?
|
||
reducedValSet := make([]types.ConsumerValidator, len(valSet)) | ||
for i, val := range valSet { | ||
consAddr, err := val.GetConsAddr() |
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.
See previous comment on potentially using CreateConsumerValidator
with empty chainID
.
Description
Closes: #1913
This PR allows validators that are not participating in consensus on the provider chain to validate on consumer chains.
The extremely high level overview can be found here (I recommend reading this to get the high-level idea):
https://informalsystems.notion.site/Non-active-validators-on-consumer-chains-d4b700e19a7d447c8d9a2e69cca3e8d0?pvs=74
I recommend then reviewing the e2e test so that you can see what the new logic does in practice.
Other notable files:
Some changed I will make later but which should not block reviewing:
Author Checklist
All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.
I have...
!
to the type prefix if the change is state-machine breakingThe LastConsensusValidatorSet needs to be initialized via migration to get a correct diff, this is still TODO
Maybe this should target a feature branch?
CHANGELOG.md
Reviewers Checklist
All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.
I have...
!
the type prefix if the change is state-machine breaking