//! Map conformance harness (SPEC §8). //! //! Runs five positive suites and a negative pack against deterministic, //! real-crypto vectors. Every check runs for real — there are no placeholder //! SKIPs. Any FAIL makes the run non-zero (CI-ready). The same vectors are //! emitted to `conformance/v1/*.json` by the `gen-vectors` binary so a //! third-party implementation in any language can certify against the published //! bytes. use ed25519_dalek::SigningKey; use map_cascade::{resolve, CascadeError, Resolution, TileSource}; use map_receipt::{Energy, Provenance, Receipt}; use map_schema::{ verify_chain, AltBand, AltSchedule, CellKey, ContentId, DirEntry, Directory, Lod, Manifest, ManifestBody, RevisionBody, S2CellId, SchemaError, Tier, TimeBucket, }; pub const STEWARD: &str = "did:example:map-reference"; #[derive(Default, Debug)] pub struct Report { pub pass: u32, pub fail: u32, pub lines: Vec, } impl Report { fn ok(&mut self, name: &str) { self.pass += 1; self.lines.push(format!(" PASS {name}")); } fn bad(&mut self, name: &str, why: impl std::fmt::Display) { self.fail += 1; self.lines.push(format!(" FAIL {name}: {why}")); } /// `check(true, …)` passes; `check(false, …)` fails. fn check(&mut self, name: &str, cond: bool, why: &str) { if cond { self.ok(name); } else { self.bad(name, why); } } } fn test_key(seed: u8) -> SigningKey { SigningKey::from_bytes(&[seed; 32]) } fn cell(s2: u64, lod: u8) -> CellKey { CellKey { s2: S2CellId(s2), alt: AltBand(0), time: TimeBucket::ATEMPORAL, lod: Lod(lod) } } /// A tier-presence map for the cascade suite. struct FixtureSource { present: Vec<(u64, Vec)>, } impl TileSource for FixtureSource { fn cached(&self, _c: &CellKey) -> Option { None } fn tile(&self, c: &CellKey, tier: Tier) -> Option { self.present .iter() .find(|(s2, _)| *s2 == c.s2.0) .filter(|(_, tiers)| tiers.contains(&tier)) .map(|_| ContentId::of_bytes(&[c.s2.0 as u8, tier.rank()])) } } /// Build the canonical sample tileset directory shared by C2/C3. fn sample_directory() -> Directory { Directory::new(vec![ DirEntry { key: cell(0x10, 0), offset: 0, len: 128, content_id: ContentId::of_bytes(b"a") }, DirEntry { key: cell(0x20, 0), offset: 128, len: 64, content_id: ContentId::of_bytes(b"b") }, DirEntry { key: cell(0x30, 0), offset: 256, len: 32, content_id: ContentId::of_bytes(b"c") }, ]) } fn sample_manifest(sk: &SigningKey, dir: &Directory) -> Result { ManifestBody { spec_version: "0.1.0".into(), datum: "WGS84".into(), alt_schedule: AltSchedule::default(), dir_root: dir.root()?, bounds: Some((cell(0x10, 0), cell(0x30, 0))), disclaimer: None, publisher: sk.verifying_key(), } .sign(sk) } /// Run all suites. Returns the report; caller decides exit code. pub fn run() -> Report { let mut r = Report::default(); c1_cell_key(&mut r); c2_directory(&mut r); c3_manifest_revision(&mut r); c4_cascade(&mut r); c5_receipt(&mut r); c_neg(&mut r); r } /// C1 — cell key canonical serialization + content id is deterministic. fn c1_cell_key(r: &mut Report) { let k = cell(0x1234_0000_0000_0001, 5); match (ContentId::of(&k), ContentId::of(&k)) { (Ok(a), Ok(b)) => r.check("C1.cell_key_deterministic", a == b, "content id not stable"), _ => r.bad("C1.cell_key_deterministic", "serialization failed"), } // Distinct axes -> distinct ids (the time axis is first-class). let t1 = CellKey { time: TimeBucket { start_tai_ms: 1, width_ms: 3_600_000 }, ..k }; let t2 = CellKey { time: TimeBucket { start_tai_ms: 2, width_ms: 3_600_000 }, ..k }; r.check( "C1.time_axis_distinguishes", ContentId::of(&t1).ok() != ContentId::of(&t2).ok(), "different time buckets hashed equal", ); } /// C2 — directory sorts, coalesces ranges, and a reader can verify payloads. fn c2_directory(r: &mut Report) { let dir = sample_directory(); r.check("C2.directory_sorted", dir.is_sorted(), "directory not Hilbert-sorted"); // Two adjacent cells -> one coalesced byte span. let ranges = dir.ranges_for(&[cell(0x10, 0), cell(0x20, 0)]); r.check("C2.range_coalesced", ranges == vec![(0, 192)], "range planner did not coalesce"); // Payload hash check: the directory's content_id must match the bytes. let entry = dir.locate(&cell(0x10, 0)).expect("located"); r.check( "C2.payload_hash_verifies", entry.content_id == ContentId::of_bytes(b"a"), "payload hash mismatch", ); } /// C3 — manifest signs/verifies; revision chain validates; tamper fails. fn c3_manifest_revision(r: &mut Report) { let sk = test_key(7); let dir = sample_directory(); let m = match sample_manifest(&sk, &dir) { Ok(m) => m, Err(e) => return r.bad("C3.manifest_sign", e), }; r.check("C3.manifest_verifies", m.verify().is_ok(), "good manifest failed to verify"); // A revision chains onto the manifest root. let root0 = m.root().unwrap(); let root1 = ContentId::of_bytes(b"next-cycle-root"); let rev = RevisionBody { prev_root: root0, new_root: root1, timestamp_tai_ms: 1, publisher: sk.verifying_key() } .sign(&sk) .unwrap(); match verify_chain(root0, &[rev]) { Ok(head) => r.check("C3.revision_chain", head == root1, "chain head wrong"), Err(e) => r.bad("C3.revision_chain", e), } } /// C4 — cascade closes at the right tier and falls back to F1. fn c4_cascade(r: &mut Report) { let src = FixtureSource { present: vec![ (0xAA, vec![Tier::Base, Tier::Field, Tier::Feature, Tier::Photoreal]), (0xBB, vec![Tier::Base]), // floor only -> must fall back ], }; let close = |s2, req| resolve(&src, &cell(s2, 0), req); match close(0xAA, Tier::Field) { Ok(Resolution { tier, fell_back, .. }) => { r.check("C4.respects_ceiling", tier == Tier::Field && !fell_back, "did not stop at ceiling"); } Err(e) => r.bad("C4.respects_ceiling", e), } match close(0xBB, Tier::Photoreal) { Ok(Resolution { tier, fell_back, .. }) => { r.check("C4.falls_back_to_floor", tier == Tier::Base && fell_back, "did not fall back to F1"); } Err(e) => r.bad("C4.falls_back_to_floor", e), } } /// C5 — receipts seal, verify, and enforce the provenance coupling. fn c5_receipt(r: &mut Report) { let sk = test_key(9); let rcpt = Receipt { cell: cell(0x42, 0), content_id: ContentId::of_bytes(b"tile"), tier: Tier::Photoreal, energy: Energy::estimate_tier(Tier::Photoreal), sequence: 1, nonce: [0, 0, 0, 0, 0, 0, 0, 1], }; match rcpt.seal(&sk, STEWARD) { Ok(env) => { let ok = rcpt.verify_envelope(&env, STEWARD, &sk.verifying_key().to_bytes()).is_ok(); r.check("C5.receipt_roundtrip", ok, "sealed receipt failed to verify"); let wrong = rcpt.verify_envelope(&env, "did:example:other", &sk.verifying_key().to_bytes()).is_err(); r.check("C5.receipt_rejects_wrong_steward", wrong, "verified under wrong steward"); } Err(e) => r.bad("C5.receipt_roundtrip", e), } } /// C-neg — the negative pack: tampered directory root, broken chain, dishonest receipt. fn c_neg(r: &mut Report) { let sk = test_key(7); let dir = sample_directory(); let mut m = sample_manifest(&sk, &dir).unwrap(); m.body.dir_root = ContentId::of_bytes(b"tampered"); r.check("Cneg.tampered_manifest_fails", m.verify().is_err(), "tampered manifest verified"); // Revision signed by an unendorsed key. let attacker = test_key(66); let root0 = ContentId::of_bytes(b"r0"); let bad_rev = RevisionBody { prev_root: root0, new_root: ContentId::of_bytes(b"r1"), timestamp_tai_ms: 1, publisher: sk.verifying_key() } .sign(&attacker) // signed by the wrong key .unwrap(); r.check("Cneg.unendorsed_revision_fails", verify_chain(root0, &[bad_rev]).is_err(), "unendorsed revision verified"); // Receipt claiming HwShunt with no measured figure. let rcpt = Receipt { cell: cell(1, 0), content_id: ContentId::of_bytes(b"x"), tier: Tier::Base, energy: Energy { measured_uj: None, estimated_uj: 1, provenance: Provenance::HwShunt }, sequence: 1, nonce: [0; 8], }; r.check("Cneg.hwshunt_without_measured_fails", rcpt.seal(&test_key(9), STEWARD).is_err(), "dishonest receipt sealed"); // An out-of-order directory must refuse to produce a root. let unsorted = Directory { entries: vec![ DirEntry { key: cell(0x30, 0), offset: 0, len: 1, content_id: ContentId::of_bytes(b"c") }, DirEntry { key: cell(0x10, 0), offset: 1, len: 1, content_id: ContentId::of_bytes(b"a") }, ], }; r.check( "Cneg.unsorted_directory_refuses_root", matches!(unsorted.root(), Err(SchemaError::Unsorted)), "unsorted directory produced a root", ); let _ = CascadeError::UncoveredRegion; // referenced for completeness } #[cfg(test)] mod tests { use super::*; #[test] fn all_suites_pass() { let report = run(); assert_eq!(report.fail, 0, "conformance failures:\n{}", report.lines.join("\n")); assert!(report.pass >= 12, "expected the full suite to run, got {} checks", report.pass); } }