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 a052a9d3800c..8b0842697237 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 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 1e8212cf6ac2..e699f2eeaf43 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 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 27a58a0eebd1..231defab6aa5 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 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 ef2adf60753d..2be549be29ed 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 d027f788d71f..abc25ebaa470 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 000000000000..68d01904d071 --- /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 e11a009c1c3f..01857360368f 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1045,6 +1045,7 @@ impl pallet_referenda::Config for Runtime { type AlarmInterval = AlarmInterval; type Tracks = TracksInfo; type Preimages = Preimage; + type BlockNumberProvider = System; } impl pallet_referenda::Config for Runtime { @@ -1065,6 +1066,7 @@ impl pallet_referenda::Config 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 67ac82787d31..895f95dbec55 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, I: 'static>(n: BlockNumberFor) { + >::BlockNumberProvider::set_block_number(n); +} + fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) { frame_system::Pallet::::assert_last_event(generic_event.into()); } @@ -151,30 +155,28 @@ fn make_failing, I: 'static>(index: ReferendumIndex) { fn skip_prepare_period, I: 'static>(index: ReferendumIndex) { let status = Referenda::::ensure_ongoing(index).unwrap(); let prepare_period_over = status.submitted + info::(index).prepare_period; - frame_system::Pallet::::set_block_number(prepare_period_over); + set_block_number::(prepare_period_over); } fn skip_decision_period, I: 'static>(index: ReferendumIndex) { let status = Referenda::::ensure_ongoing(index).unwrap(); let decision_period_over = status.deciding.unwrap().since + info::(index).decision_period; - frame_system::Pallet::::set_block_number(decision_period_over); + set_block_number::(decision_period_over); } fn skip_confirm_period, I: 'static>(index: ReferendumIndex) { let status = Referenda::::ensure_ongoing(index).unwrap(); let confirm_period_over = status.deciding.unwrap().confirming.unwrap(); - frame_system::Pallet::::set_block_number(confirm_period_over); + set_block_number::(confirm_period_over); } fn skip_timeout_period, I: 'static>(index: ReferendumIndex) { let status = Referenda::::ensure_ongoing(index).unwrap(); let timeout_period_over = status.submitted + T::UndecidingTimeout::get(); - frame_system::Pallet::::set_block_number(timeout_period_over); + set_block_number::(timeout_period_over); } -fn alarm_time, I: 'static>( - index: ReferendumIndex, -) -> frame_system::pallet_prelude::BlockNumberFor { +fn alarm_time, I: 'static>(index: ReferendumIndex) -> BlockNumberFor { let status = Referenda::::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 e72dd7f11cbb..e6a895f9c593 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, + BlockNumberFor, CallOf, PalletsOriginOf, Hasher = Self::Hashing, > + ScheduleNamed< - BlockNumberFor, + BlockNumberFor, CallOf, PalletsOriginOf, 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>; + type UndecidingTimeout: Get>; /// 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>; + type AlarmInterval: Get>; // The other stuff. /// Information concerning the different referendum tracks. #[pallet::constant] type Tracks: Get< Vec<( - , BlockNumberFor>>::Id, - TrackInfo, BlockNumberFor>, + , BlockNumberFor>>::Id, + TrackInfo, BlockNumberFor>, )>, > + TracksInfo< BalanceOf, - BlockNumberFor, + BlockNumberFor, RuntimeOrigin = ::PalletsOrigin, >; /// The preimage provider. type Preimages: QueryPreimage + 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, I: 'static> Hooks> for Pallet { + impl, I: 'static> Hooks> for Pallet { #[cfg(feature = "try-runtime")] - fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + fn try_state(_n: SystemBlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { Self::do_try_state()?; Ok(()) } @@ -462,7 +470,7 @@ pub mod pallet { origin: OriginFor, proposal_origin: Box>, proposal: BoundedCallOf, - enactment_moment: DispatchTime>, + enactment_moment: DispatchTime>, ) -> 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::::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let nudge_call = T::Preimages::bound(CallOf::::from(Call::nudge_referendum { index }))?; let status = ReferendumStatus { @@ -527,7 +535,7 @@ pub mod pallet { let track = Self::track(status.track).ok_or(Error::::NoTrack)?; status.decision_deposit = Some(Self::take_deposit(who.clone(), track.decision_deposit)?); - let now = frame_system::Pallet::::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let (info, _, branch) = Self::service_referendum(now, index, status); ReferendumInfoFor::::insert(index, info); let e = @@ -584,7 +592,7 @@ pub mod pallet { Self::note_one_fewer_deciding(status.track); Self::deposit_event(Event::::Cancelled { index, tally: status.tally }); let info = ReferendumInfo::Cancelled( - frame_system::Pallet::::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::::block_number()); + let info = ReferendumInfo::Killed(T::BlockNumberProvider::current_block_number()); ReferendumInfoFor::::insert(index, info); Ok(()) } @@ -627,7 +635,7 @@ pub mod pallet { index: ReferendumIndex, ) -> DispatchResultWithPostInfo { ensure_root(origin)?; - let now = frame_system::Pallet::::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::::get(track); let branch = if let Some((index, mut status)) = Self::next_for_deciding(&mut track_queue) { - let now = frame_system::Pallet::::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, I: 'static> Polling for Pallet { type Index = ReferendumIndex; type Votes = VotesOf; - type Moment = BlockNumberFor; + type Moment = BlockNumberFor; type Class = TrackIdOf; fn classes() -> Vec { @@ -753,12 +761,12 @@ impl, I: 'static> Polling for Pallet { fn access_poll( index: Self::Index, - f: impl FnOnce(PollStatus<&mut T::Tally, BlockNumberFor, TrackIdOf>) -> R, + f: impl FnOnce(PollStatus<&mut T::Tally, BlockNumberFor, TrackIdOf>) -> R, ) -> R { match ReferendumInfoFor::::get(index) { Some(ReferendumInfo::Ongoing(mut status)) => { let result = f(PollStatus::Ongoing(&mut status.tally, status.track)); - let now = frame_system::Pallet::::block_number(); + let now = T::BlockNumberProvider::current_block_number(); Self::ensure_alarm_at(&mut status, index, now + One::one()); ReferendumInfoFor::::insert(index, ReferendumInfo::Ongoing(status)); result @@ -772,13 +780,13 @@ impl, I: 'static> Polling for Pallet { fn try_access_poll( index: Self::Index, f: impl FnOnce( - PollStatus<&mut T::Tally, BlockNumberFor, TrackIdOf>, + PollStatus<&mut T::Tally, BlockNumberFor, TrackIdOf>, ) -> Result, ) -> Result { match ReferendumInfoFor::::get(index) { Some(ReferendumInfo::Ongoing(mut status)) => { let result = f(PollStatus::Ongoing(&mut status.tally, status.track))?; - let now = frame_system::Pallet::::block_number(); + let now = T::BlockNumberProvider::current_block_number(); Self::ensure_alarm_at(&mut status, index, now + One::one()); ReferendumInfoFor::::insert(index, ReferendumInfo::Ongoing(status)); Ok(result) @@ -800,7 +808,7 @@ impl, I: 'static> Polling for Pallet { *x += 1; r }); - let now = frame_system::Pallet::::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, I: 'static> Polling for Pallet { 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::::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, I: 'static> Pallet { ReferendumInfo::Ongoing(status) => { let track = Self::track(status.track).ok_or(Error::::NoTrack)?; let elapsed = if let Some(deciding) = status.deciding { - frame_system::Pallet::::block_number().saturating_sub(deciding.since) + T::BlockNumberProvider::current_block_number().saturating_sub(deciding.since) } else { Zero::zero() }; @@ -889,11 +897,11 @@ impl, I: 'static> Pallet { fn schedule_enactment( index: ReferendumIndex, track: &TrackInfoOf, - desired: DispatchTime>, + desired: DispatchTime>, origin: PalletsOriginOf, call: BoundedCallOf, ) { - let now = frame_system::Pallet::::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, I: 'static> Pallet { /// Set an alarm to dispatch `call` at block number `when`. fn set_alarm( call: BoundedCallOf, - when: BlockNumberFor, - ) -> Option<(BlockNumberFor, ScheduleAddressOf)> { + when: BlockNumberFor, + ) -> Option<(BlockNumberFor, ScheduleAddressOf)> { 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, I: 'static> Pallet { result.is_ok(), "Unable to schedule a new alarm at #{:?} (now: #{:?}), scheduler error: `{:?}`", when, - frame_system::Pallet::::block_number(), + T::BlockNumberProvider::current_block_number(), result.unwrap_err(), ); result.ok().map(|x| (when, x)) @@ -946,9 +954,9 @@ impl, I: 'static> Pallet { fn begin_deciding( status: &mut ReferendumStatusOf, index: ReferendumIndex, - now: BlockNumberFor, + now: BlockNumberFor, track: &TrackInfoOf, - ) -> (Option>, BeginDecidingBranch) { + ) -> (Option>, BeginDecidingBranch) { let is_passing = Self::is_passing( &status.tally, Zero::zero(), @@ -984,11 +992,11 @@ impl, I: 'static> Pallet { /// /// If `None`, then it is queued and should be nudged automatically as the queue gets drained. fn ready_for_deciding( - now: BlockNumberFor, + now: BlockNumberFor, track: &TrackInfoOf, index: ReferendumIndex, status: &mut ReferendumStatusOf, - ) -> (Option>, ServiceBranch) { + ) -> (Option>, ServiceBranch) { let deciding_count = DecidingCount::::get(status.track); if deciding_count < track.max_deciding { // Begin deciding. @@ -1023,7 +1031,7 @@ impl, I: 'static> Pallet { /// overall more efficient), however the weights become rather less easy to measure. fn note_one_fewer_deciding(track: TrackIdOf) { // Set an alarm call for the next block to nudge the track along. - let now = frame_system::Pallet::::block_number(); + let now = T::BlockNumberProvider::current_block_number(); let next_block = now + One::one(); let call = match T::Preimages::bound(CallOf::::from(Call::one_fewer_deciding { track, @@ -1045,7 +1053,7 @@ impl, I: 'static> Pallet { fn ensure_alarm_at( status: &mut ReferendumStatusOf, index: ReferendumIndex, - alarm: BlockNumberFor, + alarm: BlockNumberFor, ) -> 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, I: 'static> Pallet { /// `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, + now: BlockNumberFor, index: ReferendumIndex, mut status: ReferendumStatusOf, ) -> (ReferendumInfoOf, bool, ServiceBranch) { @@ -1102,7 +1110,7 @@ impl, I: 'static> Pallet { }; // Default the alarm to the end of the world. let timeout = status.submitted + T::UndecidingTimeout::get(); - let mut alarm = BlockNumberFor::::max_value(); + let mut alarm = BlockNumberFor::::max_value(); let branch; match &mut status.deciding { None => { @@ -1233,7 +1241,7 @@ impl, I: 'static> Pallet { }, } - let dirty_alarm = if alarm < BlockNumberFor::::max_value() { + let dirty_alarm = if alarm < BlockNumberFor::::max_value() { Self::ensure_alarm_at(&mut status, index, alarm) } else { Self::ensure_no_alarm(&mut status) @@ -1244,11 +1252,11 @@ impl, I: 'static> Pallet { /// 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, + deciding: &DecidingStatusOf, tally: &T::Tally, track_id: TrackIdOf, track: &TrackInfoOf, - ) -> BlockNumberFor { + ) -> BlockNumberFor { 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, I: 'static> Pallet { /// `approval_needed`. fn is_passing( tally: &T::Tally, - elapsed: BlockNumberFor, - period: BlockNumberFor, + elapsed: BlockNumberFor, + period: BlockNumberFor, support_needed: &Curve, approval_needed: &Curve, id: TrackIdOf, @@ -1377,7 +1385,9 @@ impl, I: 'static> Pallet { if let Some(deciding) = status.deciding { ensure!( deciding.since < - deciding.confirming.unwrap_or(BlockNumberFor::::max_value()), + deciding + .confirming + .unwrap_or(BlockNumberFor::::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 631eb7340e56..c94896649bea 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 = frame_system::pallet_prelude::BlockNumberFor; + /// Initial version of storage types. pub mod v0 { use super::*; @@ -37,7 +39,7 @@ pub mod v0 { pub type ReferendumInfoOf = ReferendumInfo< TrackIdOf, PalletsOriginOf, - frame_system::pallet_prelude::BlockNumberFor, + SystemBlockNumberFor, BoundedCallOf, BalanceOf, TallyOf, @@ -93,6 +95,21 @@ pub mod v1 { /// The log target. const TARGET: &'static str = "runtime::referenda::migration::v1"; + pub(crate) type ReferendumInfoOf = ReferendumInfo< + TrackIdOf, + PalletsOriginOf, + SystemBlockNumberFor, + BoundedCallOf, + BalanceOf, + TallyOf, + ::AccountId, + ScheduleAddressOf, + >; + + #[storage_alias] + pub type ReferendumInfoFor, I: 'static> = + StorageMap, Blake2_128Concat, ReferendumIndex, ReferendumInfoOf>; + /// Transforms a submission deposit of ReferendumInfo(Approved|Rejected|Cancelled|TimedOut) to /// optional value, making it refundable. pub struct MigrateV0ToV1(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::::insert(key, new_value); + v1::ReferendumInfoFor::::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 { + /// 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` to `BlockNumberFor` + pub struct MigrateBlockNumberProvider( + PhantomData<(T, I)>, + PhantomData, + ); + impl, T: Config, I: 'static> OnRuntimeUpgrade + for MigrateBlockNumberProvider + where + BlockConverter: BlockNumberConversion, BlockNumberFor>, + T: Config, + { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + let referendum_count = v1::ReferendumInfoFor::::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::()); + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { + let on_chain_version = Pallet::::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::::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() -> Weight + where + BlockConverter: BlockNumberConversion, BlockNumberFor>, + T: Config, + { + let in_code_version = Pallet::::in_code_storage_version(); + let on_chain_version = Pallet::::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::::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::::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::::on_runtime_upgrade(); // fetch and assert migrated into v1 the ongoing referendum. - let ongoing_v1 = ReferendumInfoFor::::get(2).unwrap(); + let ongoing_v1 = v1::ReferendumInfoFor::::get(2).unwrap(); // referendum status schema is the same for v0 and v1. assert_eq!(ReferendumInfoOf::::Ongoing(status_v0), ongoing_v1); // fetch and assert migrated into v1 the approved referendum. - let approved_v1 = ReferendumInfoFor::::get(5).unwrap(); + let approved_v1 = v1::ReferendumInfoFor::::get(5).unwrap(); assert_eq!( approved_v1, ReferendumInfoOf::::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, BlockNumberFor> for MockBlockConverter { + fn convert_block_number(block_number: SystemBlockNumberFor) -> BlockNumberFor { + block_number as u64 + 10u64 + } + } + + let referendum_ongoing = v1::ReferendumInfoOf::::Ongoing(create_status_v0()); + let referendum_approved = v1::ReferendumInfoOf::::Approved( + 50, //old block number + Some(Deposit { who: 1, amount: 10 }), + Some(Deposit { who: 2, amount: 20 }), + ); + + ReferendumCount::::mutate(|x| x.saturating_inc()); + v1::ReferendumInfoFor::::insert(1, referendum_ongoing); + + ReferendumCount::::mutate(|x| x.saturating_inc()); + v1::ReferendumInfoFor::::insert(2, referendum_approved); + + migrate_block_number_provider::(); + + let ongoing_v2 = ReferendumInfoFor::::get(1).unwrap(); + assert_eq!( + ongoing_v2, + ReferendumInfoOf::::Ongoing(create_status_v0()) + ); + + let approved_v2 = ReferendumInfoFor::::get(2).unwrap(); + assert_eq!( + approved_v2, + ReferendumInfoOf::::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 c96a50af8658..3f2c331ef861 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 e83f28b472cd..e97e7cc8df6d 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 = <>::Currency as Currency<::AccountId>>::Balance; + +pub type BlockNumberFor = + <>::BlockNumberProvider as BlockNumberProvider>::BlockNumber; + pub type NegativeImbalanceOf = <>::Currency as Currency< ::AccountId, >>::NegativeImbalance; @@ -43,7 +47,7 @@ pub type PalletsOriginOf = pub type ReferendumInfoOf = ReferendumInfo< TrackIdOf, PalletsOriginOf, - BlockNumberFor, + BlockNumberFor, BoundedCallOf, BalanceOf, TallyOf, @@ -53,19 +57,19 @@ pub type ReferendumInfoOf = ReferendumInfo< pub type ReferendumStatusOf = ReferendumStatus< TrackIdOf, PalletsOriginOf, - BlockNumberFor, + BlockNumberFor, BoundedCallOf, BalanceOf, TallyOf, ::AccountId, ScheduleAddressOf, >; -pub type DecidingStatusOf = DecidingStatus>; -pub type TrackInfoOf = TrackInfo, BlockNumberFor>; +pub type DecidingStatusOf = DecidingStatus>; +pub type TrackInfoOf = TrackInfo, BlockNumberFor>; pub type TrackIdOf = - <>::Tracks as TracksInfo, BlockNumberFor>>::Id; + <>::Tracks as TracksInfo, BlockNumberFor>>::Id; pub type ScheduleAddressOf = <>::Scheduler as Anon< - BlockNumberFor, + BlockNumberFor, CallOf, PalletsOriginOf, >>::Address;