# jwp-conformance Conformance for the labelled forms of the [JWP delivery receipts](../extensions/jwp-receipts.md) (§2.2–§2.4.2, §3, §3.3 and §5.7) and their binary form (§2.7, §5.12), for the `WAI_RECEIPT` property's readings ([moq-streaming-format](../extensions/moq-streaming-format.md) §5), and for the receipt chain per (`signer_pubkey`, `track_alias`) and an object checked by inclusion alone (§3 step 3, §4.4, §5.9 and §6.1): an object's figure, class and origin bound into its Merkle leaf; a group total's class signed under the labelled group payload; the position an object's inclusion proof must walk; the group total read from its objects; a property read as signed only where a signed statement restates it; one chain for each key and track; and an object whose receipt is checked without its envelope reported as `inclusion-verified`, never as delivered. **Provenance.** Generated here by `wai_jwp_vectors`, from fixed seeds (below). No media. ## Files | file | what it is | |---|---| | `vectors.json` | The corpus, generated by `wai_jwp_vectors gen` in `wai-rs` (feature `receipt_claims`). `wai_jwp_vectors verify` requires a byte-identical regeneration and every declared outcome. | | `jwp_verify.py` | A second verifier, written from the extension text, that shares no code with `wai-rs`. It takes BLAKE3, jwp-receipts §3.1 step 3's Ed25519 rule and the claim verifier from `../claim-conformance/claim_verify.py`, itself independent of `wai-rs`. | | `chain_gap_differential.py` | Signs genuine chains of up to 90 groups and delivers them shuffled, withheld and released stale, walked back and forward, with rewritten legacy links and forks the key signs, and, on half of them, to a verifier that joins, holds a bound of groups, or both, with receipts lost outright or the chain joined mid-stream; requires the two verifiers to agree on every one. | | `chain_differential.py` | Signs receipt chains the corpus does not hold — two keys, two tracks, both payloads, genuine links, replays with and without a rewritten link, forks, gaps, links across tracks and keys, bad signatures, `group_id` up, equal and down, `group_end` around the clock, guard and bound on and off — and requires `wai_jwp_vectors verdicts` and `jwp_verify.py verdicts` to agree on every one. | ## Procedures Each vector names one procedure and declares one outcome. | procedure | input | outcome | |---|---|---| | `leaf` | an object receipt | its leaf, in the form its label gives (§2.2) | | `group` | a group receipt, and optionally the previous one held | `accept` with its link hash and signing payload (§2.3, §3 step 3), or a rejection at a §3 step | | `object` | a group receipt, an object receipt and the envelope delivered | `accept` (§3 steps 1–5), or a rejection at a §3 step | | `total` | a group receipt and the objects delivered with their envelopes | the total's reading — `unmetered`, `unlabelled`, `weakest` (a legacy group whose every non-zero part is labelled: a class name only), or `signed` (a labelled group's own class, `checked` against every object or `unchecked`) — or a rejection | | `property` | a `WAI_RECEIPT` property, with the object, its group and its envelope, or with an `energy` claim | the property's reading — `unmetered`, `unsigned`, or `signed` with the class and the key that signed it (the group's for a leaf, the claim's for a claim) — or a rejection | | `chain` | group receipts in the order a verifier receives them, whether §6.1's `group_id` guard is on for labelled groups (it is always on for legacy ones), and §6.1's freshness bound (`now`, `max_age_s`, `max_skew_s`) or `null`; optionally `recovery` (§3 step 3): whether the verifier joins, and `max_held`, the count of groups it holds live for each chain's missing parents, or `null` for none; with a count, `max_history`, the count it holds as history for each chain, inside the gaps its anchors left, and `max_total`, the count it holds across every chain | for each group in order, `accept` (§3 steps 1–3 against the chain of its key and track, whose head it then becomes, or the head offered again), `accept again` (a group the chain admitted before, the same bytes, which changes nothing), `accept anchor` (it anchors a joined or re-anchored chain), `accept history` (inside a gap an anchor left, linking forward from the group before it or back from the group after it), or a rejection at its §3 step; the verifier holds every group it accepted, so a fork is `chain_break` and a gap `parent_not_held`; a group whose signed `group_id` is below the chain's live edge (the highest of a group that verified and arrived fresh) is exempt from the freshness bound. With holding, a held group's outcome is what became of it: accepted when its parent arrived, or when it filled a gap, `parent_not_recovered` when a bound was passed, or `parent_not_held` if still held at the end | | `inclusion` | a group receipt and an object receipt, with no envelope | `accept` with the evidence `inclusion-verified` (§3 steps 1, 2 and 5, step 4 not run; §4.4), or a rejection at a §3 step | | `binary` | bytes (hexadecimal) and their form: `receipts` (a group's receipts, §2.7.3), `group` or `object` (one standing alone) | `read` with each receipt's members in the JSON form and, for a group, its link hash over the bytes as received (§2.7.4's reading, §3 step 1's and step 4's reading of the members, and the bytes written again identical), or `reject` with `malformed` at its §3 step, or the reason §3 step 1 gives the members | A rejection names its reason (§3.3), and its §3 step where it has one; the group total and a property are not steps of §3. ## What the corpus holds Leaves in both forms, the labelled one with tags 0, 2, 4 and 5. A group in the legacy payload whose objects mix the forms, and a group in the labelled payload chained to it, with their signing payloads, signatures and link hashes. Every way a label, a figure or an origin can be restated, stripped or added, and which of them the legacy leaf leaves unbound. Labels that break their rules in correctly built trees. Objects presented at another object's position — including the duplicated last leaf of an odd level, and a duplicate that would hide a weaker class from the total. Group labels that break their rules, correctly signed; a `sign_version` other than 2; and group signatures the strict Ed25519 rule refuses (`S` ≥ L, a key of small order, a key whose `y` is not reduced). Totals read from complete and incomplete holdings, a total that does not match its parts, and labelled totals whose class, coverage, declared uncertainty or window disagree with what their parts compose to. Properties that agree with, and disagree with, a labelled leaf or an `energy` claim, including a claim signed by a key other than the publisher's, which reads as signed by that key. Chains (vector `v6_per_track_chain` and its neighbours): one key's base track and layer track, three chained labelled groups each, offered alone and interleaved; a track's second group without its first; a parent on another track; a track's first group offered again after its second, which changes nothing, a fork from a held group, a gap beside the head, and the head offered again; another key on the same track; the `group_id` guard off, on, and compared within a track, and always on for a legacy group; a legacy history replayed after one rewritten link, once and in a loop, and a labelled replay; receipts chained per key across tracks; `group_end` at the clock, with an offset, at the window's edge, stale, ahead of the clock, stale with no bound, a stale group behind the live edge, which fills a gap, a gap of seventy groups, and a stale group whose rewritten link earns it nothing; a previous group of another track or key; and a group whose signature fails, which heads nothing. Recovery (§3 step 3): a parent fetched after its children, which are held and released; a receipt lost with and without holding, the chain re-anchoring past it and regaining the held groups and the lost one as history; a re-anchor that may not move behind its head; a verifier that joins mid-stream, at the chain's first group, and on a legacy chain, with history behind its anchor where the anchor is labelled; a stale group that may not anchor; a gap fetched in ascending order after a re-anchor, each group linking forward from the one before; a joiner's fill in ascending order, held as history outside the live bound and released back from the anchor, within its history bound and past it; a joiner's fetch from the chain's first group, whose null parent is the chain's start, held as history and released back from the anchor, and a second start and a fork once that gap is closed; and the total held across two tracks, passed by one; a group offered again after the head moved on, and a joiner's anchor delivered a second time, which change nothing. Objects checked without their envelopes: a dropped layer object and a labelled one, which verify by inclusion, and a forged group, a rewritten content hash and a moved position, which do not. The binary form (§2.7, §5.12): a live track's group of one object, the mixed and labelled groups with every label tag and their proofs, a group with no object, a legacy group and an object standing alone, each read and written again byte for byte; and the bytes that do not read, each refused `malformed` at its step, with one whose members §3 step 1 refuses. ## Synthetic, and what it attests Keys are Ed25519 from fixed 32-byte seeds — 0x11 the publisher, 0x22 the meter, 0x33 a relay — and attest nothing. Envelopes, figures, origins and calibration references are invented, and calibration references use a reserved example domain. ## Running cargo run --no-default-features --features receipt_claims --bin wai_jwp_vectors -- verify ../jwp-conformance python3 jwp_verify.py vectors.json python3 chain_differential.py ../wai-rs/target/debug/wai_jwp_vectors --n 3000 --seed 1 python3 chain_gap_differential.py ../wai-rs/target/debug/wai_jwp_vectors --n 400 --seed 1 Both run in CI: the Rust verifier on every platform of the conformance matrix, the Python one on the linux runner (`.github/workflows/wai-conformance.yml`). The Python verifier needs the `cryptography` package. joule-code's `joule-moq` pins leaves, roots and signatures from this corpus in its own tests, and its workflow re-runs when the corpus changes.