@@ -787,3 +787,134 @@ fn prospective_parachains_reject_candidate() {
787
787
virtual_overseer
788
788
} ) ;
789
789
}
790
+
791
+ // Test that a validator can second multiple candidates per single relay parent.
792
+ #[ test]
793
+ fn second_multiple_candidates_per_relay_parent ( ) {
794
+ let test_state = TestState :: default ( ) ;
795
+ test_harness ( test_state. keystore . clone ( ) , |mut virtual_overseer| async move {
796
+ // Candidate `a` is seconded in a parent of the activated `leaf`.
797
+ const LEAF_BLOCK_NUMBER : BlockNumber = 100 ;
798
+ const LEAF_DEPTH : BlockNumber = 3 ;
799
+ let para_id = test_state. chain_ids [ 0 ] ;
800
+
801
+ let leaf_hash = Hash :: from_low_u64_be ( 130 ) ;
802
+ let leaf_parent = get_parent_hash ( leaf_hash) ;
803
+ let leaf_grandparent = get_parent_hash ( leaf_parent) ;
804
+ let activated = ActivatedLeaf {
805
+ hash : leaf_hash,
806
+ number : LEAF_BLOCK_NUMBER ,
807
+ status : LeafStatus :: Fresh ,
808
+ span : Arc :: new ( jaeger:: Span :: Disabled ) ,
809
+ } ;
810
+ let min_relay_parents = vec ! [ ( para_id, LEAF_BLOCK_NUMBER - LEAF_DEPTH ) ] ;
811
+ let test_leaf_a = TestLeaf { activated, min_relay_parents } ;
812
+
813
+ activate_leaf ( & mut virtual_overseer, test_leaf_a, & test_state, 0 ) . await ;
814
+
815
+ let pov = PoV { block_data : BlockData ( vec ! [ 42 , 43 , 44 ] ) } ;
816
+ let pvd = dummy_pvd ( ) ;
817
+ let validation_code = ValidationCode ( vec ! [ 1 , 2 , 3 ] ) ;
818
+
819
+ let expected_head_data = test_state. head_data . get ( & para_id) . unwrap ( ) ;
820
+
821
+ let pov_hash = pov. hash ( ) ;
822
+ let candidate_a = TestCandidateBuilder {
823
+ para_id,
824
+ relay_parent : leaf_parent,
825
+ pov_hash,
826
+ head_data : expected_head_data. clone ( ) ,
827
+ erasure_root : make_erasure_root ( & test_state, pov. clone ( ) ) ,
828
+ persisted_validation_data_hash : pvd. hash ( ) ,
829
+ validation_code : validation_code. 0 . clone ( ) ,
830
+ ..Default :: default ( )
831
+ } ;
832
+ let mut candidate_b = candidate_a. clone ( ) ;
833
+ candidate_b. relay_parent = leaf_grandparent;
834
+
835
+ // With depths.
836
+ let candidate_a = ( candidate_a. build ( ) , 1 ) ;
837
+ let candidate_b = ( candidate_b. build ( ) , 2 ) ;
838
+
839
+ for candidate in & [ candidate_a, candidate_b] {
840
+ let ( candidate, depth) = candidate;
841
+ let second = CandidateBackingMessage :: Second (
842
+ leaf_hash,
843
+ candidate. to_plain ( ) ,
844
+ pvd. clone ( ) ,
845
+ pov. clone ( ) ,
846
+ ) ;
847
+
848
+ virtual_overseer. send ( FromOverseer :: Communication { msg : second } ) . await ;
849
+
850
+ assert_validate_seconded_candidate (
851
+ & mut virtual_overseer,
852
+ candidate. descriptor ( ) . relay_parent ,
853
+ & candidate,
854
+ & pov,
855
+ & pvd,
856
+ & validation_code,
857
+ expected_head_data,
858
+ )
859
+ . await ;
860
+
861
+ // `seconding_sanity_check`
862
+ let expected_request_a = vec ! [ (
863
+ HypotheticalDepthRequest {
864
+ candidate_hash: candidate. hash( ) ,
865
+ candidate_para: para_id,
866
+ parent_head_data_hash: pvd. parent_head. hash( ) ,
867
+ candidate_relay_parent: candidate. descriptor( ) . relay_parent,
868
+ fragment_tree_relay_parent: leaf_hash,
869
+ } ,
870
+ vec![ * depth] ,
871
+ ) ] ;
872
+ assert_hypothetical_depth_requests ( & mut virtual_overseer, expected_request_a. clone ( ) )
873
+ . await ;
874
+
875
+ // Prospective parachains are notified.
876
+ assert_matches ! (
877
+ virtual_overseer. recv( ) . await ,
878
+ AllMessages :: ProspectiveParachains (
879
+ ProspectiveParachainsMessage :: CandidateSeconded (
880
+ candidate_para,
881
+ candidate_receipt,
882
+ _pvd,
883
+ tx,
884
+ ) ,
885
+ ) if & candidate_receipt == candidate && candidate_para == para_id && pvd == _pvd => {
886
+ // Any non-empty response will do.
887
+ tx. send( vec![ ( leaf_hash, vec![ 0 , 2 , 3 ] ) ] ) . unwrap( ) ;
888
+ }
889
+ ) ;
890
+
891
+ test_dispute_coordinator_notifications (
892
+ & mut virtual_overseer,
893
+ candidate. hash ( ) ,
894
+ test_state. session ( ) ,
895
+ vec ! [ ValidatorIndex ( 0 ) ] ,
896
+ )
897
+ . await ;
898
+
899
+ assert_matches ! (
900
+ virtual_overseer. recv( ) . await ,
901
+ AllMessages :: StatementDistribution (
902
+ StatementDistributionMessage :: Share (
903
+ parent_hash,
904
+ _signed_statement,
905
+ )
906
+ ) if parent_hash == candidate. descriptor( ) . relay_parent => { }
907
+ ) ;
908
+
909
+ assert_matches ! (
910
+ virtual_overseer. recv( ) . await ,
911
+ AllMessages :: CollatorProtocol ( CollatorProtocolMessage :: Seconded ( hash, statement) ) => {
912
+ assert_eq!( candidate. descriptor( ) . relay_parent, hash) ;
913
+ assert_matches!( statement. payload( ) , Statement :: Seconded ( _) ) ;
914
+ }
915
+ ) ;
916
+ }
917
+
918
+ virtual_overseer
919
+ } ) ;
920
+ }
0 commit comments