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, Debug,
27};
28
29/// A number of lock periods, plus a vote, one way or the other.
30#[derive(DecodeWithMemTracking, Copy, Clone, Eq, PartialEq, Default, Debug)]
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	Debug,
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			},
104			_ => None,
105		}
106	}
107
108	/// The total balance involved in this vote.
109	pub fn balance(self) -> Balance {
110		match self {
111			AccountVote::Standard { balance, .. } => balance,
112			AccountVote::Split { aye, nay } => aye.saturating_add(nay),
113		}
114	}
115
116	/// Returns `Some` with whether the vote is an aye vote if it is standard, otherwise `None` if
117	/// it is split.
118	pub fn as_standard(self) -> Option<bool> {
119		match self {
120			AccountVote::Standard { vote, .. } => Some(vote.aye),
121			_ => None,
122		}
123	}
124}
125
126/// A "prior" lock, i.e. a lock for some now-forgotten reason.
127#[derive(
128	Encode,
129	MaxEncodedLen,
130	Decode,
131	Default,
132	Copy,
133	Clone,
134	Eq,
135	PartialEq,
136	Ord,
137	PartialOrd,
138	Debug,
139	TypeInfo,
140)]
141pub struct PriorLock<BlockNumber, Balance>(BlockNumber, Balance);
142
143impl<BlockNumber: Ord + Copy + Zero, Balance: Ord + Copy + Zero> PriorLock<BlockNumber, Balance> {
144	/// Accumulates an additional lock.
145	pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) {
146		self.0 = self.0.max(until);
147		self.1 = self.1.max(amount);
148	}
149
150	pub fn locked(&self) -> Balance {
151		self.1
152	}
153
154	pub fn rejig(&mut self, now: BlockNumber) {
155		if now >= self.0 {
156			self.0 = Zero::zero();
157			self.1 = Zero::zero();
158		}
159	}
160}
161
162/// An indicator for what an account is doing; it can either be delegating or voting.
163#[derive(Clone, Encode, Decode, Eq, MaxEncodedLen, PartialEq, Debug, TypeInfo)]
164#[codec(mel_bound(skip_type_params(MaxVotes)))]
165#[scale_info(skip_type_params(MaxVotes))]
166pub enum Voting<Balance, AccountId, BlockNumber, MaxVotes: Get<u32>> {
167	/// The account is voting directly. `delegations` is the total amount of post-conviction voting
168	/// weight that it controls from those that have delegated to it.
169	Direct {
170		/// The current votes of the account.
171		votes: BoundedVec<(ReferendumIndex, AccountVote<Balance>), MaxVotes>,
172		/// The total amount of delegations that this account has received.
173		delegations: Delegations<Balance>,
174		/// Any pre-existing locks from past voting/delegating activity.
175		prior: PriorLock<BlockNumber, Balance>,
176	},
177	/// The account is delegating `balance` of its balance to a `target` account with `conviction`.
178	Delegating {
179		balance: Balance,
180		target: AccountId,
181		conviction: Conviction,
182		/// The total amount of delegations that this account has received.
183		delegations: Delegations<Balance>,
184		/// Any pre-existing locks from past voting/delegating activity.
185		prior: PriorLock<BlockNumber, Balance>,
186	},
187}
188
189impl<Balance: Default, AccountId, BlockNumber: Zero, MaxVotes: Get<u32>> Default
190	for Voting<Balance, AccountId, BlockNumber, MaxVotes>
191{
192	fn default() -> Self {
193		Voting::Direct {
194			votes: Default::default(),
195			delegations: Default::default(),
196			prior: PriorLock(Zero::zero(), Default::default()),
197		}
198	}
199}
200
201impl<
202		Balance: Saturating + Ord + Zero + Copy,
203		BlockNumber: Ord + Copy + Zero,
204		AccountId,
205		MaxVotes: Get<u32>,
206	> Voting<Balance, AccountId, BlockNumber, MaxVotes>
207{
208	pub fn rejig(&mut self, now: BlockNumber) {
209		match self {
210			Voting::Direct { prior, .. } => prior,
211			Voting::Delegating { prior, .. } => prior,
212		}
213		.rejig(now);
214	}
215
216	/// The amount of this account's balance that must currently be locked due to voting.
217	pub fn locked_balance(&self) -> Balance {
218		match self {
219			Voting::Direct { votes, prior, .. } => {
220				votes.iter().map(|i| i.1.balance()).fold(prior.locked(), |a, i| a.max(i))
221			},
222			Voting::Delegating { balance, prior, .. } => *balance.max(&prior.locked()),
223		}
224	}
225
226	pub fn set_common(
227		&mut self,
228		delegations: Delegations<Balance>,
229		prior: PriorLock<BlockNumber, Balance>,
230	) {
231		let (d, p) = match self {
232			Voting::Direct { ref mut delegations, ref mut prior, .. } => (delegations, prior),
233			Voting::Delegating { ref mut delegations, ref mut prior, .. } => (delegations, prior),
234		};
235		*d = delegations;
236		*p = prior;
237	}
238
239	pub fn prior(&self) -> &PriorLock<BlockNumber, Balance> {
240		match self {
241			Voting::Direct { prior, .. } => prior,
242			Voting::Delegating { prior, .. } => prior,
243		}
244	}
245}