Several tweaks needed for Governance 2.0 (#11124)

* Add stepped curve for referenda

* Treasury SpendOrigin

* Add tests

* Better Origin Or-gating

* Reciprocal curve

* Tests for reciprical and rounding in PerThings

* Tweaks and new quad curve

* Const derivation of reciprocal curve parameters

* Remove some unneeded code

* Actually useful linear curve

* Fixes

* Provisional curves

* Rejig 'turnout' as 'support'

* Use TypedGet

* Fixes

* Enable curve's ceil to be configured

* Formatting

* Fixes

* Fixes

* Fixes

* Remove EnsureOneOf

* Fixes

* Fixes

* Fixes

* Formatting

* Fixes

* Update frame/support/src/traits/dispatch.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Grumbles

* Formatting

* Fixes

* APIs of VoteTally should include class

* Fixes

* Fix overlay prefix removal result

* Second part of the overlay prefix removal fix.

* Formatting

* Fixes

* Add some tests and make clear rounding algo

* Fixes

* Formatting

* Revert questionable fix

* Introduce test for kill_prefix

* Fixes

* Formatting

* Fixes

* Fix possible overflow

* Docs

* Add benchmark test

* Formatting

* Update frame/referenda/src/types.rs

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* Docs

* Fixes

* Use latest API in tests

* Formatting

* Whitespace

* Use latest API in tests

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
This commit is contained in:
Gavin Wood
2022-05-31 11:12:34 +01:00
committed by GitHub
parent c808340d9a
commit 7808b0c349
34 changed files with 2050 additions and 339 deletions
+45 -45
View File
@@ -21,7 +21,7 @@ use std::collections::BTreeMap;
use frame_support::{
assert_noop, assert_ok, parameter_types,
traits::{ConstU32, ConstU64, Contains, Polling},
traits::{ConstU32, ConstU64, Contains, Polling, VoteTally},
};
use sp_core::H256;
use sp_runtime::{
@@ -166,7 +166,7 @@ impl Polling<TallyOf<Test>> for TestPolls {
fn create_ongoing(class: Self::Class) -> Result<Self::Index, ()> {
let mut polls = Polls::get();
let i = polls.keys().rev().next().map_or(0, |x| x + 1);
polls.insert(i, Ongoing(Tally::default(), class));
polls.insert(i, Ongoing(Tally::new(0), class));
Polls::set(polls);
Ok(i)
}
@@ -271,19 +271,19 @@ fn basic_voting_works() {
assert_ok!(Voting::vote(Origin::signed(1), 3, aye(2, 5)));
assert_eq!(tally(3), Tally::from_parts(10, 0, 2));
assert_ok!(Voting::vote(Origin::signed(1), 3, nay(2, 5)));
assert_eq!(tally(3), Tally::from_parts(0, 10, 2));
assert_eq!(tally(3), Tally::from_parts(0, 10, 0));
assert_eq!(Balances::usable_balance(1), 8);
assert_ok!(Voting::vote(Origin::signed(1), 3, aye(5, 1)));
assert_eq!(tally(3), Tally::from_parts(5, 0, 5));
assert_ok!(Voting::vote(Origin::signed(1), 3, nay(5, 1)));
assert_eq!(tally(3), Tally::from_parts(0, 5, 5));
assert_eq!(tally(3), Tally::from_parts(0, 5, 0));
assert_eq!(Balances::usable_balance(1), 5);
assert_ok!(Voting::vote(Origin::signed(1), 3, aye(10, 0)));
assert_eq!(tally(3), Tally::from_parts(1, 0, 10));
assert_ok!(Voting::vote(Origin::signed(1), 3, nay(10, 0)));
assert_eq!(tally(3), Tally::from_parts(0, 1, 10));
assert_eq!(tally(3), Tally::from_parts(0, 1, 0));
assert_eq!(Balances::usable_balance(1), 0);
assert_ok!(Voting::remove_vote(Origin::signed(1), None, 3));
@@ -300,19 +300,19 @@ fn voting_balance_gets_locked() {
assert_ok!(Voting::vote(Origin::signed(1), 3, aye(2, 5)));
assert_eq!(tally(3), Tally::from_parts(10, 0, 2));
assert_ok!(Voting::vote(Origin::signed(1), 3, nay(2, 5)));
assert_eq!(tally(3), Tally::from_parts(0, 10, 2));
assert_eq!(tally(3), Tally::from_parts(0, 10, 0));
assert_eq!(Balances::usable_balance(1), 8);
assert_ok!(Voting::vote(Origin::signed(1), 3, aye(5, 1)));
assert_eq!(tally(3), Tally::from_parts(5, 0, 5));
assert_ok!(Voting::vote(Origin::signed(1), 3, nay(5, 1)));
assert_eq!(tally(3), Tally::from_parts(0, 5, 5));
assert_eq!(tally(3), Tally::from_parts(0, 5, 0));
assert_eq!(Balances::usable_balance(1), 5);
assert_ok!(Voting::vote(Origin::signed(1), 3, aye(10, 0)));
assert_eq!(tally(3), Tally::from_parts(1, 0, 10));
assert_ok!(Voting::vote(Origin::signed(1), 3, nay(10, 0)));
assert_eq!(tally(3), Tally::from_parts(0, 1, 10));
assert_eq!(tally(3), Tally::from_parts(0, 1, 0));
assert_eq!(Balances::usable_balance(1), 0);
assert_ok!(Voting::remove_vote(Origin::signed(1), None, 3));
@@ -376,10 +376,10 @@ fn classwise_delegation_works() {
new_test_ext().execute_with(|| {
Polls::set(
vec![
(0, Ongoing(Tally::default(), 0)),
(1, Ongoing(Tally::default(), 1)),
(2, Ongoing(Tally::default(), 2)),
(3, Ongoing(Tally::default(), 2)),
(0, Ongoing(Tally::new(0), 0)),
(1, Ongoing(Tally::new(0), 1)),
(2, Ongoing(Tally::new(0), 2)),
(3, Ongoing(Tally::new(0), 2)),
]
.into_iter()
.collect(),
@@ -403,9 +403,9 @@ fn classwise_delegation_works() {
assert_eq!(
Polls::get(),
vec![
(0, Ongoing(Tally::from_parts(6, 2, 35), 0)),
(1, Ongoing(Tally::from_parts(6, 2, 35), 1)),
(2, Ongoing(Tally::from_parts(6, 2, 35), 2)),
(0, Ongoing(Tally::from_parts(6, 2, 15), 0)),
(1, Ongoing(Tally::from_parts(6, 2, 15), 1)),
(2, Ongoing(Tally::from_parts(6, 2, 15), 2)),
(3, Ongoing(Tally::from_parts(0, 0, 0), 2)),
]
.into_iter()
@@ -417,10 +417,10 @@ fn classwise_delegation_works() {
assert_eq!(
Polls::get(),
vec![
(0, Ongoing(Tally::from_parts(6, 2, 35), 0)),
(1, Ongoing(Tally::from_parts(6, 2, 35), 1)),
(2, Ongoing(Tally::from_parts(6, 2, 35), 2)),
(3, Ongoing(Tally::from_parts(0, 6, 15), 2)),
(0, Ongoing(Tally::from_parts(6, 2, 15), 0)),
(1, Ongoing(Tally::from_parts(6, 2, 15), 1)),
(2, Ongoing(Tally::from_parts(6, 2, 15), 2)),
(3, Ongoing(Tally::from_parts(0, 6, 0), 2)),
]
.into_iter()
.collect()
@@ -432,10 +432,10 @@ fn classwise_delegation_works() {
assert_eq!(
Polls::get(),
vec![
(0, Ongoing(Tally::from_parts(6, 2, 35), 0)),
(1, Ongoing(Tally::from_parts(6, 2, 35), 1)),
(2, Ongoing(Tally::from_parts(1, 7, 35), 2)),
(3, Ongoing(Tally::from_parts(0, 1, 10), 2)),
(0, Ongoing(Tally::from_parts(6, 2, 15), 0)),
(1, Ongoing(Tally::from_parts(6, 2, 15), 1)),
(2, Ongoing(Tally::from_parts(1, 7, 10), 2)),
(3, Ongoing(Tally::from_parts(0, 1, 0), 2)),
]
.into_iter()
.collect()
@@ -451,10 +451,10 @@ fn classwise_delegation_works() {
assert_eq!(
Polls::get(),
vec![
(0, Ongoing(Tally::from_parts(4, 2, 33), 0)),
(1, Ongoing(Tally::from_parts(4, 2, 33), 1)),
(2, Ongoing(Tally::from_parts(4, 2, 33), 2)),
(3, Ongoing(Tally::from_parts(0, 4, 13), 2)),
(0, Ongoing(Tally::from_parts(4, 2, 13), 0)),
(1, Ongoing(Tally::from_parts(4, 2, 13), 1)),
(2, Ongoing(Tally::from_parts(4, 2, 13), 2)),
(3, Ongoing(Tally::from_parts(0, 4, 0), 2)),
]
.into_iter()
.collect()
@@ -483,10 +483,10 @@ fn classwise_delegation_works() {
assert_eq!(
Polls::get(),
vec![
(0, Ongoing(Tally::from_parts(7, 2, 36), 0)),
(1, Ongoing(Tally::from_parts(8, 2, 37), 1)),
(2, Ongoing(Tally::from_parts(9, 2, 38), 2)),
(3, Ongoing(Tally::from_parts(0, 9, 18), 2)),
(0, Ongoing(Tally::from_parts(7, 2, 16), 0)),
(1, Ongoing(Tally::from_parts(8, 2, 17), 1)),
(2, Ongoing(Tally::from_parts(9, 2, 18), 2)),
(3, Ongoing(Tally::from_parts(0, 9, 0), 2)),
]
.into_iter()
.collect()
@@ -497,7 +497,7 @@ fn classwise_delegation_works() {
#[test]
fn redelegation_after_vote_ending_should_keep_lock() {
new_test_ext().execute_with(|| {
Polls::set(vec![(0, Ongoing(Tally::default(), 0))].into_iter().collect());
Polls::set(vec![(0, Ongoing(Tally::new(0), 0))].into_iter().collect());
assert_ok!(Voting::delegate(Origin::signed(1), 0, 2, Conviction::Locked1x, 5));
assert_ok!(Voting::vote(Origin::signed(2), 0, aye(10, 1)));
Polls::set(vec![(0, Completed(1, true))].into_iter().collect());
@@ -515,9 +515,9 @@ fn lock_amalgamation_valid_with_multiple_removed_votes() {
new_test_ext().execute_with(|| {
Polls::set(
vec![
(0, Ongoing(Tally::default(), 0)),
(1, Ongoing(Tally::default(), 0)),
(2, Ongoing(Tally::default(), 0)),
(0, Ongoing(Tally::new(0), 0)),
(1, Ongoing(Tally::new(0), 0)),
(2, Ongoing(Tally::new(0), 0)),
]
.into_iter()
.collect(),
@@ -587,7 +587,7 @@ fn lock_amalgamation_valid_with_multiple_delegations() {
#[test]
fn lock_amalgamation_valid_with_move_roundtrip_to_delegation() {
new_test_ext().execute_with(|| {
Polls::set(vec![(0, Ongoing(Tally::default(), 0))].into_iter().collect());
Polls::set(vec![(0, Ongoing(Tally::new(0), 0))].into_iter().collect());
assert_ok!(Voting::vote(Origin::signed(1), 0, aye(5, 1)));
Polls::set(vec![(0, Completed(1, true))].into_iter().collect());
assert_ok!(Voting::remove_vote(Origin::signed(1), Some(0), 0));
@@ -599,7 +599,7 @@ fn lock_amalgamation_valid_with_move_roundtrip_to_delegation() {
assert_ok!(Voting::unlock(Origin::signed(1), 0, 1));
assert_eq!(Balances::usable_balance(1), 0);
Polls::set(vec![(1, Ongoing(Tally::default(), 0))].into_iter().collect());
Polls::set(vec![(1, Ongoing(Tally::new(0), 0))].into_iter().collect());
assert_ok!(Voting::vote(Origin::signed(1), 1, aye(5, 2)));
Polls::set(vec![(1, Completed(1, true))].into_iter().collect());
assert_ok!(Voting::remove_vote(Origin::signed(1), Some(0), 1));
@@ -627,7 +627,7 @@ fn lock_amalgamation_valid_with_move_roundtrip_to_casting() {
assert_ok!(Voting::unlock(Origin::signed(1), 0, 1));
assert_eq!(Balances::usable_balance(1), 5);
Polls::set(vec![(0, Ongoing(Tally::default(), 0))].into_iter().collect());
Polls::set(vec![(0, Ongoing(Tally::new(0), 0))].into_iter().collect());
assert_ok!(Voting::vote(Origin::signed(1), 0, aye(10, 1)));
Polls::set(vec![(0, Completed(1, true))].into_iter().collect());
assert_ok!(Voting::remove_vote(Origin::signed(1), Some(0), 0));
@@ -688,9 +688,9 @@ fn lock_aggregation_over_different_classes_with_casting_works() {
new_test_ext().execute_with(|| {
Polls::set(
vec![
(0, Ongoing(Tally::default(), 0)),
(1, Ongoing(Tally::default(), 1)),
(2, Ongoing(Tally::default(), 2)),
(0, Ongoing(Tally::new(0), 0)),
(1, Ongoing(Tally::new(0), 1)),
(2, Ongoing(Tally::new(0), 2)),
]
.into_iter()
.collect(),
@@ -747,10 +747,10 @@ fn errors_with_vote_work() {
assert_ok!(Voting::undelegate(Origin::signed(1), 0));
Polls::set(
vec![
(0, Ongoing(Tally::default(), 0)),
(1, Ongoing(Tally::default(), 0)),
(2, Ongoing(Tally::default(), 0)),
(3, Ongoing(Tally::default(), 0)),
(0, Ongoing(Tally::new(0), 0)),
(1, Ongoing(Tally::new(0), 0)),
(2, Ongoing(Tally::new(0), 0)),
(3, Ongoing(Tally::new(0), 0)),
]
.into_iter()
.collect(),
+60 -55
View File
@@ -17,18 +17,16 @@
//! Miscellaneous additional datatypes.
use sp_std::marker::PhantomData;
use codec::{Codec, Decode, Encode, MaxEncodedLen};
use frame_support::{
traits::VoteTally, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound,
RuntimeDebugNoBound,
traits::VoteTally, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{Saturating, Zero},
RuntimeDebug,
};
use sp_std::{fmt::Debug, marker::PhantomData};
use super::*;
use crate::{AccountVote, Conviction, Vote};
@@ -36,7 +34,6 @@ use crate::{AccountVote, Conviction, Vote};
/// Info regarding an ongoing referendum.
#[derive(
CloneNoBound,
DefaultNoBound,
PartialEqNoBound,
EqNoBound,
RuntimeDebugNoBound,
@@ -46,84 +43,84 @@ use crate::{AccountVote, Conviction, Vote};
MaxEncodedLen,
)]
#[scale_info(skip_type_params(Total))]
pub struct Tally<
Votes: Clone + Default + PartialEq + Eq + sp_std::fmt::Debug + TypeInfo + Codec,
Total,
> {
pub struct Tally<Votes: Clone + PartialEq + Eq + Debug + TypeInfo + Codec, Total> {
/// The number of aye votes, expressed in terms of post-conviction lock-vote.
pub ayes: Votes,
/// The number of nay votes, expressed in terms of post-conviction lock-vote.
pub nays: Votes,
/// The amount of funds currently expressing its opinion. Pre-conviction.
pub turnout: Votes,
/// The basic number of aye votes, expressed pre-conviction.
pub support: Votes,
/// Dummy.
dummy: PhantomData<Total>,
}
impl<
Votes: Clone
+ Default
+ PartialEq
+ Eq
+ sp_std::fmt::Debug
+ Copy
+ AtLeast32BitUnsigned
+ TypeInfo
+ Codec,
Votes: Clone + Default + PartialEq + Eq + Debug + Copy + AtLeast32BitUnsigned + TypeInfo + Codec,
Total: Get<Votes>,
> VoteTally<Votes> for Tally<Votes, Total>
Class,
> VoteTally<Votes, Class> for Tally<Votes, Total>
{
fn ayes(&self) -> Votes {
fn new(_: Class) -> Self {
Self { ayes: Zero::zero(), nays: Zero::zero(), support: Zero::zero(), dummy: PhantomData }
}
fn ayes(&self, _: Class) -> Votes {
self.ayes
}
fn turnout(&self) -> Perbill {
Perbill::from_rational(self.turnout, Total::get())
fn support(&self, _: Class) -> Perbill {
Perbill::from_rational(self.support, Total::get())
}
fn approval(&self) -> Perbill {
fn approval(&self, _: Class) -> Perbill {
Perbill::from_rational(self.ayes, self.ayes.saturating_add(self.nays))
}
#[cfg(feature = "runtime-benchmarks")]
fn unanimity() -> Self {
Self { ayes: Total::get(), nays: Zero::zero(), turnout: Total::get(), dummy: PhantomData }
fn unanimity(_: Class) -> Self {
Self { ayes: Total::get(), nays: Zero::zero(), support: Total::get(), dummy: PhantomData }
}
#[cfg(feature = "runtime-benchmarks")]
fn from_requirements(turnout: Perbill, approval: Perbill) -> Self {
let turnout = turnout.mul_ceil(Total::get());
let ayes = approval.mul_ceil(turnout);
Self { ayes, nays: turnout - ayes, turnout, dummy: PhantomData }
fn rejection(_: Class) -> Self {
Self { ayes: Zero::zero(), nays: Total::get(), support: Total::get(), dummy: PhantomData }
}
#[cfg(feature = "runtime-benchmarks")]
fn from_requirements(support: Perbill, approval: Perbill, _: Class) -> Self {
let support = support.mul_ceil(Total::get());
let ayes = approval.mul_ceil(support);
Self { ayes, nays: support - ayes, support, dummy: PhantomData }
}
}
impl<
Votes: Clone
+ Default
+ PartialEq
+ Eq
+ sp_std::fmt::Debug
+ Copy
+ AtLeast32BitUnsigned
+ TypeInfo
+ Codec,
Votes: Clone + Default + PartialEq + Eq + Debug + Copy + AtLeast32BitUnsigned + TypeInfo + Codec,
Total: Get<Votes>,
> Tally<Votes, Total>
{
/// Create a new tally.
pub fn new(vote: Vote, balance: Votes) -> Self {
pub fn from_vote(vote: Vote, balance: Votes) -> Self {
let Delegations { votes, capital } = vote.conviction.votes(balance);
Self {
ayes: if vote.aye { votes } else { Zero::zero() },
nays: if vote.aye { Zero::zero() } else { votes },
turnout: capital,
support: capital,
dummy: PhantomData,
}
}
pub fn from_parts(ayes: Votes, nays: Votes, turnout: Votes) -> Self {
Self { ayes, nays, turnout, dummy: PhantomData }
pub fn from_parts(
ayes_with_conviction: Votes,
nays_with_conviction: Votes,
ayes: Votes,
) -> Self {
Self {
ayes: ayes_with_conviction,
nays: nays_with_conviction,
support: ayes,
dummy: PhantomData,
}
}
/// Add an account's vote into the tally.
@@ -131,16 +128,18 @@ impl<
match vote {
AccountVote::Standard { vote, balance } => {
let Delegations { votes, capital } = vote.conviction.votes(balance);
self.turnout = self.turnout.checked_add(&capital)?;
match vote.aye {
true => self.ayes = self.ayes.checked_add(&votes)?,
true => {
self.support = self.support.checked_add(&capital)?;
self.ayes = self.ayes.checked_add(&votes)?
},
false => self.nays = self.nays.checked_add(&votes)?,
}
},
AccountVote::Split { aye, nay } => {
let aye = Conviction::None.votes(aye);
let nay = Conviction::None.votes(nay);
self.turnout = self.turnout.checked_add(&aye.capital)?.checked_add(&nay.capital)?;
self.support = self.support.checked_add(&aye.capital)?;
self.ayes = self.ayes.checked_add(&aye.votes)?;
self.nays = self.nays.checked_add(&nay.votes)?;
},
@@ -153,16 +152,18 @@ impl<
match vote {
AccountVote::Standard { vote, balance } => {
let Delegations { votes, capital } = vote.conviction.votes(balance);
self.turnout = self.turnout.checked_sub(&capital)?;
match vote.aye {
true => self.ayes = self.ayes.checked_sub(&votes)?,
true => {
self.support = self.support.checked_sub(&capital)?;
self.ayes = self.ayes.checked_sub(&votes)?
},
false => self.nays = self.nays.checked_sub(&votes)?,
}
},
AccountVote::Split { aye, nay } => {
let aye = Conviction::None.votes(aye);
let nay = Conviction::None.votes(nay);
self.turnout = self.turnout.checked_sub(&aye.capital)?.checked_sub(&nay.capital)?;
self.support = self.support.checked_sub(&aye.capital)?;
self.ayes = self.ayes.checked_sub(&aye.votes)?;
self.nays = self.nays.checked_sub(&nay.votes)?;
},
@@ -172,18 +173,22 @@ impl<
/// Increment some amount of votes.
pub fn increase(&mut self, approve: bool, delegations: Delegations<Votes>) {
self.turnout = self.turnout.saturating_add(delegations.capital);
match approve {
true => self.ayes = self.ayes.saturating_add(delegations.votes),
true => {
self.support = self.support.saturating_add(delegations.capital);
self.ayes = self.ayes.saturating_add(delegations.votes);
},
false => self.nays = self.nays.saturating_add(delegations.votes),
}
}
/// Decrement some amount of votes.
pub fn reduce(&mut self, approve: bool, delegations: Delegations<Votes>) {
self.turnout = self.turnout.saturating_sub(delegations.capital);
match approve {
true => self.ayes = self.ayes.saturating_sub(delegations.votes),
true => {
self.support = self.support.saturating_sub(delegations.capital);
self.ayes = self.ayes.saturating_sub(delegations.votes);
},
false => self.nays = self.nays.saturating_sub(delegations.votes),
}
}
@@ -196,7 +201,7 @@ impl<
pub struct Delegations<Balance> {
/// The number of votes (this is post-conviction).
pub votes: Balance,
/// The amount of raw capital, used for the turnout.
/// The amount of raw capital, used for the support.
pub capital: Balance,
}