Skip to content

Commit

Permalink
change(pallet-referenda): adjust testing to consider finalizing state
Browse files Browse the repository at this point in the history
  • Loading branch information
pandres95 committed Mar 26, 2024
1 parent e9f7d28 commit 0a8ea80
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 36 deletions.
112 changes: 106 additions & 6 deletions substrate/frame/referenda/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ use super::*;
use crate as pallet_referenda;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
assert_ok, derive_impl, ord_parameter_types, parameter_types,
assert_ok, derive_impl, ord_parameter_types, parameter_types, storage_alias,
traits::{
ConstU32, ConstU64, Contains, EqualPrivilegeOnly, OnInitialize, OriginTrait, Polling,
SortedMembers,
},
weights::Weight,
};
use frame_system::{EnsureRoot, EnsureSignedBy};
use sp_core::H256;
use sp_runtime::{
traits::{BlakeTwo256, Hash},
BuildStorage, DispatchResult, Perbill,
Expand Down Expand Up @@ -118,12 +119,44 @@ impl SortedMembers<u64> for OneToFive {
fn add(_m: &u64) {}
}

#[storage_alias]
pub type TestRandomnessBlockNumberOffset<T: Config<I>, I: 'static> =
StorageValue<Pallet<T, I>, BlockNumberFor<T>>;

pub struct TestRandomness;

impl TestRandomness {
pub fn place_candle() {
TestRandomnessBlockNumberOffset::<Test, ()>::set(Some(System::block_number()));
}

fn hash_256_like_for(n: BlockNumberFor<Test>) -> [u8; 32] {
let mut encoded = n.encode();
while encoded.len() < 32 {
encoded.push(0);
}

encoded.try_into().expect("added bytes until 32")
}
}

impl frame_support::traits::Randomness<H256, BlockNumberFor<Test>> for TestRandomness {
fn random(_: &[u8]) -> (H256, BlockNumberFor<Test>) {
let block_number = TestRandomnessBlockNumberOffset::<Test, ()>::get()
.unwrap_or(System::block_number().saturating_less_one());

let block_hash: H256 = Self::hash_256_like_for(block_number).into();

(block_hash, System::block_number())
}
}

pub struct TestTracksInfo;
impl TracksInfo<u64, u64> for TestTracksInfo {
type Id = u8;
type RuntimeOrigin = <RuntimeOrigin as OriginTrait>::PalletsOrigin;
fn tracks() -> &'static [(Self::Id, TrackInfo<u64, u64>)] {
static DATA: [(u8, TrackInfo<u64, u64>); 2] = [
static DATA: [(u8, TrackInfo<u64, u64>); 3] = [
(
0u8,
TrackInfo {
Expand Down Expand Up @@ -168,6 +201,28 @@ impl TracksInfo<u64, u64> for TestTracksInfo {
},
},
),
(
2u8,
TrackInfo {
name: "signed by 100",
max_deciding: 1,
decision_deposit: 1,
prepare_period: 1,
decision_period: 14,
confirm_period: 7,
min_enactment_period: 2,
min_approval: Curve::LinearDecreasing {
length: Perbill::from_percent(100),
floor: Perbill::from_percent(50),
ceil: Perbill::from_percent(100),
},
min_support: Curve::LinearDecreasing {
length: Perbill::from_percent(50),
floor: Perbill::from_percent(0),
ceil: Perbill::from_percent(100),
},
},
),
];
&DATA[..]
}
Expand All @@ -176,6 +231,7 @@ impl TracksInfo<u64, u64> for TestTracksInfo {
match system_origin {
frame_system::RawOrigin::Root => Ok(0),
frame_system::RawOrigin::None => Ok(1),
frame_system::RawOrigin::Signed(100) => Ok(2),
_ => Err(()),
}
} else {
Expand All @@ -190,6 +246,7 @@ impl Config for Test {
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type Scheduler = Scheduler;
type Randomness = TestRandomness;
type Currency = pallet_balances::Pallet<Self>;
type SubmitOrigin = frame_system::EnsureSigned<u64>;
type CancelOrigin = EnsureSignedBy<Four, u64>;
Expand All @@ -215,7 +272,7 @@ impl Default for ExtBuilder {
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)];
let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (6, 100), (100, 100)];
pallet_balances::GenesisConfig::<Test> { balances }
.assimilate_storage(&mut t)
.unwrap();
Expand Down Expand Up @@ -293,6 +350,11 @@ pub fn set_balance_proposal_bounded(value: u64) -> BoundedCallOf<Test, ()> {
<Preimage as StorePreimage>::bound(c).unwrap()
}

pub fn transfer_balance_proposal_bounded(value: u64) -> BoundedCallOf<Test, ()> {
let c = RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { dest: 42, value });
<Preimage as StorePreimage>::bound(c).unwrap()
}

#[allow(dead_code)]
pub fn propose_set_balance(who: u64, value: u64, delay: u64) -> DispatchResult {
Referenda::submit(
Expand Down Expand Up @@ -358,6 +420,10 @@ pub fn deciding_and_failing_since(i: ReferendumIndex) -> u64 {
deciding: Some(DecidingStatus { since, confirming: None, .. }),
..
}) => since,
ReferendumInfo::Finalizing(ReferendumStatus {
deciding: Some(DecidingStatus { since, confirming: None, .. }),
..
}) => since,
_ => panic!("Not deciding"),
}
}
Expand Down Expand Up @@ -422,13 +488,47 @@ pub enum RefState {
}

