telemetry-aggregation §6.1's rule that `rejected` counts no report the aggregator aggregated had no test, and impression_aggregate_verify.py did not apply it. The flight flow now sends the helper a report it committed, and the Python flow filters as the Rust does. impression-aggregates §7.3 says what a reader does with a statement of version 1 beside one of version 2, and the corpus adds a receipt and a table of each. The ledger's corpus takes these receipts, so it is regenerated by the commit after this one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
74 lines
5.8 KiB
Markdown
74 lines
5.8 KiB
Markdown
# live-conformance/impressions — ad presentations counted over a bearer with no return path
|
||
|
||
The corpus of [`extensions/impression-aggregates.md`](../../extensions/impression-aggregates.md)
|
||
§11, in one file, `vectors.json`.
|
||
|
||
**Provenance.** Generated here by `wai_impression_aggregate_vectors gen` (`wai-rs`, feature
|
||
`impression_aggregate`), from Ed25519 keys, salts, report ids, Prio3 randomness, HPKE ephemeral
|
||
seeds and token nonces and salts that are SHA-256 of a fixed label and a name; constant instants;
|
||
and manifests, records and claims written by the generator. The issuer key is RFC 9578 Appendix
|
||
A.2's first test vector, as the presentation corpora use it, and every token is signed with it by
|
||
RSASSA-PSS over a salt from a seed. The reference aggregators of `wai-rs` run every flow. No media
|
||
and no input from outside this repository.
|
||
|
||
`flight` gives the flight's first hour (`first_hour`, in `time_precision` units), its length and
|
||
its start in milliseconds. `keys` names every key by its role, in hexadecimal: the task origin's
|
||
keys `t1` and `t2` (`aggregate/task`) and `d1` (`delivery` alone), a stranger origin's `s1`, the
|
||
stitcher and a stranger stitcher, the three statement keys and a stranger's, each aggregator's and
|
||
the collector's HPKE configuration and private key, the verification keys' seed, and the issuer.
|
||
`fixtures` holds what cases name: the creatives `A`, `B` and `C`; the manifests as text (`mpd`,
|
||
`mpd_short` without the first two events, `playlist`); the pod-input records with what each is;
|
||
the catalogues `flight`, `open` and `bound` (the flight's with a hold of 3,599 s); the tasks; the
|
||
key logs (`rev0`, `rev1`, which revokes `t2` and arrives on the bearer, and `stranger`); the
|
||
anchor records (`pinned` at `rev0`, `fetched`); `receiver`, the revisions and anchors a receiver
|
||
holds; and the task files `flight`, `open` and `twin`.
|
||
|
||
- `catalogues` (§4.3) are each a catalogue's `bytes`; `outcome` is `read`, with its `digest`, its
|
||
`cells` and its `chunk` length, or `refuse` and its `reason`.
|
||
- `tasks` (§5.3) are a TaskConfiguration (`task`) and a `catalogue`; `outcome` is `accept`, with the
|
||
`task_id` and the `retention_s`, or `refuse`.
|
||
- `task_files` (§5.4) are a file's `bytes`, the `revisions` and the `anchors` held, by fixture name;
|
||
`outcome` is `accept`, with the file's `digest`, the `task_id` and the `confirmation` as
|
||
jwp-receipts §7.4 reports it, or `refuse`.
|
||
- `contributions` (§6.2) name a `task_file` and a `record`, and give the session's `claims` (the
|
||
last is the one judged), the `region`, `at_ms` and the `pool`; every manifest and creative of
|
||
`fixtures` is held, and the task file is accepted under `receiver`. `outcome` is `report`, with
|
||
the `cell`, `creative`, `column`, `quartile`, `time`, the report `id` (`fresh` without
|
||
attestation) and the pool ids `discarded`, or `no_report` with its `reason`.
|
||
- `flows` (§6–§7) are end to end: `reports` lists each report, a presentation with its `record`,
|
||
`claim`, `region`, `at_ms` and its contribution's cell, or a report written to fail with the
|
||
`error` and the WAI `reason` it meets; `steps` are, in order, each upload (the `WIU1` `file`, the
|
||
leader's `response` as DAP UploadErrors and its `errors`, and for a relay's merge its `sources`
|
||
and each source's `shares` of the response), the aggregation job (its request, response and each
|
||
report's `outcome` and `reason`), and each collection at `now_s` (`processing`, a DAP problem
|
||
type, or `completed` with every message, the `result` and the signed `receipt`). The `flight`
|
||
flow's `late` report is uploaded after its batch's collection, and its `replay` step sends the
|
||
helper again, by `id`, a report it committed: the `init_req` and the helper's `resp`, refusing it
|
||
`report_replayed`. The helper's statement does not count it (telemetry-aggregation §6.1).
|
||
- `uploads` (§6.4) are a file's `bytes`; `outcome` is `read`, with the `task_id` and the `ids`, or
|
||
`refuse`.
|
||
- `plans` (§6.5) are the `stored` reports, the instant `t_ms` and the `budget`, under the open task
|
||
file; `outcome` gives `send` (each id and its class), `defer`, `drop` and the `file`.
|
||
- `settles` (§6.6) are the file `sent` and the `response`; `outcome` gives whether it `settles`,
|
||
and the ids to `delete` and to `keep`.
|
||
- `receipts` (§8.1) are a `receipt`, a `catalogue` and the batch's `reports` (or `null`); `outcome`
|
||
is `accept`, with telemetry-aggregation §8.2's fields, `rejected` (the `leader`'s and the
|
||
`helper`'s, each `null` for a statement of version 1) and `reports_checked`, or `refuse` with its
|
||
`reason` and where (`at`).
|
||
- `tables` (§8.2) are a `catalogue`, a `task` and `receipts`, each with its reports or `null`;
|
||
`outcome` gives each receipt's `digest` and `verdict`, a standing one's batch, instants, count,
|
||
`rejected` and `rows`, and the `totals`, with each aggregator's `rejected` summed; or `refuse` and
|
||
its `reason`.
|
||
|
||
Everything is synthetic: keys attest nothing, the key logs are test logs, origins are reserved
|
||
example domains, and regions are ISO 3166-1's user-assigned codes and subdivisions of them. Each
|
||
case states the outcome it was written to have, declared in the generator, never copied from a
|
||
verifier. The size bounds of §5.4 and §6.4 are carried by no vector; both implementations check
|
||
each at its edge: a file of exactly the bound that reads in every other respect is read, and the
|
||
same file one byte longer is refused. §4.3's bound decides no outcome, so neither checks it.
|
||
|
||
`wai_impression_aggregate_vectors verify` and `tests/impression_aggregate.rs` check that the file
|
||
regenerates byte for byte and that `wai-rs` reaches every stated outcome; the test also checks
|
||
the equivalence the extension's §11 describes, which is not stored. `../impression_aggregate_verify.py` is the second
|
||
implementation, written from the extension's text, which also mutates copies of the corpus and
|
||
drops each of its own checks in turn and requires each to be caught.
|