@@ -631,3 +631,166 @@ fn pov_decompression_failure_is_invalid() {
631631
632632 assert_matches ! ( v, Ok ( ValidationResult :: Invalid ( InvalidCandidate :: PoVDecompressionFailure ) ) ) ;
633633}
634+
635+ struct MockPreCheckBackend {
636+ result : Result < ( ) , PrepareError > ,
637+ }
638+
639+ impl MockPreCheckBackend {
640+ fn with_hardcoded_result ( result : Result < ( ) , PrepareError > ) -> Self {
641+ Self { result }
642+ }
643+ }
644+
645+ #[ async_trait]
646+ impl ValidationBackend for MockPreCheckBackend {
647+ async fn validate_candidate (
648+ & mut self ,
649+ _raw_validation_code : Vec < u8 > ,
650+ _timeout : Duration ,
651+ _params : ValidationParams ,
652+ ) -> Result < WasmValidationResult , ValidationError > {
653+ unreachable ! ( )
654+ }
655+
656+ async fn precheck_pvf ( & mut self , _pvf : Pvf ) -> Result < ( ) , PrepareError > {
657+ self . result . clone ( )
658+ }
659+ }
660+
661+ #[ test]
662+ fn precheck_works ( ) {
663+ let relay_parent = [ 3 ; 32 ] . into ( ) ;
664+ let validation_code = ValidationCode ( vec ! [ 3 ; 16 ] ) ;
665+ let validation_code_hash = validation_code. hash ( ) ;
666+
667+ let pool = TaskExecutor :: new ( ) ;
668+ let ( mut ctx, mut ctx_handle) =
669+ test_helpers:: make_subsystem_context :: < AllMessages , _ > ( pool. clone ( ) ) ;
670+
671+ let ( check_fut, check_result) = precheck_pvf (
672+ ctx. sender ( ) ,
673+ MockPreCheckBackend :: with_hardcoded_result ( Ok ( ( ) ) ) ,
674+ relay_parent,
675+ validation_code_hash,
676+ )
677+ . remote_handle ( ) ;
678+
679+ let test_fut = async move {
680+ assert_matches ! (
681+ ctx_handle. recv( ) . await ,
682+ AllMessages :: RuntimeApi ( RuntimeApiMessage :: Request (
683+ rp,
684+ RuntimeApiRequest :: ValidationCodeByHash (
685+ vch,
686+ tx
687+ ) ,
688+ ) ) => {
689+ assert_eq!( vch, validation_code_hash) ;
690+ assert_eq!( rp, relay_parent) ;
691+
692+ let _ = tx. send( Ok ( Some ( validation_code. clone( ) ) ) ) ;
693+ }
694+ ) ;
695+ assert_matches ! ( check_result. await , PreCheckOutcome :: Valid ) ;
696+ } ;
697+
698+ let test_fut = future:: join ( test_fut, check_fut) ;
699+ executor:: block_on ( test_fut) ;
700+ }
701+
702+ #[ test]
703+ fn precheck_invalid_pvf_blob_compression ( ) {
704+ let relay_parent = [ 3 ; 32 ] . into ( ) ;
705+
706+ let raw_code = vec ! [ 2u8 ; VALIDATION_CODE_BOMB_LIMIT + 1 ] ;
707+ let validation_code =
708+ sp_maybe_compressed_blob:: compress ( & raw_code, VALIDATION_CODE_BOMB_LIMIT + 1 )
709+ . map ( ValidationCode )
710+ . unwrap ( ) ;
711+ let validation_code_hash = validation_code. hash ( ) ;
712+
713+ let pool = TaskExecutor :: new ( ) ;
714+ let ( mut ctx, mut ctx_handle) =
715+ test_helpers:: make_subsystem_context :: < AllMessages , _ > ( pool. clone ( ) ) ;
716+
717+ let ( check_fut, check_result) = precheck_pvf (
718+ ctx. sender ( ) ,
719+ MockPreCheckBackend :: with_hardcoded_result ( Ok ( ( ) ) ) ,
720+ relay_parent,
721+ validation_code_hash,
722+ )
723+ . remote_handle ( ) ;
724+
725+ let test_fut = async move {
726+ assert_matches ! (
727+ ctx_handle. recv( ) . await ,
728+ AllMessages :: RuntimeApi ( RuntimeApiMessage :: Request (
729+ rp,
730+ RuntimeApiRequest :: ValidationCodeByHash (
731+ vch,
732+ tx
733+ ) ,
734+ ) ) => {
735+ assert_eq!( vch, validation_code_hash) ;
736+ assert_eq!( rp, relay_parent) ;
737+
738+ let _ = tx. send( Ok ( Some ( validation_code. clone( ) ) ) ) ;
739+ }
740+ ) ;
741+ assert_matches ! ( check_result. await , PreCheckOutcome :: Invalid ) ;
742+ } ;
743+
744+ let test_fut = future:: join ( test_fut, check_fut) ;
745+ executor:: block_on ( test_fut) ;
746+ }
747+
748+ #[ test]
749+ fn precheck_properly_classifies_outcomes ( ) {
750+ let inner = |prepare_result, precheck_outcome| {
751+ let relay_parent = [ 3 ; 32 ] . into ( ) ;
752+ let validation_code = ValidationCode ( vec ! [ 3 ; 16 ] ) ;
753+ let validation_code_hash = validation_code. hash ( ) ;
754+
755+ let pool = TaskExecutor :: new ( ) ;
756+ let ( mut ctx, mut ctx_handle) =
757+ test_helpers:: make_subsystem_context :: < AllMessages , _ > ( pool. clone ( ) ) ;
758+
759+ let ( check_fut, check_result) = precheck_pvf (
760+ ctx. sender ( ) ,
761+ MockPreCheckBackend :: with_hardcoded_result ( prepare_result) ,
762+ relay_parent,
763+ validation_code_hash,
764+ )
765+ . remote_handle ( ) ;
766+
767+ let test_fut = async move {
768+ assert_matches ! (
769+ ctx_handle. recv( ) . await ,
770+ AllMessages :: RuntimeApi ( RuntimeApiMessage :: Request (
771+ rp,
772+ RuntimeApiRequest :: ValidationCodeByHash (
773+ vch,
774+ tx
775+ ) ,
776+ ) ) => {
777+ assert_eq!( vch, validation_code_hash) ;
778+ assert_eq!( rp, relay_parent) ;
779+
780+ let _ = tx. send( Ok ( Some ( validation_code. clone( ) ) ) ) ;
781+ }
782+ ) ;
783+ assert_eq ! ( check_result. await , precheck_outcome) ;
784+ } ;
785+
786+ let test_fut = future:: join ( test_fut, check_fut) ;
787+ executor:: block_on ( test_fut) ;
788+ } ;
789+
790+ inner ( Err ( PrepareError :: Prevalidation ( "foo" . to_owned ( ) ) ) , PreCheckOutcome :: Invalid ) ;
791+ inner ( Err ( PrepareError :: Preparation ( "bar" . to_owned ( ) ) ) , PreCheckOutcome :: Invalid ) ;
792+ inner ( Err ( PrepareError :: Panic ( "baz" . to_owned ( ) ) ) , PreCheckOutcome :: Invalid ) ;
793+
794+ inner ( Err ( PrepareError :: TimedOut ) , PreCheckOutcome :: Failed ) ;
795+ inner ( Err ( PrepareError :: DidNotMakeIt ) , PreCheckOutcome :: Failed ) ;
796+ }
0 commit comments