Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Try-state for Referenda pallet #13949

Merged
merged 32 commits into from
Jul 30, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
32212bc
Try-state for Referenda pallet
Szegoo Apr 18, 2023
1ccc5e2
fix & more tests
Szegoo Apr 18, 2023
af16dd4
checking more stuff
Szegoo Apr 22, 2023
6eb9ab6
remove log
Szegoo Apr 22, 2023
a267bd5
two more tests
Szegoo Apr 22, 2023
325abf2
Merge branch 'paritytech:master' into try-state-referenda
Szegoo Apr 22, 2023
203030a
Update frame/referenda/src/lib.rs
Szegoo Apr 25, 2023
678c9fa
fixes
Szegoo Apr 25, 2023
e045e12
new check
Szegoo Apr 30, 2023
9fc44f1
Merge branch 'paritytech:master' into try-state-referenda
Szegoo Jun 21, 2023
beae91f
merge fixes
Szegoo Jun 22, 2023
df933c3
fix warning
Szegoo Jun 22, 2023
40cd097
remove check
Szegoo Jun 27, 2023
358c8a8
Update frame/referenda/src/lib.rs
Szegoo Jun 27, 2023
dec6227
separate into multiple functions
Szegoo Jun 27, 2023
07bb992
Merge branch 'paritytech:master' into try-state-referenda
Szegoo Jun 27, 2023
b776258
clean up
Szegoo Jun 27, 2023
c4e1a38
unnecessary return value specified
Szegoo Jun 27, 2023
c65a039
fix
Szegoo Jun 27, 2023
bbda2df
Merge branch 'paritytech:master' into try-state-referenda
Szegoo Jun 29, 2023
cb3c11c
Update frame/referenda/src/lib.rs
Szegoo Jun 30, 2023
3b66550
fmt
Szegoo Jun 30, 2023
3f26529
Merge branch 'paritytech:master' into try-state-referenda
Szegoo Jun 30, 2023
211359e
Merge branch 'paritytech:master' into try-state-referenda
Szegoo Jul 7, 2023
011d5b3
remove import
Szegoo Jul 28, 2023
e818842
Merge branch 'master' into try-state-referenda
Szegoo Jul 28, 2023
14797f8
fmt
Szegoo Jul 28, 2023
87cfc68
fix CI
Szegoo Jul 28, 2023
b153a12
Update frame/referenda/src/lib.rs
Szegoo Jul 28, 2023
2ac039d
last fixes
Szegoo Jul 28, 2023
59ec6a3
:P
Szegoo Jul 28, 2023
7874eb1
fmt
Szegoo Jul 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frame/referenda/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ benchmarks_instance_pallet! {

impl_benchmark_test_suite!(
Referenda,
crate::mock::new_test_ext(),
crate::mock::ExtBuilder::default().build(),
crate::mock::Test
);
}
94 changes: 93 additions & 1 deletion frame/referenda/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@

use codec::{Codec, Encode};
use frame_support::{
dispatch::DispatchResult,
ensure,
traits::{
schedule::{
Expand Down Expand Up @@ -416,6 +417,15 @@ pub mod pallet {
PreimageNotExist,
}

#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<T::BlockNumber> for Pallet<T, I> {
#[cfg(feature = "try-runtime")]
fn try_state(_n: T::BlockNumber) -> Result<(), sp_runtime::TryRuntimeError> {
Self::do_try_state()?;
Ok(())
}
}

#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Propose a referendum on a privileged action.
Expand Down Expand Up @@ -1032,7 +1042,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// - If it's ready to be decided, start deciding;
/// - If it's not ready to be decided and non-deciding timeout has passed, fail;
/// - If it's ongoing and passing, ensure confirming; if at end of confirmation period, pass.
/// - If it's ongoing and not passing, stop confirning; if it has reached end time, fail.
/// - If it's ongoing and not passing, stop confirming; if it has reached end time, fail.
///
/// Weight will be a bit different depending on what it does, but it's designed so as not to
/// differ dramatically, especially if `MaxQueue` is kept small. In particular _there are no
Expand Down Expand Up @@ -1284,4 +1294,86 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
Self::deposit_event(Event::<T, I>::MetadataCleared { index, hash });
}
}

/// Ensure the correctness of the state of this pallet.
///
/// The following assertions must always apply.
///
/// General assertions:
///
/// * `ReferendumCount` must always be equal to the number of referenda in `ReferendumInfoFor`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not a counted map?

/// * Referendum indices in `MetadataOf` must also be stored in `ReferendumInfoFor`.
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(any(feature = "try-runtime", test))]
fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
ensure!(
ReferendumCount::<T, I>::get() as usize ==
ReferendumInfoFor::<T, I>::iter_keys().count(),
"Number of referenda in `ReferendumInfoFor` is different than `ReferendumCount`"
);

