referrerpolicy=no-referrer-when-downgrade

pallet_conviction_voting/
types.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Miscellaneous additional datatypes.
19
20use codec::{Codec, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
21use core::{fmt::Debug, marker::PhantomData};
22use frame_support::{traits::VoteTally, CloneNoBound, DebugNoBound, EqNoBound, PartialEqNoBound};
23use scale_info::TypeInfo;
24use sp_runtime::traits::{Saturating, Zero};
25
26use super::*;
27use crate::{AccountVote, Conviction, Vote};
28
29/// Info regarding an ongoing referendum.
30#[derive(
31	CloneNoBound,
32	PartialEqNoBound,
33	EqNoBound,
34	DebugNoBound,
35	TypeInfo,
36	Encode,
37	Decode,
38	DecodeWithMemTracking,
39	MaxEncodedLen,
40)]
41#[scale_info(skip_type_params(Total))]
42#[codec(mel_bound(Votes: MaxEncodedLen))]
43pub struct Tally<Votes: Clone + PartialEq + Eq + Debug + TypeInfo + Codec, Total> {
44	/// The number of aye votes, expressed in terms of post-conviction lock-vote.
45	pub ayes: Votes,
46	/// The number of nay votes, expressed in terms of post-conviction lock-vote.
47	pub nays: Votes,
48	/// The basic number of aye votes, expressed pre-conviction.
49	pub support: Votes,
50	/// Dummy.
51	dummy: PhantomData<Total>,
52}
53
54impl<
55		Votes: Clone + Default + PartialEq + Eq + Debug + Copy + AtLeast32BitUnsigned + TypeInfo + Codec,
56		Total: Get<Votes>,
57		Class,
58	> VoteTally<Votes, Class> for Tally<Votes, Total>
59{
60	fn new(_: Class) -> Self {
61		Self { ayes: Zero::zero(), nays: Zero::zero(), support: Zero::zero(), dummy: PhantomData }
62	}
63
64	fn ayes(&self, _: Class) -> Votes {
65		self.ayes
66	}
67
68	fn support(&self, _: Class) -> Perbill {
69		Perbill::from_rational(self.support, Total::get())
70	}
71
72	fn approval(&self, _: Class) -> Perbill {
73		Perbill::from_rational(self.ayes, self.ayes.saturating_add(self.nays))
74	}
75
76	#[cfg(feature = "runtime-benchmarks")]
77	fn unanimity(_: Class) -> Self {
78		Self { ayes: Total::get(), nays: Zero::zero(), support: Total::get(), dummy: PhantomData }
79	}
80
81	#[cfg(feature = "runtime-benchmarks")]
82	fn rejection(_: Class) -> Self {
83		Self { ayes: Zero::zero(), nays: Total::get(), support: Total::get(), dummy: PhantomData }
84	}
85
86	#[cfg(feature = "runtime-benchmarks")]
87	fn from_requirements(support: Perbill, approval: Perbill, _: Class) -> Self {
88		let support = support.mul_ceil(Total::get());
89		let ayes = approval.mul_ceil(support);
90		Self { ayes, nays: support - ayes, support, dummy: PhantomData }
91	}
92
93	#[cfg(feature = "runtime-benchmarks")]
94	fn setup(_: Class, _: Perbill) {}
95}
96
97impl<
98		Votes: Clone + Default + PartialEq + Eq + Debug + Copy + AtLeast32BitUnsigned + TypeInfo + Codec,
99		Total: Get<Votes>,
100	> Tally<Votes, Total>
101{
102	/// Create a new tally.
103	pub fn from_vote(vote: Vote, balance: Votes) -> Self {
104		let Delegations { votes, capital } = vote.conviction.votes(balance);
105		Self {
106			ayes: if vote.aye { votes } else { Zero::zero() },
107			nays: if vote.aye { Zero::zero() } else { votes },
108			support: capital,
109			dummy: PhantomData,
110		}
111	}
112
113	pub fn from_parts(
114		ayes_with_conviction: Votes,
115		nays_with_conviction: Votes,
116		support: Votes,
117	) -> Self {
118		Self { ayes: ayes_with_conviction, nays: nays_with_conviction, support, dummy: PhantomData }
119	}
120
121	/// Add an account's vote into the tally.
122	pub fn add(&mut self, vote: AccountVote<Votes>) -> Option<()> {
123		match vote {
124			AccountVote::Standard { vote, balance } => {
125				let Delegations { votes, capital } = vote.conviction.votes(balance);
126				match vote.aye {
127					true => {
128						self.support = self.support.checked_add(&capital)?;
129						self.ayes = self.ayes.checked_add(&votes)?
130					},
131					false => self.nays = self.nays.checked_add(&votes)?,
132				}
133			},
134			AccountVote::Split { aye, nay } => {
135				let aye = Conviction::None.votes(aye);
136				let nay = Conviction::None.votes(nay);
137				self.support = self.support.checked_add(&aye.capital)?;
138				self.ayes = self.ayes.checked_add(&aye.votes)?;
139				self.nays = self.nays.checked_add(&nay.votes)?;
140			},
141			AccountVote::SplitAbstain { aye, nay, abstain } => {
142				let aye = Conviction::None.votes(aye);
143				let nay = Conviction::None.votes(nay);
144				let abstain = Conviction::None.votes(abstain);
145				self.support =
146					self.support.checked_add(&aye.capital)?.checked_add(&abstain.capital)?;
147				self.ayes = self.ayes.checked_add(&aye.votes)?;
148				self.nays = self.nays.checked_add(&nay.votes)?;
149			},
150		}
151		Some(())
152	}
153
154	/// Remove an account's vote from the tally.
155	pub fn remove(&mut self, vote: AccountVote<Votes>) -> Option<()> {
156		match vote {
157			AccountVote::Standard { vote, balance } => {
158				let Delegations { votes, capital } = vote.conviction.votes(balance);
159				match vote.aye {
160					true => {
161						self.support = self.support.checked_sub(&capital)?;
162						self.ayes = self.ayes.checked_sub(&votes)?
163					},
164					false => self.nays = self.nays.checked_sub(&votes)?,
165				}
166			},
167			AccountVote::Split { aye, nay } => {
168				let aye = Conviction::None.votes(aye);
169				let nay = Conviction::None.votes(nay);
170				self.support = self.support.checked_sub(&aye.capital)?;
171				self.ayes = self.ayes.checked_sub(&aye.votes)?;
172				self.nays = self.nays.checked_sub(&nay.votes)?;
173			},
174			AccountVote::SplitAbstain { aye, nay, abstain } => {
175				let aye = Conviction::None.votes(aye);
176				let nay = Conviction::None.votes(nay);
177				let abstain = Conviction::None.votes(abstain);
178				self.support =
179					self.support.checked_sub(&aye.capital)?.checked_sub(&abstain.capital)?;
180				self.ayes = self.ayes.checked_sub(&aye.votes)?;
181				self.nays = self.nays.checked_sub(&nay.votes)?;
182			},
183		}
184		Some(())
185	}
186
187	/// Increment some amount of votes.
188	pub fn increase(&mut self, approve: bool, delegations: Delegations<Votes>) {
189		match approve {
190			true => {
191				self.support = self.support.saturating_add(delegations.capital);
192				self.ayes = self.ayes.saturating_add(delegations.votes);
193			},
194			false => self.nays = self.nays.saturating_add(delegations.votes),
195		}
196	}
197
198	/// Decrement some amount of votes.
199	pub fn reduce(&mut self, approve: bool, delegations: Delegations<Votes>) {
200		match approve {
201			true => {
202				self.support = self.support.saturating_sub(delegations.capital);
203				self.ayes = self.ayes.saturating_sub(delegations.votes);
204			},
205			false => self.nays = self.nays.saturating_sub(delegations.votes),
206		}
207	}
208}
209
210/// Amount of votes and capital placed in delegation for an account.
211#[derive(
212	Encode,
213	Decode,
214	DecodeWithMemTracking,
215	Default,
216	Copy,
217	Clone,
218	PartialEq,
219	Eq,
220	Debug,
221	TypeInfo,
222	MaxEncodedLen,
223)]
224pub struct Delegations<Balance> {
225	/// The number of votes (this is post-conviction).
226	pub votes: Balance,
227	/// The amount of raw capital, used for the support.
228	pub capital: Balance,
229}
230
231impl<Balance: Saturating> Saturating for Delegations<Balance> {
232	fn saturating_add(self, o: Self) -> Self {
233		Self {
234			votes: self.votes.saturating_add(o.votes),
235			capital: self.capital.saturating_add(o.capital),
236		}
237	}
238
239	fn saturating_sub(self, o: Self) -> Self {
240		Self {
241			votes: self.votes.saturating_sub(o.votes),
242			capital: self.capital.saturating_sub(o.capital),
243		}
244	}
245
246	fn saturating_mul(self, o: Self) -> Self {
247		Self {
248			votes: self.votes.saturating_mul(o.votes),
249			capital: self.capital.saturating_mul(o.capital),
250		}
251	}
252
253	fn saturating_pow(self, exp: usize) -> Self {
254		Self { votes: self.votes.saturating_pow(exp), capital: self.capital.saturating_pow(exp) }
255	}
256}
257
258/// Whether an `unvote` operation is able to make actions that are not strictly always in the
259/// interest of an account.
260pub enum UnvoteScope {
261	/// Permitted to do everything.
262	Any,
263	/// Permitted to do only the changes that do not need the owner's permission.
264	OnlyExpired,
265}