open-standards/wai/live-conformance/metering/README.md
Transaction Science a2529d1339 live_metering_verify.py and the corpus README: version 2
The second implementation reads WMH1 version 1 or 2 by its own layout,
holds version 2's out and stored figures to the entries, chooses the
bound by the fifth byte (B2(M) for 2, B(M) otherwise), and states each
record's version and a version 2 record's DVR figures. New mutations
cover the stated version, figures and bound, the record's DVR bytes, and
the four new refusals. The README describes the new vectors.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:41:34 -04:00

139 lines
9.5 KiB
Markdown

# live-conformance/metering — live metering
The corpus of [`extensions/live-metering.md`](../../extensions/live-metering.md) §10: rooms,
and vectors, each a room-session receipt (`WMR1`) with what it names (the per-hop session
records, `WMH1`, of version 1 or of version 2 with a hop's DVR figures, each with its ledger; the composition descriptors and receipts, `WCD1` and
`WCR1`; the take manifests, `WTM1`; and the logs document of QoE observations), and the
outcome a verifier gives it.
**Metering equivalence.** For each vector, a conforming receipt verifier given its room (by
name, from `rooms`: the descriptor, the grants, the key-log head and the chains, each
hexadecimal), its `receipt`, `hops` (each `record` and `ledger`), `descriptors`,
`compositions`, `takes` and `logs` (the document's text, or `null`) reaches the outcome the
vector states (§5.4):
- `refuse`: the `reason` and where, `at`: `["room"]` or `["room", …]` with the room
verifier's own place (live-rooms §9.1), `["receipt"]`, `["hop", i]` for the receipt's hop
entry `i`, `["take", i]`, `["compose"]` for the list of composition receipts or
`["compose", i]` for the receipt `i` in ascending order of digest, `["qoe"]`, `["units"]`,
`["energy"]` or `["ceilings"]`;
- `accept`: the receipt's `digest`; the number of media tracks the room published in the
session (`media_tracks`, the *M* of §3.7); each hop record's `digest`, `size`, size
`bound` (B(*M*) for version 1, B₂(*M*) for version 2) and `version`, and for a version 2
record its six DVR figures by their §3.9 names (`dvr_objects_out`, `dvr_bytes_out`,
`dvr_objects_stored`, `dvr_bytes_stored`, `dvr_byte_s`, `dvr_dropped`); the `units`; the `unmetered` hop entries; the `totals`, one per class; the
`coverage`, `whole` or `partial`; each grant's `consumption` entry (its ceilings, its units,
its attributed joules by class, whether its share is `unmetered`, its `funds`, its two
verdicts and its `leaf`); the `consumption_root`; the grants `past` a ceiling with their
flags; and the `qoe_cell`.
A verifier that reaches another outcome, reason, place or fact does not conform.
**Provenance.** Generated here: `wai_live_metering_vectors gen` writes `vectors.json` from
sessions authored in code, from fixed seeds, by capture binding's corpus rules: each key is
SHA-256 of that corpus's fixed label and the principal's index (the principals it shares with
that corpus and with live recording's are the same principals), every time is a constant
(the lesson room's `t0` is 2026-10-16T00:00:00Z), every chunk's bytes are SHA-256 output under
a fixed label, and Ed25519 signing is deterministic (RFC 8032), so the file is the same bytes
on every machine. The lesson's per-hop records are aggregated from sessions written in code
by the rules a hop counts by (§3), and stand for what a relay, a compositor and an ingest
gateway would count; their joule figures are constants and were measured by nothing. The
`dvr_relay` vector's DVR figures are counted, by §3.9's rules, from a DVR window written in
code (the composite's 23 frames of 20 000 bytes, each written as it arrives a third of a
second into its second and held ten seconds or to the span's end, one dropped a second after
it arrived, a viewer joining at 20 s filled from the window): measured by nothing either. The
key-log heads are sealed by the reference's key log under a fixed root; the origin is an
`.example` name (RFC 2606). The generator asserts that each vector reaches the outcome it was
written to have before it writes it.
**Recorded data, one record.** `relay-record.wmh` and its ledger `relay-ledger.wml` were signed
by `joule-moq-relay` itself, in its test `tests/room_metering.rs` (two publishers and three
subscribers of the `relay` room over native QUIC and WebTransport, one subscriber slow, each
reading its streams concurrently), run once on 2026-10-18 with `WAI_METERING_FIXTURE_DIR`
set; the relay's key is the room's clock key, under the same label. Its counts, its span
(3.196 s of room clock from 11:45:35.966 UTC)
and its subscriber time are what that run measured on loopback, so a second run gives other
bytes: the pair is committed as recorded and read by the generator, which writes the `relay`
room's lifecycle around the record's span. The relay meters no energy, and the record says so
with the unmetered marker. The `relay` room's grants run thirteen days from its `t0`, so a
pair recorded after 2026-10-31 would fall outside them.
## Layout
One file, `vectors.json`, beside the recorded pair:
- `principals`: the keys, by name (informative).
- `rooms`: `lesson`, a lesson room (a director with the record power, a performer's device and
an accompanist's capturing two takes, a compositor laying the room out at one frame a
second, three viewers, a key log listing the room authority, the store, the relay, the
compositor, an ingest gateway and a metering key, and a revoked metering key and relay key);
`lesson_ceilings`, the same with the second viewer's joule ceiling at 1 000 µJ and the third
viewer's funds ceiling at 0; `lesson_gap`, the clock's `join` of the third viewer not
delivered; `lesson_bad_chat`, the viewer's chat carrying a bad signature; and `relay`, the
reference relay's test room, with no origin.
- `vectors`: each with `name`, `about`, `room`, `receipt`, `hops`, `descriptors`,
`compositions`, `takes`, `logs`, `outcome`, and as above `reason` and `at`, or the facts.
The vectors:
- accepted: `lesson` (a relay and a compositor read from an on-chip counter, two takes, ten
composition receipts, three viewers' logs and a suppressed QoE cell, under a tariff in
euros; signed by the metering key), `signed_by_authority`, `mixed_classes` (a relay's counter
reading, a compositor's model and an ingest gateway's estimate, one total per class),
`ceilings_past` (the second viewer past its joule ceiling, the third past its funds
ceiling), `large_audience` (ten thousand more subscriber sessions with no grant: the relay's
record is the lesson's size; sixty logs publish the QoE cell), `relay_recorded` (the
reference relay's record, unmetered, so the energy is partial) and `dvr_relay` (the lesson's
relay keeping a ten-second DVR window of the composite: a version 2 record, 48 bytes longer
than the lesson relay's under a bound 48 bytes higher, stating the frames it stored, the
byte-seconds it held them, one dropped for not verifying against its receipt, and the ten
it filled a late viewer with; every other record of the corpus is version 1);
- refused, one defect each: the receipt (`receipt_bad_signature`, `receipt_truncated`,
`receipt_wrong_room`, `session_invalid`, `signer_not_authorised`, `signer_revoked`,
`total_of_no_class`); the hop records (`record_missing`, `record_too_large`,
`record_invalid`, `energy_invalid`, `record_wrong_room`, `record_bad_signature`,
`entry_restated_wrong`, `hop_not_authorised`, `hop_class_not_authorised`, `hop_revoked`,
`span_outside_session`, `track_unknown`, `ledger_invalid`, `ledger_ceiling_mismatch`,
`ledger_root_mismatch`, `hop_units_mismatch`, and for version 2 `dvr_out_exceeds` and
`dvr_stored_exceeds`, its bytes sent from its store above its out entries' and its objects
stored above its in entries' (`record_invalid`), `record_unknown_version`, the DVR relay's
record with its fifth byte 3 and signed again (`unknown_version`), and
`dvr_record_too_large`, past B₂(*M*) by less than one entry (`record_too_large`)); the
takes, compositions and logs
(`take_missing`, `take_unknown`, `composition_missing`, `composition_refused`,
`composition_outside_session`, `logs_missing`, `qoe_invalid`, `qoe_not_suppressed`); the
units, energy and ceilings (`units_mismatch`, `recorded_mismatch`, `classes_summed`,
`total_mismatch`, `ceiling_mismatch`, `consumption_root_mismatch`); and the room
(`room_refused`, `lifecycle_incomplete`).
## Running
```
cd ../../wai-rs
cargo run --no-default-features --features live_metering --bin wai_live_metering_vectors -- verify ../live-conformance/metering
cargo run --no-default-features --features live_metering --bin wai_live_metering_vectors -- gen ../live-conformance/metering # regenerate
cargo test --no-default-features --features live_metering --test live_metering
cd ../live-conformance
python3 live_metering_verify.py metering
```
`wai_live_metering_vectors verify` and `tests/live_metering.rs` require the file to regenerate
byte for byte and `wai-rs` to give every vector its outcome; the test also requires every
reason of §9 to refuse a vector, the accepted vectors to be the ones above, the large
audience's relay record to be the lesson's size within the same bound, the DVR relay's record
to be version 2 with every figure above 0 and every other accepted record version 1, and §9
to list exactly the reasons the reference reports. `live_metering_verify.py` is the second implementation,
written from the extension's text and sharing no code with `wai-rs`: it verifies each room
with `live_rooms_verify.py`, each take with `capture_take_verify.py` and each composition
receipt with `live_compose_verify.py`, recomputes every unit, every total, every grant's
consumption and the QoE cell itself, gives every vector the same outcome, requires the same
coverage, and then mutates copies of the corpus, one defect each, each of which must fail.
Pass = an exit status of 0 from each.
To record the relay's pair again (it changes the corpus), run the relay's test with
`WAI_METERING_FIXTURE_DIR` set to this directory, then `gen`:
```
cd ../../../joule-code/joule-code-rs
WAI_METERING_FIXTURE_DIR=$PWD/../../wai/live-conformance/metering cargo test -p joule-moq-relay --test room_metering
```