diff --git a/crates/pallet-humanode-session/src/lib.rs b/crates/pallet-humanode-session/src/lib.rs index 59a164d47..884df53f9 100644 --- a/crates/pallet-humanode-session/src/lib.rs +++ b/crates/pallet-humanode-session/src/lib.rs @@ -2,6 +2,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + #[cfg(feature = "try-runtime")] use frame_support::sp_runtime::TryRuntimeError; use frame_support::traits::{Get, StorageVersion}; @@ -21,6 +23,9 @@ pub mod benchmarking; mod mock; #[cfg(test)] mod tests; +mod utils; + +use self::utils::DedupeIteratorExt as _; /// The type representing the session index in our chain. type SessionIndex = u32; @@ -275,6 +280,7 @@ impl Pallet { bootnodes .chain(bioauth_active_authentications) .chain(fixed_validators) + .dedupe(AccountIdDedupeKey::::default()) } /// Clears and re-populates the [`SessionIdentities`] for a given session with the entries. @@ -352,3 +358,20 @@ impl sp_runtime::traits::Convert>::get(session_index, account_id) } } + +/// The dedupe key extractor that provides Account Ids from Identification Tuples. +struct AccountIdDedupeKey(core::marker::PhantomData); + +impl utils::DedupeKeyExtractor> for AccountIdDedupeKey { + type Output = ::AccountId; + + fn extract_key(&self, value: &IdentificationTupleFor) -> Self::Output { + value.0.clone() + } +} + +impl Default for AccountIdDedupeKey { + fn default() -> Self { + Self(core::marker::PhantomData) + } +} diff --git a/crates/pallet-humanode-session/src/utils.rs b/crates/pallet-humanode-session/src/utils.rs new file mode 100644 index 000000000..a5a2d98bf --- /dev/null +++ b/crates/pallet-humanode-session/src/utils.rs @@ -0,0 +1,5 @@ +//! Utilities. + +mod dedupe_iter; + +pub use self::dedupe_iter::*; diff --git a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs new file mode 100644 index 000000000..c35606438 --- /dev/null +++ b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs @@ -0,0 +1,119 @@ +//! Deduplicating iterator wrapper. + +use alloc::collections::BTreeSet; + +/// Dedupe key extraction. +/// +/// Provides a dedupe key for type `T`. +pub trait DedupeKeyExtractor { + /// The type this extractor extracts; the dedupe key. + type Output; + + /// Perform the dedupe key extraction from the given value. + fn extract_key(&self, value: &T) -> Self::Output; +} + +/// The dedupe iterator. +pub struct DedupeIter +where + Source: Iterator, + DedupeKeyExtractor: self::DedupeKeyExtractor, +{ + /// The source iterator. + pub source: Source, + + /// The dedupe key extractor. + pub dedupe_key_extractor: DedupeKeyExtractor, + + /// The state for tracking the duplicates. + pub dedupe_state: + BTreeSet<>::Output>, +} + +impl DedupeIter +where + Source: Iterator, + DedupeKeyExtractor: self::DedupeKeyExtractor, +{ + /// Create a new dedupe iterator from the given iterator. + pub fn new(source: Source, dedupe_key_extractor: DedupeKeyExtractor) -> Self { + Self { + source, + dedupe_key_extractor, + dedupe_state: Default::default(), + } + } +} + +impl Iterator for DedupeIter +where + Source: Iterator, + DedupeKeyExtractor: self::DedupeKeyExtractor, + DedupeKeyExtractor::Output: Ord, +{ + type Item = Source::Item; + + fn next(&mut self) -> Option { + loop { + let item = self.source.next()?; + let dedupe_key = self.dedupe_key_extractor.extract_key(&item); + let was_new = self.dedupe_state.insert(dedupe_key); + if !was_new { + continue; + } + return Some(item); + } + } + + fn size_hint(&self) -> (usize, Option) { + let (_source_lower, source_higher) = self.source.size_hint(); + + // Lower bound is always unpredictably `0`. + (0, source_higher) + } +} + +/// [`Iterator`] extension trait for `dedupe` fn. +pub trait DedupeIteratorExt: Iterator + Sized { + /// Deduplicate the iterator. + fn dedupe>( + self, + dedupe_key_extractor: DedupeKeyExtractor, + ) -> DedupeIter; +} + +impl DedupeIteratorExt for T +where + T: Iterator, +{ + fn dedupe>( + self, + dedupe_key_extractor: DedupeKeyExtractor, + ) -> DedupeIter { + DedupeIter::new(self, dedupe_key_extractor) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct IdentityCopy; + + impl DedupeKeyExtractor for IdentityCopy { + type Output = T; + + fn extract_key(&self, value: &T) -> Self::Output { + *value + } + } + + #[test] + fn dedupe() { + let iter = vec![1usize, 2, 1].into_iter(); + + let deduped_iter = iter.dedupe(IdentityCopy); + + assert_eq!(deduped_iter.collect::>(), vec![1, 2]); + } +}