#!/usr/bin/env python3 """Second verifier for jwp-conformance/ — labelled leaves, labelled group receipts, the group total, the WAI_RECEIPT property, receipt chains per (key, track), objects checked by inclusion alone and the binary form (extensions/jwp-receipts.md §2.2-§2.4.2, §2.7, §3, §3.3, §4.4, §5.7, §5.9, §5.12 and §6.1; moq-streaming-format.md §5). Written from the extension text. It shares no code with wai-rs (`crate::jwp_delivery`), which generates the corpus. It takes from ../claim-conformance/claim_verify.py — itself independent of wai-rs — BLAKE3 written from its specification, jwp-receipts §3.1 step 3's Ed25519 rule (key decoding, small order, S < L, the cofactorless equation through the `cryptography` package), and the claim verifier an energy claim is checked with. python3 jwp_verify.py [vectors.json] verify every vector against its declared outcome python3 jwp_verify.py verdicts print `nameverdict` for every vector, ignoring any declared outcome Exit status 0 only if every vector reports its declared outcome. """ import json, os, sys from collections import deque HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.join(HERE, "..", "claim-conformance")) import claim_verify as CV # noqa: E402 LEAF1 = b"moq-jwp:merkle-leaf\x01" LEAF2 = b"moq-jwp:merkle-leaf\x02" NODE = b"moq-jwp:merkle-node\x01" SIGN1 = b"moq-jwp:group-sign\x01" SIGN2 = b"moq-jwp:group-sign\x02" LINK = b"moq-jwp:receipt-link\x01" OBJECT = b"moq-jwp:object\x01" FLOOR = 288676 # energy-measurement §2.3: joules_micro x relative_ppm, at least U32, U64 = 2 ** 32 - 1, 2 ** 64 - 1 RANK = {"Estimator": 1, "ModelBased": 2, "HwShunt": 3, "OnChipCounter": 4, "CalibratedInstrument": 5} TAG = {"HwShunt": 1, "ModelBased": 2, "Estimator": 3, "OnChipCounter": 4, "CalibratedInstrument": 5} FILTERING = {"Unknown": 0, "Off": 1, "On": 2} CLASS_KEYS = ["energy_provenance", "energy_uncertainty", "energy_counter_filtering", "energy_calibration_ref"] GROUP = ["kind", "track_alias", "group_id", "root_hash", "leaf_count", "joules_total", "group_start", "group_end", "signer_pubkey", "signer_id", "sig", "parent_receipt_hash"] GROUP_LABEL = ["sign_version"] + CLASS_KEYS + ["energy_coverage"] OBJ = ["kind", "track_alias", "group_id", "object_id", "content_hash", "joules_micro", "origin", "merkle_proof"] OBJ_LABEL = ["leaf_version"] + CLASS_KEYS class Refuse(Exception): def __init__(self, step, reason): super().__init__(reason) self.step, self.reason = step, reason def label(self): return f"reject reason={self.reason}" if self.step is None else f"reject step={self.step} reason={self.reason}" class Unreadable(Exception): """A class whose JSON members do not make one class (energy-measurement §2.7).""" def blake3(b): return CV.blake3(b) def u64(v): return isinstance(v, int) and not isinstance(v, bool) and 0 <= v <= U64 def hex_bytes(v, n=None): if not CV.is_hex(v, n): return None return bytes.fromhex(v) def present(m, k): return m.get(k) is not None # ------------------------------------------------------------ classes (§2.7) def read_class(m): """The class a receipt's JSON members name, as energy-measurement §2.7 reads it, or None for no class. Evidence without a class, an unknown class, or evidence a class does not carry make no class: Unreadable.""" label = m.get("energy_provenance") u, f, r = m.get("energy_uncertainty"), m.get("energy_counter_filtering"), m.get("energy_calibration_ref") if label is None: if u is not None or f is not None or r is not None: raise Unreadable() return None if not isinstance(label, str) or label not in TAG: raise Unreadable() if label in ("HwShunt", "ModelBased", "Estimator"): if u is not None or f is not None or r is not None: raise Unreadable() return {"label": label} if not isinstance(u, dict) or not u64(u.get("relative_ppm")) or not u64(u.get("window_us")): raise Unreadable() ppm, win = u["relative_ppm"], u["window_us"] if ppm == 0 or ppm > U32 or win == 0: raise Unreadable() if label == "OnChipCounter": if r is not None: raise Unreadable() if f is None: f = "Unknown" # an unstated filtering state is Unknown, never Off (§2.4) if f not in FILTERING: raise Unreadable() return {"label": label, "ppm": ppm, "window": win, "filtering": f} if f is not None or not isinstance(r, str) or r.strip(CV.WHITE_SPACE) == "": raise Unreadable() return {"label": label, "ppm": ppm, "window": win, "ref": r} def encode(c): """The signed encoding (§2.7); None is tag 0, the unmetered marker.""" if c is None: return b"\x00" b = bytes([TAG[c["label"]]]) if c["label"] == "OnChipCounter": b += c["ppm"].to_bytes(4, "big") + c["window"].to_bytes(8, "big") + bytes([FILTERING[c["filtering"]]]) elif c["label"] == "CalibratedInstrument": ref = c["ref"].encode() b += c["ppm"].to_bytes(4, "big") + c["window"].to_bytes(8, "big") + len(ref).to_bytes(4, "big") + ref return b def label_ok(figure, c): """A class as an optional label (energy-measurement §2.8): never HwShunt, never beside a zero figure, at the resolution floor where it declares an uncertainty.""" if c["label"] == "HwShunt" or figure == 0: return False return "ppm" not in c or figure * c["ppm"] >= FLOOR # ------------------------------------------------------------ the tree (§2.2) def node(a, b): return blake3(NODE + a + b) def leaf(o): if not o["labelled"]: return blake3(LEAF1 + o["content_hash"]) origin = o["origin"].encode() return blake3(LEAF2 + o["content_hash"] + o["joules_micro"].to_bytes(8, "big") + encode(o["class"]) + len(origin).to_bytes(4, "big") + origin) def depth(n): return 0 if n <= 1 else (n - 1).bit_length() # ------------------------------------------------------------ group receipts (§2.4.1, §3 steps 1-3) def read_group(g): """§3 step 1's reading of a group receipt.""" if not isinstance(g, dict) or "kind" not in g: raise Refuse(1, "malformed") if g["kind"] != "group": raise Refuse(1, "wrong_kind") if any(k not in g for k in GROUP) or any(k not in GROUP and k not in GROUP_LABEL for k in g): raise Refuse(1, "malformed") ok = all(u64(g[k]) for k in ("track_alias", "group_id", "leaf_count", "joules_total")) \ and all(isinstance(g[k], str) for k in ("group_start", "group_end", "signer_id")) \ and CV.is_hex(g["root_hash"], 32) and CV.is_hex(g["signer_pubkey"], 32) and CV.is_hex(g["sig"], 64) \ and (g["parent_receipt_hash"] is None or CV.is_hex(g["parent_receipt_hash"], 32)) if not ok: raise Refuse(1, "malformed") out = dict(g, labelled=False) sv = g.get("sign_version") if sv is None: if any(present(g, k) for k in GROUP_LABEL): raise Refuse(1, "group_label_invalid") return out # sign_version is a labelled member (§2.4.1): any value but 2 is not the labelled # form, and is group_label_invalid (§3.3), as leaf_version is object_label_invalid. if not (isinstance(sv, int) and not isinstance(sv, bool) and sv == 2): raise Refuse(1, "group_label_invalid") try: c = read_class(g) except Unreadable: raise Refuse(1, "group_label_invalid") cov = g.get("energy_coverage") if c is None: if cov is not None: raise Refuse(1, "group_label_invalid") cov = "Whole" elif cov not in ("Whole", "Partial"): raise Refuse(1, "group_label_invalid") out.update(labelled=True, cls=c, coverage=cov) return out def payload(g): """§2.3: the legacy payload, or the labelled one — the same fields under the 0x02 domain, then the class, the coverage byte and the parent link.""" b = (SIGN2 if g["labelled"] else SIGN1) + g["track_alias"].to_bytes(8, "big") + g["group_id"].to_bytes(8, "big") b += bytes.fromhex(g["root_hash"]) + g["leaf_count"].to_bytes(8, "big") + g["joules_total"].to_bytes(8, "big") for s in (g["group_start"], g["group_end"]): e = s.encode() b += len(e).to_bytes(4, "big") + e sid = g["signer_id"].encode() b += bytes.fromhex(g["signer_pubkey"]) + len(sid).to_bytes(4, "big") + sid if g["labelled"]: b += encode(g["cls"]) + (b"\x01" if g["cls"] is not None and g["coverage"] == "Partial" else b"\x00") parent = g["parent_receipt_hash"] b += (b"\x01" + bytes.fromhex(parent)) if parent is not None else b"\x00" + bytes(32) return b def link(g): return blake3(LINK + payload(g) + bytes.fromhex(g["sig"])) def verify_group(g): """§3 steps 1-2: the key, the signature under §3.1 step 3's rule, then a labelled group's own checks.""" try: CV.check_signature(bytes.fromhex(g["signer_pubkey"]), bytes.fromhex(g["sig"]), payload(g)) except CV.Reject as r: raise Refuse(1 if r.reason == "bad_signer_key" else 2, r.reason) if g["labelled"]: c, total = g["cls"], g["joules_total"] if c is None: ok = total == 0 and g["coverage"] == "Whole" else: ok = label_ok(total, c) if not ok: raise Refuse(2, "group_label_invalid") def check_parent(g, previous): """§3 step 3 against a verifier that holds one group of the chain, `previous`, as its head. A previous group that does not verify is not held, and neither is one under another key or on another track. Nothing held: a null parent starts the chain, any other is parent_not_held. The head held: its link hash links, a null parent is chain_break (a second chain head), any other parent names a group not held: parent_not_held.""" if previous is not None: if previous["signer_pubkey"] != g["signer_pubkey"] or previous["track_alias"] != g["track_alias"]: previous = None else: try: verify_group(previous) except Refuse: previous = None parent = g["parent_receipt_hash"] if previous is None: if parent is not None: raise Refuse(3, "parent_not_held") return if parent is None: raise Refuse(3, "chain_break") if bytes.fromhex(parent) != link(previous): raise Refuse(3, "parent_not_held") # ------------------------------------------------------------ object receipts (§2.4.2, §3 steps 4-5) def read_object(o): """The object receipt's members. Shape failures are `malformed` at step 4; a label that cannot be read is kept, and refused at step 5.""" if not isinstance(o, dict) or "kind" not in o: raise Refuse(4, "malformed") if o["kind"] != "object": raise Refuse(4, "wrong_kind") if any(k not in o for k in OBJ) or any(k not in OBJ and k not in OBJ_LABEL for k in o): raise Refuse(4, "malformed") p = o["merkle_proof"] ok = all(u64(o[k]) for k in ("track_alias", "group_id", "object_id", "joules_micro")) \ and CV.is_hex(o["content_hash"], 32) and isinstance(o["origin"], str) \ and isinstance(p, dict) and sorted(p) == ["direction_bits", "leaf_index", "siblings"] \ and u64(p["direction_bits"]) and u64(p["leaf_index"]) and isinstance(p["siblings"], list) \ and all(CV.is_hex(s, 32) for s in p["siblings"]) if not ok: raise Refuse(4, "malformed") r = {"track_alias": o["track_alias"], "group_id": o["group_id"], "object_id": o["object_id"], "content_hash": bytes.fromhex(o["content_hash"]), "joules_micro": o["joules_micro"], "origin": o["origin"], "siblings": [bytes.fromhex(s) for s in p["siblings"]], "bits": p["direction_bits"], "index": p["leaf_index"], "labelled": False, "class": None, "unreadable": False} lv = o.get("leaf_version") if lv is None: r["unreadable"] = any(present(o, k) for k in OBJ_LABEL) elif isinstance(lv, int) and not isinstance(lv, bool) and lv == 2: try: r.update(labelled=True, **{"class": read_class(o)}) except Unreadable: r["unreadable"] = True else: r["unreadable"] = True return r def inclusion(o, g): """§3 step 5: the label, the position, the walk.""" if o["unreadable"]: raise Refuse(5, "object_label_invalid") if o["labelled"]: c = o["class"] if (c is None and o["joules_micro"] != 0) or (c is not None and not label_ok(o["joules_micro"], c)): raise Refuse(5, "object_label_invalid") n = g["leaf_count"] if (o["track_alias"] != g["track_alias"] or o["group_id"] != g["group_id"] or o["object_id"] != o["index"] or not o["index"] < n or len(o["siblings"]) != depth(n) or o["bits"] != o["index"]): raise Refuse(5, "proof_position_mismatch") cur = leaf(o) for i, s in enumerate(o["siblings"]): cur = node(s, cur) if (o["bits"] >> i) & 1 else node(cur, s) if cur != bytes.fromhex(g["root_hash"]): raise Refuse(5, "inclusion_proof_failed") def accept(o, g, envelope): """§3 steps 1-5.""" verify_group(g) if blake3(OBJECT + envelope) != o["content_hash"]: raise Refuse(4, "content_hash_mismatch") inclusion(o, g) # ------------------------------------------------------------ the group total (§3) def compose(parts): """The composition rule over (figure, labelled, class) parts. None: no class may be carried; otherwise (class or None for the unmetered marker, coverage).""" any_zero = any(f == 0 for f, _, _ in parts) live = [(f, c) for f, lab, c in parts if f != 0] if any(c is None for _, c in live): return None if not live: return (None, "Whole") cov = "Partial" if any_zero else "Whole" weakest = min(RANK[c["label"]] for _, c in live) if weakest <= 2: return ({"label": "ModelBased" if weakest == 2 else "Estimator"}, cov) if any("ppm" not in c for _, c in live): return None num = sum(f * c["ppm"] for f, c in live) den = sum(f for f, _ in live) ppm = -(-num // den) win = max(c["window"] for _, c in live) if weakest == 4: fs = {c["filtering"] for _, c in live if c["label"] == "OnChipCounter"} return ({"label": "OnChipCounter", "ppm": ppm, "window": win, "filtering": fs.pop() if len(fs) == 1 else "Unknown"}, cov) refs = {c["ref"] for _, c in live} if len(refs) != 1: return None return ({"label": "CalibratedInstrument", "ppm": ppm, "window": win, "ref": refs.pop()}, cov) def same_but_uncertainty(a, b): if a["label"] != b["label"]: return False if a["label"] == "OnChipCounter": return a["filtering"] == b["filtering"] if a["label"] == "CalibratedInstrument": return a["ref"] == b["ref"] return True def signed_label(c, cov, checked): u = f" relative_ppm={c['ppm']} window_us={c['window']}" if "ppm" in c else "" return f"read signed class={c['label']}{u} coverage={cov} parts={'checked' if checked else 'unchecked'}" def total(inp): g = read_group(inp["group"]) verify_group(g) held = {} for d in inp["delivered"]: try: o = read_object(d["object"]) accept(o, g, bytes.fromhex(d["envelope"])) except Refuse: continue held.setdefault(o["index"], []).append(o) complete = g["leaf_count"] > 0 and len(held) == g["leaf_count"] and all(len(v) == 1 for v in held.values()) def own(checked): if not g["labelled"]: return "read unlabelled" if g["cls"] is None: return "read unmetered" return signed_label(g["cls"], g["coverage"], checked) if not complete: return own(False) parts = [held[i][0] for i in sorted(held)] if sum(o["joules_micro"] for o in parts) != g["joules_total"]: raise Refuse(None, "group_total_mismatch") comp = compose([(o["joules_micro"], o["labelled"], o["class"]) for o in parts]) if not g["labelled"]: if all(o["joules_micro"] == 0 for o in parts): return "read unmetered" if comp is None or comp[0] is None: return "read unlabelled" return f"read weakest class={comp[0]['label']} coverage={comp[1]}" if comp is None: raise Refuse(None, "group_class_disagrees") got, want = g["cls"], comp[0] if (got is None) != (want is None) or (got is not None and not same_but_uncertainty(got, want)): raise Refuse(None, "group_class_disagrees") if g["coverage"] != comp[1]: raise Refuse(None, "group_coverage_disagrees") if got is not None and "ppm" in got and (got["ppm"] < want["ppm"] or got["window"] != want["window"]): raise Refuse(None, "group_uncertainty_below_parts") return own(True) # ------------------------------------------------------------ the WAI_RECEIPT property def property_class(p): """moq-streaming-format §5's rules on the property alone (`property_invalid`); the class it names, or None for a zero figure.""" bad = Refuse(None, "property_invalid") acq, ev = p["acquisition"], bytes.fromhex(p["acquisition_evidence"]) if p["joules_micro"] == 0: if acq != "" or ev: raise bad return None if acq in ("HwShunt", "ModelBased", "Estimator"): if ev: raise bad return {"label": acq} if acq == "OnChipCounter": if len(ev) != 13 or ev[12] > 2: raise bad c = {"label": acq, "ppm": int.from_bytes(ev[0:4], "big"), "window": int.from_bytes(ev[4:12], "big"), "filtering": {0: "Unknown", 1: "Off", 2: "On"}[ev[12]]} elif acq == "CalibratedInstrument": if len(ev) < 16 or int.from_bytes(ev[12:16], "big") != len(ev) - 16: raise bad try: ref = ev[16:].decode() except UnicodeDecodeError: raise bad if ref.strip(CV.WHITE_SPACE) == "": raise bad c = {"label": acq, "ppm": int.from_bytes(ev[0:4], "big"), "window": int.from_bytes(ev[4:12], "big"), "ref": ref} else: raise bad if c["ppm"] == 0 or c["window"] == 0 or p["joules_micro"] * c["ppm"] < FLOOR: raise bad return c def claim_class(b): """An energy claim's class in this file's form.""" label = b["energy_provenance"] if label in ("OnChipCounter", "CalibratedInstrument"): u = b["energy_uncertainty"] c = {"label": label, "ppm": u["relative_ppm"], "window": u["window_us"]} if label == "OnChipCounter": c["filtering"] = b["energy_counter_filtering"] else: c["ref"] = b["energy_calibration_ref"] return c return {"label": label} def prop(inp): p = inp["property"] c = property_class(p) if "claim" in inp: try: claim = CV.verify_claim(inp["claim"]) except CV.Reject: return "read unsigned" if c is not None else "read unmetered" if claim["class"] != "energy" or claim["subject"] != p["content_hash"]: return "read unsigned" if c is not None else "read unmetered" b = claim["body"] if c is not None and claim_class(b) == c and b["joules_micro"] == p["joules_micro"]: # Signed by the claim's signer, whoever that is: never read as the publisher's. return f"read signed class={c['label']} by={claim['signer_pubkey']}" raise Refuse(None, "property_disagrees") g = read_group(inp["group"]) o = read_object(inp["object"]) if bytes.fromhex(p["content_hash"]) != o["content_hash"] or bytes.fromhex(p["group_receipt"]) != link(g): raise Refuse(None, "property_disagrees") accept(o, g, bytes.fromhex(inp["envelope"])) if not o["labelled"]: return "read unmetered" if c is None else "read unsigned" if o["class"] is None and c is None: return "read unmetered" if o["class"] is not None and c is not None and o["class"] == c and o["joules_micro"] == p["joules_micro"]: return f"read signed class={c['label']} by={g['signer_pubkey']}" raise Refuse(None, "property_disagrees") # ------------------------------------------------------------ chains per (key, track) (§3 step 3, §6.1) def days_from_civil(y, m, d): """Days since 1970-01-01 of a date of the proleptic Gregorian calendar, year 0000 included, in integers (the days-from-civil algorithm).""" y -= m <= 2 era = y // 400 yoe = y - era * 400 doy = (153 * ((m + 9) % 12) + 2) // 5 + d - 1 return era * 146097 + yoe * 365 + yoe // 4 - yoe // 100 + doy - 719468 def seconds(t): """An RFC 3339 date-time as seconds since 1970-01-01T00:00:00Z, its offset applied and its fraction dropped; a second written as 60 is the next minute's first. None for a text that is not one.""" if not (isinstance(t, str) and CV.is_rfc3339(t)): return None days = days_from_civil(int(t[0:4]), int(t[5:7]), int(t[8:10])) v = days * 86400 + int(t[11:13]) * 3600 + int(t[14:16]) * 60 + int(t[17:19]) tail = t[19:].lstrip(".0123456789") if tail[:1] in ("+", "-"): off = int(tail[1:3]) * 3600 + int(tail[4:6]) * 60 v += -off if tail[0] == "+" else off return v CHAIN_HELD_LINKS = 65_536 # earlier links a chain keeps, to tell a fork from a gap MAX_HELD_GROUPS = 65_536 # groups held in all, whatever the bound says CHAIN_OPEN_GAPS = 64 # gaps a chain keeps open for history def chain(inp): """Each group in order through §3 steps 1-3 against its own chain, the one for its (signer_pubkey, track_alias). The head offered again passes. With no head held, a null parent starts the chain and any other is parent_not_held. With a head held, its link hash links the group; a null parent is chain_break; a parent that is another group accepted on the chain is chain_break (a fork); any other is parent_not_held. A legacy group, whose parent no signature covers, needs a group_id above the head's whatever the guard says; with the guard on, every group does. With the bound on, a group_end outside [now - max_age, now + max_skew] is group_not_fresh, unless the group's signed group_id is below the chain's live edge: the highest group_id of a group that verified and was inside the window (or arrived with no bound), linked or not. A group that passes becomes the head. With `recovery` (§3 step 3, Holding, Re-anchoring, Joining, History): a verifier that joins anchors a chain it holds nothing of at its first group that passes steps 1 and 2 and the freshness bound, whatever its parent. Each anchor leaves a gap: below it, the last group admitted before the anchor (none for a join, and then a null parent inside the gap is the chain's true start, parent_not_held, not chain_break); above it, the earliest group admitted at or after it and the parent that group signs. A group inside a gap whose parent is the lower end (forward) or whose link is the upper end's signed parent (backward) is history, and moves that end; one that does both closes the gap. With `max_held`, `max_history` and `max_total`, a group that is parent_not_held is held: as history if its group_id lies inside an open gap, live otherwise. Holding it live past the chain's live count or the total refuses all the chain holds live (parent_not_recovered) and the chain re-anchors: its next group that passes steps 1 and 2, the bound, and whose group_id exceeds the head's, anchors it. Holding it as history past the chain's history count or the total refuses the lowest held as history below it, or, if there is none, refuses it (parent_not_recovered). A group admitted releases, in turn, the held groups whose parent is its link and the held group whose link is its parent, those of one admitted group by group_id then arrival. Each group's outcome is its last: a held group takes what became of it. A group the chain admitted before, other than its head, offered again with the same parent, is `accept again` and changes nothing; the head offered again is `accept`.""" chains, out = {}, [] guard, fresh = inp["guard"], inp.get("freshness") rec = inp.get("recovery") or {} join = rec.get("join") is True hold = None if rec.get("max_held") is not None: def count(k): v = rec.get(k) if not isinstance(v, int) or isinstance(v, bool) or v < 0: raise ValueError(f"recovery.{k}") return v hold = (count("max_held"), count("max_history"), count("max_total")) st = {"total": 0, "seq": 0} events = [] def in_window(g): if fresh is None: return True now, t = seconds(fresh["now"]), seconds(g["group_end"]) return t is not None and now - fresh["max_age_s"] <= t <= now + fresh["max_skew_s"] def new_chain(): return {"head": None, "head_link": None, "links": {}, "links_order": deque(), "edge": None, "held": {}, "by_parent": {}, "history": set(), "reanchor": False, "gaps": []} def plink(g): p = g["parent_receipt_hash"] return None if p is None else bytes.fromhex(p) def signed_parent(g): return plink(g) if g["labelled"] else None def spans(gap, gid): return gid < gap["high_id"] and (gap["low"] is None or gid > gap["low"][1]) def fill(c, g, lk): for i, gap in enumerate(c["gaps"]): if not spans(gap, g["group_id"]): continue fwd = gap["low"] is not None and plink(g) == gap["low"][0] bwd = gap["high_parent"] == lk if fwd or bwd: return i, fwd, bwd return None def check_link(g, c, lk): """§3 step 3 as before recovery: the head again, a link, or a refusal.""" head, head_link, parent = c["head"], c["head_link"], plink(g) if head_link == lk: return if head_link is None: if parent is not None: raise Refuse(3, "parent_not_held") elif parent is None: # Below a join's anchor (a gap with no lower end) a null parent is the chain's # true start: held as history until the group above it links back to it. if any(gap["low"] is None and spans(gap, g["group_id"]) for gap in c["gaps"]): raise Refuse(3, "parent_not_held") raise Refuse(3, "chain_break") elif parent != head_link: raise Refuse(3, "chain_break" if parent in c["links"] else "parent_not_held") if (guard or not g["labelled"]) and head is not None and g["group_id"] <= head["group_id"]: raise Refuse(3, "group_id_not_increasing") behind_edge = c["edge"] is not None and g["group_id"] < c["edge"] if not behind_edge and not in_window(g): raise Refuse(3, "group_not_fresh") def decide(g, c, lk): if c["head_link"] == lk: return "accept" if fill(c, g, lk) is not None: return "accept history" head = c["head"] anchoring = c["reanchor"] or (join and head is None) try: check_link(g, c, lk) return "accept" except Refuse as r: if not (anchoring and r.step == 3): raise behind_edge = c["edge"] is not None and g["group_id"] < c["edge"] if not behind_edge and not in_window(g): raise Refuse(3, "group_not_fresh") if head is not None and g["group_id"] <= head["group_id"]: raise Refuse(3, "group_id_not_increasing") return "accept anchor" def attempt(g, c, lk): try: return decide(g, c, lk), None except Refuse as r: return None, r def install(g, c, lk, how): if lk not in c["links"]: c["links"][lk] = plink(g) c["links_order"].append(lk) while len(c["links_order"]) > CHAIN_HELD_LINKS: c["links"].pop(c["links_order"].popleft(), None) if how == "accept history": f = fill(c, g, lk) if f is None: return i, fwd, bwd = f gap = c["gaps"][i] if fwd and bwd: del c["gaps"][i] elif fwd: gap["low"] = (lk, g["group_id"]) else: gap["high_id"], gap["high_parent"] = g["group_id"], signed_parent(g) if gap["low"] is None and gap["high_parent"] is None: del c["gaps"][i] elif how == "accept anchor": low = None if c["head"] is None else (c["head_link"], c["head"]["group_id"]) gap = {"low": low, "high_id": g["group_id"], "high_parent": signed_parent(g)} if low is not None or gap["high_parent"] is not None: c["gaps"].append(gap) while len(c["gaps"]) > CHAIN_OPEN_GAPS: c["gaps"].pop(0) c["head"], c["head_link"], c["reanchor"] = g, lk, False else: c["head"], c["head_link"], c["reanchor"] = g, lk, False def put(c, lk, h): if h["history"]: c["history"].add((h["g"]["group_id"], h["seq"], lk)) p = plink(h["g"]) if p is not None: c["by_parent"].setdefault(p, set()).add(lk) c["held"][lk] = h def take(c, lk): h = c["held"].pop(lk, None) if h is None: return None if h["history"]: c["history"].discard((h["g"]["group_id"], h["seq"], lk)) p = plink(h["g"]) if p is not None and p in c["by_parent"]: c["by_parent"][p].discard(lk) if not c["by_parent"][p]: del c["by_parent"][p] return h def refuse_held(c, lk): if take(c, lk) is not None: st["total"] -= 1 events.append((lk, Refuse(3, "parent_not_recovered").label())) def give_up(c): c["reanchor"] = True for _, _, lk in sorted((h["g"]["group_id"], h["seq"], lk) for lk, h in c["held"].items() if not h["history"]): refuse_held(c, lk) def hold_group(g, c, lk): live_max, history_max, total_max = hold cap = min(total_max, MAX_HELD_GROUPS) if lk in c["held"]: return None, Refuse(3, "parent_not_held") history = any(spans(gap, g["group_id"]) for gap in c["gaps"]) if history: if len(c["history"]) >= history_max or st["total"] >= cap: lowest = min(c["history"]) if c["history"] else None if lowest is not None and lowest[0] < g["group_id"]: refuse_held(c, lowest[2]) else: return None, Refuse(3, "parent_not_recovered") elif len(c["held"]) - len(c["history"]) >= live_max or st["total"] >= cap: give_up(c) return attempt(g, c, lk) st["seq"] += 1 put(c, lk, {"g": g, "seq": st["seq"], "history": history}) st["total"] += 1 return None, Refuse(3, "parent_not_held") def release(c, g, lk): work = deque([(plink(g), lk)]) while work: parent, l = work.popleft() nxt = [(c["held"][x]["g"]["group_id"], c["held"][x]["seq"], x) for x in c["by_parent"].get(l, ()) if x in c["held"]] if parent is not None and parent in c["held"]: h = c["held"][parent] nxt.append((h["g"]["group_id"], h["seq"], parent)) for _, _, x in sorted(set(nxt)): h = take(c, x) if h is None: continue st["total"] -= 1 how, r = attempt(h["g"], c, x) if r is not None and r.reason == "parent_not_held": put(c, x, h) st["total"] += 1 continue if how is not None: install(h["g"], c, x, how) work.append((plink(h["g"]), x)) events.append((x, how if how is not None else r.label())) waiting = {} for raw in inp["groups"]: try: g = read_group(raw) verify_group(g) except Refuse as r: out.append(r.label()) continue c = chains.setdefault((g["signer_pubkey"], g["track_alias"]), new_chain()) lk = link(g) if c["head_link"] == lk: out.append("accept") # the head again: nothing changes continue if lk in c["links"] and c["links"][lk] == plink(g): out.append("accept again") # a group admitted before, the same bytes: nothing changes continue how, r = attempt(g, c, lk) held = False if r is not None and r.reason == "parent_not_held" and hold is not None: how, r = hold_group(g, c, lk) held = r is not None and r.reason == "parent_not_held" if how is not None: install(g, c, lk, how) if in_window(g): c["edge"] = g["group_id"] if c["edge"] is None else max(c["edge"], g["group_id"]) if how is not None: release(c, g, lk) if held: waiting.setdefault(lk, []).append(len(out)) out.append(None) else: out.append(how if how is not None else r.label()) for elk, outcome in events: for w in waiting.pop(elk, []): out[w] = outcome events.clear() held_label = Refuse(3, "parent_not_held").label() return "chain: " + " / ".join(held_label if o is None else o for o in out) def inclusion_only(inp): """§4.4: §3 steps 1, 2 and 5 without the envelope; step 4 is not run.""" g = read_group(inp["group"]) verify_group(g) inclusion(read_object(inp["object"]), g) return "accept evidence=inclusion-verified" # ------------------------------------------------------------ the binary form (§2.7) MAGIC = b"WJR1" TAG_LABEL = {1: "HwShunt", 2: "ModelBased", 3: "Estimator", 4: "OnChipCounter", 5: "CalibratedInstrument"} FILTER_LABEL = {0: "Unknown", 1: "Off", 2: "On"} class Bytes: """A cursor over the bytes given (§2.7.4); a read past the end is malformed at `step`.""" def __init__(self, b, step): self.b, self.at, self.step = b, 0, step def take(self, n): if self.at + n > len(self.b): raise Refuse(self.step, "malformed") out = self.b[self.at:self.at + n] self.at += n return out def int(self, n): return int.from_bytes(self.take(n), "big") def text(self): try: return self.take(self.int(4)).decode("utf-8") except UnicodeDecodeError: raise Refuse(self.step, "malformed") def cls(self, m): """A class's tag and evidence (energy-measurement §2.7) as the JSON members it names; tag 0 names none.""" tag = self.int(1) if tag == 0: return if tag not in TAG_LABEL: raise Refuse(self.step, "malformed") m["energy_provenance"] = TAG_LABEL[tag] if tag >= 4: m["energy_uncertainty"] = {"relative_ppm": self.int(4), "window_us": self.int(8)} if tag == 4: f = self.int(1) if f not in FILTER_LABEL: raise Refuse(self.step, "malformed") m["energy_counter_filtering"] = FILTER_LABEL[f] else: m["energy_calibration_ref"] = self.text() def end(self): if self.at != len(self.b): raise Refuse(self.step, "malformed") def bin_parent(c): flag, h = c.int(1), c.take(32) if flag == 1: return h.hex() if flag == 0 and h == bytes(32): return None raise Refuse(c.step, "malformed") def bin_group(c): """§2.7.1: a group receipt's members, and the length of its canonical bytes.""" start = c.at dom = c.take(len(SIGN1)) if dom not in (SIGN1, SIGN2): raise Refuse(c.step, "malformed") labelled = dom == SIGN2 m = {"kind": "group", "track_alias": c.int(8), "group_id": c.int(8), "root_hash": c.take(32).hex(), "leaf_count": c.int(8), "joules_total": c.int(8), "group_start": c.text(), "group_end": c.text(), "signer_pubkey": c.take(32).hex(), "signer_id": c.text()} label = {} if labelled: c.cls(label) cov = c.int(1) if cov not in (0, 1): raise Refuse(c.step, "malformed") if cov == 1 or label: label["energy_coverage"] = "Partial" if cov == 1 else "Whole" parent = bin_parent(c) m["sig"] = c.take(64).hex() canonical = c.at - start m["parent_receipt_hash"] = parent if labelled else bin_parent(c) if labelled: m["sign_version"] = 2 m.update(label) return m, canonical def bin_object(c, track_alias, group_id): """§2.7.2: an object receipt's members from its body.""" m = {"kind": "object", "track_alias": track_alias, "group_id": group_id, "object_id": c.int(8), "content_hash": c.take(32).hex(), "joules_micro": c.int(8), "origin": c.text()} n = c.int(1) sib = [c.take(32).hex() for _ in range(n)] m["merkle_proof"] = {"siblings": sib, "direction_bits": c.int(8), "leaf_index": c.int(8)} lv = c.int(1) if lv == 2: m["leaf_version"] = 2 c.cls(m) elif lv != 1: raise Refuse(c.step, "malformed") return m def write_group(g): """§2.7.1 from a group read by §3 step 1.""" b = payload(g) + bytes.fromhex(g["sig"]) if not g["labelled"]: p = g["parent_receipt_hash"] b += b"\x01" + bytes.fromhex(p) if p is not None else b"\x00" + bytes(32) return b def write_object_body(o): """§2.7.2's body from an object read by §3 step 4; None for a label that does not read.""" if o["unreadable"]: return None origin = o["origin"].encode() b = o["object_id"].to_bytes(8, "big") + o["content_hash"] + o["joules_micro"].to_bytes(8, "big") b += len(origin).to_bytes(4, "big") + origin + bytes([len(o["siblings"])]) + b"".join(o["siblings"]) b += o["bits"].to_bytes(8, "big") + o["index"].to_bytes(8, "big") return b + (b"\x02" + encode(o["class"]) if o["labelled"] else b"\x01") def canonical(v): return json.dumps(v, sort_keys=True, separators=(",", ":"), ensure_ascii=False) def binary(inp): """§2.7.4's reading, then §3 step 1's and step 4's reading of the members, and the bytes written again from what was read, which must be the bytes given.""" b = bytes.fromhex(inp["bytes"]) form = inp["form"] out = {} if form == "receipts": c = Bytes(b, 1) if c.take(4) != MAGIC: raise Refuse(1, "malformed") start = c.at gm, n = bin_group(c) count = c.int(4) c.step = 4 oms = [bin_object(c, gm["track_alias"], gm["group_id"]) for _ in range(count)] c.step = 1 c.end() g = read_group(gm) objs = [read_object(o) for o in oms] again = MAGIC + write_group(g) + len(objs).to_bytes(4, "big") bodies = [write_object_body(o) for o in objs] again = None if None in bodies else again + b"".join(bodies) out = {"group": gm, "link_hash": blake3(LINK + b[start:start + n]).hex(), "objects": oms} elif form == "group": c = Bytes(b, 1) gm, _ = bin_group(c) c.end() g = read_group(gm) again = write_group(g) out = {"group": gm, "link_hash": link(g).hex()} elif form == "object": c = Bytes(b, 4) ta, gid = c.int(8), c.int(8) om = bin_object(c, ta, gid) c.end() o = read_object(om) body = write_object_body(o) again = None if body is None else ta.to_bytes(8, "big") + gid.to_bytes(8, "big") + body out = {"object": om} else: raise ValueError("unknown binary form " + str(form)) if again != b: return "read, but the bytes written again differ" return "read " + canonical(out) # ------------------------------------------------------------ vectors def verdict(v): inp = v["input"] try: if v["procedure"] == "leaf": return "leaf=" + leaf(read_object(inp["object"])).hex() if v["procedure"] == "group": g = read_group(inp["group"]) verify_group(g) prev = None if "previous" in inp: try: prev = read_group(inp["previous"]) except Refuse: prev = None check_parent(g, prev) return f"accept link_hash={link(g).hex()} signing_payload={payload(g).hex()}" if v["procedure"] == "object": g = read_group(inp["group"]) o = read_object(inp["object"]) accept(o, g, bytes.fromhex(inp["envelope"])) return "accept" if v["procedure"] == "total": return total(inp) if v["procedure"] == "property": return prop(inp) if v["procedure"] == "chain": return chain(inp) if v["procedure"] == "inclusion": return inclusion_only(inp) if v["procedure"] == "binary": return binary(inp) except Refuse as r: return r.label() raise ValueError("unknown procedure " + str(v["procedure"])) def expected_of(e): if "leaf" in e: return "leaf=" + e["leaf"] if e["outcome"] == "chain": return "chain: " + " / ".join(expected_of(r) for r in e["results"]) if e["outcome"] == "accept" and "admission" in e: return "accept " + e["admission"] if e["outcome"] == "accept" and "evidence" in e: return "accept evidence=" + e["evidence"] if e["outcome"] == "accept": return f"accept link_hash={e['link_hash']} signing_payload={e['signing_payload']}" if "link_hash" in e else "accept" if e["outcome"] == "read" and "reading" not in e: return "read " + canonical({k: x for k, x in e.items() if k != "outcome"}) if e["outcome"] == "read": return "read " + e["reading"] return f"reject step={e['step']} reason={e['reason']}" if "step" in e else f"reject reason={e['reason']}" def main(argv): if argv[1:2] == ["verdicts"] and len(argv) == 3: doc = json.load(open(argv[2], encoding="utf-8")) for v in doc["vectors"]: try: got = verdict(v) except (ValueError, KeyError, TypeError, AttributeError): got = "error" print(f"{v['name']}\t{got}") return 0 path = argv[1] if len(argv) > 1 else os.path.join(HERE, "vectors.json") doc = json.load(open(path, encoding="utf-8")) fail = 0 b3 = blake3(b"abc").hex() == "6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85" print(("PASS" if b3 else "FAIL") + " BLAKE3 reproduces its published value") fail += not b3 for v in doc["vectors"]: want, got = expected_of(v["expected"]), verdict(v) ok = want == got fail += not ok print(f"{'PASS' if ok else 'FAIL'} {v['name']} expected={want} got={got}") print(f"{len(doc['vectors'])} vectors, {fail} failure(s)") return 1 if fail else 0 if __name__ == "__main__": sys.exit(main(sys.argv))