Skip to content

Commit ed54223

Browse files
paritytech-release-backport-bot[bot]sandreimgithub-actions[bot]EgorPopelyaev
authored
[stable2506] Backport #9564 (#9583)
Backport #9564 into `stable2506` from sandreim. See the [documentation](https://github.com/paritytech/polkadot-sdk/blob/master/docs/BACKPORT.md) on how to use this bot. <!-- # To be used by other automation, do not modify: original-pr-number: #${pull_number} --> Signed-off-by: Andrei Sandu <[email protected]> Co-authored-by: Andrei Sandu <[email protected]> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Egor_P <[email protected]>
1 parent 43425df commit ed54223

File tree

3 files changed

+133
-22
lines changed

3 files changed

+133
-22
lines changed

polkadot/runtime/parachains/src/paras_inherent/mod.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,16 +1328,24 @@ fn filter_backed_statements_from_disabled_validators<
13281328
// The indices of statements from disabled validators in `BackedCandidate`. We have to drop
13291329
// these.
13301330
let indices_to_drop = disabled_indices.clone() & &validator_indices;
1331-
// Apply the bitmask to drop the disabled validator from `validator_indices`
1332-
validator_indices &= !disabled_indices;
1333-
// Update the backed candidate
1334-
bc.set_validator_indices_and_core_index(validator_indices, maybe_injected_core_index);
13351331

13361332
// Remove the corresponding votes from `validity_votes`
13371333
for idx in indices_to_drop.iter_ones().rev() {
1338-
bc.validity_votes_mut().remove(idx);
1334+
// Map the index in `indices_to_drop` (which is an index into the validator group)
1335+
// to the index in the validity votes vector, which might have less number of votes,
1336+
// than validators assigned to the group.
1337+
//
1338+
// For each index `idx` in `indices_to_drop`, the corresponding index in the
1339+
// validity votes vector is the number of `1` bits in `validator_indices` before `idx`.
1340+
let mapped_idx = validator_indices[..idx].count_ones();
1341+
bc.validity_votes_mut().remove(mapped_idx);
13391342
}
13401343

1344+
// Apply the bitmask to drop the disabled validator from `validator_indices`
1345+
validator_indices &= !disabled_indices;
1346+
// Update the backed candidate
1347+
bc.set_validator_indices_and_core_index(validator_indices, maybe_injected_core_index);
1348+
13411349
// By filtering votes we might render the candidate invalid and cause a failure in
13421350
// [`process_candidates`]. To avoid this we have to perform a sanity check here. If there
13431351
// are not enough backing votes after filtering we will remove the whole candidate.

polkadot/runtime/parachains/src/paras_inherent/tests.rs

Lines changed: 113 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,7 +2602,7 @@ mod sanitizers {
26022602

26032603
// Generate test data for the candidates and assert that the environment is set as expected
26042604
// (check the comments for details)
2605-
fn get_test_data_one_core_per_para() -> TestData {
2605+
fn get_test_data_one_core_per_para(backing_kind: BackingKind) -> TestData {
26062606
const RELAY_PARENT_NUM: u32 = 3;
26072607

26082608
// Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing
@@ -2629,6 +2629,10 @@ mod sanitizers {
26292629
sp_keyring::Sr25519Keyring::Charlie,
26302630
sp_keyring::Sr25519Keyring::Dave,
26312631
sp_keyring::Sr25519Keyring::Eve,
2632+
sp_keyring::Sr25519Keyring::Ferdie,
2633+
sp_keyring::Sr25519Keyring::One,
2634+
sp_keyring::Sr25519Keyring::Two,
2635+
sp_keyring::Sr25519Keyring::AliceStash,
26322636
];
26332637
for validator in validators.iter() {
26342638
Keystore::sr25519_generate_new(
@@ -2657,8 +2661,8 @@ mod sanitizers {
26572661

26582662
// Set the validator groups in `scheduler`
26592663
scheduler::Pallet::<Test>::set_validator_groups(vec![
2660-
vec![ValidatorIndex(0), ValidatorIndex(1)],
2661-
vec![ValidatorIndex(2), ValidatorIndex(3)],
2664+
vec![ValidatorIndex(0), ValidatorIndex(1), ValidatorIndex(2), ValidatorIndex(3)],
2665+
vec![ValidatorIndex(4), ValidatorIndex(5), ValidatorIndex(6), ValidatorIndex(7)],
26622666
]);
26632667

26642668
// Update scheduler's claimqueue with the parachains
@@ -2713,8 +2717,8 @@ mod sanitizers {
27132717
// Callback used for backing candidates
27142718
let group_validators = |group_index: GroupIndex| {
27152719
match group_index {
2716-
group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]),
2717-
group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]),
2720+
group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1, 2, 3]),
2721+
group_index if group_index == GroupIndex::from(1) => Some(vec![4, 5, 6, 7]),
27182722
_ => panic!("Group index out of bounds"),
27192723
}
27202724
.map(|m| m.into_iter().map(ValidatorIndex).collect::<Vec<_>>())
@@ -2748,7 +2752,7 @@ mod sanitizers {
27482752
group_validators(GroupIndex::from(idx0 as u32)).unwrap().as_ref(),
27492753
&keystore,
27502754
&signing_context,
2751-
BackingKind::Threshold,
2755+
backing_kind,
27522756
CoreIndex(idx0 as u32),
27532757
);
27542758
backed
@@ -2767,7 +2771,11 @@ mod sanitizers {
27672771
ValidatorIndex(1),
27682772
ValidatorIndex(2),
27692773
ValidatorIndex(3),
2770-
ValidatorIndex(4)
2774+
ValidatorIndex(4),
2775+
ValidatorIndex(5),
2776+
ValidatorIndex(6),
2777+
ValidatorIndex(7),
2778+
ValidatorIndex(8),
27712779
]
27722780
);
27732781

@@ -4218,7 +4226,7 @@ mod sanitizers {
42184226
backed_candidates,
42194227
expected_backed_candidates_with_core,
42204228
scheduled_paras: scheduled,
4221-
} = get_test_data_one_core_per_para();
4229+
} = get_test_data_one_core_per_para(BackingKind::Threshold);
42224230

