Skip to content

Commit 40a8dce

Browse files
committed
add disabled validators to unwanted mask
1 parent bf43fc3 commit 40a8dce

File tree

5 files changed

+123
-73
lines changed

5 files changed

+123
-73
lines changed

polkadot/node/network/protocol/src/request_response/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ impl Protocol {
248248
name,
249249
fallback_names,
250250
max_request_size: 1_000,
251-
/// Responses are just confirmation, in essence not even a bit. So 100 seems
252-
/// plenty.
251+
// Responses are just confirmation, in essence not even a bit. So 100 seems
252+
// plenty.
253253
max_response_size: 100,
254254
request_timeout: DISPUTE_REQUEST_TIMEOUT,
255255
inbound_queue: tx,

polkadot/node/network/statement-distribution/src/v2/mod.rs

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -596,9 +596,9 @@ pub(crate) async fn handle_active_leaves_update<Context>(
596596
?disabled_validators,
597597
"Disabled validators detected"
598598
);
599-
}
600599

601-
per_session.extend_disabled_validators(disabled_validators);
600+
per_session.extend_disabled_validators(disabled_validators);
601+
}
602602

603603
let local_validator = per_session.local_validator.and_then(|v| {
604604
find_local_validator_state(
@@ -2778,17 +2778,24 @@ pub(crate) async fn dispatch_requests<Context>(ctx: &mut Context, state: &mut St
27782778
}
27792779
}
27802780

2781+
// Add disabled validators to the unwanted mask.
2782+
let disabled_mask = per_session
2783+
.disabled_bitmask(group_index)
2784+
.expect("group existence checked above; qed");
2785+
unwanted_mask.seconded_in_group |= &disabled_mask;
2786+
unwanted_mask.validated_in_group |= &disabled_mask;
2787+
27812788
// don't require a backing threshold for cluster candidates.
27822789
let require_backing = relay_parent_state.local_validator.as_ref()?.group != group_index;
27832790

2784-
Some(RequestProperties {
2785-
unwanted_mask,
2786-
backing_threshold: if require_backing {
2787-
Some(per_session.groups.get_size_and_backing_threshold(group_index)?.1)
2788-
} else {
2789-
None
2790-
},
2791-
})
2791+
let backing_threshold = if require_backing {
2792+
let threshold = per_session.groups.get_size_and_backing_threshold(group_index)?.1;
2793+
Some(threshold)
2794+
} else {
2795+
None
2796+
};
2797+
2798+
Some(RequestProperties { unwanted_mask, backing_threshold })
27922799
};
27932800

27942801
while let Some(request) = state.request_manager.next_request(
@@ -2861,10 +2868,6 @@ pub(crate) async fn handle_response<Context>(
28612868
Some(g) => g,
28622869
};
28632870

2864-
let disabled_mask = per_session
2865-
.disabled_bitmask(group_index)
2866-
.expect("group_index checked above; qed");
2867-
28682871
let res = response.validate_response(
28692872
&mut state.request_manager,
28702873
group,
@@ -2879,7 +2882,6 @@ pub(crate) async fn handle_response<Context>(
28792882

28802883
Some(g_index) == expected_group
28812884
},
2882-
disabled_mask,
28832885
);
28842886

28852887
for (peer, rep) in res.reputation_changes {
@@ -2977,6 +2979,14 @@ pub(crate) async fn handle_response<Context>(
29772979
// includable.
29782980
}
29792981

2982+
/// Returns true if the statement filter meets the backing threshold for grid requests.
2983+
pub(crate) fn seconded_and_sufficient(
2984+
filter: &StatementFilter,
2985+
backing_threshold: Option<usize>,
2986+
) -> bool {
2987+
backing_threshold.map_or(true, |t| filter.has_seconded() && filter.backing_validators() >= t)
2988+
}
2989+
29802990
/// Answer an incoming request for a candidate.
29812991
pub(crate) fn answer_request(state: &mut State, message: ResponderMessage) {
29822992
let ResponderMessage { request, sent_feedback } = message;
@@ -3017,11 +3027,13 @@ pub(crate) fn answer_request(state: &mut State, message: ResponderMessage) {
30173027
Some(d) => d,
30183028
};
30193029

3020-
let group_size = per_session
3030+
let group_index = confirmed.group_index();
3031+
let group = per_session
30213032
.groups
3022-
.get(confirmed.group_index())
3023-
.expect("group from session's candidate always known; qed")
3024-
.len();
3033+
.get(group_index)
3034+
.expect("group from session's candidate always known; qed");
3035+
3036+
let group_size = group.len();
30253037

30263038
// check request bitfields are right size.
30273039
if mask.seconded_in_group.len() != group_size || mask.validated_in_group.len() != group_size {
@@ -3075,17 +3087,56 @@ pub(crate) fn answer_request(state: &mut State, message: ResponderMessage) {
30753087
validated_in_group: !mask.validated_in_group.clone(),
30763088
};
30773089

3078-
// Ignore disabled validators when sending the response.
3079-
let disabled_mask = per_session.disabled_bitmask(confirmed.group_index()).unwrap_or_default();
3090+
// Ignore disabled validators from the latest state when sending the response.
3091+
let disabled_mask = per_session
3092+
.disabled_bitmask(confirmed.group_index())
3093+
.expect("group existence checked; qed");
30803094
and_mask.mask_seconded(&disabled_mask);
30813095
and_mask.mask_valid(&disabled_mask);
30823096

3097+
let mut sent_filter = StatementFilter::blank(group_size);
30833098
let statements: Vec<_> = relay_parent_state
30843099
.statement_store
30853100
.group_statements(&per_session.groups, confirmed.group_index(), *candidate_hash, &and_mask)
30863101
.map(|s| s.as_unchecked().clone())
3102+
.inspect(|s| {
3103+
let index_in_group = |v: ValidatorIndex| group.iter().position(|x| &v == x);
3104+
let Some(i) = index_in_group(s.unchecked_validator_index()) else {
3105+
return
3106+
};
3107+
3108+
match s.unchecked_payload() {
3109+
CompactStatement::Seconded(_) => {
3110+
sent_filter.seconded_in_group.set(i, true);
3111+
},
3112+
CompactStatement::Valid(_) => {
3113+
sent_filter.validated_in_group.set(i, true);
3114+
},
3115+
}
3116+
})
30873117
.collect();
30883118

3119+
// There should be no response at all for grid requests when the
3120+
// backing threshold is no longer met as a result of disabled validators.
3121+
if !is_cluster {
3122+
let threshold = per_session
3123+
.groups
3124+
.get_size_and_backing_threshold(group_index)
3125+
.expect("group existence checked above; qed")
3126+
.1;
3127+
3128+
if !seconded_and_sufficient(&sent_filter, Some(threshold)) {
3129+
gum::info!(
3130+
target: LOG_TARGET,
3131+
?candidate_hash,
3132+
relay_parent = ?confirmed.relay_parent(),
3133+
?group_index,
3134+
"Dropping a request from a grid peer because the backing threshold is no longer met."
3135+
);
3136+
return
3137+
}
3138+
}
3139+
30893140
// Update bookkeeping about which statements peers have received.
30903141
for statement in &statements {
30913142
if is_cluster {

polkadot/node/network/statement-distribution/src/v2/requests.rs

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,12 @@
3030
//! (which requires state not owned by the request manager).
3131
3232
use super::{
33-
BENEFIT_VALID_RESPONSE, BENEFIT_VALID_STATEMENT, COST_DISABLED_VALIDATOR,
33+
seconded_and_sufficient, BENEFIT_VALID_RESPONSE, BENEFIT_VALID_STATEMENT,
3434
COST_IMPROPERLY_DECODED_RESPONSE, COST_INVALID_RESPONSE, COST_INVALID_SIGNATURE,
3535
COST_UNREQUESTED_RESPONSE_STATEMENT, REQUEST_RETRY_DELAY,
3636
};
3737
use crate::LOG_TARGET;
3838

39-
use bitvec::prelude::{BitVec, Lsb0};
4039
use polkadot_node_network_protocol::{
4140
request_response::{
4241
outgoing::{Recipient as RequestRecipient, RequestError},
@@ -496,10 +495,6 @@ fn find_request_target_with_update(
496495
}
497496
}
498497

499-
fn seconded_and_sufficient(filter: &StatementFilter, backing_threshold: Option<usize>) -> bool {
500-
backing_threshold.map_or(true, |t| filter.has_seconded() && filter.backing_validators() >= t)
501-
}
502-
503498
/// A response to a request, which has not yet been handled.
504499
pub struct UnhandledResponse {
505500
response: TaggedResponse,
@@ -529,7 +524,6 @@ impl UnhandledResponse {
529524
/// * If the response is partially valid, misbehavior by the responding peer.
530525
/// * If there are other peers which have advertised the same candidate for different
531526
/// relay-parents or para-ids, misbehavior reports for those peers will also be generated.
532-
/// * Statements from disabled validators are filtered out and reported as misbehavior.
533527
///
534528
/// Finally, in the case that the response is either valid or partially valid,
535529
/// this will clean up all remaining requests for the candidate in the manager.
@@ -544,7 +538,6 @@ impl UnhandledResponse {
544538
session: SessionIndex,
545539
validator_key_lookup: impl Fn(ValidatorIndex) -> Option<ValidatorId>,
546540
allowed_para_lookup: impl Fn(ParaId, GroupIndex) -> bool,
547-
disabled_mask: BitVec<u8, Lsb0>,
548541
) -> ResponseValidationOutput {
549542
let UnhandledResponse {
550543
response: TaggedResponse { identifier, requested_peer, props, response },
@@ -628,7 +621,6 @@ impl UnhandledResponse {
628621
session,
629622
validator_key_lookup,
630623
allowed_para_lookup,
631-
disabled_mask,
632624
);
633625

634626
if let CandidateRequestStatus::Complete { .. } = output.request_status {
@@ -648,11 +640,10 @@ fn validate_complete_response(
648640
session: SessionIndex,
649641
validator_key_lookup: impl Fn(ValidatorIndex) -> Option<ValidatorId>,
650642
allowed_para_lookup: impl Fn(ParaId, GroupIndex) -> bool,
651-
mut disabled_mask: BitVec<u8, Lsb0>,
652643
) -> ResponseValidationOutput {
653644
let RequestProperties { backing_threshold, mut unwanted_mask } = props;
654645

655-
// sanity check bitmasks size. this is based entirely on
646+
// sanity check bitmask size. this is based entirely on
656647
// local logic here.
657648
if !unwanted_mask.has_len(group.len()) {
658649
gum::error!(
@@ -665,16 +656,6 @@ fn validate_complete_response(
665656
unwanted_mask.seconded_in_group.resize(group.len(), true);
666657
unwanted_mask.validated_in_group.resize(group.len(), true);
667658
}
668-
if disabled_mask.len() != group.len() {
669-
gum::error!(
670-
target: LOG_TARGET,
671-
group_len = group.len(),
672-
"Logic bug: group size != disabled bitmask len"
673-
);
674-
675-
// resize and attempt to continue.
676-
disabled_mask.resize(group.len(), false);
677-
}
678659

679660
let invalid_candidate_output = || ResponseValidationOutput {
680661
request_status: CandidateRequestStatus::Incomplete,
@@ -752,11 +733,6 @@ fn validate_complete_response(
752733
rep_changes.push((requested_peer, COST_UNREQUESTED_RESPONSE_STATEMENT));
753734
continue
754735
}
755-
756-
if disabled_mask[i] {
757-
rep_changes.push((requested_peer, COST_DISABLED_VALIDATOR));
758-
continue
759-
}
760736
},
761737
CompactStatement::Valid(_) => {
762738
if unwanted_mask.validated_in_group[i] {
@@ -768,11 +744,6 @@ fn validate_complete_response(
768744
rep_changes.push((requested_peer, COST_UNREQUESTED_RESPONSE_STATEMENT));
769745
continue
770746
}
771-
772-
if disabled_mask[i] {
773-
rep_changes.push((requested_peer, COST_DISABLED_VALIDATOR));
774-
continue
775-
}
776747
},
777748
}
778749

@@ -1038,7 +1009,6 @@ mod tests {
10381009
let group = &[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)];
10391010

10401011
let unwanted_mask = StatementFilter::blank(group_size);
1041-
let disabled_mask = unwanted_mask.seconded_in_group.clone();
10421012
let request_properties = RequestProperties { unwanted_mask, backing_threshold: None };
10431013

10441014
// Get requests.
@@ -1082,7 +1052,6 @@ mod tests {
10821052
0,
10831053
validator_key_lookup,
10841054
allowed_para_lookup,
1085-
disabled_mask.clone(),
10861055
);
10871056
assert_eq!(
10881057
output,
@@ -1121,7 +1090,6 @@ mod tests {
11211090
0,
11221091
validator_key_lookup,
11231092
allowed_para_lookup,
1124-
disabled_mask,
11251093
);
11261094
assert_eq!(
11271095
output,
@@ -1161,7 +1129,6 @@ mod tests {
11611129
let group = &[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)];
11621130

11631131
let unwanted_mask = StatementFilter::blank(group_size);
1164-
let disabled_mask = unwanted_mask.seconded_in_group.clone();
11651132
let request_properties = RequestProperties { unwanted_mask, backing_threshold: None };
11661133
let peer_advertised =
11671134
|_identifier: &CandidateIdentifier, _peer: &_| Some(StatementFilter::full(group_size));
@@ -1202,7 +1169,6 @@ mod tests {
12021169
0,
12031170
validator_key_lookup,
12041171
allowed_para_lookup,
1205-
disabled_mask,
12061172
);
12071173
assert_eq!(
12081174
output,
@@ -1243,7 +1209,6 @@ mod tests {
12431209
let group = &[ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2)];
12441210

12451211
let unwanted_mask = StatementFilter::blank(group_size);
1246-
let disabled_mask = unwanted_mask.seconded_in_group.clone();
12471212
let request_properties = RequestProperties { unwanted_mask, backing_threshold: None };
12481213
let peer_advertised =
12491214
|_identifier: &CandidateIdentifier, _peer: &_| Some(StatementFilter::full(group_size));
@@ -1282,7 +1247,6 @@ mod tests {
12821247
0,
12831248
validator_key_lookup,
12841249
allowed_para_lookup,
1285-
disabled_mask,
12861250
);
12871251
assert_eq!(
12881252
output,

0 commit comments

Comments
 (0)