#!/usr/bin/env python3
"""
Independent runner for https://aere.network/verifiable-claims.json

Runs the checkable claims in that file against the live Aere Network endpoints and
prints PASS or FAIL for each. It trusts nothing in the file except the addresses and
the expected values, which is the point: if Aere is lying, this script says FAIL.

Revision 2 of this script (2026-08-05) carried typed numbers for the things that
move (validator count, seal count, block interval, transaction pool) and went stale
in silence: on 2026-09-05 it failed four of its own checks against a healthy chain.
Since revision 3 those expectations are READ FROM THE CLAIMS FILE ITSELF, so the
file and this script cannot drift apart, and every one of them is a value the
claims file derives from the chain, not a number anyone typed. Everything fixed by
a standard (EVM gas costs, NIST vectors, EIP-1967 slots) stays fixed here.

  python verifiable-claims-check.py

No wallet, no key, no gas, no account. All calls are read-only.
Requires only the Python standard library.

Since revision 4 (2026-09-25) the header check judges the FORM of every header at
its height: before AIP-22 stage 5 a header carries classical ECDSA committed seals;
from the stage-5 height the claims file publishes (`aip22Stage5From`, null while the
stage is not scheduled) it carries none, and one post-quantum proposer seal instead.
Options, for running the header section against another chain or a pinned head:
  --rpc URL        endpoint to read (repeat for a second one; default: Aere mainnet)
  --claims SRC     claims file, a path or a URL (default: the published one)
  --only header    run section 0 (the claims file) and section 3 (headers) only
  --head N         judge the window ending at block N instead of the chain head
"""
import json, urllib.request, hashlib, math, os, re, sys

ENDPOINTS = ["https://rpc.aere.network", "https://rpc2.aere.network"]
CLAIMS_URL = "https://aere.network/verifiable-claims.json"
UA = "aere-claims-check/4"          # NOTE: default Python-urllib UA is 403'd by the CDN

def _opt(name):
    return [sys.argv[i+1] for i, a in enumerate(sys.argv[:-1]) if a == name]
if _opt("--rpc"):
    ENDPOINTS = _opt("--rpc")
ONLY = (_opt("--only") or [None])[0]
HEAD_AT = int(_opt("--head")[0]) if _opt("--head") else None
if ONLY not in (None, "header"):
    raise SystemExit("--only takes one value: header")
PASS, FAIL = [], []

def rpc(method, params, url=None):
    url = url or ENDPOINTS[0]
    body = json.dumps({"jsonrpc": "2.0", "method": method, "params": params, "id": 1}).encode()
    req = urllib.request.Request(url, data=body,
                                 headers={"content-type": "application/json", "User-Agent": UA})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.loads(r.read())

def check(name, got, want, note=""):
    ok = (got == want)
    (PASS if ok else FAIL).append(name)
    print(f"  [{'PASS' if ok else 'FAIL'}] {name}")
    if not ok:
        print(f"         expected {want!r}")
        print(f"         got      {got!r}")
    elif note:
        print(f"         {note}")
    return ok

# ------------------------------------------------------------ 0. the claims file
# Fetched with a cache-busting parameter so the CDN cannot hand back yesterday's copy.
print("\n0. The claims file this script checks against")
SRC = (_opt("--claims") or [CLAIMS_URL])[0]
if SRC.startswith("http://") or SRC.startswith("https://"):
    req = urllib.request.Request(f"{SRC}{'&' if '?' in SRC else '?'}o={os.getpid()}", headers={"User-Agent": UA})
    with urllib.request.urlopen(req, timeout=30) as r:
        CLAIMS = json.loads(r.read())
else:
    with open(SRC, "rb") as fh:
        CLAIMS = json.loads(fh.read())
def claim(cid):
    for c in CLAIMS.get("claims", []) + CLAIMS.get("notClaimed", []):
        if c.get("id") == cid:
            return c
    raise SystemExit(f"claims file has no entry '{cid}'; this script and the file are out of step")
check("claims file parses and names its revision", isinstance(CLAIMS.get("revision"), int), True,
      f"revision {CLAIMS.get('revision')}, generated {CLAIMS.get('generatedAt')}, "
      f"{len(CLAIMS.get('claims', []))} claims, {len(CLAIMS.get('notClaimed', []))} things not claimed")

