diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs index a052a9d3800cc6880ccee84358883194cb46eb80..8b0842697237fb51b0097b49d8f74b18fab32970 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs @@ -154,6 +154,7 @@ impl pallet_referenda::Config<AmbassadorReferendaInstance> for Runtime { type AlarmInterval = AlarmInterval; type Tracks = tracks::TracksInfo; type Preimages = Preimage; + type BlockNumberProvider = System; } parameter_types! { diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs index 1e8212cf6ac280a358fe168fc1e22d1eed6fc274..e699f2eeaf43862d204d8f21e400acab3da6cbe1 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs @@ -106,6 +106,7 @@ impl pallet_referenda::Config<FellowshipReferendaInstance> for Runtime { type AlarmInterval = ConstU32<1>; type Tracks = tracks::TracksInfo; type Preimages = Preimage; + type BlockNumberProvider = crate::System; } pub type FellowshipCollectiveInstance = pallet_ranked_collective::Instance1; diff --git a/polkadot/runtime/rococo/src/governance/fellowship.rs b/polkadot/runtime/rococo/src/governance/fellowship.rs index 27a58a0eebd183bc5d21efa655113cf3a0251cc0..231defab6aa52419b42131a510637e0b1c04c889 100644 --- a/polkadot/runtime/rococo/src/governance/fellowship.rs +++ b/polkadot/runtime/rococo/src/governance/fellowship.rs @@ -308,6 +308,7 @@ impl pallet_referenda::Config<FellowshipReferendaInstance> for Runtime { type AlarmInterval = AlarmInterval; type Tracks = TracksInfo; type Preimages = Preimage; + type BlockNumberProvider = System; } pub type FellowshipCollectiveInstance = pallet_ranked_collective::Instance1; diff --git a/polkadot/runtime/rococo/src/governance/mod.rs b/polkadot/runtime/rococo/src/governance/mod.rs index ef2adf60753d541c44eaf6073a3a012e66bf9f5c..2be549be29ed14a80c3856cc0db2fa359c83cbcc 100644 --- a/polkadot/runtime/rococo/src/governance/mod.rs +++ b/polkadot/runtime/rococo/src/governance/mod.rs @@ -90,4 +90,5 @@ impl pallet_referenda::Config for Runtime { type AlarmInterval = AlarmInterval; type Tracks = TracksInfo; type Preimages = Preimage; + type BlockNumberProvider = System; } diff --git a/polkadot/runtime/westend/src/governance/mod.rs b/polkadot/runtime/westend/src/governance/mod.rs index d027f788d71f6bf255b71157bfe21ba1fdc16f5a..abc25ebaa470833a9bd188b5747ac641638a2fcc 100644 --- a/polkadot/runtime/westend/src/governance/mod.rs +++ b/polkadot/runtime/westend/src/governance/mod.rs @@ -94,4 +94,5 @@ impl pallet_referenda::Config for Runtime { type AlarmInterval = AlarmInterval; type Tracks = TracksInfo; type Preimages = Preimage; + type BlockNumberProvider = System; } diff --git a/prdoc/pr_6338.prdoc b/prdoc/pr_6338.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..68d01904d071975de79188b98ba06cd8aaae8bc7 --- /dev/null +++ b/prdoc/pr_6338.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Update Referenda to Support Block Number Provider + +doc: + - audience: Runtime Dev + description: | + This PR makes the referenda pallet uses the relay chain as a block provider for a parachain on a regular schedule. + To migrate existing referenda implementations, simply add `type BlockNumberProvider = System` to have the same behavior as before. + +crates: +- name: pallet-referenda + bump: major \ No newline at end of file diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 905b8735c5df9411e2a85f036159ff0a6968f00c..610ef4fb629a106bf4fc990b7bb6e88bb22da52d 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1073,6 +1073,7 @@ impl pallet_referenda::Config for Runtime { type AlarmInterval = AlarmInterval; type Tracks = TracksInfo; type Preimages = Preimage; + type BlockNumberProvider = System; } impl pallet_referenda::Config<pallet_referenda::Instance2> for Runtime { @@ -1093,6 +1094,7 @@ impl pallet_referenda::Config<pallet_referenda::Instance2> for Runtime { type AlarmInterval = AlarmInterval; type Tracks = TracksInfo; type Preimages = Preimage; + type BlockNumberProvider = System; } impl pallet_ranked_collective::Config for Runtime { diff --git a/substrate/frame/referenda/src/benchmarking.rs b/substrate/frame/referenda/src/benchmarking.rs index 67ac82787d31d78a859ed0158d7f6be2b1276aea..895f95dbec556e77781f9d46f2cf552ed8cb0956 100644 --- a/substrate/frame/referenda/src/benchmarking.rs +++ b/substrate/frame/referenda/src/benchmarking.rs @@ -33,6 +33,10 @@ use sp_runtime::traits::Bounded as ArithBounded; const SEED: u32 = 0; +fn set_block_number<T: Config<I>, I: 'static>(n: BlockNumberFor<T, I>) { + <T as Config<I>>::BlockNumberProvider::set_block_number(n); +} + fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) { frame_system::Pallet::<T>::assert_last_event(generic_event.into()); } @@ -151,30 +155,28 @@ fn make_failing<T: Config<I>, I: 'static>(index: ReferendumIndex) { fn skip_prepare_period<T: Config<I>, I: 'static>(index: ReferendumIndex) { let status = Referenda::<T, I>::ensure_ongoing(index).unwrap(); let prepare_period_over = status.submitted + info::<T, I>(index).prepare_period; - frame_system::Pallet::<T>::set_block_number(prepare_period_over); + set_block_number::<T, I>(prepare_period_over); } fn skip_decision_period<T: Config<I>, I: 'static>(index: ReferendumIndex) { let status = Referenda::<T, I>::ensure_ongoing(index).unwrap(); let decision_period_over = status.deciding.unwrap().since + info::<T, I>(index).decision_period; - frame_system::Pallet::<T>::set_block_number(decision_period_over); + set_block_number::<T, I>(decision_period_over); } fn skip_confirm_period<T: Config<I>, I: 'static>(index: ReferendumIndex) { let status = Referenda::<T, I>::ensure_ongoing(index).unwrap(); let confirm_period_over = status.deciding.unwrap().confirming.unwrap(); - frame_system::Pallet::<T>::set_block_number(confirm_period_over); + set_block_number::<T, I>(confirm_period_over); } fn skip_timeout_period<T: Config<I>, I: 'static>(index: ReferendumIndex) { let status = Referenda::<T, I>::ensure_ongoing(index).unwrap(); let timeout_period_over = status.submitted + T::UndecidingTimeout::get(); - frame_system::Pallet::<T>::set_block_number(timeout_period_over); + set_block_number::<T, I>(timeout_period_over); } -fn alarm_time<T: Config<I>, I: 'static>( - index: ReferendumIndex, -) -> frame_system::pallet_prelude::BlockNumberFor<T> { +fn alarm_time<T: Config<I>, I: 'static>(index: ReferendumIndex) -> BlockNumberFor<T, I> { let status = Referenda::<T, I>::ensure_ongoing(index).unwrap(); status.alarm.unwrap().0 } diff --git a/substrate/frame/referenda/src/lib.rs b/substrate/frame/referenda/src/lib.rs index e72dd7f11cbb2f8f17f32ec9d9b1768ee015ec2d..e6a895f9c5933961a831525035f7201d9e20cbe3 100644 --- a/substrate/frame/referenda/src/lib.rs +++ b/substrate/frame/referenda/src/lib.rs @@ -82,7 +82,6 @@ use frame_support::{ }, BoundedVec, }; -use frame_system::pallet_prelude::BlockNumberFor; use scale_info::TypeInfo; use sp_runtime::{ traits::{AtLeast32BitUnsigned, Bounded, Dispatchable, One, Saturating, Zero}, @@ -98,14 +97,15 @@ use self::branch::{BeginDecidingBranch, OneFewerDecidingBranch, ServiceBranch}; pub use self::{ pallet::*, types::{ - BalanceOf, BoundedCallOf, CallOf, Curve, DecidingStatus, DecidingStatusOf, Deposit, - InsertSorted, NegativeImbalanceOf, PalletsOriginOf, ReferendumIndex, ReferendumInfo, - ReferendumInfoOf, ReferendumStatus, ReferendumStatusOf, ScheduleAddressOf, TallyOf, - TrackIdOf, TrackInfo, TrackInfoOf, TracksInfo, VotesOf, + BalanceOf, BlockNumberFor, BoundedCallOf, CallOf, Curve, DecidingStatus, DecidingStatusOf, + Deposit, InsertSorted, NegativeImbalanceOf, PalletsOriginOf, ReferendumIndex, + ReferendumInfo, ReferendumInfoOf, ReferendumStatus, ReferendumStatusOf, ScheduleAddressOf, + TallyOf, TrackIdOf, TrackInfo, TrackInfoOf, TracksInfo, VotesOf, }, weights::WeightInfo, }; pub use alloc::vec::Vec; +use sp_runtime::traits::BlockNumberProvider; #[cfg(test)] mod mock; @@ -144,7 +144,10 @@ const ASSEMBLY_ID: LockIdentifier = *b"assembly"; pub mod pallet { use super::*; use frame_support::{pallet_prelude::*, traits::EnsureOriginWithArg}; - use frame_system::pallet_prelude::*; + use frame_system::pallet_prelude::{ + ensure_root, ensure_signed, ensure_signed_or_root, BlockNumberFor as SystemBlockNumberFor, + OriginFor, + }; /// The in-code storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); @@ -167,12 +170,12 @@ pub mod pallet { type WeightInfo: WeightInfo; /// The Scheduler. type Scheduler: ScheduleAnon< - BlockNumberFor<Self>, + BlockNumberFor<Self, I>, CallOf<Self, I>, PalletsOriginOf<Self>, Hasher = Self::Hashing, > + ScheduleNamed< - BlockNumberFor<Self>, + BlockNumberFor<Self, I>, CallOf<Self, I>, PalletsOriginOf<Self>, Hasher = Self::Hashing, @@ -215,30 +218,35 @@ pub mod pallet { /// The number of blocks after submission that a referendum must begin being decided by. /// Once this passes, then anyone may cancel the referendum. #[pallet::constant] - type UndecidingTimeout: Get<BlockNumberFor<Self>>; + type UndecidingTimeout: Get<BlockNumberFor<Self, I>>; /// Quantization level for the referendum wakeup scheduler. A higher number will result in /// fewer storage reads/writes needed for smaller voters, but also result in delays to the /// automatic referendum status changes. Explicit servicing instructions are unaffected. #[pallet::constant] - type AlarmInterval: Get<BlockNumberFor<Self>>; + type AlarmInterval: Get<BlockNumberFor<Self, I>>; // The other stuff. /// Information concerning the different referendum tracks. #[pallet::constant] type Tracks: Get< Vec<( - <Self::Tracks as TracksInfo<BalanceOf<Self, I>, BlockNumberFor<Self>>>::Id, - TrackInfo<BalanceOf<Self, I>, BlockNumberFor<Self>>, + <Self::Tracks as TracksInfo<BalanceOf<Self, I>, BlockNumberFor<Self, I>>>::Id, + TrackInfo<BalanceOf<Self, I>, BlockNumberFor<Self, I>>, )>, > + TracksInfo< BalanceOf<Self, I>, - BlockNumberFor<Self>, + BlockNumberFor<Self, I>, RuntimeOrigin = <Self::RuntimeOrigin as OriginTrait>::PalletsOrigin, >; /// The preimage provider. type Preimages: QueryPreimage<H = Self::Hashing> + StorePreimage; + + /// Provider for the block number. + /// + /// Normally this is the `frame_system` pallet. + type BlockNumberProvider: BlockNumberProvider; } /// The next free referendum index, aka the number of referenda started so far. @@ -432,9 +440,9 @@ pub mod pallet { } #[pallet::hooks] - impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> { + impl<T: Config<I>, I: 'static> Hooks<SystemBlockNumberFor<T>> for Pallet<T, I> { #[cfg(feature = "try-runtime")] - fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> { + fn try_state(_n: SystemBlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> { Self::do_try_state()?; Ok(()) } @@ -462,7 +470,7 @@ pub mod pallet { origin: OriginFor<T>, proposal_origin: Box<PalletsOriginOf<T>>, proposal: BoundedCallOf<T, I>, - enactment_moment: DispatchTime<BlockNumberFor<T>>, + enactment_moment: DispatchTime<BlockNumberFor<T, I>>, ) -> DispatchResult { let proposal_origin = *proposal_origin; let who = T::SubmitOrigin::ensure_origin(origin, &proposal_origin)?; @@ -485,7 +493,7 @@ pub mod pallet { *x += 1; r }); - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let nudge_call = T::Preimages::bound(CallOf::<T, I>::from(Call::nudge_referendum { index }))?; let status = ReferendumStatus { @@ -527,7 +535,7 @@ pub mod pallet { let track = Self::track(status.track).ok_or(Error::<T, I>::NoTrack)?; status.decision_deposit = Some(Self::take_deposit(who.clone(), track.decision_deposit)?); - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let (info, _, branch) = Self::service_referendum(now, index, status); ReferendumInfoFor::<T, I>::insert(index, info); let e = @@ -584,7 +592,7 @@ pub mod pallet { Self::note_one_fewer_deciding(status.track); Self::deposit_event(Event::<T, I>::Cancelled { index, tally: status.tally }); let info = ReferendumInfo::Cancelled( - frame_system::Pallet::<T>::block_number(), + T::BlockNumberProvider::current_block_number(), Some(status.submission_deposit), status.decision_deposit, ); @@ -611,7 +619,7 @@ pub mod pallet { Self::slash_deposit(Some(status.submission_deposit.clone())); Self::slash_deposit(status.decision_deposit.clone()); Self::do_clear_metadata(index); - let info = ReferendumInfo::Killed(frame_system::Pallet::<T>::block_number()); + let info = ReferendumInfo::Killed(T::BlockNumberProvider::current_block_number()); ReferendumInfoFor::<T, I>::insert(index, info); Ok(()) } @@ -627,7 +635,7 @@ pub mod pallet { index: ReferendumIndex, ) -> DispatchResultWithPostInfo { ensure_root(origin)?; - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let mut status = Self::ensure_ongoing(index)?; // This is our wake-up, so we can disregard the alarm. status.alarm = None; @@ -658,7 +666,7 @@ pub mod pallet { let mut track_queue = TrackQueue::<T, I>::get(track); let branch = if let Some((index, mut status)) = Self::next_for_deciding(&mut track_queue) { - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let (maybe_alarm, branch) = Self::begin_deciding(&mut status, index, now, track_info); if let Some(set_alarm) = maybe_alarm { @@ -744,7 +752,7 @@ pub mod pallet { impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> { type Index = ReferendumIndex; type Votes = VotesOf<T, I>; - type Moment = BlockNumberFor<T>; + type Moment = BlockNumberFor<T, I>; type Class = TrackIdOf<T, I>; fn classes() -> Vec<Self::Class> { @@ -753,12 +761,12 @@ impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> { fn access_poll<R>( index: Self::Index, - f: impl FnOnce(PollStatus<&mut T::Tally, BlockNumberFor<T>, TrackIdOf<T, I>>) -> R, + f: impl FnOnce(PollStatus<&mut T::Tally, BlockNumberFor<T, I>, TrackIdOf<T, I>>) -> R, ) -> R { match ReferendumInfoFor::<T, I>::get(index) { Some(ReferendumInfo::Ongoing(mut status)) => { let result = f(PollStatus::Ongoing(&mut status.tally, status.track)); - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); Self::ensure_alarm_at(&mut status, index, now + One::one()); ReferendumInfoFor::<T, I>::insert(index, ReferendumInfo::Ongoing(status)); result @@ -772,13 +780,13 @@ impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> { fn try_access_poll<R>( index: Self::Index, f: impl FnOnce( - PollStatus<&mut T::Tally, BlockNumberFor<T>, TrackIdOf<T, I>>, + PollStatus<&mut T::Tally, BlockNumberFor<T, I>, TrackIdOf<T, I>>, ) -> Result<R, DispatchError>, ) -> Result<R, DispatchError> { match ReferendumInfoFor::<T, I>::get(index) { Some(ReferendumInfo::Ongoing(mut status)) => { let result = f(PollStatus::Ongoing(&mut status.tally, status.track))?; - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); Self::ensure_alarm_at(&mut status, index, now + One::one()); ReferendumInfoFor::<T, I>::insert(index, ReferendumInfo::Ongoing(status)); Ok(result) @@ -800,7 +808,7 @@ impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> { *x += 1; r }); - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let dummy_account_id = codec::Decode::decode(&mut sp_runtime::traits::TrailingZeroInput::new(&b"dummy"[..])) .expect("infinite length input; no invalid inputs for type; qed"); @@ -828,7 +836,7 @@ impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> { let mut status = Self::ensure_ongoing(index).map_err(|_| ())?; Self::ensure_no_alarm(&mut status); Self::note_one_fewer_deciding(status.track); - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let info = if approved { ReferendumInfo::Approved(now, Some(status.submission_deposit), status.decision_deposit) } else { @@ -868,7 +876,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { ReferendumInfo::Ongoing(status) => { let track = Self::track(status.track).ok_or(Error::<T, I>::NoTrack)?; let elapsed = if let Some(deciding) = status.deciding { - frame_system::Pallet::<T>::block_number().saturating_sub(deciding.since) + T::BlockNumberProvider::current_block_number().saturating_sub(deciding.since) } else { Zero::zero() }; @@ -889,11 +897,11 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { fn schedule_enactment( index: ReferendumIndex, track: &TrackInfoOf<T, I>, - desired: DispatchTime<BlockNumberFor<T>>, + desired: DispatchTime<BlockNumberFor<T, I>>, origin: PalletsOriginOf<T>, call: BoundedCallOf<T, I>, ) { - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); // Earliest allowed block is always at minimum the next block. let earliest_allowed = now.saturating_add(track.min_enactment_period.max(One::one())); let desired = desired.evaluate(now); @@ -912,8 +920,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { /// Set an alarm to dispatch `call` at block number `when`. fn set_alarm( call: BoundedCallOf<T, I>, - when: BlockNumberFor<T>, - ) -> Option<(BlockNumberFor<T>, ScheduleAddressOf<T, I>)> { + when: BlockNumberFor<T, I>, + ) -> Option<(BlockNumberFor<T, I>, ScheduleAddressOf<T, I>)> { let alarm_interval = T::AlarmInterval::get().max(One::one()); // Alarm must go off no earlier than `when`. // This rounds `when` upwards to the next multiple of `alarm_interval`. @@ -931,7 +939,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { result.is_ok(), "Unable to schedule a new alarm at #{:?} (now: #{:?}), scheduler error: `{:?}`", when, - frame_system::Pallet::<T>::block_number(), + T::BlockNumberProvider::current_block_number(), result.unwrap_err(), ); result.ok().map(|x| (when, x)) @@ -946,9 +954,9 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { fn begin_deciding( status: &mut ReferendumStatusOf<T, I>, index: ReferendumIndex, - now: BlockNumberFor<T>, + now: BlockNumberFor<T, I>, track: &TrackInfoOf<T, I>, - ) -> (Option<BlockNumberFor<T>>, BeginDecidingBranch) { + ) -> (Option<BlockNumberFor<T, I>>, BeginDecidingBranch) { let is_passing = Self::is_passing( &status.tally, Zero::zero(), @@ -984,11 +992,11 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { /// /// If `None`, then it is queued and should be nudged automatically as the queue gets drained. fn ready_for_deciding( - now: BlockNumberFor<T>, + now: BlockNumberFor<T, I>, track: &TrackInfoOf<T, I>, index: ReferendumIndex, status: &mut ReferendumStatusOf<T, I>, - ) -> (Option<BlockNumberFor<T>>, ServiceBranch) { + ) -> (Option<BlockNumberFor<T, I>>, ServiceBranch) { let deciding_count = DecidingCount::<T, I>::get(status.track); if deciding_count < track.max_deciding { // Begin deciding. @@ -1023,7 +1031,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { /// overall more efficient), however the weights become rather less easy to measure. fn note_one_fewer_deciding(track: TrackIdOf<T, I>) { // Set an alarm call for the next block to nudge the track along. - let now = frame_system::Pallet::<T>::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let next_block = now + One::one(); let call = match T::Preimages::bound(CallOf::<T, I>::from(Call::one_fewer_deciding { track, @@ -1045,7 +1053,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { fn ensure_alarm_at( status: &mut ReferendumStatusOf<T, I>, index: ReferendumIndex, - alarm: BlockNumberFor<T>, + alarm: BlockNumberFor<T, I>, ) -> bool { if status.alarm.as_ref().map_or(true, |&(when, _)| when != alarm) { // Either no alarm or one that was different @@ -1090,7 +1098,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { /// `TrackQueue`. Basically this happens when a referendum is in the deciding queue and receives /// a vote, or when it moves into the deciding queue. fn service_referendum( - now: BlockNumberFor<T>, + now: BlockNumberFor<T, I>, index: ReferendumIndex, mut status: ReferendumStatusOf<T, I>, ) -> (ReferendumInfoOf<T, I>, bool, ServiceBranch) { @@ -1102,7 +1110,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { }; // Default the alarm to the end of the world. let timeout = status.submitted + T::UndecidingTimeout::get(); - let mut alarm = BlockNumberFor::<T>::max_value(); + let mut alarm = BlockNumberFor::<T, I>::max_value(); let branch; match &mut status.deciding { None => { @@ -1233,7 +1241,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { }, } - let dirty_alarm = if alarm < BlockNumberFor::<T>::max_value() { + let dirty_alarm = if alarm < BlockNumberFor::<T, I>::max_value() { Self::ensure_alarm_at(&mut status, index, alarm) } else { Self::ensure_no_alarm(&mut status) @@ -1244,11 +1252,11 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { /// Determine the point at which a referendum will be accepted, move into confirmation with the /// given `tally` or end with rejection (whichever happens sooner). fn decision_time( - deciding: &DecidingStatusOf<T>, + deciding: &DecidingStatusOf<T, I>, tally: &T::Tally, track_id: TrackIdOf<T, I>, track: &TrackInfoOf<T, I>, - ) -> BlockNumberFor<T> { + ) -> BlockNumberFor<T, I> { deciding.confirming.unwrap_or_else(|| { // Set alarm to the point where the current voting would make it pass. let approval = tally.approval(track_id); @@ -1307,8 +1315,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { /// `approval_needed`. fn is_passing( tally: &T::Tally, - elapsed: BlockNumberFor<T>, - period: BlockNumberFor<T>, + elapsed: BlockNumberFor<T, I>, + period: BlockNumberFor<T, I>, support_needed: &Curve, approval_needed: &Curve, id: TrackIdOf<T, I>, @@ -1377,7 +1385,9 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { if let Some(deciding) = status.deciding { ensure!( deciding.since < - deciding.confirming.unwrap_or(BlockNumberFor::<T>::max_value()), + deciding + .confirming + .unwrap_or(BlockNumberFor::<T, I>::max_value()), "Deciding status cannot begin before confirming stage." ) } diff --git a/substrate/frame/referenda/src/migration.rs b/substrate/frame/referenda/src/migration.rs index 631eb7340e567e8115e1755d513980fb148700c5..c94896649beab7c59a1941755959013480f20475 100644 --- a/substrate/frame/referenda/src/migration.rs +++ b/substrate/frame/referenda/src/migration.rs @@ -25,6 +25,8 @@ use log; #[cfg(feature = "try-runtime")] use sp_runtime::TryRuntimeError; +type SystemBlockNumberFor<T> = frame_system::pallet_prelude::BlockNumberFor<T>; + /// Initial version of storage types. pub mod v0 { use super::*; @@ -37,7 +39,7 @@ pub mod v0 { pub type ReferendumInfoOf<T, I> = ReferendumInfo< TrackIdOf<T, I>, PalletsOriginOf<T>, - frame_system::pallet_prelude::BlockNumberFor<T>, + SystemBlockNumberFor<T>, BoundedCallOf<T, I>, BalanceOf<T, I>, TallyOf<T, I>, @@ -93,6 +95,21 @@ pub mod v1 { /// The log target. const TARGET: &'static str = "runtime::referenda::migration::v1"; + pub(crate) type ReferendumInfoOf<T, I> = ReferendumInfo< + TrackIdOf<T, I>, + PalletsOriginOf<T>, + SystemBlockNumberFor<T>, + BoundedCallOf<T, I>, + BalanceOf<T, I>, + TallyOf<T, I>, + <T as frame_system::Config>::AccountId, + ScheduleAddressOf<T, I>, + >; + + #[storage_alias] + pub type ReferendumInfoFor<T: Config<I>, I: 'static> = + StorageMap<Pallet<T, I>, Blake2_128Concat, ReferendumIndex, ReferendumInfoOf<T, I>>; + /// Transforms a submission deposit of ReferendumInfo(Approved|Rejected|Cancelled|TimedOut) to /// optional value, making it refundable. pub struct MigrateV0ToV1<T, I = ()>(PhantomData<(T, I)>); @@ -137,7 +154,7 @@ pub mod v1 { if let Some(new_value) = maybe_new_value { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); log::info!(target: TARGET, "migrating referendum #{:?}", &key); - ReferendumInfoFor::<T, I>::insert(key, new_value); + v1::ReferendumInfoFor::<T, I>::insert(key, new_value); } else { weight.saturating_accrue(T::DbWeight::get().reads(1)); } @@ -161,10 +178,125 @@ pub mod v1 { } } +/// Migration for when changing the block number provider. +/// +/// This migration is not guarded +pub mod switch_block_number_provider { + use super::*; + + /// The log target. + const TARGET: &'static str = "runtime::referenda::migration::change_block_number_provider"; + /// Convert from one to another block number provider/type. + pub trait BlockNumberConversion<Old, New> { + /// Convert the `old` block number type to the new block number type. + /// + /// Any changes in the rate of blocks need to be taken into account. + fn convert_block_number(block_number: Old) -> New; + } + + /// Transforms `SystemBlockNumberFor<T>` to `BlockNumberFor<T,I>` + pub struct MigrateBlockNumberProvider<BlockConverter, T, I = ()>( + PhantomData<(T, I)>, + PhantomData<BlockConverter>, + ); + impl<BlockConverter: BlockNumberConversion<T, I>, T: Config<I>, I: 'static> OnRuntimeUpgrade + for MigrateBlockNumberProvider<BlockConverter, T, I> + where + BlockConverter: BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, I>>, + T: Config<I>, + { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> { + let referendum_count = v1::ReferendumInfoFor::<T, I>::iter().count(); + log::info!( + target: TARGET, + "pre-upgrade state contains '{}' referendums.", + referendum_count + ); + Ok((referendum_count as u32).encode()) + } + + fn on_runtime_upgrade() -> Weight { + let mut weight = Weight::zero(); + weight.saturating_accrue(migrate_block_number_provider::<BlockConverter, T, I>()); + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> { + let on_chain_version = Pallet::<T, I>::on_chain_storage_version(); + ensure!(on_chain_version == 1, "must upgrade from version 1 to 2."); + let pre_referendum_count: u32 = Decode::decode(&mut &state[..]) + .expect("failed to decode the state from pre-upgrade."); + let post_referendum_count = ReferendumInfoFor::<T, I>::iter().count() as u32; + ensure!(post_referendum_count == pre_referendum_count, "must migrate all referendums."); + log::info!(target: TARGET, "migrated all referendums."); + Ok(()) + } + } + + pub fn migrate_block_number_provider<BlockConverter, T, I: 'static>() -> Weight + where + BlockConverter: BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, I>>, + T: Config<I>, + { + let in_code_version = Pallet::<T, I>::in_code_storage_version(); + let on_chain_version = Pallet::<T, I>::on_chain_storage_version(); + let mut weight = T::DbWeight::get().reads(1); + log::info!( + target: "runtime::referenda::migration::change_block_number_provider", + "running migration with in-code storage version {:?} / onchain {:?}.", + in_code_version, + on_chain_version + ); + if on_chain_version == 0 { + log::error!(target: TARGET, "skipping migration from v0 to switch_block_number_provider."); + return weight + } + + // Migration logic here + v1::ReferendumInfoFor::<T, I>::iter().for_each(|(key, value)| { + let maybe_new_value = match value { + ReferendumInfo::Ongoing(_) | ReferendumInfo::Killed(_) => None, + ReferendumInfo::Approved(e, s, d) => { + let new_e = BlockConverter::convert_block_number(e); + Some(ReferendumInfo::Approved(new_e, s, d)) + }, + ReferendumInfo::Rejected(e, s, d) => { + let new_e = BlockConverter::convert_block_number(e); + Some(ReferendumInfo::Rejected(new_e, s, d)) + }, + ReferendumInfo::Cancelled(e, s, d) => { + let new_e = BlockConverter::convert_block_number(e); + Some(ReferendumInfo::Cancelled(new_e, s, d)) + }, + ReferendumInfo::TimedOut(e, s, d) => { + let new_e = BlockConverter::convert_block_number(e); + Some(ReferendumInfo::TimedOut(new_e, s, d)) + }, + }; + if let Some(new_value) = maybe_new_value { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + log::info!(target: TARGET, "migrating referendum #{:?}", &key); + ReferendumInfoFor::<T, I>::insert(key, new_value); + } else { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + } + }); + + weight + } +} + #[cfg(test)] pub mod test { use super::*; - use crate::mock::{Test as T, *}; + use crate::{ + migration::switch_block_number_provider::{ + migrate_block_number_provider, BlockNumberConversion, + }, + mock::{Test as T, *}, + }; use core::str::FromStr; // create referendum status v0. @@ -185,7 +317,6 @@ pub mod test { deciding: None, } } - #[test] pub fn referendum_status_v0() { // make sure the bytes of the encoded referendum v0 is decodable. @@ -214,11 +345,11 @@ pub mod test { // run migration from v0 to v1. v1::MigrateV0ToV1::<T, ()>::on_runtime_upgrade(); // fetch and assert migrated into v1 the ongoing referendum. - let ongoing_v1 = ReferendumInfoFor::<T, ()>::get(2).unwrap(); + let ongoing_v1 = v1::ReferendumInfoFor::<T, ()>::get(2).unwrap(); // referendum status schema is the same for v0 and v1. assert_eq!(ReferendumInfoOf::<T, ()>::Ongoing(status_v0), ongoing_v1); // fetch and assert migrated into v1 the approved referendum. - let approved_v1 = ReferendumInfoFor::<T, ()>::get(5).unwrap(); + let approved_v1 = v1::ReferendumInfoFor::<T, ()>::get(5).unwrap(); assert_eq!( approved_v1, ReferendumInfoOf::<T, ()>::Approved( @@ -229,4 +360,48 @@ pub mod test { ); }); } + + #[test] + fn migration_v1_to_switch_block_number_provider_works() { + ExtBuilder::default().build_and_execute(|| { + pub struct MockBlockConverter; + + impl BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, ()>> for MockBlockConverter { + fn convert_block_number(block_number: SystemBlockNumberFor<T>) -> BlockNumberFor<T, ()> { + block_number as u64 + 10u64 + } + } + + let referendum_ongoing = v1::ReferendumInfoOf::<T, ()>::Ongoing(create_status_v0()); + let referendum_approved = v1::ReferendumInfoOf::<T, ()>::Approved( + 50, //old block number + Some(Deposit { who: 1, amount: 10 }), + Some(Deposit { who: 2, amount: 20 }), + ); + + ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc()); + v1::ReferendumInfoFor::<T, ()>::insert(1, referendum_ongoing); + + ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc()); + v1::ReferendumInfoFor::<T, ()>::insert(2, referendum_approved); + + migrate_block_number_provider::<MockBlockConverter, T, ()>(); + + let ongoing_v2 = ReferendumInfoFor::<T, ()>::get(1).unwrap(); + assert_eq!( + ongoing_v2, + ReferendumInfoOf::<T, ()>::Ongoing(create_status_v0()) + ); + + let approved_v2 = ReferendumInfoFor::<T, ()>::get(2).unwrap(); + assert_eq!( + approved_v2, + ReferendumInfoOf::<T, ()>::Approved( + 50, + Some(Deposit { who: 1, amount: 10 }), + Some(Deposit { who: 2, amount: 20 }) + ) + ); + }); + } } diff --git a/substrate/frame/referenda/src/mock.rs b/substrate/frame/referenda/src/mock.rs index 5d36ce137d46d2308166ed131cdfb8a464a1c661..c46236586f1f74d81dcab2ad5f6c21f18c700959 100644 --- a/substrate/frame/referenda/src/mock.rs +++ b/substrate/frame/referenda/src/mock.rs @@ -206,6 +206,7 @@ impl Config for Test { type AlarmInterval = AlarmInterval; type Tracks = TestTracksInfo; type Preimages = Preimage; + type BlockNumberProvider = System; } pub struct ExtBuilder {} diff --git a/substrate/frame/referenda/src/types.rs b/substrate/frame/referenda/src/types.rs index e83f28b472cda7fa3dc6329fd026440ec415874d..e97e7cc8df6d880ddc6aa2631878e715ba97344a 100644 --- a/substrate/frame/referenda/src/types.rs +++ b/substrate/frame/referenda/src/types.rs @@ -30,6 +30,10 @@ use sp_runtime::{FixedI64, PerThing, RuntimeDebug}; pub type BalanceOf<T, I = ()> = <<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; + +pub type BlockNumberFor<T, I> = + <<T as Config<I>>::BlockNumberProvider as BlockNumberProvider>::BlockNumber; + pub type NegativeImbalanceOf<T, I> = <<T as Config<I>>::Currency as Currency< <T as frame_system::Config>::AccountId, >>::NegativeImbalance; @@ -43,7 +47,7 @@ pub type PalletsOriginOf<T> = pub type ReferendumInfoOf<T, I> = ReferendumInfo< TrackIdOf<T, I>, PalletsOriginOf<T>, - BlockNumberFor<T>, + BlockNumberFor<T, I>, BoundedCallOf<T, I>, BalanceOf<T, I>, TallyOf<T, I>, @@ -53,19 +57,19 @@ pub type ReferendumInfoOf<T, I> = ReferendumInfo< pub type ReferendumStatusOf<T, I> = ReferendumStatus< TrackIdOf<T, I>, PalletsOriginOf<T>, - BlockNumberFor<T>, + BlockNumberFor<T, I>, BoundedCallOf<T, I>, BalanceOf<T, I>, TallyOf<T, I>, <T as frame_system::Config>::AccountId, ScheduleAddressOf<T, I>, >; -pub type DecidingStatusOf<T> = DecidingStatus<BlockNumberFor<T>>; -pub type TrackInfoOf<T, I = ()> = TrackInfo<BalanceOf<T, I>, BlockNumberFor<T>>; +pub type DecidingStatusOf<T, I> = DecidingStatus<BlockNumberFor<T, I>>; +pub type TrackInfoOf<T, I = ()> = TrackInfo<BalanceOf<T, I>, BlockNumberFor<T, I>>; pub type TrackIdOf<T, I> = - <<T as Config<I>>::Tracks as TracksInfo<BalanceOf<T, I>, BlockNumberFor<T>>>::Id; + <<T as Config<I>>::Tracks as TracksInfo<BalanceOf<T, I>, BlockNumberFor<T, I>>>::Id; pub type ScheduleAddressOf<T, I> = <<T as Config<I>>::Scheduler as Anon< - BlockNumberFor<T>, + BlockNumberFor<T, I>, CallOf<T, I>, PalletsOriginOf<T>, >>::Address;