referrerpolicy=no-referrer-when-downgrade

pallet_democracy/
vote.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//! The vote datatype.
19
20use crate::{Conviction, Delegations, ReferendumIndex};
21use codec::{Decode, DecodeWithMemTracking, Encode, EncodeLike, Input, MaxEncodedLen, Output};
22use frame_support::traits::Get;
23use scale_info::TypeInfo;
24use sp_runtime::{
25	traits::{Saturating, Zero},
26	BoundedVec, RuntimeDebug,
27};
28
29/// A number of lock periods, plus a vote, one way or the other.
30#[derive(DecodeWithMemTracking, Copy, Clone, Eq, PartialEq, Default, RuntimeDebug)]
31pub struct Vote {
32	pub aye: bool,
33	pub conviction: Conviction,
34}
35
36impl Encode for Vote {
37	fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
38		output.push_byte(u8::from(self.conviction) | if self.aye { 0b1000_0000 } else { 0 });
39	}
40}
41
42impl MaxEncodedLen for Vote {
43	fn max_encoded_len() -> usize {
44		1
45	}
46}
47
48impl EncodeLike for Vote {}
49
50impl Decode for Vote {
51	fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
52		let b = input.read_byte()?;
53		Ok(Vote {
54			aye: (b & 0b1000_0000) == 0b1000_0000,
55			conviction: Conviction::try_from(b & 0b0111_1111)
56				.map_err(|_| codec::Error::from("Invalid conviction"))?,
57		})
58	}
59}
60
61impl TypeInfo for Vote {
62	type Identity = Self;
63
64	fn type_info() -> scale_info::Type {
65		scale_info::Type::builder()
66			.path(scale_info::Path::new("Vote", module_path!()))
67			.composite(
68				scale_info::build::Fields::unnamed()
69					.field(|f| f.ty::<u8>().docs(&["Raw vote byte, encodes aye + conviction"])),
70			)
71	}
72}
73
74/// A vote for a referendum of a particular account.
75#[derive(
76	Encode,
77	DecodeWithMemTracking,
78	MaxEncodedLen,
79	Decode,
80	Copy,
81	Clone,
82	Eq,
83	PartialEq,
84	RuntimeDebug,
85	TypeInfo,
86)]
87pub enum AccountVote<Balance> {
88	/// A standard vote, one-way (approve or reject) with a given amount of conviction.
89	Standard { vote: Vote, balance: Balance },
90	/// A split vote with balances given for both ways, and with no conviction, useful for
91	/// parachains when voting.
92	Split { aye: Balance, nay: Balance },
93}
94
95impl<Balance: Saturating> AccountVote<Balance> {
96	/// Returns `Some` of the lock periods that the account is locked for, assuming that the
97	/// referendum passed iff `approved` is `true`.
98	pub fn locked_if(self, approved: bool) -> Option<(u32, Balance)> {
99		// winning side: can only be removed after the lock period ends.
100		match self {
101			AccountVote::Standard { vote, balance } if vote.aye == approved =>
102				Some((vote.conviction.lock_periods(), balance)),
103			_ => None,
104		}
105	}
106
107	/// The total balance involved in this vote.
108	pub fn balance(self) -> Balance {
109		match self {
110			AccountVote::Standard { balance, .. } => balance,
111			AccountVote::Split { aye, nay } => aye.saturating_add(nay),
112		}
113	}
114
115	/// Returns `Some` with whether the vote is an aye vote if it is standard, otherwise `None` if
116	/// it is split.
117	pub fn as_standard(self) -> Option<bool> {
118		match self {
119			AccountVote::Standard { vote, .. } => Some(vote.aye),
120			_ => None,
121		}
122	}
123}
124
125/// A "prior" lock, i.e. a lock for some now-forgotten reason.
126#[derive(
127	Encode,
128	MaxEncodedLen,
129	Decode,
130	Default,
131	Copy,
132	Clone,
133	Eq,
134	PartialEq,
135	Ord,
136	PartialOrd,
137	RuntimeDebug,
138	TypeInfo,
139)]
140pub struct PriorLock<BlockNumber, Balance>(BlockNumber, Balance);
141
142impl<BlockNumber: Ord + Copy + Zero, Balance: Ord + Copy + Zero> PriorLock<BlockNumber, Balance> {
143	/// Accumulates an additional lock.
144	pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) {
145		self.0 = self.0.max(until);
146		self.1 = self.1.max(amount);
147	}
148
149	pub fn locked(&self) -> Balance {
150		self.1
151	}
152
153	pub fn rejig(&mut self, now: BlockNumber) {
154		if now >= self.0 {
155			self.0 = Zero::zero();
156			self.1 = Zero::zero();
157		}
158	}
159}
160
161/// An indicator for what an account is doing; it can either be delegating or voting.
162#[derive(Clone, Encode, Decode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
163#[codec(mel_bound(skip_type_params(MaxVotes)))]
164#[scale_info(skip_type_params(MaxVotes))]
165pub enum Voting<Balance, AccountId, BlockNumber, MaxVotes: Get<u32>> {
166	/// The account is voting directly. `delegations` is the total amount of post-conviction voting
167	/// weight that it controls from those that have delegated to it.
168	Direct {
169		/// The current votes of the account.
170		votes: BoundedVec<(ReferendumIndex, AccountVote<Balance>), MaxVotes>,
171		/// The total amount of delegations that this account has received.
172		delegations: Delegations<Balance>,
173		/// Any pre-existing locks from past voting/delegating activity.
174		prior: PriorLock<BlockNumber, Balance>,
175	},
176	/// The account is delegating `balance` of its balance to a `target` account with `conviction`.
177	Delegating {
178		balance: Balance,
179		target: AccountId,
180		conviction: Conviction,
181		/// The total amount of delegations that this account has received.
182		delegations: Delegations<Balance>,
183		/// Any pre-existing locks from past voting/delegating activity.
184		prior: PriorLock<BlockNumber, Balance>,
185	},
186}
187
188impl<Balance: Default, AccountId, BlockNumber: Zero, MaxVotes: Get<u32>> Default
189	for Voting<Balance, AccountId, BlockNumber, MaxVotes>
190{
191	fn default() -> Self {
192		Voting::Direct {
193			votes: Default::default(),
194			delegations: Default::default(),
195			prior: PriorLock(Zero::zero(), Default::default()),
196		}
197	}
198}
199
200impl<
201		Balance: Saturating + Ord + Zero + Copy,
202		BlockNumber: Ord + Copy + Zero,
203		AccountId,
204		MaxVotes: Get<u32>,
205	> Voting<Balance, AccountId, BlockNumber, MaxVotes>
206{
207	pub fn rejig(&mut self, now: BlockNumber) {
208		match self {
209			Voting::Direct { prior, .. } => prior,
210			Voting::Delegating { prior, .. } => prior,
211		}
212		.rejig(now);
213	}
214
215	/// The amount of this account's balance that must currently be locked due to voting.
216	pub fn locked_balance(&self) -> Balance {
217		match self {
218			Voting::Direct { votes, prior, .. } =>
219				votes.iter().map(|i| i.1.balance()).fold(prior.locked(), |a, i| a.max(i)),
220			Voting::Delegating { balance, prior, .. } => *balance.max(&prior.locked()),
221		}
222	}
223
224	pub fn set_common(
225		&mut self,
226		delegations: Delegations<Balance>,
227		prior: PriorLock<BlockNumber, Balance>,
228	) {
229		let (d, p) = match self {
230			Voting::Direct { ref mut delegations, ref mut prior, .. } => (delegations, prior),
231			Voting::Delegating { ref mut delegations, ref mut prior, .. } => (delegations, prior),
232		};
233		*d = delegations;
234		*p = prior;
235	}
236
237	pub fn prior(&self) -> &PriorLock<BlockNumber, Balance> {
238		match self {
239			Voting::Direct { prior, .. } => prior,
240			Voting::Delegating { prior, .. } => prior,
241		}
242	}
243}