@@ -301,6 +301,10 @@ async fn leader_upload(version: DapVersion) {
301301 report_metadata : ReportMetadata {
302302 id : ReportId ( [ 1 ; 16 ] ) ,
303303 time : t. now ,
304+ public_extensions : match version {
305+ DapVersion :: Draft09 => None ,
306+ DapVersion :: Latest => Some ( Vec :: new ( ) ) ,
307+ } ,
304308 } ,
305309 public_share : b"public share" . to_vec ( ) ,
306310 encrypted_input_shares : [
@@ -533,6 +537,150 @@ async fn leader_upload_taskprov_wrong_version(version: DapVersion) {
533537
534538async_test_versions ! ( leader_upload_taskprov_wrong_version) ;
535539
540+ #[ tokio:: test]
541+ async fn leader_upload_taskprov_public ( ) {
542+ let version = DapVersion :: Latest ;
543+ let t = TestRunner :: default_with_version ( version) . await ;
544+ let client = t. http_client ( ) ;
545+ let hpke_config_list = t. get_hpke_configs ( version, client) . await . unwrap ( ) ;
546+
547+ let ( task_config, task_id, taskprov_advertisement) = DapTaskParameters {
548+ version,
549+ min_batch_size : 10 ,
550+ query : DapBatchMode :: TimeInterval ,
551+ leader_url : t. task_config . leader_url . clone ( ) ,
552+ helper_url : t. task_config . helper_url . clone ( ) ,
553+ ..Default :: default ( )
554+ }
555+ . to_config_with_taskprov (
556+ b"cool task" . to_vec ( ) ,
557+ t. now ,
558+ daphne:: roles:: aggregator:: TaskprovConfig {
559+ hpke_collector_config : & t. taskprov_collector_hpke_receiver . config ,
560+ vdaf_verify_key_init : & t. taskprov_vdaf_verify_key_init ,
561+ } ,
562+ )
563+ . unwrap ( ) ;
564+
565+ let mut report = task_config
566+ . vdaf
567+ . produce_report (
568+ & hpke_config_list,
569+ t. now + 1 ,
570+ & task_id,
571+ DapMeasurement :: U32Vec ( vec ! [ 1 ; 10 ] ) ,
572+ version,
573+ )
574+ . unwrap ( ) ;
575+ report. report_metadata . public_extensions = Some ( vec ! [ Extension :: Taskprov ] ) ;
576+ t. leader_request_expect_ok (
577+ client,
578+ & format ! ( "tasks/{}/reports" , task_id. to_base64url( ) ) ,
579+ & http:: Method :: POST ,
580+ DapMediaType :: Report ,
581+ Some (
582+ & taskprov_advertisement
583+ . serialize_to_header_value ( version)
584+ . unwrap ( ) ,
585+ ) ,
586+ report. get_encoded_with_param ( & version) . unwrap ( ) ,
587+ )
588+ . await
589+ . unwrap ( ) ;
590+ }
591+
592+ #[ tokio:: test]
593+ async fn leader_upload_taksprov_public_errors ( ) {
594+ let version = DapVersion :: Latest ;
595+ let t = TestRunner :: default_with_version ( version) . await ;
596+ let client = t. http_client ( ) ;
597+ let hpke_config_list = t. get_hpke_configs ( version, client) . await . unwrap ( ) ;
598+
599+ let ( task_config, task_id, taskprov_advertisement) = DapTaskParameters {
600+ version,
601+ min_batch_size : 10 ,
602+ query : DapBatchMode :: TimeInterval ,
603+ leader_url : t. task_config . leader_url . clone ( ) ,
604+ helper_url : t. task_config . helper_url . clone ( ) ,
605+ ..Default :: default ( )
606+ }
607+ . to_config_with_taskprov (
608+ b"cool task" . to_vec ( ) ,
609+ t. now ,
610+ daphne:: roles:: aggregator:: TaskprovConfig {
611+ hpke_collector_config : & t. taskprov_collector_hpke_receiver . config ,
612+ vdaf_verify_key_init : & t. taskprov_vdaf_verify_key_init ,
613+ } ,
614+ )
615+ . unwrap ( ) ;
616+
617+ // Repeated public extension
618+ let mut report = task_config
619+ . vdaf
620+ . produce_report (
621+ & hpke_config_list,
622+ t. now + 1 ,
623+ & task_id,
624+ DapMeasurement :: U32Vec ( vec ! [ 1 ; 10 ] ) ,
625+ version,
626+ )
627+ . unwrap ( ) ;
628+ report. report_metadata . public_extensions = Some ( vec ! [ Extension :: Taskprov , Extension :: Taskprov ] ) ;
629+ t. leader_request_expect_abort (
630+ client,
631+ None ,
632+ & format ! ( "tasks/{}/reports" , task_id. to_base64url( ) ) ,
633+ & http:: Method :: POST ,
634+ DapMediaType :: Report ,
635+ Some (
636+ & taskprov_advertisement
637+ . serialize_to_header_value ( version)
638+ . unwrap ( ) ,
639+ ) ,
640+ report. get_encoded_with_param ( & version) . unwrap ( ) ,
641+ 400 ,
642+ "invalidMessage" ,
643+ )
644+ . await
645+ . unwrap ( ) ;
646+
647+ // Unsupported public extension
648+ let mut report = task_config
649+ . vdaf
650+ . produce_report (
651+ & hpke_config_list,
652+ t. now + 1 ,
653+ & task_id,
654+ DapMeasurement :: U32Vec ( vec ! [ 1 ; 10 ] ) ,
655+ version,
656+ )
657+ . unwrap ( ) ;
658+ report. report_metadata . public_extensions = Some ( vec ! [
659+ Extension :: Taskprov ,
660+ Extension :: NotImplemented {
661+ typ: 3 ,
662+ payload: b"ignore" . to_vec( ) ,
663+ } ,
664+ ] ) ;
665+ t. leader_request_expect_abort (
666+ client,
667+ None ,
668+ & format ! ( "tasks/{}/reports" , task_id. to_base64url( ) ) ,
669+ & http:: Method :: POST ,
670+ DapMediaType :: Report ,
671+ Some (
672+ & taskprov_advertisement
673+ . serialize_to_header_value ( version)
674+ . unwrap ( ) ,
675+ ) ,
676+ report. get_encoded_with_param ( & version) . unwrap ( ) ,
677+ 400 ,
678+ "unsupportedExtension" ,
679+ )
680+ . await
681+ . unwrap ( ) ;
682+ }
683+
536684async fn internal_leader_process ( version : DapVersion ) {
537685 let t = TestRunner :: default_with_version ( version) . await ;
538686 let path = t. upload_path ( ) ;
@@ -1348,6 +1496,116 @@ async fn leader_selected() {
13481496 . unwrap ( ) ;
13491497}
13501498
1499+ #[ tokio:: test]
1500+ async fn leader_collect_taskprov_repeated_abort ( ) {
1501+ let version = DapVersion :: Latest ;
1502+ const DAP_TASKPROV_COLLECTOR_TOKEN : & str = "I-am-the-collector" ;
1503+ let t = TestRunner :: default_with_version ( version) . await ;
1504+ let batch_interval = t. batch_interval ( ) ;
1505+
1506+ let client = t. http_client ( ) ;
1507+ let hpke_config_list = t. get_hpke_configs ( version, client) . await . unwrap ( ) ;
1508+
1509+ let ( task_config, task_id, taskprov_advertisement) = DapTaskParameters {
1510+ version,
1511+ min_batch_size : 10 ,
1512+ query : DapBatchMode :: TimeInterval ,
1513+ leader_url : t. task_config . leader_url . clone ( ) ,
1514+ helper_url : t. task_config . helper_url . clone ( ) ,
1515+ ..Default :: default ( )
1516+ }
1517+ . to_config_with_taskprov (
1518+ b"cool task" . to_vec ( ) ,
1519+ t. now ,
1520+ daphne:: roles:: aggregator:: TaskprovConfig {
1521+ hpke_collector_config : & t. taskprov_collector_hpke_receiver . config ,
1522+ vdaf_verify_key_init : & t. taskprov_vdaf_verify_key_init ,
1523+ } ,
1524+ )
1525+ . unwrap ( ) ;
1526+
1527+ let path = TestRunner :: upload_path_for_task ( & task_id) ;
1528+ let method = & Method :: POST ;
1529+ // The reports are uploaded in the background.
1530+ let mut rng = thread_rng ( ) ;
1531+ for _ in 0 ..t. task_config . min_batch_size {
1532+ let extensions = vec ! [ Extension :: Taskprov ] ;
1533+ let now = rng. gen_range ( TestRunner :: report_interval ( & batch_interval) ) ;
1534+ t. leader_request_expect_ok (
1535+ client,
1536+ & path,
1537+ method,
1538+ DapMediaType :: Report ,
1539+ Some (
1540+ & taskprov_advertisement
1541+ . serialize_to_header_value ( version)
1542+ . unwrap ( ) ,
1543+ ) ,
1544+ {
1545+ let mut report = task_config
1546+ . vdaf
1547+ . produce_report_with_extensions (
1548+ & hpke_config_list,
1549+ now,
1550+ & task_id,
1551+ DapMeasurement :: U32Vec ( vec ! [ 1 ; 10 ] ) ,
1552+ extensions,
1553+ version,
1554+ )
1555+ . unwrap ( ) ;
1556+ report. report_metadata . public_extensions = Some ( vec ! [ Extension :: Taskprov ] ) ;
1557+ report. get_encoded_with_param ( & version) . unwrap ( )
1558+ } ,
1559+ )
1560+ . await
1561+ . unwrap ( ) ;
1562+ }
1563+
1564+ let agg_param = DapAggregationParam :: Empty ;
1565+
1566+ // Get the collect URI.
1567+ let collect_req = CollectionReq {
1568+ query : Query :: TimeInterval { batch_interval } ,
1569+ agg_param : agg_param. get_encoded ( ) . unwrap ( ) ,
1570+ } ;
1571+ let collect_uri = t
1572+ . leader_post_collect_using_token (
1573+ client,
1574+ DAP_TASKPROV_COLLECTOR_TOKEN ,
1575+ Some ( & taskprov_advertisement) ,
1576+ Some ( & task_id) ,
1577+ collect_req. get_encoded_with_param ( & t. version ) . unwrap ( ) ,
1578+ )
1579+ . await
1580+ . unwrap ( ) ;
1581+ println ! ( "collect_uri: {collect_uri}" ) ;
1582+
1583+ // Poll the collect URI before the CollectResp is ready.
1584+ let resp = t
1585+ . poll_collection_url_using_token ( client, & collect_uri, DAP_TASKPROV_COLLECTOR_TOKEN )
1586+ . await
1587+ . unwrap ( ) ;
1588+ #[ expect( clippy:: format_in_format_args) ]
1589+ {
1590+ assert_eq ! (
1591+ resp. status( ) ,
1592+ 202 ,
1593+ "response: {} {}" ,
1594+ format!( "{resp:?}" ) ,
1595+ resp. text( ) . await . unwrap( )
1596+ ) ;
1597+ }
1598+
1599+ // The reports are aggregated in the background.
1600+ let agg_telem = t. internal_process ( client) . await . unwrap ( ) ;
1601+ assert_eq ! (
1602+ agg_telem. reports_processed, task_config. min_batch_size,
1603+ "reports processed"
1604+ ) ;
1605+ assert_eq ! ( agg_telem. reports_aggregated, 0 , "reports aggregated" ) ;
1606+ assert_eq ! ( agg_telem. reports_collected, 0 , "reports collected" ) ;
1607+ }
1608+
13511609async fn leader_collect_taskprov_ok ( version : DapVersion ) {
13521610 const DAP_TASKPROV_COLLECTOR_TOKEN : & str = "I-am-the-collector" ;
13531611 let t = TestRunner :: default_with_version ( version) . await ;
0 commit comments