-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Society pallet supports non-consecutive block provider #9497
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
Changes from all commits
9c874d8
b02c1b4
838dace
e813fd1
8b9fdc2
257d556
7f69a64
6cc2487
960368a
a52b177
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,12 @@ | ||
| title: 'Society pallet supports non-consecutive block provider' | ||
| doc: | ||
| - audience: Runtime Dev | ||
| description: |- | ||
| Society pallet correctly handles situations where on_initialize is invoked with block numbers that: | ||
| - increase but are not strictly consecutive (e.g., jump from 5 → 10), or | ||
| - are repeated (e.g., multiple blocks are built at the same Relay Chain parent block, all reporting the same BlockNumberProvider value). | ||
| This situation may occur when the BlockNumberProvider is not local - for example, on a parachain using the Relay Chain block number provider. | ||
|
|
||
| crates: | ||
| - name: pallet-society | ||
| bump: major |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -519,13 +519,14 @@ pub mod pallet { | |
| #[pallet::constant] | ||
| type PeriodSpend: Get<BalanceOf<Self, I>>; | ||
|
|
||
| /// The number of blocks on which new candidates should be voted on. Together with | ||
| /// The number of [Config::BlockNumberProvider] blocks on which new candidates should be | ||
| /// voted on. Together with | ||
| /// `ClaimPeriod`, this sums to the number of blocks between candidate intake periods. | ||
| #[pallet::constant] | ||
| type VotingPeriod: Get<BlockNumberFor<Self, I>>; | ||
|
|
||
| /// The number of blocks on which new candidates can claim their membership and be the | ||
| /// named head. | ||
| /// The number of [Config::BlockNumberProvider] blocks on which new candidates can claim | ||
| /// their membership and be the named head. | ||
| #[pallet::constant] | ||
| type ClaimPeriod: Get<BlockNumberFor<Self, I>>; | ||
|
|
||
|
|
@@ -536,9 +537,9 @@ pub mod pallet { | |
| /// The origin that is allowed to call `found`. | ||
| type FounderSetOrigin: EnsureOrigin<Self::RuntimeOrigin>; | ||
|
|
||
| /// The number of blocks between membership challenges. | ||
| /// The number of [Config::BlockNumberProvider] blocks between membership challenges. | ||
| #[pallet::constant] | ||
| type ChallengePeriod: Get<SystemBlockNumberFor<Self>>; | ||
| type ChallengePeriod: Get<BlockNumberFor<Self, I>>; | ||
|
|
||
| /// The maximum number of payouts a member may have waiting unclaimed. | ||
| #[pallet::constant] | ||
|
|
@@ -784,11 +785,20 @@ pub mod pallet { | |
| pub type DefenderVotes<T: Config<I>, I: 'static = ()> = | ||
| StorageDoubleMap<_, Twox64Concat, RoundIndex, Twox64Concat, T::AccountId, Vote>; | ||
|
|
||
| /// Next intake rotation scheduled with [Config::BlockNumberProvider]. | ||
| #[pallet::storage] | ||
| pub type NextIntakeAt<T: Config<I>, I: 'static = ()> = StorageValue<_, BlockNumberFor<T, I>>; | ||
|
|
||
| /// Next challenge rotation scheduled with [Config::BlockNumberProvider]. | ||
| #[pallet::storage] | ||
| pub type NextChallengeAt<T: Config<I>, I: 'static = ()> = StorageValue<_, BlockNumberFor<T, I>>; | ||
|
|
||
| #[pallet::hooks] | ||
| impl<T: Config<I>, I: 'static> Hooks<SystemBlockNumberFor<T>> for Pallet<T, I> { | ||
| fn on_initialize(n: SystemBlockNumberFor<T>) -> Weight { | ||
| fn on_initialize(_n: SystemBlockNumberFor<T>) -> Weight { | ||
| let mut weight = Weight::zero(); | ||
| let weights = T::BlockWeights::get(); | ||
| let now = T::BlockNumberProvider::current_block_number(); | ||
|
|
||
| let phrase = b"society_rotation"; | ||
| // we'll need a random seed here. | ||
|
|
@@ -801,18 +811,21 @@ pub mod pallet { | |
| let mut rng = ChaChaRng::from_seed(seed); | ||
|
|
||
| // Run a candidate/membership rotation | ||
| match Self::period() { | ||
| Period::Voting { elapsed, .. } if elapsed.is_zero() => { | ||
| Self::rotate_intake(&mut rng); | ||
| weight.saturating_accrue(weights.max_block / 20); | ||
| }, | ||
| _ => {}, | ||
| let is_intake_moment = match Self::period() { | ||
| Period::Intake { .. } => true, | ||
| _ => false, | ||
| }; | ||
| if is_intake_moment { | ||
| Self::rotate_intake(&mut rng); | ||
| weight.saturating_accrue(weights.max_block / 20); | ||
| Self::set_next_intake_at(); | ||
| } | ||
|
|
||
| // Run a challenge rotation | ||
| if (n % T::ChallengePeriod::get()).is_zero() { | ||
| if now >= Self::next_challenge_at() { | ||
| Self::rotate_challenge(&mut rng); | ||
| weight.saturating_accrue(weights.max_block / 20); | ||
| Self::set_next_challenge_at(); | ||
| } | ||
|
|
||
| weight | ||
|
|
@@ -1475,9 +1488,11 @@ impl_ensure_origin_with_arg_ignoring_arg! { | |
| {} | ||
| } | ||
|
|
||
| #[derive(Debug, PartialEq, Eq)] | ||
| pub enum Period<BlockNumber> { | ||
| Voting { elapsed: BlockNumber, more: BlockNumber }, | ||
| Claim { elapsed: BlockNumber, more: BlockNumber }, | ||
| Intake { elapsed: BlockNumber }, | ||
| } | ||
|
|
||
| impl<T: Config<I>, I: 'static> Pallet<T, I> { | ||
|
|
@@ -1488,13 +1503,72 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| let rotation_period = voting_period + claim_period; | ||
| let now = T::BlockNumberProvider::current_block_number(); | ||
| let phase = now % rotation_period; | ||
| if phase < voting_period { | ||
| if now >= Self::next_intake_at() { | ||
| Period::Intake { elapsed: now - Self::next_intake_at() } | ||
| } else if phase < voting_period { | ||
| Period::Voting { elapsed: phase, more: voting_period - phase } | ||
| } else { | ||
| Period::Claim { elapsed: phase - voting_period, more: rotation_period - phase } | ||
| } | ||
| } | ||
|
|
||
| /// Next intake (candidate/membership) rotation scheduled with [Config::BlockNumberProvider]. | ||
| /// | ||
| /// Rounds the previous block number up to the next rotation period (voting + claim periods). | ||
| pub fn next_intake_at() -> BlockNumberFor<T, I> { | ||
| match NextIntakeAt::<T, I>::get() { | ||
| Some(next) => next, | ||
| None => { | ||
| // executed once. | ||
| let now = T::BlockNumberProvider::current_block_number(); | ||
| let prev_block = now.saturating_sub(BlockNumberFor::<T, I>::one()); | ||
| let rotation_period = T::VotingPeriod::get().saturating_add(T::ClaimPeriod::get()); | ||
| let elapsed = prev_block % rotation_period; | ||
| let next_intake_at = prev_block + (rotation_period - elapsed); | ||
| NextIntakeAt::<T, I>::put(next_intake_at); | ||
| next_intake_at | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| /// Set the next intake (candidate/membership) rotation. | ||
| /// | ||
| /// This supposed to be called once the current intake is executed. | ||
| fn set_next_intake_at() { | ||
| let prev_next_intake_at = Self::next_intake_at(); | ||
|
Member
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 have to call the function here instead of directly using
Contributor
Author
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. with the current flow here it cannot be unset. but I think its safer and more correct to get it with that function since we have a lazy initialization for it |
||
| let next_intake_at = prev_next_intake_at | ||
| .saturating_add(T::VotingPeriod::get().saturating_add(T::ClaimPeriod::get())); | ||
| NextIntakeAt::<T, I>::put(next_intake_at); | ||
| } | ||
|
|
||
| /// Returns the next challenge rotation scheduled with [Config::BlockNumberProvider]. | ||
| /// | ||
| /// Rounds the previous block number up to the next multiple of the challenge duration. | ||
| pub fn next_challenge_at() -> BlockNumberFor<T, I> { | ||
| match NextChallengeAt::<T, I>::get() { | ||
| Some(next) => next, | ||
| None => { | ||
| // executed once. | ||
| let now = T::BlockNumberProvider::current_block_number(); | ||
| let prev_block = now.saturating_sub(BlockNumberFor::<T, I>::one()); | ||
| let challenge_period = T::ChallengePeriod::get(); | ||
| let elapsed = prev_block % challenge_period; | ||
| let next_challenge_at = prev_block + (challenge_period - elapsed); | ||
| NextChallengeAt::<T, I>::put(next_challenge_at); | ||
| next_challenge_at | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| /// Set the next challenge rotation. | ||
| /// | ||
| /// This supposed to be called once the current challenge is executed. | ||
| fn set_next_challenge_at() { | ||
| let prev_next_challenge_at = Self::next_challenge_at(); | ||
| let next_challenge_at = prev_next_challenge_at.saturating_add(T::ChallengePeriod::get()); | ||
| NextChallengeAt::<T, I>::put(next_challenge_at); | ||
| } | ||
|
|
||
| /// Returns true if the given `target_round` is still in its initial voting phase. | ||
| fn in_progress(target_round: RoundIndex) -> bool { | ||
| let round = RoundCount::<T, I>::get(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.