@@ -190,7 +190,14 @@ mod tests {
190190
191191 mod contests_requests_query {
192192 use super :: * ;
193+ use crate :: config:: PlatformConfig ;
193194 use dapi_grpc:: platform:: v0:: get_contested_resources_request:: get_contested_resources_request_v0;
195+ use dpp:: dashcore:: Network ;
196+ use dpp:: data_contract:: TokenConfiguration ;
197+ use dpp:: prelude:: CoreBlockHeight ;
198+ use drive:: config:: DEFAULT_QUERY_LIMIT ;
199+ use drive_proof_verifier:: types:: { ContestedResources , VotePollsGroupedByTimestamp } ;
200+ use drive_proof_verifier:: { ContextProvider , ContextProviderError , FromProof } ;
194201
195202 #[ tokio:: test]
196203 async fn test_not_proved_contests_request ( ) {
@@ -393,6 +400,234 @@ mod tests {
393400 assert_eq ! ( contests. len( ) , 2 ) ;
394401 }
395402
403+ /// Serves the DPNS contract and no quorum keys, so `FromProof` checks the Drive part
404+ /// of a proof and then stops at the quorum key the signature check asks for
405+ struct DpnsOnlyContextProvider ( Arc < DataContract > ) ;
406+
407+ impl ContextProvider for DpnsOnlyContextProvider {
408+ fn get_data_contract (
409+ & self ,
410+ id : & Identifier ,
411+ _platform_version : & PlatformVersion ,
412+ ) -> Result < Option < Arc < DataContract > > , ContextProviderError > {
413+ Ok ( ( * id == self . 0 . id ( ) ) . then ( || Arc :: clone ( & self . 0 ) ) )
414+ }
415+
416+ fn get_token_configuration (
417+ & self ,
418+ _token_id : & Identifier ,
419+ ) -> Result < Option < TokenConfiguration > , ContextProviderError > {
420+ Ok ( None )
421+ }
422+
423+ fn get_quorum_public_key (
424+ & self ,
425+ _quorum_type : u32 ,
426+ _quorum_hash : [ u8 ; 32 ] ,
427+ _core_chain_locked_height : u32 ,
428+ ) -> Result < [ u8 ; 48 ] , ContextProviderError > {
429+ Err ( ContextProviderError :: Config (
430+ "no quorum keys in this test" . to_string ( ) ,
431+ ) )
432+ }
433+
434+ fn get_platform_activation_height (
435+ & self ,
436+ ) -> Result < CoreBlockHeight , ContextProviderError > {
437+ Ok ( 1 )
438+ }
439+ }
440+
441+ fn assert_drive_part_of_proof_verified ( error : drive_proof_verifier:: Error ) {
442+ assert ! (
443+ matches!(
444+ error,
445+ drive_proof_verifier:: Error :: QuorumKeyUnavailable { .. }
446+ ) ,
447+ "expected the Drive part of the proof to verify and the signature check to \
448+ stop at the quorum key, got {error:?}"
449+ ) ;
450+ }
451+
452+ /// Drive answers a proved request naming no limit with a page of the node's
453+ /// `default_query_limit` and proves that page. On a node keeping the default (100),
454+ /// the proof of a full page verifies only as that page
455+ #[ tokio:: test]
456+ async fn should_verify_a_full_default_page_of_contests_requested_with_no_limit ( ) {
457+ verify_101_contests_requested_with_no_limit ( DEFAULT_QUERY_LIMIT ) . await ;
458+ }
459+
460+ /// On a node configured with a larger default page, every contest fits it and the
461+ /// proof verifies only as the whole range
462+ #[ tokio:: test]
463+ async fn should_verify_every_contest_requested_with_no_limit_from_a_node_with_a_larger_default (
464+ ) {
465+ verify_101_contests_requested_with_no_limit ( 200 ) . await ;
466+ }
467+
468+ /// Creates 101 contests on a node whose `default_query_limit` is
469+ /// `node_default_query_limit`, and checks that `FromProof` verifies the Drive part of
470+ /// the proofs of a contested resources request and a vote polls by end date request
471+ /// naming no limit
472+ async fn verify_101_contests_requested_with_no_limit ( node_default_query_limit : u16 ) {
473+ let platform_version = PlatformVersion :: latest ( ) ;
474+ let mut config = PlatformConfig :: default_testnet ( ) ;
475+ config. drive . default_query_limit = node_default_query_limit;
476+ let mut platform = TestPlatformBuilder :: new ( )
477+ . with_config ( config)
478+ . with_latest_protocol_version ( )
479+ . build_with_mock_rpc ( )
480+ . set_genesis_state ( ) ;
481+
482+ let platform_state = platform. state . load ( ) ;
483+
484+ // One contest more than the standard default page (100). The suffix leaves out the letters
485+ // DPNS normalizes to digits (i, l and o), which would merge two names into one contest
486+ const SUFFIX_LETTERS : & [ u8 ] = b"abcdefghjkmnpqrstuvwxyz" ;
487+ let mut dpns_contract = None ;
488+ for n in 0 ..=DEFAULT_QUERY_LIMIT as u64 {
489+ let letter = |i : u64 | SUFFIX_LETTERS [ i as usize % SUFFIX_LETTERS . len ( ) ] as char ;
490+ let name = format ! (
491+ "contested{}{}" ,
492+ letter( n / SUFFIX_LETTERS . len( ) as u64 ) ,
493+ letter( n)
494+ ) ;
495+ let ( _, _, contract) = create_dpns_identity_name_contest (
496+ & mut platform,
497+ & platform_state,
498+ 1_000 + n,
499+ & name,
500+ platform_version,
501+ )
502+ . await ;
503+ dpns_contract = Some ( contract) ;
504+ }
505+ let dpns_contract = dpns_contract. expect ( "expected the dpns contract" ) ;
506+ let provider = DpnsOnlyContextProvider ( Arc :: clone ( & dpns_contract) ) ;
507+
508+ let domain = dpns_contract
509+ . document_type_for_name ( "domain" )
510+ . expect ( "expected the domain document type" ) ;
511+ let index_name = "parentNameAndLabel" . to_string ( ) ;
512+ let dash_encoded = bincode:: encode_to_vec (
513+ Value :: Text ( "dash" . to_string ( ) ) ,
514+ bincode:: config:: standard ( )
515+ . with_big_endian ( )
516+ . with_no_limit ( ) ,
517+ )
518+ . expect ( "expected to encode value" ) ;
519+
520+ let request = GetContestedResourcesRequest {
521+ version : Some ( get_contested_resources_request:: Version :: V0 (
522+ GetContestedResourcesRequestV0 {
523+ contract_id : dpns_contract. id ( ) . to_vec ( ) ,
524+ document_type_name : domain. name ( ) . clone ( ) ,
525+ index_name : index_name. clone ( ) ,
526+ start_index_values : vec ! [ dash_encoded] ,
527+ end_index_values : vec ! [ ] ,
528+ start_at_value_info : None ,
529+ count : None ,
530+ order_ascending : true ,
531+ prove : true ,
532+ } ,
533+ ) ) ,
534+ } ;
535+ let response = platform
536+ . query_contested_resources ( request. clone ( ) , & platform_state, platform_version)
537+ . expect ( "expected to execute query" )
538+ . into_data ( )
539+ . expect ( "expected query to be valid" ) ;
540+
541+ let Some ( get_contested_resources_response:: Version :: V0 (
542+ GetContestedResourcesResponseV0 {
543+ result : Some ( get_contested_resources_response_v0:: Result :: Proof ( proof) ) ,
544+ ..
545+ } ,
546+ ) ) = response. version . clone ( )
547+ else {
548+ panic ! ( "expected a proof" )
549+ } ;
550+
551+ // Drive proved a page of the node's default: checked against the whole range and
552+ // against a page of the standard default, only the check matching that page holds
553+ let contests_count = DEFAULT_QUERY_LIMIT as usize + 1 ;
554+ let mut query = ResolvedVotePollsByDocumentTypeQuery {
555+ contract : DataContractResolvedInfo :: BorrowedDataContract (
556+ dpns_contract. as_ref ( ) ,
557+ ) ,
558+ document_type_name : domain. name ( ) ,
559+ index_name : & index_name,
560+ start_index_values : & vec ! [ "dash" . into( ) ] ,
561+ end_index_values : & vec ! [ ] ,
562+ limit : None ,
563+ order_ascending : true ,
564+ start_at_value : & None ,
565+ } ;
566+ let whole_range =
567+ query. verify_contests_proof ( proof. grovedb_proof . as_ref ( ) , platform_version) ;
568+ query. limit = Some ( DEFAULT_QUERY_LIMIT ) ;
569+ let standard_page =
570+ query. verify_contests_proof ( proof. grovedb_proof . as_ref ( ) , platform_version) ;
571+ if ( node_default_query_limit as usize ) < contests_count {
572+ whole_range
573+ . expect_err ( "expected the proof of a full page to fail with no limit" ) ;
574+ let ( _, contests) =
575+ standard_page. expect ( "expected the proof to verify as a default page" ) ;
576+ assert_eq ! ( contests. len( ) , DEFAULT_QUERY_LIMIT as usize ) ;
577+ } else {
578+ let ( _, contests) =
579+ whole_range. expect ( "expected the proof to verify as the whole range" ) ;
580+ assert_eq ! ( contests. len( ) , contests_count) ;
581+ standard_page
582+ . expect_err ( "expected the proof of every contest to fail as a 100 page" ) ;
583+ }
584+
585+ let error = <ContestedResources as FromProof < GetContestedResourcesRequest > >:: maybe_from_proof_with_metadata (
586+ request,
587+ response,
588+ Network :: Testnet ,
589+ platform_version,
590+ & provider,
591+ )
592+ . expect_err ( "expected no quorum key" ) ;
593+ assert_drive_part_of_proof_verified ( error) ;
594+
595+ // The same contests read by end date: they all end at once
596+ let request = GetVotePollsByEndDateRequest {
597+ version : Some ( get_vote_polls_by_end_date_request:: Version :: V0 (
598+ GetVotePollsByEndDateRequestV0 {
599+ start_time_info : None ,
600+ end_time_info : None ,
601+ limit : None ,
602+ offset : None ,
603+ ascending : true ,
604+ prove : true ,
605+ } ,
606+ ) ) ,
607+ } ;
608+ let response = platform
609+ . query_vote_polls_by_end_date_query (
610+ request. clone ( ) ,
611+ & platform_state,
612+ platform_version,
613+ )
614+ . expect ( "expected to execute query" )
615+ . into_data ( )
616+ . expect ( "expected query to be valid" ) ;
617+
618+ let error = <VotePollsGroupedByTimestamp as FromProof <
619+ GetVotePollsByEndDateRequest ,
620+ > >:: maybe_from_proof_with_metadata (
621+ request,
622+ response,
623+ Network :: Testnet ,
624+ platform_version,
625+ & provider,
626+ )
627+ . expect_err ( "expected no quorum key" ) ;
628+ assert_drive_part_of_proof_verified ( error) ;
629+ }
630+
396631 #[ tokio:: test]
397632 async fn test_empty_string_start_index_value ( ) {
398633 let platform_version = PlatformVersion :: latest ( ) ;
0 commit comments