42234231
assert_eq!(
42244232
sanitize_backed_candidates::<Test>(
@@ -4430,7 +4438,7 @@ mod sanitizers {
44304438
let TestData { backed_candidates, .. } = if multiple_cores_per_para {
44314439
get_test_data_multiple_cores_per_para(v2_descriptor)
44324440
} else {
4433-
get_test_data_one_core_per_para()
4441+
get_test_data_one_core_per_para(BackingKind::Threshold)
44344442
};
44354443
let scheduled = BTreeMap::new();
44364444

@@ -4451,7 +4459,7 @@ mod sanitizers {
44514459
fn concluded_invalid_are_filtered_out_single_core_per_para() {
44524460
new_test_ext(default_config()).execute_with(|| {
44534461
let TestData { backed_candidates, scheduled_paras: scheduled, .. } =
4454-
get_test_data_one_core_per_para();
4462+
get_test_data_one_core_per_para(BackingKind::Threshold);
44554463

44564464
// mark every second one as concluded invalid
44574465
let set = {
@@ -4564,15 +4572,15 @@ mod sanitizers {
45644572
fn disabled_non_signing_validator_doesnt_get_filtered() {
45654573
new_test_ext(default_config()).execute_with(|| {
45664574
let TestData { mut expected_backed_candidates_with_core, .. } =
4567-
get_test_data_one_core_per_para();
4575+
get_test_data_one_core_per_para(BackingKind::Threshold);
45684576

4569-
// Disable Eve
4570-
set_disabled_validators(vec![4]);
4577+
// Disable `AliceStash`
4578+
set_disabled_validators(vec![7]);
45714579

45724580
let before = expected_backed_candidates_with_core.clone();
45734581

4574-
// Eve is disabled but no backing statement is signed by it so nothing should be
4575-
// filtered
4582+
// AliceStash is disabled but no backing statement is signed by it so nothing should
4583+
// be filtered
45764584
filter_backed_statements_from_disabled_validators::<Test>(
45774585
&mut expected_backed_candidates_with_core,
45784586
&shared::AllowedRelayParents::<Test>::get(),
@@ -4585,7 +4593,7 @@ mod sanitizers {
45854593
fn drop_statements_from_disabled_without_dropping_candidate() {
45864594
new_test_ext(default_config()).execute_with(|| {
45874595
let TestData { mut expected_backed_candidates_with_core, .. } =
4588-
get_test_data_one_core_per_para();
4596+
get_test_data_one_core_per_para(BackingKind::Threshold);
45894597

45904598
// Disable Alice
45914599
set_disabled_validators(vec![0]);
@@ -4686,7 +4694,7 @@ mod sanitizers {
46864694
fn drop_candidate_if_all_statements_are_from_disabled_single_core_per_para() {
46874695
new_test_ext(default_config()).execute_with(|| {
46884696
let TestData { mut expected_backed_candidates_with_core, .. } =
4689-
get_test_data_one_core_per_para();
4697+
get_test_data_one_core_per_para(BackingKind::Threshold);
46904698

46914699
// Disable Alice and Bob
46924700
set_disabled_validators(vec![0, 1]);
@@ -4776,5 +4784,93 @@ mod sanitizers {
47764784
});
47774785
}
47784786
}
4787+
4788+
// Disable Dave which is 4th validator in group for core 0.
4789+
// Para 0 candidate has a single valid vote that is removed
4790+
// because Dave is disabled.
4791+
//
4792+
// This test ensures we remove the right validity vote from the backed candidate.
4793+
#[test]
4794+
fn drop_right_vote_basic() {
4795+
new_test_ext(default_config()).execute_with(|| {
4796+
let TestData { mut expected_backed_candidates_with_core, .. } =
4797+
get_test_data_one_core_per_para(BackingKind::Unanimous);
4798+
4799+
set_disabled_validators(vec![3]);
4800+
4801+
let (backed, _) = expected_backed_candidates_with_core
4802+
.get_mut(&ParaId::from(1))
4803+
.unwrap()
4804+
.first_mut()
4805+
.unwrap();
4806+
let (indices, core) = backed.validator_indices_and_core_index();
4807+
let mut indices = BitVec::<_>::from(indices);
4808+
4809+
indices.set(0, false);
4810+
indices.set(1, false);
4811+
backed.validity_votes_mut().remove(0);
4812+
backed.validity_votes_mut().remove(1);
4813+
4814+
backed.set_validator_indices_and_core_index(indices, core);
4815+
4816+
let mut untouched = expected_backed_candidates_with_core.clone();
4817+
4818+
filter_backed_statements_from_disabled_validators::<Test>(
4819+
&mut expected_backed_candidates_with_core,
4820+
&shared::AllowedRelayParents::<Test>::get(),
4821+
);
4822+
4823+
untouched.remove(&ParaId::from(1)).unwrap();
4824+
4825+
assert_eq!(expected_backed_candidates_with_core, untouched);
4826+
});
4827+
}
4828+
4829+
// Disable Bob which is second validator in group for core 0.
4830+
//
4831+
// This test ensures we remove the right validty votes from the backed candidate,
4832+
// and calls `process_candidates` that will check the vote signatures.
4833+
// Will fail if we remove the wrong vote.
4834+
#[test]
4835+
fn drop_right_vote_and_process_candidates() {
4836+
new_test_ext(default_config()).execute_with(|| {
4837+
let TestData { mut expected_backed_candidates_with_core, .. } =
4838+
get_test_data_one_core_per_para(BackingKind::Unanimous);
4839+
4840+
// Second validator in group is disabled.
4841+
set_disabled_validators(vec![1]);
4842+
4843+
let (backed, _) = expected_backed_candidates_with_core
4844+
.get_mut(&ParaId::from(1))
4845+
.unwrap()
4846+
.first_mut()
4847+
.unwrap();
4848+
let (indices, core) = backed.validator_indices_and_core_index();
4849+
let mut indices = BitVec::<_>::from(indices);
4850+
4851+
// First validator did not provide vote.
4852+
indices.set(0, false);
4853+
backed.validity_votes_mut().remove(0);
4854+
backed.set_validator_indices_and_core_index(indices, core);
4855+
4856+
let untouched = expected_backed_candidates_with_core.clone();
4857+
4858+
filter_backed_statements_from_disabled_validators::<Test>(
4859+
&mut expected_backed_candidates_with_core,
4860+
&shared::AllowedRelayParents::<Test>::get(),
4861+
);
4862+
4863+
let candidate_receipt_with_backing_validator_indices =
4864+
inclusion::Pallet::<Test>::process_candidates(
4865+
&shared::AllowedRelayParents::<Test>::get(),
4866+
&expected_backed_candidates_with_core,
4867+
scheduler::Pallet::<Test>::group_validators,
4868+
)
4869+
.unwrap();
4870+
4871+
// No candidate was dropped.
4872+
assert_eq!(candidate_receipt_with_backing_validator_indices.len(), untouched.len());
4873+
});
4874+
}
47794875
}
47804876
}

prdoc/pr_9564.prdoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
title: 'Fix disabled validator filtering in the parachains runtime'
2+
doc:
3+
- audience: Runtime Dev
4+
description: Correctly map group indices to vote indices when filtering backing statements.
5+
crates:
6+
- name: polkadot-runtime-parachains
7+
bump: patch

0 commit comments

Comments
 (0)