impl RefState {
/// Create default referendum:
///
/// - **Track**: _Root_ (`0`)
/// - **Call**: `Balances::force_set_balance(42, 1)`
pub fn create(self) -> ReferendumIndex {
Self::create_with_params(
self,
frame_support::dispatch::RawOrigin::Root.into(),
set_balance_proposal_bounded(1),
)
}

/// Create referendum with long duration:
///
/// - **Track**: _Signed by 100_ (`2`)
/// - **Call**: `Balances::transfer_keep_alive(42, 10)`
pub fn create_long(self) -> ReferendumIndex {
Self::create_with_params(
self,
frame_support::dispatch::RawOrigin::Signed(100).into(),
transfer_balance_proposal_bounded(10),
)
}

pub fn create_with_params(
self,
origin: PalletsOriginOf<Test>,
call: BoundedCallOf<Test, ()>,
) -> ReferendumIndex {
assert_ok!(Referenda::submit(
RuntimeOrigin::signed(1),
Box::new(frame_support::dispatch::RawOrigin::Root.into()),
set_balance_proposal_bounded(1),
Box::new(origin.clone()),
call,
DispatchTime::At(10),
));

let track_id = TestTracksInfo::track_for(&origin)
.expect("track should exist as the ref was successfully submitted; qed");
let tracks = TestTracksInfo::tracks();
let (_, track) = &tracks[track_id as usize];

assert_ok!(Referenda::place_decision_deposit(RuntimeOrigin::signed(2), 0));
if matches!(self, RefState::Confirming { immediate: true }) {
set_tally(0, 100, 0);
Expand All @@ -442,7 +542,7 @@ impl RefState {
run_to(System::block_number() + 1);
}
if matches!(self, RefState::Confirming { .. }) {
assert_eq!(confirming_until(index), System::block_number() + 2);
assert_eq!(confirming_until(index), System::block_number() + track.confirm_period);
}
if matches!(self, RefState::Passing) {
set_tally(0, 100, 99);
Expand Down
95 changes: 65 additions & 30 deletions substrate/frame/referenda/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn params_should_work() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(ReferendumCount::<Test>::get(), 0);
assert_eq!(Balances::free_balance(42), 0);
assert_eq!(Balances::total_issuance(), 600);
assert_eq!(Balances::total_issuance(), 700);
});
}

Expand All @@ -57,14 +57,14 @@ fn basic_happy_path_works() {
set_tally(0, 100, 0);
run_to(7);
assert_eq!(confirming_until(0), 9);
run_to(9);
run_to(10);
// #8: Should be confirmed & ended.
assert_eq!(approved_since(0), 9);
assert_eq!(approved_since(0), 10);
assert_ok!(Referenda::refund_decision_deposit(RuntimeOrigin::signed(2), 0));
run_to(12);
run_to(13);
// #9: Should not yet be enacted.
assert_eq!(Balances::free_balance(&42), 0);
run_to(13);
run_to(14);
// #10: Proposal should be executed.
assert_eq!(Balances::free_balance(&42), 1);
});
Expand All @@ -89,8 +89,8 @@ fn confirm_then_reconfirm_with_elapsed_trigger_works() {
set_tally(r, 100, 99);
run_to(8);
assert_eq!(deciding_and_failing_since(r), 5);
run_to(11);
assert_eq!(approved_since(r), 11);
run_to(12);
assert_eq!(approved_since(r), 12);
});
}

