//! `wai.delivery.impressions` (extensions/impression-aggregates.md): the committed corpus //! `live-conformance/impressions/` regenerates byte for byte from its fixed seeds, holds no other //! file, every case in it gets the outcome it states, §10 lists exactly the reasons the reference //! reports, each standing receipt's result is its contributions' tally and stitched presentation's //! own count of the flight's claims (§8.11), and the size bounds of §5.4 and §6.4 decide a //! file's outcome at their edge. #![cfg(feature = "impression_aggregate")] use std::collections::BTreeMap; use std::path::PathBuf; use wai::impression_aggregate::{ accept, corpus, encode_hpke_list, TaskFile, UploadFile, CATALOGUE_MALFORMED, CODES, MAX_TASK_FILE, MAX_UPLOAD, TASK_FILE_MALFORMED, UPLOAD_MALFORMED, }; use wai::telemetry_aggregate::dap::{HpkeCiphertext, HpkeConfig, Report, ReportMetadata}; fn wai_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..") } fn committed() -> BTreeMap { let dir = wai_dir().join("live-conformance/impressions"); std::fs::read_dir(&dir) .expect("the committed corpus") .flatten() .map(|e| (e.file_name().to_string_lossy().into_owned(), e.path())) .filter(|(n, _)| n != "README.md") .map(|(n, p)| (n, std::fs::read_to_string(p).expect("a text file"))) .collect() } #[test] fn the_committed_corpus_regenerates_from_its_seeds() { let files = committed(); let generated: BTreeMap = corpus::generate().into_iter().collect(); let extra: Vec<&String> = files.keys().filter(|k| !generated.contains_key(*k)).collect(); let missing: Vec<&String> = generated.keys().filter(|k| !files.contains_key(*k)).collect(); let differ: Vec<&String> = generated.iter().filter(|(k, v)| files.get(*k).is_some_and(|f| f != *v)).map(|(k, _)| k).collect(); assert!(extra.is_empty() && missing.is_empty() && differ.is_empty(), "extra {extra:?}, missing {missing:?}, differ {differ:?}: run `wai_impression_aggregate_vectors gen`"); } #[test] fn every_case_gets_its_stated_outcome() { let fails = corpus::verify(&committed()); assert!(fails.is_empty(), "{}", fails.join("\n")); } /// §8.11: each standing receipt's result is the plaintext tally of its contributions, and the /// flight's table gives per creative what stitched presentation's verifier gives. #[test] fn aggregation_reproduces_the_plaintext_and_stitched_counts() { let fails = corpus::equivalence(); assert!(fails.is_empty(), "{}", fails.join("\n")); } /// telemetry-aggregation §6.1, §7.3 here: each aggregator states its own `rejected` and the two /// differ on the flight's first hour, the leader counting what its helper rejected as well; a table /// carries each, a statement of version 1 states none, and a count changed after its aggregator /// signed is refused at that aggregator. #[test] fn each_aggregator_states_what_it_rejected() { let doc: serde_json::Value = serde_json::from_str(&committed()["vectors.json"]).unwrap(); let case = |kind: &str, name: &str| doc[kind].as_array().unwrap().iter().find(|c| c["name"] == name).unwrap_or_else(|| panic!("no case {name}"))["outcome"].clone(); let pair = |v: &serde_json::Value| (v["leader"].as_u64(), v["helper"].as_u64()); let r0 = case("receipts", "r0"); // b0's seven failures reach the leader; three reach the helper and are refused there. assert_eq!(pair(&r0["rejected"]), (Some(7), Some(3))); let swapped = case("receipts", "r0_rejected_as_each_states"); assert_eq!((swapped["outcome"].as_str(), pair(&swapped["rejected"])), (Some("accept"), (Some(0), Some(43))), "nothing holds one count to the other"); let none = case("receipts", "r0_of_version_1"); assert_eq!((none["outcome"].as_str(), pair(&none["rejected"])), (Some("accept"), (None, None))); let changed = case("receipts", "rejected_count_changed"); assert_eq!((changed["reason"].as_str(), changed["at"].as_str()), (Some("bad_signature"), Some("helper"))); let table = case("tables", "flight"); assert_eq!(pair(&table["receipts"][0]["rejected"]), (Some(7), Some(3))); assert_eq!(pair(&table["receipts"][1]["rejected"]), (Some(0), Some(0))); assert_eq!(pair(&table["totals"]["rejected"]), (Some(7), Some(3))); let unstated = case("tables", "flight_a_statement_of_no_count"); assert_eq!(pair(&unstated["totals"]["rejected"]), (None, None), "a total of what one statement does not state is none, never a part"); // A reader tolerates a statement of version 1 beside one of version 2: each count is its own. let both = case("receipts", "r0_of_both_versions"); assert_eq!((both["outcome"].as_str(), pair(&both["rejected"])), (Some("accept"), (Some(7), None))); let half = case("tables", "flight_one_statement_of_no_count"); assert_eq!(pair(&half["receipts"][0]["rejected"]), (Some(7), None)); assert_eq!(pair(&half["totals"]["rejected"]), (Some(7), None), "the leader's total stands beside a helper's of none"); // A report the helper committed and was sent again: refused, and none of what it states. let flow = doc["flows"].as_array().unwrap().iter().find(|f| f["name"] == "flight").unwrap(); let replay = flow["steps"].as_array().unwrap().iter().find(|s| s["step"] == "replay").expect("a replay"); let replayed = format!("{}02{:02x}", replay["id"].as_str().unwrap(), wai::telemetry_aggregate::dap::report_error::REPORT_REPLAYED); assert_eq!(replay["resp"].as_str(), Some(replayed.as_str())); } /// §10 of the extension lists exactly the codes `CODES` has, each once, in order. #[test] fn the_extension_lists_the_reasons() { let text = std::fs::read_to_string(wai_dir().join("extensions/impression-aggregates.md")).expect("the extension"); let at = text.find("\n## 10. Reasons").expect("a §10"); let end = text[at + 1..].find("\n## ").map_or(text.len(), |e| at + 1 + e); let listed: Vec = text[at..end].lines().filter(|l| l.starts_with("| `")).filter_map(|l| l.split('`').nth(1).map(String::from)).collect(); let want: Vec = CODES.iter().map(|c| c.to_string()).collect(); assert_eq!(listed, want, "§10's table and CODES differ"); } /// The size bounds of §5.4 and §6.4, which no vector carries. A file of exactly the bound that /// reads in every other respect is read: a task file passes step 1 and is refused at step 2, /// where its catalogue of zeros does not read, and an upload file reads. The same file one byte /// longer is refused at step 1, so a reader without the bound, or with another, fails here. /// §4.3's bound decides no outcome: no catalogue that meets §4.3 comes near it. #[test] fn the_size_bounds_decide_at_their_edge() { let list = |id: u8| encode_hpke_list(&[HpkeConfig { id, kem: 0x0020, kdf: 0x0001, aead: 0x0001, public_key: vec![9; 32] }]); let task_file = |catalogue: usize| { let f = TaskFile { task: vec![], catalogue: vec![0; catalogue], leader_hpke: list(1), helper_hpke: list(2), signer: [0; 32], signer_id: "x".into(), sig: [0; 64] }; f.to_bytes() }; let at = MAX_TASK_FILE - task_file(0).len(); let (inside, past) = (task_file(at), task_file(at + 1)); assert_eq!((inside.len(), past.len()), (MAX_TASK_FILE, MAX_TASK_FILE + 1)); assert_eq!(accept(&inside, &[], &[], None).err(), Some(CATALOGUE_MALFORMED), "a task file at the bound reaches step 2"); assert_eq!(accept(&past, &[], &[], None).err(), Some(TASK_FILE_MALFORMED), "one byte past it is refused at step 1"); let ct = HpkeCiphertext { config_id: 1, enc: vec![1], payload: vec![1] }; let upload = |share: usize| { let metadata = ReportMetadata { id: [0; 16], time: 0, public_extensions: vec![] }; let r = Report { metadata, public_share: vec![0; share], leader_share: ct.clone(), helper_share: ct.clone() }; UploadFile { task_id: [0; 32], reports: vec![r] }.encode() }; let at = MAX_UPLOAD - upload(0).len(); let (inside, past) = (upload(at), upload(at + 1)); assert_eq!((inside.len(), past.len()), (MAX_UPLOAD, MAX_UPLOAD + 1)); assert_eq!(UploadFile::parse(&inside).map(|f| f.reports.len()).ok(), Some(1), "an upload file at the bound reads"); assert_eq!(UploadFile::parse(&past).err(), Some(UPLOAD_MALFORMED), "one byte past it is refused"); }