# ---------------------------------------------------------------- RLP (headers)
def rlp(b, i=0):
    p = b[i]
    if p <= 0x7f:  return ("s", b[i:i+1], i+1)
    if p <= 0xb7:  L = p-0x80; return ("s", b[i+1:i+1+L], i+1+L)
    if p <= 0xbf:
        n = p-0xb7; L = int.from_bytes(b[i+1:i+1+n], 'big')
        return ("s", b[i+1+n:i+1+n+L], i+1+n+L)
    if p <= 0xf7: n, L = 0, p-0xc0
    else:
        n = p-0xf7; L = int.from_bytes(b[i+1:i+1+n], 'big')
    start = i+1+n; end = start+L; items = []; j = start
    while j < end:
        t, v, j = rlp(b, j); items.append((t, v))
    return ("l", items, end)

def decode_extra(hexstr):
    # A QBFT extraData list has five items; an anchor header carries a sixth, the
    # post-quantum certificate (see `pq-certificate-under-block-hash`). From AIP-22
    # stage 5 EVERY header carries seven: item 4 (the ECDSA committed seals) is EMPTY,
    # item 5 is the certificate slot (an empty list between anchors) and item 6 is the
    # proposer's post-quantum seal, [index, Falcon-512 signature]. Shapes legal
    # nowhere raise; which of the legal shapes is legal WHERE is judged by the caller.
    _, items, _ = rlp(bytes.fromhex(hexstr[2:]))
    if not 5 <= len(items) <= 7:
        raise ValueError(f"extraData is a {len(items)}-item list, legal nowhere")
    x = {"validators": ["0x"+v[1].hex() for v in items[1][1]], "seals": items[4][1],
         "nitems": len(items), "anchor": len(items) >= 6 and items[5][0] == "l" and len(items[5][1]) > 0,
         "proposer": None}
    if len(items) == 7:
        kind, v = items[6]
        if items[5][0] != "l" or kind != "l" or len(v) != 2 or v[0][0] != "s" or v[1][0] != "s":
            raise ValueError("item 6 of a 7-item header is not [index, signature]")
        x["proposer"] = (int.from_bytes(v[0][1], "big"), len(v[1][1]))
    return x

def header_form_problem(x, height, stage5, pq_band, quorum, seal_bytes):
    """None when the header has the form legal at its height, else what is wrong."""
    must_t5 = stage5 is not None and height >= stage5
    if x["proposer"] is not None and not must_t5:
        return (f"{height:,}: a stage-5 header, but stage 5 is not scheduled on this chain" if stage5 is None
                else f"{height:,}: a stage-5 header below the stage-5 height {stage5:,}")
    if x["proposer"] is None and must_t5:
        return f"{height:,}: a header with ECDSA seals at or above the stage-5 height {stage5:,}"
    if x["proposer"] is not None:
        idx, size = x["proposer"]
        if x["seals"]:
            return f"{height:,}: {len(x['seals'])} ECDSA seal(s) in a stage-5 header"
        if not pq_band[0] <= size <= pq_band[1]:
            return f"{height:,}: proposer seal is {size} bytes, Falcon-512 is {pq_band[0]}..{pq_band[1]}"
        if idx >= len(x["validators"]):
            return f"{height:,}: proposer index {idx} names no row of the {len(x['validators'])}-member set"
        return None
    if len(x["seals"]) < quorum:
        return f"{height:,}: {len(x['seals'])} committed seals, under the quorum {quorum}"
    if any(len(s[1]) != seal_bytes for s in x["seals"]):
        return f"{height:,}: a committed seal that is not {seal_bytes} bytes"
    return None

# ------------------------------------------------------------------ 1. identity
if ONLY != "header":
    print("\n1. Chain identity")
    check("chainId == 0xaf0", rpc("eth_chainId", [])["result"], "0xaf0")
    check("net_version == 2800", rpc("net_version", [])["result"], "2800")
    check("not syncing", rpc("eth_syncing", [])["result"], False)
    cv = rpc("web3_clientVersion", [])["result"]
    check("client is Besu", cv.startswith("besu/"), True, cv)

# --------------------------------------------------------------- 2. block time
# The band is the one the claims file published from its own 100,000-block
# measurement, on the date it names. A band typed here went stale within a month.
head = rpc("eth_getBlockByNumber", [hex(HEAD_AT) if HEAD_AT is not None else "latest", False])["result"]
n = int(head["number"], 16)
if ONLY != "header":
    print("\n2. Block interval over 100,000 blocks")
    old = rpc("eth_getBlockByNumber", [hex(n-100000), False])["result"]
    dt = (int(head["timestamp"], 16) - int(old["timestamp"], 16)) / 100000
    band = re.search(r"between\s+([0-9.]+)\s+and\s+([0-9.]+)", str(claim("block-interval")["verify"]["expect"]))
    if band:
        lo, hi = float(band.group(1)), float(band.group(2))
        check(f"{lo:.2f} <= mean block interval <= {hi:.2f} s (band published by the claims file)",
              lo <= dt <= hi, True, f"measured {dt:.4f} s/block over blocks {n-100000:,} to {n:,}")
    else:
        check("claims file states a block-interval band", "no band found", "between A and B")

