Skip to content

Commit 39373d7

Browse files
paritytech-release-backport-bot[bot]alexgghgithub-actions[bot]
authored
[stable2509] Backport #10082 (#10087)
Backport #10082 into `stable2509` from alexggh. 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: Alexandru Gheorghe <[email protected]> Co-authored-by: Alexandru Gheorghe <[email protected]> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent f5e2d9e commit 39373d7

File tree

4 files changed

+118
-5
lines changed

4 files changed

+118
-5
lines changed

polkadot/node/network/approval-distribution/src/lib.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,8 @@ enum InvalidAssignmentError {
684684
enum InvalidVoteError {
685685
// The candidate index was out of bounds.
686686
CandidateIndexOutOfBounds,
687+
// The candidate hash was not found in the block's candidate list.
688+
CandidateHashNotFound,
687689
// The validator index was out of bounds.
688690
ValidatorIndexOutOfBounds,
689691
// The signature of the vote was invalid.
@@ -2032,7 +2034,7 @@ impl State {
20322034
))
20332035
.check_signature(
20342036
&pubkey,
2035-
*candidate_hashes.first().unwrap(),
2037+
*candidate_hashes.first().ok_or(InvalidVoteError::CandidateHashNotFound)?,
20362038
entry.session,
20372039
&vote.signature,
20382040
)
@@ -2408,10 +2410,22 @@ impl State {
24082410
) -> Vec<IndirectSignedApprovalVoteV2> {
24092411
let mut sanitized_approvals = Vec::new();
24102412
for approval in approval.into_iter() {
2411-
if approval.candidate_indices.len() as usize > MAX_BITFIELD_SIZE {
2413+
let has_no_approved_candidates = approval.candidate_indices.first_one().is_none();
2414+
if approval.candidate_indices.len() as usize > MAX_BITFIELD_SIZE ||
2415+
has_no_approved_candidates
2416+
{
24122417
// Punish the peer for the invalid message.
2413-
modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD)
2414-
.await;
2418+
modify_reputation(
2419+
&mut self.reputation,
2420+
sender,
2421+
peer_id,
2422+
if has_no_approved_candidates {
2423+
COST_INVALID_MESSAGE
2424+
} else {
2425+
COST_OVERSIZED_BITFIELD
2426+
},
2427+
)
2428+
.await;
24152429
gum::debug!(
24162430
target: LOG_TARGET,
24172431
block_hash = ?approval.block_hash,

polkadot/node/network/approval-distribution/src/tests.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4446,3 +4446,94 @@ fn subsystem_accepts_tranche0_duplicate_assignments() {
44464446
},
44474447
);
44484448
}
4449+
4450+
#[test]
4451+
fn test_empty_bitfield_gets_rejected_early() {
4452+
let peers = make_peers_and_authority_ids(15);
4453+
let peer_a = peers.get(0).unwrap().0;
4454+
let parent_hash = Hash::repeat_byte(0xFF);
4455+
let hash = Hash::repeat_byte(0xAA);
4456+
let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB));
4457+
4458+
let _ = test_harness(
4459+
Arc::new(MockAssignmentCriteria { tranche: Ok(0) }),
4460+
Arc::new(SystemClock {}),
4461+
state_without_reputation_delay(),
4462+
|mut virtual_overseer| async move {
4463+
let overseer = &mut virtual_overseer;
4464+
4465+
// Setup peer
4466+
setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await;
4467+
4468+
let mut keystore = LocalKeystore::in_memory();
4469+
let session = dummy_session_info_valid(1, &mut keystore, 1);
4470+
4471+
// Setup block with one candidate
4472+
let meta = BlockApprovalMeta {
4473+
hash,
4474+
parent_hash,
4475+
number: 1,
4476+
candidates: vec![(candidate_hash, 0.into(), 0.into())],
4477+
slot: 1.into(),
4478+
session: 1,
4479+
vrf_story: RelayVRFStory(Default::default()),
4480+
};
4481+
overseer_send(overseer, ApprovalDistributionMessage::NewBlocks(vec![meta])).await;
4482+
4483+
// Setup gossip topology
4484+
let peers_with_optional_peer_id = peers
4485+
.iter()
4486+
.map(|(peer_id, authority)| (Some(*peer_id), authority.clone()))
4487+
.collect_vec();
4488+
setup_gossip_topology(
4489+
overseer,
4490+
make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1),
4491+
)
4492+
.await;
4493+
4494+
// Send assignment first
4495+
let validator_index = ValidatorIndex(0);
4496+
let candidate_index = 0u32;
4497+
let cert = fake_assignment_cert_v2(hash, validator_index, CoreIndex(0).into());
4498+
let assignments = vec![(cert.clone(), candidate_index.into())];
4499+
let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments);
4500+
send_message_from_peer_v3(overseer, &peer_a, msg).await;
4501+
provide_session(overseer, session.clone()).await;
4502+
4503+
// Should receive the assignment
4504+
assert_matches!(
4505+
overseer_recv(overseer).await,
4506+
AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment(_, _))
4507+
);
4508+
expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await;
4509+
4510+
// Create an approval with empty candidate_indices is rejected early
4511+
let mut candidate_indices: CandidateBitfield = vec![0].try_into().unwrap();
4512+
candidate_indices.inner_mut().clear();
4513+
4514+
let normal_approval = IndirectSignedApprovalVoteV2 {
4515+
block_hash: hash,
4516+
candidate_indices: candidate_indices.clone(),
4517+
validator: validator_index,
4518+
signature: signature_for(
4519+
&keystore,
4520+
&session,
4521+
vec![candidate_hash],
4522+
validator_index,
4523+
),
4524+
};
4525+
4526+
let approval_to_send = normal_approval;
4527+
4528+
// Send the approval
4529+
let msg =
4530+
protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval_to_send.clone()]);
4531+
send_message_from_peer_v3(overseer, &peer_a, msg).await;
4532+
4533+
// Expect rejection due to invalid message
4534+
expect_reputation_change(overseer, &peer_a, COST_INVALID_MESSAGE).await;
4535+
4536+
virtual_overseer
4537+
},
4538+
);
4539+
}

polkadot/node/primitives/src/approval/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ pub mod v2 {
296296
}
297297

298298
/// For testing purpose, we want a inner mutable ref.
299-
#[cfg(test)]
300299
pub fn inner_mut(&mut self) -> &mut BitVec<u8, bitvec::order::Lsb0> {
301300
&mut self.0
302301
}

prdoc/pr_10082.prdoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
title: 'approval-distribution: improve test coverage'
2+
doc:
3+
- audience: Node Dev
4+
description: Add few more test in approval-distribution to improve coverage.
5+
crates:
6+
- name: polkadot-approval-distribution
7+
bump: patch
8+
- name: polkadot-node-primitives
9+
bump: minor

0 commit comments

Comments
 (0)