Skip to content
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

feat(democracy): fast-track referendum #653

Merged
merged 2 commits into from Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion crates/democracy/README.md
Expand Up @@ -70,10 +70,12 @@ Preimage actions:

#### Fast Track Origin

This call can only be made by the `FastTrackOrigin`.
These calls can only be made by the `FastTrackOrigin`.

- `fast_track` - Schedules the current externally proposed proposal that
is "majority-carries" to become a referendum immediately.
- `fast_track_referendum` - Schedules an active referendum to end in `FastTrackVotingPeriod`
blocks.

#### Root

Expand Down
24 changes: 23 additions & 1 deletion crates/democracy/src/lib.rs
Expand Up @@ -67,6 +67,8 @@
//!
//! - `fast_track` - Schedules the current externally proposed proposal that is "majority-carries" to become a
//! referendum immediately.
//! - `fast_track_referendum` - Schedules an active referendum to end in `FastTrackVotingPeriod`
//! blocks.
//!
//! #### Root
//!
Expand Down Expand Up @@ -327,8 +329,10 @@ pub mod pallet {
Tabled(PropIndex, BalanceOf<T>, Vec<T::AccountId>),
/// A referendum has begun. \[ref_index, threshold\]
Started(ReferendumIndex, VoteThreshold),
/// A referendum has been fast tracked. \[ref_index\]
/// A proposal has been fast tracked. \[ref_index\]
FastTrack(ReferendumIndex),
/// A referendum has been fast tracked. \[ref_index\]
FastTrackReferendum(ReferendumIndex),
/// A proposal has been approved by referendum. \[ref_index\]
Passed(ReferendumIndex),
/// A proposal has been rejected by referendum. \[ref_index\]
Expand Down Expand Up @@ -373,6 +377,9 @@ pub mod pallet {
PreimageMissing,
/// Vote given for invalid referendum
ReferendumInvalid,
/// Fast tracking failed, because the referendum is
/// ending sooner than the fast track voting period.
ReferendumFastTrackFailed,
/// Invalid preimage
PreimageInvalid,
/// No proposals waiting
Expand Down Expand Up @@ -538,6 +545,21 @@ pub mod pallet {
Ok(())
}

#[pallet::weight(T::WeightInfo::fast_track_referendum())]
pub fn fast_track_referendum(origin: OriginFor<T>, #[pallet::compact] ref_index: PropIndex) -> DispatchResult {
T::FastTrackOrigin::ensure_origin(origin)?;
let mut status = Self::referendum_status(ref_index)?;
let now = <frame_system::Pallet<T>>::block_number();
let voting_period = T::FastTrackVotingPeriod::get();
let end_block = now.saturating_add(voting_period);
ensure!(status.end > end_block, Error::<T>::ReferendumFastTrackFailed);
status.end = end_block;

ReferendumInfoOf::<T>::insert(ref_index, ReferendumInfo::Ongoing(status));
Self::deposit_event(Event::<T>::FastTrackReferendum(ref_index));
Ok(())
}

/// Remove a referendum.
///
/// The dispatch origin of this call must be _Root_.
Expand Down
59 changes: 58 additions & 1 deletion crates/democracy/src/tests/fast_tracking.rs
Expand Up @@ -3,7 +3,7 @@
use super::*;

#[test]
fn fast_track_referendum_works() {
fn fast_track_works() {
new_test_ext().execute_with(|| {
System::set_block_number(0);
// let h = set_balance_proposal_hash_and_note(2);
Expand Down Expand Up @@ -32,6 +32,63 @@ fn fast_track_referendum_works() {
});
}

#[test]
fn fast_track_referendum_works() {
new_test_ext().execute_with(|| {
System::set_block_number(0);
let fast_track_voting_period = <tests::Test as Config>::FastTrackVotingPeriod::get();
let ref_index = Democracy::inject_referendum(
fast_track_voting_period * 2,
set_balance_proposal_hash(2),
VoteThreshold::SuperMajorityAgainst,
0,
);

assert_noop!(
Democracy::fast_track_referendum(Origin::signed(1), ref_index),
BadOrigin
);
assert_ok!(Democracy::fast_track_referendum(Origin::signed(5), ref_index));

let start_height = System::block_number();
let end_height = start_height + fast_track_voting_period;
assert_eq!(
Democracy::referendum_status(ref_index),
Ok(ReferendumStatus {
end: end_height,
proposal_hash: set_balance_proposal_hash_and_note(2),
threshold: VoteThreshold::SuperMajorityAgainst,
delay: 0,
tally: Tally {
ayes: 0,
nays: 0,
turnout: 0
},
})
);
});
}

#[test]
fn fast_track_referendum_fails() {
new_test_ext().execute_with(|| {
System::set_block_number(0);
let fast_track_voting_period = <tests::Test as Config>::FastTrackVotingPeriod::get();
let ref_index = Democracy::inject_referendum(
fast_track_voting_period - 1,
set_balance_proposal_hash(2),
VoteThreshold::SuperMajorityAgainst,
0,
);

// Fails because the referendum ends too soon
assert_noop!(
Democracy::fast_track_referendum(Origin::signed(5), ref_index),
Error::<Test>::ReferendumFastTrackFailed
);
});
}

// #[test]
// fn instant_referendum_works() {
// new_test_ext().execute_with(|| {
Expand Down
13 changes: 13 additions & 0 deletions crates/democracy/src/weights.rs
Expand Up @@ -38,6 +38,7 @@ pub trait WeightInfo {
fn external_propose_majority() -> Weight;
fn external_propose_default() -> Weight;
fn fast_track() -> Weight;
fn fast_track_referendum() -> Weight;
fn veto_external(v: u32, ) -> Weight;
fn cancel_proposal(p: u32, ) -> Weight;
fn cancel_referendum() -> Weight;
Expand Down Expand Up @@ -143,6 +144,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
// Storage: Democracy ReferendumInfoOf (r:1 w:1)
fn fast_track_referendum() -> Weight {
(29_129_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
// Storage: Democracy NextExternal (r:1 w:1)
// Storage: Democracy Blacklist (r:1 w:1)
fn veto_external(v: u32, ) -> Weight {
Expand Down Expand Up @@ -378,6 +385,12 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
}
// Storage: Democracy ReferendumInfoOf (r:1 w:1)
fn fast_track_referendum() -> Weight {
(29_129_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
// Storage: Democracy NextExternal (r:1 w:1)
// Storage: Democracy Blacklist (r:1 w:1)
fn veto_external(v: u32, ) -> Weight {
Expand Down
50 changes: 47 additions & 3 deletions standalone/runtime/tests/test_governance.rs
Expand Up @@ -2,7 +2,7 @@ mod mock;
use crate::assert_eq;
use mock::*;

use democracy::{PropIndex, ReferendumIndex, ReferendumInfo, Vote};
use democracy::{PropIndex, ReferendumIndex, ReferendumInfo, ReferendumStatus, Tally, Vote, VoteThreshold};
use frame_support::traits::{Currency, Hooks};
use orml_vesting::VestingSchedule;
use sp_core::{Encode, Hasher};
Expand Down Expand Up @@ -116,7 +116,7 @@ fn assert_technical_committee_executed_event() {
.expect("execution failed");
}

fn create_proposal(encoded_proposal: Vec<u8>) {
fn create_proposal(encoded_proposal: Vec<u8>) -> H256 {
let proposal_hash = BlakeTwo256::hash(&encoded_proposal[..]);

assert_ok!(
Expand All @@ -128,9 +128,11 @@ fn create_proposal(encoded_proposal: Vec<u8>) {
value: <Runtime as democracy::Config>::MinimumDeposit::get(),
})
.dispatch(origin_of(account_of(ALICE))));

proposal_hash
}

fn create_set_balance_proposal(amount_to_set: Balance) {
fn create_set_balance_proposal(amount_to_set: Balance) -> H256 {
create_proposal(set_balance_proposal(account_of(EVE), amount_to_set))
}

Expand Down Expand Up @@ -395,6 +397,48 @@ fn integration_test_governance_voter_can_change_vote() {
});
}

#[test]
fn integration_test_fast_track_referendum() {
test_with(|| {
let amount_to_set = 1000;
let proposal_hash = create_set_balance_proposal(amount_to_set);

let start_height = <Runtime as democracy::Config>::LaunchPeriod::get();
DemocracyPallet::on_initialize(start_height);
let index = assert_democracy_started_event();

// create motion to fast-track simple-majority referendum
assert_ok!(Call::TechnicalCommittee(TechnicalCommitteeCall::propose {
threshold: 1, // member count
proposal: Box::new(Call::Democracy(DemocracyCall::fast_track_referendum {
ref_index: index,
})),
length_bound: 100000 // length bound
})
.dispatch(origin_of(account_of(ALICE))));
// should be executed immediately with only one member
assert_technical_committee_executed_event();

let now = SystemPallet::block_number();
let fast_track_voting_period = <Runtime as democracy::Config>::FastTrackVotingPeriod::get();
let end = now + fast_track_voting_period;
assert_eq!(
DemocracyPallet::referendum_info(index),
Some(ReferendumInfo::Ongoing(ReferendumStatus {
end,
proposal_hash,
threshold: VoteThreshold::SuperMajorityAgainst,
delay: <Runtime as democracy::Config>::EnactmentPeriod::get(),
tally: Tally {
ayes: 0,
nays: 0,
turnout: 0
},
}))
);
});
}

#[test]
fn integration_test_governance_voter_can_change_vote_with_limited_funds() {
test_with(|| {
Expand Down