MetadataOf::<T, I>::iter_keys().try_for_each(|referendum_index| -> DispatchResult {
liamaharon marked this conversation as resolved.
Show resolved Hide resolved
ensure!(
ReferendumInfoFor::<T, I>::contains_key(referendum_index),
"Referendum indices in `MetadataOf` must also be stored in `ReferendumInfoOf`"
);
Ok(())
})?;

Self::try_state_referenda_info()?;
Self::try_state_tracks()?;

Ok(())
}

/// Looking at referenda info:
///
/// - Data regarding ongoing phase:
///
/// * There must exist track info for the track of the referendum.
/// * The deciding stage has to begin before confirmation period.
/// * If alarm is set the nudge call has to be at most `UndecidingTimeout` blocks away
/// from the submission block.
#[cfg(any(feature = "try-runtime", test))]
fn try_state_referenda_info() -> Result<(), sp_runtime::TryRuntimeError> {
ReferendumInfoFor::<T, I>::iter().try_for_each(|(_, referendum)| {
match referendum {
ReferendumInfo::Ongoing(status) => {
ensure!(
Self::track(status.track).is_some(),
"No track info for the track of the referendum."
);

if let Some(deciding) = status.deciding {
ensure!(
deciding.since <
deciding.confirming.unwrap_or(T::BlockNumber::max_value()),
"Deciding status cannot begin before confirming stage."
)
}
},
_ => {},
}
Ok(())
})
}

/// Looking at tracks:
///
/// * The referendum indices stored in `TrackQueue` must exist as keys in the
/// `ReferendumInfoFor` storage map.
#[cfg(any(feature = "try-runtime", test))]
fn try_state_tracks() -> Result<(), sp_runtime::TryRuntimeError> {
T::Tracks::tracks().iter().try_for_each(|track| {
TrackQueue::<T, I>::get(track.0).iter().try_for_each(
|(referendum_index, _)| -> Result<(), sp_runtime::TryRuntimeError> {
ensure!(
ReferendumInfoFor::<T, I>::contains_key(referendum_index),
"`ReferendumIndex` inside the `TrackQueue` should be a key in `ReferendumInfoFor`"
);
Ok(())
},
)?;
Ok(())
})
}
}
12 changes: 11 additions & 1 deletion frame/referenda/src/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@ pub mod test {
}
}

fn increment_referendum_index() {
ReferendumCount::<T, ()>::mutate(|x| {
let r = *x;
*x += 1;
r
});
}
Szegoo marked this conversation as resolved.
Show resolved Hide resolved

#[test]
pub fn referendum_status_v0() {
// make sure the bytes of the encoded referendum v0 is decodable.
Expand All @@ -199,17 +207,19 @@ pub mod test {

#[test]
fn migration_v0_to_v1_works() {
new_test_ext().execute_with(|| {
ExtBuilder::default().build_and_execute(|| {
// create and insert into the storage an ongoing referendum v0.
let status_v0 = create_status_v0();
let ongoing_v0 = v0::ReferendumInfoOf::<T, ()>::Ongoing(status_v0.clone());
increment_referendum_index();
v0::ReferendumInfoFor::<T, ()>::insert(2, ongoing_v0);
// create and insert into the storage an approved referendum v0.
let approved_v0 = v0::ReferendumInfoOf::<T, ()>::Approved(
123,
Deposit { who: 1, amount: 10 },
Some(Deposit { who: 2, amount: 20 }),
);
increment_referendum_index();
v0::ReferendumInfoFor::<T, ()>::insert(5, approved_v0);
// run migration from v0 to v1.
v1::MigrateV0ToV1::<T, ()>::on_runtime_upgrade();
Expand Down
37 changes: 23 additions & 14 deletions frame/referenda/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,23 +225,32 @@ impl Config for Test {
type Tracks = TestTracksInfo;
type Preimages = Preimage;
}
pub struct ExtBuilder {}

pub fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (6, 100)];
pallet_balances::GenesisConfig::<Test> { balances }
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
impl Default for ExtBuilder {
fn default() -> Self {
Self {}
}
}

/// Execute the function two times, with `true` and with `false`.
#[allow(dead_code)]
pub fn new_test_ext_execute_with_cond(execute: impl FnOnce(bool) -> () + Clone) {
new_test_ext().execute_with(|| (execute.clone())(false));
new_test_ext().execute_with(|| execute(true));
impl ExtBuilder {
pub fn build(self) -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (6, 100)];
pallet_balances::GenesisConfig::<Test> { balances }
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}

pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
self.build().execute_with(|| {
test();
Referenda::do_try_state().unwrap();
})
}
Szegoo marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Encode, Debug, Decode, TypeInfo, Eq, PartialEq, Clone, MaxEncodedLen)]
Expand Down
Loading