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
+31 -17
View File
@@ -41,7 +41,7 @@
//! In order to become concluded, one of three things must happen:
//! - The referendum should remain in an unbroken _Passing_ state for a period of time. This
//! is known as the _Confirmation Period_ and is determined by the track. A referendum is considered
//! _Passing_ when there is a sufficiently high turnout and approval, given the amount of time it
//! _Passing_ when there is a sufficiently high support and approval, given the amount of time it
//! has been being decided. Generally the threshold for what counts as being "sufficiently high"
//! will reduce over time. The curves setting these thresholds are determined by the track. In this
//! case, the referendum is considered _Approved_ and the proposal is scheduled for dispatch.
@@ -54,6 +54,10 @@
//!
//! Once a referendum is concluded, the decision deposit may be refunded.
//!
//! ## Terms
//! - *Support*: The number of aye-votes, pre-conviction, as a proportion of the total number of
//! pre-conviction votes able to be cast in the population.
//!
//! - [`Config`]
//! - [`Call`]
@@ -148,7 +152,12 @@ pub mod pallet {
/// The counting type for votes. Usually just balance.
type Votes: AtLeast32BitUnsigned + Copy + Parameter + Member;
/// The tallying type.
type Tally: VoteTally<Self::Votes> + Default + Clone + Codec + Eq + Debug + TypeInfo;
type Tally: VoteTally<Self::Votes, TrackIdOf<Self, I>>
+ Clone
+ Codec
+ Eq
+ Debug
+ TypeInfo;
// Constants
/// The minimum amount to be used as a deposit for a public referendum proposal.
@@ -369,7 +378,7 @@ pub mod pallet {
submission_deposit,
decision_deposit: None,
deciding: None,
tally: Default::default(),
tally: TallyOf::<T, I>::new(track),
in_queue: false,
alarm: Self::set_alarm(nudge_call, now.saturating_add(T::UndecidingTimeout::get())),
};
@@ -613,7 +622,7 @@ impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> {
submission_deposit: Deposit { who: dummy_account_id, amount: Zero::zero() },
decision_deposit: None,
deciding: None,
tally: Default::default(),
tally: TallyOf::<T, I>::new(class),
in_queue: false,
alarm: None,
};
@@ -723,8 +732,9 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
&status.tally,
Zero::zero(),
track.decision_period,
&track.min_turnout,
&track.min_support,
&track.min_approval,
status.track,
);
status.in_queue = false;
Self::deposit_event(Event::<T, I>::DecisionStarted {
@@ -740,7 +750,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
None
};
let deciding_status = DecidingStatus { since: now, confirming };
let alarm = Self::decision_time(&deciding_status, &status.tally, track);
let alarm = Self::decision_time(&deciding_status, &status.tally, status.track, track);
status.deciding = Some(deciding_status);
let branch =
if is_passing { BeginDecidingBranch::Passing } else { BeginDecidingBranch::Failing };
@@ -765,7 +775,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
(r.0, r.1.into())
} else {
// Add to queue.
let item = (index, status.tally.ayes());
let item = (index, status.tally.ayes(status.track));
status.in_queue = true;
TrackQueue::<T, I>::mutate(status.track, |q| q.insert_sorted_by_key(item, |x| x.1));
(None, ServiceBranch::Queued)
@@ -872,7 +882,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
// Are we already queued for deciding?
if status.in_queue {
// Does our position in the queue need updating?
let ayes = status.tally.ayes();
let ayes = status.tally.ayes(status.track);
let mut queue = TrackQueue::<T, I>::get(status.track);
let maybe_old_pos = queue.iter().position(|(x, _)| *x == index);
let new_pos = queue.binary_search_by_key(&ayes, |x| x.1).unwrap_or_else(|x| x);
@@ -930,8 +940,9 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
&status.tally,
now.saturating_sub(deciding.since),
track.decision_period,
&track.min_turnout,
&track.min_support,
&track.min_approval,
status.track,
);
branch = if is_passing {
match deciding.confirming {
@@ -996,7 +1007,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
ServiceBranch::ContinueNotConfirming
}
};
alarm = Self::decision_time(deciding, &status.tally, track);
alarm = Self::decision_time(deciding, &status.tally, status.track, track);
},
}
@@ -1009,15 +1020,16 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
fn decision_time(
deciding: &DecidingStatusOf<T>,
tally: &T::Tally,
track_id: TrackIdOf<T, I>,
track: &TrackInfoOf<T, I>,
) -> T::BlockNumber {
deciding.confirming.unwrap_or_else(|| {
// Set alarm to the point where the current voting would make it pass.
let approval = tally.approval();
let turnout = tally.turnout();
let approval = tally.approval(track_id);
let support = tally.support(track_id);
let until_approval = track.min_approval.delay(approval);
let until_turnout = track.min_turnout.delay(turnout);
let offset = until_turnout.max(until_approval);
let until_support = track.min_support.delay(support);
let offset = until_support.max(until_approval);
deciding.since.saturating_add(offset * track.decision_period)
})
}
@@ -1062,16 +1074,18 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
}
/// Determine whether the given `tally` would result in a referendum passing at `elapsed` blocks
/// into a total decision `period`, given the two curves for `turnout_needed` and
/// into a total decision `period`, given the two curves for `support_needed` and
/// `approval_needed`.
fn is_passing(
tally: &T::Tally,
elapsed: T::BlockNumber,
period: T::BlockNumber,
turnout_needed: &Curve,
support_needed: &Curve,
approval_needed: &Curve,
id: TrackIdOf<T, I>,
) -> bool {
let x = Perbill::from_rational(elapsed.min(period), period);
turnout_needed.passing(x, tally.turnout()) && approval_needed.passing(x, tally.approval())
support_needed.passing(x, tally.support(id)) &&
approval_needed.passing(x, tally.approval(id))
}
}