open-standards/wai/live-conformance/revenue_share_verify.py
Transaction Science 2108af0587 wai: revenue_share_verify.py, the revenue share's second implementation
Written from extensions/revenue-share.md, sharing no code with wai-rs: reads
every contract, run, marker and statement itself, verifies each ledger document
with receipt_ledger_verify.py, recomputes every attribution, line, division and
citation, and requires every mutated copy of the corpus and every weakened
verifier to fail. The text states the aired test of a run exactly; the module's
complex types are named.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 17:08:53 -04:00

893 lines
39 KiB
Python

#!/usr/bin/env python3
"""Independent verifier of the revenue-share corpus (extensions/revenue-share.md).
Written from the extension's text. It shares no code with wai-rs, which generates the corpus,
and never runs it. It checks Ed25519 signatures with ../claim-conformance/_ed25519.py, WAI's
strict rule (SPEC §7 *Signatures*) written from RFC 8032 with the standard library, and BLAKE3
with ../claim-conformance/_blake3.py. Each ledger document is verified by the ledger corpus's
own second implementation, receipt_ledger_verify.py (receipt-ledger §7), which states what each
source contributes. Everything else is here:
- the share contract (§3): reading, rules, splits, the operator's signature and each
countersignature, and when two contracts overlap;
- the program-as-run (§4): its JSON carriage, its signed bytes, its identity and when it
verifies;
- the impressions of the contract's carriers (§5.2), their prices, their instants (§5.3) and the
date-time form a marker states (§5.4);
- attribution (§6), the statement (§7): its reading, its lines, the largest-remainder division
and the citations; verification (§8), compared in §8.2's order.
For every vector it requires the outcome the corpus states; of the corpus, that every reason of
§11 and every code it reuses appears in it and that no other file is there. Then it mutates copies
of the corpus, one defect each, and requires every copy to fail; and it drops each of its own
checks in turn and requires the corpus to catch the weakened verifier.
python3 -I revenue_share_verify.py <live-conformance/share>
python3 -I revenue_share_verify.py --document <verification document>
Exit status 0 only if every vector verifies, every mutated copy fails and every weakened verifier
fails. Python 3.8 or later; standard library only.
"""
import copy
import datetime
import functools
import hashlib
import json
import os
import re
import struct
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
sys.path.insert(0, os.path.join(HERE, "..", "claim-conformance"))
import _blake3 as B3 # noqa: E402
import _ed25519 as ED # noqa: E402
import receipt_ledger_verify as RLV # noqa: E402
EXTENSION = os.path.join(HERE, "..", "extensions", "revenue-share.md")
CONTRACT_MAGIC, STATEMENT_MAGIC = b"WRC1", b"WRS1" # §3.1, §7.1
CONTRACT_DOMAIN = b"wai:share-contract\x01" # §3.3
ACCEPT_DOMAIN = b"wai:share-accept\x01" # §3.3
STATEMENT_DOMAIN = b"wai:share-statement\x01" # §7.5
CITES_DOMAIN = b"wai:share-cites\x01" # §7.4
ENTRY_DOMAIN = b"wai:video-channel-entry\x01" # §4.2
RUN_DOMAIN = b"wai:video-channel-receipt\x01" # §4.2
RUN_ID_DOMAIN = b"wai:video-channel-receipt-id\x01" # §4.2
LEAF, NODE = b"moq-jwp:merkle-leaf\x01", b"moq-jwp:merkle-node\x01" # jwp-receipts §2.2
WHOLE = 10 ** 6
U64, U32 = 2 ** 64 - 1, 2 ** 32 - 1
ZERO = bytes(32)
ROOM, STITCHER = 1, 2 # §3.1 carriers
PRECEDING, FOLLOWING, BETWEEN = 1, 2, 3 # §3.1 rule
PRESENTED, BEACONS, UNPLACED = 1, 2, 3 # §5.2 classes
CLASS_NAMES = {PRESENTED: "presented", BEACONS: "beacons", UNPLACED: "unplaced"}
AD_RECEIPT, CLAIM, POD_INPUT, STITCHED_CLAIM, RUN, INVOICE = 6, 7, 8, 9, 10, 11 # §7.4
KINDS = {"program": 1, "ad": 2, "filler": 3} # §4.2 entry kind tags
CLASS_TAG = {"ModelBased": 2, "Estimator": 3, "OnChipCounter": 4, "CalibratedInstrument": 5} # energy-measurement §2.7
EVIDENCE = {"ModelBased": [], "Estimator": [], "OnChipCounter": ["energy_uncertainty", "energy_counter_filtering"],
"CalibratedInstrument": ["energy_uncertainty", "energy_calibration_ref"]}
FILTERING = {"Unknown": 0, "Off": 1, "On": 2}
FLOOR = 288676 # live-ads §8.5 rule 5
WHITE_SPACE = set(map(chr, [0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x20, 0x85, 0xA0, 0x1680] + list(range(0x2000, 0x200B))
+ [0x2028, 0x2029, 0x202F, 0x205F, 0x3000]))
RUN_MEMBERS = ["channel_id", "schedule_root", "schedule", "window_start_ms", "window_end_ms", "aired", "work_schedule_ops",
"joules_micro", "energy_provenance", "grant", "parent_receipt_hash", "signer_pubkey", "signer_id", "sig"]
REUSED = ["truncated", "malformed", "unknown_version", "bad_key", "bad_signature", "outside_period", "line_unsourced",
"citation_mismatch"]
DATE = re.compile(rb"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})") # §5.4
EPOCH = datetime.date(1970, 1, 1).toordinal()
SKIP = set()
class Refuse(Exception):
def __init__(self, reason, at):
super().__init__(reason)
self.reason, self.at = reason, at
class NoRead(Exception):
pass
def sha(*parts):
h = hashlib.sha256()
for p in parts:
h.update(p)
return h.digest()
def b3(*parts):
return B3.blake3(b"".join(parts))
def be64(v):
return struct.pack(">Q", v)
def be32(v):
return struct.pack(">I", v)
@functools.lru_cache(maxsize=None)
def ed_verify(key, msg, sig):
"""WAI's strict rule: None, `bad_key` or `bad_signature`. A pure function, held once."""
return ED.verify(key, msg, sig)
class R:
"""A cursor over little-endian fields; running out is `truncated`."""
def __init__(self, b, p=0):
self.b, self.p = b, p
def take(self, n):
if self.p + n > len(self.b):
raise ValueError("truncated")
s = self.b[self.p:self.p + n]
self.p += n
return s
def u(self, n):
return int.from_bytes(self.take(n), "little")
def text(self):
return self.take(self.u(2))
def head(b, magic):
"""§3.4's first steps."""
if len(b) < 4:
return "truncated"
if b[:4] != magic:
return "malformed"
if len(b) < 5:
return "truncated"
if b[4] != 1:
return "unknown_version"
return None
def capitals(c):
return len(c) == 3 and all(0x41 <= x <= 0x5A for x in c)
def ascending(xs):
return all(a < b for a, b in zip(xs, xs[1:]))
def good_text(t):
"""UTF-8 of 1 to 1024 bytes (§3.4)."""
if not 1 <= len(t) <= 1024:
return False
try:
t.decode("utf-8")
except UnicodeDecodeError:
return False
return True
def unhex(v):
if not isinstance(v, str) or len(v) % 2 or any(c not in "0123456789abcdef" for c in v):
raise Refuse("malformed", [])
return bytes.fromhex(v)
# ---------------------------------------------------------------- §3 the contract
def read_contract(b):
"""§3.4: the contract held to its rules and its splits' sums, or Refuse (place relative)."""
bad = head(b, CONTRACT_MAGIC)
if bad:
raise Refuse(bad, [])
r = R(b, 5)
try:
c = {"operator": r.take(32), "start": r.u(8), "end": r.u(8), "currency": r.take(3), "channel": r.text(),
"channel_signer": r.take(32), "rule": r.u(1), "between": r.u(2)}
c["carriers"] = [(r.u(1), r.take(32)) for _ in range(r.u(2))]
c["owners"] = [r.take(32) for _ in range(r.u(1))]
c["splits"] = []
for _ in range(r.u(2)):
op = r.u(4)
c["splits"].append({"operator_ppm": op, "shares": [(r.u(1), r.u(4)) for _ in range(r.u(1))]})
c["assets"] = [(r.text(), r.u(2)) for _ in range(r.u(4))]
body_end = r.p
c["operator_sig"] = r.take(64)
c["countersigs"] = [r.take(64) for _ in c["owners"]]
except ValueError:
raise Refuse("truncated", [])
if r.p != len(b):
raise Refuse("malformed", [])
n = len(c["owners"])
rules = (c["start"] < c["end"] and capitals(c["currency"]) and c["operator"] != ZERO and c["channel_signer"] != ZERO
and c["operator"] not in c["owners"] and good_text(c["channel"]) and 1 <= c["rule"] <= 3
and c["between"] < len(c["splits"])
and c["carriers"] and ascending(c["carriers"]) and all(k in (ROOM, STITCHER) and i != ZERO for k, i in c["carriers"])
and n >= 1 and ascending(c["owners"]) and all(o != ZERO for o in c["owners"])
and c["splits"]
and all(ascending([i for i, _ in s["shares"]]) and all(i < n and p > 0 for i, p in s["shares"]) for s in c["splits"])
and all(any(i == o for s in c["splits"] for i, _ in s["shares"]) for o in range(n))
and c["assets"] and ascending([a for a, _ in c["assets"]])
and all(good_text(a) and s < len(c["splits"]) for a, s in c["assets"]))
if not rules and "contract-rules" not in SKIP:
raise Refuse("contract_invalid", [])
if any(s["operator_ppm"] + sum(p for _, p in s["shares"]) != WHOLE for s in c["splits"]) and "split-sums" not in SKIP:
raise Refuse("split_not_whole", [])
c["body"] = b[:body_end]
c["digest"] = sha(c["body"])
return c
def in_force(b):
"""§3.3, §3.4: read, then the operator's signature, then each countersignature in turn."""
c = read_contract(b)
if "operator-signature" not in SKIP:
bad = ed_verify(c["operator"], CONTRACT_DOMAIN + c["body"], c["operator_sig"])
if bad:
raise Refuse(bad, ["operator"])
if "countersignatures" not in SKIP:
for i, (k, s) in enumerate(zip(c["owners"], c["countersigs"])):
if s == bytes(64):
raise Refuse("not_countersigned", ["owner", i])
bad = ed_verify(k, ACCEPT_DOMAIN + c["body"], s)
if bad:
raise Refuse(bad, ["owner", i])
return c
def contract_at(b, at):
try:
return in_force(b)
except Refuse as e:
raise Refuse(e.reason, at + e.at)
def overlaps(a, b):
"""§3.5."""
meet = a["start"] < b["end"] and b["start"] < a["end"]
return meet and (a["channel"] == b["channel"] or any(x in b["carriers"] for x in a["carriers"]))
def parties(c, split):
"""§3.2: a split's parties in their order: its owners as it lists them, then the operator with a share."""
s = c["splits"][split]
out = [(c["owners"][i], p) for i, p in s["shares"]]
if s["operator_ppm"] > 0:
out.append((c["operator"], s["operator_ppm"]))
return out
def split_of(c, asset):
for a, s in c["assets"]:
if a == asset:
return s
return None
# ---------------------------------------------------------------- §4 the program-as-run
def num(v, top=U64):
if not isinstance(v, int) or isinstance(v, bool) or not 0 <= v <= top:
raise NoRead()
return v
def hexn(v, n):
if not isinstance(v, str) or len(v) != 2 * n or any(c not in "0123456789abcdef" for c in v):
raise NoRead()
return bytes.fromhex(v)
def text(v):
if not isinstance(v, str):
raise NoRead()
return v
def read_run(o):
"""§4.1: the run, or NoRead."""
if not isinstance(o, dict):
raise NoRead()
label = o.get("energy_provenance")
if label is not None and label not in EVIDENCE:
raise NoRead()
if set(o) != set(RUN_MEMBERS) | set(EVIDENCE.get(label, [])):
raise NoRead()
r = {"channel_id": text(o["channel_id"]), "schedule_root": hexn(o["schedule_root"], 32),
"window_start_ms": num(o["window_start_ms"]), "window_end_ms": num(o["window_end_ms"]),
"work_schedule_ops": num(o["work_schedule_ops"]), "joules_micro": num(o["joules_micro"]),
"signer_pubkey": hexn(o["signer_pubkey"], 32), "signer_id": text(o["signer_id"]), "sig": hexn(o["sig"], 64)}
if not isinstance(o["schedule"], list) or not isinstance(o["aired"], list):
raise NoRead()
r["schedule"] = []
for e in o["schedule"]:
if not isinstance(e, dict) or set(e) != {"asset_id", "start_ms", "duration_ms", "kind"} or e.get("kind") not in KINDS:
raise NoRead()
x = {"asset_id": text(e["asset_id"]), "start_ms": num(e["start_ms"]), "duration_ms": num(e["duration_ms"]),
"kind": e["kind"]}
if x["start_ms"] + x["duration_ms"] > U64:
raise NoRead()
r["schedule"].append(x)
r["aired"] = [num(a) for a in o["aired"]]
g = o["grant"]
if not isinstance(g, dict) or set(g) != {"grant_hash", "capability", "joule_ceiling_micro", "funds_ceiling"}:
raise NoRead()
r["grant"] = {"grant_hash": hexn(g["grant_hash"], 32), "capability": text(g["capability"]),
"joule_ceiling_micro": num(g["joule_ceiling_micro"]),
"funds_ceiling": None if g["funds_ceiling"] is None else num(g["funds_ceiling"])}
r["parent"] = None if o["parent_receipt_hash"] is None else hexn(o["parent_receipt_hash"], 32)
r["class"] = None
if label is not None:
c = {"label": label}
if label in ("OnChipCounter", "CalibratedInstrument"):
u = o["energy_uncertainty"]
if not isinstance(u, dict) or set(u) != {"relative_ppm", "window_us"}:
raise NoRead()
c["ppm"], c["window"] = num(u["relative_ppm"], U32), num(u["window_us"])
if label == "OnChipCounter":
if o["energy_counter_filtering"] not in FILTERING:
raise NoRead()
c["filtering"] = FILTERING[o["energy_counter_filtering"]]
if label == "CalibratedInstrument":
c["ref"] = text(o["energy_calibration_ref"])
r["class"] = c
return r
def leaf(e):
a = e["asset_id"].encode()
return b3(ENTRY_DOMAIN, be32(len(a)), a, be64(e["start_ms"]), be64(e["duration_ms"]), bytes([KINDS[e["kind"]]]))
def merkle(hashes):
"""jwp-receipts §2.2, each leaf of its first form; zero for none."""
if not hashes:
return ZERO
level = [b3(LEAF, h) for h in hashes]
while len(level) > 1:
if len(level) % 2:
level.append(level[-1])
level = [b3(NODE, level[k], level[k + 1]) for k in range(0, len(level), 2)]
return level[0]
def class_bytes(c):
"""energy-measurement §2.7's signed encoding; tag 0 for none."""
if c is None:
return b"\x00"
out = bytes([CLASS_TAG[c["label"]]])
if c["label"] == "OnChipCounter":
out += be32(c["ppm"]) + be64(c["window"]) + bytes([c["filtering"]])
elif c["label"] == "CalibratedInstrument":
ref = c["ref"].encode()
out += be32(c["ppm"]) + be64(c["window"]) + be32(len(ref)) + ref
return out
def run_payload(r):
"""§4.2: the signing payload."""
g = r["grant"]
cid, cap, sid = r["channel_id"].encode(), g["capability"].encode(), r["signer_id"].encode()
out = [RUN_DOMAIN, be32(len(cid)), cid, r["schedule_root"], be64(r["window_start_ms"]), be64(r["window_end_ms"]),
be64(r["work_schedule_ops"]), be64(r["joules_micro"]), class_bytes(r["class"]), g["grant_hash"], be32(len(cap)),
cap, be64(g["joule_ceiling_micro"])]
out.append(b"\x00" if g["funds_ceiling"] is None else b"\x01" + be64(g["funds_ceiling"]))
out.append(b"\x00" if r["parent"] is None else b"\x01" + r["parent"])
out.append(be64(len(r["aired"])))
out += [be64(a) for a in r["aired"]]
out.append(be64(len(r["schedule"])))
out += [leaf(e) for e in r["schedule"]]
out += [r["signer_pubkey"], be32(len(sid)), sid]
return b"".join(out)
def run_identity(r):
return b3(RUN_ID_DOMAIN, run_payload(r), r["sig"])
def coheres(j, c):
"""live-ads §8.5 rule 5: the figure and its class stand or fall together."""
if c is None:
return j == 0
if j == 0:
return False
if c["label"] in ("OnChipCounter", "CalibratedInstrument"):
if c["ppm"] == 0 or c["window"] == 0 or j * c["ppm"] < FLOOR:
return False
if c["label"] == "CalibratedInstrument" and all(ch in WHITE_SPACE for ch in c["ref"]):
return False
return True
def run_verifies(r):
"""§4.3."""
s = r["schedule"]
if merkle([leaf(e) for e in s]) != r["schedule_root"] or r["work_schedule_ops"] != len(s):
return False
if r["window_start_ms"] >= r["window_end_ms"]:
return False
if any(a["start_ms"] + a["duration_ms"] != b["start_ms"] for a, b in zip(s, s[1:])):
return False
touch = [k for k, e in enumerate(s) if e["start_ms"] < r["window_end_ms"] and e["start_ms"] + e["duration_ms"] > r["window_start_ms"]]
if r["aired"] != touch:
return False
if not coheres(r["joules_micro"], r["class"]):
return False
return ed_verify(r["signer_pubkey"], run_payload(r), r["sig"]) is None
# ---------------------------------------------------------------- §5 the instants
def instant_of(s):
"""§5.4: the microseconds since 1970 a START-DATE states, or None."""
m = DATE.fullmatch(s)
if not m:
return None
y, mo, d, h, mi, sec = (int(x) for x in m.groups()[:6])
frac, zone = m.group(7), m.group(8)
if h > 23 or mi > 59 or sec > 59:
return None
try:
days = datetime.date(y, mo, d).toordinal() - EPOCH
except ValueError:
return None
off = 0
if zone != b"Z":
oh, om = int(zone[1:3]), int(zone[4:6])
if oh > 23 or om > 59:
return None
off = (oh * 3600 + om * 60) * (1 if zone[:1] == b"+" else -1)
secs = days * 86400 + h * 3600 + mi * 60 + sec - off
if secs < 0:
return None
us = int((frac or b"").ljust(6, b"0")[:6] or b"0")
return secs * 10 ** 6 + us
def marker_instant(claim):
"""A stitched claim's marker (stitched-presentation §5.1, its long field at offset 125): the
instant its START-DATE states, for carriage 1 or 2 (§5.4); None otherwise."""
carriage = claim[5]
if carriage not in (1, 2):
return None
r = R(claim, 125)
try:
body = r.text()
b = R(body)
b.text()
start_date = b.text()
except ValueError:
return None
return instant_of(start_date)
# ---------------------------------------------------------------- §7 the statement
def read_statement(b):
"""§7.6: the statement held to its rules, or Refuse at `statement`."""
at = ["statement"]
bad = head(b, STATEMENT_MAGIC)
if bad:
raise Refuse(bad, at)
r = R(b, 5)
try:
s = {"contract": r.take(32), "party": r.take(32), "start": r.u(8), "end": r.u(8), "currency": r.take(3)}
s["invoices"] = [r.take(32) for _ in range(r.u(2))]
s["runs"] = [r.take(32) for _ in range(r.u(2))]
s["lines"] = []
for _ in range(r.u(2)):
s["lines"].append({"split": r.u(2), "class": r.u(1), "impressions": r.u(8), "revenue": r.u(8), "ppm": r.u(4),
"amount": r.u(8), "cited": r.u(4), "cites": r.take(32)})
s["total"] = r.u(8)
s["sig"] = r.take(64)
except ValueError:
raise Refuse("truncated", at)
if r.p != len(b):
raise Refuse("malformed", at)
rules = (s["start"] < s["end"] and capitals(s["currency"]) and s["party"] != ZERO and ascending(s["invoices"])
and ascending(s["runs"]) and ascending([(x["split"], x["class"]) for x in s["lines"]])
and all(1 <= x["class"] <= 3 and x["impressions"] > 0 and x["cited"] > 0 and 1 <= x["ppm"] <= WHOLE
and x["amount"] <= x["revenue"] for x in s["lines"]))
if not rules and "statement-rules" not in SKIP:
raise Refuse("share_statement_invalid", at)
s["body"] = b[:-64]
s["digest"] = sha(s["body"])
return s
def divide(revenue, ppms):
"""§7.3: the largest-remainder rule. Ties go to the earlier party."""
quota = [revenue * p for p in ppms]
parts = [q // WHOLE for q in quota]
left = revenue - sum(parts)
order = sorted(range(len(ppms)), key=lambda i: (-(quota[i] % WHOLE), i))
for i in order[:left]:
parts[i] += 1
return parts
def cites_digest(cited):
return sha(CITES_DOMAIN, *[bytes([k]) + i for k, i in sorted(cited)])
# ---------------------------------------------------------------- §8 verification
_LEDGERS = {}
def ledger_outcome(d):
"""receipt-ledger §7: a ledger document's outcome, by the ledger corpus's own verifier, and, when
it is accepted, its tariff, its invoice and what each source states (receipt-ledger §5.2). A
pure function of the document: each is verified once."""
key = json.dumps(d, sort_keys=True)
if key not in _LEDGERS:
got, facts = RLV.verify(d), None
if got["outcome"] == "accept":
t = RLV.read_tariff(bytes.fromhex(d["tariff"]))
inv = RLV.read_invoice(bytes.fromhex(d["invoice"]), ["invoice"])
signers = [i for k, i in t["scope"] if k == 4]
facts = (t, inv, [RLV.verify_source(s, signers) for s in d["sources"]])
_LEDGERS[key] = (got, facts)
return _LEDGERS[key]
def attributed(c, start, end, runs, ledgers, docs):
"""§8.2 step 6: every impression of the contract's carriers, placed and attributed (§5, §6)."""
ids = [run_identity(r) for r in runs]
out = []
for k, (t, inv, facts) in enumerate(ledgers):
prices = {PRESENTED: RLV.price(t, 4, 1), UNPLACED: RLV.price(t, 4, 1), BEACONS: RLV.price(t, 4, 2)}
for j, f in enumerate(facts):
at = ["ledger", k, "source", j]
found = []
if f["kind"] == RLV.ADS and (ROOM, f["room"]) in c["carriers"]:
for rc in f["receipts"]:
for i in rc["impressions"]:
if not i["counts"]:
continue
recs = [(AD_RECEIPT, rc["identity"])]
if f["claims_required"]:
if i["claim"] is not None:
recs.append((CLAIM, i["claim"]))
cls = PRESENTED
else:
cls = BEACONS
found.append((recs, cls, True, f["t0"] + rc["t"]))
elif f["kind"] == RLV.STITCHED:
given = [bytes.fromhex(x) for x in docs[k]["sources"][j]["claims"]]
digests = [sha(g[:-64]) if len(g) >= 64 else None for g in given]
for rec in f["records"]:
if (STITCHER, rec["signer"]) not in c["carriers"]:
continue
for _, claims in rec["ads"]:
for i, d in sorted((digests.index(d), d) for d in claims if d in digests):
inst = marker_instant(given[i])
found.append(([(POD_INPUT, rec["digest"]), (STITCHED_CLAIM, d)],
PRESENTED if inst is not None else UNPLACED, inst is not None, inst))
for recs, cls, placed, inst in found:
imp = {"ledger": k, "source": j, "records": recs, "class": cls, "price": prices[cls], "invoice": inv["digest"],
"at": None, "run": None, "entry": None, "asset": None, "split": c["between"]}
if placed:
if not (start <= inst < end and inst <= U64) and "impression-period" not in SKIP:
raise Refuse("outside_period", at)
holds = [n for n, r in enumerate(runs) if r["window_start_ms"] * 1000 <= inst < r["window_end_ms"] * 1000]
entry = None
if holds:
sched = runs[holds[0]]["schedule"]
es = [e for e, x in enumerate(sched) if x["start_ms"] * 1000 <= inst < (x["start_ms"] + x["duration_ms"]) * 1000]
entry = es[0] if es else None
if entry is None:
if "window" not in SKIP:
raise Refuse("outside_window", at)
out.append(imp)
continue
asset = asset_of(sched, entry, c["rule"])
if asset is not None:
split = split_of(c, asset.encode())
if split is None:
if "unmapped" not in SKIP:
raise Refuse("asset_unmapped", at)
split = c["between"]
imp["split"] = split
imp.update({"at": inst, "run": ids[holds[0]], "entry": entry, "asset": asset})
out.append(imp)
return out
def asset_of(sched, e, rule):
"""§6.2: a programme's own asset, or by the rule; None for none."""
if sched[e]["kind"] == "program":
return sched[e]["asset_id"]
if rule == PRECEDING:
before = [x for x in sched[:e] if x["kind"] == "program"]
return before[-1]["asset_id"] if before else None
if rule == FOLLOWING:
after = [x for x in sched[e + 1:] if x["kind"] == "program"]
return after[0]["asset_id"] if after else None
return None
def groups_of(imps):
g = {}
for i in imps:
e = g.setdefault((i["split"], i["class"]), {"impressions": 0, "revenue": 0, "cited": set()})
e["impressions"] += 1
e["revenue"] += i["price"]
e["cited"].update(i["records"])
if i["run"] is not None:
e["cited"].add((RUN, i["run"]))
e["cited"].add((INVOICE, i["invoice"]))
return g
def lines_of(c, party, groups):
"""§7.2 to §7.4: the party's lines, each with its cited records and its split's division; None
for a figure past its field."""
out = []
for split, cls in sorted(groups):
g = groups[(split, cls)]
ps = parties(c, split)
mine = [n for n, (p, _) in enumerate(ps) if p == party]
if not mine:
continue
if g["revenue"] > U64 or len(g["cited"]) > U32:
return None
parts = divide(g["revenue"], [p for _, p in ps])
me = mine[0]
out.append({"split": split, "class": cls, "impressions": g["impressions"], "revenue": g["revenue"], "ppm": ps[me][1],
"amount": parts[me], "cited": len(g["cited"]), "cites": cites_digest(g["cited"]),
"cited_records": sorted(g["cited"]), "division": [(p, s, a) for (p, s), a in zip(ps, parts)]})
return out
def verify(doc):
"""§8.2: the outcome of a verification document."""
try:
if not isinstance(doc, dict) or set(doc) != {"contract", "contracts", "statement", "runs", "ledgers"}:
raise Refuse("malformed", [])
if not isinstance(doc["contracts"], list) or not isinstance(doc["runs"], list) or not isinstance(doc["ledgers"], list):
raise Refuse("malformed", [])
cb, sb = unhex(doc["contract"]), unhex(doc["statement"])
others = [unhex(x) for x in doc["contracts"]]
c = contract_at(cb, ["contract"]) # step 1
for k, ob in enumerate(others): # step 2
o = contract_at(ob, ["contracts", k])
if o["digest"] == c["digest"]:
continue
if overlaps(o, c) and "overlap" not in SKIP:
raise Refuse("contract_overlap", ["contracts", k])
s = read_statement(sb) # step 3
if "binding" not in SKIP and (s["contract"] != c["digest"] or s["currency"] != c["currency"]
or (s["party"] != c["operator"] and s["party"] not in c["owners"])):
raise Refuse("contract_mismatch", ["statement"])
if "contract-period" not in SKIP and not (c["start"] <= s["start"] and s["end"] <= c["end"]):
raise Refuse("outside_contract", ["statement"])
if "statement-signature" not in SKIP:
bad = ed_verify(c["operator"], STATEMENT_DOMAIN + s["body"], s["sig"])
if bad:
raise Refuse(bad, ["statement"])
runs = [] # step 4
for k, o in enumerate(doc["runs"]):
try:
r = read_run(o)
except NoRead:
raise Refuse("malformed", ["run", k])
if "run-verify" not in SKIP and not run_verifies(r):
raise Refuse("run_invalid", ["run", k])
if "run-foreign" not in SKIP and (r["channel_id"].encode() != c["channel"] or r["signer_pubkey"] != c["channel_signer"]):
raise Refuse("run_foreign", ["run", k])
if "run-overlap" not in SKIP and any(r["window_start_ms"] < x["window_end_ms"] and x["window_start_ms"] < r["window_end_ms"]
for x in runs):
raise Refuse("run_overlap", ["run", k])
runs.append(r)
ledgers, seen, breaks = [], set(), set() # step 5
for k, d in enumerate(doc["ledgers"]):
if not isinstance(d, dict):
raise Refuse("malformed", ["ledger", k, "tariff"])
got, held = ledger_outcome(d)
if got["outcome"] != "accept":
raise Refuse(got["reason"], ["ledger", k] + got["at"])
t, inv, facts = held
if "invoice-foreign" not in SKIP and (inv["seller"] != c["operator"] or inv["currency"] != c["currency"]):
raise Refuse("invoice_foreign", ["ledger", k])
if "ledger-period" not in SKIP and not (s["start"] <= inv["start"] and inv["end"] <= s["end"]):
raise Refuse("outside_period", ["ledger", k])
records = {x for x in inv["records"] if 6 <= x[0] <= 9}
these = {(f["room"], rc["cue"], rc["session"]) for f in facts if f["kind"] == RLV.ADS for rc in f["receipts"]}
if "duplicate" not in SKIP and (records & seen or these & breaks):
raise Refuse("impression_duplicate", ["ledger", k])
seen |= records
breaks |= these
ledgers.append((t, inv, facts))
imps = attributed(c, s["start"], s["end"], runs, ledgers, doc["ledgers"]) # step 6
groups = groups_of(imps) # step 7
for x in s["lines"]:
g = groups.get((x["split"], x["class"]))
holds = any(p == s["party"] for p, _ in parties(c, x["split"])) if x["split"] < len(c["splits"]) else False
if "unsourced" not in SKIP and (g is None or not holds or x["impressions"] > g["impressions"]):
raise Refuse("line_unsourced", ["statement"])
lines = lines_of(c, s["party"], groups)
if lines is None:
raise Refuse("share_statement_mismatch", ["statement"])
for x in s["lines"]:
mine = [y for y in lines if (y["split"], y["class"]) == (x["split"], x["class"])]
if mine and "citations" not in SKIP and (x["cited"] != mine[0]["cited"] or x["cites"] != mine[0]["cites"]):
raise Refuse("citation_mismatch", ["statement"])
total = sum(y["amount"] for y in lines)
same = (s["invoices"] == sorted(inv["digest"] for _, inv, _ in ledgers)
and s["runs"] == sorted(run_identity(r) for r in runs)
and [{k: x[k] for k in ("split", "class", "impressions", "revenue", "ppm", "amount", "cited", "cites")} for x in s["lines"]]
== [{k: y[k] for k in ("split", "class", "impressions", "revenue", "ppm", "amount", "cited", "cites")} for y in lines]
and s["total"] == total and total <= U64)
if not same and "mismatch" not in SKIP:
raise Refuse("share_statement_mismatch", ["statement"])
except Refuse as e:
return {"outcome": "refuse", "reason": e.reason, "at": e.at}
return accepted(c, s, imps, lines)
def accepted(c, s, imps, lines):
"""§8.3."""
return {
"outcome": "accept",
"statement": s["digest"].hex(),
"contract": s["contract"].hex(),
"party": s["party"].hex(),
"role": "operator" if s["party"] == c["operator"] else "owner",
"currency": s["currency"].decode("ascii"),
"start": s["start"],
"end": s["end"],
"invoices": [d.hex() for d in s["invoices"]],
"runs": [d.hex() for d in s["runs"]],
"impressions": [{"ledger": i["ledger"], "source": i["source"], "records": [[k, d.hex()] for k, d in i["records"]],
"class": CLASS_NAMES[i["class"]], "price": i["price"], "at": i["at"],
"run": None if i["run"] is None else i["run"].hex(), "entry": i["entry"], "asset": i["asset"],
"split": i["split"]} for i in imps],
"lines": [{"split": y["split"], "class": CLASS_NAMES[y["class"]], "impressions": y["impressions"], "revenue": y["revenue"],
"ppm": y["ppm"], "amount": y["amount"], "cites": [[k, d.hex()] for k, d in y["cited_records"]],
"division": [{"party": p.hex(), "ppm": pp, "part": a} for p, pp, a in y["division"]]} for y in lines],
"total": s["total"],
}
# ---------------------------------------------------------------- the corpus
def document(doc, v):
"""A vector as a verification document (§8.1), its parts resolved by name."""
def ledger(n):
x = doc["ledgers"][n]
return {"tariff": x["tariff"], "invoice": x["invoice"], "previous": x["previous"],
"sources": [doc["sources"][s] for s in x["sources"]]}
return {"contract": doc["contracts"][v["contract"]], "contracts": [doc["contracts"][n] for n in v["contracts"]],
"statement": v["statement"], "runs": None if v["runs"] is None else [doc["runs"][n] for n in v["runs"]],
"ledgers": [ledger(n) for n in v["ledgers"]]}
def check(doc, quiet=False):
"""The failures: every vector whose stated outcome is not the one this verifier reaches."""
fails = []
for v in doc.get("vectors", []):
try:
got = verify(document(doc, v))
except Exception as e: # a crash on a mutated copy is a failure to verify it
got = {"outcome": "crash", "error": "%s: %s" % (type(e).__name__, e)}
ok = got == v.get("outcome")
if not quiet:
what = got["outcome"] + (" %s at %s" % (got["reason"], got["at"]) if got["outcome"] == "refuse" else "")
print(("PASS" if ok else "FAIL") + " %s: %s" % (v.get("name"), what))
if not ok:
fails.append(v.get("name"))
return fails
def reasons_listed():
"""§11's table, in order."""
text_ = open(EXTENSION, encoding="utf-8").read()
at = text_.index("\n## 11. Reasons")
end = text_.find("\n## ", at + 1)
return [m.group(1) for m in re.finditer(r"^\| `([a-z_]+)` \|", text_[at:end], re.M)]
def flip_hex(h, at):
b = bytearray(bytes.fromhex(h))
b[at % len(b)] ^= 0x01
return b.hex()
def mutations(doc):
"""Copies of the corpus, one defect each, that a verifier must reject."""
out = []
accepted_ = [(k, v) for k, v in enumerate(doc["vectors"]) if v["outcome"]["outcome"] == "accept" and v["outcome"]["lines"]]
for k, v in accepted_:
for at in (40, 70, 90, -100, -70, -1):
m = copy.deepcopy(doc)
m["vectors"][k]["statement"] = flip_hex(v["statement"], at)
out.append(("%s: statement byte %d flipped" % (v["name"], at), m))
m = copy.deepcopy(doc)
m["vectors"][k]["runs"] = v["runs"][:-1]
out.append(("%s: its last run dropped" % v["name"], m))
m = copy.deepcopy(doc)
m["vectors"][k]["ledgers"] = v["ledgers"][:-1]
out.append(("%s: its last ledger document dropped" % v["name"], m))
m = copy.deepcopy(doc)
m["vectors"][k]["outcome"]["total"] += 1
out.append(("%s: its stated total raised" % v["name"], m))
m = copy.deepcopy(doc)
m["vectors"][k]["outcome"]["lines"][0]["division"].pop()
out.append(("%s: its first line's division cut short" % v["name"], m))
m = copy.deepcopy(doc)
m["vectors"][k]["outcome"]["impressions"][0]["asset"] = "a/ep-103"
out.append(("%s: its first impression moved to another asset" % v["name"], m))
for name, at in (("preceding", 60), ("preceding", -1), ("preceding", -130)):
m = copy.deepcopy(doc)
m["contracts"][name] = flip_hex(doc["contracts"][name], at)
out.append(("the contract %s: byte %d flipped" % (name, at), m))
m = copy.deepcopy(doc)
m["runs"]["oct_25"]["schedule"][0]["asset_id"] = "b/doc-8"
out.append(("the run of the 25th: its first programme renamed", m))
m = copy.deepcopy(doc)
m["runs"]["oct_16"]["sig"] = flip_hex(doc["runs"]["oct_16"]["sig"], 3)
out.append(("the run of the 16th: its signature flipped", m))
for k, v in enumerate(doc["vectors"]):
if v["outcome"]["outcome"] == "refuse":
m = copy.deepcopy(doc)
m["vectors"][k]["outcome"]["reason"] = "share_statement_mismatch" if v["outcome"]["reason"] != "share_statement_mismatch" else "malformed"
out.append(("%s: its stated reason changed" % v["name"], m))
break
return out
CHECKS = ["contract-rules", "split-sums", "operator-signature", "countersignatures", "overlap", "statement-rules", "binding",
"contract-period", "statement-signature", "run-verify", "run-foreign", "run-overlap", "invoice-foreign",
"ledger-period", "duplicate", "impression-period", "window", "unmapped", "unsourced", "citations", "mismatch"]
def main(corpus_dir):
doc = json.load(open(os.path.join(corpus_dir, "vectors.json"), encoding="utf-8"))
fails = check(doc)
files = sorted(os.listdir(corpus_dir))
if files != ["README.md", "vectors.json"]:
fails.append("the corpus holds other files: %s" % files)
seen = {v["outcome"].get("reason") for v in doc["vectors"]}
listed = reasons_listed()
missing = [r for r in listed + REUSED if r not in seen]
if missing:
fails.append("no vector is refused %s" % missing)
print("%s every reason of §11 and every reused code refuses a vector (%d listed)" % ("PASS" if not missing else "FAIL", len(listed)))
muts = mutations(doc)
weak = [name for name, m in muts if not check(m, quiet=True)]
print("%s every mutated copy of the corpus fails (%d copies)" % ("PASS" if not weak else "FAIL", len(muts)))
fails += ["the mutated copy %r verifies" % w for w in weak]
undetected = []
for c in CHECKS:
SKIP.add(c)
try:
if not check(doc, quiet=True):
undetected.append(c)
finally:
SKIP.discard(c)
print("%s the corpus catches the verifier with each of its checks dropped (%d checks)" % ("PASS" if not undetected else "FAIL", len(CHECKS)))
fails += ["dropping the check %r goes unnoticed" % c for c in undetected]
for f in fails:
print("FAIL", f)
return 1 if fails else 0
def check_document(path):
"""One verification document (§8.1): its outcome, printed. Status 0 only when it is accepted."""
doc = json.load(open(path, encoding="utf-8"))
got = verify(doc)
print(json.dumps(got, indent=1))
return 0 if got["outcome"] == "accept" else 1
if __name__ == "__main__":
if len(sys.argv) == 3 and sys.argv[1] == "--document":
sys.exit(check_document(sys.argv[2]))
if len(sys.argv) != 2:
print("usage: revenue_share_verify.py <live-conformance/share> | --document FILE", file=sys.stderr)
sys.exit(2)
sys.exit(main(sys.argv[1]))