1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
// This file is part of Substrate.
// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Traits and associated data structures concerned with voting, and moving between tokens and
//! votes.
use crate::dispatch::{DispatchError, Parameter};
use codec::{HasCompact, MaxEncodedLen};
use sp_arithmetic::{
traits::{SaturatedConversion, UniqueSaturatedFrom, UniqueSaturatedInto},
Perbill,
};
use sp_runtime::traits::Member;
use sp_std::prelude::*;
/// A trait similar to `Convert` to convert values from `B` an abstract balance type
/// into u64 and back from u128. (This conversion is used in election and other places where complex
/// calculation over balance type is needed)
///
/// Total issuance of the currency is passed in, but an implementation of this trait may or may not
/// use it.
///
/// # WARNING
///
/// the total issuance being passed in implies that the implementation must be aware of the fact
/// that its values can affect the outcome. This implies that if the vote value is dependent on the
/// total issuance, it should never ber written to storage for later re-use.
pub trait CurrencyToVote<B> {
/// Convert balance to u64.
fn to_vote(value: B, issuance: B) -> u64;
/// Convert u128 to balance.
fn to_currency(value: u128, issuance: B) -> B;
}
/// An implementation of `CurrencyToVote` tailored for chain's that have a balance type of u128.
///
/// The factor is the `(total_issuance / u64::MAX).max(1)`, represented as u64. Let's look at the
/// important cases:
///
/// If the chain's total issuance is less than u64::MAX, this will always be 1, which means that
/// the factor will not have any effect. In this case, any account's balance is also less. Thus,
/// both of the conversions are basically an `as`; Any balance can fit in u64.
///
/// If the chain's total issuance is more than 2*u64::MAX, then a factor might be multiplied and
/// divided upon conversion.
pub struct U128CurrencyToVote;
impl U128CurrencyToVote {
fn factor(issuance: u128) -> u128 {
(issuance / u64::MAX as u128).max(1)
}
}
impl CurrencyToVote<u128> for U128CurrencyToVote {
fn to_vote(value: u128, issuance: u128) -> u64 {
(value / Self::factor(issuance)).saturated_into()
}
fn to_currency(value: u128, issuance: u128) -> u128 {
value.saturating_mul(Self::factor(issuance))
}
}
/// A naive implementation of `CurrencyConvert` that simply saturates all conversions.
///
/// # Warning
///
/// This is designed to be used mostly for testing. Use with care, and think about the consequences.
pub struct SaturatingCurrencyToVote;
impl<B: UniqueSaturatedInto<u64> + UniqueSaturatedFrom<u128>> CurrencyToVote<B>
for SaturatingCurrencyToVote
{
fn to_vote(value: B, _: B) -> u64 {
value.unique_saturated_into()
}
fn to_currency(value: u128, _: B) -> B {
B::unique_saturated_from(value)
}
}
pub trait VoteTally<Votes, Class> {
fn new(_: Class) -> Self;
fn ayes(&self, class: Class) -> Votes;
fn support(&self, class: Class) -> Perbill;
fn approval(&self, class: Class) -> Perbill;
#[cfg(feature = "runtime-benchmarks")]
fn unanimity(class: Class) -> Self;
#[cfg(feature = "runtime-benchmarks")]
fn rejection(class: Class) -> Self;
#[cfg(feature = "runtime-benchmarks")]
fn from_requirements(support: Perbill, approval: Perbill, class: Class) -> Self;
#[cfg(feature = "runtime-benchmarks")]
/// A function that should be called before any use of the `runtime-benchmarks` gated functions
/// of the `VoteTally` trait.
///
/// Should be used to set up any needed state in a Pallet which implements `VoteTally` so that
/// benchmarks that execute will complete successfully. `class` can be used to set up a
/// particular class of voters, and `granularity` is used to determine the weight of one vote
/// relative to total unanimity.
///
/// For example, in the case where there are a number of unique voters, and each voter has equal
/// voting weight, a granularity of `Perbill::from_rational(1, 1000)` should create `1_000`
/// users.
fn setup(class: Class, granularity: Perbill);
}
pub enum PollStatus<Tally, Moment, Class> {
None,
Ongoing(Tally, Class),
Completed(Moment, bool),
}
impl<Tally, Moment, Class> PollStatus<Tally, Moment, Class> {
pub fn ensure_ongoing(self) -> Option<(Tally, Class)> {
match self {
Self::Ongoing(t, c) => Some((t, c)),
_ => None,
}
}
}
pub struct ClassCountOf<P, T>(sp_std::marker::PhantomData<(P, T)>);
impl<T, P: Polling<T>> sp_runtime::traits::Get<u32> for ClassCountOf<P, T> {
fn get() -> u32 {
P::classes().len() as u32
}
}
pub trait Polling<Tally> {
type Index: Parameter + Member + Ord + PartialOrd + Copy + HasCompact + MaxEncodedLen;
type Votes: Parameter + Member + Ord + PartialOrd + Copy + HasCompact + MaxEncodedLen;
type Class: Parameter + Member + Ord + PartialOrd + MaxEncodedLen;
type Moment;
/// Provides a vec of values that `T` may take.
fn classes() -> Vec<Self::Class>;
/// `Some` if the referendum `index` can be voted on, along with the tally and class of
/// referendum.
///
/// Don't use this if you might mutate - use `try_access_poll` instead.
fn as_ongoing(index: Self::Index) -> Option<(Tally, Self::Class)>;
fn access_poll<R>(
index: Self::Index,
f: impl FnOnce(PollStatus<&mut Tally, Self::Moment, Self::Class>) -> R,
) -> R;
fn try_access_poll<R>(
index: Self::Index,
f: impl FnOnce(PollStatus<&mut Tally, Self::Moment, Self::Class>) -> Result<R, DispatchError>,
) -> Result<R, DispatchError>;
/// Create an ongoing majority-carries poll of given class lasting given period for the purpose
/// of benchmarking.
///
/// May return `Err` if it is impossible.
#[cfg(feature = "runtime-benchmarks")]
fn create_ongoing(class: Self::Class) -> Result<Self::Index, ()>;
/// End the given ongoing poll and return the result.
///
/// Returns `Err` if `index` is not an ongoing poll.
#[cfg(feature = "runtime-benchmarks")]
fn end_ongoing(index: Self::Index, approved: bool) -> Result<(), ()>;
/// The maximum amount of ongoing polls within any single class. By default it practically
/// unlimited (`u32::max_value()`).
#[cfg(feature = "runtime-benchmarks")]
fn max_ongoing() -> (Self::Class, u32) {
(Self::classes().into_iter().next().expect("Always one class"), u32::max_value())
}
}