open-standards/wai/claim-conformance/differential.py
Transaction Science cc371d815a wai: energy reports, labelled JWP leaves and what signatures cover
Two claim classes report on one energy figure without changing the
receipt that seals it. `measurement` carries a bracket's values as
integers with each bracket's liveness facts, the build and the power
reader, and is accepted only against the receipt that carries its
figure. `operating-point` records the requested, committed and
delivered operating points and a prediction. The ratio, verdict and
residual are derived by every reader, never stated.

energy-measurement §6.2 gives the figure and the least declaration a
report's integers support as exact integer formulas; wai-rs
`energy_report` implements them with no floating-point arithmetic.
§3.1 defines a live power reader: a rail must advance in two
consecutive intervals, in at least five and in at least four of every
five, over at least 300 ms. A frozen or held rail is refused rather than
sealed under a hardware class. An unmetered `0` is never a figure of
zero joules.

A JWP object's figure, class and origin are bound into its Merkle leaf
(`\x02`), and a labelled group payload signs the total's class, its
coverage and the parent link. An object must sit at the position its
inclusion proof walks, and group signatures follow the strict Ed25519
rule claims use. `jwp_delivery` and `energy_figure` implement this in
wai-rs; joule-moq 0.2.0 builds and verifies both labelled forms.

New corpus `jwp-conformance/` with `jwp_verify.py`, more claim vectors
with the earlier ones pinned, and a CI job that runs claim_verify.py,
differential.py and jwp_verify.py. Legacy receipts keep their bytes.
External JWP implementations must adopt the `\x02` leaf and the
labelled group payload before verifying objects or groups that use
them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 06:37:25 -04:00

