@@ -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+ }
0 commit comments