Skip to content

Commit 204298f

Browse files
tdimitrovpepoviola
authored andcommitted
Cache locally controlled validator indices in dispute-coordinator (#8837)
Fixes #8823 --------- Co-authored-by: Javier Viola <[email protected]>
1 parent 88479b5 commit 204298f

File tree

8 files changed

+118
-29
lines changed

8 files changed

+118
-29
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

polkadot/node/core/dispute-coordinator/src/import.rs

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,12 @@ use polkadot_node_primitives::{
3232
disputes::ValidCandidateVotes, CandidateVotes, DisputeStatus, SignedDisputeStatement, Timestamp,
3333
};
3434
use polkadot_node_subsystem::overseer;
35-
use polkadot_node_subsystem_util::runtime::RuntimeInfo;
35+
use polkadot_node_subsystem_util::{runtime::RuntimeInfo, ControlledValidatorIndices};
3636
use polkadot_primitives::{
3737
vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, DisputeStatement,
3838
ExecutorParams, Hash, IndexedVec, SessionIndex, SessionInfo, ValidDisputeStatementKind,
39-
ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature,
39+
ValidatorId, ValidatorIndex, ValidatorSignature,
4040
};
41-
use sc_keystore::LocalKeystore;
4241

4342
use crate::LOG_TARGET;
4443

@@ -63,12 +62,12 @@ impl<'a> CandidateEnvironment<'a> {
6362
///
6463
/// Return: `None` in case session is outside of session window.
6564
pub async fn new<Context>(
66-
keystore: &LocalKeystore,
6765
ctx: &mut Context,
6866
runtime_info: &'a mut RuntimeInfo,
6967
session_index: SessionIndex,
7068
relay_parent: Hash,
7169
disabled_offchain: impl IntoIterator<Item = ValidatorIndex>,
70+
controlled_indices: &mut ControlledValidatorIndices,
7271
) -> Option<CandidateEnvironment<'a>> {
7372
let disabled_onchain = runtime_info
7473
.get_disabled_validators(ctx.sender(), relay_parent)
@@ -105,7 +104,8 @@ impl<'a> CandidateEnvironment<'a> {
105104
d
106105
};
107106

108-
let controlled_indices = find_controlled_validator_indices(keystore, &session.validators);
107+
let controlled_indices = controlled_indices.get(session_index, &session.validators).clone();
108+
109109
Some(Self { session_index, session, executor_params, controlled_indices, disabled_indices })
110110
}
111111

@@ -632,22 +632,3 @@ impl ImportResult {
632632
}
633633
}
634634
}
635-
636-
/// Find indices controlled by this validator.
637-
///
638-
/// That is all `ValidatorIndex`es we have private keys for. Usually this will only be one.
639-
fn find_controlled_validator_indices(
640-
keystore: &LocalKeystore,
641-
validators: &IndexedVec<ValidatorIndex, ValidatorId>,
642-
) -> HashSet<ValidatorIndex> {
643-
let mut controlled = HashSet::new();
644-
for (index, validator) in validators.iter().enumerate() {
645-
if keystore.key_pair::<ValidatorPair>(validator).ok().flatten().is_none() {
646-
continue
647-
}
648-
649-
controlled.insert(ValidatorIndex(index as _));
650-
}
651-
652-
controlled
653-
}