# ------------------------------------------------- 3. validator set from header
# The count and the seal count are what the claims file derived from a header on
# its own run; the quorum is recomputed here as ceil(2n/3), not copied.
print("\n3. QBFT validator set and quorum, decoded from the block header")
exp = claim("qbft-validator-set-from-header")["verify"]["expect"]
want_n, want_seals, want_bytes = exp["validators_count"], exp["committedSeals_count"], exp["each_seal_bytes"]
# A claims file older than revision 9 has no `aip22Stage5From`: it predates stage 5 and means "not scheduled".
stage5 = exp.get("aip22Stage5From")
pq_band = tuple(exp.get("proposerSealBytes", [600, 700]))
x = decode_extra(head["extraData"])
check(f"validator count == {want_n} (the count the claims file derived from a header)", len(x["validators"]), want_n)
q = math.ceil(2*len(x["validators"])/3)
check(f"quorum ceil(2n/3) recomputed locally == committed seals count the claims file derived", q, want_seals)
form = "stage 5, no ECDSA seal" if x["proposer"] else "ECDSA committed seals"
where = f"{stage5:,}" if stage5 is not None else "not scheduled"
detail = f"{len(x['seals'])} ECDSA seals"
if x["proposer"]:
    detail += f", proposer seal {x['proposer'][1]} bytes at index {x['proposer'][0]}"
check(f"the head has the form legal at its height ({form}; stage 5 from: {where})",
      header_form_problem(x, n, stage5, pq_band, q, want_bytes), None, detail + f" on block {n:,}")
sets, sealcounts, pqsizes, anchors, problems = set(), set(), set(), 0, []
for k in range(0, 200, 10):
    b = rpc("eth_getBlockByNumber", [hex(n-k), False])["result"]
    xb = decode_extra(b["extraData"])
    sets.add(tuple(xb["validators"])); anchors += xb["anchor"]
    if xb["proposer"]:
        pqsizes.add(xb["proposer"][1])
    else:
        sealcounts.add(len(xb["seals"]))
    pr = header_form_problem(xb, n-k, stage5, pq_band, q, want_bytes)
    if pr:
        problems.append(pr)
check("validator set identical across 20 sampled blocks", len(sets), 1)
seen = f"ECDSA seal counts seen: {sorted(sealcounts)}" if sealcounts else "no ECDSA-form header in the window"
if pqsizes:
    seen += f"; stage-5 proposer seals of {min(pqsizes)}..{max(pqsizes)} bytes"
check("every sampled header has the form legal at its height", problems, [],
      seen + f"; {anchors} of the 20 sampled headers carried a post-quantum certificate")
if ONLY == "header":
    print("\n" + "="*66)
    print(f"  PASS {len(PASS)}   FAIL {len(FAIL)}   (sections 0 and 3 only, {ENDPOINTS[0]})")
    if FAIL:
        print("  FAILED:", ", ".join(FAIL))
    print("="*66)
    sys.exit(1 if FAIL else 0)

# ------------------------------------------------------ 4. precompiles declared
print("\n4. Post-quantum precompiles declared by the node")
cfg = rpc("eth_config", [])["result"]["current"]
pc = cfg["precompiles"]
for nm, ad in [("AereFalcon512", "ae1"), ("AereFalcon1024", "ae2"), ("AereMLDSA44", "ae3"),
               ("AereSLHDSA128s", "ae4"), ("AereSHAKE256", "ae5")]:
    check(f"{nm} at 0x0{ad.upper()}", pc.get(nm), "0x"+"0"*37+ad)
check("0x0AE6 / 0x0AE7 NOT declared (testnet only)",
      [k for k in pc if k in ("AereMLKEM", "AereHashToPoint")], [])
check("fork activationTime", cfg["activationTime"], 1783820272)

