referrerpolicy=no-referrer-when-downgrade

pallet_recovery/
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//! Generic types that can be moved to frame-support once stable.
19
20use super::*;
21use core::{
22	marker::PhantomData,
23	ops::{Div, Rem},
24};
25
26/// Bitfield helper for tracking friend votes.
27///
28/// Uses a vector of u16 values where each bit represents whether a friend at that index has voted.
29#[derive(
30	CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, MaxEncodedLen,
31)]
32#[scale_info(skip_type_params(MaxEntries))]
33pub struct Bitfield<MaxEntries: Get<u32>>(pub BoundedVec<u16, BitfieldLenOf<MaxEntries>>);
34
35/// Calculates the length of the bitfield in u16 words.
36pub type BitfieldLenOf<MaxEntries> = ConstDivCeil<MaxEntries, ConstU32<16>, u32, u32>;
37
38/// Calculate division of two `Get` types and round the result up.
39pub struct ConstDivCeil<Dividend, Divisor, R, T>(pub PhantomData<(Dividend, Divisor, R, T)>);
40
41impl<Dividend: Get<T>, Divisor: Get<T>, R: AtLeast32BitUnsigned, T: Into<R>> Get<R>
42	for ConstDivCeil<Dividend, Divisor, R, T>
43where
44	R: Div + Rem + Zero + One + Copy,
45{
46	fn get() -> R {
47		let dividend: R = Dividend::get().into();
48		let divisor: R = Divisor::get().into();
49
50		let v = dividend / divisor;
51		let remainder = dividend % divisor;
52
53		if remainder.is_zero() {
54			v
55		} else {
56			v + One::one()
57		}
58	}
59}
60
61impl<MaxEntries: Get<u32>> Default for Bitfield<MaxEntries> {
62	fn default() -> Self {
63		Self(
64			vec![0u16; BitfieldLenOf::<MaxEntries>::get() as usize]
65				.try_into()
66				.expect("Bitfield construction checked in integrity test; qed."),
67		)
68	}
69}
70
71impl<MaxEntries: Get<u32>> Bitfield<MaxEntries> {
72	/// Set the bit at the given index to true (friend has voted).
73	pub fn set_if_not_set(&mut self, index: usize) -> Result<(), ()> {
74		let word_index = index / 16;
75		let bit_index = index % 16;
76
77		let word = self.0.get_mut(word_index).ok_or(())?;
78		if (*word & (1u16 << bit_index)) == 0 {
79			*word |= 1u16 << bit_index;
80			Ok(())
81		} else {
82			Err(())
83		}
84	}
85
86	/// A new bitfield with the given bit indices set.
87	///
88	/// Errors if there are duplicate or our of bounds indices.
89	pub fn with_bits(mut self, indices: impl IntoIterator<Item = usize>) -> Result<Self, ()> {
90		for index in indices {
91			self.set_if_not_set(index)?;
92		}
93		Ok(self)
94	}
95
96	/// Count the total number of set bits (total votes).
97	pub fn count_ones(&self) -> u32 {
98		self.0.iter().cloned().map(u16::count_ones).sum()
99	}
100}
101
102/// A `Consideration`-like type that tracks who paid for it.
103///
104/// This is useful in situations where the consideration may be moved around between accounts.
105/// Normally, a consideration is just enacted and then later dropped. But if it must be moved
106/// between accounts, then tracking this manually is necessary. Hence this type to not blow up the
107/// storage type definitions.
108#[derive(
109	Clone,
110	Eq,
111	PartialEq,
112	Encode,
113	Decode,
114	Default,
115	Debug,
116	TypeInfo,
117	MaxEncodedLen,
118	DecodeWithMemTracking,
119)]
120pub struct IdentifiedConsideration<AccountId, Footprint, C> {
121	/// Account that paid the storage deposit.
122	///
123	/// This is also the account that will receive the refund.
124	pub depositor: AccountId,
125
126	/// Opaque ticket to track the payment of a deposit.
127	pub ticket: Option<C>,
128
129	#[doc(hidden)]
130	pub _phantom: PhantomData<Footprint>,
131}
132
133impl<AccountId: Clone + Eq, Footprint, C: Consideration<AccountId, Footprint>>
134	IdentifiedConsideration<AccountId, Footprint, C>
135{
136	/// Try to take a deposit from `depositor` for the given footprint.
137	pub fn new(
138		depositor: &AccountId,
139		fp: impl Into<Option<Footprint>>,
140	) -> Result<Self, DispatchError> {
141		let ticket = if let Some(fp) = fp.into() {
142			Some(Consideration::<AccountId, Footprint>::new(depositor, fp)?)
143		} else {
144			None
145		};
146
147		Ok(Self { depositor: depositor.clone(), ticket, _phantom: Default::default() })
148	}
149
150	/// Update either the depositor or the footprint of the consideration.
151	pub fn update(
152		self,
153		new_depositor: &AccountId,
154		new_fp: impl Into<Option<Footprint>>,
155	) -> Result<Self, DispatchError> {
156		let fp = new_fp.into();
157		if let Some(ticket) = self.ticket {
158			ticket.drop(&self.depositor)?;
159		}
160
161		let ticket = if let Some(fp) = fp {
162			Some(Consideration::<AccountId, Footprint>::new(&new_depositor, fp)?)
163		} else {
164			None
165		};
166		Ok(Self { depositor: new_depositor.clone(), ticket, _phantom: Default::default() })
167	}
168
169	/// Try to drop the consideration and refund the deposit.
170	pub fn try_drop(self) -> Result<(), DispatchError> {
171		if let Some(ticket) = self.ticket {
172			ticket.drop(&self.depositor)?;
173		}
174		Ok(())
175	}
176}