Skip to content

Commit c3c8b04

Browse files
committed
chore: Added nominators
1 parent 7766176 commit c3c8b04

File tree

2 files changed

+28
-4
lines changed

2 files changed

+28
-4
lines changed

src/dynamic/election_data.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,11 @@ where
432432
.await
433433
.map_err(|e| Error::Other(format!("Failed to fetch candidates: {e}")))?;
434434

435-
let nominators = fetch_nominators(client)
435+
// Extract candidate accounts for nominator filtering logic
436+
let candidate_accounts: Vec<AccountId> =
437+
candidates.iter().map(|(account, _)| account.clone()).collect();
438+
439+
let nominators = fetch_nominators(client, &candidate_accounts)
436440
.await
437441
.map_err(|e| Error::Other(format!("Failed to fetch nominators: {e}")))?;
438442

src/dynamic/staking.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
};
99
use codec::{Decode, Encode};
1010
use scale_value::At;
11-
use std::time::Duration;
11+
use std::{collections::HashSet, time::Duration};
1212
use subxt::dynamic::Value;
1313

1414
/// Fetch all candidate validators (stash AccountId) with their active stake
@@ -238,9 +238,15 @@ pub(crate) async fn fetch_stakes_batch_static(
238238
Ok(stakes)
239239
}
240240

241-
/// Fetch all nominators (stash, stake, targets) from Staking::Nominators
241+
/// Fetch all nominators (stash, stake, targets) from `Staking::Nominators`.
242+
///
243+
/// `candidate_accounts` is the set of validator candidates for the election. We
244+
/// use this to filter out "pure self" validator nominators (validators whose
245+
/// only nomination target is themselves), since those will be injected as
246+
/// self-votes separately when building the synthetic snapshots.
242247
pub(crate) async fn fetch_nominators(
243248
client: &Client,
249+
candidate_accounts: &[AccountId],
244250
) -> Result<Vec<(AccountId, u64, Vec<AccountId>)>, Error> {
245251
/// Nominations data structure from the blockchain
246252
#[derive(Debug, Clone, Decode)]
@@ -289,6 +295,11 @@ pub(crate) async fn fetch_nominators(
289295
log::info!(target: LOG_TARGET, "Processed {count} nominators...");
290296
log::info!(target: LOG_TARGET, "Fetching stakes of nominators");
291297

298+
// Build a fast lookup set of validator candidates to detect validators that
299+
// also appear as nominators.
300+
let candidate_set: HashSet<AccountId> =
301+
candidate_accounts.iter().cloned().collect();
302+
292303
let nominator_stashes: Vec<AccountId> = nominators.iter().map(|e| e.0.clone()).collect();
293304
let nominator_stakes_u128 = fetch_stakes_in_batches(client, &nominator_stashes, true).await?;
294305

@@ -297,7 +308,16 @@ pub(crate) async fn fetch_nominators(
297308
let mut filtered_count = 0usize;
298309
for ((stash, targets), stake_u128) in nominators.into_iter().zip(nominator_stakes_u128) {
299310
// Filter out nominators with zero stake or empty targets (matching snapshot behavior)
300-
if stake_u128 == 0 || targets.is_empty() {
311+
// and validators that only self-nominate (pure self-nominators). The latter would
312+
// otherwise show up as duplicate "validator-as-nominator" entries in the exported
313+
// nominators JSON, even though they are handled as self-votes in the snapshots.
314+
let is_candidate = candidate_set.contains(&stash);
315+
let is_pure_self_nominator =
316+
is_candidate &&
317+
!targets.is_empty() &&
318+
targets.iter().all(|t| t == &stash);
319+
320+
if stake_u128 == 0 || targets.is_empty() || is_pure_self_nominator {
301321
filtered_count += 1;
302322
continue;
303323
}

0 commit comments

Comments
 (0)