referrerpolicy=no-referrer-when-downgrade

pallet_election_provider_multi_phase/
helpers.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//! Some helper functions/macros for this crate.
19
20use crate::{
21	unsigned::{MinerConfig, MinerVoterOf},
22	SolutionTargetIndexOf, SolutionVoterIndexOf, VoteWeight,
23};
24use alloc::{collections::btree_map::BTreeMap, vec::Vec};
25
26#[macro_export]
27macro_rules! log {
28	($level:tt, $pattern:expr $(, $values:expr)* $(,)?) => {
29		log::$level!(
30			target: $crate::LOG_TARGET,
31			concat!("[#{:?}] 🗳  ", $pattern), frame_system::Pallet::<T>::block_number() $(, $values)*
32		)
33	};
34}
35
36// This is only useful for a context where a `<T: Config>` is not in scope.
37#[macro_export]
38macro_rules! log_no_system {
39	($level:tt, $pattern:expr $(, $values:expr)* $(,)?) => {
40		log::$level!(
41			target: $crate::LOG_TARGET,
42			concat!("🗳 ", $pattern) $(, $values)*
43		)
44	};
45}
46
47/// Generate a btree-map cache of the voters and their indices.
48///
49/// This can be used to efficiently build index getter closures.
50pub fn generate_voter_cache<T: MinerConfig>(
51	snapshot: &Vec<MinerVoterOf<T>>,
52) -> BTreeMap<T::AccountId, usize> {
53	let mut cache: BTreeMap<T::AccountId, usize> = BTreeMap::new();
54	snapshot.iter().enumerate().for_each(|(i, (x, _, _))| {
55		let _existed = cache.insert(x.clone(), i);
56		// if a duplicate exists, we only consider the last one. Defensive only, should never
57		// happen.
58		debug_assert!(_existed.is_none());
59	});
60
61	cache
62}
63
64/// Create a function that returns the index of a voter in the snapshot.
65///
66/// The returning index type is the same as the one defined in `T::Solution::Voter`.
67///
68/// ## Warning
69///
70/// Note that this will represent the snapshot data from which the `cache` is generated.
71pub fn voter_index_fn<T: MinerConfig>(
72	cache: &BTreeMap<T::AccountId, usize>,
73) -> impl Fn(&T::AccountId) -> Option<SolutionVoterIndexOf<T>> + '_ {
74	move |who| {
75		cache
76			.get(who)
77			.and_then(|i| <usize as TryInto<SolutionVoterIndexOf<T>>>::try_into(*i).ok())
78	}
79}
80
81/// Create a function that returns the index of a voter in the snapshot.
82///
83/// Same as [`voter_index_fn`] but the returned function owns all its necessary data; nothing is
84/// borrowed.
85pub fn voter_index_fn_owned<T: MinerConfig>(
86	cache: BTreeMap<T::AccountId, usize>,
87) -> impl Fn(&T::AccountId) -> Option<SolutionVoterIndexOf<T>> {
88	move |who| {
89		cache
90			.get(who)
91			.and_then(|i| <usize as TryInto<SolutionVoterIndexOf<T>>>::try_into(*i).ok())
92	}
93}
94
95/// Same as [`voter_index_fn`], but the returning index is converted into usize, if possible.
96///
97/// ## Warning
98///
99/// Note that this will represent the snapshot data from which the `cache` is generated.
100pub fn voter_index_fn_usize<T: MinerConfig>(
101	cache: &BTreeMap<T::AccountId, usize>,
102) -> impl Fn(&T::AccountId) -> Option<usize> + '_ {
103	move |who| cache.get(who).cloned()
104}
105
106/// A non-optimized, linear version of [`voter_index_fn`] that does not need a cache and does a
107/// linear search.
108///
109/// ## Warning
110///
111/// Not meant to be used in production.
112#[cfg(test)]
113pub fn voter_index_fn_linear<T: MinerConfig>(
114	snapshot: &Vec<MinerVoterOf<T>>,
115) -> impl Fn(&T::AccountId) -> Option<SolutionVoterIndexOf<T>> + '_ {
116	move |who| {
117		snapshot
118			.iter()
119			.position(|(x, _, _)| x == who)
120			.and_then(|i| <usize as TryInto<SolutionVoterIndexOf<T>>>::try_into(i).ok())
121	}
122}
123
124/// Create a function that returns the index of a target in the snapshot.
125///
126/// The returned index type is the same as the one defined in `T::Solution::Target`.
127///
128/// Note: to the extent possible, the returned function should be cached and reused. Producing that
129/// function requires a `O(n log n)` data transform. Each invocation of that function completes
130/// in `O(log n)`.
131pub fn target_index_fn<T: MinerConfig>(
132	snapshot: &Vec<T::AccountId>,
133) -> impl Fn(&T::AccountId) -> Option<SolutionTargetIndexOf<T>> + '_ {
134	let cache: BTreeMap<_, _> =
135		snapshot.iter().enumerate().map(|(idx, account_id)| (account_id, idx)).collect();
136	move |who| {
137		cache
138			.get(who)
139			.and_then(|i| <usize as TryInto<SolutionTargetIndexOf<T>>>::try_into(*i).ok())
140	}
141}
142
143/// Create a function the returns the index to a target in the snapshot.
144///
145/// The returned index type is the same as the one defined in `T::Solution::Target`.
146///
147/// ## Warning
148///
149/// Not meant to be used in production.
150#[cfg(test)]
151pub fn target_index_fn_linear<T: MinerConfig>(
152	snapshot: &Vec<T::AccountId>,
153) -> impl Fn(&T::AccountId) -> Option<SolutionTargetIndexOf<T>> + '_ {
154	move |who| {
155		snapshot
156			.iter()
157			.position(|x| x == who)
158			.and_then(|i| <usize as TryInto<SolutionTargetIndexOf<T>>>::try_into(i).ok())
159	}
160}
161
162/// Create a function that can map a voter index ([`SolutionVoterIndexOf`]) to the actual voter
163/// account using a linearly indexable snapshot.
164pub fn voter_at_fn<T: MinerConfig>(
165	snapshot: &Vec<MinerVoterOf<T>>,
166) -> impl Fn(SolutionVoterIndexOf<T>) -> Option<T::AccountId> + '_ {
167	move |i| {
168		<SolutionVoterIndexOf<T> as TryInto<usize>>::try_into(i)
169			.ok()
170			.and_then(|i| snapshot.get(i).map(|(x, _, _)| x).cloned())
171	}
172}
173
174/// Create a function that can map a target index ([`SolutionTargetIndexOf`]) to the actual target
175/// account using a linearly indexable snapshot.
176pub fn target_at_fn<T: MinerConfig>(
177	snapshot: &Vec<T::AccountId>,
178) -> impl Fn(SolutionTargetIndexOf<T>) -> Option<T::AccountId> + '_ {
179	move |i| {
180		<SolutionTargetIndexOf<T> as TryInto<usize>>::try_into(i)
181			.ok()
182			.and_then(|i| snapshot.get(i).cloned())
183	}
184}
185
186/// Create a function to get the stake of a voter.
187///
188/// This is not optimized and uses a linear search.
189#[cfg(test)]
190pub fn stake_of_fn_linear<T: MinerConfig>(
191	snapshot: &Vec<MinerVoterOf<T>>,
192) -> impl Fn(&T::AccountId) -> VoteWeight + '_ {
193	move |who| {
194		snapshot
195			.iter()
196			.find(|(x, _, _)| x == who)
197			.map(|(_, x, _)| *x)
198			.unwrap_or_default()
199	}
200}
201
202/// Create a function to get the stake of a voter.
203///
204/// ## Warning
205///
206/// The cache need must be derived from the same snapshot. Zero is returned if a voter is
207/// non-existent.
208pub fn stake_of_fn<'a, T: MinerConfig>(
209	snapshot: &'a Vec<MinerVoterOf<T>>,
210	cache: &'a BTreeMap<T::AccountId, usize>,
211) -> impl Fn(&T::AccountId) -> VoteWeight + 'a {
212	move |who| {
213		if let Some(index) = cache.get(who) {
214			snapshot.get(*index).map(|(_, x, _)| x).cloned().unwrap_or_default()
215		} else {
216			0
217		}
218	}
219}