open-standards/wai/quantum-conformance/tools/receipt_differential.py
Transaction Science 2778ddd525 wai-quantum: the planner under a contract, its reasoning sealed, a provider's answer shadowed
Re-landed from PR #668 (branch wai/quantum-planner-contract, merged with main
495651ddd at 37350335b) as one commit on main, under the land-it model. Five
review rounds; round 5's findings (two high: workers that died as PID 1) were
fixed on the branch, and its CI reds were traced and fixed before landing.

A job may carry a contract (deadline, energy ceiling, cost, accuracy); the
planner chooses a route that meets it or refuses with every route's reason,
and its reasoning is sealed in the receipt. Under #676's reading child the
contract job is planned in the child from the per-method timings the parent
sends, bounded as a job naming its chosen method plus its reasoning, kept
across a restart. Both verifiers (Rust and receipt_verify.py) check the
planner statement beside wasmQ's; the receipts corpus is regenerated with
both kinds (268 crafted receipts, 14 plants caught). The views page carries
its engine raw-deflated and stays under its 1 MiB limit (669,484 bytes).

Passed before the push (Framework, linux; M3, macOS): every repository
check, receipts conformance, breaking, corpora, catalogue, the qpu clients,
energy (wai-rs, wai-quantum full, serve), the CLI tests and clippy, writer,
smoke, cross-simulator corpus, job receipts, wasmQ tests and vectors, the
engine with and without receipts under -D warnings, the docs pages, and the
service suite with clippy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:42:46 -05:00

1001 lines
63 KiB
Python