polkadot/node/core/dispute-coordinator/src/initialized.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ use polkadot_node_subsystem::{
4040
},
4141
overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, RuntimeApiError,
4242
};
43-
use polkadot_node_subsystem_util::runtime::{
44-
self, key_ownership_proof, submit_report_dispute_lost, RuntimeInfo,
43+
use polkadot_node_subsystem_util::{
44+
runtime::{self, key_ownership_proof, submit_report_dispute_lost, RuntimeInfo},
45+
ControlledValidatorIndices,
4546
};
4647
use polkadot_primitives::{
4748
slashing,
@@ -98,6 +99,8 @@ pub(crate) struct Initialized {
9899
/// We have the onchain state of disabled validators as well as the offchain
99100
/// state that is based on the lost disputes.
100101
offchain_disabled_validators: OffchainDisabledValidators,
102+
/// The indices of the controlled validators, cached by session.
103+
controlled_validator_indices: ControlledValidatorIndices,
101104
/// This is the highest `SessionIndex` seen via `ActiveLeavesUpdate`. It doesn't matter if it
102105
/// was cached successfully or not. It is used to detect ancient disputes.
103106
highest_session_seen: SessionIndex,
@@ -133,6 +136,7 @@ impl Initialized {
133136
highest_session_seen: SessionIndex,
134137
gaps_in_cache: bool,
135138
offchain_disabled_validators: OffchainDisabledValidators,
139+
controlled_validator_indices: ControlledValidatorIndices,
136140
) -> Self {
137141
let DisputeCoordinatorSubsystem {
138142
config: _,
@@ -149,6 +153,7 @@ impl Initialized {
149153
keystore,
150154
runtime_info,
151155
offchain_disabled_validators,
156+
controlled_validator_indices,
152157
highest_session_seen,
153158
gaps_in_cache,
154159
spam_slots,
@@ -975,12 +980,12 @@ impl Initialized {
975980
};
976981

977982
let env = match CandidateEnvironment::new(
978-
&self.keystore,
979983
ctx,
980984
&mut self.runtime_info,
981985
session,
982986
relay_parent,
983987
self.offchain_disabled_validators.iter(session),
988+
&mut self.controlled_validator_indices,
984989
)
985990
.await
986991
{
@@ -1450,12 +1455,12 @@ impl Initialized {
14501455

14511456
// Load environment:
14521457
let env = match CandidateEnvironment::new(
1453-
&self.keystore,
14541458
ctx,
14551459
&mut self.runtime_info,
14561460
session,
14571461
candidate_receipt.descriptor.relay_parent(),
14581462
self.offchain_disabled_validators.iter(session),
1463+
&mut self.controlled_validator_indices,
14591464
)
14601465
.await
14611466
{

polkadot/node/core/dispute-coordinator/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use polkadot_node_subsystem::{
4444
use polkadot_node_subsystem_util::{
4545
database::Database,
4646
runtime::{Config as RuntimeInfoConfig, RuntimeInfo},
47+
ControlledValidatorIndices,
4748
};
4849
use polkadot_primitives::{
4950
vstaging::ScrapedOnChainVotes, DisputeStatement, SessionIndex, SessionInfo, ValidatorIndex,
@@ -236,6 +237,7 @@ impl DisputeCoordinatorSubsystem {
236237
highest_session_seen,
237238
gaps_in_cache,
238239
offchain_disabled_validators,
240+
controlled_validator_indices,
239241
) = match self
240242
.handle_startup(ctx, first_leaf.clone(), &mut runtime_info, &mut overlay_db, clock)
241243
.await
@@ -263,6 +265,7 @@ impl DisputeCoordinatorSubsystem {
263265
highest_session_seen,
264266
gaps_in_cache,
265267
offchain_disabled_validators,
268+
controlled_validator_indices,
266269
),
267270
backend,
268271
)))
@@ -289,6 +292,7 @@ impl DisputeCoordinatorSubsystem {
289292
SessionIndex,
290293
bool,
291294
initialized::OffchainDisabledValidators,
295+
ControlledValidatorIndices,
292296
)> {
293297
let now = clock.now();
294298

@@ -357,16 +361,18 @@ impl DisputeCoordinatorSubsystem {
357361

358362
let mut participation_requests = Vec::new();
359363
let mut spam_disputes: UnconfirmedDisputes = UnconfirmedDisputes::new();
364+
let mut controlled_indices =
365+
ControlledValidatorIndices::new(self.keystore.clone(), DISPUTE_WINDOW.get());
360366
let leaf_hash = initial_head.hash;
361367
let (scraper, votes) = ChainScraper::new(ctx.sender(), initial_head).await?;
362368
for ((session, ref candidate_hash), _) in active_disputes {
363369
let env = match CandidateEnvironment::new(
364-
&self.keystore,
365370
ctx,
366371
runtime_info,
367372
highest_session,
368373
leaf_hash,
369374
offchain_disabled_validators.iter(session),
375+
&mut controlled_indices,
370376
)
371377
.await
372378
{
@@ -452,6 +458,7 @@ impl DisputeCoordinatorSubsystem {
452458
highest_session,
453459
gap_in_cache,
454460
offchain_disabled_validators,
461+
controlled_indices,
455462
))
456463
}
457464
}

polkadot/node/subsystem-util/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ polkadot-node-subsystem-types = { workspace = true, default-features = true }
3232
polkadot-overseer = { workspace = true, default-features = true }
3333
polkadot-primitives = { workspace = true, default-features = true }
3434

35+
sc-keystore = { workspace = true, default-features = true }
3536
sp-application-crypto = { workspace = true, default-features = true }
3637
sp-core = { workspace = true, default-features = true }
3738
sp-keystore = { workspace = true, default-features = true }
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (C) Parity Technologies (UK) Ltd.
2+
// This file is part of Polkadot.
3+
4+
// Polkadot is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// Polkadot is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16+
17+
//! `ControlledValidatorIndices` implementation.
18+
19+
use polkadot_primitives::{IndexedVec, SessionIndex, ValidatorId, ValidatorIndex, ValidatorPair};
20+
use sc_keystore::LocalKeystore;
21+
use schnellru::{ByLength, LruMap};
22+
use sp_application_crypto::{AppCrypto, ByteArray};
23+
use sp_keystore::Keystore;
24+
use std::{collections::HashSet, sync::Arc};
25+
26+
/// Keeps track of the validator indices controlled by the local validator in a given session. For
27+
/// better performance, the values for each session are cached.
28+
pub struct ControlledValidatorIndices {
29+
/// The indices of the controlled validators, cached by session.
30+
controlled_validator_indices: LruMap<SessionIndex, HashSet<ValidatorIndex>>,
31+
keystore: Arc<LocalKeystore>,
32+
}
33+
34+
impl ControlledValidatorIndices {
35+
/// Create a new instance of `ControlledValidatorIndices`.
36+
pub fn new(keystore: Arc<LocalKeystore>, cache_size: u32) -> Self {
37+
let controlled_validator_indices = LruMap::new(ByLength::new(cache_size));
38+
Self { controlled_validator_indices, keystore }
39+
}
40+
41+
/// Get the controlled validator indices for a given session. If the indices are not known they
42+
/// will be fetched from `session_validators` and cached.
43+
pub fn get(
44+
&mut self,
45+
session: SessionIndex,
46+
session_validators: &IndexedVec<ValidatorIndex, ValidatorId>,
47+
) -> &HashSet<ValidatorIndex> {
48+
if self.controlled_validator_indices.get(&session).is_none() {
49+
let indices =
50+
Self::find_controlled_validator_indices(&self.keystore, session_validators);
51+
self.controlled_validator_indices.insert(session, indices.clone());
52+
}
53+
54+
self.controlled_validator_indices
55+
.get(&session)
56+
.expect("We just inserted the controlled indices; qed")
57+
}
58+
59+
/// Find indices controlled by this validator.
60+
///
61+
/// That is all `ValidatorIndex`es we have private keys for. Usually this will only be one.
62+
fn find_controlled_validator_indices(
63+
keystore: &LocalKeystore,
64+
validators: &IndexedVec<ValidatorIndex, ValidatorId>,
65+
) -> HashSet<ValidatorIndex> {
66+
let mut controlled = HashSet::new();
67+
for (index, validator) in validators.iter().enumerate() {
68+
if !Keystore::has_keys(keystore, &[(validator.to_raw_vec(), ValidatorPair::ID)]) {
69+
continue
70+
}
71+
72+
controlled.insert(ValidatorIndex(index as _));
73+
}
74+
75+
controlled
76+
}
77+
}

polkadot/node/subsystem-util/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ pub mod reputation;
9999

100100
mod determine_new_blocks;
101101

102+
mod controlled_validator_indices;
103+
pub use controlled_validator_indices::ControlledValidatorIndices;
104+
102105
#[cfg(test)]
103106
mod tests;
104107

prdoc/pr_8837.prdoc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
title: Cache locally controlled validator indices in dispute-coordinator
2+
doc:
3+
- audience: Node Dev
4+
description: |
5+
`dispute-coordinator` uses `keystore.key_pair()` to obtain the set of locally controlled
6+
validator IDs. This operation happens on each import and is expensive because it involves key
7+
generation from a seed phrase. This patch lazily determines the set of locally controlled
8+
validator IDs and caches the result for each session.
9+
10+
crates:
11+
- name: polkadot-node-core-dispute-coordinator
12+
bump: minor
13+
- name: polkadot-node-subsystem-util
14+
bump: minor

0 commit comments

Comments
 (0)