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