Skip to content

Commit decd6c8

Browse files
paritytech-release-backport-bot[bot]alexgghgithub-actions[bot]
authored
[stable2412] Backport #10082 (#10084)
Backport #10082 into `stable2412` 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 <alexandru.gheorghe@parity.io> Co-authored-by: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent cd93ba6 commit decd6c8

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
@@ -688,6 +688,8 @@ enum InvalidAssignmentError {
688688
enum InvalidVoteError {
689689
// The candidate index was out of bounds.
690690
CandidateIndexOutOfBounds,
691+
// The candidate hash was not found in the block's candidate list.
692+
CandidateHashNotFound,
691693
// The validator index was out of bounds.
692694
ValidatorIndexOutOfBounds,
693695
// The signature of the vote was invalid.
@@ -2077,7 +2079,7 @@ impl State {
20772079
))
20782080
.check_signature(
20792081
&pubkey,
2080-
*candidate_hashes.first().unwrap(),
2082+
*candidate_hashes.first().ok_or(InvalidVoteError::CandidateHashNotFound)?,
20812083
entry.session,
20822084
&vote.signature,
20832085
)
@@ -2515,10 +2517,22 @@ impl State {
25152517
) -> Vec<IndirectSignedApprovalVoteV2> {
25162518
let mut sanitized_approvals = Vec::new();
25172519
for approval in approval.into_iter() {
2518-
if approval.candidate_indices.len() as usize > MAX_BITFIELD_SIZE {
2520+
let has_no_approved_candidates = approval.candidate_indices.first_one().is_none();
2521+
if approval.candidate_indices.len() as usize > MAX_BITFIELD_SIZE ||
2522+
has_no_approved_candidates
2523+
{
25192524
// Punish the peer for the invalid message.
2520-
modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD)
2521-
.await;
2525+
modify_reputation(
2526+
&mut self.reputation,
2527+
sender,
2528+
peer_id,
2529+
if has_no_approved_candidates {
2530+
COST_INVALID_MESSAGE
2531+
} else {
2532+
COST_OVERSIZED_BITFIELD
2533+
},
2534+
)
2535+
.await;
25222536
gum::debug!(
25232537
target: LOG_TARGET,
25242538
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
@@ -4845,3 +4845,94 @@ fn subsystem_accepts_tranche0_duplicate_assignments() {
48454845
},
48464846
);
48474847
}
4848+
4849+
#[test]
4850+
fn test_empty_bitfield_gets_rejected_early() {
4851+
let peers = make_peers_and_authority_ids(15);
4852+
let peer_a = peers.get(0).unwrap().0;
4853+
let parent_hash = Hash::repeat_byte(0xFF);
4854+
let hash = Hash::repeat_byte(0xAA);
4855+
let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB));
4856+
4857+
let _ = test_harness(
4858+
Arc::new(MockAssignmentCriteria { tranche: Ok(0) }),
4859+
Arc::new(SystemClock {}),
4860+
state_without_reputation_delay(),
4861+
|mut virtual_overseer| async move {
4862+
let overseer = &mut virtual_overseer;
4863+
4864+
// Setup peer
4865+
setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await;
4866+
4867+
let mut keystore = LocalKeystore::in_memory();
4868+
let session = dummy_session_info_valid(1, &mut keystore, 1);
4869+
4870+
// Setup block with one candidate
4871+
let meta = BlockApprovalMeta {
4872+
hash,
4873+
parent_hash,
4874+
number: 1,
4875+
candidates: vec![(candidate_hash, 0.into(), 0.into())],
4876+
slot: 1.into(),
4877+
session: 1,
4878+
vrf_story: RelayVRFStory(Default::default()),
4879+
};
4880+
overseer_send(overseer, ApprovalDistributionMessage::NewBlocks(vec![meta])).await;
4881+
4882+
// Setup gossip topology
4883+
let peers_with_optional_peer_id = peers
4884+
.iter()
4885+
.map(|(peer_id, authority)| (Some(*peer_id), authority.clone()))
4886+
.collect_vec();
4887+
setup_gossip_topology(
4888+
overseer,
4889+
make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1),
4890+
)
4891+
.await;
4892+
4893+
// Send assignment first
4894+
let validator_index = ValidatorIndex(0);
4895+
let candidate_index = 0u32;
4896+
let cert = fake_assignment_cert_v2(hash, validator_index, CoreIndex(0).into());
4897+
let assignments = vec![(cert.clone(), candidate_index.into())];
4898+
let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments);
4899+
send_message_from_peer_v3(overseer, &peer_a, msg).await;
4900+
provide_session(overseer, session.clone()).await;
4901+
4902+
// Should receive the assignment
4903+
assert_matches!(
4904+
overseer_recv(overseer).await,
4905+
AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment(_, _))
4906+
);
4907+
expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await;
4908+
4909+
// Create an approval with empty candidate_indices is rejected early
4910+
let mut candidate_indices: CandidateBitfield = vec![0].try_into().unwrap();
4911+
candidate_indices.inner_mut().clear();
4912+
4913+
let normal_approval = IndirectSignedApprovalVoteV2 {
4914+
block_hash: hash,
4915+
candidate_indices: candidate_indices.clone(),
4916+
validator: validator_index,
4917+
signature: signature_for(
4918+
&keystore,
4919+
&session,
4920+
vec![candidate_hash],
4921+
validator_index,
4922+
),
4923+
};
4924+
4925+
let approval_to_send = normal_approval;
4926+
4927+
// Send the approval
4928+
let msg =
4929+
protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval_to_send.clone()]);
4930+
send_message_from_peer_v3(overseer, &peer_a, msg).await;
4931+
4932+
// Expect rejection due to invalid message
4933+
expect_reputation_change(overseer, &peer_a, COST_INVALID_MESSAGE).await;
4934+
4935+
virtual_overseer
4936+
},
4937+
);
4938+
}

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)