# ------------------------------------------------------------ 5. SHAKE256 KAT
print("\n5. NIST SHAKE256 known-answer test at 0x0AE5")
want = "0x" + hashlib.shake_256(b"").hexdigest(32)   # computed locally, not taken from Aere
for ep in ENDPOINTS:
    got = rpc("eth_call", [{"to": "0x"+"0"*37+"AE5",
                            "data": "0x"+"0"*62+"20"}, "latest"], ep)["result"]
    check(f"SHAKE256(empty,32) matches local computation [{ep.split('//')[1]}]", got, want)

# ------------------------------------------- 6. precompiles execute (gas probe)
#
# One address literal, so there is nothing to substitute twice:
#   GAS; STATICCALL(0x0f4240, target, args 0..32, ret 0..32); POP; GAS; SWAP1; SUB;
#   PUSH1 0; MSTORE; RETURN 32
#
# The probe's own overhead is NOT one number. EIP-2929 pre-warms precompiles 0x01
# to 0x09 (access cost 100); every other address is cold (access cost 2600). The
# Aere band at 0x0AE1+ is NOT in the pre-warmed set, so it pays the cold price.
# Compare each target only against a control of the same warmth, or you will read
# the identity precompile as 2,482 gas CHEAPER than an empty address and conclude
# something silly.
print("\n6. Precompiles consume gas (positive proof of execution)")
def gas(addr20, ep=ENDPOINTS[0]):
    code = ("0x5a" "6020" "6000" "6020" "6000" "73" + addr20 + "620f4240" "fa"
            "50" "5a" "9003" "600052" "60206000f3")
    r = rpc("eth_call", [{"to": "0x"+"0"*36+"b0b0", "data": "0x", "gas": "0x1c9c380"},
                         "latest", {"0x"+"0"*36+"b0b0": {"code": code}}], ep)
    return int(r["result"], 16)

WARM, COLD = 125, gas("0"*36+"dead")
check("codeless control (cold address) costs the bare probe overhead", COLD, 2625)
# Calibration. These two costs are fixed by the EVM specification, not by Aere.
# A probe nobody has calibrated is not a measurement, it is a number.
check("IDENTITY 0x04 costs exactly 18 (EVM spec: 15+3*1)",  gas("0"*39+"4") - WARM, 18)
check("SHA-256  0x02 costs exactly 72 (EVM spec: 60+12*1)", gas("0"*39+"2") - WARM, 72)
for nm, ad, exp in [("Falcon-512 0x0AE1",   "0"*37+"ae1", 40000),
                    ("Falcon-1024 0x0AE2",  "0"*37+"ae2", 75000),
                    ("ML-DSA-44 0x0AE3",    "0"*37+"ae3", 55000),
                    ("SLH-DSA-128s 0x0AE4", "0"*37+"ae4", 350000),
                    ("SHAKE256 0x0AE5",     "0"*37+"ae5", 60)]:
    check(f"{nm} consumes {exp} gas above the cold control", gas(ad) - COLD, exp)
# Honest scope. A zero here is CONSISTENT with absence and is not proof of it: a
# precompile that charged nothing would read the same. Revision 1 of the claims
# file called this proof of absence; that was an overstatement and is withdrawn.
# The positive statement of absence is the eth_config enumeration in section 4.
for ad in ("0"*37+"ae6", "0"*37+"ae7"):
    check(f"0x{ad[-3:].upper()} costs 0 above control (consistent with absence, NOT proof)",
          gas(ad) - COLD, 0, "absence is asserted from eth_config in section 4, not from this")

# ------------------------------------------------------- 7. EVM opcode parity
print("\n7. Cancun opcode set, with a negative control")
A = "0x" + "0"*38 + "c0"
def run(code):
    r = rpc("eth_call", [{"to": A, "data": "0x"}, "latest", {A: {"code": code}}])
    return r.get("result"), r.get("error", {}).get("message", "")
for nm, code, exp in [("PUSH0 (EIP-3855)",           "0x602a5f5260205ff3",            0x2a),
                      ("TSTORE/TLOAD (EIP-1153)",    "0x604260015d60015c60005260206000f3", 0x42),
                      ("MCOPY (EIP-5656)",           "0x60426020526020602060005e60206000f3", 0x42),
                      ("CHAINID (EIP-1344)",         "0x4660005260206000f3",          2800)]:
    res, err = run(code)
    check(nm, int(res, 16) if res else err, exp)
for nm, code in [("BLOBBASEFEE (EIP-7516) executes", "0x4a60005260206000f3"),
                 ("BLOBHASH (EIP-4844) executes",    "0x60004960005260206000f3")]:
    res, err = run(code)
    check(nm, "Invalid opcode" not in err and res is not None, True, "no Invalid-opcode error")
