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		let total = self.ayes.saturating_add(self.nays);
74		if total.is_zero() {
75			Perbill::zero()
76		} else {
77			Perbill::from_rational(self.ayes, total)
78		}
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	///
128	/// Returns `None` and leaves `self` unchanged if any field overflows.
129	pub fn add(&mut self, vote: AccountVote<Votes>) -> Option<()> {
130		let (mut ayes, mut nays, mut support) = (self.ayes, self.nays, self.support);
131		match vote {
132			AccountVote::Standard { vote, balance } => {
133				let Delegations { votes, capital } = vote.conviction.votes(balance);
134				match vote.aye {
135					true => {
136						support = support.checked_add(&capital)?;
137						ayes = ayes.checked_add(&votes)?
138					},
139					false => nays = nays.checked_add(&votes)?,
140				}
141			},
142			AccountVote::Split { aye, nay } => {
143				let aye = Conviction::None.votes(aye);
144				let nay = Conviction::None.votes(nay);
145				support = support.checked_add(&aye.capital)?;
146				ayes = ayes.checked_add(&aye.votes)?;
147				nays = nays.checked_add(&nay.votes)?;
148			},
149			AccountVote::SplitAbstain { aye, nay, abstain } => {
150				let aye = Conviction::None.votes(aye);
151				let nay = Conviction::None.votes(nay);
152				let abstain = Conviction::None.votes(abstain);
153				support = support.checked_add(&aye.capital)?.checked_add(&abstain.capital)?;
154				ayes = ayes.checked_add(&aye.votes)?;
155				nays = nays.checked_add(&nay.votes)?;
156			},
157		}
158		// Apply changes only after all calculations succeed.
159		*self = Self::from_parts(ayes, nays, support);
160		Some(())
161	}
162
163	/// Remove an account's vote from the tally.
164	///
165	/// Returns `None` and leaves `self` unchanged if any field underflows.
166	pub fn remove(&mut self, vote: AccountVote<Votes>) -> Option<()> {
167		let (mut ayes, mut nays, mut support) = (self.ayes, self.nays, self.support);
168		match vote {
169			AccountVote::Standard { vote, balance } => {
170				let Delegations { votes, capital } = vote.conviction.votes(balance);
171				match vote.aye {
172					true => {
173						support = support.checked_sub(&capital)?;
174						ayes = ayes.checked_sub(&votes)?
175					},
176					false => nays = nays.checked_sub(&votes)?,
177				}
178			},
179			AccountVote::Split { aye, nay } => {
180				let aye = Conviction::None.votes(aye);
181				let nay = Conviction::None.votes(nay);
182				support = support.checked_sub(&aye.capital)?;
183				ayes = ayes.checked_sub(&aye.votes)?;
184				nays = nays.checked_sub(&nay.votes)?;
185			},
186			AccountVote::SplitAbstain { aye, nay, abstain } => {
187				let aye = Conviction::None.votes(aye);
188				let nay = Conviction::None.votes(nay);
189				let abstain = Conviction::None.votes(abstain);
190				support = support.checked_sub(&aye.capital)?.checked_sub(&abstain.capital)?;
191				ayes = ayes.checked_sub(&aye.votes)?;
192				nays = nays.checked_sub(&nay.votes)?;
193			},
194		}
195		// Apply changes only after all calculations succeed.
196		*self = Self::from_parts(ayes, nays, support);
197		Some(())
198	}
199
200	/// Increment some amount of votes.
201	pub fn increase(&mut self, approve: bool, delegations: Delegations<Votes>) {
202		match approve {
203			true => {
204				self.support = self.support.saturating_add(delegations.capital);
205				self.ayes = self.ayes.saturating_add(delegations.votes);
206			},
207			false => self.nays = self.nays.saturating_add(delegations.votes),
208		}
209	}
210
211	/// Decrement some amount of votes.
212	pub fn reduce(&mut self, approve: bool, delegations: Delegations<Votes>) {
213		match approve {
214			true => {
215				self.support = self.support.saturating_sub(delegations.capital);
216				self.ayes = self.ayes.saturating_sub(delegations.votes);
217			},
218			false => self.nays = self.nays.saturating_sub(delegations.votes),
219		}
220	}
221}
222
223/// Amount of votes and capital placed in delegation for an account.
224#[derive(
225	Encode,
226	Decode,
227	DecodeWithMemTracking,
228	Default,
229	Copy,
230	Clone,
231	PartialEq,
232	Eq,
233	Debug,
234	TypeInfo,
235	MaxEncodedLen,
236)]
237pub struct Delegations<Balance> {
238	/// The number of votes (this is post-conviction).
239	pub votes: Balance,
240	/// The amount of raw capital, used for the support.
241	pub capital: Balance,
242}
243
244impl<Balance: Saturating> Saturating for Delegations<Balance> {
245	fn saturating_add(self, o: Self) -> Self {
246		Self {
247			votes: self.votes.saturating_add(o.votes),
248			capital: self.capital.saturating_add(o.capital),
249		}
250	}
251
252	fn saturating_sub(self, o: Self) -> Self {
253		Self {
254			votes: self.votes.saturating_sub(o.votes),
255			capital: self.capital.saturating_sub(o.capital),
256		}
257	}
258
259	fn saturating_mul(self, o: Self) -> Self {
260		Self {
261			votes: self.votes.saturating_mul(o.votes),
262			capital: self.capital.saturating_mul(o.capital),
263		}
264	}
265
266	fn saturating_pow(self, exp: usize) -> Self {
267		Self { votes: self.votes.saturating_pow(exp), capital: self.capital.saturating_pow(exp) }
268	}
269}
270
271/// Whether an `unvote` operation is able to make actions that are not strictly always in the
272/// interest of an account.
273pub enum UnvoteScope {
274	/// Permitted to do everything.
275	Any,
276	/// Permitted to do only the changes that do not need the owner's permission.
277	OnlyExpired,
278}