open-standards/wai/live-conformance/route_record_verify.py
Transaction Science ca4fd375ef A delivery's decision states its offer, and its route's receipt is held to it
A price book of version 3 states each entry's declared uncertainty. A delivery-decision record of
version 2 rests on one and states offer: the route's entry by its place in the book, what the
delivery carries, its estimates and its energy figure's class, uncertainty and grade, which a
verifier recomputes. A delivery in a verification document may carry its route's OER/2 receipt,
signed under the route's name, naming the decision and the delivered objects' root.

The receipt's grade is no higher than the offer's. A figure more than one declared uncertainty
above the offer's states price_exceeded as its work.outcome, an open member of the receipt the hop
signs, and no other does. So no document kind is added and no earlier record reads differently.
route_record_verify.py recomputes the offer and holds each receipt to it. The controls of an unknown
version move on to a price book of version 4 and a decision of version 3; every other vector is
unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 03:01:50 -05:00

1546 lines
77 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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
"""Independent verifier of the route record corpus (extensions/route-record.md).
Written from the extension's text. It shares no code with wai-rs, which generates the corpus,
and never runs it. It checks the router's Ed25519 signature with
../claim-conformance/_ed25519.py, WAI's strict rule (SPEC §7 *Signatures*) written from RFC 8032
with the standard library, and verifies the hops' OER/2 receipts with the OER/2 reference
verifier, ../oer-conformance/oer2_verify.py (which uses the `cryptography` package). Everything
else is here:
- the documents (§2, §3): read strictly, held to the subset, their forms and digests, and RFC
8785's canonical form;
- routing (§4): the candidates, their estimates, every hard limit, the key list, the ranking,
the order, the decision and the allowed attempts;
- the record (§5): its members and forms, its signature, its binding to the documents, its
candidates, order and decision recomputed, and its attempts held to the order and the
billing rules;
- the hop receipts (§6), and the outcome of §7.3;
- version 2 (§3.4): a price book that prices each operation of an endpoint and states its
failures over their basis (§3.2.1), the candidates of the request's operation, the expected
cost and time to first byte per successful job and the ranking that reads them (§4.2, §4.4),
a record's version held to its price book's, and the served hop's receipt held to the
outputs the record states, their commitment computed with ../claim-conformance/_blake3.py
(BLAKE3 written from its specification) as oer-receipts §3 *Output* defines it;
- version 3 of the record (§3.4), on a price book of version 2: what the served hop delivered
stated by its count and root, the served hop's receipt held to the root; an output's
inclusion proof (§6.1, `inclusion_proof`), which the receipt ledger's verifier checks a job
source's outputs with; and each third-party hop record (§6.2): its members and forms, no key,
header or URL query in it, its signature under its own domain, and its binding to its attempt,
its candidate and what its hop's receipt names;
- each delivery-decision record (§6.3): its members and forms, its signature under its own
domain, its binding to its delivery's documents, the routing of §4 over them recomputed, the
route and the key each other route was passed over by, its parent and its chain;
- version 3 of the price book (§3.4), and a delivery's offer and its route's receipt (§6.4): an
entry's declared uncertainty, the offer of a decision of version 2 recomputed from the route's
entry, and the receipt held to its route, its decision, its objects and its offer: its grade,
and its figure read to whole microjoules against the offer's and one declared uncertainty, or
the hop's flag.
For every vector it requires the outcome the corpus states; of the corpus, that every reason of
§8 and every code it reuses appears in it, that §8 names every reason it raises, and that no
other file is there. Then it mutates copies of the corpus, one defect each, and requires every
copy to fail; and it drops each of its own checks in turn and requires the corpus to catch the
weakened verifier.
python3 -I route_record_verify.py <live-conformance/routes>
python3 -I route_record_verify.py --document <verification document>
Exit status 0 only if every vector verifies, every mutated copy fails and every weakened
verifier fails. Python 3.8 or later.
"""
import copy
from fractions import Fraction
import hashlib
import io
import json
import os
import re
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(HERE, "..", "claim-conformance"))
sys.path.insert(0, os.path.join(HERE, "..", "oer-conformance"))
import _ed25519 as ED # noqa: E402
import oer2_verify as OER # noqa: E402
from _blake3 import blake3 # noqa: E402
EXTENSION = os.path.join(HERE, "..", "extensions", "route-record.md")
SIGN_DOMAIN = b"wai:route-record\x01" # §5.3
LIMITS = ["policy", "unit", "region", "residency", "training", "retention", "ga", "consent", "unit_price", "spend"] # §4.3
KEYS = ["price", "ttfb", "energy", "quality"] # §3.3
ERRORS = ["timeout", "server_error", "rate_limited", "refused", "unavailable"] # §5.2
CLASSES = ["CalibratedInstrument", "OnChipCounter", "ModelBased", "Estimator"] # §2
DECIDED_BY = ["none", "only_survivor", "targets", "price", "ttfb", "energy", "quality", "endpoint"] # §4.5
MAX = 2 ** 53 - 1
ENTRY = ["endpoint", "provider", "capability", "region", "residency", "trains_on_inputs", "retention_days", "ga",
"needs_consent", "unit", "divisor", "price", "ttfb_ms", "energy_uj", "energy_class", "quality"] # §3.2
ENTRY_2 = ENTRY + ["operation", "failures"] # §3.2, version 2
ENTRY_3 = ENTRY_2 + ["energy_ppm"] # §3.2, version 3
DECLARING = ["CalibratedInstrument", "OnChipCounter"] # energy-measurement §2.3
MAX_PPM = 2 ** 32 - 1 # §3.2
FAILURES = ["hops", "from", "to", "failed", "billed_price", "failed_ms"] # §3.2.1
MIN_BASIS = 100 # §3.2.1
RECORD = ["kind", "version", "job", "operation", "capability", "fallback", "quantities", "request", "policy", "pricebook",
"candidates", "order", "decision", "accepted", "deadline", "attempts", "signer", "sig"] # §5.1
ATTEMPT = ["hop", "endpoint", "outcome", "error", "start", "end", "billed", "not_billed", "receipt"] # §5.2
LEAF, NODE = b"moq-jwp:merkle-leaf\x01", b"moq-jwp:merkle-node\x01" # jwp-receipts §2.2
def book_of(version):
"""§3.4: the price book version a record of `version` rests on: 1, or 2 for versions 2 and 3."""
return 2 if version in (2, 3) else 1
def entry_members(version):
"""§3.2: an entry's members in a price book of `version`."""
return {1: ENTRY, 2: ENTRY_2, 3: ENTRY_3}[version]
def candidate_members(version):
"""§5.1: an entry's members, the five a candidate adds, and from version 2 the expected two."""
return entry_members(version) + ["via_fallback", "cost", "energy", "removed", "rank"] \
+ (["expected_cost", "expected_ttfb_ms"] if version >= 2 else [])
SKIP = set()
class Refuse(Exception):
def __init__(self, reason, at):
super().__init__(reason)
self.reason, self.at = reason, at
def on(check):
return check not in SKIP
# ---------------------------------------------------------------- §2 encodings
class _Bad(ValueError):
pass
def _no_duplicates(pairs):
d = {}
for k, v in pairs:
if k in d:
raise _Bad("member %r twice" % k)
d[k] = v
return d
def _int(s):
# A number is an integer 0 to 2^53 - 1 in plain decimal: no sign (so `-0` too is out).
if s.startswith("-"):
raise _Bad("a negative number")
v = int(s)
if v > MAX:
raise _Bad("an integer past 2^53 - 1")
return v
def _not_a_number(s):
raise _Bad("not an integer: %s" % s)
def _walk(v):
stack = [(v, 1)]
while stack:
x, d = stack.pop()
yield x, d
if isinstance(x, dict):
for k, y in x.items():
stack.append((k, d + 1))
stack.append((y, d + 1))
elif isinstance(x, list):
stack.extend((y, d + 1) for y in x)
def read_document(text):
"""§2: a document, or None (`malformed`)."""
if not isinstance(text, str):
return None
try:
v = json.loads(text, object_pairs_hook=_no_duplicates, parse_int=_int, parse_float=_not_a_number,
parse_constant=_not_a_number)
except (ValueError, RecursionError):
return None
depth = 0
for x, d in _walk(v):
if isinstance(x, (dict, list)):
depth = max(depth, d)
if isinstance(x, str) and any("\ud800" <= c <= "\udfff" for c in x):
return None
if depth > 64:
return None
return v
def canonical(v):
"""RFC 8785 over §2's subset: members sorted by their UTF-16 code units, strings escaped as
§3.2.2.2 there, integers in plain decimal."""
if v is None:
return "null"
if v is True:
return "true"
if v is False:
return "false"
if isinstance(v, int):
return str(v)
if isinstance(v, str):
return json.dumps(v, ensure_ascii=False)
if isinstance(v, list):
return "[" + ",".join(canonical(x) for x in v) + "]"
keys = sorted(v, key=lambda k: k.encode("utf-16-be"))
return "{" + ",".join(json.dumps(k, ensure_ascii=False) + ":" + canonical(v[k]) for k in keys) + "}"
def digest(v):
return hashlib.sha256(canonical(v).encode("utf-8")).hexdigest()
def same(a, b):
"""Two values are the same JSON value: compared by their canonical forms, so `0` is not
`false` and `1` not `true`, as Python's `==` would have them."""
return canonical(a) == canonical(b)
TOKEN = re.compile(r"\A[A-Za-z0-9._:/-]{1,128}\Z")
UNIT = re.compile(r"\A[a-z][a-z0-9_]{0,63}\Z")
CAP = re.compile(r"\Awai\.[a-z0-9_.]{0,124}\Z")
OPERATION = re.compile(r"\A[a-z][a-z0-9]*\.[a-z][a-z0-9]*\Z")
CURRENCY = re.compile(r"\A[A-Z]{3}\Z")
RECEIPT_ID = re.compile(r"\A(?:sha256|blake3):[0-9a-f]{64}\Z")
def is_int(v, lo=0, hi=MAX):
return isinstance(v, int) and not isinstance(v, bool) and lo <= v <= hi
def is_token(v):
return isinstance(v, str) and TOKEN.match(v) is not None
def is_unit(v):
return isinstance(v, str) and UNIT.match(v) is not None
def is_cap(v):
return isinstance(v, str) and CAP.match(v) is not None
def is_hex(v, n):
return isinstance(v, str) and len(v) == n and all(c in "0123456789abcdef" for c in v)
def exactly(v, members):
return isinstance(v, dict) and set(v) == set(members) and len(v) == len(members)
def ascending(xs):
return all(xs[i].encode() < xs[i + 1].encode() for i in range(len(xs) - 1))
def null_or(v, f):
return v is None or f(v)
def tokens(v, top):
return isinstance(v, list) and 1 <= len(v) <= top and all(is_token(x) for x in v) and ascending(v)
def quantities_ok(v):
if not (isinstance(v, list) and 1 <= len(v) <= 16):
return False
if not all(exactly(q, ["unit", "quantity"]) and is_unit(q["unit"]) and is_int(q["quantity"], 1) for q in v):
return False
return ascending([q["unit"] for q in v])
# ---------------------------------------------------------------- §3 the documents
def request_ok(r):
if not exactly(r, ["kind", "job", "operation", "capability", "fallback", "quantities", "limits", "prefer", "deadline_us"]):
return False
if r["kind"] != "wai-route-request" or not is_token(r["job"]) or not isinstance(r["operation"], str) \
or not OPERATION.match(r["operation"]) or not is_cap(r["capability"]):
return False
if not null_or(r["fallback"], lambda f: is_cap(f) and f != r["capability"]):
return False
if not quantities_ok(r["quantities"]):
return False
lim = r["limits"]
if not exactly(lim, ["regions", "residency", "no_training", "zero_retention", "ga_only", "consent_on_file", "max_price", "spend_ceiling"]):
return False
if not all(null_or(lim[k], lambda x: tokens(x, 64)) for k in ("regions", "residency")):
return False
if not all(isinstance(lim[k], bool) for k in ("no_training", "zero_retention", "ga_only", "consent_on_file")):
return False
mp = lim["max_price"]
if not (isinstance(mp, list) and len(mp) <= 16 and all(
exactly(m, ["unit", "price", "divisor"]) and is_unit(m["unit"]) and is_int(m["price"]) and is_int(m["divisor"], 1)
for m in mp) and ascending([m["unit"] for m in mp])):
return False
if not null_or(lim["spend_ceiling"], lambda c: exactly(c, ["currency", "amount"]) and isinstance(c["currency"], str)
and CURRENCY.match(c["currency"]) is not None and is_int(c["amount"])):
return False
p = r["prefer"]
if not exactly(p, ["sort", "ttfb_ms", "quality"]):
return False
if not (null_or(p["sort"], lambda s: s in KEYS) and null_or(p["ttfb_ms"], is_int)
and null_or(p["quality"], lambda q: is_int(q, 0, 1000))):
return False
return null_or(r["deadline_us"], lambda d: is_int(d, 1))
def failures_form(f):
"""§3.2.1's table, each member of its form."""
return exactly(f, FAILURES) and is_int(f["hops"], 1) and all(is_int(f[k]) for k in FAILURES)
def failures_ok(f, issued):
"""§3.2.1: the form, and its relations: no more failed than sent, a period that ends by the
book's issue, and no billed price or time without a failed hop."""
if not failures_form(f):
return False
if not on("failures-relations"):
return True
return f["failed"] <= f["hops"] and f["from"] <= f["to"] <= issued \
and (f["failed"] > 0 or (f["billed_price"] == 0 and f["failed_ms"] == 0))
def ppm_ok(v):
"""§3.2: `energy_ppm`'s form."""
return v is None or is_int(v, 1, MAX_PPM)
def entry_ok(e, version=1, issued=0):
if not exactly(e, entry_members(version)):
return False
if version >= 2 and not (isinstance(e["operation"], str) and OPERATION.match(e["operation"])
and null_or(e["failures"], lambda f: failures_ok(f, issued))):
return False
if version == 3 and on("ppm-form") and not ppm_ok(e["energy_ppm"]):
return False
if version == 3 and on("ppm-class") and (e["energy_ppm"] is not None) != (e["energy_class"] in DECLARING):
return False
return (is_token(e["endpoint"]) and is_token(e["provider"]) and is_cap(e["capability"])
and null_or(e["region"], is_token) and null_or(e["residency"], is_token)
and null_or(e["trains_on_inputs"], lambda b: isinstance(b, bool)) and null_or(e["retention_days"], is_int)
and null_or(e["ga"], lambda b: isinstance(b, bool)) and null_or(e["needs_consent"], lambda b: isinstance(b, bool))
and is_unit(e["unit"]) and is_int(e["divisor"], 1) and is_int(e["price"]) and null_or(e["ttfb_ms"], is_int)
and null_or(e["energy_uj"], is_int) and null_or(e["energy_class"], lambda c: c in CLASSES)
and (e["energy_uj"] is None) == (e["energy_class"] is None)
and null_or(e["quality"], lambda q: is_int(q, 0, 1000)))
def book_version(b):
"""§3.4: a price book with no `version` member is of version 1."""
return b.get("version", 1) if isinstance(b, dict) else 1
def pricebook_ok(b):
if not isinstance(b, dict):
return False
# A `version` member gives a later version's members; its value must be 2 or 3.
known = is_int(b.get("version")) and b["version"] in (2, 3)
shape = (b["version"] if known else 2) if "version" in b else 1
if shape >= 2 and on("pricebook-version") and not known:
return False
members = ["kind", "version", "currency", "issued", "entries"] if shape >= 2 else ["kind", "currency", "issued", "entries"]
if not exactly(b, members) or b["kind"] != "wai-route-pricebook":
return False
if not (isinstance(b["currency"], str) and CURRENCY.match(b["currency"]) and is_int(b["issued"])):
return False
es = b["entries"]
if not (isinstance(es, list) and 1 <= len(es) <= 1024 and all(entry_ok(e, shape, b["issued"]) for e in es)):
return False
if shape >= 2:
# By endpoint, then operation: compared as a pair of byte strings, never joined.
keys = [(e["endpoint"].encode(), e["operation"].encode()) for e in es]
return all(keys[i] < keys[i + 1] for i in range(len(keys) - 1))
return ascending([e["endpoint"] for e in es])
def policy_ok(p):
if not exactly(p, ["kind", "name", "version", "keys", "max_attempts", "exclude"]) or p["kind"] != "wai-route-policy":
return False
if not (is_token(p["name"]) and is_int(p["version"], 1) and is_int(p["max_attempts"], 1, 16)):
return False
if not (isinstance(p["keys"], list) and len(p["keys"]) == 4 and sorted(k for k in p["keys"] if isinstance(k, str)) == sorted(KEYS)):
return False
ex = p["exclude"]
return isinstance(ex, list) and len(ex) <= 64 and all(is_token(x) for x in ex) and ascending(ex)
def read(text, ok, invalid, at):
v = read_document(text)
if v is None:
raise Refuse("malformed", at)
if on(invalid) and not ok(v):
raise Refuse(invalid, at)
return v
# ---------------------------------------------------------------- §4 routing
def quantity(req, unit):
for q in req["quantities"]:
if q["unit"] == unit:
return q["quantity"]
return None
def estimate(q, x, d):
"""§4.2: ⌊q × x / d⌋, null past 2^53 - 1."""
if q is None or x is None:
return None
e = q * x // d
return e if e <= MAX else None
def removals(req, book, pol, e, cost):
"""§4.3, in its order: (limit, value)."""
lim, out = req["limits"], []
if on("limit-policy") and e["provider"] in pol["exclude"]:
out.append(("policy", e["provider"]))
if on("limit-unit") and quantity(req, e["unit"]) is None:
out.append(("unit", e["unit"]))
for name, key in (("region", "regions"), ("residency", "residency")):
allowed = lim[key]
if on("limit-" + name) and allowed is not None and (e[name] is None or e[name] not in allowed):
out.append((name, e[name]))
if on("limit-training") and lim["no_training"] and e["trains_on_inputs"] is not False:
out.append(("training", e["trains_on_inputs"]))
if on("limit-retention") and lim["zero_retention"] and e["retention_days"] != 0:
out.append(("retention", e["retention_days"]))
if on("limit-ga") and lim["ga_only"] and e["ga"] is not True:
out.append(("ga", e["ga"]))
if on("limit-consent") and not lim["consent_on_file"] and e["needs_consent"] is not False:
out.append(("consent", e["needs_consent"]))
for m in lim["max_price"]:
if on("limit-unit-price") and m["unit"] == e["unit"] and e["price"] * m["divisor"] > m["price"] * e["divisor"]:
out.append(("unit_price", e["price"]))
c = lim["spend_ceiling"]
if on("limit-spend") and c is not None and (c["currency"] != book["currency"] or cost is None or cost > c["amount"]):
out.append(("spend", cost))
return out
def expected(q, e, cost):
"""§4.2, version 2: the expected cost and time to first byte per successful job. A job sent
to the endpoint until served meets failed / (hops - failed) failures on average; each costs
the router its billed price for the job's quantity and costs the job its time. Unknown
failures (none stated, or fewer than 100 hops), an unknown list figure, a basis every hop
of which failed, and a figure past 2^53 - 1 give None."""
f = e["failures"]
least = MIN_BASIS if on("thin-basis") else 1
if f is None or f["hops"] < least:
return None, None
ok = f["hops"] - f["failed"]
if ok == 0:
if on("unbounded"):
return None, None
ok = None
def plus(lst, per_failure):
if lst is None or per_failure is None:
return None
t = lst + (0 if ok is None else per_failure * f["failed"] // ok)
return t if t <= MAX else None
return plus(cost, estimate(q, f["billed_price"], e["divisor"])), plus(e["ttfb_ms"], f["failed_ms"])
def ttfb_read(c):
"""The time to first byte §4.4 reads: in version 2, the expected one."""
return c["expected_ttfb_ms"] if "expected_ttfb_ms" in c and on("expected-targets") else c["ttfb_ms"]
def meets(req, c):
t, q = req["prefer"]["ttfb_ms"], req["prefer"]["quality"]
return (t is None or (ttfb_read(c) is not None and ttfb_read(c) <= t)) and \
(q is None or (c["quality"] is not None and c["quality"] >= q))
def key_list(req, pol):
"""§4.4."""
p, k = req["prefer"], []
if p["ttfb_ms"] is not None or p["quality"] is not None:
k.append("targets")
if p["sort"] is not None:
k.append(p["sort"])
k += [x for x in pol["keys"] if x != p["sort"]]
return k + ["endpoint"]
def compare(req, key, a, b):
"""-1 when a comes first under the key, 1 when b does, 0 when they do not differ."""
def nulls_last(x, y, high_first=False):
if x is None and y is None:
return 0
if x is None:
return 1
if y is None:
return -1
if x == y:
return 0
return (-1 if x > y else 1) if high_first else (-1 if x < y else 1)
v2 = "expected_cost" in a
if key == "targets":
ma, mb = meets(req, a), meets(req, b)
return 0 if ma == mb else (-1 if ma else 1)
if key == "price":
# Version 2: the expected cost, then the list cost (§4.4).
first = nulls_last(a["expected_cost"], b["expected_cost"]) if v2 and on("expected-price") else 0
return first or nulls_last(a["cost"], b["cost"])
if key == "ttfb":
first = nulls_last(a["expected_ttfb_ms"], b["expected_ttfb_ms"]) if v2 and on("expected-ttfb") else 0
return first or nulls_last(a["ttfb_ms"], b["ttfb_ms"])
if key == "energy":
return nulls_last(a["energy"], b["energy"])
if key == "quality":
return nulls_last(a["quality"], b["quality"], high_first=True)
ea, eb = a["endpoint"].encode(), b["endpoint"].encode()
return 0 if ea == eb else (-1 if ea < eb else 1)
def route(req, book, pol):
"""§4: the candidates as the record states them, the order, the decision, the allowed."""
cands = []
v2 = book_version(book) != 1
for e in book["entries"]:
if v2 and on("operation-candidates") and e["operation"] != req["operation"]:
continue
if e["capability"] == req["capability"]:
via = False
elif req["fallback"] is not None and e["capability"] == req["fallback"]:
via = True
else:
continue
q = quantity(req, e["unit"])
cost = estimate(q, e["price"], e["divisor"])
energy = estimate(q, e["energy_uj"], e["divisor"])
c = dict(e)
c.update(via_fallback=via, cost=cost, energy=energy,
removed=[{"limit": l, "value": v} for l, v in removals(req, book, pol, e, cost)], rank=None)
if v2:
c["expected_cost"], c["expected_ttfb_ms"] = expected(q, e, cost)
cands.append(c)
keys = key_list(req, pol)
survivors = [c for c in cands if not c["removed"]]
def first(a, b):
for k in keys:
r = compare(req, k, a, b)
if r:
return r, k
return 0, None
# An insertion sort under the total order of the key list.
order = []
for c in survivors:
i = len(order)
while i > 0 and first(c, order[i - 1])[0] < 0:
i -= 1
order.insert(i, c)
for n, c in enumerate(order):
c["rank"] = n + 1
if not order:
decision = {"decided_by": "none", "runner_up": None}
elif len(order) == 1:
decision = {"decided_by": "only_survivor", "runner_up": None}
else:
decision = {"decided_by": first(order[0], order[1])[1], "runner_up": order[1]["endpoint"]}
allowed = min(len(order), pol["max_attempts"])
return cands, [c["endpoint"] for c in order], decision, allowed
# ---------------------------------------------------------------- §5 the record
def scalar(v):
return not isinstance(v, (dict, list))
def candidate_form(c, version=1):
"""§5.1: each member of its own form. Relations between members (an energy figure and its
class, a cost and its price, a failure count and its hops) are the recomputation's, at §7.2
step 6."""
if not exactly(c, candidate_members(version)):
return False
if version >= 2 and not (isinstance(c["operation"], str) and OPERATION.match(c["operation"])
and null_or(c["failures"], failures_form)
and null_or(c["expected_cost"], is_int) and null_or(c["expected_ttfb_ms"], is_int)):
return False
if version == 3 and not ppm_ok(c["energy_ppm"]):
return False
is_bool = lambda b: isinstance(b, bool) # noqa: E731
return (is_token(c["endpoint"]) and is_token(c["provider"]) and is_cap(c["capability"])
and null_or(c["region"], is_token) and null_or(c["residency"], is_token)
and null_or(c["trains_on_inputs"], is_bool) and null_or(c["retention_days"], is_int)
and null_or(c["ga"], is_bool) and null_or(c["needs_consent"], is_bool)
and is_unit(c["unit"]) and is_int(c["divisor"], 1) and is_int(c["price"]) and null_or(c["ttfb_ms"], is_int)
and null_or(c["energy_uj"], is_int) and null_or(c["energy_class"], lambda x: x in CLASSES)
and null_or(c["quality"], lambda q: is_int(q, 0, 1000))
and isinstance(c["via_fallback"], bool) and null_or(c["cost"], is_int) and null_or(c["energy"], is_int)
and isinstance(c["removed"], list) and len(c["removed"]) <= len(LIMITS)
and all(exactly(x, ["limit", "value"]) and x["limit"] in LIMITS and scalar(x["value"]) for x in c["removed"])
and null_or(c["rank"], lambda n: is_int(n, 1)))
def outputs_form(a):
"""§5.2, version 2: a served attempt delivered 1 to 1024 objects, each named once by its
content hash; a failed one, none."""
xs = a["outputs"]
if not (isinstance(xs, list) and all(is_hex(x, 64) for x in xs) and len(set(xs)) == len(xs)):
return False
return 1 <= len(xs) <= 1024 if a["outcome"] == "served" else not xs
def delivered_form(a):
"""§5.2, version 3: a served attempt's count, at least 1, and the root of what it delivered,
64 lowercase hexadecimal digits; a failed one's, 0 and 64 zeros."""
d = a["delivered"]
if not (exactly(d, ["count", "root"]) and is_int(d["count"]) and is_hex(d["root"], 64)):
return False
return d["count"] >= 1 if a["outcome"] == "served" else d["count"] == 0 and d["root"] == "00" * 32
def attempt_form(i, a, version=1):
extra = {2: ["outputs"], 3: ["delivered"]}.get(version, [])
if not exactly(a, ATTEMPT + extra) or a["outcome"] not in ("served", "failed"):
return False
if version == 2 and on("outputs-form") and not outputs_form(a):
return False
if version == 3 and on("delivered-form") and not delivered_form(a):
return False
served = a["outcome"] == "served"
error_ok = a["error"] is None if served else a["error"] in ERRORS
return (is_int(a["hop"]) and a["hop"] == i + 1 and is_token(a["endpoint"]) and error_ok and is_int(a["start"])
and is_int(a["end"]) and isinstance(a["billed"], bool) and a["not_billed"] in (None, "failed", "deadline_missed")
and isinstance(a["receipt"], str) and RECEIPT_ID.match(a["receipt"]) is not None)
def record_form(r):
if not exactly(r, RECORD):
return False
v = r["version"]
return (is_int(v) and v in (1, 2, 3) and is_token(r["job"]) and isinstance(r["operation"], str)
and OPERATION.match(r["operation"]) is not None and is_cap(r["capability"]) and null_or(r["fallback"], is_cap)
and quantities_ok(r["quantities"]) and is_hex(r["request"], 64)
and exactly(r["policy"], ["name", "version", "digest"]) and is_token(r["policy"]["name"])
and is_int(r["policy"]["version"], 1) and is_hex(r["policy"]["digest"], 64)
and exactly(r["pricebook"], ["digest", "currency", "issued"]) and is_hex(r["pricebook"]["digest"], 64)
and isinstance(r["pricebook"]["currency"], str) and CURRENCY.match(r["pricebook"]["currency"]) is not None
and is_int(r["pricebook"]["issued"])
and isinstance(r["candidates"], list) and len(r["candidates"]) <= 1024 and all(candidate_form(c, book_of(v)) for c in r["candidates"])
and isinstance(r["order"], list) and len(r["order"]) <= 1024 and all(is_token(x) for x in r["order"])
and exactly(r["decision"], ["decided_by", "runner_up"]) and r["decision"]["decided_by"] in DECIDED_BY
and null_or(r["decision"]["runner_up"], is_token)
and is_int(r["accepted"]) and null_or(r["deadline"], is_int)
and isinstance(r["attempts"], list) and len(r["attempts"]) <= 16
and all(attempt_form(i, a, v) for i, a in enumerate(r["attempts"]))
and is_hex(r["signer"], 64) and is_hex(r["sig"], 128))
_VERIFIED = {}
def ed_verify(key, msg, sig):
k = (key, hashlib.sha256(msg).digest(), sig)
if k not in _VERIFIED:
_VERIFIED[k] = ED.verify(key, msg, sig)
return _VERIFIED[k]
def signing_bytes(r):
body = {k: v for k, v in r.items() if k != "sig"}
return SIGN_DOMAIN + canonical(body).encode("utf-8")
def billing(served, end, deadline):
"""§5.2: (billed, not_billed)."""
if not served:
return False, "failed"
if deadline is not None and end > deadline:
return False, "deadline_missed"
return True, None
# ---------------------------------------------------------------- §7 verification
def verify(doc):
try:
return verify_steps(doc)
except Refuse as f:
return {"outcome": "refuse", "reason": f.reason, "at": f.at}
def verify_steps(doc):
has_third = isinstance(doc, dict) and "third_party" in doc
has_deliveries = isinstance(doc, dict) and "deliveries" in doc
members = ["request", "pricebook", "policy", "record", "receipts", "keys"] + (["third_party"] if has_third else []) \
+ (["deliveries"] if has_deliveries else [])
if not exactly(doc, members) \
or (has_deliveries and not (isinstance(doc["deliveries"], list) and all(
exactly(x, ["request", "pricebook", "policy", "decision"] + (["receipt"] if isinstance(x, dict) and "receipt" in x else []))
and all(isinstance(x[k], str) for k in x)
for x in doc["deliveries"]))) \
or (has_deliveries and doc["keys"] is None and any("receipt" in x for x in doc["deliveries"])) \
or (has_third and not (isinstance(doc["third_party"], list) and all(isinstance(x, str) for x in doc["third_party"]))) \
or (has_third and doc["receipts"] is None) \
or not all(isinstance(doc[k], str) for k in ("request", "pricebook", "policy", "record")) \
or not (doc["receipts"] is None or (isinstance(doc["receipts"], list) and all(isinstance(x, str) for x in doc["receipts"]))) \
or not (doc["keys"] is None or isinstance(doc["keys"], dict)) \
or (doc["receipts"] is not None and doc["keys"] is None):
raise Refuse("malformed", [])
# 1-3. The documents.
req = read(doc["request"], request_ok, "request_invalid", ["request"])
book = read(doc["pricebook"], pricebook_ok, "pricebook_invalid", ["pricebook"])
pol = read(doc["policy"], policy_ok, "policy_invalid", ["policy"])
# 4. The record.
rec = read_document(doc["record"])
if not isinstance(rec, dict) or rec.get("kind") != "wai-route-record":
raise Refuse("malformed", ["record"])
if on("version") and is_int(rec.get("version")) and rec["version"] not in (1, 2, 3):
raise Refuse("unknown_version", ["record", "version"])
if on("record-form") and not record_form(rec):
raise Refuse("record_invalid", ["record"])
if on("signature"):
bad = ed_verify(bytes.fromhex(rec["signer"]), signing_bytes(rec), bytes.fromhex(rec["sig"]))
if bad:
raise Refuse(bad, ["record", "sig"])
# 5. The binding.
deadline = None if req["deadline_us"] is None else rec["accepted"] + req["deadline_us"]
if on("binding-request"):
for m, want in (("job", req["job"]), ("operation", req["operation"]), ("capability", req["capability"]),
("fallback", req["fallback"]), ("quantities", req["quantities"]), ("request", digest(req)),
("deadline", deadline)):
if not same(rec[m], want):
raise Refuse("request_mismatch", ["record", m])
if on("binding-policy") and not same(rec["policy"], {"name": pol["name"], "version": pol["version"], "digest": digest(pol)}):
raise Refuse("policy_mismatch", ["record", "policy"])
if on("record-version") and not book_of(rec["version"]) == book_version(book):
raise Refuse("pricebook_mismatch", ["record", "version"])
if on("binding-pricebook") and not same(rec["pricebook"], {"digest": digest(book), "currency": book["currency"], "issued": book["issued"]}):
raise Refuse("pricebook_mismatch", ["record", "pricebook"])
# 6. The candidates, the order, the decision.
cands, order, decision, allowed = route(req, book, pol)
stated = rec["candidates"]
for i in range(max(len(stated), len(cands))):
at = ["record", "candidates", i]
if i >= len(stated) or i >= len(cands):
raise Refuse("candidate_mismatch", at)
s, c = stated[i], cands[i]
if on("candidates") and not same({k: v for k, v in s.items() if k not in ("removed", "rank")},
{k: v for k, v in c.items() if k not in ("removed", "rank")}):
raise Refuse("candidate_mismatch", at)
if on("removals") and not same(s["removed"], c["removed"]):
raise Refuse("removal_mismatch", at)
if on("ranks") and not same(s["rank"], c["rank"]):
raise Refuse("rank_mismatch", at)
if on("order") and not same(rec["order"], order):
raise Refuse("rank_mismatch", ["record", "order"])
if on("decision") and not same(rec["decision"], decision):
raise Refuse("decision_mismatch", ["record", "decision"])
# 7. The attempts.
atts, prev_end = rec["attempts"], rec["accepted"]
for i, a in enumerate(atts):
at = ["record", "attempts", i]
served = a["outcome"] == "served"
if on("allowed") and i >= allowed:
raise Refuse("attempts_exceeded", at)
if on("removed") and a["endpoint"] not in order:
raise Refuse("attempt_removed", at)
if on("in-order") and (i >= len(order) or a["endpoint"] != order[i]):
raise Refuse("attempt_out_of_order", at)
if on("served-last") and served and i != len(atts) - 1:
raise Refuse("served_not_last", at)
if on("times") and (a["start"] < prev_end or a["end"] < a["start"]):
raise Refuse("times_invalid", at)
prev_end = a["end"]
billed, not_billed = billing(served, a["end"], deadline)
if on("billing") and not (same(a["billed"], billed) and same(a["not_billed"], not_billed)):
raise Refuse("billing_invalid", at)
last_served = bool(atts) and atts[-1]["outcome"] == "served"
if on("complete") and not last_served and len(atts) < allowed:
raise Refuse("attempts_incomplete", ["record", "attempts"])
# 8. The hop receipts.
unmetered = None
if doc["receipts"] is not None:
unmetered = hop_receipts(rec, doc["receipts"], doc["keys"])
# 9. The third-party hop records.
third_party = third_party_records(rec, doc["third_party"], doc["receipts"]) if has_third else None
# 10. The delivery decisions.
deliveries = delivery_decisions(rec, doc["deliveries"], doc["keys"]) if has_deliveries else None
served_attempt = atts[-1] if last_served else None
billed_hop = next((a["hop"] for a in atts if a["billed"]), None)
if deadline is None or served_attempt is None:
dl = "none"
else:
dl = "missed" if served_attempt["end"] > deadline else "met"
accepted = {
"outcome": "accept",
"digest": digest(rec),
"job": req["job"],
"operation": req["operation"],
"served": None if served_attempt is None else dict(
{"endpoint": served_attempt["endpoint"], "hop": served_attempt["hop"]},
**({"outputs": served_attempt["outputs"]} if "outputs" in served_attempt else {}),
**({"delivered": served_attempt["delivered"]} if "delivered" in served_attempt else {})),
"billed": billed_hop,
"deadline": dl,
"order": order,
"decision": decision,
"removed": [{"endpoint": c["endpoint"], "limits": [x["limit"] for x in c["removed"]]} for c in cands if c["removed"]],
"receipts": "checked" if unmetered is not None else "not_checked",
"unmetered": unmetered,
}
if third_party is not None:
accepted["third_party"] = third_party
if deliveries is not None:
accepted["deliveries"] = deliveries
return accepted
def commitment(outputs):
"""oer-receipts §3 *Output*: one output's content hash, or the jwp-receipts §2.2 root over
several in their order, every leaf unlabelled, an odd level padded with its last node."""
hs = [bytes.fromhex(x) for x in outputs]
if len(hs) == 1:
return hs[0].hex()
level = [blake3(LEAF + h) for h in hs]
while len(level) > 1:
if len(level) % 2:
level.append(level[-1])
level = [blake3(NODE + level[i] + level[i + 1]) for i in range(0, len(level), 2)]
return level[0].hex() if level else "00" * 32
def inclusion_proof(h, index, siblings, count, root):
"""§6.1: whether `siblings` proves the object whose content hash is `h` (bytes) the one at
`index` of `count` objects delivered under `root` (bytes): `index` below `count`, exactly
ceil(log2 count) siblings (none for one object, whose root is its content hash), and the walk
from its unlabelled leaf, the sibling on the left where bit i of `index` is 1, ends at
`root`."""
if not (0 <= index < count) or len(siblings) != (count - 1).bit_length():
return False
if count == 1:
return h == root
node = blake3(LEAF + h)
for i, sib in enumerate(siblings):
node = blake3(NODE + sib + node) if (index >> i) & 1 else blake3(NODE + node + sib)
return node == root
def hop_receipts(rec, texts, keys):
"""§6: each attempt held to its hop's OER/2 receipt. The hops whose receipts state the
unmetered marker."""
read_ = []
for j, t in enumerate(texts):
try:
read_.append(OER.load(io.StringIO(t)))
except ValueError:
raise Refuse("receipt_invalid", ["receipts", j])
unmetered = []
for i, a in enumerate(rec["attempts"]):
at = ["record", "attempts", i, "receipt"]
r = next((x for x in read_ if isinstance(x, dict) and x.get("receipt_id") == a["receipt"]), None)
if r is None:
if on("receipt-given"):
raise Refuse("receipt_missing", at)
continue
if on("receipt-verifies") and OER.verify_receipt(r, keys):
raise Refuse("receipt_invalid", at)
status = 1 if a["outcome"] == "served" else -1
work = r.get("work") if isinstance(r.get("work"), dict) else {}
got = work.get("status")
status_ok = isinstance(got, int) and not isinstance(got, bool) and got == status
if on("receipt-matches") and (r.get("kind") != "job" or r.get("capability") != "oer.job." + rec["operation"] or not status_ok):
raise Refuse("receipt_mismatch", at)
# Version 2 (§6): the served hop's receipt names the outputs the attempt states.
if on("receipt-output") and a["outcome"] == "served" and "outputs" in a:
subject = r.get("subject") if isinstance(r.get("subject"), dict) else {}
if subject.get("output") != commitment(a["outputs"]):
raise Refuse("receipt_output_mismatch", at)
# Version 3 (§6): it names the root the attempt states.
if on("receipt-root") and a["outcome"] == "served" and "delivered" in a:
subject = r.get("subject") if isinstance(r.get("subject"), dict) else {}
if subject.get("output") != a["delivered"]["root"]:
raise Refuse("receipt_output_mismatch", at)
if OER.is_unmetered(r):
unmetered.append(a["hop"])
return unmetered
# ---------------------------------------------------------------- §6.2 third-party hop records
THIRD_DOMAIN = b"wai:route-third-party-hop\x01"
THIRD = ["kind", "version", "job", "hop", "endpoint", "provider", "receipt", "origin", "account", "provider_job", "outcome",
"error", "exchanges", "outputs", "usage", "charge", "start", "end", "signer", "sig"]
EXCHANGE = ["method", "url", "start", "end", "sent", "status", "received", "json"]
ORIGIN = re.compile(r"\Ahttps://[a-z0-9.-]{1,253}(?::[0-9]{1,5})?\Z")
METHOD = re.compile(r"\A[A-Z]{1,16}\Z")
EMPTY_SHA256 = hashlib.sha256(b"").hexdigest()
def is_text(v, top):
return isinstance(v, str) and 1 <= len(v.encode("utf-8")) <= top
def url_ok(v):
if not isinstance(v, str) or "?" in v or "#" in v:
return False
for scheme in ("https://", "http://"):
if v.startswith(scheme):
return 1 <= len(v[len(scheme):].encode("utf-8")) <= 2040
return False
def is_link(s):
return s.startswith("https://") or s.startswith("http://")
def keeps_a_query(v):
"""A string, a member's name among them, that begins as a URL and holds a `?` or a `#`."""
if isinstance(v, str):
return is_link(v) and ("?" in v or "#" in v)
if isinstance(v, list):
return any(keeps_a_query(x) for x in v)
if isinstance(v, dict):
return any(keeps_a_query(k) or keeps_a_query(x) for k, x in v.items())
return False
def body_ok(v):
return exactly(v, ["sha256", "bytes"]) and is_hex(v["sha256"], 64) and is_int(v["bytes"])
def exchange_ok(x, previous, end):
if not exactly(x, EXCHANGE):
return False
if on("tp-method") and not (isinstance(x["method"], str) and METHOD.match(x["method"])):
return False
if on("tp-url") and not url_ok(x["url"]):
return False
if not (is_int(x["start"]) and is_int(x["end"])):
return False
if on("tp-exchange-times") and x["start"] > x["end"]:
return False
if on("tp-past-end") and x["end"] > end:
return False
if on("tp-exchange-order") and x["start"] < previous:
return False
if on("tp-body") and not (body_ok(x["sent"]) and body_ok(x["received"])):
return False
if on("tp-status") and not (is_int(x["status"]) and (x["status"] == 0 or 100 <= x["status"] <= 599)):
return False
if on("tp-no-response") and x["status"] == 0 and (x["received"] != {"sha256": EMPTY_SHA256, "bytes": 0} or x["json"] is not None):
return False
if on("tp-kept-query") and keeps_a_query(x["json"]):
return False
return True
def usage_ok(u):
if not exactly(u, ["unit", "quantity", "source"]):
return False
return (not on("tp-usage-unit") or is_unit(u["unit"])) and (not on("tp-usage-quantity") or is_int(u["quantity"])) \
and (not on("tp-usage-source") or is_text(u["source"], 512))
def charge_ok(c):
if not exactly(c, ["amount", "unit", "source"]):
return False
return (not on("tp-charge-amount") or is_text(c["amount"], 64)) and (not on("tp-charge-unit") or is_text(c["unit"], 64)) \
and (not on("tp-charge-source") or is_text(c["source"], 512))
def third_outputs_ok(v):
if not isinstance(v, list):
return False
names, hashes = set(), set()
for e in v:
if not (isinstance(e, list) and len(e) == 2 and isinstance(e[0], str) and isinstance(e[1], str)):
return False
if on("tp-name-form") and not is_text(e[0], 255):
return False
if on("tp-name-slash") and "/" in e[0]:
return False
if on("tp-hash-form") and not is_hex(e[1], 64):
return False
if on("tp-name-twice") and e[0] in names:
return False
if on("tp-object-twice") and e[1] in hashes:
return False
names.add(e[0])
hashes.add(e[1])
return True
def third_party_form(r):
if on("tp-members") and not exactly(r, THIRD):
return False
if not exactly(r, THIRD):
return True
if on("tp-version-form") and not (is_int(r["version"]) and r["version"] == 1):
return False
if not (is_int(r["start"]) and is_int(r["end"]) and isinstance(r["exchanges"], list)):
return False
if on("tp-exchanges-number") and not r["exchanges"]:
return False
if on("tp-most-exchanges") and len(r["exchanges"]) > 1024:
return False
previous = r["start"]
for x in r["exchanges"]:
if not exchange_ok(x, previous, r["end"]):
return False
if isinstance(x, dict) and is_int(x.get("start")):
previous = x["start"]
if on("tp-outputs-form") and not third_outputs_ok(r["outputs"]):
return False
if on("tp-outcome-form"):
if r["outcome"] == "served":
if not (r["error"] is None and isinstance(r["outputs"], list) and len(r["outputs"]) >= 1 and usage_ok(r["usage"])):
return False
if on("tp-most-outputs") and len(r["outputs"]) > 1024:
return False
elif r["outcome"] == "failed":
err = r["error"]
if not (exactly(err, ["class", "code"]) and r["outputs"] == [] and (r["usage"] is None or usage_ok(r["usage"]))):
return False
if on("tp-error-class") and err["class"] not in ERRORS:
return False
if on("tp-error-code") and not is_text(err["code"], 256):
return False
else:
return False
if on("tp-hop-form") and not (is_int(r["hop"]) and r["hop"] >= 1):
return False
if on("tp-origin") and not (isinstance(r["origin"], str) and ORIGIN.match(r["origin"])):
return False
if on("tp-account") and r["account"] not in ("operator", "customer"):
return False
if on("tp-charge-form") and not (r["charge"] is None or charge_ok(r["charge"])):
return False
if on("tp-signer-form") and not (is_hex(r["signer"], 64) and is_hex(r["sig"], 128)):
return False
if on("tp-job-form") and not is_token(r["job"]):
return False
if on("tp-endpoint-form") and not is_token(r["endpoint"]):
return False
if on("tp-provider-form") and not is_token(r["provider"]):
return False
if on("tp-receipt-form") and not (isinstance(r["receipt"], str) and RECEIPT_ID.match(r["receipt"])):
return False
if on("tp-provider-job") and not (r["provider_job"] is None or is_text(r["provider_job"], 256)):
return False
return True
def third_party_records(rec, texts, receipt_texts):
"""§7.2 step 9: each record read, its signature checked, and held to its hop."""
receipts = [OER.load(io.StringIO(t)) for t in receipt_texts]
seen = set()
out = []
for i, t in enumerate(texts):
place = ["third_party", i]
r = read_document(t)
if not isinstance(r, dict) or r.get("kind") != "wai-route-third-party-hop":
raise Refuse("malformed", place)
if on("tp-version") and is_int(r.get("version")) and r["version"] != 1:
raise Refuse("unknown_version", place + ["version"])
if on("tp-form") and not third_party_form(r):
raise Refuse("third_party_invalid", place)
if on("tp-signature"):
body = {k: v for k, v in r.items() if k != "sig"}
bad = ed_verify(bytes.fromhex(r["signer"]), THIRD_DOMAIN + canonical(body).encode("utf-8"), bytes.fromhex(r["sig"]))
if bad:
raise Refuse(bad, place + ["sig"])
mismatch = lambda m: Refuse("third_party_mismatch", place + [m])
if on("tp-signer") and r["signer"] != rec["signer"]:
raise mismatch("signer")
if on("tp-job") and r["job"] != rec["job"]:
raise mismatch("job")
atts = rec["attempts"]
if r["hop"] > len(atts):
if on("tp-hop"):
raise mismatch("hop")
continue
if on("tp-one-a-hop") and r["hop"] in seen:
raise mismatch("hop")
seen.add(r["hop"])
a = atts[r["hop"] - 1]
for m in ("endpoint", "receipt", "outcome"):
if on("tp-" + m) and r[m] != a[m]:
raise mismatch(m)
if on("tp-error") and r["outcome"] == "failed" and r["error"]["class"] != a["error"]:
raise mismatch("error")
c = next((x for x in rec["candidates"] if x["endpoint"] == r["endpoint"]), None)
if on("tp-provider") and (c is None or r["provider"] != c["provider"]):
raise mismatch("provider")
if on("tp-unit") and r["usage"] is not None and (c is None or r["usage"]["unit"] != c["unit"]):
raise mismatch("usage")
if on("tp-start") and r["start"] < a["start"]:
raise mismatch("start")
if on("tp-end") and r["end"] > a["end"]:
raise mismatch("end")
if r["outcome"] == "served" and a["outcome"] == "served":
root = commitment([e[1] for e in r["outputs"]])
receipt = next(x for x in receipts if isinstance(x, dict) and x.get("receipt_id") == a["receipt"])
subject = receipt.get("subject") if isinstance(receipt.get("subject"), dict) else {}
if on("tp-root") and subject.get("output") != root:
raise Refuse("third_party_output_mismatch", place + ["outputs"])
if on("tp-count") and "delivered" in a and a["delivered"]["count"] != len(r["outputs"]):
raise Refuse("third_party_output_mismatch", place + ["outputs"])
out.append({"hop": r["hop"], "digest": digest(r), "account": r["account"], "usage": r["usage"], "charge": r["charge"]})
out.sort(key=lambda x: x["hop"])
return out
# ---------------------------------------------------------------- §6.3 a delivery's decision
DELIVERY_DOMAIN = b"wai:route-delivery-decision\x01"
DELIVERY = ["kind", "version", "job", "parent", "seq", "prev", "objects", "delivery", "operation", "capability", "fallback",
"quantities", "request", "policy", "pricebook", "candidates", "order", "decision", "route", "passed", "decided",
"signer", "sig"]
PASSED_KEYS = ["targets", "price", "ttfb", "energy", "quality", "endpoint"] # §4.4
OFFER = ["entry", "unit", "quantity", "cost", "energy", "energy_class", "energy_ppm", "grade"] # §6.4
GRADES = ["projected", "simulated", "derived", "measured"] # oer-receipts §2, weakest first
GRADE_OF = {"CalibratedInstrument": "measured", "OnChipCounter": "measured", "ModelBased": "derived", "Estimator": "projected"}
PRICE_EXCEEDED = "price_exceeded" # §6.4
def offer_form(o):
"""§6.4: an offer's members, each of its own form."""
return (exactly(o, OFFER) and is_int(o["entry"]) and is_unit(o["unit"]) and is_int(o["quantity"], 1)
and null_or(o["cost"], is_int) and null_or(o["energy"], is_int)
and null_or(o["energy_class"], lambda c: c in CLASSES) and ppm_ok(o["energy_ppm"])
and null_or(o["grade"], lambda g: g in GRADES))
def book_of_decision(version):
"""§3.4: the price book version a decision of `version` rests on."""
return 3 if version == 2 else 2
def delivery_form(d):
"""§6.3: the members, each of its own form. Relations between them are step 10's."""
version = d.get("version") if isinstance(d, dict) else None
known = is_int(version) and version in (1, 2)
if not exactly(d, DELIVERY + (["offer"] if known and version == 2 else [])):
return False
o = d["objects"]
return (known and (version == 1 or null_or(d["offer"], offer_form))
and is_token(d["job"]) and is_hex(d["parent"], 64) and is_int(d["seq"], 1)
and null_or(d["prev"], lambda x: is_hex(x, 64))
and exactly(o, ["count", "root"]) and is_int(o["count"], 1) and is_hex(o["root"], 64)
and is_token(d["delivery"]) and isinstance(d["operation"], str) and OPERATION.match(d["operation"]) is not None
and is_cap(d["capability"]) and null_or(d["fallback"], is_cap) and quantities_ok(d["quantities"])
and is_hex(d["request"], 64)
and exactly(d["policy"], ["name", "version", "digest"]) and is_token(d["policy"]["name"])
and is_int(d["policy"]["version"], 1) and is_hex(d["policy"]["digest"], 64)
and exactly(d["pricebook"], ["digest", "currency", "issued"]) and is_hex(d["pricebook"]["digest"], 64)
and isinstance(d["pricebook"]["currency"], str) and CURRENCY.match(d["pricebook"]["currency"]) is not None
and is_int(d["pricebook"]["issued"])
and isinstance(d["candidates"], list) and len(d["candidates"]) <= 1024 and all(candidate_form(c, book_of_decision(version)) for c in d["candidates"])
and isinstance(d["order"], list) and len(d["order"]) <= 1024 and all(is_token(x) for x in d["order"])
and exactly(d["decision"], ["decided_by", "runner_up"]) and d["decision"]["decided_by"] in DECIDED_BY
and null_or(d["decision"]["runner_up"], is_token)
and null_or(d["route"], is_token)
and isinstance(d["passed"], list) and len(d["passed"]) <= 1023
and all(exactly(x, ["endpoint", "key"]) and is_token(x["endpoint"]) and x["key"] in PASSED_KEYS for x in d["passed"])
and is_int(d["decided"]) and is_hex(d["signer"], 64) and is_hex(d["sig"], 128))
def passed_over(req, pol, cands):
"""§6.3 `passed`: each survivor after the first, in the order, with the first key of the key
list on which it and the first differ."""
survivors = sorted((c for c in cands if c["rank"] is not None), key=lambda c: c["rank"])
if not survivors:
return []
out = []
for c in survivors[1:]:
key = next(k for k in key_list(req, pol) if compare(req, k, survivors[0], c))
out.append({"endpoint": c["endpoint"], "key": key})
return out
def delivered_by(rec):
"""What the record's served attempt delivered: (count, root, end), or None for a record of
version 1 and one no hop served."""
atts = rec["attempts"]
if not atts or atts[-1]["outcome"] != "served":
return None
a = atts[-1]
if "delivered" in a:
return a["delivered"]["count"], a["delivered"]["root"], a["end"]
if "outputs" in a:
return len(a["outputs"]), commitment(a["outputs"]), a["end"]
return None
def offer_of(req, book, cands, order):
"""§6.4: what the first of the order was taken at, or None when no route survives."""
if not order:
return None
c = next(x for x in cands if x["endpoint"] == order[0])
place = next(i for i, e in enumerate(book["entries"]) if e["endpoint"] == c["endpoint"] and e["operation"] == c["operation"])
return {"entry": place, "unit": c["unit"], "quantity": quantity(req, c["unit"]), "cost": c["cost"], "energy": c["energy"],
"energy_class": c["energy_class"], "energy_ppm": c["energy_ppm"], "grade": GRADE_OF.get(c["energy_class"])}
def micro_joules(v):
"""A receipt's figure in whole microjoules, rounded to the nearest and a half to the even one
(round() on a Fraction does exactly that); None when it is negative or past what a u64 holds."""
n = round(Fraction(str(v)) * 10 ** 6)
return n if 0 <= n < 2 ** 64 and Fraction(str(v)) >= 0 else None
def held_to_offer(text, d, decision_digest, keys, here):
"""§6.4: a delivery's receipt held to its decision and its offer. What it gave (§7.3)."""
try:
r = OER.load(io.StringIO(text))
except ValueError:
raise Refuse("receipt_invalid", here)
if on("dr-verifies") and OER.verify_receipt(r, keys):
raise Refuse("receipt_invalid", here)
subject = r.get("subject") if isinstance(r.get("subject"), dict) else {}
work = r.get("work") if isinstance(r.get("work"), dict) else {}
chain = r.get("chain") if isinstance(r.get("chain"), dict) else {}
status = work.get("status")
inputs = subject.get("inputs") if isinstance(subject.get("inputs"), list) else []
for check_, holds in (("dr-kind", r.get("kind") == "job"),
("dr-operation", r.get("capability") == "oer.job." + d["operation"]),
("dr-status", isinstance(status, int) and not isinstance(status, bool) and status == 1),
("dr-route", chain.get("signer") == d["route"]),
("dr-decision", "sha256:" + decision_digest in inputs)):
if on(check_) and not holds:
raise Refuse("receipt_mismatch", here)
if on("dr-output") and subject.get("output") != d["objects"]["root"]:
raise Refuse("receipt_output_mismatch", here)
offer = d["offer"]
how = r["provenance"]["how"]
if on("dr-grade") and offer["grade"] is not None and GRADES.index(how) > GRADES.index(offer["grade"]):
raise Refuse("delivery_grade_exceeded", here)
figure = None if OER.is_unmetered(r) else micro_joules(r["energy"]["joules_total"])
if on("dr-figure") and figure is None and not OER.is_unmetered(r):
raise Refuse("receipt_mismatch", here)
ppm = (offer["energy_ppm"] or 0) if on("dr-uncertainty") else 0
allowed = None if offer["energy"] is None else offer["energy"] * (10 ** 6 + ppm)
above = figure is not None and allowed is not None and figure * 10 ** 6 > allowed
flagged = work.get("outcome") == PRICE_EXCEEDED
if on("dr-unflagged") and above and not flagged:
raise Refuse("delivery_price_unflagged", here)
if on("dr-misflagged") and flagged and not above:
raise Refuse("delivery_price_misflagged", here)
verdict = "no_figure" if allowed is None else "unmetered" if figure is None else "exceeded" if above else "within"
return {"verdict": verdict, "receipt": r["receipt_id"], "energy": figure, "grade": how}
def delivery_decisions(rec, entries, keys):
"""§7.2 step 10: a job's decisions, from the first and in order."""
parent = delivered_by(rec)
out, before, seen = [], None, set()
for i, t in enumerate(entries):
place = ["deliveries", i]
req = read(t["request"], request_ok, "request_invalid", place + ["request"])
book = read(t["pricebook"], pricebook_ok, "pricebook_invalid", place + ["pricebook"])
pol = read(t["policy"], policy_ok, "policy_invalid", place + ["policy"])
d = read_document(t["decision"])
at = lambda m: place + ["decision", m] # noqa: E731
if not isinstance(d, dict) or d.get("kind") != "wai-route-delivery-decision":
raise Refuse("malformed", place + ["decision"])
if on("dd-version") and is_int(d.get("version")) and d["version"] not in (1, 2):
raise Refuse("unknown_version", at("version"))
if on("dd-form") and not delivery_form(d):
raise Refuse("delivery_invalid", place + ["decision"])
if on("dd-signature"):
body = {k: v for k, v in d.items() if k != "sig"}
bad = ed_verify(bytes.fromhex(d["signer"]), DELIVERY_DOMAIN + canonical(body).encode("utf-8"), bytes.fromhex(d["sig"]))
if bad:
raise Refuse(bad, at("sig"))
# Its documents.
for m, want in (("delivery", req["job"]), ("operation", req["operation"]), ("capability", req["capability"]),
("fallback", req["fallback"]), ("quantities", req["quantities"]), ("request", digest(req))):
if on("dd-request-" + m) and not same(d[m], want):
raise Refuse("request_mismatch", at(m))
for m, want in (("name", pol["name"]), ("version", pol["version"]), ("digest", digest(pol))):
if on("dd-policy-" + m) and not same(d["policy"][m], want):
raise Refuse("policy_mismatch", at("policy"))
if on("dd-pricebook-version") and book_version(book) != book_of_decision(d["version"]):
raise Refuse("pricebook_mismatch", at("pricebook"))
for m, want in (("digest", digest(book)), ("currency", book["currency"]), ("issued", book["issued"])):
if on("dd-pricebook-" + m) and not same(d["pricebook"][m], want):
raise Refuse("pricebook_mismatch", at("pricebook"))
# §4 over them.
cands, order, decision, _ = route(req, book, pol)
stated = d["candidates"]
for j in range(max(len(stated), len(cands))):
here = place + ["decision", "candidates", j]
if j >= len(stated) or j >= len(cands):
raise Refuse("candidate_mismatch", here)
s_, c = stated[j], cands[j]
if on("dd-candidates") and not same({k: v for k, v in s_.items() if k not in ("removed", "rank")},
{k: v for k, v in c.items() if k not in ("removed", "rank")}):
raise Refuse("candidate_mismatch", here)
if on("dd-removals") and not same(s_["removed"], c["removed"]):
raise Refuse("removal_mismatch", here)
if on("dd-ranks") and not same(s_["rank"], c["rank"]):
raise Refuse("rank_mismatch", here)
if on("dd-order") and not same(d["order"], order):
raise Refuse("rank_mismatch", at("order"))
if on("dd-decision") and not same(d["decision"], decision):
raise Refuse("decision_mismatch", at("decision"))
# Its route.
route_ = order[0] if order else None
if on("dd-route") and not same(d["route"], route_):
raise Refuse("delivery_route_mismatch", at("route"))
passed = passed_over(req, pol, cands)
if on("dd-passed") and not same(d["passed"], passed):
raise Refuse("delivery_route_mismatch", at("passed"))
# Its offer.
if d["version"] == 2:
offer = offer_of(req, book, cands, order)
if on("dd-offer") and (offer is None) != (d["offer"] is None):
raise Refuse("delivery_offer_mismatch", at("offer"))
for m in OFFER if offer is not None and d["offer"] is not None else ():
if on("dd-offer-" + m) and not same(d["offer"][m], offer[m]):
raise Refuse("delivery_offer_mismatch", at("offer"))
# Its parent.
for m in ("signer", "job"):
if on("dd-parent-" + m) and d[m] != rec[m]:
raise Refuse("delivery_parent_mismatch", at(m))
if on("dd-parent") and d["parent"] != digest(rec):
raise Refuse("delivery_parent_mismatch", at("parent"))
if parent is None:
raise Refuse("delivery_parent_mismatch", at("objects"))
count, root, served_end = parent
if on("dd-objects") and not same(d["objects"], {"count": count, "root": root}):
raise Refuse("delivery_parent_mismatch", at("objects"))
# Its chain.
if on("dd-seq") and d["seq"] != i + 1:
raise Refuse("delivery_chain_broken", at("seq"))
if on("dd-prev") and d["prev"] != (before[0] if before else None):
raise Refuse("delivery_chain_broken", at("prev"))
if on("dd-decided") and (d["decided"] < served_end or (before is not None and d["decided"] < before[1])):
raise Refuse("delivery_chain_broken", at("decided"))
if on("dd-once") and req["job"] in seen:
raise Refuse("delivery_chain_broken", at("delivery"))
seen.add(req["job"])
# Its receipt.
conformance = {"verdict": "not_checked", "receipt": None, "energy": None, "grade": None}
if "receipt" in t:
if on("dr-version") and d["version"] != 2:
raise Refuse("delivery_offer_mismatch", at("version"))
if on("dr-a-route") and route_ is None:
raise Refuse("delivery_offer_mismatch", place + ["receipt"])
conformance = held_to_offer(t["receipt"], d, digest(d), keys, place + ["receipt"])
before = (digest(d), d["decided"])
read_ = {"seq": i + 1, "delivery": req["job"], "digest": digest(d), "route": route_, "decision": decision, "passed": passed}
if d["version"] == 2:
read_.update(offer=d["offer"], conformance=conformance)
out.append(read_)
return out
# ---------------------------------------------------------------- the corpus
def check(doc, quiet=False):
fails = []
for v in doc.get("vectors", []):
try:
got = verify(v["document"])
except Exception as e: # a crash on a mutated copy is a failure to verify it
got = {"outcome": "crash", "error": "%s: %s" % (type(e).__name__, e)}
ok = got == v.get("outcome")
if not quiet:
what = got["outcome"] + (" %s at %s" % (got["reason"], got["at"]) if got["outcome"] == "refuse" else "")
print(("PASS" if ok else "FAIL") + " %s: %s" % (v.get("name"), what))
if not ok:
fails.append(v.get("name"))
if quiet: # a mutated copy or a weakened verifier only asks whether anything fails
break
return fails
def reasons_listed():
"""§8's table, in order, and the codes its paragraph names as reused."""
text = open(EXTENSION, encoding="utf-8").read()
start = text.index("\n## 8. Reasons")
section = text[start:text.find("\n## ", start + 1)]
added = [m.group(1) for m in re.finditer(r"^\| `([a-z_]+)` \|", section, re.M)]
reused = [r for r in re.findall(r"`([a-z_]+)`", section.split("also raises", 1)[1]) if r not in added]
return added, reused
def flip(text, at):
b = bytearray(text.encode("utf-8"))
i = at % len(b)
b[i] = ord("1") if b[i] != ord("1") else ord("2")
return b.decode("utf-8", "replace")
def mutations(doc):
"""Copies of the corpus, one defect each, that a verifier must reject."""
out = []
def copy_with(name, k, edit):
m = copy.deepcopy(doc)
edit(m["vectors"][k])
out.append(("%s: %s" % (doc["vectors"][k]["name"], name), m))
for k, v in enumerate(doc["vectors"]):
d, o = v["document"], v["outcome"]
if o["outcome"] != "accept":
r = o["reason"]
copy_with("its stated reason changed", k, lambda x, r=r: x["outcome"].__setitem__("reason", "record_invalid" if r != "record_invalid" else "malformed"))
copy_with("its stated place changed", k, lambda x: x["outcome"].__setitem__("at", ["record", "elsewhere"]))
continue
for member in ("request", "pricebook", "policy", "record"):
digits = [i for i, c in enumerate(d[member]) if c in "23456789"]
if digits:
copy_with("a digit of its %s changed" % member, k, lambda x, m=member, i=digits[len(digits) // 2]:
x["document"].__setitem__(m, x["document"][m][:i] + str(int(x["document"][m][i]) - 1) + x["document"][m][i + 1:]))
at_sig = d["record"].index('"sig":"') + len('"sig":"')
copy_with("its record's signature flipped", k, lambda x, i=at_sig: x["document"].__setitem__(
"record", d["record"][:i] + ("1" if d["record"][i] == "0" else "0") + d["record"][i + 1:]))
if d["receipts"]:
copy_with("its receipts dropped", k, lambda x: x["document"].__setitem__("receipts", []))
copy_with("its first receipt's figure changed", k, lambda x: x["document"]["receipts"].__setitem__(
0, x["document"]["receipts"][0].replace('"latency_s":"1.0"', '"latency_s":"2.0"', 1)))
copy_with("its keys swapped for another", k, lambda x: x["document"].__setitem__(
"keys", {n: "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a" for n in x["document"]["keys"]}))
copy_with("its stated served hop changed", k, lambda x: x["outcome"].__setitem__("served", None if o["served"] else {"endpoint": "im.alpha", "hop": 1}))
copy_with("its stated billed hop changed", k, lambda x: x["outcome"].__setitem__("billed", None if o["billed"] else 1))
if o["served"] and "outputs" in o["served"]:
copy_with("its stated outputs reversed and one more", k, lambda x: x["outcome"]["served"].__setitem__(
"outputs", list(reversed(o["served"]["outputs"])) + ["00" * 32]))
copy_with("its served hop's outputs dropped from the outcome", k, lambda x: x["outcome"]["served"].pop("outputs"))
if o["served"] and "delivered" in o["served"]:
copy_with("its stated count one more", k, lambda x: x["outcome"]["served"]["delivered"].__setitem__("count", o["served"]["delivered"]["count"] + 1))
copy_with("its stated root another", k, lambda x: x["outcome"]["served"]["delivered"].__setitem__("root", "00" * 32))
copy_with("its served hop's delivered dropped from the outcome", k, lambda x: x["outcome"]["served"].pop("delivered"))
if "third_party" in o and o["third_party"]:
if o["third_party"][-1]["usage"] is not None:
copy_with("its stated third-party usage dropped", k, lambda x: x["outcome"]["third_party"][-1].__setitem__("usage", None))
copy_with("its stated third-party charge changed", k, lambda x: x["outcome"]["third_party"][0].__setitem__("charge", {"amount": "1", "unit": "x", "source": "y"}))
copy_with("its stated third-party account changed", k, lambda x: x["outcome"]["third_party"][0].__setitem__("account", "someone"))
copy_with("its stated third-party digest changed", k, lambda x: x["outcome"]["third_party"][0].__setitem__("digest", "00" * 32))
copy_with("a record's body named by another digest", k, lambda x: x["document"]["third_party"].__setitem__(
0, x["document"]["third_party"][0].replace('"bytes":', '"bytes":1', 1)))
if "deliveries" in o and o["deliveries"]:
copy_with("its stated route changed", k, lambda x: x["outcome"]["deliveries"][0].__setitem__("route", "relay.us" if o["deliveries"][0]["route"] != "relay.us" else None))
copy_with("its stated passed routes dropped", k, lambda x: x["outcome"]["deliveries"][0].__setitem__("passed", [] if o["deliveries"][0]["passed"] else [{"endpoint": "x", "key": "price"}]))
copy_with("its stated decision's digest changed", k, lambda x: x["outcome"]["deliveries"][0].__setitem__("digest", "00" * 32))
copy_with("its stated delivery's seq changed", k, lambda x: x["outcome"]["deliveries"][-1].__setitem__("seq", 7))
last = o["deliveries"][-1]
if "offer" in last:
copy_with("its stated offer dropped", k, lambda x: x["outcome"]["deliveries"][-1].pop("offer"))
copy_with("its stated conformance dropped", k, lambda x: x["outcome"]["deliveries"][-1].pop("conformance"))
other = "exceeded" if last["conformance"]["verdict"] != "exceeded" else "within"
copy_with("its stated verdict changed", k, lambda x, c=other: x["outcome"]["deliveries"][-1]["conformance"].__setitem__("verdict", c))
copy_with("its stated figure changed by one", k, lambda x, e=last["conformance"]["energy"]:
x["outcome"]["deliveries"][-1]["conformance"].__setitem__("energy", 1 if e is None else e + 1))
copy_with("its stated grade changed", k, lambda x, g=last["conformance"]["grade"]:
x["outcome"]["deliveries"][-1]["conformance"].__setitem__("grade", "simulated" if g != "simulated" else "derived"))
given = d["deliveries"][-1] if d["deliveries"] else {}
if "receipt" in given:
copy_with("its receipt dropped", k, lambda x: x["document"]["deliveries"][-1].pop("receipt"))
at_sig = given["receipt"].index('"signature":"') + len('"signature":"')
copy_with("its receipt's signature flipped", k, lambda x, i=at_sig, t=given["receipt"]: x["document"]["deliveries"][-1].__setitem__(
"receipt", t[:i] + ("1" if t[i] == "0" else "0") + t[i + 1:]))
copy_with("its last decision dropped", k, lambda x: x["document"]["deliveries"].pop())
copy_with("a decision's text another", k, lambda x: x["document"]["deliveries"][0].__setitem__(
"decision", x["document"]["deliveries"][0]["decision"].replace('"decided":', '"decided":1', 1)))
if "deliveries" in o and not o["deliveries"]:
copy_with("its stated deliveries dropped", k, lambda x: x["outcome"].pop("deliveries"))
copy_with("its stated deadline changed", k, lambda x: x["outcome"].__setitem__("deadline", "met" if o["deadline"] != "met" else "missed"))
copy_with("its stated order reversed", k, lambda x: x["outcome"].__setitem__("order", list(reversed(o["order"])) + ["x"]))
copy_with("its stated decision changed", k, lambda x: x["outcome"]["decision"].__setitem__("decided_by", "price" if o["decision"]["decided_by"] != "price" else "ttfb"))
copy_with("its stated removals dropped", k, lambda x: x["outcome"].__setitem__("removed", o["removed"][1:] if o["removed"] else [{"endpoint": "x", "limits": ["ga"]}]))
changed = [] if o["unmetered"] is None else (None if o["unmetered"] == [] else [])
copy_with("its stated unmetered hops changed", k, lambda x, c=changed: x["outcome"].__setitem__("unmetered", c))
return out
CHECKS = ["request_invalid", "pricebook_invalid", "policy_invalid", "version", "record-form", "signature",
"binding-request", "binding-policy", "binding-pricebook", "candidates", "removals", "ranks", "order", "decision",
"allowed", "removed", "in-order", "served-last", "times", "billing", "complete",
"receipt-given", "receipt-verifies", "receipt-matches",
"pricebook-version", "failures-relations", "operation-candidates", "thin-basis", "unbounded", "expected-price",
"expected-ttfb", "expected-targets", "record-version", "outputs-form", "receipt-output", "delivered-form",
"receipt-root",
"tp-version", "tp-form", "tp-signature", "tp-members", "tp-version-form", "tp-exchanges-number", "tp-method", "tp-url",
"tp-exchange-times", "tp-past-end", "tp-exchange-order", "tp-body", "tp-status", "tp-no-response",
"tp-kept-query", "tp-outputs-form", "tp-name-twice", "tp-object-twice", "tp-outcome-form", "tp-hop-form", "tp-origin",
"tp-account", "tp-charge-form", "tp-signer-form", "tp-usage-unit", "tp-usage-quantity", "tp-usage-source", "tp-charge-amount",
"tp-charge-unit", "tp-charge-source", "tp-name-form", "tp-name-slash", "tp-hash-form", "tp-error-class", "tp-error-code",
"tp-job-form", "tp-endpoint-form", "tp-provider-form", "tp-receipt-form", "tp-provider-job", "tp-most-outputs",
"tp-most-exchanges", "tp-signer", "tp-job", "tp-hop", "tp-one-a-hop", "tp-endpoint",
"tp-receipt", "tp-outcome", "tp-error", "tp-provider", "tp-unit", "tp-start", "tp-end", "tp-root", "tp-count",
"limit-policy", "limit-unit", "limit-region", "limit-residency", "limit-training", "limit-retention", "limit-ga",
"limit-consent", "limit-unit-price", "limit-spend",
"dd-version", "dd-form", "dd-signature", "dd-request-delivery", "dd-request-operation", "dd-request-capability",
"dd-request-fallback", "dd-request-quantities", "dd-request-request", "dd-policy-name", "dd-policy-version",
"dd-policy-digest", "dd-pricebook-version", "dd-pricebook-digest", "dd-pricebook-currency", "dd-pricebook-issued",
"dd-candidates", "dd-removals",
"dd-ranks", "dd-order", "dd-decision", "dd-route", "dd-passed", "dd-parent-signer", "dd-parent-job", "dd-parent",
"dd-objects", "dd-seq", "dd-prev", "dd-decided", "dd-once",
"ppm-form", "ppm-class", "dd-offer", "dd-offer-entry", "dd-offer-unit", "dd-offer-quantity", "dd-offer-cost",
"dd-offer-energy", "dd-offer-energy_class", "dd-offer-energy_ppm", "dd-offer-grade",
"dr-version", "dr-a-route", "dr-verifies", "dr-kind", "dr-operation", "dr-status", "dr-route", "dr-decision", "dr-output",
"dr-grade", "dr-figure", "dr-uncertainty", "dr-unflagged", "dr-misflagged"]
def main(corpus_dir):
doc = json.load(open(os.path.join(corpus_dir, "vectors.json"), encoding="utf-8"))
fails = check(doc)
files = sorted(os.listdir(corpus_dir))
if files != ["README.md", "vectors.json"]:
fails.append("the corpus holds other files: %s" % files)
seen = {v["outcome"].get("reason") for v in doc["vectors"]}
added, reused = reasons_listed()
missing = [r for r in added + reused if r not in seen]
if missing:
fails.append("no vector is refused %s" % missing)
print("%s every reason of §8 and every code it reuses refuses a vector (%d added, %d reused)"
% ("PASS" if not missing else "FAIL", len(added), len(reused)))
unlisted = sorted(r for r in seen if r is not None and r not in added + reused)
if unlisted:
fails.append("§8 does not name %s, which the corpus raises" % unlisted)
print("%s §8 names every reason the corpus raises" % ("PASS" if not unlisted else "FAIL"))
copies = mutations(doc)
weak = [name for name, m in copies if not check(m, quiet=True)]
print("%s every mutated copy of the corpus fails (%d copies)" % ("PASS" if not weak else "FAIL", len(copies)))
fails += ["the mutated copy %r verifies" % w for w in weak]
undetected = []
for c in CHECKS:
SKIP.add(c)
try:
if not check(doc, quiet=True):
undetected.append(c)
finally:
SKIP.discard(c)
print("%s the corpus catches the verifier with each of its checks dropped (%d checks)" % ("PASS" if not undetected else "FAIL", len(CHECKS)))
fails += ["dropping the check %r goes unnoticed" % c for c in undetected]
for f in fails:
print("FAIL", f)
return 1 if fails else 0
def check_document(path):
"""One verification document: its outcome, printed. Status 0 only when it is accepted."""
got = verify(json.load(open(path, encoding="utf-8")))
print(json.dumps(got, indent=1))
return 0 if got["outcome"] == "accept" else 1
if __name__ == "__main__":
if len(sys.argv) == 3 and sys.argv[1] == "--document":
sys.exit(check_document(sys.argv[2]))
if len(sys.argv) != 2:
print("usage: route_record_verify.py <live-conformance/routes> | --document FILE", file=sys.stderr)
sys.exit(2)
sys.exit(main(sys.argv[1]))