From 15cc8b30ada9d8c85a556fb89d194de67d875a73 Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Wed, 8 Oct 2025 02:35:31 +0400 Subject: [PATCH 1/7] Add dedupe utils --- crates/pallet-humanode-session/src/lib.rs | 3 + crates/pallet-humanode-session/src/utils.rs | 5 + .../src/utils/dedupe_iter.rs | 106 ++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 crates/pallet-humanode-session/src/utils.rs create mode 100644 crates/pallet-humanode-session/src/utils/dedupe_iter.rs diff --git a/crates/pallet-humanode-session/src/lib.rs b/crates/pallet-humanode-session/src/lib.rs index 59a164d47..03d7c4b81 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,7 @@ pub mod benchmarking; mod mock; #[cfg(test)] mod tests; +mod utils; /// The type representing the session index in our chain. type SessionIndex = u32; 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..c397e8672 --- /dev/null +++ b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs @@ -0,0 +1,106 @@ +//! Deduplicating iterator wrapper. + +use alloc::collections::BTreeSet; + +/// Dedupe key extraction. +/// +/// Provides a dedupe key for type `T`. +pub trait DedupeKeyExtractor { + /// The type this extracts 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 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 in 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) + } +} + +impl DedupeKeyExtractor for fn(&T) -> R +where + R: Eq + core::hash::Hash, +{ + type Output = R; + + fn extract_key(&self, value: &T) -> Self::Output { + (self)(value) + } +} From c66dc16ab77bbdf04e079964631bb709aa4367ab Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Wed, 8 Oct 2025 03:17:49 +0400 Subject: [PATCH 2/7] Implement validator deduplication based on account id and the first-in order --- crates/pallet-humanode-session/src/lib.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/pallet-humanode-session/src/lib.rs b/crates/pallet-humanode-session/src/lib.rs index 03d7c4b81..884df53f9 100644 --- a/crates/pallet-humanode-session/src/lib.rs +++ b/crates/pallet-humanode-session/src/lib.rs @@ -25,6 +25,8 @@ mod mock; mod tests; mod utils; +use self::utils::DedupeIteratorExt as _; + /// The type representing the session index in our chain. type SessionIndex = u32; @@ -278,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. @@ -355,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) + } +} From bec07a1865d553fdf1f3ddd37f64c216bbd28f75 Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Thu, 9 Oct 2025 15:21:53 +0400 Subject: [PATCH 3/7] Fix the doc comment on the DedupeKeyExtractor::Output --- crates/pallet-humanode-session/src/utils/dedupe_iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs index c397e8672..eebad2f1c 100644 --- a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs +++ b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs @@ -6,7 +6,7 @@ use alloc::collections::BTreeSet; /// /// Provides a dedupe key for type `T`. pub trait DedupeKeyExtractor { - /// The type this extracts extracts; the dedupe key. + /// The type this extractor extracts; the dedupe key. type Output; /// Perform the dedupe key extraction from the given value. From 0a46684968ddc068ccaa0c2ed3d1719b1462ce43 Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Thu, 9 Oct 2025 15:23:50 +0400 Subject: [PATCH 4/7] Fixed doc comment on DedupeIter::dedupe_key_extractor --- crates/pallet-humanode-session/src/utils/dedupe_iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs index eebad2f1c..1a93271eb 100644 --- a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs +++ b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs @@ -22,7 +22,7 @@ where /// The source iterator. pub source: Source, - /// The the dedupe key extractor. + /// The dedupe key extractor. pub dedupe_key_extractor: DedupeKeyExtractor, /// The state for tracking the duplicates. From cc5f06800c1126a95f3303c0cc652d8f4e8d29f5 Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Thu, 9 Oct 2025 15:24:46 +0400 Subject: [PATCH 5/7] Apply suggestion from @dmitrylavrenov Co-authored-by: Dmitry Lavrenov <39522748+dmitrylavrenov@users.noreply.github.com> --- crates/pallet-humanode-session/src/utils/dedupe_iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs index 1a93271eb..bacf3d65e 100644 --- a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs +++ b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs @@ -68,7 +68,7 @@ where fn size_hint(&self) -> (usize, Option) { let (_source_lower, source_higher) = self.source.size_hint(); - // Lower bound in always unpredictably `0`. + // Lower bound is always unpredictably `0`. (0, source_higher) } } From 1c1a362c884ecfb05b5d13583380588a57f83727 Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Thu, 9 Oct 2025 15:40:59 +0400 Subject: [PATCH 6/7] Add a test --- .../src/utils/dedupe_iter.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs index bacf3d65e..35d915169 100644 --- a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs +++ b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs @@ -104,3 +104,27 @@ where (self)(value) } } + +#[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]); + } +} From f5776c3e38fccac5bbb0c42de5d010dbadf74efa Mon Sep 17 00:00:00 2001 From: MOZGIII Date: Thu, 9 Oct 2025 15:41:33 +0400 Subject: [PATCH 7/7] Remove the broken impl (doesn't work either way) --- .../pallet-humanode-session/src/utils/dedupe_iter.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs index 35d915169..c35606438 100644 --- a/crates/pallet-humanode-session/src/utils/dedupe_iter.rs +++ b/crates/pallet-humanode-session/src/utils/dedupe_iter.rs @@ -94,17 +94,6 @@ where } } -impl DedupeKeyExtractor for fn(&T) -> R -where - R: Eq + core::hash::Hash, -{ - type Output = R; - - fn extract_key(&self, value: &T) -> Self::Output { - (self)(value) - } -} - #[cfg(test)] mod tests { use super::*;