mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 00:31:07 +00:00
Lock-voting (#1254)
* Optionally long lock voting * Lock voting. * Fix tests * Comment update * Comments * Minor grumbles * Docs.
This commit is contained in:
committed by
Robert Habermeier
parent
025bcdda0f
commit
27f69def9a
@@ -114,7 +114,6 @@ impl<Xt> traits::Extrinsic for ExtrinsicWrapper<Xt> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Xt: Encode> serde::Serialize for ExtrinsicWrapper<Xt>
|
||||
{
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
|
||||
|
||||
BIN
Binary file not shown.
@@ -92,6 +92,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
|
||||
voting_period: 5 * MINUTES, // 3 days to discuss & vote on an active referendum
|
||||
minimum_deposit: 50 * DOLLARS, // 12000 as the minimum deposit for a referendum
|
||||
public_delay: 0,
|
||||
max_lock_periods: 6,
|
||||
}),
|
||||
council_seats: Some(CouncilSeatsConfig {
|
||||
active_council: vec![],
|
||||
@@ -213,6 +214,7 @@ pub fn testnet_genesis(
|
||||
voting_period: 18,
|
||||
minimum_deposit: 10,
|
||||
public_delay: 0,
|
||||
max_lock_periods: 6,
|
||||
}),
|
||||
council_seats: Some(CouncilSeatsConfig {
|
||||
active_council: endowed_accounts.iter()
|
||||
|
||||
@@ -311,9 +311,9 @@ mod tests {
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
if support_changes_trie {
|
||||
hex!("dd0ab9b04b84b2a0704a6c38968d97a41e693345a9019dae880a80cf8176676c").into()
|
||||
hex!("1a7758d96d7353732f3054a3dacb18f04f42fc48f6706378d6f7be744c6022f1").into()
|
||||
} else {
|
||||
hex!("f5d85f3baebdd85aff8acfdeab0c6e00322d8b64b10a277a0231f4ca10dca7ee").into()
|
||||
hex!("1cf270c8a484df4931af562f7afdc9f44d99ae1bd35fe30fbd2cf3c1be2e933b").into()
|
||||
},
|
||||
if support_changes_trie {
|
||||
vec![changes_trie_log(
|
||||
@@ -339,7 +339,7 @@ mod tests {
|
||||
construct_block(
|
||||
2,
|
||||
block1(false).1,
|
||||
hex!("807f9952d1962fcc62e8ee58fb4a3b5dc0247f314e9920a25354c1a1915e6941").into(),
|
||||
hex!("a208e27269f8a17e7f7cf9513396d3579066df10a853e030345847ec96593c2e").into(),
|
||||
vec![ // session changes here, so we add a grandpa change signal log.
|
||||
Log::from(::grandpa::RawLog::AuthoritiesChangeSignal(0, vec![
|
||||
(Keyring::One.to_raw_public().into(), 1),
|
||||
@@ -368,7 +368,7 @@ mod tests {
|
||||
construct_block(
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
hex!("5cc895b7c1d5fa824a2b6aefdcf34088156b3762509050bc384853106d48c7e4").into(),
|
||||
hex!("a506a69fefa4dc1be6838b68dc6e5799bd5fec545ef890cadac20edc0254d37a").into(),
|
||||
vec![],
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
@@ -658,7 +658,7 @@ mod tests {
|
||||
let b = construct_block(
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
hex!("06d7654fe2799df26ef3ae932b04d17c163b5a89e6e5bc4ad514acced17bf6c4").into(),
|
||||
hex!("3af4e1ba0769122b1e92b138fecf7ce8bb2fe4f2a65fba3b423f87942f1ba8c8").into(),
|
||||
vec![],
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
|
||||
BIN
Binary file not shown.
@@ -128,6 +128,7 @@ mod tests {
|
||||
voting_period: 3,
|
||||
minimum_deposit: 1,
|
||||
public_delay: 0,
|
||||
max_lock_periods: 6,
|
||||
}.build_storage().unwrap().0);
|
||||
t.extend(seats::GenesisConfig::<Test> {
|
||||
candidacy_bond: 9,
|
||||
|
||||
@@ -236,7 +236,7 @@ mod tests {
|
||||
use ::tests::*;
|
||||
use ::tests::{Call, Origin};
|
||||
use srml_support::Hashable;
|
||||
use democracy::VoteThreshold;
|
||||
use democracy::{ReferendumInfo, VoteThreshold};
|
||||
|
||||
#[test]
|
||||
fn basic_environment_works() {
|
||||
@@ -272,7 +272,7 @@ mod tests {
|
||||
System::set_block_number(1);
|
||||
let proposal = set_balance_proposal(42);
|
||||
assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove, 0), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove, 0)]);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, ReferendumInfo::new(4, proposal, VoteThreshold::SuperMajorityApprove, 0))]);
|
||||
|
||||
let cancellation = cancel_referendum_proposal(0);
|
||||
let hash = cancellation.blake2_256().into();
|
||||
@@ -305,7 +305,7 @@ mod tests {
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove, 0)]);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, ReferendumInfo::new(4, proposal, VoteThreshold::SuperMajorityApprove, 0))]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ mod tests {
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove, 0)]);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, ReferendumInfo::new(4, proposal, VoteThreshold::SuperMajorityApprove, 0))]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ mod tests {
|
||||
System::set_block_number(4);
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 7, set_balance_proposal(42), VoteThreshold::SimpleMajority, 0)]);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, ReferendumInfo::new(7, set_balance_proposal(42), VoteThreshold::SimpleMajority, 0))]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -453,7 +453,7 @@ mod tests {
|
||||
System::set_block_number(2);
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SuperMajorityAgainst, 0)]);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, ReferendumInfo::new(5, proposal, VoteThreshold::SuperMajorityAgainst, 0))]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ mod tests {
|
||||
System::set_block_number(2);
|
||||
assert_ok!(CouncilVoting::end_block(System::block_number()));
|
||||
assert_eq!(CouncilVoting::proposals().len(), 0);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, 5, proposal, VoteThreshold::SimpleMajority, 0)]);
|
||||
assert_eq!(Democracy::active_referendums(), vec![(0, ReferendumInfo::new(5, proposal, VoteThreshold::SimpleMajority, 0))]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,35 @@ pub use vote_threshold::{Approved, VoteThreshold};
|
||||
pub type PropIndex = u32;
|
||||
/// A referendum index.
|
||||
pub type ReferendumIndex = u32;
|
||||
/// A number of lock periods.
|
||||
pub type LockPeriods = i8;
|
||||
|
||||
/// A number of lock periods, plus a vote, one way or the other.
|
||||
#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Default)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct Vote(i8);
|
||||
|
||||
impl Vote {
|
||||
/// Create a new instance.
|
||||
pub fn new(aye: bool, multiplier: LockPeriods) -> Self {
|
||||
let m = multiplier.max(1) - 1;
|
||||
Vote(if aye {
|
||||
-1 - m
|
||||
} else {
|
||||
m
|
||||
})
|
||||
}
|
||||
|
||||
/// Is this an aye vote?
|
||||
pub fn is_aye(self) -> bool {
|
||||
self.0 < 0
|
||||
}
|
||||
|
||||
/// The strength (measured in lock periods).
|
||||
pub fn multiplier(self) -> LockPeriods {
|
||||
1 + if self.0 < 0 { -(self.0 + 1) } else { self.0 }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Trait: balances::Trait + Sized {
|
||||
type Proposal: Parameter + Dispatchable<Origin=Self::Origin> + IsSubType<Module<Self>>;
|
||||
@@ -94,18 +123,19 @@ decl_module! {
|
||||
<DepositOf<T>>::insert(proposal, deposit);
|
||||
}
|
||||
|
||||
/// Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal;
|
||||
/// false would be a vote to keep the status quo.
|
||||
fn vote(origin, ref_index: Compact<ReferendumIndex>, approve_proposal: bool) {
|
||||
/// Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;
|
||||
/// otherwise it is a vote to keep the status quo.
|
||||
fn vote(origin, ref_index: Compact<ReferendumIndex>, vote: Vote) {
|
||||
let who = ensure_signed(origin)?;
|
||||
let ref_index = ref_index.into();
|
||||
ensure!(vote.multiplier() <= Self::max_lock_periods(), "vote has too great a strength");
|
||||
ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum.");
|
||||
ensure!(!<balances::Module<T>>::total_balance(&who).is_zero(),
|
||||
"transactor must have balance to signal approval.");
|
||||
if !<VoteOf<T>>::exists(&(ref_index, who.clone())) {
|
||||
<VotersFor<T>>::mutate(ref_index, |voters| voters.push(who.clone()));
|
||||
}
|
||||
<VoteOf<T>>::insert(&(ref_index, who), approve_proposal);
|
||||
<VoteOf<T>>::insert(&(ref_index, who), vote);
|
||||
}
|
||||
|
||||
/// Start a referendum.
|
||||
@@ -152,6 +182,13 @@ pub struct ReferendumInfo<BlockNumber: Parameter, Proposal: Parameter> {
|
||||
delay: BlockNumber,
|
||||
}
|
||||
|
||||
impl<BlockNumber: Parameter, Proposal: Parameter> ReferendumInfo<BlockNumber, Proposal> {
|
||||
/// Create a new instance.
|
||||
pub fn new(end: BlockNumber, proposal: Proposal, threshold: VoteThreshold, delay: BlockNumber) -> Self {
|
||||
ReferendumInfo { end, proposal, threshold, delay }
|
||||
}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Democracy {
|
||||
|
||||
@@ -167,6 +204,8 @@ decl_storage! {
|
||||
pub MinimumDeposit get(minimum_deposit) config(): T::Balance;
|
||||
/// The delay before enactment for all public referenda.
|
||||
pub PublicDelay get(public_delay) config(): T::BlockNumber;
|
||||
/// The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`.
|
||||
pub MaxLockPeriods get(max_lock_periods) config(): LockPeriods;
|
||||
|
||||
/// How often (in blocks) to check for new votes.
|
||||
pub VotingPeriod get(voting_period) config(): T::BlockNumber = T::BlockNumber::sa(1000);
|
||||
@@ -186,8 +225,10 @@ decl_storage! {
|
||||
/// Get the voters for the current proposal.
|
||||
pub VotersFor get(voters_for): map ReferendumIndex => Vec<T::AccountId>;
|
||||
|
||||
/// Get the vote, if Some, of `who`.
|
||||
pub VoteOf get(vote_of): map (ReferendumIndex, T::AccountId) => Option<bool>;
|
||||
/// Get the vote in a given referendum of a particular voter. The result is meaningful only if `voters_for` includes the
|
||||
/// voter when called with the referendum (you'll get the default `Vote` value otherwise). If you don't want to check
|
||||
/// `voters_for`, then you can also check for simple existence with `VoteOf::exists` first.
|
||||
pub VoteOf get(vote_of): map (ReferendumIndex, T::AccountId) => Vote;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,30 +259,38 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Get all referendums currently active.
|
||||
pub fn active_referendums() -> Vec<(ReferendumIndex, T::BlockNumber, T::Proposal, VoteThreshold, T::BlockNumber)> {
|
||||
pub fn active_referendums() -> Vec<(ReferendumIndex, ReferendumInfo<T::BlockNumber, T::Proposal>)> {
|
||||
let next = Self::next_tally();
|
||||
let last = Self::referendum_count();
|
||||
(next..last).into_iter()
|
||||
.filter_map(|i| Self::referendum_info(i).map(|ReferendumInfo{ end, proposal, threshold, delay }| (i, end, proposal, threshold, delay)))
|
||||
.filter_map(|i| Self::referendum_info(i).map(|info| (i, info)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get all referendums ready for tally at block `n`.
|
||||
pub fn maturing_referendums_at(n: T::BlockNumber) -> Vec<(ReferendumIndex, T::BlockNumber, T::Proposal, VoteThreshold, T::BlockNumber)> {
|
||||
pub fn maturing_referendums_at(n: T::BlockNumber) -> Vec<(ReferendumIndex, ReferendumInfo<T::BlockNumber, T::Proposal>)> {
|
||||
let next = Self::next_tally();
|
||||
let last = Self::referendum_count();
|
||||
(next..last).into_iter()
|
||||
.filter_map(|i| Self::referendum_info(i).map(|ReferendumInfo{ end, proposal, threshold, delay }| (i, end, proposal, threshold, delay)))
|
||||
.take_while(|&(_, block_number, _, _, _)| block_number == n)
|
||||
.filter_map(|i| Self::referendum_info(i).map(|info| (i, info)))
|
||||
.take_while(|&(_, ref info)| info.end == n)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get the voters for the current proposal.
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) {
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance, T::Balance) {
|
||||
Self::voters_for(ref_index).iter()
|
||||
.map(|a| (<balances::Module<T>>::total_balance(a), Self::vote_of((ref_index, a.clone())).unwrap_or(false)/*defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed*/))
|
||||
.map(|(bal, vote)| if vote { (bal, Zero::zero()) } else { (Zero::zero(), bal) })
|
||||
.fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d))
|
||||
.map(|voter| (
|
||||
<balances::Module<T>>::total_balance(voter),
|
||||
Self::vote_of((ref_index, voter.clone())),
|
||||
))
|
||||
.map(|(bal, vote)|
|
||||
if vote.is_aye() {
|
||||
(bal * T::Balance::sa(vote.multiplier() as u64), Zero::zero(), bal)
|
||||
} else {
|
||||
(Zero::zero(), bal * T::Balance::sa(vote.multiplier() as u64), bal)
|
||||
}
|
||||
).fold((Zero::zero(), Zero::zero(), Zero::zero()), |(a, b, c), (d, e, f)| (a + d, b + e, c + f))
|
||||
}
|
||||
|
||||
// Exposed mutables.
|
||||
@@ -292,51 +341,75 @@ impl<T: Trait> Module<T> {
|
||||
Self::deposit_event(RawEvent::Executed(index, ok));
|
||||
}
|
||||
|
||||
fn launch_next(now: T::BlockNumber) -> Result {
|
||||
let mut public_props = Self::public_props();
|
||||
if let Some((winner_index, _)) = public_props.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|x| Self::locked_for((x.1).0).unwrap_or_else(Zero::zero)/*defensive only: All current public proposals have an amount locked*/)
|
||||
{
|
||||
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
|
||||
<PublicProps<T>>::put(public_props);
|
||||
|
||||
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (T::Balance, Vec<T::AccountId>) =
|
||||
// refund depositors
|
||||
for d in &depositors {
|
||||
<balances::Module<T>>::unreserve(d, deposit);
|
||||
}
|
||||
Self::deposit_event(RawEvent::Tabled(prop_index, deposit, depositors));
|
||||
Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove, Self::public_delay())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bake_referendum(now: T::BlockNumber, index: ReferendumIndex, info: ReferendumInfo<T::BlockNumber, T::Proposal>) -> Result {
|
||||
let (approve, against, capital) = Self::tally(index);
|
||||
let total_issuance = <balances::Module<T>>::total_issuance();
|
||||
let approved = info.threshold.approved(approve, against, capital, total_issuance);
|
||||
let lock_period = Self::public_delay();
|
||||
|
||||
// Logic defined in https://www.slideshare.net/gavofyork/governance-in-polkadot-poc3
|
||||
// Essentially, we extend the lock-period of the coins behind the winning votes to be the
|
||||
// vote strength times the public delay period from now.
|
||||
for (a, vote) in Self::voters_for(index).into_iter()
|
||||
.map(|a| (a.clone(), Self::vote_of((index, a))))
|
||||
// ^^^ defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed
|
||||
.filter(|&(_, vote)| vote.is_aye() == approved) // Just the winning coins
|
||||
{
|
||||
// now plus: the base lock period multiplied by the number of periods this voter offered to
|
||||
// lock should they win...
|
||||
let locked_until = now + lock_period * T::BlockNumber::sa((vote.multiplier()) as u64);
|
||||
// ...extend their bondage until at least then.
|
||||
<Bondage<T>>::mutate(a, |b| if *b < locked_until { *b = locked_until });
|
||||
}
|
||||
|
||||
Self::clear_referendum(index);
|
||||
if approved {
|
||||
Self::deposit_event(RawEvent::Passed(index));
|
||||
if info.delay.is_zero() {
|
||||
Self::enact_proposal(info.proposal, index);
|
||||
} else {
|
||||
<DispatchQueue<T>>::mutate(now + info.delay, |q| q.push(Some((info.proposal, index))));
|
||||
}
|
||||
} else {
|
||||
Self::deposit_event(RawEvent::NotPassed(index));
|
||||
}
|
||||
<NextTally<T>>::put(index + 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Current era is ending; we should finish up any proposals.
|
||||
fn end_block(now: T::BlockNumber) -> Result {
|
||||
// pick out another public referendum if it's time.
|
||||
if (now % Self::launch_period()).is_zero() {
|
||||
let mut public_props = Self::public_props();
|
||||
if let Some((winner_index, _)) = public_props.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|x| Self::locked_for((x.1).0).unwrap_or_else(Zero::zero)/*defensive only: All current public proposals have an amount locked*/)
|
||||
{
|
||||
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
|
||||
<PublicProps<T>>::put(public_props);
|
||||
|
||||
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (T::Balance, Vec<T::AccountId>) =
|
||||
// refund depositors
|
||||
for d in &depositors {
|
||||
<balances::Module<T>>::unreserve(d, deposit);
|
||||
}
|
||||
Self::deposit_event(RawEvent::Tabled(prop_index, deposit, depositors));
|
||||
Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove, Self::public_delay())?;
|
||||
}
|
||||
}
|
||||
Self::launch_next(now.clone())?;
|
||||
}
|
||||
|
||||
// tally up votes for any expiring referenda.
|
||||
for (index, _, proposal, threshold, delay) in Self::maturing_referendums_at(now) {
|
||||
let (approve, against) = Self::tally(index);
|
||||
let total_issuance = <balances::Module<T>>::total_issuance();
|
||||
let approved = threshold.approved(approve, against, total_issuance);
|
||||
|
||||
Self::voters_for(index).into_iter()
|
||||
.filter(|a| (Self::vote_of((index, a.clone())).unwrap_or(false)/*defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed*/ == approved))
|
||||
.for_each(|a| <Bondage<T>>::mutate(a, |b| if *b < now + delay { *b = now + delay }));
|
||||
|
||||
Self::clear_referendum(index);
|
||||
if approved {
|
||||
Self::deposit_event(RawEvent::Passed(index));
|
||||
if delay.is_zero() {
|
||||
Self::enact_proposal(proposal, index);
|
||||
} else {
|
||||
<DispatchQueue<T>>::mutate(now + delay, |q| q.push(Some((proposal, index))));
|
||||
}
|
||||
} else {
|
||||
Self::deposit_event(RawEvent::NotPassed(index));
|
||||
}
|
||||
<NextTally<T>>::put(index + 1);
|
||||
for (index, info) in Self::maturing_referendums_at(now).into_iter() {
|
||||
Self::bake_referendum(now.clone(), index, info)?;
|
||||
}
|
||||
|
||||
for (proposal, index) in <DispatchQueue<T>>::take(now).into_iter().filter_map(|x| x) {
|
||||
@@ -371,6 +444,9 @@ mod tests {
|
||||
use primitives::traits::{BlakeTwo256};
|
||||
use primitives::testing::{Digest, DigestItem, Header};
|
||||
|
||||
const AYE: Vote = Vote(-1);
|
||||
const NAY: Vote = Vote(0);
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
@@ -410,6 +486,10 @@ mod tests {
|
||||
}
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities<Blake2Hasher> {
|
||||
new_test_ext_with_public_delay(0)
|
||||
}
|
||||
|
||||
fn new_test_ext_with_public_delay(public_delay: u64) -> runtime_io::TestExternalities<Blake2Hasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
|
||||
t.extend(balances::GenesisConfig::<Test>{
|
||||
balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
|
||||
@@ -424,7 +504,8 @@ mod tests {
|
||||
launch_period: 1,
|
||||
voting_period: 1,
|
||||
minimum_deposit: 1,
|
||||
public_delay: 0,
|
||||
public_delay,
|
||||
max_lock_periods: 6,
|
||||
}.build_storage().unwrap().0);
|
||||
runtime_io::TestExternalities::new(t)
|
||||
}
|
||||
@@ -442,6 +523,34 @@ mod tests {
|
||||
assert_eq!(Democracy::referendum_count(), 0);
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
assert_eq!(Balances::total_issuance(), 210);
|
||||
assert_eq!(Democracy::public_delay(), 0);
|
||||
assert_eq!(Democracy::max_lock_periods(), 6);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vote_should_work() {
|
||||
assert_eq!(Vote::new(true, 0).multiplier(), 1);
|
||||
assert_eq!(Vote::new(true, 1).multiplier(), 1);
|
||||
assert_eq!(Vote::new(true, 2).multiplier(), 2);
|
||||
assert_eq!(Vote::new(true, 0).is_aye(), true);
|
||||
assert_eq!(Vote::new(true, 1).is_aye(), true);
|
||||
assert_eq!(Vote::new(true, 2).is_aye(), true);
|
||||
assert_eq!(Vote::new(false, 0).multiplier(), 1);
|
||||
assert_eq!(Vote::new(false, 1).multiplier(), 1);
|
||||
assert_eq!(Vote::new(false, 2).multiplier(), 2);
|
||||
assert_eq!(Vote::new(false, 0).is_aye(), false);
|
||||
assert_eq!(Vote::new(false, 1).is_aye(), false);
|
||||
assert_eq!(Vote::new(false, 2).is_aye(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_vote_strength_should_not_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0).unwrap();
|
||||
assert_noop!(Democracy::vote(Origin::signed(1), r.into(), Vote::new(true, 7)), "vote has too great a strength");
|
||||
assert_noop!(Democracy::vote(Origin::signed(1), r.into(), Vote::new(false, 7)), "vote has too great a strength");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -475,12 +584,12 @@ mod tests {
|
||||
|
||||
System::set_block_number(2);
|
||||
let r = 0;
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), AYE));
|
||||
|
||||
assert_eq!(Democracy::referendum_count(), 1);
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
||||
assert_eq!(Democracy::tally(r), (10, 0));
|
||||
assert_eq!(Democracy::vote_of((r, 1)), AYE);
|
||||
assert_eq!(Democracy::tally(r), (10, 0, 10));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
@@ -554,17 +663,17 @@ mod tests {
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
System::set_block_number(1);
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 0.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 0.into(), AYE));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
assert_eq!(Balances::free_balance(&42), 4);
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 1.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 1.into(), AYE));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
assert_eq!(Balances::free_balance(&42), 3);
|
||||
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 2.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), 2.into(), AYE));
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
});
|
||||
}
|
||||
@@ -574,11 +683,11 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0).unwrap();
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), AYE));
|
||||
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
assert_eq!(Democracy::vote_of((r, 1)), Some(true));
|
||||
assert_eq!(Democracy::tally(r), (10, 0));
|
||||
assert_eq!(Democracy::vote_of((r, 1)), AYE);
|
||||
assert_eq!(Democracy::tally(r), (10, 0, 10));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
@@ -591,7 +700,7 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0).unwrap();
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), AYE));
|
||||
assert_ok!(Democracy::cancel_referendum(r.into()));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
@@ -605,11 +714,11 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0).unwrap();
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), NAY));
|
||||
|
||||
assert_eq!(Democracy::voters_for(r), vec![1]);
|
||||
assert_eq!(Democracy::vote_of((r, 1)), Some(false));
|
||||
assert_eq!(Democracy::tally(r), (0, 10));
|
||||
assert_eq!(Democracy::vote_of((r, 1)), NAY);
|
||||
assert_eq!(Democracy::tally(r), (0, 10, 10));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
@@ -622,14 +731,14 @@ mod tests {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0).unwrap();
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(2), r.into(), false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(3), r.into(), false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(4), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r.into(), false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), AYE));
|
||||
assert_ok!(Democracy::vote(Origin::signed(2), r.into(), NAY));
|
||||
assert_ok!(Democracy::vote(Origin::signed(3), r.into(), NAY));
|
||||
assert_ok!(Democracy::vote(Origin::signed(4), r.into(), AYE));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r.into(), NAY));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r.into(), AYE));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (110, 100));
|
||||
assert_eq!(Democracy::tally(r), (110, 100, 210));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
@@ -637,15 +746,69 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delayed_enactment_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 1).unwrap();
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), AYE));
|
||||
assert_ok!(Democracy::vote(Origin::signed(2), r.into(), AYE));
|
||||
assert_ok!(Democracy::vote(Origin::signed(3), r.into(), AYE));
|
||||
assert_ok!(Democracy::vote(Origin::signed(4), r.into(), AYE));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r.into(), AYE));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r.into(), AYE));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (210, 0, 210));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
assert_eq!(Balances::free_balance(&42), 0);
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
assert_eq!(Balances::free_balance(&42), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext_with_public_delay(1), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0).unwrap();
|
||||
assert_ok!(Democracy::vote(Origin::signed(1), r.into(), Vote::new(false, 6)));
|
||||
assert_ok!(Democracy::vote(Origin::signed(2), r.into(), Vote::new(true, 5)));
|
||||
assert_ok!(Democracy::vote(Origin::signed(3), r.into(), Vote::new(true, 4)));
|
||||
assert_ok!(Democracy::vote(Origin::signed(4), r.into(), Vote::new(true, 3)));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r.into(), Vote::new(true, 2)));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r.into(), Vote::new(false, 1)));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (440, 120, 210));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
assert_eq!(Democracy::bondage(1), 0);
|
||||
assert_eq!(Democracy::bondage(2), 6);
|
||||
assert_eq!(Democracy::bondage(3), 5);
|
||||
assert_eq!(Democracy::bondage(4), 4);
|
||||
assert_eq!(Democracy::bondage(5), 3);
|
||||
assert_eq!(Democracy::bondage(6), 0);
|
||||
|
||||
System::set_block_number(2);
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
assert_eq!(Balances::free_balance(&42), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn controversial_low_turnout_voting_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0).unwrap();
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r.into(), false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r.into(), NAY));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r.into(), AYE));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (60, 50));
|
||||
assert_eq!(Democracy::tally(r), (60, 50, 110));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
@@ -661,11 +824,11 @@ mod tests {
|
||||
|
||||
System::set_block_number(1);
|
||||
let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0).unwrap();
|
||||
assert_ok!(Democracy::vote(Origin::signed(4), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r.into(), false));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r.into(), true));
|
||||
assert_ok!(Democracy::vote(Origin::signed(4), r.into(), AYE));
|
||||
assert_ok!(Democracy::vote(Origin::signed(5), r.into(), NAY));
|
||||
assert_ok!(Democracy::vote(Origin::signed(6), r.into(), AYE));
|
||||
|
||||
assert_eq!(Democracy::tally(r), (100, 50));
|
||||
assert_eq!(Democracy::tally(r), (100, 50, 150));
|
||||
|
||||
assert_eq!(Democracy::end_block(System::block_number()), Ok(()));
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ pub trait Approved<Balance> {
|
||||
/// Given `approve` votes for and `against` votes against from a total electorate size of
|
||||
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
|
||||
/// overall outcome is in favour of approval.
|
||||
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool;
|
||||
fn approved(&self, approve: Balance, against: Balance, voters: Balance, electorate: Balance) -> bool;
|
||||
}
|
||||
|
||||
/// Return `true` iff `n1 / d1 < n2 / d2`. `d1` and `d2` may not be zero.
|
||||
@@ -68,10 +68,12 @@ fn compare_rationals<T: Zero + Mul<T, Output = T> + Div<T, Output = T> + Rem<T,
|
||||
|
||||
impl<Balance: IntegerSquareRoot + Zero + Ord + Add<Balance, Output = Balance> + Mul<Balance, Output = Balance> + Div<Balance, Output = Balance> + Rem<Balance, Output = Balance> + Copy> Approved<Balance> for VoteThreshold {
|
||||
/// Given `approve` votes for and `against` votes against from a total electorate size of
|
||||
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
|
||||
/// `electorate` of whom `voters` voted (`electorate - voters` are abstainers) then returns true if the
|
||||
/// overall outcome is in favour of approval.
|
||||
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool {
|
||||
let voters = approve + against;
|
||||
///
|
||||
/// We assume each *voter* may cast more than one *vote*, hence `voters` is not necessarily equal to
|
||||
/// `approve + against`.
|
||||
fn approved(&self, approve: Balance, against: Balance, voters: Balance, electorate: Balance) -> bool {
|
||||
let sqrt_voters = voters.integer_sqrt();
|
||||
let sqrt_electorate = electorate.integer_sqrt();
|
||||
if sqrt_voters.is_zero() { return false; }
|
||||
@@ -91,7 +93,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_work() {
|
||||
assert_eq!(VoteThreshold::SuperMajorityApprove.approved(60, 50, 210), false);
|
||||
assert_eq!(VoteThreshold::SuperMajorityApprove.approved(100, 50, 210), true);
|
||||
assert_eq!(VoteThreshold::SuperMajorityApprove.approved(60, 50, 110, 210), false);
|
||||
assert_eq!(VoteThreshold::SuperMajorityApprove.approved(100, 50, 150, 210), true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user