#!/usr/bin/env python3
"""receipt_differential.py: two verifiers of job receipts, held to each other and
to the corpus (wai/quantum-conformance/receipts/README.md).
python3 -I receipt_differential.py --reference PATH/wai-quantum [--extra DIR ...]
python3 -I receipt_differential.py --reference PATH/wai-quantum --plant NAME
python3 -I receipt_differential.py --second-only [--plant NAME]
The reference is `wai-quantum verify --json` (and `--lines`); the second verifier
is receipt_verify.py beside this file, written from
wai/extensions/quantum-providers.md section 8 alone. For every receipt of the
corpus, both must report exactly the reasons the manifest gives. Then every
valid receipt of the corpus is changed one member at a time, each member of
its request, its result, its receipt and its unsealed envelope, by removing it
and by replacing its value, and both must report the same reasons for every
change. Two oracles hold beside agreement: a change inside the request or the
result is never valid, and a change to the envelope, or a member added to the
receipt, never changes the verdict.
`--extra DIR` adds receipts a person holds and does not publish (one with an
account number in it): both verifiers must find each valid.
`--second-only` holds the second verifier to the manifest alone, with no build
of the reference, and changes nothing.
`--plant NAME` runs the second verifier with one of its planted defects
(`receipt_verify.py --plant`), and succeeds only when the differential catches
it: the proof that the check can fail.
`--second FILE` holds another second verifier (a mutant of receipt_verify.py) to the
same stages; `--wasmq-only` keeps to what holds a wasmQ job (its receipts, their
one-member changes, the rules stage and the labelled cases); `--first` stops at the
first stage that disagrees; and `RD_CACHE=DIR` keeps each verifier's answers, so that a
run over many mutants of one verifier asks the other once (`wasmq_mutants.py`).
Exit status 0 only when everything agrees (or, with --plant, when the plant is
caught). Standard library only, Python 3.9+.
"""
import argparse
import base64
import copy
import hashlib
import importlib.util
import json
import os
import re
import subprocess
import sys
import tempfile
from collections import Counter
HERE = os.path.dirname(os.path.abspath(__file__))
CORPUS = os.path.join(HERE, "..", "receipts")
SECOND = os.path.join(HERE, "receipt_verify.py")
ORIGINAL_SECOND = SECOND
def compact(v):
return json.dumps(v, ensure_ascii=False, separators=(",", ":"))
def spelt(v, raw):
"""A line of v's text, each `raw` literal as it is written."""
if isinstance(v, raw):
return v.literal
if isinstance(v, dict):
return "{" + ",".join(json.dumps(k, ensure_ascii=False) + ":" + spelt(x, raw) for k, x in v.items()) + "}"
if isinstance(v, list):
return "[" + ",".join(spelt(x, raw) for x in v) + "]"
return compact(v)
def verdicts(lines_text, json_lines):
out = []
for line in lines_text.splitlines():
line = line.strip()
if line:
out.append(json.loads(line))
if len(out) != json_lines:
raise SystemExit("a verifier gave %d verdicts for %d receipts" % (len(out), json_lines))
return out
def _who(cmd):
"""What decides a verifier's answers, for the cache: the reference binary (its path, size
and time) and the mutant it is asked to be, or the second verifier's text and its plant."""
h = hashlib.sha256()
script = any(part.endswith(".py") for part in cmd)
for part in cmd:
h.update(part.encode() + b"\0")
if part.endswith(".py") and os.path.isfile(part):
with open(part, "rb") as f:
h.update(f.read())
elif os.path.isfile(part) and os.access(part, os.X_OK):
st = os.stat(part)
h.update(("%d %d" % (st.st_size, st.st_mtime_ns)).encode())
if not script:
h.update(os.environ.get("WQ_MUTANT", "").encode())
return h.hexdigest()
def cached(cmd, inputs, compute):
"""compute(), or what it gave before for the same verifier and the same inputs when
RD_CACHE names a directory: a run of many mutants of one verifier asks the other the
same questions each time."""
d = os.environ.get("RD_CACHE")
if not d:
return compute()
h = hashlib.sha256(_who(cmd).encode())
for b in inputs:
h.update(hashlib.sha256(b).digest())
path = os.path.join(d, h.hexdigest() + ".json")
try:
with open(path, encoding="utf-8") as f:
return json.load(f)
except (OSError, ValueError):
pass
out = compute()
os.makedirs(d, exist_ok=True)
with open(path + ".tmp%d" % os.getpid(), "w", encoding="utf-8") as f:
json.dump(out, f)
os.replace(path + ".tmp%d" % os.getpid(), path)
return out
def _contents(files):
out = []
for f in files:
with open(f, "rb") as h:
out.append(h.read())
return out
def run_reference_files(ref, files):
def compute():
out = []
for f in files:
p = subprocess.run([ref, "verify", "--json", f], capture_output=True, text=True)
if p.returncode not in (0, 1) or not p.stdout.strip():
raise SystemExit("the reference failed on %s (status %d): %s" % (f, p.returncode, p.stderr.strip()[:400]))
out.append(json.loads(p.stdout.strip().splitlines()[-1]))
return out
return cached([ref, "verify", "--json"], _contents(files), compute)
def run_second_files(files, plant):
cmd = [sys.executable, "-I", SECOND] + (["--plant", plant] if plant else [])
def compute():
p = subprocess.run(cmd + files, capture_output=True, text=True)
if p.returncode not in (0, 1):
raise SystemExit("the second verifier failed (status %d): %s" % (p.returncode, p.stderr.strip()[:400]))
return verdicts(p.stdout, len(files))
return cached(cmd, _contents(files), compute)
def run_lines(cmd, path, n):
def compute():
p = subprocess.run(cmd + [path], capture_output=True, text=True)
if p.returncode not in (0, 1):
raise SystemExit("%s failed (status %d): %s" % (cmd[0], p.returncode, p.stderr.strip()[:400]))
return verdicts(p.stdout, n)
return cached(cmd, _contents([path]), compute)
def rules_of(v):
"""The numbers of the rules of a wasmQ job's form a verdict states as broken (each
problem names its rule first), or None when it states none or only the first of many."""
w = v.get("wasmq")
if not isinstance(w, dict) or not isinstance(w.get("problems"), list):
return None
out = set()
for p in w["problems"]:
if not isinstance(p, str) or p.startswith("and "):
return None
m = re.match(r"rule (\d+): ", p)
if not m:
return None
out.add(int(m.group(1)))
return out
def same_rules(what, x, y, problems):
"""Both verifiers state the same rules broken, where both state them in full."""
rx, ry = rules_of(x), rules_of(y)
if rx is not None and ry is not None and rx != ry:
problems.append("%s: the reference states rules %s broken; the second verifier %s" % (what, sorted(rx), sorted(ry)))
def _same_number(v):
"""A stated value with each number past 2^53 read as the double both verifiers
hold it as, so that one writing it whole and the other as a double agree."""
if isinstance(v, bool) or v is None:
return v
if isinstance(v, int):
return float(v) if abs(v) > 2 ** 53 else v
if isinstance(v, dict):
return dict((k, _same_number(x)) for k, x in v.items())
if isinstance(v, list):
return [_same_number(x) for x in v]
return v
def said(v):
"""What a verdict says that both verifiers must agree on: its reasons, its key
and form, what it reads of a provider's result, and of a job under a contract
the choice, the class wanted and the class given, what was kept, the stable
choice, and the shadow (whether it ran and its total variation)."""
p = v.get("provider") or {}
c = v.get("planner") or {}
return (tuple(sorted(v.get("reasons", []))), v.get("key"), v.get("form"), p.get("form"), p.get("read_as"), p.get("counts_source"),
c.get("choice"), c.get("wanted"), c.get("class"), c.get("stable_choice"), _same_number(c.get("kept")), c.get("shadow_ran"),
_same_number(c.get("shadow_total_variation")))
# ----------------------------------------------------------------- one change at a time
def mutations(body):
"""(what, changed body, expected): each member removed, and each value
replaced; expected is `invalid` for a change inside the request, the result
or the receipt, and `unchanged` for one to the envelope or a member added
to the receipt, which is ignored."""
out = []
def other(v):
if isinstance(v, bool):
return not v
if isinstance(v, int):
return v + 1
if isinstance(v, float):
return v * 2 + 1
if isinstance(v, str):
return v + "x"
if v is None:
return 0
if isinstance(v, list):
return v + [copy.deepcopy(v[-1]) if v else 0]
added = dict(v)
added["z_added"] = 1
return added
def walk(v, path):
if isinstance(v, dict):
for k in list(v):
yield path + (k,), v[k], True
for x in walk(v[k], path + (k,)):
yield x
elif isinstance(v, list):
for i in sorted({0, len(v) - 1} if v else set()):
yield path + (i,), v[i], True
for x in walk(v[i], path + (i,)):
yield x
def set_at(b, path, value=None, remove=False):
o = b
for k in path[:-1]:
o = o[k]
if remove:
del o[path[-1]]
else:
o[path[-1]] = value
for path, v, removable in walk(body, ()):
envelope = path[0] not in ("request", "result")
name = "/".join(map(str, path))
b = copy.deepcopy(body)
set_at(b, path, other(v))
added_to_receipt = path == ("result", "receipt") and isinstance(v, dict)
# A set's item (section 8.6): its index, request, result, error and path are
# read; anything else it holds is the host's, unsealed, as an item added to
# is, and a member added to its own receipt is ignored as one added to a job's.
in_item = len(path) >= 3 and path[:2] == ("result", "items") and body["result"].get("format") == "wai.quantum.set/1"
item_extra = in_item and (len(path) == 3 and isinstance(v, dict) or len(path) >= 4 and path[3] not in ("index", "request", "result", "error", "path"))
item_receipt = in_item and len(path) >= 5 and path[3:5] == ("result", "receipt") and (len(path) == 5 and isinstance(v, dict) or len(path) >= 6 and path[5] not in ("alg", "canon", "key", "digest", "signature"))
# A member of the receipt beyond its five is ignored (section 8.2).
ignored = len(path) >= 3 and path[:2] == ("result", "receipt") and path[2] not in ("alg", "canon", "key", "digest", "signature")
out.append((name + " replaced", b, "unchanged" if envelope or added_to_receipt or ignored or item_extra or item_receipt else "invalid"))
if removable:
b = copy.deepcopy(body)
set_at(b, path, remove=True)
out.append((name + " removed", b, "unchanged" if envelope or ignored or (item_extra and len(path) >= 4) or (item_receipt and len(path) >= 6) else "invalid"))
return out
# Whole numbers past what a double holds, and others a verifier might cap or round.
EDGES = (2 ** 53, 2 ** 53 + 1, 1e16, 1e30, -1, -0.0, 0.5)
SHOTS = (2 ** 53, 1e16, "300", 300.0, 0, None)
def generator():
"""generate.py, for its seal: it holds the published demo seed."""
spec = importlib.util.spec_from_file_location("receipts_generate", os.path.join(CORPUS, "generate.py"))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def edges(body, seal):
"""(what, changed body, "agree"): a provider's result, each number set to each
of EDGES, and its shots in both its request and its result set to each of SHOTS.
There is no oracle here but the other verifier: the point is a limit one has and
the other does not. Those the demo key sealed are also sealed again, so that each
is judged by what it says and not only by its digest."""
out = []
if not (isinstance(body.get("result"), dict) and body["result"].get("method") == "provider"):
return out
def numbers(v, path):
if isinstance(v, dict):
for k in v:
if not (path == ("result",) and k == "receipt"):
yield from numbers(v[k], path + (k,))
elif isinstance(v, list):
for i in sorted({0, len(v) - 1} if v else set()):
yield from numbers(v[i], path + (i,))
elif isinstance(v, (int, float)) and not isinstance(v, bool):
yield path
demo = seal is not None and isinstance(body["result"].get("receipt"), dict) and body["result"]["receipt"].get("key") == seal[1]
def both(name, b):
out.append((name, b, "agree"))
if demo:
out.append((name + ", sealed again", seal[0](b), "agree"))
for path in numbers({"request": body.get("request"), "result": body["result"]}, ()):
for value in EDGES:
b = copy.deepcopy(body)
o = b
for k in path[:-1]:
o = o[k]
o[path[-1]] = value
both("/".join(map(str, path)) + " set to %r" % (value,), b)
for value in SHOTS:
b = copy.deepcopy(body)
b["request"]["shots"] = value
b["result"]["shots"] = value
both("the shots of both set to %r" % (value,), b)
return out
# ------------------------------------------------- the rules of a wasmQ job's form
def rule_cases(corpus, seal):
"""(what, body, expected reasons): for each rule of quantum-wasmq section 6.5, the
genuine wasmQ jobs of the corpus changed at the rule's bound, on both sides of it where
it has two, and sealed again with the demo key, so that the rule alone decides. Each
expected verdict is written here by hand from the text, as the manifest's are: none is
copied from a verifier. A verifier with a rule dropped, or a bound moved, gives some
case here another verdict."""
def load(name):
with open(os.path.join(corpus, "jobs", name), encoding="utf-8") as f:
return json.load(f)
run, fault, init, job, frun, prior = (load("cli-wasmq-%s.json" % n) for n in ("run", "fault", "fault-init", "state-job", "fault-run", "fault-prior"))
out = []
g = generator()
def case(rule, what, base, edit, valid, respell=None):
b = copy.deepcopy(base)
edit(b)
# With no call returned, the transcript the writer gives the result as changed
# (rule 35), unless the change was to the transcript.
sealed = seal(g.retranscribed(base, b))
if respell:
# After the seal: another spelling of a number, not another value, so it holds.
respell(sealed)
sealed = spelt(sealed, g.Raw)
out.append(("rule %s, %s" % (rule, what), sealed, [] if valid else ["wasmq_form"]))
def contract(b):
return b["request"]["wasmq"]
def budget(name, value):
def f(b):
contract(b)["run"]["fuel"][name] = value
b["result"]["fuel"]["budget"][name] = value
return f
def stretches(b):
return sum(2 + len(m["priors"]) for m in contract(b)["modules"])
def second_module(bytes0, bytes1, named=True):
def f(b):
c = contract(b)
c["modules"][0]["bytes"] = bytes0
c["modules"].append({"name": "second", "blake3": "cd" * 32, "bytes": bytes1, "priors": []})
b["result"]["modules"].append({"name": "second", "blake3": "cd" * 32})
if named:
c["callouts"].append({"extern": "g", "module": 1, "export": "g"})
return f
def each(*edits):
def f(b):
for e in edits:
e(b)
return f
def at(path, value):
def f(b):
o = b
for k in path[:-1]:
o = o[k]
o[path[-1]] = value
return f
def calls(b):
(name, e), = b["result"]["fuel"]["callouts"].items()
return e
def passed(b):
# What passing every prior to one shot's instances takes at the least (rule 21):
# cabi_realloc, 2 at least, its bytes, and wasmq-prior, 1 at least.
return sum(p["bytes"] + 3 for m in contract(b)["modules"] for p in m["priors"])
def shots(n):
def f(b):
width = len(next(iter(b["result"]["counts"])))
contract(b)["run"]["shots"] = b["result"]["shots"] = b["result"]["shots_run"] = n
b["result"]["counts"] = {"1" * width: n}
fuel = b["result"]["fuel"]
more = max(0, n * passed(b) - fuel["instances"])
fuel.update(instances=fuel["instances"] + more, used=fuel["used"] + more)
return f
def later_shot(shot, state=None, instances=1, fuel=1, calls=None):
# The init fault moved to `shot`, after that many shots of one call each.
def f(b):
n = shot if calls is None else calls
if state:
contract(b)["run"]["state"] = state
b["result"].update(shots_run=shot, counts={"0": shot} if shot else {}, callouts=n)
b["result"]["fuel"].update(instances=instances, callouts={"f": {"calls": n, "fuel": n, "max": 1}} if n else {}, used=instances + n)
b["result"]["fault"].update(shot=shot, fuel=fuel)
return f
def fault_module(name):
def f(b):
second_module(57, 57)(b)
b["result"]["fault"]["module"] = name
return f
def counts(width, n):
return lambda b: b["result"].update(counts={"0" * width: n})
def work_left(b):
# The most per_job the rest of rule 30's work leaves room for.
c, r = contract(b), b["result"]
n, s = r["qubits"], c["run"]["shots"]
inst = 1 if c["run"]["state"] == "job" else s
rest = s * 2 * n * max(1, -(-n // 32)) + inst * sum(4096 * (1 + 2 * len(m["priors"])) + m["bytes"] for m in c["modules"]) + 131072 * sum(m["bytes"] for m in c["modules"]) + 4097 * r["callouts"]
return (2 ** 42 - rest) // 8
def frames(b):
c = contract(b)
return c["program"]["bytes"] + sum(m["bytes"] + sum(p["bytes"] for p in m["priors"]) for m in c["modules"])
def most_prior(b):
# The most bytes the first prior may have: 2^27 less the envelope's other bytes, its
# magic and lengths, its manifest, its contract and its other frames (rule 31).
c, r = contract(b), b["result"]
c["modules"][0]["priors"][0]["bytes"] = 10 ** 8
manifest = 192 + sum(len(str(x)) for x in (r["qubits"], c["run"]["shots"], c["run"]["seed"]))
rest = 20 + manifest + len(g.text(c).encode("utf-8")) + frames(b) - 10 ** 8
return 2 ** 27 - rest
def calls_of(n, max_fuel=1):
# n calls of the run's one callout, each of 1 but the most.
def f(b):
b["result"]["callouts"] = n
calls(b).update(calls=n, fuel=n - 1 + max_fuel, max=max_fuel)
b["result"]["fuel"]["used"] = b["result"]["fuel"]["instances"] + calls(b)["fuel"]
return f
def own_prior(nbytes):
# The first module given a prior of nbytes.
return lambda b: contract(b)["modules"][0].update(priors=[{"name": "p", "blake3": "ef" * 32, "bytes": nbytes}])
def fault_of(reason, fuel):
# The call's fault of `fault`, stopped for reason after fuel.
def f(b):
b["result"]["fault"].update(reason=reason, fuel=fuel)
b["result"]["fault"].pop("budget", None)
calls(b).update(calls=1, fuel=fuel, max=fuel)
b["result"]["fuel"]["used"] = fuel
return f
def own_fuel(delta):
def f(b):
e = calls(b)
e["fuel"] = e["max"] + delta
b["result"]["fuel"]["used"] = b["result"]["fuel"]["instances"] + e["fuel"]
return f
R, F = ("result",), ("result", "fuel")
# 1: no number past 2^53 - 1 (it breaks another rule too: each place a number may be holds it)
case(1, "an input of 2^53", run, lambda b: contract(b)["program"].update(inputs={"n": 2 ** 53}), False)
# 2: the request
case(2, "a member the request does not define", run, at(("request", "x"), 1), False)
case(2, "envelope_blake3 not a digest", run, lambda b: b["request"].update(envelope_blake3=b["request"]["envelope_blake3"].upper()), False)
# 3, 4: the contract's members and profile
case(3, "a member the contract does not define", run, lambda b: contract(b).update(x=1), False)
case(3, "a member the run does not define", run, lambda b: contract(b)["run"].update(x=1), False)
case(4, "another profile", run, lambda b: contract(b).update(profile="wai.quantum.wasmq/2"), False)
# 5: the program
case(5, "a program of 2^20 bytes", run, lambda b: contract(b)["program"].update(bytes=2 ** 20), True)
case(5, "a program of 2^20 + 1 bytes", run, lambda b: contract(b)["program"].update(bytes=2 ** 20 + 1), False)
case(5, "a program of 0 bytes", run, lambda b: contract(b)["program"].update(bytes=0), False)
case(5, "a program of 1 byte", run, lambda b: contract(b)["program"].update(bytes=1), True)
case(5, "an input of 2^53 - 1", run, lambda b: contract(b)["program"].update(inputs={"n": 2 ** 53 - 1}), True)
case(5, "an input of -(2^53 - 1)", run, lambda b: contract(b)["program"].update(inputs={"n": -(2 ** 53 - 1)}), True)
case(5, "an input of 0.5", run, lambda b: contract(b)["program"].update(inputs={"n": 0.5}), False)
# 6: the modules and their priors
case(6, "a module of 1 byte", run, lambda b: contract(b)["modules"][0].update(bytes=1), True)
case(6, "a module of 0 bytes", run, lambda b: contract(b)["modules"][0].update(bytes=0), False)
case(6, "a prior of 0 bytes", run, lambda b: contract(b)["modules"][0]["priors"][0].update(bytes=0), True)
case(6, "a prior of 0.5 bytes", prior, lambda b: contract(b)["modules"][0]["priors"][0].update(bytes=0.5), False)
case(6, "a module of 2^19 bytes", run, lambda b: contract(b)["modules"][0].update(bytes=2 ** 19), True)
case(6, "a module of 2^19 + 1 bytes", run, lambda b: contract(b)["modules"][0].update(bytes=2 ** 19 + 1), False)
case(6, "a module's priors null", run, lambda b: contract(b)["modules"][0].update(priors=None), False)
case(6, "a prior's name null", run, lambda b: contract(b)["modules"][0]["priors"][0].update(name=None), False)
case(6, "a module's name null, alike in the result", run, lambda b: (contract(b)["modules"][0].update(name=None), b["result"]["modules"][0].update(name=None)), False)
case(6, "modules of 2^19 bytes together", run, second_module(2 ** 18, 2 ** 18), True)
case(6, "modules of 2^19 + 1 bytes together", run, second_module(2 ** 18, 2 ** 18 + 1), False)
# 7: the callouts
case(7, "a module no callout names", run, second_module(39442, 1, named=False), False)
case(7, "two callouts of one extern", run, lambda b: contract(b)["callouts"].append({"extern": "decode64", "module": 0, "export": "x"}), False)
case(7, "a callout of a module the contract does not hold", run, lambda b: contract(b)["callouts"][0].update(module=1), False)
case(7, "a callout of the module past the last, beside one of each module", run, lambda b: contract(b)["callouts"].append({"extern": "g", "module": 1, "export": "g"}), False)
case(7, "a callout of the last module, beside one of each module", run, second_module(39442, 57), True)
# No module and no callout: rule 7's first clause alone holds it, its others vacuous.
def nothing(b):
contract(b).update(modules=[], callouts=[])
b["result"].update(modules=[], callouts=0)
b["result"]["fuel"].update(callouts={}, instances=0, used=0)
case(7, "no module and no callout", run, nothing, False)
case(7, "no module, null for the modules, and no callout", run, each(nothing, lambda b: contract(b).update(modules=None)), False)
# 8: the run
case(8, "2^20 shots", run, shots(2 ** 20), True)
case(8, "2^20 + 1 shots", run, shots(2 ** 20 + 1), False)
for name in ("per_call", "per_instance", "per_job"):
# per_job at 2^39 is past rule 30's work beside any module: it is held there.
case(30 if name == "per_job" else 8, "the budget %s at 2^39" % name, run, budget(name, 2 ** 39), name != "per_job")
case(30 if name == "per_job" else 8, "the budget %s at 2^39 + 1" % name, run, budget(name, 2 ** 39 + 1), False)
case(8, "a seed of 0, alike in the result", run, lambda b: (contract(b)["run"].update(seed=0), b["result"].update(seed=0)), True)
case(8, "a seed below 0, alike in the result", run, lambda b: (contract(b)["run"].update(seed=-1), b["result"].update(seed=-1)), False)
case(8, "a seed of 2^53 - 1, alike in the result", run, lambda b: (contract(b)["run"].update(seed=2 ** 53 - 1), b["result"].update(seed=2 ** 53 - 1)), True)
case(8, "1 shot", run, shots(1), True)
case(8, "0 shots", job, lambda b: (contract(b)["run"].update(shots=0), b["result"].update(shots=0, shots_run=0, counts={}, callouts=0), b["result"]["fuel"].update(callouts={}, used=b["result"]["fuel"]["instances"])), False)
case(8, "a budget of 0", fault, budget("per_instance", 0), True)
case(8, "a call's budget of 0, its one call stopped at once", fault, each(budget("per_call", 0), lambda b: (calls(b).update(fuel=0, max=0), b["result"]["fuel"].update(used=0), b["result"]["fault"].update(fuel=0))), True)
case(8, "another state", run, at(("request", "wasmq", "run", "state"), "call"), False)
# 9, 10: the result's members, and the form of its seal
case(9, "a member the result does not define", run, at(R + ("x",), 1), False)
first = copy.deepcopy(run)
out.append(("rule 10, sealed in the first form", seal(first, form="first"), ["wasmq_form"]))
# 11: what the result repeats
case(11, "another seed in the result", run, lambda b: b["result"].update(seed=b["result"]["seed"] + 1), False)
case(11, "another shots in the result", run, lambda b: b["result"].update(shots=b["result"]["shots"] + 1), False)
case(11, "another accuracy", run, at(R + ("accuracy",), {"class": "exact", "x": 1}), False)
case(11, "another energy", run, at(R + ("energy",), {"unmetered": "a meter"}), False)
# 12: qubits
case(12, "2^15 qubits", run, at(R + ("qubits",), 2 ** 15), True)
case(12, "1 qubit", run, at(R + ("qubits",), 1), True)
case(12, "0 qubits", run, at(R + ("qubits",), 0), False)
case(12, "qubits null", run, at(R + ("qubits",), None), False)
case(12, "2^15 + 1 qubits", run, at(R + ("qubits",), 2 ** 15 + 1), False)
# 13: the fuel's budget and what it used
case(13, "used equal to per_job", run, lambda b: budget("per_job", b["result"]["fuel"]["used"])(b), True)
case(13, "used past per_job", run, lambda b: budget("per_job", b["result"]["fuel"]["used"] - 1)(b), False)
case(13, "a budget the contract does not give", run, lambda b: b["result"]["fuel"]["budget"].update(per_call=b["result"]["fuel"]["budget"]["per_call"] + 1), False)
# 14: each callout's fuel
case(14, "a call's largest equal to per_call", run, lambda b: budget("per_call", calls(b)["max"])(b), True)
case(14, "a call's largest past per_call", run, lambda b: budget("per_call", calls(b)["max"] - 1)(b), False)
case(14, "a call's largest equal to its fuel", fault, own_fuel(0), True)
case(14, "a call's largest past its fuel", run, own_fuel(-1), False)
case(14, "a callout with no call", run, lambda b: (calls(b).update(calls=0, fuel=0, max=0), b["result"].update(callouts=0), b["result"]["fuel"].update(used=b["result"]["fuel"]["instances"])), False)
case(20, "fuel past its calls of their largest", run, lambda b: (calls(b).update(fuel=calls(b)["calls"] * calls(b)["max"] + 1), b["result"]["fuel"].update(used=b["result"]["fuel"]["instances"] + calls(b)["fuel"])), False)
# 15: what adds up
case(15, "a call more than the fuel counts", run, lambda b: b["result"].update(callouts=b["result"]["callouts"] + 1), False)
# 16: the result's modules
case(16, "a module of the result named otherwise", run, lambda b: b["result"]["modules"][0].update(name="other"), False)
# 17: what the instances could be given
case(17, "a fault's instance counted: one, given 1 each stretch", init, budget("per_instance", 1), True)
case(17, "one instance for the run, at its bound", job, lambda b: budget("per_instance", -(-b["result"]["fuel"]["instances"] // stretches(b)))(b), True)
case(17, "one instance for the run, past its bound", job, lambda b: budget("per_instance", (b["result"]["fuel"]["instances"] - 1) // stretches(b))(b), False)
# 18: how it ended
case(18, "a fault in init within its phase's budget", init, each(budget("per_instance", 2), at(F + ("instances",), 2), at(F + ("used",), 2), at(R + ("fault", "fuel"), 2)), True)
case(18, "a fault in init past its phase's budget", init, each(budget("per_instance", 1), at(F + ("instances",), 2), at(F + ("used",), 2), at(R + ("fault", "fuel"), 2)), False)
case(18, "a fault after every shot", fault, lambda b: (b["result"].update(shots_run=32, counts={"0": 32}), b["result"]["fault"].update(shot=32)), False)
case(18, "a fault in another shot than the next", fault, at(R + ("fault", "shot"), 1), False)
case(28, "a call's fuel fault of the run's budget, with the run's budget not spent", fault, at(R + ("fault", "budget"), "run"), False)
case(18, "a call's fuel fault of the instance's budget", fault, at(R + ("fault", "budget"), "instance"), False)
case(18, "a fault in init that names a callout", init, at(R + ("fault", "callout"), "f"), False)
case(18, "a call's fault the fuel counts no call of", fault, lambda b: (b["result"].update(callouts=0), b["result"]["fuel"].update(callouts={}, instances=400)), False)
case(18, "a call that stopped at its callout's largest", fault, lambda b: (b["result"].update(callouts=2), b["result"]["fuel"]["callouts"]["f"].update(calls=2, fuel=401, max=400), b["result"]["fuel"].update(used=401)), True)
case(18, "a call that stopped past its callout's largest", fault, lambda b: (b["result"].update(callouts=2), b["result"]["fuel"]["callouts"]["f"].update(calls=2, fuel=401, max=399), b["result"]["fuel"].update(used=401)), False)
case(18, "a lifecycle fault whose callout is null", init, at(R + ("fault", "callout"), None), False)
case(18, "a trap whose budget is null", init, at(R + ("fault", "budget"), None), False)
case(18, "a trap in seed", init, at(R + ("fault", "phase"), "seed"), True)
case(18, "a fault in another phase", init, at(R + ("fault", "phase"), "start"), False)
case(18, "a trap of another reason the profile names", init, at(R + ("fault", "reason"), "invalid_conversion_to_integer"), True)
case(18, "a fault in a shot after shots_run", frun, at(R + ("fault", "shot"), 1), False)
case(18, "a fault in a module named null", init, at(R + ("fault", "module"), None), False)
case(18, "a trap that names a budget", init, at(R + ("fault", "budget"), "instance"), False)
case(18, "an ok result whose fault is null", run, at(R + ("fault",), None), False)
case(18, "a call's fault whose callout is null", fault, at(R + ("fault", "callout"), None), False)
case(18, "a fuel fault whose budget is null", fault, at(R + ("fault", "budget"), None), False)
# 19: the counts
case(19, "outcomes of two widths", run, lambda b: (b["result"]["counts"].update({next(iter(b["result"]["counts"])): b["result"]["counts"][next(iter(b["result"]["counts"]))] - 1}), b["result"]["counts"].update({"0": 1})), False)
case(19, "a count of 0", run, lambda b: b["result"]["counts"].update({"0" * len(next(iter(b["result"]["counts"]))): 0}), False)
case(19, "an outcome of no bits", run, counts(0, 32), True)
case(19, "an outcome holding a 2", run, lambda b: b["result"].update(counts={"012": 32}), False)
case(19, "counts null", fault, at(R + ("counts",), None), False)
# Round 4's labelled cases, each where a weakening of one rule moves a verdict.
case(2, "envelope_blake3 of 65 digits", run, lambda b: b["request"].update(envelope_blake3=b["request"]["envelope_blake3"] + "0"), False)
case(12, "transcript_blake3 of 63 digits", run, lambda b: b["result"].update(transcript_blake3=b["result"]["transcript_blake3"][:63]), False)
case(3, "run's fuel a member more, the budget alike", run, budget("z", 1), False)
case(3, "the program a member more", run, lambda b: contract(b)["program"].update(z=1), False)
case(4, "a profile one letter longer", run, lambda b: contract(b).update(profile="wai.quantum.wasmq/1x"), False)
case(5, "inputs null", run, lambda b: contract(b)["program"].update(inputs=None), False)
case(5, "the program's bytes 1104.5", run, lambda b: contract(b)["program"].update(bytes=1104.5), False)
case(5, "an input that is true", run, lambda b: contract(b)["program"].update(inputs={"n": True}), False)
case(6, "a module's bytes 39442.5", run, lambda b: contract(b)["modules"][0].update(bytes=39442.5), False)
case(7, "an export null", run, lambda b: contract(b)["callouts"][0].update(export=None), False)
case(7, "an extern null, with no call made", init, lambda b: contract(b)["callouts"][0].update(extern=None), False)
case(7, "a callout of module 0.5", run, lambda b: contract(b)["callouts"].append({"extern": "g", "module": 0.5, "export": "g"}), False)
def respell(path, text, value):
def f(s):
o = s
for k in path[:-1]:
o = o[k]
o[path[-1]] = g.Raw(text, value)
return f
case(11, "the result's seed written 1234.0", run, lambda b: None, True, respell(R + ("seed",), "1234.0", run["result"]["seed"]))
case(12, "qubits written 17.0", run, lambda b: None, True, respell(R + ("qubits",), "17.0", 17))
case(13, "a budget written 1e7", run, lambda b: None, True, respell(F + ("budget", "per_call"), "1e7", 10000000))
case(14, "the callouts' fuel null", init, at(F + ("callouts",), None), False)
case(14, "the callouts' fuel an array", init, at(F + ("callouts",), []), False)
case(15, "a call fewer than the fuel counts", run, lambda b: b["result"].update(callouts=b["result"]["callouts"] - 1), False)
case(16, "the result's modules in another order", run, lambda b: (second_module(39442, 1)(b), b["result"].update(modules=b["result"]["modules"][::-1])), False)
case(17, "the instances' fuel past 32 x S x per_instance", run, budget("per_instance", 2583), False)
case(17, "one instance for the run, a fault, past 1 x S x per_instance", init, each(at(("request", "wasmq", "run", "state"), "job"), budget("per_instance", 1), at(F + ("instances",), 3), at(F + ("used",), 3)), False)
case(25, "an init fault's fuel past what was used", init, at(R + ("fault", "fuel"), 2), False)
case(18, "a call's fault past its callout's max", frun, at(R + ("fault", "fuel"), 24), False)
case(18, "an ok result one shot past its shots", run, lambda b: (b["result"]["counts"].update({next(iter(b["result"]["counts"])): b["result"]["counts"][next(iter(b["result"]["counts"]))] + 1}), b["result"].update(shots_run=33)), False)
# 20: the fuel of the calls that returned, each 1 to the most one used
case(20, "calls that returned using 1 each", run, lambda b: (calls(b).update(fuel=32, max=1), b["result"]["fuel"].update(used=b["result"]["fuel"]["instances"] + 32)), True)
case(20, "calls that returned using nothing", run, lambda b: (calls(b).update(fuel=0, max=0), b["result"]["fuel"].update(used=b["result"]["fuel"]["instances"])), False)
case(20, "two calls that returned and one that stopped, in 6", frun, each(budget("per_job", 6), lambda b: (calls(b).update(fuel=6, max=4), b["result"]["fuel"].update(used=6))), True)
case(20, "two calls that returned and one that stopped, in 5", frun, each(budget("per_job", 5), lambda b: (calls(b).update(fuel=5, max=4), b["result"]["fuel"].update(used=5))), False)
case(20, "calls that returned at the most one used", frun, lambda b: calls(b).update(max=23), True)
case(20, "calls that returned past the most one used", frun, lambda b: calls(b).update(max=21), False)
case(20, "the one call, the one that stopped, its fuel more than the fault's", fault, lambda b: (b["result"]["fault"].update(reason="unreachable", fuel=10), b["result"]["fault"].pop("budget")), False)
case(20, "the one call, the one that stopped, its fuel the fault's", fault, lambda b: (b["result"]["fault"].update(reason="unreachable", fuel=10), b["result"]["fault"].pop("budget"), calls(b).update(fuel=10, max=10), b["result"]["fuel"].update(used=10)), True)
# 21: the priors passed to each shot's instances
case(21, "instances that passed each prior at the least", run, lambda b: b["result"]["fuel"].update(instances=32 * passed(b), used=32 * passed(b) + calls(b)["fuel"]), True)
case(21, "instances that passed the priors in less", run, lambda b: b["result"]["fuel"].update(instances=32 * passed(b) - 1, used=32 * passed(b) - 1 + calls(b)["fuel"]), False)
case(21, "one instance for the run, its prior in less", job, lambda b: b["result"]["fuel"].update(instances=passed(b) - 1, used=passed(b) - 1 + calls(b)["fuel"]), False)
case(21, "a prior's fault: no prior passed whole", prior, lambda b: None, True)
# 22: a call's fault in its callout's module
case(22, "a call's fault in its callout's module, beside another", fault, fault_module("m"), True)
case(22, "a call's fault in the other module", fault, fault_module("second"), False)
# 23, 24: a phase against the module's priors, a reason against its phase
case(23, "a prior's fault in a module with none", init, at(R + ("fault", "phase"), "prior"), False)
case(24, "a bad_result while a prior is passed", prior, at(R + ("fault", "reason"), "bad_result"), True)
case(24, "a bad_result in a call", fault, each(at(R + ("fault", "reason"), "bad_result"), lambda b: b["result"]["fault"].pop("budget")), True)
case(24, "a bad_result in init", init, at(R + ("fault", "reason"), "bad_result"), False)
case(24, "a bad_result in seed", init, each(at(R + ("fault", "reason"), "bad_result"), at(R + ("fault", "phase"), "seed")), False)
# 25, 26, 27: a lifecycle fault against the instances, the state and the calls
case(25, "an init fault in shot 1 within the instances' fuel", init, later_shot(1, instances=3, fuel=2), True)
case(25, "an init fault in shot 1, shot 0's init uncounted", init, later_shot(1, instances=2, fuel=2), False)
case(25, "an init fault in shot 1 past the instances' fuel", init, later_shot(1, instances=1, fuel=2), False)
case(26, "one instance for the run, failing in shot 0", init, at(("request", "wasmq", "run", "state"), "job"), True)
case(26, "one instance for the run, failing in shot 2", init, later_shot(2, state="job"), False)
case(26, "a fresh instance each shot, failing in shot 2", init, later_shot(2, instances=3), True)
case(27, "the first shot's instances failing after a call", init, later_shot(0, calls=1), False)
# 28: a fuel fault used its whole limit
case(28, "a call's fuel fault at its limit", fault, each(budget("per_call", 10), lambda b: (calls(b).update(fuel=10, max=10), b["result"]["fuel"].update(used=10), b["result"]["fault"].update(fuel=10))), True)
case(28, "a call's fuel fault under its limit", fault, lambda b: (calls(b).update(fuel=10, max=10), b["result"]["fuel"].update(used=10), b["result"]["fault"].update(fuel=10)), False)
case(28, "an instance's fuel fault at its limit", init, each(budget("per_instance", 1), at(R + ("fault", "reason"), "fuel_exhausted"), at(R + ("fault", "budget"), "instance")), True)
case(28, "an instance's fuel fault under its limit", init, each(at(R + ("fault", "reason"), "fuel_exhausted"), at(R + ("fault", "budget"), "instance")), False)
case(28, "the run's budget run out, not all used", frun, budget("per_job", 51), False)
case(28, "the run's budget the limit, under the call's own", frun, each(budget("per_call", 24), at(R + ("fault", "fuel"), 23), lambda b: calls(b).update(max=23)), True)
case(28, "the run's budget the limit, at the call's own", frun, each(budget("per_call", 23), at(R + ("fault", "fuel"), 23)), False)
case(28, "an instance's run budget, at the instance's own", init, each(budget("per_job", 1), budget("per_instance", 1), at(R + ("fault", "reason"), "fuel_exhausted"), at(R + ("fault", "budget"), "run")), False)
case(28, "an instance's run budget, under the instance's own", init, each(budget("per_job", 1), budget("per_instance", 2), at(R + ("fault", "reason"), "fuel_exhausted"), at(R + ("fault", "budget"), "run")), True)
# 29: the bits of a shot, and of the shots
case(29, "outcomes of 2^20 bits", run, counts(2 ** 20, 32), True)
case(29, "outcomes of 2^20 + 1 bits", run, counts(2 ** 20 + 1, 32), False)
case(29, "2^20 shots of 256 bits", frun, each(lambda b: (contract(b)["run"].update(shots=2 ** 20), b["result"].update(shots=2 ** 20)), counts(256, 2)), True)
case(29, "2^20 shots of 257 bits", frun, each(lambda b: (contract(b)["run"].update(shots=2 ** 20), b["result"].update(shots=2 ** 20)), counts(257, 2)), False)
# 30: the work a receipt shows
case(30, "per_job at the most the rest leaves", run, lambda b: budget("per_job", work_left(b))(b), True)
case(30, "per_job one past it", run, lambda b: budget("per_job", work_left(b) + 1)(b), False)
case(30, "2^15 qubits over 2^20 shots", frun, each(lambda b: (contract(b)["run"].update(shots=2 ** 20), b["result"].update(shots=2 ** 20, qubits=2 ** 15))), False)
case(30, "one instance for the run: its instance counted once", job, lambda b: budget("per_job", work_left(b))(b), True)
# 31: the envelope
case(31, "an envelope of 2^27 bytes", prior, lambda b: contract(b)["modules"][0]["priors"][0].update(bytes=most_prior(b)), True)
case(31, "an envelope of 2^27 + 1 bytes", prior, lambda b: contract(b)["modules"][0]["priors"][0].update(bytes=most_prior(b) + 1), False)
case(31, "frames one short of 2^27, the envelope past it", prior, lambda b: contract(b)["modules"][0]["priors"][0].update(bytes=2 ** 27 - 1 - (frames(b) - contract(b)["modules"][0]["priors"][0]["bytes"])), False)
# 20: the most one call used, a returned call's, and each other 1 at least
case(20, "32 calls, the most 21751, in 21751 + 31", run, calls_of(32, 21751), True)
case(20, "32 calls, the most 21751, in 21751 + 30", run, each(calls_of(32, 21751), lambda b: (calls(b).update(fuel=21751 + 30), b["result"]["fuel"].update(used=b["result"]["fuel"]["instances"] + 21751 + 30))), False)
case(20, "two returned, the most 23, beside a stopped call of 4, in 28", frun, each(budget("per_job", 28), lambda b: (calls(b).update(fuel=28, max=23), b["result"]["fuel"].update(used=28))), True)
case(20, "two returned, the most 23, beside a stopped call of 4, in 27", frun, each(budget("per_job", 27), lambda b: (calls(b).update(fuel=27, max=23), b["result"]["fuel"].update(used=27))), False)
case(20, "one returned in 5, then one stopped at 0, the run's budget spent", frun, each(budget("per_job", 5), lambda b: (b["result"].update(callouts=2), calls(b).update(calls=2, fuel=5, max=5), b["result"]["fuel"].update(used=5), b["result"]["fault"].update(fuel=0))), True)
case(20, "two returned in 11, the most 10, then one stopped at 0", frun, each(budget("per_job", 11), lambda b: (calls(b).update(fuel=11, max=10), b["result"]["fuel"].update(used=11), b["result"]["fault"].update(fuel=0))), True)
case(20, "two returned in 10, the most 10, then one stopped at 0", frun, each(budget("per_job", 10), lambda b: (calls(b).update(fuel=10, max=10), b["result"]["fuel"].update(used=10), b["result"]["fault"].update(fuel=0))), False)
# 21, 25: what a lifecycle fault shows of the set it stopped
case(25, "an init stopped after its 100-byte prior, in 104", init, each(own_prior(100), at(F + ("instances",), 104), at(F + ("used",), 104)), True)
case(25, "an init stopped after its 100-byte prior, in 103", init, each(own_prior(100), at(F + ("instances",), 103), at(F + ("used",), 103)), False)
case(21, "an init stopped after its 100-byte prior, in 102", init, each(own_prior(100), at(F + ("instances",), 102), at(F + ("used",), 102)), False)
case(25, "a seed stopped after its 100-byte prior's module, passed none", init, each(own_prior(100), at(R + ("fault", "phase"), "seed")), True)
case(25, "the second module's init stopped after the first's prior, in 104", init, each(second_module(50, 57), own_prior(100), at(R + ("fault", "module"), "second"), at(F + ("instances",), 104), at(F + ("used",), 104)), True)
case(25, "the second module's init stopped after the first's prior, in 103", init, each(second_module(50, 57), own_prior(100), at(R + ("fault", "module"), "second"), at(F + ("instances",), 103), at(F + ("used",), 103)), False)
case(25, "a seed stopped in shot 1, shot 0's seed shown", init, each(later_shot(1, instances=2, fuel=1), at(R + ("fault", "phase"), "seed")), True)
case(25, "a seed stopped in shot 1, shot 0's seed uncounted", init, each(later_shot(1, instances=1, fuel=1), at(R + ("fault", "phase"), "seed")), False)
# 29: the longest name, in characters
case(29, "two widths, the longer past 2^20", run, lambda b: b["result"].update(counts={next(iter(b["result"]["counts"])): 31, "0" * (2 ** 20 + 1): 1}), False)
case(19, "2^20 shots of a name of 256 characters in 257 bytes", frun, each(lambda b: (contract(b)["run"].update(shots=2 ** 20), b["result"].update(shots=2 ** 20)), lambda b: b["result"].update(counts={"\u00e9" + "0" * 255: 2})), False)
case(29, "2^20 shots of a name of 257 characters", frun, each(lambda b: (contract(b)["run"].update(shots=2 ** 20), b["result"].update(shots=2 ** 20)), lambda b: b["result"].update(counts={"\u00e9" + "0" * 256: 2})), False)
# 32: the calls a shot can make
case(32, "32 shots of 2^21 calls", run, calls_of(32 * 2 ** 21), True)
case(32, "32 shots of 2^21 calls and one more", run, calls_of(32 * 2 ** 21 + 1), False)
case(32, "a call's fault in the first shot, after 2^21 - 1 calls", fault, lambda b: (b["result"].update(callouts=2 ** 21), calls(b).update(calls=2 ** 21, fuel=2 ** 21 - 1 + 400), b["result"]["fuel"].update(used=2 ** 21 - 1 + 400)), True)
case(32, "a call's fault in the first shot, after 2^21 calls", fault, lambda b: (b["result"].update(callouts=2 ** 21 + 1), calls(b).update(calls=2 ** 21 + 1, fuel=2 ** 21 + 400), b["result"]["fuel"].update(used=2 ** 21 + 400)), False)
# 33: a prior passed whole within one lifecycle call's limit
case(33, "a prior of 87 bytes under 90 a lifecycle call", run, each(budget("per_instance", 90), at(F + ("instances",), 32 * 90), at(F + ("used",), 32 * 90 + 588348)), True)
case(33, "a prior of 87 bytes under 89 a lifecycle call", run, each(budget("per_instance", 89), at(F + ("instances",), 32 * 90), at(F + ("used",), 32 * 90 + 588348)), False)
case(33, "an init stopped after its 100-byte prior, under 103", init, each(own_prior(100), budget("per_instance", 103), at(F + ("instances",), 104), at(F + ("used",), 104)), True)
case(33, "an init stopped after its 100-byte prior, under 102", init, each(own_prior(100), budget("per_instance", 102), at(F + ("instances",), 104), at(F + ("used",), 104)), False)
# 34: what a fault's reason shows was charged
case(34, "a trap after nothing charged", init, each(at(R + ("fault", "fuel"), 0)), False)
case(34, "a result that is not one, after 2", fault, fault_of("bad_result", 2), True)
case(34, "a result that is not one, after 1", fault, fault_of("bad_result", 1), False)
case(34, "the stack past 2^23 after 788", fault, each(budget("per_call", 1000), fault_of("stack_exhausted", 788)), True)
case(34, "the stack past 2^23 after 787", fault, each(budget("per_call", 1000), fault_of("stack_exhausted", 787)), False)
# 35: the transcript of no returned call
case(35, "an ok run of no call, its transcript the prefix's", run, lambda b: (b["result"].update(callouts=0), b["result"]["fuel"].update(callouts={}, used=b["result"]["fuel"]["instances"])), True)
case(35, "an ok run of no call, its transcript the calls'", run, lambda b: (b["result"].update(callouts=0, transcript_blake3=b["result"]["transcript_blake3"][::-1]), b["result"]["fuel"].update(callouts={}, used=b["result"]["fuel"]["instances"])), False)
case(35, "a call's fault of no returned call, another transcript", fault, at(R + ("transcript_blake3",), "ab" * 32), False)
case(35, "a lifecycle fault, another transcript", init, at(R + ("transcript_blake3",), "ab" * 32), False)
# Places where a member's own rule and another's both read it: each verifier must state
# every rule it breaks, so that one reading it otherwise in one rule shows.
case(13, "per_job 2^53, alike in the result", run, budget("per_job", 2 ** 53), False)
case(6, "a module's blake3 in upper case, alike in the result", run, lambda b: (contract(b)["modules"][0].update(blake3=contract(b)["modules"][0]["blake3"].upper()), b["result"]["modules"][0].update(blake3=b["result"]["modules"][0]["blake3"].upper())), False)
def named_one(b):
contract(b)["modules"][0]["name"] = 1
b["result"]["modules"][0]["name"] = 1
if "fault" in b["result"]:
b["result"]["fault"]["module"] = 1
case(6, "a module named 1, the result's module 1.0", run, named_one, False, respell(R + ("modules", 0, "name"), "1.0", 1))
case(6, "a module named 1, an init's fault in 1.0", init, named_one, False, respell(R + ("fault", "module"), "1.0", 1))
case(6, "a module named 1, a prior's fault in 1.0", prior, named_one, False, respell(R + ("fault", "module"), "1.0", 1))
case(26, "one instance for the run, a fault in shot 0.5", init, each(at(("request", "wasmq", "run", "state"), "job"), at(R + ("fault", "shot"), 0.5)), False)
case(18, "a fault in shot 0.5, after a call", init, each(at(R + ("fault", "shot"), 0.5), at(R + ("callouts",), 1)), False)
# A lifecycle fault is one that has a phase: null among them, absent not.
case(26, "a fault whose phase is null, in shot 2 of one instance for the run", init, each(later_shot(2, state="job"), at(R + ("fault", "phase"), None)), False)
case(18, "a fault with no phase, in shot 2 of one instance for the run", init, each(later_shot(2, state="job"), lambda b: b["result"]["fault"].pop("phase")), False)
# Every member of each genuine job given a value of another kind (half a unit off, below
# 0, past 2^53 - 1, null, a digest in upper case), sealed again. No member of a wasmQ job
# holds such a value: each verifier must refuse each, and both must state the same rules
# broken (the differential holds them to that), so that a rule's check read loosely in
# one, or a null read as absent, shows wherever another rule's check also refuses it.
def leaves(v, path):
if isinstance(v, dict):
for k, x in v.items():
yield path + (k,), x
for y in leaves(x, path + (k,)):
yield y
elif isinstance(v, list):
for i, x in enumerate(v[:1]):
yield path + (i,), x
for y in leaves(x, path + (i,)):
yield y
for name, base in (("run", run), ("fault", fault), ("init", init), ("fault-run", frun), ("prior", prior), ("state-job", job)):
for part in ("request", "result"):
for path, v in leaves(base[part], (part,)):
# The receipt is the seal's; the format says what kind of job it is.
if "receipt" in path or path == ("result", "format"):
continue
where = "%s %s" % (name, "/".join(map(str, path)))
if isinstance(v, (int, float)) and not isinstance(v, bool):
case("kinds", "%s + 0.5" % where, base, at(path, v + 0.5), False)
case("kinds", "%s at -1" % where, base, at(path, -1), False)
case("kinds", "%s at 2^53" % where, base, at(path, 2 ** 53), False)
if isinstance(v, str) and re.fullmatch("[0-9a-f]{64}", v):
case("kinds", "%s in upper case" % where, base, at(path, v.upper()), False)
case("kinds", "%s null" % where, base, at(path, None), False)
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--reference", help="the wai-quantum binary")
ap.add_argument("--second-only", action="store_true", help="the second verifier against the manifest alone")
ap.add_argument("--corpus", default=CORPUS)
ap.add_argument("--extra", action="append", default=[], help="a directory of receipts both must find valid")
ap.add_argument("--plant", help="run the second verifier with this planted defect; succeed only if it is caught")
ap.add_argument("--second", help="another second verifier's file in place of receipt_verify.py (a mutant of it)")
ap.add_argument("--wasmq-only", action="store_true", help="only what holds a wasmQ job: its receipts, their changes, the rules and the labelled cases")
ap.add_argument("--first", action="store_true", help="stop at the first stage that finds a disagreement")
a = ap.parse_args()
global SECOND
if a.second:
SECOND = os.path.abspath(a.second)
if not a.second_only and not a.reference:
ap.error("--reference is required, unless --second-only")
ref = os.path.abspath(a.reference) if a.reference else None
with open(os.path.join(a.corpus, "manifest.json"), encoding="utf-8") as f:
manifest = json.load(f)["entries"]
problems = []
listed = {e["file"] for e in manifest}
present = {"jobs/" + n for n in os.listdir(os.path.join(a.corpus, "jobs")) if n.endswith(".json")}
for f in sorted(present - listed):
problems.append("%s is in the corpus and not in the manifest" % f)
for f in sorted(listed - present):
problems.append("%s is in the manifest and not in the corpus" % f)
# The corpus: both verifiers and the manifest, receipt by receipt.
entries = [e for e in manifest if e["file"] in present and (not a.wasmq_only or "wasmq" in e["file"])]
files = [os.path.join(a.corpus, e["file"]) for e in entries]
r2 = run_second_files(files, a.plant)
r1 = r2 if a.second_only else run_reference_files(ref, files)
agree = 0
for e, x, y in zip(entries, r1, r2):
if not a.second_only:
same_rules(e["file"], x, y, problems)
want = tuple(sorted(e["reasons"]))
if said(x) != said(y) or said(x)[0] != want:
problems.append("%s: the manifest says %s; the reference %s; the second verifier %s" % (e["file"], list(want), list(said(x)), list(said(y))))
else:
agree += 1
# A crafted wasmQ receipt that names one rule breaks that rule alone: one problem from the
# reference, and that rule alone from the second verifier, which numbers its rules.
alone = 0
for e, x, y in zip(entries, r1, r2):
m = re.search(r"\(rule (\d+)\)$", e.get("what", ""))
if not m or not e["file"].startswith("jobs/crafted-wasmq-"):
continue
alone += 1
for who, v in ((None if a.second_only else "the reference", x), ("the second verifier", y)):
if who and rules_of(v) != {int(m.group(1))}:
problems.append("%s breaks rule %s alone, but %s states %s" % (e["file"], m.group(1), who, (v.get("wasmq") or {}).get("problems")))
print("corpus: %d crafted wasmQ receipts each break the one rule they name, and no other" % alone)
valid = sum(1 for e in entries if not e["reasons"])
print("corpus: %d receipts (%d valid, %d not), %d agree %s" % (len(entries), valid, len(entries) - valid, agree, "with the manifest (second verifier only)" if a.second_only else "three ways (manifest, reference, second)"))
if a.second_only:
g = generator()
problems += rules_stage(None, a.plant, (g.seal, g.public_key(g.DEMO).hex()))
problems += labelled_stage(None, a.plant)
return finish(a, problems)
if a.first and problems:
return finish(a, problems)
# The rules and the labelled cases first: each is short, and each has its verdict written.
g = generator()
seal = (g.seal, g.public_key(g.DEMO).hex())
problems += rules_stage(ref, a.plant, seal)
if a.first and problems:
return finish(a, problems)
problems += labelled_stage(ref, a.plant)
if a.first and problems:
return finish(a, problems)
# Receipts a person holds: both must find them valid.
extra = []
for d in a.extra:
extra += [os.path.join(d, n) for n in sorted(os.listdir(d)) if n.endswith(".json")]
if extra:
x1, x2 = run_reference_files(ref, extra), run_second_files(extra, a.plant)
for f, x, y in zip(extra, x1, x2):
if said(x) != said(y) or said(x)[0]:
problems.append("%s (extra): the reference %s; the second verifier %s" % (os.path.basename(f), list(said(x)), list(said(y))))
print("extra: %d receipts held outside the corpus, %d valid to both" % (len(extra), sum(1 for x, y in zip(x1, x2) if not said(x)[0] and said(x) == said(y))))
# Every valid receipt, one member changed at a time.
lines, meta = [], []
for e in entries:
if e["reasons"]:
continue
try:
with open(os.path.join(a.corpus, e["file"]), encoding="utf-8") as f:
body = json.load(f)
except ValueError:
continue
if not isinstance(body, dict):
continue
for what, b, expected in mutations(body) + edges(body, seal):
lines.append(compact(b))
meta.append((e["file"], what, expected))
with tempfile.TemporaryDirectory() as tmp:
path = os.path.join(tmp, "changed.jsonl")
with open(path, "w", encoding="utf-8") as f:
f.write("\n".join(lines) + "\n")
m1 = run_lines([ref, "verify", "--lines"], path, len(lines))
m2 = run_lines([sys.executable, "-I", SECOND] + (["--plant", a.plant] if a.plant else []) + ["--lines"], path, len(lines))
agree, caught, held, compared = 0, 0, 0, 0
reasons = Counter()
for (fname, what, expected), x, y in zip(meta, m1, m2):
sx, sy = said(x), said(y)
same_rules("%s, %s" % (fname, what), x, y, problems)
if sx != sy:
problems.append("%s, %s: the reference %s; the second verifier %s" % (fname, what, list(sx), list(sy)))
continue
agree += 1
reasons.update(sx[0])
if expected == "invalid" and not sx[0]:
problems.append("%s, %s: a change inside the request, the result or the receipt was found valid by both" % (fname, what))
elif expected == "unchanged" and sx[0]:
problems.append("%s, %s: a change that is not sealed changed the verdict: %s" % (fname, what, list(sx[0])))
elif expected == "invalid":
caught += 1
elif expected == "agree":
compared += 1
else:
held += 1
print("changed: %d receipts from %d valid ones; %d agree (%d refused, as every change inside the seal must be; %d valid, as every change outside it must be; %d at the edges of a number, judged only by the other verifier)" % (len(lines), valid, agree, caught, held, compared))
print("reasons given to the changed receipts: " + ", ".join("%s %d" % kv for kv in sorted(reasons.items())))
return finish(a, problems)
def rules_stage(ref, plant, seal):
"""Each verifier given every case of rule_cases: each must give the reasons written for
it (the second alone with --second-only)."""
cases = rule_cases(CORPUS, seal[0])
with tempfile.TemporaryDirectory() as tmp:
path = os.path.join(tmp, "rules.jsonl")
with open(path, "w", encoding="utf-8") as f:
f.write("\n".join(b if isinstance(b, str) else compact(b) for _, b, _ in cases) + "\n")
second = run_lines([sys.executable, "-I", SECOND] + (["--plant", plant] if plant else []) + ["--lines"], path, len(cases))
first = run_lines([ref, "verify", "--lines"], path, len(cases)) if ref else second
problems = []
for (what, _, want), x, y in zip(cases, first, second):
if ref:
same_rules(what, x, y, problems)
rule = re.match(r"rule (\d+), ", what)
for who, v in (("the reference", x), ("the second verifier", y)):
if tuple(sorted(v.get("reasons", []))) != tuple(want):
problems.append("%s: written %s; %s %s" % (what, want, who, sorted(v.get("reasons", []))))
elif want and rule and rules_of(v) is not None and int(rule.group(1)) not in rules_of(v):
problems.append("%s: %s finds it breaks rules %s, not the rule it is written for" % (what, who, sorted(rules_of(v))))
rules = len({w.split(",")[0] for w, _, _ in cases if re.match(r"rule \d+,", w)})
kinds = sum(1 for w, _, _ in cases if w.startswith("rule kinds,"))
print("rules: %d cases, %d at the bounds of %d rules of a wasmQ job's form and %d of members of another kind, sealed again; %d as written, the same rules broken by both" % (len(cases), len(cases) - kinds, rules, kinds, len(cases) - len({p.split(":")[0] for p in problems})))
return problems
def labelled_stage(ref, plant):
"""The second verifier's own hand-labelled wasmQ cases (`receipt_verify.py --wasmq-cases`),
each as its exact text, given to both verifiers: each must give the reasons its author
wrote, so that one labelled corpus binds both. The labels come from receipt_verify.py as
written, whichever second verifier is under test."""
with tempfile.TemporaryDirectory() as tmp:
out = os.path.join(tmp, "cases.jsonl")
p = subprocess.run([sys.executable, "-I", ORIGINAL_SECOND, "--wasmq-cases", out], capture_output=True, text=True)
if p.returncode != 0:
raise SystemExit("receipt_verify.py --wasmq-cases failed: %s" % p.stderr.strip()[:400])
with open(out, encoding="utf-8") as f:
cases = [json.loads(line) for line in f if line.strip()]
files = []
for i, c in enumerate(cases):
path = os.path.join(tmp, "case-%04d.json" % i)
with open(path, "wb") as f:
f.write(base64.b64decode(c["raw"]))
files.append(path)
second = run_second_files(files, plant)
first = run_reference_files(ref, files) if ref else second
problems = []
for c, x, y in zip(cases, first, second):
if ref:
same_rules("labelled %s" % c["name"], x, y, problems)
want = sorted(c["reasons"])
for who, v in (("the reference", x), ("the second verifier", y)):
if sorted(v.get("reasons", [])) != want:
problems.append("labelled %s: written %s; %s %s" % (c["name"], want, who, sorted(v.get("reasons", []))))
print("labelled: %d of the second verifier's hand-labelled wasmQ cases, given to both; %d as written" % (len(cases), len(cases) - len({p.split(":")[0] for p in problems})))
return problems
def finish(a, problems):
if a.plant:
if problems:
print("plant %s: caught, by %d disagreements; the first: %s" % (a.plant, len(problems), problems[0]))
return 0
print("plant %s: NOT caught: the differential cannot tell this defect from the spec" % a.plant)
return 1
for p in problems[:60]:
print("DISAGREE " + p)
if problems:
print("%d disagreements" % len(problems))
return 1
print("receipt_differential: the two verifiers and the manifest agree on every receipt")
return 0
if __name__ == "__main__":
sys.exit(main())