for op in ("0c", "21", "a5"):
    res, err = run("0x"+op+"60005260206000f3")
    check(f"NEGATIVE CONTROL: undefined opcode 0x{op} must fail", "Invalid opcode" in err, True)

# --------------------------------------------------- 8. contract code + proxies
print("\n8. Deployed bytecode and EIP-1967 proxy slots")
SLOTS = {"implementation": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
         "admin":          "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103",
         "beacon":         "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50"}
CONTRACTS = {
    "AereFeeBurnVault":       ("0x696afDF4f814e6Fd6aa45CE14C498ed9375fB2c6", 1313),
    "AereSink":               ("0x69581B86A48161b067Ff4E01544780625B231676", 4164),
    "AereCoinbaseSplitter":   ("0xb4b0eCe9011613A5b84248a9B42a0f309E6F01Ec", 2614),
    "AereFalcon512Verifier":  ("0x4E8e9682329e646784fB3bd01430aA4bA54D8fFC", 7333),
    "AereFalcon1024Verifier": ("0xF0aFA59BaB2058e4B6e6B424b7f76750F1F66e36", 7334),
    "AereMLDSA44Verifier":    ("0xf1F7A6Acd82D5DAf9AF3166a2F736EE52C5F85AE", 8654),
    "AereCancunCanary":       ("0x8DbFC002bB23124cBeCd7B4916c179D2AFd65498", 1541),
    "AereKZGVerifier":        ("0x6596307BD8f54d9A91FE364EBC3e594F200AC862", 1988),
    "AerePQCAttestation":     ("0x465d9e3b476bf98aa1393079e240db5d2a9bea6a", 4402),
}
zero = "0x" + "0"*64
for nm, (ad, size) in CONTRACTS.items():
    code = rpc("eth_getCode", [ad, "latest"])["result"]
    check(f"{nm} code is {size} bytes", len(code)//2 - 1, size)
    slots = [rpc("eth_getStorageAt", [ad, s, "latest"])["result"] for s in SLOTS.values()]
    check(f"{nm} is not an EIP-1967 proxy", set(slots), {zero})

# ------------------------------------------------- 9. burn vault has no owner
print("\n9. Burn vault administration surface")
V = "0x696afDF4f814e6Fd6aa45CE14C498ed9375fB2c6"
code = rpc("eth_getCode", [V, "latest"])["result"]
def push4(c):
    b = bytes.fromhex(c[2:]); out = set(); i = 0
    while i < len(b):
        op = b[i]
        if op == 0x63 and i+5 <= len(b): out.add(b[i+1:i+5].hex()); i += 5; continue
        if 0x60 <= op <= 0x7f: i += 1 + (op-0x5f); continue
        i += 1
    return out
sels = push4(code)
for nm, s in [("owner()", "8da5cb5b"), ("transferOwnership(address)", "f2fde38b"),
              ("renounceOwnership()", "715018a6"), ("upgradeTo(address)", "3659cfe6"),
              ("admin()", "f851a440")]:
    check(f"burn vault does NOT expose {nm}", s in sels, False)
check("burn vault DOES expose sweepToZero()", "579663e2" in sels, True)
r = rpc("eth_call", [{"to": V, "data": "0x579663e2",
                      "from": "0x"+"0"*36+"dEaD"}, "latest"])
check("sweepToZero() is permissionless (no revert from an arbitrary caller)",
      "error" not in r, True)

# -------------------------------------- 10. burn rate is a parameter, not a law
print("\n10. Burn rate is owner-settable (this REFUTES 'no admin' copy)")
SP = "0xb4b0eCe9011613A5b84248a9B42a0f309E6F01Ec"
spc = rpc("eth_getCode", [SP, "latest"])["result"]
ssel = push4(spc)
check("splitter EXPOSES owner()", "8da5cb5b" in ssel, True)
check("splitter EXPOSES transferOwnership()", "f2fde38b" in ssel, True)
own = rpc("eth_call", [{"to": SP, "data": "0x8da5cb5b"}, "latest"])["result"]
owner = "0x" + own[-40:]
check("splitter owner is 0x0243a4f4..f3c3", owner, "0x0243a4f47d44b40b65d33f20329de20d00c6f3c3")
ocode = rpc("eth_getCode", [owner, "latest"])["result"]
check("splitter owner is a plain EOA, NOT a multisig or timelock", ocode, "0x",
      "a single key can change the burn rate")

# ------------------------------------------- 11. burn is not currently flowing
print("\n11. Is the burn engine actually burning? (we claim it is NOT)")
# The state window is 512 blocks and its edge is a RACE: `n` was captured when this
# script started, so n-500 may already be out of window by the time we get here.
# Re-read the head NOW and step back 400, safely inside the window.
hnow = int(rpc("eth_blockNumber", [])["result"], 16)
now = int(rpc("eth_getBalance", [V, "latest"])["result"], 16)
was = rpc("eth_getBalance", [V, hex(hnow-400)])["result"]
if was is None:
    check("burn vault balance comparison", "state pruned, NOT MEASURED", "a value", "")
else:
    check("burn vault balance UNCHANGED over 400 blocks",
          now - int(was, 16), 0,
          f"balance {now} wei at both ends; nothing is being routed in")
check("block proposer is a validator, not the splitter",
      head["miner"].lower() == SP.lower(), False, f"miner = {head['miner']}")

# -------------------------------------------------------- 12. receipts survive
print("\n12. Historical receipts (full history, beyond the 512-block state window)")
for h, blk in [("0x3a1838deb9f1b4b26f7af5ef1b4609a8aea77a3ebe654e38151caeadfff4fd5c", 8735205),
               ("0xcd1350372f0a61e26e8a3228da599be9211839f378eef0ea49f2025a20920660", 9200532)]:
    r = rpc("eth_getTransactionReceipt", [h])["result"]
    check(f"receipt {h[:12]}... status success at block {blk}",
          (r["status"], int(r["blockNumber"], 16)) if r else None, ("0x1", blk))

# ------------------------------------- 13. the chain is idle, and the TPS number
# Added in revision 2, after a hostile read of revision 1 pointed out that every
# performance-adjacent figure here was measured on an empty chain and never said
# so. These checks PASS by confirming things that are bad for Aere. That is the
# point: a file that can only produce good news cannot be used to catch us.
# Revision 3: the transaction-pool expectation is read from the claims file, which
# states what txpool_status returned on its own run; revision 2 typed "a backlog
# exists" and that stopped being true.
print("\n13. Load and throughput (we claim the chain is IDLE and the TPS number is not derivable)")
h13 = int(rpc("eth_blockNumber", [])["result"], 16)
tot, empty, sampled = 0, 0, 0
for i in range(0, 3000, 250):
    b = rpc("eth_getBlockByNumber", [hex(h13-i), False])["result"]
    sampled += 1; tot += len(b["transactions"])
    if b["gasUsed"] == "0x0": empty += 1
check("sampled blocks are nearly all empty (idle chain)", empty >= sampled - 1, True,
      f"{sampled} headers across the most recent 3,000 blocks, {empty} with gasUsed 0x0, {tot} transactions in total")
tp = rpc("txpool_status", [])["result"]
pool = re.search(r"pending\s+(0x[0-9a-f]+)\s+and\s+queued\s+(0x[0-9a-f]+)", str(claim("no-mainnet-traffic").get("theTruth", "")))
if pool:
    check(f"txpool_status matches what the claims file measured (pending {pool.group(1)}, queued {pool.group(2)})",
          (int(tp["pending"], 16), int(tp["queued"], 16)), (int(pool.group(1), 16), int(pool.group(2), 16)),
          f"pending={int(tp['pending'],16)}, queued={int(tp['queued'],16)}")
else:
    check("claims file states a txpool measurement", "not found", "pending 0x.. and queued 0x..")
gl = int(rpc("eth_getBlockByNumber", ["latest", False])["result"]["gasLimit"], 16)
check("gasLimit is 2^53-1, a JavaScript artifact and not an engineering parameter",
      gl, 2**53-1,
      "so a 'gas limit / block time' TPS derivation does not produce any published "
      "number; it produces ~8e11. No throughput number is claimed.")

# ------------------------------------------------------------------ verdict
print("\n" + "="*66)
print(f"  PASS {len(PASS)}   FAIL {len(FAIL)}")
if FAIL:
    print("  FAILED:", ", ".join(FAIL))
print("="*66)
print("\n  Sections 10, 11 and 13 are deliberately unflattering to Aere: they PASS by")
print("  confirming that the burn rate is settable by one key, that nothing is being")
print("  burned, and that the chain is idle. A claims file that can only produce good")
print("  news is a brochure. This one is checkable in both directions.\n")
sys.exit(1 if FAIL else 0)