Expand All @@ -103,8 +103,8 @@ fn instaconfirm_then_reconfirm_with_elapsed_trigger_works() {
set_tally(r, 100, 99);
run_to(7);
assert_eq!(deciding_and_failing_since(r), 5);
run_to(11);
assert_eq!(approved_since(r), 11);
run_to(12);
assert_eq!(approved_since(r), 12);
});
}

Expand All @@ -121,8 +121,8 @@ fn instaconfirm_then_reconfirm_with_voting_trigger_works() {
set_tally(r, 100, 0);
run_to(9);
assert_eq!(confirming_until(r), 11);
run_to(11);
assert_eq!(approved_since(r), 11);
run_to(12);
assert_eq!(approved_since(r), 12);
});
}

Expand All @@ -132,21 +132,22 @@ fn voting_should_extend_for_late_confirmation() {
let r = Passing.create();
run_to(10);
assert_eq!(confirming_until(r), 11);
run_to(11);
assert_eq!(approved_since(r), 11);
run_to(12);
assert_eq!(approved_since(r), 12);
});
}

#[test]
fn should_instafail_during_extension_confirmation() {
fn should_fails_during_extension_confirmation_if_on_candle() {
ExtBuilder::default().build_and_execute(|| {
let r = Passing.create();
run_to(10);
assert_eq!(confirming_until(r), 11);
// Should insta-fail since it's now past the normal voting time.
TestRandomness::place_candle();
// Should record failure. I'll fail anyways
set_tally(r, 100, 101);
run_to(11);
assert_eq!(rejected_since(r), 11);
run_to(12);
assert_eq!(rejected_since(r), 12);
});
}

Expand All @@ -165,6 +166,41 @@ fn confirming_then_fail_works() {
});
}

// TODO: Add a new test to decide using candle method
// Note: use a deterministic (i.e. manually set the seed)
// method to be able to accurately determine which is
// the "winner" block
#[test]
fn confirming_decided_by_candle() {
ExtBuilder::default().build_and_execute(|| {
let r = Passing.create_long();
// Normally ends at 5 + 4 (voting period) = 9.
assert_eq!(deciding_and_failing_since(r), 2);
run_to(16);
assert_eq!(confirming_until(r), 23);
run_to(19);
TestRandomness::place_candle();
run_to(20);
set_tally(r, 100, 101);
run_to(24);
assert_eq!(approved_since(r), 24);
});

ExtBuilder::default().build_and_execute(|| {
let r = Passing.create_long();
// Normally ends at 5 + 4 (voting period) = 9.
assert_eq!(deciding_and_failing_since(r), 2);
run_to(16);
assert_eq!(confirming_until(r), 23);
run_to(20);
set_tally(r, 100, 101);
run_to(21);
TestRandomness::place_candle();
run_to(24);
assert_eq!(rejected_since(r), 24);
});
}

#[test]
fn queueing_works() {
ExtBuilder::default().build_and_execute(|| {
Expand Down Expand Up @@ -231,35 +267,34 @@ fn queueing_works() {
println!("{:?}", Vec::<_>::from(TrackQueue::<Test>::get(0)));

// Let confirmation period end.
run_to(9);
run_to(10);

// #4 should have been confirmed.
assert_eq!(approved_since(4), 9);
assert_eq!(approved_since(4), 10);

// On to the next block to select the new referendum
run_to(10);
run_to(11);
// #1 (the one with the most approvals) should now be being decided.
assert_eq!(deciding_since(1), 10);
assert_eq!(deciding_since(1), 11);

// Let it end unsuccessfully.
run_to(14);
assert_eq!(rejected_since(1), 14);
run_to(15);
assert_eq!(rejected_since(1), 15);

// Service queue.
run_to(15);
run_to(16);
// #2 should now be being decided. It will (barely) pass.
assert_eq!(deciding_and_failing_since(2), 15);
assert_eq!(deciding_and_failing_since(2), 16);

// #2 moves into confirming at the last moment with a 50% approval.
run_to(19);
assert_eq!(confirming_until(2), 21);
run_to(20);
assert_eq!(confirming_until(2), 22);

// #2 gets approved.
run_to(21);
assert_eq!(approved_since(2), 21);
run_to(23);
assert_eq!(approved_since(2), 23);

// The final one has since timed out.
run_to(22);
assert_eq!(timed_out_since(3), 22);
});
}
Expand Down

0 comments on commit 0a8ea80

Please sign in to comment.