From f333487240dab9fe3bfae131e1ab6258a891e657 Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 29 Feb 2024 14:38:54 +0100 Subject: [PATCH 1/4] modified ADR --- docs/docs/adrs/adr-014-epochs.md | 39 +++++++++++++++++++------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/docs/docs/adrs/adr-014-epochs.md b/docs/docs/adrs/adr-014-epochs.md index ce75063ec7..bc87e6881d 100644 --- a/docs/docs/adrs/adr-014-epochs.md +++ b/docs/docs/adrs/adr-014-epochs.md @@ -5,7 +5,8 @@ title: Epochs # ADR 014: Epochs ## Changelog -* 2024-01-105: Proposed, first draft of ADR. +* 2024-01-05: Proposed, first draft of ADR. +* 2024-02-29: Updated so that it describes the implementation where we store the whole consumer validator set. ## Status @@ -22,24 +23,29 @@ As a matter of fact, this already happens due to relaying delays. As a solution, this ADR introduces the concept of _epochs_. An epoch consists of multiple blocks. The provider sends `VSCPacket`s once per epoch. -A `VSCPacket` contains all the valset changes that occurred throughout the epoch. +A `VSCPacket` contains all the validator updates that are needed by consumer chains. ## Decision The implementation of epochs requires the following changes: -- Add a param that sets the number of blocks in an epoch, i.e., `BlocksPerEpoch`. - We can use `BlockHeight() % BlocksPerEpoch == 0` to decide when an epoch is over. - Note that `BlocksPerEpoch` can also be a hardcoded constant as it's unlikely that it will change often. -- In every provider `EndBlock()`, instead of queueing `VSCPacket` data for every consumer chain, we accumulate the validator changes (similarly to how is done on the consumer, see `AccumulateChanges`). -- Modify the key assignment logic to allow for `MustApplyKeyAssignmentToValUpdates` to be called once per epoch. - Currently, this method is called in every block before queueing a `VSCPacket`. - Also, the method uses the `KeyAssignmentReplacement` state, which is pruned at the end of every block. - This needs to be done once per epoch instead. -- At the end of every epoch, if there were validator set changes on the provider, then for every consumer chain, construct a `VSCPacket` with all the accumulated validator changes and add it to the list of `PendingVSCPackets`. - -As an optional change, to better accommodate [the Partial Set Security design](https://informalsystems.notion.site/Partial-Set-Security-398ca9a1453740068be5c7964a4059bb), the validator changes should be accumulated per consumer chain. -Like this, it would make it easier to have validators opting out from certain consumer chains. +- For each consumer chain, we store the consumer validator set that is currently (i.e., in this epoch) validating the + consumer chain. For each validator in the set we store its voting power as well as the public key that it is + using on the consumer chain. Specifically, for each consumer chain, we store a list of `ConsumerValidator`s where each + `ConsumerValidator` contains the i) power the validator has, and ii) the public key the validator + uses on the consumer chain during the epoch. + The initial consumer validator set for a chain is set during the creation of the consumer genesis. +- We add the `BlocksPerEpoch` param that sets the number of blocks in an epoch. In the provider `EndBlock` we check + `BlockHeight() % BlocksPerEpoch() == 0` to decide when an epoch has ended. +- At the end of every epoch, if there were validator set changes on the provider, then for every consumer chain, we + construct a `VSCPacket` with all the validator updates and add it to the list of `PendingVSCPackets`. We compute the + validator updates needed by a consumer chain by comparing the stored list of `ConsumerValidator`s with the current + bonded validators on the provider, with something similar to this: +```go +currentValidators := GetConsumerValSet(consumerChain) +valUpdates := DiffValidators(currentValidators, stakingmodule.GetBondedValidators()) +SetConsumerValSet(consumerChain, nextValidators) +``` ## Consequences @@ -47,12 +53,13 @@ Like this, it would make it easier to have validators opting out from certain co - Reduce the cost of relaying. - Reduce the amount of IBC packets needed for ICS. +- Simplifies [key-assignment code](https://github.com/cosmos/interchain-security/blob/main/docs/docs/adrs/adr-001-key-assignment.md) because + we only need to check if the `consumer_public_key` has been modified since the last epoch to generate an update. ### Negative -- Additional logic on the provider side as valset changes need to be accumulated. -- The changes might impact the key-assignment logic so special care is needed to avoid introducing bugs. - Increase the delay in the propagation of validator set changes (but for reasonable epoch lengths on the order of ~hours or less, this is unlikely to be significant). + ### Neutral N/A From 38bdd7fbf80f2310d7d401e4bd525b93cec3d27b Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 29 Feb 2024 14:49:43 +0100 Subject: [PATCH 2/4] small updates --- docs/docs/adrs/adr-014-epochs.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/docs/adrs/adr-014-epochs.md b/docs/docs/adrs/adr-014-epochs.md index bc87e6881d..0b667d6a3f 100644 --- a/docs/docs/adrs/adr-014-epochs.md +++ b/docs/docs/adrs/adr-014-epochs.md @@ -30,21 +30,23 @@ A `VSCPacket` contains all the validator updates that are needed by consumer cha The implementation of epochs requires the following changes: - For each consumer chain, we store the consumer validator set that is currently (i.e., in this epoch) validating the - consumer chain. For each validator in the set we store its voting power as well as the public key that it is - using on the consumer chain. Specifically, for each consumer chain, we store a list of `ConsumerValidator`s where each - `ConsumerValidator` contains the i) power the validator has, and ii) the public key the validator - uses on the consumer chain during the epoch. + consumer chain. For each validator in the set we store i) its voting power, and ii) the public key that it is + using on the consumer chain during the current (i.e., ongoing) epoch. The initial consumer validator set for a chain is set during the creation of the consumer genesis. - We add the `BlocksPerEpoch` param that sets the number of blocks in an epoch. In the provider `EndBlock` we check `BlockHeight() % BlocksPerEpoch() == 0` to decide when an epoch has ended. - At the end of every epoch, if there were validator set changes on the provider, then for every consumer chain, we construct a `VSCPacket` with all the validator updates and add it to the list of `PendingVSCPackets`. We compute the - validator updates needed by a consumer chain by comparing the stored list of `ConsumerValidator`s with the current + validator updates needed by a consumer chain by comparing the stored list of consumer validators with the current bonded validators on the provider, with something similar to this: ```go +// get the valset that has been validating the consumer chain during this epoch currentValidators := GetConsumerValSet(consumerChain) +// generate the validator updates needed to be sent through a `VSCPacket` by comparing the current validators +// in the epoch with the latest bonded validators valUpdates := DiffValidators(currentValidators, stakingmodule.GetBondedValidators()) -SetConsumerValSet(consumerChain, nextValidators) +// update the current validators set for the upcoming epoch to be the latest bonded validators instead +SetConsumerValSet(stakingmodule.GetBondedValidators()) ``` ## Consequences From 8774209bc06d30b96da24be34dafe1f9db73aa52 Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 1 Mar 2024 16:22:47 +0100 Subject: [PATCH 3/4] added one more sentence on multiple key-assignment changes --- docs/docs/adrs/adr-014-epochs.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docs/adrs/adr-014-epochs.md b/docs/docs/adrs/adr-014-epochs.md index 0b667d6a3f..df54c6257d 100644 --- a/docs/docs/adrs/adr-014-epochs.md +++ b/docs/docs/adrs/adr-014-epochs.md @@ -48,6 +48,10 @@ valUpdates := DiffValidators(currentValidators, stakingmodule.GetBondedValidator // update the current validators set for the upcoming epoch to be the latest bonded validators instead SetConsumerValSet(stakingmodule.GetBondedValidators()) ``` +Note that a validator can change its consumer public key for a specific consumer chain an arbitrary amount of times during +a block and during an epoch. Then, when we generate the validator updates in `DiffValidators`, we have to check on whether +the current consumer public key (retrieved by calling `GetValidatorConsumerPubKey`) is different from the consumer public +key the validator was using in the current epoch. ## Consequences From 5e7df669330c41f4f15b0944ad905f37cc724783 Mon Sep 17 00:00:00 2001 From: insumity Date: Tue, 5 Mar 2024 11:25:02 +0100 Subject: [PATCH 4/4] added comment that the BlocksPerEpoch param can be changed through a gov proposal --- docs/docs/adrs/adr-014-epochs.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/docs/adrs/adr-014-epochs.md b/docs/docs/adrs/adr-014-epochs.md index df54c6257d..2f4f5df087 100644 --- a/docs/docs/adrs/adr-014-epochs.md +++ b/docs/docs/adrs/adr-014-epochs.md @@ -33,8 +33,11 @@ The implementation of epochs requires the following changes: consumer chain. For each validator in the set we store i) its voting power, and ii) the public key that it is using on the consumer chain during the current (i.e., ongoing) epoch. The initial consumer validator set for a chain is set during the creation of the consumer genesis. -- We add the `BlocksPerEpoch` param that sets the number of blocks in an epoch. In the provider `EndBlock` we check - `BlockHeight() % BlocksPerEpoch() == 0` to decide when an epoch has ended. +- We introduce the `BlocksPerEpoch` param that sets the number of blocks in an epoch. By default, `BlocksPerEpoch` is + set to be 600 which corresponds to 1 hour, assuming 6 seconds per block. This param can be changed through + a _governance proposal_ to be anywhere between `[1, MaxBlocksPerEpoch]` where `MaxBlocksPerEpoch` can be up to 1200 + (2 hours if we assume 6 seconds per block). In the provider `EndBlock` we check `BlockHeight() % BlocksPerEpoch() == 0` + to decide when an epoch has ended. - At the end of every epoch, if there were validator set changes on the provider, then for every consumer chain, we construct a `VSCPacket` with all the validator updates and add it to the list of `PendingVSCPackets`. We compute the validator updates needed by a consumer chain by comparing the stored list of consumer validators with the current