966 lines
56 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Differential check: wai-rs and claim_verify.py give the same verdict on inputs the corpus
does not hold.
The corpus pins one declared outcome per vector, which shows both verifiers agree on those
vectors and nothing more. This script generates inputs around the edges where two readings
of jwp-receipts §3.1-§3.2 could part — JSON numbers and strings at the limits of what a
parser accepts, nesting, calendar dates, URI authorities, acquisition classes with every
combination of evidence around energy-measurement §2.3-§2.5's rules, OER figures near a
half-microjoule and at the edges of oer-receipts §4's quantities, OER receipts whose sums
and floors sit at §6's tolerances, OER receipts holding numbers §4 forbids in a body
(`-0`, fractions, integers beyond I-JSON's range) in members of every kind, context
receipts that do not themselves verify, and bodies of every class with one defect, or two
at different positions, where the order §3.1 step 2 reads a body in decides the reason;
measurement reports whose brackets sit at the least declaration and one ppm below it, at a
half microjoule, at the ratio thresholds, with a flat idle bracket, with products far past
128 bits and with every pattern of liveness facts; operating-point claims with every
presence combination of their points and prediction; reports checked against figure views
that agree and disagree in each member; and energy claims against labelled objects —
writes them in the corpus's format with no declared outcome, runs both verifiers'
`verdicts` mode, and reports every input on which they differ.
Some inputs cannot share a file with the rest: a number whose nearest double is infinite,
`NaN`, an unpaired surrogate, a repeated member name or nesting past 64 levels makes a
whole file unreadable (oer-receipts §4 "The document"), for both verifiers or — if they
differ — for one. Each of those is written to a file of its own with a readable twin beside
it, and the two verifiers must either both refuse the file or give the same verdicts on it.
python3 differential.py <wai_claim_vectors binary> [--random N] [--seed S] [--keep FILE]
Keys are the corpus's fixed seeds, so every signed input is reproducible. Exit status 0
only if the two verdict lists are identical, every file of its own is read or refused
alike, and neither verifier printed `crash`; any difference, whatever step it is at,
exits 1.
An energy claim that names an OER receipt reaches the OER verifier of its own language
(§2.4.5 check 2): wai-rs's `crate::oer`, and the reference `oer2_verify.py`. A difference
there is still a difference in the claim verdict, and is reported like any other; the
listing marks it, since its cause is in the two OER verifiers rather than the claim rules.
Those two are compared on their own by `../oer-conformance/fuzz_differential.py`.
"""
import argparse, copy, io, json, os, random, subprocess, sys, tempfile
from decimal import Decimal, localcontext
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import claim_verify as CV # noqa: E402
from cryptography.hazmat.primitives import serialization # noqa: E402
from cryptography.hazmat.primitives.asymmetric import ed25519 # noqa: E402
OER = CV.OER
def key(seed):
return ed25519.Ed25519PrivateKey.from_private_bytes(bytes([seed] * 32))
def pk(k):
return k.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw).hex()
PUBLISHER, METER, OPERATOR, VIEWER, RESOLVER = (key(s) for s in (0x11, 0x22, 0x33, 0x44, 0x55))
OER_SIGNER = "ed25519:claim-conformance-meter"
WM_ALG = "org.example.watermark.1"
def sign(c, k):
"""`c` with its signature made under `k` over §2.4.3's signing input."""
c = copy.deepcopy(c)
c.pop("sig", None)
c["signer_pubkey"] = pk(k)
c["sig"] = k.sign(CV.signing_input(c)).hex()
return c
RAW = {}
def raw(spelling):
"""A JSON value written into the input file exactly as `spelling` — `-0`, an integer
beyond 64 bits, a string with an unpaired surrogate escape — where json.dumps would
write another spelling or none. A placeholder string until text_of() writes it."""
k = f"\u0000raw{len(RAW)}\u0000"
RAW[k] = spelling
return k
def text_of(v, **kw):
"""json.dumps(v, ensure_ascii=True), every raw() placeholder written as its spelling."""
t = json.dumps(v, ensure_ascii=True, **kw)
if "\\u0000raw" in t:
for k, spelling in RAW.items():
t = t.replace(json.dumps(k), spelling)
return t
def seal_oer(template, joules_total, terms=None, floor=None, edit=None):
"""`template` recording `joules_total`, re-sealed and signed as oer-receipts §4-§5
define. The breakdown is `terms` (compute_j first) or, by default, `joules_total` as
compute_j with the other terms as the template has them (zero); `floor`, if given, is
(bits_committed, temperature_k, floor_j); `edit`, if given, changes the receipt last.
The content address is computed over the receipt as the file will be read: its text,
read as §4 reads a document (`OER.load`). A receipt whose body holds a number §4
forbids is sealed all the same; both verifiers must refuse it before its address."""
r = copy.deepcopy(template)
r.pop("chain", None)
r.pop("receipt_id", None)
r["energy"]["joules_total"] = joules_total
b = r["energy"]["breakdown"]
for k, t in zip(list(b), terms if terms is not None else [joules_total]):
b[k] = t
if floor is not None:
r["floor"]["bits_committed"], r["energy"]["temperature_k"], r["floor"]["floor_j"] = floor
if edit is not None:
edit(r)
body = OER.canonical_bytes(OER.load(io.StringIO(text_of(r))))
prev = "sha256:" + "0" * 64
rh = OER.h("sha-256", prev.encode() + body)
r["chain"] = {"prev_hash": prev, "receipt_hash": rh, "signer": OER_SIGNER,
"signature": METER.sign(rh.encode()).hex()}
r["receipt_id"] = OER.h("sha-256", body)
return r
class Inputs:
def __init__(self, corpus):
self.by_name = {v["name"]: v for v in corpus["vectors"]}
self.out = []
def receipt(self, name):
return json.loads(self.by_name[name]["receipt"])
def wire(self, name):
return self.by_name[name]["receipt"]
def add(self, name, receipt=None, context=None, procedure="claim"):
v = {"name": f"{len(self.out):05d}_{name}", "procedure": procedure}
if receipt is not None:
v["receipt"] = receipt
if context:
v["context"] = context
self.out.append(v)
# ------------------------------------------------------------------ input families
NUMBER_TOKENS = [
"0", "-0", "1", "-1", "9007199254740991", "9007199254740992", "18446744073709551615",
"18446744073709551616", "1" + "0" * 30, "1" + "0" * 308, "1" + "0" * 309, "1" + "0" * 400,
"-" + "1" + "0" * 400, "0.0", "-0.0", "1.0", "1e0", "1E2", "1e-2", "1e308", "1e309", "1e400",
"-1e400", "1E400", "1e-400", "1.7976931348623157e308", "1.7976931348623158e308",
"1.7976931348623159e308", "1.8e308", "2e308", "1e99999999999999999999",
"179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791",
"01", "+1", ".5", "1.", "1e", "1e+", "--1", "0x10", "NaN", "Infinity", "-Infinity", "1_0",
"1", "١",
]
STRING_BODIES = [
"\\ud800", "\\udc00", "\\udbff\\udfff", "\\ud800\\u0041", "\\ud83d\\ude00", "\\ud83d", "a\\u0000b",
"\\u00e9", "\\u00E9", "\\/", "\x01", "\x7f", "
", "", "￿", "﷐", "\\ud800x",
"\\uDFFF", "\\u", "\\u12", "\\x41", "\\'", "tab\there",
]
def numbers(ins):
wire = ins.wire("energy_accept")
for t in NUMBER_TOKENS:
ins.add("seq_" + t[:24], wire.replace('"seq":0', '"seq":' + t, 1))
ins.add("work_units_" + t[:24], wire.replace('"work_units":49152', '"work_units":' + t, 1))
def strings(ins):
wire = ins.wire("energy_accept")
for s in STRING_BODIES:
ins.add("signer_id_" + s.encode("unicode_escape").decode()[:24],
wire.replace('"did:example:meter"', '"' + s + '"', 1))
ins.add("member_name_" + s.encode("unicode_escape").decode()[:24],
wire.replace('{"body":', '{"' + s + '":1,"body":', 1))
# A validly signed claim whose signer_id holds characters outside the BMP and a control
# character, sent with every such character escaped: canonicalisation undoes escapes.
c = sign(dict(ins.receipt("energy_accept"), signer_id="did:example:m\U0001F600é\u001f"), METER)
escaped = json.dumps(c, ensure_ascii=True, separators=(", ", ": "))
ins.add("escaped_astral_signer_id", escaped)
ins.add("canonical_astral_signer_id", CV.jcs(c))
ins.add("bom_prefixed", "" + wire)
ins.add("trailing_garbage", wire + " x")
ins.add("trailing_whitespace", wire + " \n\t\r")
ins.add("nbsp_whitespace", " " + wire)
ins.add("empty", "")
def nesting(ins):
wire = ins.wire("energy_accept")
for d in list(range(1, 40)) + [63, 64, 65, 126, 127, 128, 129, 200, 999, 1001, 5000]:
ins.add(f"nesting_{d}", wire.replace('"did:example:meter"', "[" * d + "]" * d, 1))
ins.add("nesting_obj_40", wire.replace('"did:example:meter"', '{"a":' * 40 + "1" + "}" * 40, 1))
def dates(ins, rng, n):
base = ins.receipt("energy_accept")
fixed = ["2026-02-28T00:00:00Z", "2026-02-29T00:00:00Z", "2028-02-29T00:00:00Z", "2100-02-29T00:00:00Z",
"2000-02-29T00:00:00Z", "1900-02-29T00:00:00Z", "0000-02-29T00:00:00Z", "0004-02-29T00:00:00Z",
"2026-04-31T00:00:00Z", "2026-04-30T23:59:60Z", "2026-12-31T23:59:60+14:00", "2026-01-00T00:00:00Z",
"2026-00-10T00:00:00Z", "2026-13-01T00:00:00Z", "2026-09-27T24:00:00Z", "2026-09-27T12:60:00Z",
"2026-09-27T12:00:61Z", "2026-09-27T12:00:00.Z", "2026-09-27T12:00:00.000000001Z",
"2026-09-27t12:00:00z", "2026-09-27T12:00:00-00:00", "2026-09-27T12:00:00+24:00",
"2026-09-27T12:00:00+23:60", "2026-09-27 12:00:00Z", "2026-9-27T12:00:00Z",
"2026-09-27T12:00:00Z", "2026-09-2٧T12:00:00Z", "2026-09-27T12:00:00Z",
"+2026-09-27T12:00:00Z", "2026-09-27T12:00:00Z\n", "2026-09-31T12:00:00Z", "2026-06-31T12:00:00Z",
"2026-11-31T12:00:00Z", "2026-01-31T12:00:00Z", "2026-03-31T12:00:00Z"]
cases = list(fixed)
for _ in range(n):
y, mo, d = rng.randrange(0, 10000), rng.randrange(0, 14), rng.randrange(0, 33)
cases.append(f"{y:04d}-{mo:02d}-{d:02d}T{rng.randrange(0, 25):02d}:{rng.randrange(0, 61):02d}:"
f"{rng.randrange(0, 62):02d}{rng.choice(['Z', '+05:30', '-12:00', '.5Z', 'z'])}")
for t in cases:
ins.add("issued_at_" + t.encode("unicode_escape").decode()[:32], CV.jcs(sign(dict(base, issued_at=t), METER)))
def endpoints(ins, rng, n):
base = ins.receipt("soft_binding_accept")
fixed = ["https://resolver.example.org/matches", "https://:443/m", "https://:/m", "https://resolver.example.org:/m",
"https://resolver.example.org:8443/m", "https://resolver.example.org:x/m", "https://resolver.example.org:1:2/m",
"https://[2001:db8::1]:8443/m", "https://[2001:db8::1]/m", "https://[]/m", "https://[/m", "https://[::1/m",
"https://::1]/m", "https://[::1]x/m", "https://[::1]:/m", "https://[::1]:x/m", "https://h]:1/m",
"https://[email protected]/m", "https://@/m", "https://resolver.example.org",
"https://resolver.example.org?q", "https://resolver.example.org#f", "https://?q", "https://#f", "https://",
"https:///m", "HTTPS://resolver.example.org/m", "http://resolver.example.org/m", "https:resolver.example.org",
"https://résolver.example.org/m", "https://resolver.example.org:8/m", "https://1.2.3.4:5/m",
"https://%72esolver.example.org/m", "https://a/b@c"]
alphabet = "ab:[]@/?#1%."
cases = list(fixed) + ["https://" + "".join(rng.choice(alphabet) for _ in range(rng.randrange(0, 9))) for _ in range(n)]
for e in cases:
body = dict(base["body"], resolution_endpoint=e)
ins.add("endpoint_" + e.encode("unicode_escape").decode()[:32], CV.jcs(sign(dict(base, body=body), PUBLISHER)))
FIGURES = ["0.0120005", "0.012000500000000000000000000000000", "0.0120004999999999999999999999999999",
"0.0120005000000000000000000000000000", "0.012000500000000000000000000000000000000000000",
"0.0120004999999999999999999999999999999999999999", "0.012000", "0.0120004", "0.0120006", "1.20005e-2",
"120005e-7", "1.20005E-2", "1.20005e-02", "1.20005e-002", "1.20005e-0002", "12000.5e-6",
"0.0000000000000000000000000000000000000000000000120005e43",
"+0.0120005", " 0.0120005", "0.0120005 ", "00.0120005", ".0120005", "0.0120005e", "0_0120005", "1_2",
"0.0120005", "NaN", "Infinity", "-0.0120005", "0", "-0", "0e0", "1e-999", "1e999", "1e-1000",
"12000500000000000000000000000000000000000000000e-51", "0.00000050", "0.0000005", "0.0000015", "0.0000025",
"18446744073709.5516155", "18446744073709.551615", "9007199254.740991", "9007199254.7409915"]
# JSON numbers: §4 admits integers within I-JSON's range in a body and nothing else, so every
# fraction, `-0` and integer beyond the range here must make the receipt malformed. Among
# them, doubles half-way between two shortest decimals, which a formatter could write two
# ways — no receipt body may carry one, so no content address may depend on it.
NUMERIC_FIGURES = [0.0120005, 0.012, 0.0120004, 0.0120006, 0.0000025, 1.5e-6, 2.5e-6, 12, 0, -0.0120005, 1e-300,
9007199254.740991, 1.2000500000000001e-2, True, False, None, -0.0,
1e37, 9.999999999999999e37, 1e38, 1e-38, 9.9e-39, 1.7976931348623157e308, 5e-324,
2 ** 53 - 1, 2 ** 53, -(2 ** 53 - 1), -(2 ** 53), 2 ** 63 - 1, 2 ** 64 - 1, 2 ** 64, 10 ** 30,
75.036163330078125, 2365.15167236328125, 5.9604644775390625e-8, 983123238.6575775]
# §4's decimal strings at their limits: 34 significand digits and a three-digit exponent.
RANGE_FIGURES = ["9" * 34, "9" * 35, "0." + "0" * 32 + "1", "0." + "0" * 33 + "1", "-" + "9" * 34, "1e999", "1e-999",
"9.999999999999999999999999999999999e999", "9.9999999999999999999999999999999999e999",
"0.0120004" + "9" * 26, "0.0120004" + "9" * 27, "0.0120005" + "0" * 26, "0.0120005" + "0" * 26 + "1"]
LABELS = ["OnChipCounter", "CalibratedInstrument", "ModelBased", "Estimator", "HwShunt", "onchipcounter",
"HWSHUNT", "Measured", "", " OnChipCounter", "OnChipCounter\u200b"]
UNCERTAINTIES = [None, {"relative_ppm": 20000, "window_us": 1000000}, {"relative_ppm": 0, "window_us": 1},
{"relative_ppm": 1, "window_us": 0}, {"relative_ppm": 2 ** 32 - 1, "window_us": 1},
{"relative_ppm": 2 ** 32, "window_us": 1}, {"relative_ppm": 2 ** 53 - 1, "window_us": 2 ** 53 - 1},
{"relative_ppm": 288676, "window_us": 1}, {"relative_ppm": 288675, "window_us": 1},
{"relative_ppm": 144338, "window_us": 1}, {"relative_ppm": 144337, "window_us": 1},
{"relative_ppm": 20000}, {"window_us": 1}, {}, {"relative_ppm": 20000, "window_us": 1, "k": 2},
{"relative_ppm": "20000", "window_us": 1}, [20000, 1], "20000", 20000, True]
FILTERINGS = [None, "On", "Off", "Unknown", "unknown", "", "Maybe", 0, False]
# A calibration reference is blank when it is White_Space only (energy-measurement §2.5, as
# the reference implementation trims it); U+001C to U+001F, U+200B and U+FEFF are not.
CALIBRATION_REFS = [None, "cert 7", "", " ", "\t", "\u3000", "\u0085", "\u00a0", "\u2028", "\u001c", "\u001f",
"\u200b", "\ufeff", " x ", "https://calibration.example.org/certificates/0001", 7]
CLASS_FIGURES = [1, 2, 14, 15, 12000, 2 ** 53 - 1, 0]
def classes(ins, rng, n):
"""§2.4.5 rule 2 and its interaction with rule 5: energy claims with every acquisition
label, uncertainty, filtering state and calibration reference above in fixed
combinations — each field varied alone from a valid counter, a valid instrument and a
ModelBased figure — and `n` random combinations of all four with a figure and a grade.
Each is signed, so its verdict is decided at step 2, 4 or later, never by the signature."""
base = ins.receipt("energy_accept")
bases = {
"OnChipCounter": dict(base["body"], energy_provenance="OnChipCounter",
energy_uncertainty={"relative_ppm": 20000, "window_us": 1000000},
energy_counter_filtering="Unknown", grade="measured"),
"CalibratedInstrument": dict(base["body"], energy_provenance="CalibratedInstrument",
energy_uncertainty={"relative_ppm": 5000, "window_us": 1000000},
energy_calibration_ref="cert 7", grade="measured"),
"ModelBased": dict(base["body"], oer_receipt=None),
}
cases = []
for bname, b in bases.items():
b = dict(b, oer_receipt=None)
cases += [(f"{bname}_label_{i}", dict(b, energy_provenance=x)) for i, x in enumerate(LABELS)]
cases += [(f"{bname}_uncertainty_{i}", dict(b, energy_uncertainty=x)) for i, x in enumerate(UNCERTAINTIES)]
cases += [(f"{bname}_filtering_{i}", dict(b, energy_counter_filtering=x)) for i, x in enumerate(FILTERINGS)]
cases += [(f"{bname}_calibration_{i}", dict(b, energy_calibration_ref=x)) for i, x in enumerate(CALIBRATION_REFS)]
cases += [(f"{bname}_figure_{i}", dict(b, joules_micro=x)) for i, x in enumerate(CLASS_FIGURES)]
cases += [(f"{bname}_grade_{g}", dict(b, grade=g)) for g in OER.GRADES + ["certified"]]
for i in range(n):
cases.append((f"random_{i}", dict(base["body"], oer_receipt=None, energy_provenance=rng.choice(LABELS),
energy_uncertainty=rng.choice(UNCERTAINTIES),
energy_counter_filtering=rng.choice(FILTERINGS),
energy_calibration_ref=rng.choice(CALIBRATION_REFS),
joules_micro=rng.choice(CLASS_FIGURES), grade=rng.choice(OER.GRADES))))
for name, body in cases:
ins.add("class_" + name, CV.jcs(sign(dict(base, body=body), METER)))
# §3.1 step 2 reads a body's members, then each member's type and encoding together in its
# table's order, a nested object's members where the table reaches it and a lawful-basis
# body's `record` first. One defect shows where each is caught; two at different positions
# show the order. Per accepted corpus claim: its signer and, in table order, each position
# (a member, or a member of a nested object) with the values put there. DELETE removes the
# member; a position ending in EXTRA adds a member the table does not have. Values stay in
# what the canonical form can write, so every input is signed — numbers at the edges of
# what a claim may hold are the `numbers` family's.
DELETE = object()
EXTRA = "+"
BODY_DEFECTS = {
"lawful_basis_declaration_accept": ("OPERATOR", [
(("record",), ["consent", "decision", 5, DELETE]),
(("vocabulary",), [5, None, "w3id.org/dpv", "", "https://w3id.org/ dpv", DELETE]),
(("fragment",), [5, "zz", "AB" * 32, "ab", DELETE]),
(("purposes",), ["x", [], [5], {}]),
(("purposes", 0, "purpose"), [5, "dpv:A B", "", "dpv:\u3000", DELETE]),
(("purposes", 0, "basis"), [5, None, "", "a b", DELETE]),
(("purposes", 0, "data"), ["x", [5], [""], ["a b"], [], DELETE]),
(("purposes", 0, EXTRA), [1]),
(("purposes", 1, "purpose"), [5, "dpv:A B", "dpv:ServiceProvision", DELETE]),
(("purposes", 1, "basis"), [5, "", "a\tb", DELETE]),
(("purposes", 1, "data"), [[5], ["x y"], DELETE]),
(("purposes", 1, EXTRA), [1]),
((EXTRA,), [1]),
]),
"lawful_basis_decision_accept": ("VIEWER", [
(("record",), ["declaration", "x", 5, DELETE]),
(("vocabulary",), [5, "dpv", DELETE]),
(("declaration",), [5, "zz", "AB" * 32, DELETE]),
(("purpose",), [5, "", "a b", DELETE]),
(("outcome",), [5, "", DELETE]),
((EXTRA,), [1]),
]),
"energy_on_chip_counter_accept": ("METER", [
(("joules_micro",), ["1", 0, True, None, DELETE]),
(("energy_provenance",), [5, "Measured", "HwShunt", DELETE]),
(("energy_uncertainty",), ["x", [1, 1], None, {}, DELETE]),
(("energy_uncertainty", "relative_ppm"), ["1", 0, DELETE]),
(("energy_uncertainty", "window_us"), [None, 0, DELETE]),
(("energy_uncertainty", EXTRA), [2]),
(("energy_counter_filtering",), [5, None, "Maybe", DELETE]),
(("energy_calibration_ref",), [5, "cert", DELETE]),
(("coverage",), [5, "Most", DELETE]),
(("grade",), [5, "certified", DELETE]),
(("method",), [5, "x", "", DELETE]),
(("work_units",), ["1", 0, DELETE]),
(("work_unit",), [5, "", "a b", DELETE]),
(("oer_receipt",), [5, "md5:1", DELETE]),
((EXTRA,), [1]),
]),
"soft_binding_accept": ("PUBLISHER", [
(("alg",), [5, "", "a b", "bare", DELETE]),
(("registry",), [5, "x", DELETE]),
(("binding_type",), [5, "hash", DELETE]),
(("value",), [5, "zz", "ABCD", "", DELETE]),
(("resolution_endpoint",), [5, "x", "http://x/", "https://u@x/", DELETE]),
((EXTRA,), [1]),
]),
"binding_recovery_accept": ("RESOLVER", [
(("alg",), [5, "", "bare", DELETE]),
(("binding_type",), [5, "hash", DELETE]),
(("value",), [5, "zz", "", DELETE]),
(("declaration",), [5, "zz", DELETE]),
(("resolution_endpoint",), [5, "x", "http://x/", DELETE]),
(("observed",), [5, "zz", DELETE]),
(("attests",), [5, "object", DELETE]),
((EXTRA,), [1]),
]),
"log_inclusion_accept": ("PUBLISHER", [
(("log",), [5, "x", DELETE]),
(("profile",), [5, "", DELETE]),
(("entry",), [5, "", "a b", DELETE]),
(("log_receipt",), [5, "zz", "", DELETE]),
((EXTRA,), [1]),
]),
}
def edit_at(body, path, value):
"""Put `value` at `path` in `body` (DELETE removes the member there; a path ending in
EXTRA adds the member `note`). False if an earlier edit left no object on the path."""
*parents, last = path
node = body
for p in parents:
if isinstance(p, int):
if not (isinstance(node, list) and p < len(node)):
return False
elif not (isinstance(node, dict) and p in node):
return False
node = node[p]
if not isinstance(node, dict):
return False
if last == EXTRA:
node["note"] = value
elif value is DELETE:
node.pop(last, None)
else:
node[last] = value
return True
def bodies(ins):
keys = {"OPERATOR": OPERATOR, "VIEWER": VIEWER, "METER": METER, "PUBLISHER": PUBLISHER, "RESOLVER": RESOLVER}
for name, (signer, positions) in BODY_DEFECTS.items():
base = ins.receipt(name)
singles = [(path, i, value) for path, values in positions for i, value in enumerate(values)]
cases = [[s] for s in singles]
for a, first in enumerate(singles):
for second in singles[a + 1:]:
p, q = first[0], second[0]
if p[:len(q)] != q and q[:len(p)] != p: # different positions, neither inside the other
cases.append([first, second])
for case in cases:
body = copy.deepcopy(base["body"])
if not all(edit_at(body, path, value) for path, _, value in case):
continue
label = "+".join(".".join(map(str, path)) + f"#{i}" for path, i, _ in case)
ins.add(f"body_{name[:-len('_accept')]}_{label}", CV.jcs(sign(dict(base, body=body), keys[signer])))
def figures(ins, rng, n):
oer_t = ins.by_name["energy_matches_oer_accept"]["context"]["oer"]["receipt"]
claim_t = ins.receipt("energy_matches_oer_accept")
keys = {OER_SIGNER: pk(METER)}
cases = [(f, uj) for f in FIGURES + NUMERIC_FIGURES + RANGE_FIGURES for uj in (1, 12000, 12001, 11999, 2, 3)]
for _ in range(n):
uj = rng.randrange(1, 10 ** rng.randrange(1, 16)) # a claim's integers stop at 2^53 - 1
frac = "".join(rng.choice("0123456789") for _ in range(rng.randrange(0, 50)))
pick = rng.randrange(4)
if pick == 0: # exactly half-way, with trailing zeros
f = f"{uj // 10**6}.{uj % 10**6:06d}5" + "0" * rng.randrange(0, 40)
elif pick == 1: # just either side of half-way
f = f"{uj // 10**6}.{uj % 10**6:06d}{rng.choice(['49', '50', '51'])}{frac}"
elif pick == 2: # exponent form of the same digits
s = f"{uj}5{frac}"
f = f"{s}e-{len(s) - len(str(uj)) + 6}"
else: # a double near the figure
f = (uj + rng.choice([0.5, 0.4999999, 0.5000001, 0.25])) / 1e6
cases.append((f, rng.choice([uj, uj + 1, uj - 1 if uj > 1 else uj])))
for f, uj in cases:
r = seal_oer(oer_t, f)
body = dict(claim_t["body"], joules_micro=uj, oer_receipt=r["receipt_id"])
c = sign(dict(claim_t, body=body), METER)
ins.add(f"figure_{str(f)[:40]}_uj{uj}", CV.jcs(c), {"oer": {"receipt": r, "keys": keys}})
def sums(ins, rng, n):
"""OER receipts whose breakdown or floor exercises §6's exact arithmetic: large terms
that cancel, differences at and one unit past the 1e-9 tolerance, floors exactly on
the 1% bound, and quantities at §4's limits. The claim names each receipt with the
figure's microjoules, so the verdict turns on whether each OER verifier accepts the
receipt."""
oer_t = ins.by_name["energy_matches_oer_accept"]["context"]["oer"]["receipt"]
claim_t = ins.receipt("energy_matches_oer_accept")
keys = {OER_SIGNER: pk(METER)}
e37 = "1" + "0" * 37
cases = [
("cancel_1e24", "0.012", ["1000000000000000000000000.012", "-1000000000000000000000000", "0", "0", "0"], None),
("cancel_1e30", "0.012", ["1000000000000000000000000000000.012", "-1000000000000000000000000000000", "0", "0", "0"], None),
("cancel_1e37", "0.012", ["10000000000000000000000000000000000000.012", "-" + e37, "0", "0", "0"], None),
("wide_1e37_1e-38", e37, [e37, "0." + "0" * 37 + "1", "0", "0", "0"], None),
("at_tolerance", "9999999990000000000000000000000000000", [e37, "0", "0", "0", "0"], None),
("past_tolerance", "9999999989999999999999999999999999999", [e37, "0", "0", "0", "0"], None),
("at_tolerance_unit", "1.000000001", ["2", "-1", "0", "0", "0"], None),
("past_tolerance_unit", "1.000000001000000000000000000000000001", ["2", "-1", "0", "0", "0"], None),
("negative_total", "-0.012", ["-0.012", "0", "0", "0", "0"], None),
("floor_tiny_temperature", "0.012", None, (256, "1e-38", "0")),
("floor_huge_temperature", "0.012", None, (256, "9e37", "1.6e+17")),
("floor_zero_bits", "0.012", None, (0, "300", "0")),
("floor_zero_bits_tiny_floor_j", "0.012", None, (0, "300", "1e-38")),
("floor_bits_beyond_64", "0.012", None, (2 ** 64, "300", "1e-18")),
("floor_bits_float", "0.012", None, (256.0, "300", "1.838e-18")),
]
# floor_j exactly on each 1% bound, and one unit of its last digit either side: §6's
# check is exact, so a difference exactly at the tolerance passes and one past it does
# not, in both verifiers or neither.
with localcontext(OER.EXACT):
for bits in (1, 256, 510, 4096, 2 ** 40):
for temp in ("300", "3.000000e+02", "0.001", "77.5"):
exact = Decimal(bits) * Decimal("1.380649e-23") * Decimal(temp) * Decimal("0.6931471805599453")
for side in ("0.99", "1.01"):
on = exact * Decimal(side)
unit = Decimal((0, (1,), on.as_tuple().exponent))
for name, fj in (("on", on), ("below", on - unit), ("above", on + unit)):
cases.append((f"floor_{name}_bound_{bits}_{temp}_{side}", "0.012", None, (bits, temp, format(fj, "e"))))
def term():
whole = "".join(rng.choice("0123456789") for _ in range(rng.randrange(1, 20))).lstrip("0") or "0"
frac = "".join(rng.choice("0123456789") for _ in range(rng.randrange(1, 25)))
return ("-" if rng.random() < 0.3 else "") + whole + "." + frac
for _ in range(n):
# Five random terms, some negative, up to 43 digits (so some exceed §4's 34 and make
# the receipt malformed), and a total exactly at, inside or outside the tolerance of
# their sum.
terms = [term() for _ in range(5)]
with localcontext(OER.EXACT):
total = sum(Decimal(t) for t in terms)
tol = Decimal("1e-9") * max(Decimal(1), abs(total))
jt = total + rng.choice([Decimal(0), tol, -tol, tol * 2, Decimal("1e-30")])
cases.append((f"random_sum_{len(cases)}", format(jt, "f"), terms, None))
for name, jt, terms, floor in cases:
r = seal_oer(oer_t, jt, terms, floor)
uj = Decimal(jt) * 1000000 if OER.DECIMAL.fullmatch(jt) else 1
uj = max(1, min(int(abs(uj).to_integral_value()), 2 ** 53 - 1))
body = dict(claim_t["body"], joules_micro=uj, oer_receipt=r["receipt_id"])
ins.add("sum_" + name, CV.jcs(sign(dict(claim_t, body=body), METER)), {"oer": {"receipt": r, "keys": keys}})
def readings(ins):
"""oer-receipts §4 "Numbers": numbers a receipt body must not carry — `-0` however
spelt, fractions, exponents, integers beyond I-JSON's range — beside the range's own
bounds, in figures and in members no check reads. A body holding a forbidden number is
`malformed` before its content address is computed, so the verdict is the same however
a reader would have rounded it. And a delivered object's microjoules spelt as numbers
the corpus does not give them as."""
oer_t = ins.by_name["energy_matches_oer_accept"]["context"]["oer"]["receipt"]
claim_t = ins.receipt("energy_matches_oer_accept")
keys = {OER_SIGNER: pk(METER)}
def k_floor(bits): # temperature_k "300", computed as §6 fixes it
with localcontext(OER.EXACT):
return format(Decimal(bits) * OER.KBOLTZ * Decimal(300) * OER.LN2, "e")
def at(path, spelling):
def edit(r):
*outer, last = path
for p in outer:
r = r[p]
r[last] = raw(spelling)
return edit
def both(*edits):
return lambda r: [e(r) for e in edits]
zero_terms = ["0.012000", "0", "0", "0", "0"]
cases = [ # (name, joules_total, breakdown, floor, edit, microjoules)
("term_neg_zero", "0.012000", zero_terms, None, at(("energy", "breakdown", "movement_j"), "-0"), 12000),
("term_neg_zero_float", "0.012000", zero_terms, None, at(("energy", "breakdown", "movement_j"), "-0.0"), 12000),
("term_neg_zero_exp", "0.012000", zero_terms, None, at(("energy", "breakdown", "movement_j"), "-0e7"), 12000),
("total_neg_zero", "0", ["0", "0", "0", "0", "0"], None,
both(at(("energy", "joules_total"), "-0"), at(("energy", "breakdown", "compute_j"), "-0")), 1),
("latency_neg_zero", "0.012000", None, None, at(("work", "latency_s"), "-0"), 12000),
("status_neg_zero", "0.012000", None, None, at(("work", "status"), "-0"), 12000),
("status_i64_min", "0.012000", None, None, at(("work", "status"), "-9223372036854775808"), 12000),
("status_below_i64", "0.012000", None, None, at(("work", "status"), "-9223372036854775809"), 12000),
("status_u64_max", "0.012000", None, None, at(("work", "status"), "18446744073709551615"), 12000),
("status_i_json_max", "0.012000", None, None, at(("work", "status"), "9007199254740991"), 12000),
("status_i_json_min", "0.012000", None, None, at(("work", "status"), "-9007199254740991"), 12000),
("status_beyond_i_json", "0.012000", None, None, at(("work", "status"), "9007199254740992"), 12000),
("status_below_i_json", "0.012000", None, None, at(("work", "status"), "-9007199254740992"), 12000),
("status_one_point_zero", "0.012000", None, None, at(("work", "status"), "1.0"), 12000),
("status_exponent", "0.012000", None, None, at(("work", "status"), "1e0"), 12000),
("status_beyond_u64", "0.012000", None, None, at(("work", "status"), "18446744073709551616"), 12000),
("status_1e30_integer", "0.012000", None, None, at(("work", "status"), "1" + "0" * 30), 12000),
("status_308_digits", "0.012000", None, None, at(("work", "status"), "1" + "0" * 308), 12000),
("status_exponent_zeros", "0.012000", None, None, at(("work", "status"), "1e" + "0" * 400 + "5"), 12000),
("status_underflow", "0.012000", None, None, at(("work", "status"), "-1e-400"), 12000),
("bits_beyond_u64", "0.012000", None, (2 ** 64, "300", k_floor(float(2 ** 64))),
at(("floor", "bits_committed"), "18446744073709551616"), 12000),
("bits_1e30_integer", "0.012000", None, (10 ** 30, "300", k_floor(1e30)),
at(("floor", "bits_committed"), "1" + "0" * 30), 12000),
("bits_neg_zero", "0.012000", None, (0, "300", "0"), at(("floor", "bits_committed"), "-0"), 12000),
("total_beyond_u64_micro", "18446744073709.551616", ["18446744073709.551616", "0", "0", "0", "0"], None,
at(("energy", "joules_total"), "18446744073709551616e-6"), 2 ** 53 - 1),
("total_number_spelt_long", "0.012000", zero_terms, None,
at(("energy", "joules_total"), "0.01200000000000000000000000000000000000000000000001"), 12000),
]
for name, jt, terms, floor, edit, uj in cases:
r = seal_oer(oer_t, jt, terms, floor, edit)
body = dict(claim_t["body"], joules_micro=uj, oer_receipt=r["receipt_id"])
c = CV.jcs(sign(dict(claim_t, body=body), METER))
ins.add(f"reading_{name}", c, {"oer": {"receipt": r, "keys": keys}})
delivery = ins.by_name["energy_matches_delivery_accept"]
for spelling in ["12000", "-0", "12000.0", "1.2e4", "18446744073709551616", "-12000"]:
ctx = dict(delivery["context"], object=dict(delivery["context"]["object"], joules_micro=raw(spelling)))
ins.add("delivered_joules_" + spelling[:24], delivery["receipt"], ctx)
# (name, spelling) of a value placed in an OER receipt's `work.outcome`, each in a file of
# its own: whether the file is read at all is the first thing compared.
DOCUMENTS = [
("control", '"encoded"'), ("max_double", "1.7976931348623157e308"), ("below_max_half_ulp", "1.7976931348623158e308"),
("past_max_half_ulp", "1.7976931348623159e308"), ("e400", "1e400"), ("neg_e400", "-1e400"), ("e308_8", "1.8e308"),
("digits_400", "1" + "0" * 400), ("neg_digits_400", "-1" + "0" * 400), ("zero_e_huge", "0e99999999999999999999"),
("one_e_huge", "1e99999999999999999999"), ("one_e_neg_huge", "1e-99999999999999999999"),
("nan", "NaN"), ("infinity", "Infinity"), ("neg_infinity", "-Infinity"),
("lone_high_surrogate", '"\\ud800"'), ("lone_low_surrogate", '"\\udc00"'), ("paired_surrogates", '"\\ud83d\\ude00"'),
("high_surrogate_then_letter", '"\\ud800\\u0041"'),
# A repeated member name, in an object no check reads, and a repeated figure.
("repeated_member", '{"a":1,"a":2}'), ("repeated_member_same_value", '{"a":1,"a":1}'),
("distinct_members", '{"a":1,"b":2}'),
]
# work.outcome sits at level 7 of a corpus file ({vectors:[{context:{oer:{receipt:{work:
# ...}}}}]}), so arrays nested d deep inside it reach level 7 + d; §4 reads 64 levels.
DOCUMENTS += [(f"nesting_to_level_{7 + d}", "[" * d + "]" * d) for d in (55, 56, 57, 58, 121, 200, 1100, 5000)]
def documents(corpus):
"""One corpus-format file per DOCUMENTS entry: an energy claim naming an OER receipt that
holds the value, sealed over §4's reading where the text is one §4 reads."""
ins = Inputs(corpus)
oer_t = ins.by_name["energy_matches_oer_accept"]["context"]["oer"]["receipt"]
claim_t = ins.receipt("energy_matches_oer_accept")
keys = {OER_SIGNER: pk(METER)}
out = []
for name, spelling in DOCUMENTS:
def edit(r, spelling=spelling):
r["work"]["outcome"] = raw(spelling)
try:
r = seal_oer(oer_t, "0.012000", None, None, edit)
except (ValueError, RecursionError):
# §4 does not read the receipt's own text. Sealed with a placeholder, which only
# matters if a verifier reads the file anyway — and then the verdicts differ.
r = seal_oer(oer_t, "0.012000")
edit(r)
body = dict(claim_t["body"], joules_micro=12000, oer_receipt=r["receipt_id"])
one = Inputs(corpus)
one.add("document_" + name, CV.jcs(sign(dict(claim_t, body=body), METER)), {"oer": {"receipt": r, "keys": keys}})
out.append((name, text_of({"about": "one differential input", "vectors": one.out})))
return out
def reports(ins, rng, n):
"""measurement and operating-point claims (§2.4.8-§2.4.9) around the edges of their
rules: brackets at and one ppm below the least declaration, figures one off, ties at a
half microjoule, the ratio thresholds, flat brackets and a held value, products past 128 bits,
every pattern of liveness facts; operating points in every presence combination; and
reports and energy claims against figure views and labelled objects."""
m0 = ins.receipt("measurement_accept")
fig0 = ins.by_name["measurement_accept"]["context"]["figure"]
op0 = ins.receipt("operating_point_accept")
hw = ["OnChipCounter", "CalibratedInstrument"]
def summary(n_, mean, sd, live=None, run=None):
live = n_ if live is None else live
return {"n": n_, "mean_uw": mean, "sd_uw": sd, "live_intervals": live, "longest_live_run": n_ if run is None else run}
def class_fields(label, ppm, window, figure):
b = {"energy_provenance": label, "energy_uncertainty": None, "energy_counter_filtering": None,
"energy_calibration_ref": None}
if label == "OnChipCounter":
b.update(energy_uncertainty={"relative_ppm": ppm, "window_us": window}, energy_counter_filtering="Unknown")
elif label == "CalibratedInstrument":
b.update(energy_uncertainty={"relative_ppm": ppm, "window_us": window},
energy_calibration_ref="https://calibration.example.org/certificates/0001")
return dict(b, joules_micro=figure)
def report(br, label, ppm, window, figure, name, ctx=None):
body = dict(m0["body"], bracket=br, **class_fields(label, ppm, window, figure))
ins.add(name, CV.jcs(sign(dict(m0, body=body), METER)), ctx)
def no_reader(br, label, ppm, window, figure, name, ctx=None):
body = dict(m0["body"], bracket=br, power_reader=None, **class_fields(label, ppm, window, figure))
ins.add(name, CV.jcs(sign(dict(m0, body=body), METER)), ctx)
def around(name, br, labels=("OnChipCounter", "CalibratedInstrument", "ModelBased")):
f = CV.bracket_figure(br)
for label in labels:
if f is None:
report(br, label, 300_000, br["window_us"], 1, f"{name}_nofig_{label}")
continue
p = CV.least_ppm(br, f) or 2 ** 32 - 1
for d_ppm, d_fig, d_win in [(0, 0, 0), (-1, 0, 0), (1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1)]:
ppm = max(1, min(2 ** 32 - 1, p + d_ppm))
fig = f + d_fig
if fig <= 0 or fig > CV.MAX_INT:
continue
report(br, label, ppm, br["window_us"] + d_win, fig, f"{name}_{label}_{d_ppm}_{d_fig}_{d_win}")
def bracket(idle, busy, t, c, nn, opening):
return {"idle": idle, "busy": busy, "window_us": t, "completions": c, "in_flight": nn, "opening": opening}
# Fixed edges.
around("half_tie", bracket(summary(10, 20_000_000, 1_000), summary(14, 21_000_000, 1_000), 2_500_000, 1_000_000, 1, "running"))
around("flat_idle", bracket(summary(10, 20_000_000, 0), summary(14, 30_000_000, 115_471), 3_000_000, 60, 10, "from-rest"))
around("flat_busy", bracket(summary(10, 20_000_000, 115_471), summary(14, 30_000_000, 0), 3_000_000, 60, 10, "from-rest"))
around("held_value", bracket(summary(10, 5_000_000, 0, 1, 1), summary(14, 10_500_000, 2_980_708, 2, 1), 3_000_000, 60, 10, "from-rest"))
# A report that names no power reader, with and without a bracket, under each class.
for label in ("OnChipCounter", "CalibratedInstrument", "ModelBased", "Estimator"):
unit_br = m0["body"]["bracket"]
f = CV.bracket_figure(unit_br)
p = CV.least_ppm(unit_br, f)
for br in (unit_br, None):
no_reader(br, label, p, unit_br["window_us"], f, f"no_reader_{label}_{br is None}", fig0)
around("wide", bracket(summary(10, 20_000_000, 115_471), summary(14, 30_000_000, 115_471), 10 ** 11, 10 ** 13, 64, "from-rest"))
for k in (10, 3, 1):
for d in (0, -1):
around(f"ratio_{k}_{d}", bracket(summary(10, 20_000_000, 100_000), summary(14, 20_000_000 + k * 100_000 + d, 100_000),
3_000_000, 60, 10, "from-rest"), labels=("OnChipCounter", "ModelBased"))
# Liveness facts: every pattern around the thresholds, in each bracket.
for n_ in (4, 5, 6, 10, 14):
for live in sorted({0, 1, 2, 4, 5, n_ * 4 // 5, n_ * 4 // 5 + 1, n_ - 1, n_, n_ + 1}):
for run in sorted({0, 1, 2, 3, live, live + 1}):
idle = summary(10, 20_000_000, 115_471)
busy = summary(n_, 30_000_000, 115_471, live, run)
br = bracket(idle, busy, 3_000_000, 60, 10, "from-rest")
f = CV.bracket_figure(br)
p = CV.least_ppm(br, f)
report(br, "OnChipCounter", p, 3_000_000, f, f"live_busy_{n_}_{live}_{run}")
br2 = bracket(summary(n_, 20_000_000, 115_471, live, run), summary(14, 30_000_000, 115_471), 3_000_000, 60, 10, "from-rest")
report(br2, "OnChipCounter", CV.least_ppm(br2, f), 3_000_000, f, f"live_idle_{n_}_{live}_{run}")
# Random brackets.
for i in range(n):
ni, nb = rng.choice([1, 2, 3, 5, 10, 14, 50]), rng.choice([1, 2, 3, 5, 10, 14, 50])
idle_mean = rng.randrange(0, 50_000_000)
busy_mean = idle_mean + rng.choice([-1, 0, 1, rng.randrange(1, 40_000_000)])
sd = lambda: rng.choice([0, 1, rng.randrange(1, 5_000_000)]) # noqa: E731
live = lambda m: rng.choice([m, m - 1, 0, 2, 5]) # noqa: E731
idle = summary(ni, idle_mean, sd(), live(ni))
busy = summary(nb, max(0, busy_mean), sd(), live(nb))
t = rng.choice([0, 1, rng.randrange(1, 10 ** 7), rng.randrange(1, 10 ** 12)])
c = rng.choice([0, 1, rng.randrange(1, 10 ** 6), rng.randrange(1, 10 ** 13)])
nn = rng.choice([0, 1, rng.randrange(1, 65)])
br = bracket(idle, busy, t, c, nn, rng.choice(["from-rest", "running"]))
f = CV.bracket_figure(br)
label = rng.choice(hw + ["ModelBased", "Estimator"])
if f is None:
report(br, label, 300_000, max(1, t), rng.randrange(1, 10 ** 6), f"random_{i}_nofig")
continue
p = CV.least_ppm(br, f)
ppm = max(1, min(2 ** 32 - 1, (p or 2 ** 32 - 1) + rng.choice([0, 0, -1, 1, 1000])))
if f * ppm < 288_676:
ppm = 288_676 // f + 1
report(br, label, ppm, rng.choice([max(1, t), t + 1]), f + rng.choice([0, 0, 0, 1, -1]) or 1, f"random_{i}")
# Operating points: every presence combination, and points that are empty, not terms,
# or not integers.
pts = [None, {}, {"height": 1080}, {"height": 720}, {"frame rate": 1}, {"height": "1080"}, {"height": -1}]
pred = op0["body"]["prediction"]
for ri, r in enumerate(pts):
for ci, c in enumerate(pts):
for di, d in enumerate(pts):
for pi, pr in enumerate([None, pred]):
body = dict(op0["body"], op_requested=r, op_committed=c, op_delivered=d, prediction=pr)
ins.add(f"op_{ri}{ci}{di}{pi}", CV.jcs(sign(dict(op0, body=body), METER)) if all(
not isinstance(x, dict) or all(isinstance(v, int) and v >= 0 for v in x.values()) for x in (r, c, d))
else text_of(sign_raw(dict(op0, body=body), METER)), {"figure": fig0})
# Figure views that agree and disagree in each member.
e = json.loads(fig0["claim"])
eb = e["body"]
base = {"profile": "moq-jwp:claim-link", "identity": CV.link_hash(e), "group": None,
"figures": [{"index": None, "joules_micro": eb["joules_micro"], "energy_provenance": eb["energy_provenance"],
"energy_uncertainty": eb["energy_uncertainty"],
"energy_counter_filtering": eb["energy_counter_filtering"]}],
"signer": pk(METER)}
variants = [("same", {}), ("profile", {"profile": "wai:asset-receipt-id"}), ("identity", {"identity": "ab" * 32}),
("group", {"group": "cd" * 32}), ("signer", {"signer": pk(OPERATOR)}), ("no_signer", {"signer": None})]
fig_variants = [("figure_plus", {"joules_micro": eb["joules_micro"] + 1}), ("unlabelled", {"energy_provenance": None,
"energy_uncertainty": None, "energy_counter_filtering": None}),
("filtering_absent", {"energy_counter_filtering": None}), ("filtering_off", {"energy_counter_filtering": "Off"}),
("ppm", {"energy_uncertainty": dict(eb["energy_uncertainty"], relative_ppm=eb["energy_uncertainty"]["relative_ppm"] + 1)}),
("index", {"index": 0})]
for name, edit in variants:
ins.add("view_" + name, CV.jcs(m0), {"figure": {"view": dict(base, **edit)}})
ins.add("view_op_" + name, CV.jcs(op0), {"figure": {"view": dict(base, **edit)}})
for name, edit in fig_variants:
fv = [{k: v for k, v in dict(base["figures"][0], **edit).items() if v is not None or k == "index"}]
ins.add("view_fig_" + name, CV.jcs(m0), {"figure": {"view": dict(base, figures=fv)}})
ins.add("view_none_given_claim_not_json", CV.jcs(m0), {"figure": {"claim": "not json"}})
# Energy claims against labelled objects.
ec = ins.receipt("energy_on_chip_counter_accept")
b = ec["body"]
lab = {"energy_provenance": "OnChipCounter", "energy_uncertainty": b["energy_uncertainty"], "energy_counter_filtering": "Unknown"}
for name, j, extra in [("same", b["joules_micro"], lab), ("no_label", b["joules_micro"], {}),
("filtering_absent", b["joules_micro"], {k: v for k, v in lab.items() if k != "energy_counter_filtering"}),
("model", b["joules_micro"], {"energy_provenance": "ModelBased"}),
("ppm", b["joules_micro"], dict(lab, energy_uncertainty={"relative_ppm": 20_001, "window_us": 1_000_000})),
("figure", b["joules_micro"] + 1, lab)]:
ins.add("labelled_object_" + name, CV.jcs(ec), {"object": dict({"content_hash": ec["subject"], "joules_micro": j}, **extra)})
def sign_raw(c, k):
"""`c` signed over its JCS where JCS can serialise it, with a member JCS refuses (a
string or negative point value) left for the verifier to refuse at step 2. The
signature covers a canonical form computed with that member as written."""
c = copy.deepcopy(c)
c.pop("sig", None)
c["signer_pubkey"] = pk(k)
def canon(v):
if isinstance(v, int) and not isinstance(v, bool) and v < 0:
return str(v)
if isinstance(v, dict):
keys = sorted(v, key=lambda x: x.encode("utf-16-be"))
return "{" + ",".join(json.dumps(x, ensure_ascii=False) + ":" + canon(v[x]) for x in keys) + "}"
if isinstance(v, list):
return "[" + ",".join(canon(x) for x in v) + "]"
return CV.jcs(v)
c["sig"] = k.sign(b"moq-jwp:claim\x01" + canon(c).encode()).hex()
return c
def contexts(ins):
decl = ins.receipt("soft_binding_accept")
record = ins.receipt("binding_recovery_accept")
lawful = ins.receipt("lawful_basis_declaration_accept")
decision = ins.receipt("lawful_basis_decision_accept")
e0 = ins.receipt("energy_accept")
e1 = ins.receipt("energy_chain_accept")
ppk = pk(PUBLISHER)
def recovery_case(name, declaration_wire, rec, algs=(WM_ALG,), publisher=ppk):
ctx = {"declaration": declaration_wire, "publisher": publisher, "exact_match_algs": list(algs)}
ins.add(name, CV.jcs(rec), ctx)
def naming(rec_template, declaration, k, **body):
b = dict(rec_template["body"], declaration=CV.link_hash(declaration), **body)
return sign(dict(rec_template, body=b), k)
# A resolver-signed declaration with its signer_pubkey rewritten to the publisher's
# key, and a record naming that forgery: a declaration parsed but not verified.
forged = dict(sign(decl, RESOLVER), signer_pubkey=ppk)
recovery_case("recovery_forged_declaration", CV.jcs(forged), naming(record, forged, RESOLVER))
forged_pub = dict(decl, seq=decl["seq"] + 1) # the publisher's declaration, altered after signing
recovery_case("recovery_altered_declaration", CV.jcs(forged_pub), naming(record, forged_pub, RESOLVER))
recovery_case("recovery_declaration_not_json", "not json", record)
recovery_case("recovery_declaration_not_a_claim", CV.jcs(dict(decl, kind="group")), record)
recovery_case("recovery_declaration_lawful", CV.jcs(lawful), naming(record, lawful, RESOLVER))
recovery_case("recovery_declaration_energy_forged", CV.jcs(dict(e0, seq=5)), naming(record, dict(e0, seq=5), RESOLVER))
recovery_case("recovery_record_alg_differs", CV.jcs(decl), naming(record, decl, RESOLVER, alg="org.example.watermark.2"))
recovery_case("recovery_record_type_differs", CV.jcs(decl), naming(record, decl, RESOLVER, binding_type="fingerprint"))
recovery_case("recovery_record_no_matcher", CV.jcs(decl), record, algs=())
recovery_case("recovery_record_ok", CV.jcs(decl), record)
recovery_case("recovery_record_rogue_publisher", CV.jcs(decl), record, publisher=pk(RESOLVER))
# A decision naming a declaration altered after the operator signed it (a purpose added).
altered = copy.deepcopy(lawful)
altered["body"]["purposes"].append({"purpose": "dpv:PersonalisedAdvertising", "basis": "dpv:Consent", "data": []})
dec_b = dict(decision["body"], declaration=CV.link_hash(altered), purpose="dpv:PersonalisedAdvertising")
ins.add("decision_altered_declaration", CV.jcs(sign(dict(decision, body=dec_b), VIEWER)), {"declaration": CV.jcs(altered)})
ins.add("decision_declaration_not_json", CV.jcs(decision), {"declaration": "{"})
ins.add("decision_declaration_is_decision", CV.jcs(sign(dict(decision, body=dict(decision["body"], declaration=CV.link_hash(decision))), VIEWER)),
{"declaration": CV.jcs(decision)})
ins.add("decision_declaration_is_soft_binding", CV.jcs(sign(dict(decision, body=dict(decision["body"], declaration=CV.link_hash(decl))), VIEWER)),
{"declaration": CV.jcs(decl)})
st = sign(dict(decision, subject_type="receipt"), VIEWER)
ins.add("decision_subject_type_differs", CV.jcs(st), {"declaration": CV.jcs(lawful)})
ins.add("declaration_given_to_a_declaration", CV.jcs(lawful), {"declaration": CV.jcs(lawful)})
ins.add("declaration_given_to_energy", CV.jcs(e0), {"declaration": "not json"})
# Step 5 against a previous claim that does not itself verify.
bad_prev = dict(e0, seq=0, issued_at="2026-09-27T12:00:01Z") # altered after signing
e1_bad = sign(dict(e1, parent_receipt_hash=CV.link_hash(bad_prev)), METER)
ins.add("previous_altered_named", CV.jcs(e1_bad), {"previous": CV.jcs(bad_prev)})
ins.add("previous_altered_head", CV.jcs(sign(dict(e1, parent_receipt_hash=None), METER)), {"previous": CV.jcs(bad_prev)})
ins.add("previous_not_json", CV.jcs(e1), {"previous": "[]"})
ins.add("previous_ok", CV.jcs(e1), {"previous": CV.jcs(e0)})
# §3.2 with the same declarations.
for name, d in [("forged", forged), ("altered", forged_pub), ("ok", decl)]:
ins.add("recover_" + name, None, {"declaration": CV.jcs(d), "publisher": ppk, "observed_value": decl["body"]["value"],
"observed_hash": None, "exact_match_algs": [WM_ALG]}, procedure="recover")
# ------------------------------------------------------------------ run and compare
def run_raw(cmd):
env = dict(os.environ, PYTHONIOENCODING="utf-8", PYTHONDONTWRITEBYTECODE="1")
return subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", env=env)
def run(cmd):
p = run_raw(cmd)
if p.returncode != 0:
sys.exit(f"{' '.join(cmd)} exited {p.returncode}: {p.stderr.strip()[:400]}")
return [line.split("\t", 1) for line in p.stdout.splitlines()]
def document_outcome(p, python):
"""`refused`, the verdict lines, or `crash: ...`. The reference refuses a file with exit
status 1, no output and `not read:` on stderr; anything else that exits non-zero is a
crash. wai-rs exits 1 with its reader's error on stderr."""
if p.returncode == 0:
return "\n".join(p.stdout.splitlines())
if p.stdout or p.returncode != 1 or (python and "not read:" not in p.stderr):
return "crash: exit %d: %s" % (p.returncode, p.stderr.strip().splitlines()[-1:] or "")
return "refused"
def main(argv):
ap = argparse.ArgumentParser()
ap.add_argument("rust_bin")
ap.add_argument("--random", type=int, default=200)
ap.add_argument("--seed", type=int, default=1)
ap.add_argument("--keep")
a = ap.parse_args(argv[1:])
rng = random.Random(a.seed)
corpus = OER.load(open(os.path.join(HERE, "vectors.json"), encoding="utf-8"))
ins = Inputs(corpus)
numbers(ins)
strings(ins)
nesting(ins)
dates(ins, rng, a.random)
endpoints(ins, rng, a.random)
figures(ins, rng, a.random)
sums(ins, rng, a.random)
classes(ins, random.Random(f"classes {a.seed}"), a.random) # its own stream: other families' draws are unchanged
readings(ins)
contexts(ins)
bodies(ins)
reports(ins, random.Random(f"reports {a.seed}"), a.random) # its own stream, as classes()
doc = text_of({"about": "differential inputs; no outcome is declared", "vectors": ins.out})
with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as f:
f.write(doc)
path = f.name
try:
rust = run([a.rust_bin, "verdicts", path])
py = run([sys.executable, os.path.join(HERE, "claim_verify.py"), "verdicts", path])
finally:
if a.keep:
os.replace(path, a.keep)
else:
os.unlink(path)
if len(rust) != len(ins.out) or len(py) != len(ins.out):
sys.exit(f"verdict counts differ: {len(ins.out)} inputs, wai-rs {len(rust)}, python {len(py)}")
differ = [(n, r, p) for (n, r), (_, p) in zip(rust, py) if r != p]
crashes = [(n, v) for n, v in rust + py if v.startswith("crash")]
tally = {}
for _, v in rust:
k = v.split(" link_hash=")[0].split(" ancestor=")[0]
tally[k] = tally.get(k, 0) + 1
oer = "reject step=6 reason=energy_oer_invalid"
for n, r, p in differ:
# Marked, never set apart: the verdicts differ either way.
mark = " (one side's OER verifier refused the receipt)" if oer in (r, p) else ""
print(f"DIFFER {n}{mark}\n wai-rs: {r}\n python: {p}")
for n, v in crashes:
print(f"CRASH {n}: {v}")
print(f"{len(ins.out)} inputs; verdicts: " + ", ".join(f"{k} x{c}" for k, c in sorted(tally.items())))
# Files of their own: read by both or refused by both, and the same verdicts if read.
doc_differ, doc_crash, doc_tally = 0, 0, {}
with tempfile.TemporaryDirectory() as d:
for name, text in documents(corpus):
path = os.path.join(d, name + ".json")
with open(path, "w", encoding="utf-8") as f:
f.write(text)
r = document_outcome(run_raw([a.rust_bin, "verdicts", path]), False)
p = document_outcome(run_raw([sys.executable, os.path.join(HERE, "claim_verify.py"), "verdicts", path]), True)
key = r.split("\t", 1)[-1].split(" link_hash=")[0]
doc_tally[key] = doc_tally.get(key, 0) + 1
if r.startswith("crash") or p.startswith("crash"):
doc_crash += 1
print(f"CRASH document {name}\n wai-rs: {r}\n python: {p}")
elif r != p:
doc_differ += 1
print(f"DIFFER document {name}\n wai-rs: {r}\n python: {p}")
print(f"{len(DOCUMENTS)} documents; wai-rs: " + ", ".join(f"{k} x{c}" for k, c in sorted(doc_tally.items())))
print(f"{len(differ)} differ, {len(crashes)} crash; documents: {doc_differ} differ, {doc_crash} crash")
return 1 if differ or crashes or doc_differ or doc_crash else 0
if __name__ == "__main__":
sys.exit(main(sys.argv))