Skip to content

Commit c13fa40

Browse files
tdimitrovordianordian
committed
Handling of disabled validators in backing subsystem (#1259)
Handles validator disabling in the backing subsystem. The PR introduces two changes: 1. Don't import statements from disabled validators. 2. Don't validate and second if the local validator is disabled. The purpose of this change is first to ignore statements from disabled validators on the node side. This is an optimisation as these statements are supposed to be cleared in the runtime too (still not implemented at the moment). Additionally if the local node is a validator which is disabled - don't process any new candidates. --------- Co-authored-by: ordian <[email protected]> Co-authored-by: ordian <[email protected]>
1 parent 4f832ea commit c13fa40

File tree

6 files changed

+556
-73
lines changed

6 files changed

+556
-73
lines changed

polkadot/node/core/backing/src/lib.rs

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ use statement_table::{
118118
},
119119
Config as TableConfig, Context as TableContextTrait, Table,
120120
};
121+
use util::vstaging::get_disabled_validators_with_fallback;
121122

122123
mod error;
123124

@@ -383,6 +384,21 @@ struct TableContext {
383384
validator: Option<Validator>,
384385
groups: HashMap<ParaId, Vec<ValidatorIndex>>,
385386
validators: Vec<ValidatorId>,
387+
disabled_validators: Vec<ValidatorIndex>,
388+
}
389+
390+
impl TableContext {
391+
// Returns `true` if the provided `ValidatorIndex` is in the disabled validators list
392+
pub fn validator_is_disabled(&self, validator_idx: &ValidatorIndex) -> bool {
393+
self.disabled_validators
394+
.iter()
395+
.any(|disabled_val_idx| *disabled_val_idx == *validator_idx)
396+
}
397+
398+
// Returns `true` if the local validator is in the disabled validators list
399+
pub fn local_validator_is_disabled(&self) -> Option<bool> {
400+
self.validator.as_ref().map(|v| v.disabled())
401+
}
386402
}
387403

388404
impl TableContextTrait for TableContext {
@@ -1010,21 +1026,33 @@ async fn construct_per_relay_parent_state<Context>(
10101026
let minimum_backing_votes =
10111027
try_runtime_api!(request_min_backing_votes(parent, session_index, ctx.sender()).await);
10121028

1029+
// TODO: https://github.com/paritytech/polkadot-sdk/issues/1940
1030+
// Once runtime ver `DISABLED_VALIDATORS_RUNTIME_REQUIREMENT` is released remove this call to
1031+
// `get_disabled_validators_with_fallback`, add `request_disabled_validators` call to the
1032+
// `try_join!` above and use `try_runtime_api!` to get `disabled_validators`
1033+
let disabled_validators = get_disabled_validators_with_fallback(ctx.sender(), parent)
1034+
.await
1035+
.map_err(Error::UtilError)?;
1036+
10131037
let signing_context = SigningContext { parent_hash: parent, session_index };
1014-
let validator =
1015-
match Validator::construct(&validators, signing_context.clone(), keystore.clone()) {
1016-
Ok(v) => Some(v),
1017-
Err(util::Error::NotAValidator) => None,
1018-
Err(e) => {
1019-
gum::warn!(
1020-
target: LOG_TARGET,
1021-
err = ?e,
1022-
"Cannot participate in candidate backing",
1023-
);
1038+
let validator = match Validator::construct(
1039+
&validators,
1040+
&disabled_validators,
1041+
signing_context.clone(),
1042+
keystore.clone(),
1043+
) {
1044+
Ok(v) => Some(v),
1045+
Err(util::Error::NotAValidator) => None,
1046+
Err(e) => {
1047+
gum::warn!(
1048+
target: LOG_TARGET,
1049+
err = ?e,
1050+
"Cannot participate in candidate backing",
1051+
);
10241052

1025-
return Ok(None)
1026-
},
1027-
};
1053+
return Ok(None)
1054+
},
1055+
};
10281056

10291057
let mut groups = HashMap::new();
10301058
let n_cores = cores.len();
@@ -1054,7 +1082,7 @@ async fn construct_per_relay_parent_state<Context>(
10541082
}
10551083
}
10561084

1057-
let table_context = TableContext { groups, validators, validator };
1085+
let table_context = TableContext { validator, groups, validators, disabled_validators };
10581086
let table_config = TableConfig {
10591087
allow_multiple_seconded: match mode {
10601088
ProspectiveParachainsMode::Enabled { .. } => true,
@@ -1726,6 +1754,19 @@ async fn kick_off_validation_work<Context>(
17261754
background_validation_tx: &mpsc::Sender<(Hash, ValidatedCandidateCommand)>,
17271755
attesting: AttestingData,
17281756
) -> Result<(), Error> {
1757+
// Do nothing if the local validator is disabled or not a validator at all
1758+
match rp_state.table_context.local_validator_is_disabled() {
1759+
Some(true) => {
1760+
gum::info!(target: LOG_TARGET, "We are disabled - don't kick off validation");
1761+
return Ok(())
1762+
},
1763+
Some(false) => {}, // we are not disabled - move on
1764+
None => {
1765+
gum::debug!(target: LOG_TARGET, "We are not a validator - don't kick off validation");
1766+
return Ok(())
1767+
},
1768+
}
1769+
17291770
let candidate_hash = attesting.candidate.hash();
17301771
if rp_state.issued_statements.contains(&candidate_hash) {
17311772
return Ok(())
@@ -1783,6 +1824,16 @@ async fn maybe_validate_and_import<Context>(
17831824
},
17841825
};
17851826

1827+
// Don't import statement if the sender is disabled
1828+
if rp_state.table_context.validator_is_disabled(&statement.validator_index()) {
1829+
gum::debug!(
1830+
target: LOG_TARGET,
1831+
sender_validator_idx = ?statement.validator_index(),
1832+
"Not importing statement because the sender is disabled"
1833+
);
1834+
return Ok(())
1835+
}
1836+
17861837
let res = import_statement(ctx, rp_state, &mut state.per_candidate, &statement).await;
17871838

17881839
// if we get an Error::RejectedByProspectiveParachains,
@@ -1944,6 +1995,13 @@ async fn handle_second_message<Context>(
19441995
Some(r) => r,
19451996
};
19461997

1998+
// Just return if the local validator is disabled. If we are here the local node should be a
1999+
// validator but defensively use `unwrap_or(false)` to continue processing in this case.
2000+
if rp_state.table_context.local_validator_is_disabled().unwrap_or(false) {
2001+
gum::warn!(target: LOG_TARGET, "Local validator is disabled. Don't validate and second");
2002+
return Ok(())
2003+
}
2004+
19472005
// Sanity check that candidate is from our assignment.
19482006
if Some(candidate.descriptor().para_id) != rp_state.assignment {
19492007
gum::debug!(
@@ -1990,6 +2048,7 @@ async fn handle_statement_message<Context>(
19902048
) -> Result<(), Error> {
19912049
let _timer = metrics.time_process_statement();
19922050

2051+
// Validator disabling is handled in `maybe_validate_and_import`
19932052
match maybe_validate_and_import(ctx, state, relay_parent, statement).await {
19942053
Err(Error::ValidationFailed(_)) => Ok(()),
19952054
Err(e) => Err(e),

0 commit comments

Comments
 (0)