Skip to content

Commit 69d711d

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 5980602 commit 69d711d

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,
@@ -1728,6 +1756,19 @@ async fn kick_off_validation_work<Context>(
17281756
background_validation_tx: &mpsc::Sender<(Hash, ValidatedCandidateCommand)>,
17291757
attesting: AttestingData,
17301758
) -> Result<(), Error> {
1759+
// Do nothing if the local validator is disabled or not a validator at all
1760+
match rp_state.table_context.local_validator_is_disabled() {
1761+
Some(true) => {
1762+
gum::info!(target: LOG_TARGET, "We are disabled - don't kick off validation");
1763+
return Ok(())
1764+
},
1765+
Some(false) => {}, // we are not disabled - move on
1766+
None => {
1767+
gum::debug!(target: LOG_TARGET, "We are not a validator - don't kick off validation");
1768+
return Ok(())
1769+
},
1770+
}
1771+
17311772
let candidate_hash = attesting.candidate.hash();
17321773
if rp_state.issued_statements.contains(&candidate_hash) {
17331774
return Ok(())
@@ -1785,6 +1826,16 @@ async fn maybe_validate_and_import<Context>(
17851826
},
17861827
};
17871828

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

17901841
// if we get an Error::RejectedByProspectiveParachains,
@@ -1946,6 +1997,13 @@ async fn handle_second_message<Context>(
19461997
Some(r) => r,
19471998
};
19481999

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

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

0 commit comments

Comments
 (0)