#!/usr/bin/env python3 """generate.py: the crafted half of the receipt corpus (README.md). python3 -I generate.py # writes jobs/crafted-*.json and manifest.json python3 -I generate.py --check # fails unless both are what it would write The corpus has two halves. The made half (`jobs/cli-*`, `jobs/service-*`, `jobs/scripted-*`, `jobs/fixture-*`) was sealed by the command line, the job service, the provider clients and earlier engines, and is kept as it was made: nothing here writes it. The crafted half is made here from the made half, each receipt changed in one way and, where the change is inside what is sealed, sealed again with the published demo key (seed 42 repeated, the key `wai-quantum --demo-key` signs with), so that the one rule it breaks is the only thing wrong with it. The manifest names, for every receipt in both halves, the reasons a verifier must give (wai/extensions/quantum-providers.md section 8.4), each written here by hand from that section: none is copied from a verifier's output. To seal, it needs BLAKE3, the RFC 8785 text and Ed25519 signing. BLAKE3 is the integer-only referee's (../tools/referee.py, which shares no code with the engine); the other two are written below. Neither verifier is imported: the two verifiers judge what this writes, and must agree with each other and with the manifest. Standard library only, Python 3.9+. """ import copy import hashlib import importlib.util import json import math import os import struct import sys from decimal import Decimal HERE = os.path.dirname(os.path.abspath(__file__)) JOBS = os.path.join(HERE, "jobs") TRUST = os.path.join(HERE, "trust") HELD_DIR = os.path.join(HERE, "held") sys.dont_write_bytecode = True _spec = importlib.util.spec_from_file_location("referee", os.path.join(HERE, "..", "tools", "referee.py")) _referee = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(_referee) blake3 = _referee.blake3 # ------------------------------------------------------------------ JSON text class Raw(object): """A number written as this literal, whatever value it stands for.""" def __init__(self, literal, value): self.literal, self.value = literal, value def es_number(x): """ECMAScript's Number::toString of the double x (section 8.2).""" x = float(x) if x == 0: return "0" sign = "-" if x < 0 else "" t = Decimal(repr(abs(x))).as_tuple() digits = "".join(map(str, t.digits)).rstrip("0") or "0" k = len(digits) n = len("".join(map(str, t.digits))) + t.exponent if k <= n <= 21: body = digits + "0" * (n - k) elif 0 < n <= 21: body = digits[:n] + "." + digits[n:] elif -6 < n <= 0: body = "0." + "0" * (-n) + digits else: e = n - 1 body = digits[0] + ("." + digits[1:] if k > 1 else "") + "e" + ("+" if e >= 0 else "-") + str(abs(e)) return sign + body def first_number(v): """A number as the first form's writer wrote it (section 8.2).""" if isinstance(v, bool): raise TypeError if isinstance(v, int) and (0 <= v < 2 ** 64 or -2 ** 63 <= v < 0): return str(v) x = float(v) if x == 0: return "-0.0" if str(x).startswith("-") else "0.0" sign = "-" if x < 0 else "" t = Decimal(repr(abs(x))).as_tuple() digits = "".join(map(str, t.digits)).rstrip("0") or "0" k = len(digits) n = len("".join(map(str, t.digits))) + t.exponent if k <= n <= 16: body = digits + "0" * (n - k) + ".0" elif 0 < n <= 16: body = digits[:n] + "." + digits[n:] elif -5 < n <= 0: body = "0." + "0" * (-n) + digits else: e = n - 1 body = digits[0] + ("." + digits[1:] if k > 1 else "") + "e" + ("-" if e < 0 else "+") + str(abs(e)) return sign + body _SHORT = {'"': '\\"', "\\": "\\\\", "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r"} def string_text(s): out = ['"'] for c in s: if c in _SHORT: out.append(_SHORT[c]) elif ord(c) < 0x20: out.append("\\u%04x" % ord(c)) else: out.append(c) out.append('"') return "".join(out) def utf16(s): return s.encode("utf-16-be") def text(v, form="canonical"): """The canonical text of v, or its first-form text (members in order).""" if v is None: return "null" if v is True: return "true" if v is False: return "false" if isinstance(v, Raw): return text(v.value, form) if isinstance(v, (int, float)): return es_number(v) if form == "canonical" else first_number(v) if isinstance(v, str): return string_text(v) if isinstance(v, list): return "[" + ",".join(text(x, form) for x in v) + "]" keys = sorted(v, key=utf16) if form == "canonical" else list(v) return "{" + ",".join(string_text(k) + ":" + text(v[k], form) for k in keys) + "}" def dump(v, indent=0): """A readable file's text of v: two-space indents, Raw literals as given.""" pad, inner = " " * indent, " " * (indent + 1) if isinstance(v, Raw): return v.literal if isinstance(v, dict): if not v: return "{}" return "{\n" + ",\n".join(inner + json.dumps(k, ensure_ascii=False) + ": " + dump(x, indent + 1) for k, x in v.items()) + "\n" + pad + "}" if isinstance(v, list): if not v: return "[]" return "[\n" + ",\n".join(inner + dump(x, indent + 1) for x in v) + "\n" + pad + "]" if isinstance(v, float) and v == 0 and str(v).startswith("-"): return "-0.0" return json.dumps(v, ensure_ascii=False) # ---------------------------------------------------------------- Ed25519 (RFC 8032) P = 2 ** 255 - 19 L = 2 ** 252 + 27742317777372353535851937790883648493 D = -121665 * pow(121666, P - 2, P) % P SQRT_M1 = pow(2, (P - 1) // 4, P) def _add(a, b): x1, y1, z1, t1 = a x2, y2, z2, t2 = b A, B = (y1 - x1) * (y2 - x2) % P, (y1 + x1) * (y2 + x2) % P C, E = t1 * 2 * D * t2 % P, z1 * 2 * z2 % P e, f, g, h = B - A, E - C, E + C, B + A return (e * f % P, g * h % P, f * g % P, e * h % P) def _mul(s, pt): q = (0, 1, 1, 0) while s: if s & 1: q = _add(q, pt) pt = _add(pt, pt) s >>= 1 return q def _encode(pt): x, y, z, _ = pt zi = pow(z, P - 2, P) x, y = x * zi % P, y * zi % P return (y | ((x & 1) << 255)).to_bytes(32, "little") def _decode(b): y = int.from_bytes(b, "little") & ((1 << 255) - 1) sign = b[31] >> 7 if y >= P: return None xx = (y * y - 1) * pow(D * y * y + 1, P - 2, P) % P x = pow(xx, (P + 3) // 8, P) if (x * x - xx) % P: x = x * SQRT_M1 % P if (x * x - xx) % P: return None if x == 0 and sign: return None if x & 1 != sign: x = P - x return (x, y, 1, x * y % P) BASE = _decode((4 * pow(5, P - 2, P) % P).to_bytes(32, "little")) def public_key(seed): h = hashlib.sha512(seed).digest() a = int.from_bytes(h[:32], "little") a = (a & ((1 << 254) - 8)) | (1 << 254) return _encode(_mul(a, BASE)) def sign(seed, msg): h = hashlib.sha512(seed).digest() a = int.from_bytes(h[:32], "little") a = (a & ((1 << 254) - 8)) | (1 << 254) A = _encode(_mul(a, BASE)) r = int.from_bytes(hashlib.sha512(h[32:] + msg).digest(), "little") % L R = _encode(_mul(r, BASE)) k = int.from_bytes(hashlib.sha512(R + A + msg).digest(), "little") % L return R + ((r + k * a) % L).to_bytes(32, "little") DEMO = bytes([42] * 32) OTHER = bytes([43] * 32) IDENTITY = bytes([1] + [0] * 31) # A point of order 8. ORDER8 = bytes.fromhex("c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a") # --------------------------------------------------------------------- sealing DOMAIN = b"wai:quantum-service-receipt" def digest(request, result, form="canonical", time=None): """The digest a receipt signs (section 8.2); with `time`, the one that seals it: the domain's byte 3, and the time's canonical text after the result's.""" q = text(request, form).encode("utf-8") t = text({k: v for k, v in result.items() if k != "receipt"}, form).encode("utf-8") if time is not None: v = text(time).encode("utf-8") return blake3(DOMAIN + bytes([3]) + struct.pack(">Q", len(q)) + q + struct.pack(">Q", len(t)) + t + struct.pack(">Q", len(v)) + v) return blake3(DOMAIN + bytes([2 if form == "canonical" else 1]) + struct.pack(">Q", len(q)) + q + struct.pack(">Q", len(t)) + t) def seal(body, seed=DEMO, form="canonical", time=None): """body with its result sealed again by seed's key, in form; with `time`, a receipt that seals it (section 8.2).""" d = digest(body["request"], body["result"], form, time) receipt = {"alg": "ed25519", "canon": "rfc8785", "key": public_key(seed).hex(), "digest": d.hex(), "signature": sign(seed, d).hex()} if form != "canonical": del receipt["canon"] if time is not None: receipt["time"] = time out = copy.deepcopy(body) out["result"] = {k: v for k, v in out["result"].items() if k != "receipt"} out["result"]["receipt"] = receipt return out MERKLE_LEAF = b"moq-jwp:merkle-leaf\x01" MERKLE_NODE = b"moq-jwp:merkle-node\x01" def merkle_levels(leaves): """Each level of the tree over leaves (section 8.6), the leaves' hashes first.""" level = [blake3(MERKLE_LEAF + x) for x in leaves] levels = [level] while len(level) > 1: if len(level) % 2: level = level + [level[-1]] levels[-1] = level level = [blake3(MERKLE_NODE + level[i] + level[i + 1]) for i in range(0, len(level), 2)] levels.append(level) return levels def set_leaf(item): """An item's leaf (section 8.6): the digest of its request and its result, or its error.""" if "result" in item: return digest(item["request"], item["result"]) return digest(item["request"], {"error": item["error"]}) def seal_set(body, seed=DEMO): """A set with its root and paths made from its items, and its receipt over its request and its head, sealed again.""" out = copy.deepcopy(body) r = out["result"] levels = merkle_levels([set_leaf(it) for it in r["items"]]) r["root"] = levels[-1][0].hex() for i, it in enumerate(r["items"]): path, at = [], i for level in levels[:-1]: path.append(level[at ^ 1].hex()) at //= 2 it["path"] = path head = {k: v for k, v in r.items() if k not in ("items", "receipt")} d = digest(out["request"], head) r["receipt"] = {"alg": "ed25519", "canon": "rfc8785", "key": public_key(seed).hex(), "digest": d.hex(), "signature": sign(seed, d).hex()} return out def coin_split(n): """Section 8.7's m(n): what a fair coin's split of n leaves between its sides on average.""" if n == 0: return 0.0 j = (n - 1) // 2 if j < 4096: m = 1.0 for i in range(j): m = m * float(2 * i + 3) / float(2 * i + 2) return m jj = float(j) c = (1.0 - 1.0 / (8.0 * jj) + 1.0 / (128.0 * jj * jj) + 5.0 / (1024.0 * jj * jj * jj)) / math.sqrt(math.pi * jj) return (2.0 * jj + 1.0) * c def _count(v): """A count's value (quantum-wasmq section 6.5): a whole number from 0 to 2^53 - 1.""" if isinstance(v, Raw): v = v.value if isinstance(v, bool) or not isinstance(v, (int, float)) or v != v or v in (float("inf"), float("-inf")): return None return int(v) if v == int(v) and 0 <= v <= 2 ** 53 - 1 else None def bare_transcript(r): """The transcript_blake3 of a wasmQ result in which no call returned (quantum-wasmq section 6.5, rule 35, which the second version keeps, section 11.6): the prefix and, after a fault, its shot, reason and fuel. None when a call returned, or its callouts are no count.""" if not isinstance(r, dict) or r.get("format") not in ("wai.quantum.wasmq.result/1", "wai.quantum.wasmq.result/2"): return None fault = r.get("fault") if r.get("status") == "fault" and isinstance(r.get("fault"), dict) else None in_call = 1 if fault is not None and fault.get("phase") == "call" else 0 k = _count(r.get("callouts")) if k is None or k > in_call: return None data = b"wai:wasmq-transcript\x01" if fault is not None: reason = fault.get("reason") data += b"fault" + struct.pack("= 0 again = copy.deepcopy(made) replay_as(L2)(again) assert again["result"]["replay"] == r["replay"], "the replay digest begins with the second version's name" add("landscape2-board-certified", edited(l2run, board2()), [], "a second-version run from a board the host graded bit-exact on that version's set, its audit beside it and no capacity: every rule holds") add("landscape2-board-uncertified", edited(l2settle, board2(parity="uncertified")), [], "a second-version settle from a board run by name, uncertified, with no capacity: every rule holds") add("landscape2-first-form", seal(l2run, form="first"), LF, "a second-version run sealed in the first form, older than the extension (rule 1)") add("landscape2-schema-first", edited(l2run, res(schema=L1)), LF, "a second-version answer whose schema names the first version (rule 2)") add("landscape2-protocol-first", edited(l2run, res(protocol=L1)), LF, "a second-version answer whose protocol names the first version, its schema the second (rule 2)") add("landscape2-request-no-protocol", edited(l2run, lambda b: b["request"].pop("protocol")), LF, "a second-version answer to a request that names no protocol, which is a job of the first version (rule 3)") add("landscape2-request-protocol-first", edited(l2run, req(protocol=L1)), LF, "a second-version answer to a request that names the first version (rule 3)") add("landscape2-request-protocol-null", edited(l2run, req(protocol=None)), LF, "a second-version answer to a request whose protocol is null, which is absent (rule 3)") add("landscape2-kernel-first", edited(l2run, member(kernel=S1)), LF, "a second-version run whose member names the first version's sampling kernel (rule 6, and rule 10's kernel)") add("landscape2-statistical", edited(l2run, board2(parity="statistical")), LF, "a second-version sampling kernel graded statistical, which that version grants no kernel (rule 6)") add("landscape2-compare-kernels", edited(l2compare, lambda b: b["result"]["member"]["kernels"].append({"kernel": T2, "parity": "reference"})), LF, "a second-version comparison whose member names the settle kernel too (rule 6)") add("landscape2-compare-kernel-first", edited(l2compare, lambda b: b["result"]["member"]["kernels"][0].update(kernel=S1)), LF, "a second-version comparison whose member names the first version's sampling kernel (rule 6, and its rows' parity, rule 12)") add("landscape2-capacity-uncertified", edited(l2run, board2(parity="uncertified", capacity={"cells": 64})), LF, "a second-version member stating a capacity, which that version holds no member to (rule 8)") add("landscape2-capacity-certified", edited(l2run, board2(capacity={"cells": 64})), LF, "a second-version board graded bit-exact within a capacity of 64 cells, as the first version's would be: no capacity is stated there (rule 8)") add("landscape2-replay-first-name", edited(l2run, replay_as(L1)), LF, "a second-version run whose replay digest begins with the first version's name (rule 10)") add("landscape2-replay-other", edited(l2settle, res(replay="0" * 64)), LF, "a second-version settle whose replay digest is not the one its program, plan, state and energy give (rule 10)") add("landscape2-sample-priced", edited(l2run, joules(kind="priced", pj=12.5)), LF, "a sampling run priced: no rate with evidence covers a run above temperature 0 (section 4.3, rule 10)") add("landscape2-sample-figure-zero", edited(l2run, joules(pj=0)), LF, "a sampling run unpriced with a figure of 0: an unpriced run gives no figure, of 0 or any other (rule 10)") add("landscape2-sample-no-why", edited(l2run, joules(why="")), LF, "a sampling run unpriced with an empty why (rule 10)") add("landscape2-sample-why-null", edited(l2run, joules(why=None)), LF, "a sampling run unpriced with no why (rule 10)") add("landscape2-sample-pj-absent", edited(l2run, lambda b: b["result"]["joules"].pop("pj")), LF, "a sampling run unpriced with no pj at all: it is null, present (rule 10)") add("landscape2-settle-unpriced", edited(l2settle, joules(kind="unpriced", pj=None, why="no rate")), LF, "a settle stated unpriced: a run at temperature 0 is priced (rule 10)") add("landscape2-settle-no-figure", edited(l2settle, joules(pj=None)), LF, "a settle priced with no figure (rule 10)") add("landscape2-quench-figure-negative", edited(l2quench, joules(pj=-1)), LF, "a quench priced below 0 (rule 10)") add("landscape2-quench-figure-zero", edited(l2quench, joules(pj=0)), [], "a quench priced at 0: a number of at least 0 (rule 10 holds)") add("landscape2-joules-measured", edited(l2settle, joules(kind="measured")), LF, "a settle's joules stated as measured: they are priced (section 4.3, rule 10)") add("landscape2-metered-absent", edited(l2settle, lambda b: b["result"]["joules"].pop("metered")), LF, "a settle's joules with neither a meter's reading nor null beside them (rule 10)") add("landscape2-mode-kernel", edited(l2run, res(mode="settle")), LF, "a second-version sampling run stated as a settle: the kernel, the temperature and the joules say otherwise (rule 10)") add("landscape2-replay-line-dropped", edited(l2replay, lambda b: b["result"].update(lines="".join(b["result"]["lines"].splitlines(True)[:-1]))), LF, "a second-version replay of four seeds with three lines (rule 11)") add("landscape2-sweep-row-kernel-first", edited(l2sweep, lambda b: b["result"]["rows"][0].update(kernel="wai.quantum.landscape/settle" if b["result"]["rows"][0]["mode"] == "settle" else S1)), LF, "a second-version sweep's row naming the first version's kernel, which its member does not state (rule 12)") add("landscape2-sweep-row-parity", edited(l2sweep, lambda b: b["result"]["rows"][-1].update(parity="bit-exact")), LF, "a second-version sweep's row stating another parity than the member's for its kernel (rule 12)") add("landscape2-compare-classical-parity", edited(l2compare, lambda b: b["result"]["rows"][-1].update(parity="reference")), LF, "a classical method's row stating a parity, in a second-version comparison (rule 12)") add("landscape2-provider-name-other", edited(l2prov, lambda b: b["result"]["provider"].update(name="other")), LF, "a second-version provider's job naming another provider than its request (rule 13)") add("landscape2-provider-unrequested", edited(l2prov, lambda b: b["request"].pop("provider")), LF, "a provider's members on a second-version job whose request names no provider (rule 13)") add("landscape2-provider-unconfirmed", edited(l2priced, lambda b: b["result"]["cost"].update(confirmed=False)), LF, "a second-version run on a provider that was not free, not confirmed (rule 13)") add("landscape2-provider-protocol-other", edited(l2prov, lambda b: b["result"]["provider"].update(protocol=L2)), LF, "a provider's job whose provider protocol is the landscape protocol's name, not wai.quantum.provider/1 (rule 13)") add("landscape2-tampered", (lambda b: (b["result"].update(state="-" * len(b["result"]["state"])), b)[1])(copy.deepcopy(l2run)), ["digest_mismatch", "landscape_form"], "a second-version run whose state was changed after it was sealed: the seal, and its hash and replay digest (rule 10)") # The first version beside the second: an answer of one to a request of the other, and # one version's kernel in the other's answer. add("landscape-request-protocol-second", edited(lrun, req(protocol=L2)), LF, "a first-version answer to a request that names the second version (rule 3)") add("landscape-kernel-second", edited(lrun, member(kernel=S2)), LF, "a first-version run whose member names the second version's sampling kernel (rule 6, and rule 10's kernel)") add("landscape-replay-second-name", edited(lrun, replay_as(L2)), LF, "a first-version run whose replay digest begins with the second version's name (rule 10)") add("landscape-request-protocol-first", edited(lrun, req(protocol=L1)), [], "a first-version run whose request names the first version, as it may: every rule holds") return out # ------------------------------------------- the time, and whose key (sections 8.2, 8.8) T0 = 1762862400000 # 2025-11-11T12:00:00Z, in milliseconds since 1970 KEYS_FORMAT = "wai.quantum.receipt-keys/1" TRANSITION_DOMAIN = b"wai:quantum-key-transition\x01" def transition(old, new, at_ms): """A rotation's statement, signed by the key retiring and its successor (section 8.8).""" statement = {"format": "wai.quantum.key-transition/1", "retiring": public_key(old).hex(), "successor": public_key(new).hex(), "at_ms": at_ms} d = blake3(TRANSITION_DOMAIN + text(statement).encode("utf-8")) return {"statement": statement, "retiring_signature": sign(old, d).hex(), "successor_signature": sign(new, d).hex()} def trust_lists(): """(file name, the text, whether a verifier reads it, what it is): every trust list of trust/. One a verifier does not read judges nothing: it says why, and exits 2.""" demo, other = public_key(DEMO).hex(), public_key(OTHER).hex() def keys(*entries, **top): doc = {"format": KEYS_FORMAT, "algorithm": "ed25519"} doc.update(top) doc["keys"] = list(entries) return doc rotation = transition(DEMO, OTHER, T0 + 1000) rotated = keys( {"key": demo, "not_before_ms": None, "current": False, "not_after_ms": T0 + 1000, "successor": other, "transition": rotation}, {"key": other, "not_before_ms": T0 + 1000, "current": True}, note="A set as the job service publishes one after a rotation.") good = keys({"key": demo, "not_before_ms": T0, "not_after_ms": T0 + 1000}) out = [ ("demo-open.json", keys({"key": demo, "not_before_ms": None, "current": True}), True, "the demo key, signing from the start with no end"), ("demo-window.json", good, True, "the demo key in a window of one second from T0"), ("demo-revoked.json", keys({"key": demo, "not_before_ms": None, "revoked_from_ms": T0 + 500}), True, "the demo key revoked from half a second after T0"), ("demo-rotated.json", rotated, True, "the demo key retired a second after T0 for another, with the transition both signed"), ("other-only.json", keys({"key": other}), True, "another key alone"), ("times-respelt.json", keys({"key": demo, "not_before_ms": Raw("1.7628624e12", T0), "not_after_ms": Raw("1762862401000.0", T0 + 1000), "revoked_from_ms": None}), True, "the window of demo-window, its times written as other numbers of the same value"), ] def refused(name, doc_or_text, what): out.append(("refused-" + name + ".json", doc_or_text, False, what)) def changed(edit): d = copy.deepcopy(good) edit(d) return d refused("format", changed(lambda d: d.update(format="wai.quantum.key-transition/1")), "another document's format") refused("no-format", changed(lambda d: d.pop("format")), "no format") refused("algorithm", changed(lambda d: d.update(algorithm="p256")), "another algorithm") refused("algorithm-null", changed(lambda d: d.update(algorithm=None)), "an algorithm that is null") refused("no-keys", changed(lambda d: d.pop("keys")), "no keys") refused("keys-object", changed(lambda d: d.update(keys={demo: {}})), "keys that are an object") refused("entry-not-object", changed(lambda d: d["keys"].append(demo)), "an entry that is a string") refused("key-upper", changed(lambda d: d["keys"][0].update(key=demo.upper())), "a key in upper-case hex") refused("key-short", changed(lambda d: d["keys"][0].update(key=demo[:-2])), "a key of 31 bytes") refused("key-twice", changed(lambda d: d["keys"].append({"key": demo})), "a key listed twice") refused("time-fraction", changed(lambda d: d["keys"][0].update(not_after_ms=T0 + 0.5)), "a time with a fraction") refused("time-negative", changed(lambda d: d["keys"][0].update(not_before_ms=-1)), "a time before 1970") refused("time-past-safe", changed(lambda d: d["keys"][0].update(not_after_ms=Raw("9007199254740992", 2 ** 53))), "a time past 2^53 - 1") refused("time-string", changed(lambda d: d["keys"][0].update(not_after_ms=str(T0))), "a time as a string") refused("revoked-true", changed(lambda d: d["keys"][0].update(revoked_from_ms=True)), "a revocation that is no time") refused("transition-unsigned", changed(lambda d: d["keys"][0].update(transition={"statement": rotation["statement"]})), "a transition with no signatures") refused("transition-changed", changed(lambda d: d["keys"][0].update(transition=dict(rotation, statement=dict(rotation["statement"], at_ms=T0)))), "a transition changed after it was signed") refused("transition-other-key", keys({"key": other, "transition": rotation}), "a transition on its successor's entry, which it does not name as retiring") refused("transition-exchanged", changed(lambda d: d["keys"][0].update(transition=dict(rotation, retiring_signature=rotation["successor_signature"], successor_signature=rotation["retiring_signature"]))), "a transition whose two signatures are exchanged") whole = dump(good) + "\n" refused("member-twice", whole.replace('"algorithm": "ed25519",', '"algorithm": "ed25519",\n "algorithm": "ed25519",', 1), "a member named twice") refused("not-json", "format wai.quantum.receipt-keys/1\n", "not JSON") refused("array", "[]\n", "an array") refused("two-values", whole + whole, "two values") return [(name, t if isinstance(t, str) else dump(t) + "\n", reads, what) for name, t, reads, what in out] def rx_time(body, edit): """body with only its receipt changed by edit: nothing sealed again.""" b = copy.deepcopy(body) edit(b["result"]["receipt"]) return b def time_cases(add, run): """Receipts that seal a time (section 8.2): the time inside the digest, and of one form.""" issued = {"issued_ms": T0} until = {"issued_ms": T0, "not_after_ms": T0 + 3600000} timed, lasting = seal(run, time=issued), seal(run, time=until) # --- the time's seal (section 8.2) add("time-issued", timed, [], "a receipt that seals when it was issued") add("time-until", lasting, [], "a receipt that seals when it was issued and until when it is offered") add("time-respelt", seal(run, time={"issued_ms": Raw("1.7628624e12", T0), "not_after_ms": Raw("1762866000000.0", T0 + 3600000)}), [], "the same times written as other numbers of the same value: the same time, and the same digest") add("time-zero", seal(run, time={"issued_ms": Raw("-0", 0)}), [], "issued at 0, written -0") add("time-largest", seal(run, time={"issued_ms": 2 ** 53 - 2, "not_after_ms": 2 ** 53 - 1}), [], "the largest times a receipt states") add("time-changed", rx_time(timed, lambda r: r["time"].update(issued_ms=T0 + 1)), ["digest_mismatch"], "a time changed after it was sealed") add("time-end-added", rx_time(timed, lambda r: r["time"].update(not_after_ms=T0 + 1)), ["digest_mismatch"], "an end put on a time sealed without one") add("time-end-removed", rx_time(lasting, lambda r: r["time"].pop("not_after_ms")), ["digest_mismatch"], "a sealed end taken off") add("time-removed", rx_time(timed, lambda r: r.pop("time")), ["digest_mismatch"], "a sealed time taken off: the digest is not one of a receipt that seals none") add("time-put-on", rx_time(run, lambda r: r.update(time=dict(issued))), ["digest_mismatch"], "a time put on a receipt that sealed none") for name, bad, what in ( ("null", None, "a time that is null"), ("number", T0, "a time that is a number"), ("string", "2026-11-11T12:00:00Z", "a time that is a string"), ("array", [T0], "a time that is an array"), ("empty", {}, "a time with no issue"), ("end-alone", {"not_after_ms": T0}, "an end and no issue"), ("issued-null", {"issued_ms": None}, "an issue that is null"), ("issued-negative", {"issued_ms": -1}, "issued before 1970"), ("issued-fraction", {"issued_ms": T0 + 0.5}, "an issue with a fraction"), ("issued-string", {"issued_ms": str(T0)}, "an issue that is a string"), ("issued-past-safe", {"issued_ms": Raw("9007199254740992", 2 ** 53)}, "issued past 2^53 - 1"), ("end-at-issue", {"issued_ms": T0, "not_after_ms": T0}, "an end at the issue"), ("end-before-issue", {"issued_ms": T0, "not_after_ms": T0 - 1}, "an end before the issue"), ("end-null", {"issued_ms": T0, "not_after_ms": None}, "an end that is null"), ("member", {"issued_ms": T0, "clock": "host"}, "a member a time does not have")): # Sealed as it is written, so that its form alone is wrong (null, which a seal here # reads as no time, is put on a receipt that sealed one). body = rx_time(timed, lambda r: r.update(time=None)) if bad is None else seal(run, time=bad) add("time-form-" + name, body, ["receipt_form"], what + ": not a receipt of its form") add("time-first-form", rx_time(seal(run, form="first"), lambda r: r.update(time=dict(issued))), ["receipt_form"], "a time on a receipt of the first form, which seals none") def held_to(add, run): """Receipts held to a trust list and to a time (section 8.8), each rule at its bound.""" issued = {"issued_ms": T0} until = {"issued_ms": T0, "not_after_ms": T0 + 3600000} timed, lasting = seal(run, time=issued), seal(run, time=until) def at(ms, seed=DEMO): return seal(run, seed, time={"issued_ms": ms}) # --- held to a trust list def trust(name, **more): return dict({"trust": "trust/" + name + ".json"}, **more) add("open-untimed", run, [], "no time sealed, by a key whose window has no end: it can have been signed at any time in it", trust("demo-open")) add("open-timed", timed, [], "a time sealed, by a key signing from the start with no end", trust("demo-open")) add("window-before", at(T0 - 1), ["key_window"], "issued a millisecond before its key's window opened", trust("demo-window")) add("window-first", at(T0), [], "issued at the first moment of its key's window", trust("demo-window")) add("window-last", at(T0 + 999), [], "issued in the last millisecond of its key's window", trust("demo-window")) add("window-end", at(T0 + 1000), ["key_window"], "issued at its key's window's end, which is its successor's first moment", trust("demo-window")) add("window-untimed", run, ["key_window"], "no time sealed, by a key whose window has an end: it cannot be placed before it", trust("demo-window")) add("window-respelt", at(T0 + 999), [], "the same window, its times written as other numbers", trust("times-respelt")) add("window-respelt-end", at(T0 + 1000), ["key_window"], "the same window written so, at its end", trust("times-respelt")) add("revoked-before", at(T0 + 499), [], "issued a millisecond before its key was revoked: it stands", trust("demo-revoked")) add("revoked-at", at(T0 + 500), ["key_revoked"], "issued at the moment its key is revoked from", trust("demo-revoked")) add("revoked-untimed", run, ["key_revoked"], "no time sealed, by a revoked key: it cannot be placed before the revocation", trust("demo-revoked")) add("unlisted", timed, ["key_untrusted"], "a key the trust list does not name", trust("other-only")) add("rotated-old", at(T0 + 999), [], "the retired key's receipt, issued before the rotation: it stands", trust("demo-rotated")) add("rotated-old-after", at(T0 + 1000), ["key_window"], "the retired key's receipt, issued at the rotation", trust("demo-rotated")) add("rotated-new", at(T0 + 1000, OTHER), [], "the successor's receipt, issued at the rotation", trust("demo-rotated")) add("rotated-new-before", at(T0 + 999, OTHER), ["key_window"], "the successor's receipt, issued before the rotation", trust("demo-rotated")) add("rotated-new-untimed", seal(run, OTHER), [], "the successor's receipt with no time sealed: its window has no end", trust("demo-rotated")) # --- held to a time add("at-before-end", lasting, [], "held to the last millisecond before the end it seals", {"at_ms": T0 + 3599999}) add("at-end", lasting, ["expired"], "held to the end it seals", {"at_ms": T0 + 3600000}) add("at-issue", lasting, [], "held to the moment it was issued", {"at_ms": T0}) add("at-before-issue", lasting, ["issued_later"], "held to a millisecond before it says it was issued", {"at_ms": T0 - 1}) add("at-within-skew", lasting, [], "issued five milliseconds after the time it is held to, five allowed", {"at_ms": T0 - 5, "skew_ms": 5}) add("at-past-skew", lasting, ["issued_later"], "issued six milliseconds after the time it is held to, five allowed", {"at_ms": T0 - 6, "skew_ms": 5}) add("at-no-end", timed, [], "no end sealed: it does not expire", {"at_ms": 2 ** 53 - 1}) add("at-untimed", run, [], "no time sealed: nothing to hold to a time", {"at_ms": 0}) # --- both, and beside the seal's own reasons add("both", at(T0 + 999), [], "in its key's window, and held to a later time", trust("demo-window", at_ms=T0 + 5000)) add("both-refused", seal(run, time={"issued_ms": T0 + 1000, "not_after_ms": T0 + 2000}), ["expired", "key_window"], "outside its key's window, and past its end", trust("demo-window", at_ms=T0 + 2000)) add("changed", rx_time(at(T0 + 999), lambda r: r["time"].update(issued_ms=T0 + 1000)), ["digest_mismatch", "key_window"], "a time changed after it was sealed, to one outside its key's window: both are said", trust("demo-window")) add("not-of-its-form", seal(run, time={"issued_ms": None}), ["receipt_form"], "a receipt not of its form names no key and no time: it is held to nothing", trust("other-only", at_ms=0)) # --------------------------------------------------------------------- the made half MADE = { "cli-run-observables.json": ("the command line, on this machine, with --demo-key: a Bell pair's expectation values", []), "cli-run-statevector.json": ("the command line: a three-qubit circuit's counts by the state vector", []), "cli-run-trajectories.json": ("the command line: noisy trajectories, an estimate with its error", []), "cli-run-mps.json": ("the command line: thirty qubits as a matrix product state", []), "cli-compile.json": ("the command line: a compile, its certificate and its receipt", []), "cli-provider-reference.json": ("the command line, on the reference provider over a local process: a simulator's stated result", []), "cli-provider-reference-priced.json": ("the command line, on the reference provider with a price, confirmed with --yes", []), "cli-contract-local.json": ("the command line under a contract: exact, a deadline and a ceiling, the cheapest route here chosen and its reasoning sealed", []), "cli-contract-auto.json": ("the command line under a contract that names no class: auto's pick kept, held to a deadline", []), "cli-contract-routed.json": ("the command line under a contract that allows the person's providers and wants a simulator's counts: the free one chosen, a classical method's counts and their distance beside its own", []), "cli-contract-provider-priced.json": ("the command line on a provider it names, allowed by its contract and under a ceiling, confirmed with --yes: the estimate held to the ceiling, the charge to it after, and the shadow", []), "cli-contract-routed-seed.json": ("the same routed job with the seed 2^53 - 2: its shadow's seed, the request's plus 2^32, wraps at 2^53", []), "cli-contract-any.json": ("the command line under a contract that takes any class, the person's providers allowed: a free method here and a free provider tie on money and on time to the millisecond, and the method here comes first", []), "cli-contract-bounded.json": ("the command line under a contract that wants bounded: a single-precision route does not meet it, and the state vector is chosen", []), "cli-contract-fabric-cpu.json": ("the command line on the CPU it asks for, under a deadline: each route that runs on an accelerator rejected for it", []), "cli-contract-target.json": ("the command line on a device target, the person's providers allowed beside it, under a deadline: the target's method is named, and the providers are not", []), "cli-contract-pulse.json": ("the command line, a pulse program under a contract that wants bounded and a deadline: its one route", []), "service-run.json": ("the free job service, signed with its key", []), "service-run-stabilizer.json": ("the free job service: a stabilizer job with observables", []), "scripted-sampler-v2.json": ("sampler-v2 through a service that follows its contract", []), "scripted-sampler-v2-post-selected.json": ("sampler-v2 on a device that answered 13 of its 400 shots fewer, as a post-selected readout does: its counts sealed as the 387 shots it kept (accepted_shots)", []), "scripted-prx-circuits.json": ("prx-circuits through a service that follows its contract", []), "scripted-contract-prx-circuits.json": ("prx-circuits named under a contract that wants a device's counts, confirmed: its counts, the planner's reasoning, and a classical method's counts and their distance beside them", []), "scripted-prx-circuits-pinned.json": ("prx-circuits sent pinned to the calibration set it was compiled against, dynamical decoupling disabled and the service's other modes sent: its second row (section 8.3)", []), "scripted-prx-circuits-recalibrated.json": ("prx-circuits through a service that recalibrated between the read of its device and the submission: the job pinned to the set it was compiled against, placed by that set's quality metrics read as a target, and the result's calibration the set the service says it compiled against", []), "scripted-native-turns.json": ("native-turns, its simulator, through a service that follows its contract", []), "scripted-native-turns-noise.json": ("native-turns, its simulator under a device's noise model", []), "scripted-native-turns-processor.json": ("native-turns on a processor of gpi, gpi2 and zz, read from the service's list of backends, its pairs from its characterization: counts tallied from the job's shot records, none of them flagged as leaked (leakage, section 8.1)", []), "scripted-native-turns-leaked.json": ("native-turns on a processor whose shot records flag 3 of its 400 shots as leaked: each counted as it was read, and the 3, with the qubits flagged, sealed (leakage)", []), "scripted-native-turns-seeded.json": ("native-turns, its simulator under a device's noise model, sent the job's seed so that the simulation is the same at each run of it: the seed sealed among the settings sent (section 8.3), the counts tallied from shot records", []), "scripted-native-turns-ms-processor.json": ("native-turns on a processor whose gates are gpi, gpi2 and ms: the program in gpims, each cz one ms(0, 0, 1/4) between quarter-turn rotations", []), "scripted-native-turns-priced.json": ("native-turns on a processor priced by its gates, named under a ceiling and confirmed: the rate card's figure for exactly its gates and shots held to the ceiling, and what the service charged held to it after (section 8.7)", []), "scripted-cloud-tasks-processor.json": ("cloud-tasks on a processor: a verbatim box, rows of shots", []), "scripted-cloud-tasks-processor-probabilities.json": ("cloud-tasks on a processor whose result holds probabilities: counts rebuilt", []), "scripted-cloud-tasks-simulator.json": ("cloud-tasks on its simulator: simulated", []), "fixture-job-legacy.json": ("a run sealed before results named their engine and schema (the engine's pinned fixture)", []), "fixture-provider-job-legacy.json": ("a provider job of the earlier form: a simulator's counts sealed as measured, read as simulated", []), "fixture-compile-legacy.json": ("a compile as an earlier page sealed it (the engine's pinned fixture)", []), "cli-set-programs.json": ("the command line: a set of two programs, one receipt over both and each its own (section 8.6)", []), "cli-set-sweep.json": ("the command line: a sweep of an input at three points, sealed as a set", []), "first-form-service-run.json": ("the job service as it was on 18 October 2026, commit 72d18742d, sealing with the first form's own writer: counts", []), "first-form-service-observables.json": ("the same service, the same writer: expectation values, doubles in its own text", []), "cli-wasmq-run.json": ("the command line: a wasmQ run, a surface-code round decoded by WebAssembly between its gates, 32 shots", []), "cli-wasmq-state-job.json": ("the command line: a wasmQ run of one instance for the whole run, its decoder reading a prior", []), "cli-wasmq-fault.json": ("the command line: a wasmQ run that stopped in a call past the call's fuel budget", []), "cli-wasmq-fault-run.json": ("the command line: a wasmQ run that stopped in its third shot when the run's fuel ran out", []), "cli-wasmq-fault-init.json": ("the command line: a wasmQ run that stopped in an instance's init", []), "cli-wasmq-fault-prior.json": ("the command line: a wasmQ run that stopped while a module read its prior", []), "cli-landscape-run.json": ("the command line: an energy-landscape run (wai.quantum.landscape/1), sampling from the default start, every arrangement checked", []), "cli-landscape-settle.json": ("the command line: an energy-landscape settle, the deterministic kernel, one end of a chain pinned", []), "cli-landscape-quench.json": ("the command line: an energy-landscape quench, chains at temperature 0", []), "cli-landscape-replay.json": ("the command line: an energy-landscape replay of four seeds, one line each", []), "cli-landscape-sweep.json": ("the command line: an energy-landscape sweep, the program at each temperature", []), "cli-landscape-compare.json": ("the command line: an energy-landscape comparison with three classical methods, on a program that fails on purpose", []), "cli-landscape-provider.json": ("the command line, an energy-landscape run on the reference provider over a local process (quantum-providers section 11): the provider, its job and its cost added", []), "cli-landscape-provider-priced.json": ("the same on the reference provider with a price, confirmed with --yes: charged", []), "cli-landscape2-run.json": ("the command line: an energy-landscape run of the second version (wai.quantum.landscape/2), a 3x3 assignment sampled and solved, its joules stated unpriced", []), "cli-landscape2-settle.json": ("the command line: a second-version settle of the same assignment, which never comes to rest: failed, its joules priced at the settle rate", []), "cli-landscape2-quench.json": ("the command line: a second-version quench, four regions coloured from chains at temperature 0, its joules priced", []), "cli-landscape2-replay.json": ("the command line: a second-version replay of four seeds, one line each", []), "cli-landscape2-sweep.json": ("the command line: a second-version sweep, four queens at each temperature, each row priced or not as its temperature says", []), "cli-landscape2-compare.json": ("the command line: a second-version comparison with three classical methods, on four regions that all touch, which fails on purpose", []), "cli-landscape2-provider.json": ("the command line, a second-version run on the reference provider over a local process, whose description lists that version (quantum-providers section 11.1): the provider, its job and its cost added", []), "cli-landscape2-provider-priced.json": ("a second-version quench on the reference provider with a price, confirmed with --yes: charged", []), "cli-wasmq2-t-toffoli.json": ("the command line: a wasmQ/2 run by the exact method, h, t, h and a Toffoli under two set controls, its energy as the host read it", []), "cli-wasmq2-feedback-rotation.json": ("the command line: a wasmQ/2 run whose callout turns a measured bit into three eighths of a turn, applied as its bits' phases", []), "cli-wasmq2-angle-arguments.json": ("the command line: a wasmQ/2 run with an angle[8] known on reading and an angle[4] of measured bits, given back by the module", []), "cli-wasmq2-rotations-and-a-reset.json": ("the command line: a wasmQ/2 run of a random reset and each kind of gate the exact method takes", []), "cli-wasmq2-stabilizer.json": ("the command line: a wasmQ/2 run of a Clifford program by the stabilizer method", []), "cli-wasmq2-exact-of-a-clifford-program.json": ("the command line: the same Clifford program by the exact method, the same counts", []), "cli-qudit-qutrit-bell.json": ("the command line: a qudit job (wai.quantum.qudit/1, quantum-sim section 9.7), a qutrit Bell pair by a Fourier gate and a controlled sum, its three amplitudes asked for as integers", []), "cli-qudit-ququarts-phase.json": ("the command line: two ququarts, a controlled sum to the third power, a phase on each level and a Fourier gate to the third power", []), "cli-qudit-six-levels.json": ("the command line: two qudits of six levels (F_2 x F_3), a Fourier gate, a shift, a controlled sum and a clock, two amplitudes asked for", []), "cli-qudit-qubits-ghz.json": ("the command line: a qudit job of dimension 2, three qubits in a GHZ state by a Fourier gate and two controlled sums", []), "service-wasmq-surface3-decode64.json": ("a job service built from this checkout (POST /v1/wasmq), sealed with its key: the surface-code round run as a job, the service's report around it, its own members unsealed", []), "service-wasmq2-t-toffoli.json": ("the same service: a wasmQ/2 run by the exact method, its energy what the service read of the worker that ran it", []), "service-wasmq-fault-fuel.json": ("the same service: a wasmQ run that stopped in a call past the call's fuel budget, the fault sealed in the job", []), "service-qudit-qutrit-bell.json": ("the same service (POST /v1/qudit): a qutrit Bell pair run as a job, its energy what the service read of its worker", []), "cli-time-now.json": ("the command line, asked to seal the time (WAI_QUANTUM_RECEIPT_TIME=now): its receipt seals this machine's clock at the seal (section 8.2)", []), "cli-time-stated.json": ("the command line, sealing a time its person states and an end an hour after it (WAI_QUANTUM_RECEIPT_TIME, WAI_QUANTUM_RECEIPT_VALID_MS)", []), "cli-time-set.json": ("the command line: a set of two programs sealed with the time, the set's receipt and each item's own", []), "service-time-run.json": ("a job service built from this checkout (POST /v1/jobs), sealed with its key: every receipt it seals states when it was issued, by its clock", []), } # Section 8.8: made receipts judged held to something, each (its file in jobs/, what it is # held to, the reasons, what it is); and the trust lists that were made (a job service's own # key set, as it published it), each with what it is and its SHA-256. MADE_HELD = [ ("service-time-run.json", {"trust": "trust/service-keys.json"}, [], "the service's receipt held to the key set that service published: its key is listed, signing from the start with no end"), ("service-time-run.json", {"trust": "trust/service-keys.json", "at_ms": 1794403403830}, [], "the same, held also to the moment it was issued"), ("service-time-run.json", {"trust": "trust/service-keys.json", "at_ms": 1794403403829}, ["issued_later"], "the same, held to a millisecond before it says it was issued"), ("service-time-run.json", {"trust": "trust/demo-open.json"}, ["key_untrusted"], "the service's receipt held to a list that names another key"), ("cli-time-stated.json", {"trust": "trust/demo-window.json", "at_ms": 1762865999999}, [], "the command line's receipt, issued at the first moment of its key's window, held to the last millisecond before the end it seals"), ("cli-time-stated.json", {"trust": "trust/demo-window.json", "at_ms": 1762866000000}, ["expired"], "the same, held to the end it seals"), ("cli-time-set.json", {"trust": "trust/demo-revoked.json"}, ["key_revoked"], "a set sealed long after its key was revoked: placed by its own receipt's time"), ("cli-time-set.json", {"trust": "trust/demo-open.json", "at_ms": 1794403402747}, [], "the set held to a key with no end, and to the moment it was issued"), ("cli-run-observables.json", {"trust": "trust/demo-window.json"}, ["key_window"], "a receipt made before receipts sealed a time, held to a key whose window has ended: it can be placed in none"), ("cli-run-observables.json", {"trust": "trust/demo-open.json", "at_ms": 0}, [], "the same receipt held to a key with no end, and to a time: it states none to hold to one"), ] MADE_TRUST = { "service-keys.json": ("the key set a job service built from this checkout published at /.well-known/wai-quantum-receipt-keys, as it gave it: the trust list for that service's receipts", "ee251d47fb21d2307e7978cf1fceb21f6b66a359fc55a31897a46a3ca58247b1"), } def manifest(crafted): entries = [] for name in sorted(MADE): what, reasons = MADE[name] entries.append({"file": "jobs/" + name, "reasons": reasons, "made_by": "made", "what": what}) for name, _, reasons, what in crafted: entries.append({"file": "jobs/" + name, "reasons": reasons, "made_by": "crafted", "what": what}) # Section 8.8: receipts judged held to a trust list or a time beside their seal. held = [{"file": "jobs/" + name, "held": to, "reasons": reasons, "made_by": "made", "what": what} for name, to, reasons, what in MADE_HELD] held += [{"file": "held/" + name, "held": to, "reasons": reasons, "made_by": "crafted", "what": what} for name, _, reasons, what, to in held_cases()] lists = [{"file": "trust/" + name, "reads": reads, "made_by": "crafted", "what": what} for name, _, reads, what in trust_lists()] lists += [{"file": "trust/" + name, "reads": True, "made_by": "made", "what": what} for name, (what, _) in sorted(MADE_TRUST.items())] return { "about": "Job receipts and the reasons a verifier must give for each (wai/extensions/quantum-providers.md section 8.4): valid when there are none. made: sealed by the command line, the job service, the provider clients or an earlier engine, kept as made. crafted: made by generate.py from those, each changed in one way and sealed again with the published demo key where the change is inside the seal. Every expectation is written by hand from section 8.4. held: receipts judged held to a trust list (a file of trust/) and a time as well, each when its `held` gives one, and the reasons a verifier must then give (section 8.8); a receipt of jobs/ may be named here too, held to something. trust_lists: every list of trust/, and whether a verifier reads it; one it does not read judges nothing.", "entries": entries, "held": held, "trust_lists": lists, } LOCK = os.path.join(HERE, "made.lock.json") def lock_of(): try: with open(LOCK, encoding="utf-8") as f: return json.load(f) except OSError: return {} def digest_of(name): with open(os.path.join(JOBS, name), "rb") as f: return hashlib.sha256(f.read()).hexdigest() def write_lock(lock): with open(LOCK, "w", encoding="utf-8") as f: f.write(json.dumps(lock, indent=2, sort_keys=True) + "\n") def check_made(): """A made receipt is kept: named in MADE, in jobs/, and the bytes pinned.""" lock, problems = lock_of(), [] for name in sorted(set(MADE) | set(lock)): if name not in MADE: problems.append("%s is pinned, and no longer in MADE: a made receipt is kept" % name) elif name not in lock: problems.append("%s is in MADE, and not pinned: generate.py --pin %s" % (name, name)) elif not os.path.exists(os.path.join(JOBS, name)): problems.append("%s is pinned, and gone from jobs/: a made receipt is kept" % name) elif digest_of(name) != lock[name]: problems.append("%s has changed since it was pinned: a made receipt is kept as it was made, and a new one takes a new name" % name) for name, (_, pinned) in sorted(MADE_TRUST.items()): try: with open(os.path.join(TRUST, name), "rb") as f: if hashlib.sha256(f.read()).hexdigest() != pinned: problems.append("trust/%s has changed since it was pinned: a made list is kept as it was made" % name) except OSError: problems.append("trust/%s is in MADE_TRUST, and gone from trust/" % name) on_disk = {n for n in os.listdir(JOBS) if n.endswith(".json") and not n.startswith("crafted-")} for name in sorted(on_disk - set(MADE)): problems.append("%s is in jobs/, and not in MADE" % name) return problems def pin(names): lock = lock_of() for name in names: if name in lock: sys.exit("generate.py --pin: %s is pinned already: a made receipt is kept as it was made" % name) if name not in MADE: sys.exit("generate.py --pin: %s is not in MADE" % name) lock[name] = digest_of(name) write_lock(lock) print("generate.py --pin: %d pinned, %d in all" % (len(names), len(lock))) def self_test(): """The pins can fail: a made receipt dropped, changed, or unnamed, in a copy.""" global JOBS, LOCK, MADE import shutil import tempfile jobs, lock, made = JOBS, LOCK, MADE failures = 0 with tempfile.TemporaryDirectory() as tmp: try: JOBS, LOCK = os.path.join(tmp, "jobs"), os.path.join(tmp, "made.lock.json") os.mkdir(JOBS) for name in made: shutil.copy(os.path.join(jobs, name), JOBS) shutil.copy(lock, LOCK) name = sorted(made)[0] def expect(what, want): nonlocal failures problems = check_made() held = any(want in p for p in problems) print(("PASS " if held else "FAIL ") + what) failures += not held if check_made(): print("FAIL the copy holds as it is: %s" % check_made()) failures += 1 with open(os.path.join(JOBS, name), "ab") as f: f.write(b" ") expect("a made receipt changed is found", "has changed") os.remove(os.path.join(JOBS, name)) expect("a made receipt dropped is found", "gone from jobs/") MADE = {k: v for k, v in made.items() if k != name} expect("a made receipt dropped from MADE too is found", "no longer in MADE") MADE = dict(made) shutil.copy(os.path.join(jobs, name), JOBS) with open(os.path.join(JOBS, "made-by-no-one.json"), "wb") as f: f.write(b"{}") expect("a receipt in jobs/ that MADE does not name is found", "not in MADE") os.remove(os.path.join(JOBS, "made-by-no-one.json")) MADE = dict(made, **{"new.json": ("a new one", [])}) expect("a name in MADE that is not pinned is found", "not pinned") finally: JOBS, LOCK, MADE = jobs, lock, made if failures: sys.exit(1) print("generate.py --self-test: the pins can fail, each way") def main(): if sys.argv[1:2] == ["--pin"]: return pin(sys.argv[2:]) if sys.argv[1:] == ["--self-test"]: return self_test() check = "--check" in sys.argv[1:] problems = check_made() if problems: for p in problems: print("generate.py: " + p) sys.exit(1) crafted = cases() want = {os.path.join(JOBS, name): (t if isinstance(t, bytes) else t.encode("utf-8")) for name, t, _, _ in crafted} want[os.path.join(HERE, "manifest.json")] = (json.dumps(manifest(crafted), indent=2, ensure_ascii=False) + "\n").encode("utf-8") os.makedirs(TRUST, exist_ok=True) os.makedirs(HELD_DIR, exist_ok=True) for name, t, _, _ in trust_lists(): want[os.path.join(TRUST, name)] = t.encode("utf-8") for name, t, _, _, _ in held_cases(): want[os.path.join(HELD_DIR, name)] = t.encode("utf-8") stale = [n for n in os.listdir(JOBS) if n.startswith("crafted-") and os.path.join(JOBS, n) not in want] stale_lists = [os.path.join(TRUST, n) for n in os.listdir(TRUST) if os.path.join(TRUST, n) not in want and n not in MADE_TRUST] stale_lists += [os.path.join(HELD_DIR, n) for n in os.listdir(HELD_DIR) if os.path.join(HELD_DIR, n) not in want] bad = [] for path, data in sorted(want.items()): if check: try: with open(path, "rb") as f: if f.read() != data: bad.append(path) except OSError: bad.append(path) else: with open(path, "wb") as f: f.write(data) if check: for p in bad + [os.path.join(JOBS, n) for n in stale] + stale_lists: print("generate.py --check: %s is not what generate.py writes" % os.path.relpath(p, HERE)) if bad or stale or stale_lists: sys.exit(1) print("generate.py --check: %d crafted receipts, %d held to a trust list or a time, %d trust lists and the manifest are as written" % (len(crafted), len(held_cases()), len(trust_lists()))) else: for n in stale: os.remove(os.path.join(JOBS, n)) for p in stale_lists: os.remove(p) print("generate.py: wrote %d crafted receipts, %d held to a trust list or a time, %d trust lists and the manifest" % (len(crafted), len(held_cases()), len(trust_lists()))) if __name__ == "__main__": main()