Skip to content

Commit 6b02a65

Browse files
Perform the deduplication of the validators set (#1621)
* Add dedupe utils * Implement validator deduplication based on account id and the first-in order * Fix the doc comment on the DedupeKeyExtractor::Output * Fixed doc comment on DedupeIter::dedupe_key_extractor * Apply suggestion from @dmitrylavrenov Co-authored-by: Dmitry Lavrenov <39522748+dmitrylavrenov@users.noreply.github.com> * Add a test * Remove the broken impl (doesn't work either way) --------- Co-authored-by: Dmitry Lavrenov <39522748+dmitrylavrenov@users.noreply.github.com>
1 parent 73850c9 commit 6b02a65

File tree

3 files changed

+147
-0
lines changed

3 files changed

+147
-0
lines changed

crates/pallet-humanode-session/src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
#![cfg_attr(not(feature = "std"), no_std)]
44

5+
extern crate alloc;
6+
57
#[cfg(feature = "try-runtime")]
68
use frame_support::sp_runtime::TryRuntimeError;
79
use frame_support::traits::{Get, StorageVersion};
@@ -21,6 +23,9 @@ pub mod benchmarking;
2123
mod mock;
2224
#[cfg(test)]
2325
mod tests;
26+
mod utils;
27+
28+
use self::utils::DedupeIteratorExt as _;
2429

2530
/// The type representing the session index in our chain.
2631
type SessionIndex = u32;
@@ -275,6 +280,7 @@ impl<T: Config> Pallet<T> {
275280
bootnodes
276281
.chain(bioauth_active_authentications)
277282
.chain(fixed_validators)
283+
.dedupe(AccountIdDedupeKey::<T>::default())
278284
}
279285

280286
/// Clears and re-populates the [`SessionIdentities`] for a given session with the entries.
@@ -352,3 +358,20 @@ impl<T: Config> sp_runtime::traits::Convert<T::AccountId, Option<IdentificationF
352358
<SessionIdentities<T>>::get(session_index, account_id)
353359
}
354360
}
361+
362+
/// The dedupe key extractor that provides Account Ids from Identification Tuples.
363+
struct AccountIdDedupeKey<T>(core::marker::PhantomData<T>);
364+
365+
impl<T: Config> utils::DedupeKeyExtractor<IdentificationTupleFor<T>> for AccountIdDedupeKey<T> {
366+
type Output = <T as frame_system::Config>::AccountId;
367+
368+
fn extract_key(&self, value: &IdentificationTupleFor<T>) -> Self::Output {
369+
value.0.clone()
370+
}
371+
}
372+
373+
impl<T> Default for AccountIdDedupeKey<T> {
374+
fn default() -> Self {
375+
Self(core::marker::PhantomData)
376+
}
377+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//! Utilities.
2+
3+
mod dedupe_iter;
4+
5+
pub use self::dedupe_iter::*;
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//! Deduplicating iterator wrapper.
2+
3+
use alloc::collections::BTreeSet;
4+
5+
/// Dedupe key extraction.
6+
///
7+
/// Provides a dedupe key for type `T`.
8+
pub trait DedupeKeyExtractor<T> {
9+
/// The type this extractor extracts; the dedupe key.
10+
type Output;
11+
12+
/// Perform the dedupe key extraction from the given value.
13+
fn extract_key(&self, value: &T) -> Self::Output;
14+
}
15+
16+
/// The dedupe iterator.
17+
pub struct DedupeIter<Source, DedupeKeyExtractor>
18+
where
19+
Source: Iterator,
20+
DedupeKeyExtractor: self::DedupeKeyExtractor<Source::Item>,
21+
{
22+
/// The source iterator.
23+
pub source: Source,
24+
25+
/// The dedupe key extractor.
26+
pub dedupe_key_extractor: DedupeKeyExtractor,
27+
28+
/// The state for tracking the duplicates.
29+
pub dedupe_state:
30+
BTreeSet<<DedupeKeyExtractor as self::DedupeKeyExtractor<Source::Item>>::Output>,
31+
}
32+
33+
impl<Source, DedupeKeyExtractor> DedupeIter<Source, DedupeKeyExtractor>
34+
where
35+
Source: Iterator,
36+
DedupeKeyExtractor: self::DedupeKeyExtractor<Source::Item>,
37+
{
38+
/// Create a new dedupe iterator from the given iterator.
39+
pub fn new(source: Source, dedupe_key_extractor: DedupeKeyExtractor) -> Self {
40+
Self {
41+
source,
42+
dedupe_key_extractor,
43+
dedupe_state: Default::default(),
44+
}
45+
}
46+
}
47+
48+
impl<Source, DedupeKeyExtractor> Iterator for DedupeIter<Source, DedupeKeyExtractor>
49+
where
50+
Source: Iterator,
51+
DedupeKeyExtractor: self::DedupeKeyExtractor<Source::Item>,
52+
DedupeKeyExtractor::Output: Ord,
53+
{
54+
type Item = Source::Item;
55+
56+
fn next(&mut self) -> Option<Self::Item> {
57+
loop {
58+
let item = self.source.next()?;
59+
let dedupe_key = self.dedupe_key_extractor.extract_key(&item);
60+
let was_new = self.dedupe_state.insert(dedupe_key);
61+
if !was_new {
62+
continue;
63+
}
64+
return Some(item);
65+
}
66+
}
67+
68+
fn size_hint(&self) -> (usize, Option<usize>) {
69+
let (_source_lower, source_higher) = self.source.size_hint();
70+
71+
// Lower bound is always unpredictably `0`.
72+
(0, source_higher)
73+
}
74+
}
75+
76+
/// [`Iterator`] extension trait for `dedupe` fn.
77+
pub trait DedupeIteratorExt: Iterator + Sized {
78+
/// Deduplicate the iterator.
79+
fn dedupe<DedupeKeyExtractor: self::DedupeKeyExtractor<Self::Item>>(
80+
self,
81+
dedupe_key_extractor: DedupeKeyExtractor,
82+
) -> DedupeIter<Self, DedupeKeyExtractor>;
83+
}
84+
85+
impl<T> DedupeIteratorExt for T
86+
where
87+
T: Iterator,
88+
{
89+
fn dedupe<DedupeKeyExtractor: self::DedupeKeyExtractor<Self::Item>>(
90+
self,
91+
dedupe_key_extractor: DedupeKeyExtractor,
92+
) -> DedupeIter<Self, DedupeKeyExtractor> {
93+
DedupeIter::new(self, dedupe_key_extractor)
94+
}
95+
}
96+
97+
#[cfg(test)]
98+
mod tests {
99+
use super::*;
100+
101+
struct IdentityCopy;
102+
103+
impl<T: Copy> DedupeKeyExtractor<T> for IdentityCopy {
104+
type Output = T;
105+
106+
fn extract_key(&self, value: &T) -> Self::Output {
107+
*value
108+
}
109+
}
110+
111+
#[test]
112+
fn dedupe() {
113+
let iter = vec![1usize, 2, 1].into_iter();
114+
115+
let deduped_iter = iter.dedupe(IdentityCopy);
116+
117+
assert_eq!(deduped_iter.collect::<Vec<_>>(), vec![1, 2]);
118+
}
119+
}

0 commit comments

Comments
 (0)