#!/usr/bin/env python3 # ----------------------------------------------------------------------------- # run_consensus_verification.py # # One-command re-run of the AERE formal-verification suite (z3): the QBFT/Falcon # CONSENSUS models, the CONTRACT SMT models (fund-flow / registry / PQC-verifier # invariants) AND the APPLICATION / registry / PQC-account models. Every *_smt.py # in this directory is covered; the COMPLETENESS GUARD below refuses to print a # green result while any model present here is unwired. # Reports a single PASS/FAIL plus the verdict-line tally. Each model # exits 0 only if every PROOF is PROVED and every NEGATIVE CONTROL fired, so a green # run means the proofs are discharged AND demonstrably non-vacuous. # # python run_consensus_verification.py # # The verdict-line tally counts the per-check lines each model prints (one line per # property, "[PROVED ] ..." / "[CEX-FOUND] ..." / "[FAILED ] ..."); it is a count # of distinct property checks, not of SUMMARY echoes. # # (The quint/Apalache spec FalconQuorum.qnt is an OPTIONAL secondary cross-check; # see CONSENSUS-VERIFICATION-2026-07-12.md for the `quint run` commands.) # ----------------------------------------------------------------------------- import subprocess, sys, os, re # --- QBFT / Falcon consensus models (unchanged) --- CONSENSUS_MODELS = [ ("QBFT/IBFT 2.0 SAFETY (agreement)", "qbft_safety_smt.py"), ("Falcon LOG-ONLY no-op", "falcon_logonly_noop_smt.py"), ("Falcon BLOCKING safety/liveness/N>=7", "falcon_blocking_smt.py"), ("Anchor THRESHOLD safety (K=3 of 9 on anchor blocks since 13,034,000, registry rotation)", "anchor_blocking_quorum_smt.py"), ("PQC ACTIVATION transition safety (log-only->blocking + contract-anchored registry)", "qbft_pqc_activation_smt.py"), ("QBFT LIVENESS (partial synchrony)", "qbft_liveness_smt.py"), ("QBFT explicit-Prepare counting (2026-07-14 2nd-client bug+fix)", "qbft_prepare_counting_smt.py"), ("QBFT digest-keyed vote tally (2026-07-14 Finding-A shipped fix)", "qbft_digest_keyed_smt.py"), ("QBFT IBFT 2.0 locking, engine-role model (2026-07-14 Findings D/E)", "qbft_locking_smt.py"), ] # --- CONTRACT SMT models: fund-flow + registry + PQC-verifier invariants (wired in # this loop). Each proves a load-bearing safety invariant by asserting NOT-property # under the real guards (UNSAT) with a firing negative control (guard removed -> SAT). --- CONTRACT_MODELS = [ ("ComputeMarketV3 ESCROW SOLVENCY (bal[T] >= totalLiabilities[T])", "computemarket_smt.py"), ("DestinationSettler NO-ARBITRARY-RECIPIENT + AT-MOST-ONCE + OUTPUT-MATCH", "destinationsettler_smt.py"), ("AccountMigrator ONLY-DESTINATION + ATOMICITY (no custody / no strand)", "migrator_smt.py"), ("PQ FinalityCertificate FAIL-CLOSED accept (quorum/root/size/domain/rotation)", "pqfinality_smt.py"), ("PQ AggregateVerifier FAIL-CLOSED t-of-n authorization (root/digest binding)", "pqaggregate_smt.py"), ("TrustRegistry + VerifiableCredential FAIL-CLOSED compliance trust", "credential_registry_smt.py"), ("RecoveryRegistry APPEND-ONLY + STRICT NONCE + FAIL-CLOSED (Falcon-512)", "recovery_registry_smt.py"), ("AP2MandateVerifier SPEND-AUTH cap + window + L3 executor gate", "ap2_mandate_smt.py"), ("ReputationRegistry8004 M1 author-gate + delta-clamp forward-safety", "reputation_gate_smt.py"), ("VectorStore VERSION-MONOTONICITY + APPEND-ONLY + one-shot receipt", "vectorstore_smt.py"), ("BitstringStatusList REVOCATION MONOTONICITY (terminal) + epoch + frame", "bitstring_status_smt.py"), ] # --- APPLICATION / REGISTRY / PQC-ACCOUNT models. These existed as standalone # scripts but were NOT wired into this runner, so the "one command" run # silently covered only 19 of the 29 models in this directory. Wired in # 2026-07-20; each was confirmed to pass standalone (rc=0) first. --- APPLICATION_MODELS = [ ("Falcon HYBRID DUAL-QUORUM (no-worse-than-ECDSA, N>=9 at f=2)", "falcon_hybrid_dualquorum_smt.py"), ("HybridAuthorizer ECDSA+Falcon AND-gate authorization", "hybridauthorizer_smt.py"), ("AereThresholdAccount PQC t-of-n (4337) authorization", "threshold_account_smt.py"), ("PQCKeyRegistry rotation / binding / fail-closed", "pqckeyregistry_smt.py"), ("CryptoRegistry algorithm-agility registration invariants", "cryptoregistry_smt.py"), ("AgentDID LIFECYCLE (create / update / revoke monotonicity)", "agentdid_lifecycle_smt.py"), ("AgentDID SESSION delegation scope + expiry", "agentdid_session_smt.py"), ("SettlementHub settlement invariants", "settlementhub_smt.py"), ("SpokePool cross-chain deposit / fill / refund invariants", "spokepool_smt.py"), ("LendingMarket liquidation clamp + solvency", "lending_liquidation_smt.py"), ] MODELS = CONSENSUS_MODELS + CONTRACT_MODELS + APPLICATION_MODELS # --- COMPLETENESS GUARD ----------------------------------------------------- # The failure this guards against is drift, not a wrong proof: a new *_smt.py # lands in this directory, nobody adds it to MODELS, and the canonical one-command # run keeps printing a green tally that no longer covers the corpus. A reader has # no way to see the gap from the output. So: refuse to report a green result while # any model in this directory is unwired. Add it to MODELS, or add it to # _INTENTIONALLY_UNWIRED with the reason. _INTENTIONALLY_UNWIRED = set() # e.g. {"superseded_model_smt.py"} _wired = {fname for _, fname in MODELS} _present = {f for f in os.listdir(os.path.dirname(os.path.abspath(__file__))) if f.endswith("_smt.py")} _missing = sorted(_present - _wired - _INTENTIONALLY_UNWIRED) if _missing: print("COMPLETENESS GUARD FAILED: these z3 models exist in this directory but") print("are NOT wired into the canonical runner, so the tally below would") print("understate the corpus and overstate what one command actually checks:") for f in _missing: print(f" - {f}") sys.exit(2) _TAG_RE = re.compile(r'^\[(PROVED|CEX-FOUND|FAILED)\s*\]') here = os.path.dirname(os.path.abspath(__file__)) overall = True summary = [] totals = {"PROVED": 0, "CEX-FOUND": 0, "FAILED": 0} for label, fname in MODELS: path = os.path.join(here, fname) print("=" * 78) print(f"### {label} [{fname}]") print("=" * 78) r = subprocess.run([sys.executable, path], capture_output=True, text=True) sys.stdout.write(r.stdout) if r.stderr: sys.stderr.write(r.stderr) for line in r.stdout.splitlines(): m = _TAG_RE.match(line) if m: totals[m.group(1)] += 1 ok = (r.returncode == 0) overall = overall and ok summary.append((label, fname, ok)) print() print("#" * 78) print("### AERE FORMAL-VERIFICATION SUITE -- OVERALL (consensus + contract + application models)") print("#" * 78) for label, fname, ok in summary: print(f" {'PASS' if ok else 'FAIL'} {label:66} ({fname})") print() print(f" models run: {len(MODELS)} passed: {sum(1 for _,_,ok in summary if ok)}" f" ({len(CONSENSUS_MODELS)} consensus + {len(CONTRACT_MODELS)} contract" f" + {len(APPLICATION_MODELS)} application)") print(f" verdict lines: {totals['PROVED']} PROVED {totals['CEX-FOUND']} CEX-FOUND " f"{totals['FAILED']} FAILED") print(f" coverage: {len(_wired & _present)} of {len(_present)} *_smt.py files in this" f" directory are wired into this run" + (f" ({len(_INTENTIONALLY_UNWIRED)} intentionally unwired)" if _INTENTIONALLY_UNWIRED else "")) print("RESULT:", "ALL MODELS PASS (proofs PROVED, negative controls fired)" if overall else "FAILURE -- see model output above") sys.exit(0 if overall else 1)