aere-research/formal-consensus/falcon_blocking_smt.py
Aere Network 4a0b48588c Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:30 +03:00

399 lines
23 KiB
Python

#!/usr/bin/env python3
# -----------------------------------------------------------------------------
# falcon_blocking_smt.py
#
# MACHINE-CHECKED (z3) analysis of the AERE Falcon-512 quorum certificate in
# BLOCKING mode (post-fork): a block is VALID only if it carries >= quorum(N)
# DISTINCT valid Falcon seals over the correct commit hash
# (FalconSealValidationRule; consensus-pqc/QUORUM-DESIGN.md sec 3, 6).
#
# This is the z3 counterpart of formal-consensus/FalconQuorum.qnt (which targets
# Apalache/TLC and needs Java). It RUNS locally on z3 and proves:
# B-SAFETY in blocking mode, <= f Falcon-key-faulty validators (which may
# EQUIVOCATE -- place a valid own-key seal on BOTH conflicting
# blocks) can never certify two conflicting blocks. N in {4,5,7}.
# B-LIVENESS at the fault bound, the honest+correct set (N-f) still reaches
# quorum, so a certificate can always form. Proved UNBOUNDED (all N)
# and per-N for {4,5,7}.
# THRESHOLD WHY N>=7 is required to make blocking safe: N>=7 is the smallest
# validator count that is BOTH safe (f>=2) AND live (N-2>=quorum)
# against 2 simultaneous Falcon faults; and a SINGLE Falcon fault
# leaves strictly-more-than-quorum correct signers (liveness margin
# > 0) only at N>=7. At N in {4,5} blocking can STALL (shown SAT).
#
# Besu quorum: quorum(N)=ceil(2N/3); BFT fault bound f(N)=floor((N-1)/3).
#
# HONEST BOUNDARY: B-SAFETY per-N is a BOUNDED check; the intersection lemma and
# the N-f>=quorum liveness identity are UNBOUNDED (all N). This is the certificate
# COMBINATORICS of the design, not the Besu Java code, and NOT mainnet: chain
# 2800 runs classical ECDSA QBFT and the Falcon layer there is LOG-ONLY, not
# blocking (blocking requires N>=7 + a re-genesis with a Falcon anchor).
# -----------------------------------------------------------------------------
from z3 import (Int, Bool, Solver, And, Or, Not, Implies, If, Sum, sat, unsat)
def quorum(n): return (2 * n + 2) // 3 # ceil(2N/3)
def faultbound(n): return (n - 1) // 3 # floor((N-1)/3)
results = []
def check(name, s, expect_unsat=True, kind="PROOF"):
r = s.check()
if expect_unsat:
ok = (r == unsat); tag = "PROVED" if ok else "FAILED"
else:
ok = (r == sat); tag = "CEX-FOUND" if ok else "FAILED"
results.append((name, tag, ok, kind))
print(f"[{tag:9}] ({kind}) {name}: z3={r} (expected {'unsat' if expect_unsat else 'sat'})")
if r == sat:
m = s.model(); wit = {}
for d in m.decls():
try: wit[str(d)] = m[d].as_long()
except Exception:
try: wit[str(d)] = bool(m[d])
except Exception: wit[str(d)] = "?"
print(" witness:", {k: wit[k] for k in sorted(wit)})
return ok
print("### AERE Falcon quorum certificate -- BLOCKING mode: safety, liveness, and the N>=7 threshold\n")
print(" " + ", ".join(f"N={n}: q={quorum(n)}, f={faultbound(n)}, N-f={n-faultbound(n)}"
for n in (4, 5, 6, 7)) + "\n")
# =============================================================================
# B-SAFETY -- blocking mode: two conflicting blocks cannot both be certified.
# Per validator i: honest_i, sealA_i, sealB_i.
# * HONEST validator signs at most ONE block per height: honest => !(sealA & sealB)
# * FALCON-FAULTY validator holds its own key and may EQUIVOCATE: a valid seal
# on BOTH conflicting blocks (counts toward both certs).
# * <= f Falcon-faulty. A cert needs >= quorum distinct valid seals.
# =============================================================================
def blocking_safety(N, qv, fv):
s = Solver()
honest = [Bool(f'honest_{i}') for i in range(N)]
sealA = [Bool(f'sealA_{i}') for i in range(N)]
sealB = [Bool(f'sealB_{i}') for i in range(N)]
s.add(Sum([If(Not(honest[i]), 1, 0) for i in range(N)]) <= fv) # <= f faulty
for i in range(N):
s.add(Implies(honest[i], Not(And(sealA[i], sealB[i])))) # honest: one block
s.add(Sum([If(sealA[i], 1, 0) for i in range(N)]) >= qv) # cert A formed
s.add(Sum([If(sealB[i], 1, 0) for i in range(N)]) >= qv) # cert B formed
return s
for N in (4, 5, 7):
s = blocking_safety(N, quorum(N), faultbound(N))
check(f"B-SAFETY N={N} (q={quorum(N)},f={faultbound(N)}): two conflicting blocks "
f"cannot both reach a Falcon quorum under <= f equivocating faulty", s)
# ---- NEG-CTRL B-SAFETY: drop the <= f bound -> >f equivocators certify BOTH ------
def blocking_safety_no_bound(N, qv):
s = Solver()
honest = [Bool(f'honest_{i}') for i in range(N)]
sealA = [Bool(f'sealA_{i}') for i in range(N)]
sealB = [Bool(f'sealB_{i}') for i in range(N)]
# NO fault bound; faulty may equivocate
for i in range(N):
s.add(Implies(honest[i], Not(And(sealA[i], sealB[i]))))
s.add(Sum([If(sealA[i], 1, 0) for i in range(N)]) >= qv)
s.add(Sum([If(sealB[i], 1, 0) for i in range(N)]) >= qv)
return s
for N in (4,):
s = blocking_safety_no_bound(N, quorum(N))
check(f"NEG-CTRL B-SAFETY N={N} WITHOUT the <= f bound: two blocks CAN both be "
f"certified (proves the <= f assumption is load-bearing)", s,
expect_unsat=False, kind="NEG-CTRL")
# ---- NEG-CTRL B-SAFETY: lower the quorum to ceil(N/2) -> certs collide -----------
def ceil_half(n): return (n + 1) // 2
for N in (4,):
s = blocking_safety(N, ceil_half(N), faultbound(N))
check(f"NEG-CTRL B-SAFETY N={N} majority quorum ceil(N/2)={ceil_half(N)}: two blocks "
f"CAN both be certified (proves ceil(2N/3) is load-bearing)", s,
expect_unsat=False, kind="NEG-CTRL")
# =============================================================================
# B-LIVENESS -- at the fault bound, honest+correct (N-f) still forms a quorum.
# =============================================================================
# UNBOUNDED (all N): N - f(N) >= quorum(N).
s = Solver()
N, q, f = Int('N'), Int('q'), Int('f')
s.add(N >= 1)
s.add(3 * q >= 2 * N, 3 * q <= 2 * N + 2) # q = ceil(2N/3)
s.add(3 * f <= N - 1, N - 1 <= 3 * f + 2) # f = floor((N-1)/3)
s.add(N - f < q) # negate: honest+correct below quorum
check("B-LIVENESS (all N): honest+correct set N-f >= quorum(N) -> a cert can always form", s)
# per-N witness that the honest set alone certifies (non-vacuous, config theorem)
for N in (4, 5, 7):
ok = (N - faultbound(N)) >= quorum(N)
s = Solver(); s.add(Int('dummy') == (0 if ok else 1)); s.add(Int('dummy') == 0)
check(f"B-LIVENESS N={N}: N-f={N-faultbound(N)} >= quorum={quorum(N)} "
f"(honest+correct alone forms the Falcon cert)", s,
expect_unsat=not ok, kind="CONFIG")
# =============================================================================
# THRESHOLD -- WHY N>=7 is required to activate BLOCKING.
# -----------------------------------------------------------------------------
# (T1) To be BOTH safe and live against 2 simultaneous Falcon faults you need
# safe2(N): f(N) >= 2 AND live2(N): (N-2) >= quorum(N).
# Claim: no N in {4,5,6} satisfies both; N=7 does. N>=7 is the crossover.
# =============================================================================
def safe2(N): return faultbound(N) >= 2
def live2(N): return (N - 2) >= quorum(N)
# no N in {4,5,6} is both safe and live for 2 faults:
s = Solver()
Nv = Int('N')
s.add(Or(Nv == 4, Nv == 5, Nv == 6))
# express safe2 & live2 for the concrete small set via a disjunction of the true cases
both_cases = Or(*[And(Nv == n, safe2(n), live2(n)) for n in (4, 5, 6)])
s.add(both_cases)
check("THRESHOLD-T1a no N in {4,5,6} is BOTH safe(f>=2) AND live(N-2>=q) for 2 faults", s)
# N=7 IS both safe and live for 2 faults (non-vacuous positive witness):
s = Solver()
s.add(Int('ok7') == (1 if (safe2(7) and live2(7)) else 0)); s.add(Int('ok7') == 1)
check("THRESHOLD-T1b N=7 IS safe(f=2) AND live(N-2=5>=q=5) for 2 faults "
f"(f(7)={faultbound(7)}, N-2=5, q(7)={quorum(7)})", s, expect_unsat=False, kind="CONFIG")
# (T2) SINGLE-Falcon-fault liveness margin: margin1(N) = (N-1) - quorum(N).
# margin1 == 0 EXACTLY at N in {4,5} (a single fault forces ALL remaining
# honest to sign; any further crash/slowness halts) and margin1 >= 1 at
# N >= 6. The current AERE mainnet size N=5 has ZERO single-fault margin;
# N=7 (the next BFT-OPTIMAL 3f+1 size) both restores positive margin AND
# tolerates 2 Falcon faults (T1) -- that combination is the driver for N>=7.
print(" single-fault margin (N-1)-quorum(N): "
+ ", ".join(f"N={n}->{(n-1)-quorum(n)}" for n in (4, 5, 6, 7)))
for N in (4, 5):
margin = (N - 1) - quorum(N)
s = Solver(); s.add(Int('m') == margin); s.add(Int('m') != 0)
check(f"THRESHOLD-T2 N={N}: single-fault liveness margin (N-1)-q == 0 "
f"(a single Falcon fault forces ALL {N-1} remaining honest to sign)", s)
for N in (6, 7):
margin = (N - 1) - quorum(N)
s = Solver(); s.add(Int('m') == margin); s.add(Int('m') >= 1)
check(f"THRESHOLD-T2 N={N}: single-fault liveness margin (N-1)-q = {margin} >= 1 "
f"(a single Falcon fault does NOT force all-honest participation)", s,
expect_unsat=False, kind="CONFIG")
# (T3) STALL at N<7: with 2 Falcon-faulty (withholding) the correct set is below
# quorum, so NO post-fork block can be certified -> the chain STALLS.
def stall_witness(N, qv, faulty):
"""Correct signers = N-faulty all sign their single canonical block; if
N-faulty < qv the cert cannot form (block rejected under blocking)."""
s = Solver()
sealA = [Bool(f'sealA_{i}') for i in range(N)]
correct = [Bool(f'correct_{i}') for i in range(N)]
s.add(Sum([If(Not(correct[i]), 1, 0) for i in range(N)]) == faulty) # 'faulty' Falcon-faulty
for i in range(N):
# faulty validators withhold a valid seal (crash / bad key / correlated defect)
s.add(Implies(Not(correct[i]), Not(sealA[i])))
# correct validators all sign the one canonical block
s.add(Implies(correct[i], sealA[i]))
s.add(Sum([If(sealA[i], 1, 0) for i in range(N)]) < qv) # cert CANNOT form -> stall
return s
for N in (5,):
s = stall_witness(N, quorum(N), faulty=2)
check(f"THRESHOLD-T3 STALL at N={N} with 2 Falcon-faulty: correct set {N-2} < "
f"quorum {quorum(N)} -> no cert can form (post-fork block stalls)", s,
expect_unsat=False, kind="STALL")
# and at N=7 with 2 faulty NO stall (cert still forms) -> the reason to grow to 7:
s = stall_witness(7, quorum(7), faulty=2)
check(f"THRESHOLD-T3 N=7 with 2 Falcon-faulty: correct set 5 == quorum 5 -> cert "
f"STILL forms (no stall); this is why blocking needs N>=7", s) # expect UNSAT: cannot stall
# =============================================================================
# AUD-CONSENSUS-1 / -2 FIX: registry size M DECOUPLED from validator count N.
# -----------------------------------------------------------------------------
# The reviewed defect: the blocking quorum tracked the DYNAMIC validator-set size
# N (quorum = ceil(2N/3)) while the signer registry was IMMUTABLE at its anchored
# size M=N0, and seals were counted by REGISTRY membership (not current-validator
# membership). So (a) adding a validator raised the quorum above the fixed key
# count -> HALT, and (b) a removed validator's key kept counting -> a safety gap.
#
# The FIX binds BOTH the quorum AND the counted-seal set to ONE well-defined set:
# eligible = currentValidators INTERSECT registry , k = |eligible|
# quorum = ceil(2k/3)
# valid = # distinct ELIGIBLE addresses whose Falcon seal verifies
# and ECDSA stays decisive (a hybrid block also needs ceil(2N/3) ECDSA seals over
# the full set N). This section models M != N and machine-checks that the fix
# removes both bugs, stays live under validator changes, and is never weaker than
# ECDSA. Every PROOF is unsat-of-negation; every claim is paired with a FIRING
# control (BUG-DEMO / NEG-CTRL) so the results are demonstrably non-vacuous.
# =============================================================================
print("\n### AUD-CONSENSUS-1/-2 FIX: eligible-signer set (immutable registry M != validator count N)\n")
def ceil23(x):
return (2 * x + 2) // 3
# ---- BUG-DEMO (firing): the OLD rule HALTS when N grows past the registry. ----
# Armed with an M=7 manifest, grow the validator set to N=11. Old quorum ceil(2N/3)
# = 8, but only k=7 keyed-and-current validators can produce a verifying seal, so
# valid <= 7 < 8 and every post-fork block is REJECTED (deadlock reachable = SAT).
s = Solver()
Nv, Mv, kv, qold = Int('N'), Int('M'), Int('k'), Int('qold')
s.add(Mv == 7, Nv == 11, kv == 7) # k = |V ∩ R| = 7 keyed validators
s.add(3 * qold >= 2 * Nv, 3 * qold <= 2 * Nv + 2) # OLD quorum = ceil(2N/3) = 8
s.add(kv < qold) # available signers below OLD quorum
check("BUG-DEMO (OLD rule) M=7, validators grown to N=11: quorum ceil(2N/3)=8 > k=7 keyed "
"signers -> post-fork DEADLOCK reachable (the halt this fix removes)", s,
expect_unsat=False, kind="BUG-DEMO")
# ---- FIX-F1 LIVENESS (unbounded): the eligible set always meets its OWN quorum. ----
# quorum = ceil(2k/3) and there are exactly k eligible signers, so k >= ceil(2k/3)
# for ALL k -> adding or removing validators can never make the eligible Falcon
# quorum unreachable. No silent block-production halt is possible from a set change.
s = Solver()
k, q = Int('k'), Int('q')
s.add(k >= 0)
s.add(3 * q >= 2 * k, 3 * q <= 2 * k + 2) # q = ceil(2k/3)
s.add(k < q) # negate: eligible below its own quorum
check("FIX-F1 LIVENESS (all k): eligible-set size k >= ceil(2k/3) -> a validator ADD or REMOVE "
"can NEVER make the eligible Falcon quorum unreachable (no silent halt)", s)
# ---- FIX-F1b CONFIG: adding an UNKEYED validator does not raise the eligible quorum. ----
# Registry M=7 (armed). The eligible set stays the 7 keyed validators, so quorum
# stays ceil(2*7/3)=5 (LIVE), regardless of N growth, unlike the OLD ceil(2N/3).
# N=8, M=7 (one unkeyed added validator), 2 keyed validators briefly offline:
# OLD quorum ceil(2*8/3)=6, valid=5 -> REJECT (margin erased by the unkeyed add);
# NEW quorum over eligible ceil(2*7/3)=5, valid=5 -> LIVE.
s = Solver()
s.add(Int('v') == 5) # 7 keyed - 2 offline = 5 valid seals
s.add(Int('v') < ceil23(8)) # 5 < OLD quorum 6 -> OLD rejects
check("FIX-vs-BUG N=8,M=7 (2 keyed offline): OLD quorum ceil(2*8/3)=6 > valid=5 -> OLD rule "
"REJECTS (an unkeyed add erased the margin)", s, expect_unsat=False, kind="BUG-DEMO")
s = Solver()
s.add(Int('ok') == (1 if 5 >= ceil23(7) else 0))
s.add(Int('ok') == 1)
check("FIX-F1b N=8,M=7 (2 keyed offline): NEW eligible quorum ceil(2*7/3)=5 <= valid=5 -> LIVE "
"(the fix keeps the chain producing where the old rule rejected)", s,
expect_unsat=False, kind="CONFIG")
# N=11, M=7: OLD quorum ceil(2*11/3)=8 > all 7 keys -> PERMANENT halt; NEW quorum 5 -> live.
s = Solver()
s.add(Int('gap') == ceil23(11) - 7)
s.add(Int('gap') >= 1) # OLD quorum 8 exceeds every keyed signer
check("FIX-vs-BUG N=11,M=7: OLD quorum ceil(2*11/3)=8 EXCEEDS all 7 keyed signers -> every "
"post-fork block PERMANENTLY rejected (the chain-halt bug)", s,
expect_unsat=False, kind="BUG-DEMO")
s = Solver()
s.add(Int('ok') == (1 if 7 >= ceil23(7) else 0))
s.add(Int('ok') == 1)
check("FIX-F1b N=11,M=7: NEW eligible quorum ceil(2*7/3)=5 <= 7 keyed signers -> LIVE "
"(quorum over the eligible set, unaffected by N growth)", s,
expect_unsat=False, kind="CONFIG")
# ---- FIX-F2 SAFETY over the ELIGIBLE set (size k): two conflicting eligible ----
# quorums are impossible under <= f_e = floor((k-1)/3) equivocating faulty signers.
# Same intersection argument as B-SAFETY, now proved to hold on the eligible set the
# fixed rule actually uses (k = |V ∩ R|), for k in {5,7}.
for k in (5, 7):
s = blocking_safety(k, quorum(k), faultbound(k))
check(f"FIX-F2 ELIGIBLE-SAFETY k={k} (q={quorum(k)},f={faultbound(k)}): two conflicting "
f"blocks cannot both reach the eligible Falcon quorum under <= f equivocators", s)
# ---- FIX-F3 REMOVAL-SAFETY (AUD-CONSENSUS-2): a registered-but-removed validator's ----
# seal cannot count. Model N=5 current validators, M=7 registered keys (2 removed
# without re-anchor). NEW rule counts only eligible (current) indices.
N, M = 5, 7
s = Solver()
inV = [Bool(f'inV_{i}') for i in range(M)] # is registry index i a CURRENT validator?
counts = [Bool(f'c_{i}') for i in range(M)] # does index i's seal count (NEW rule)?
s.add(Sum([If(inV[i], 1, 0) for i in range(M)]) == N) # 5 of the 7 keyed are current
for i in range(M):
s.add(Implies(counts[i], inV[i])) # NEW: only eligible indices count
s.add(Or(*[And(counts[i], Not(inV[i])) for i in range(M)])) # negate: an ex-validator counted
check("FIX-F3 REMOVAL-SAFETY N=5,M=7: no registered-but-removed validator's seal can count "
"toward the eligible quorum (counted set is a subset of current validators)", s)
# NEG-CTRL F3: the OLD registry-membership rule LETS an ex-validator's key count.
s = Solver()
inV = [Bool(f'inV_{i}') for i in range(M)]
counts = [Bool(f'c_{i}') for i in range(M)]
s.add(Sum([If(inV[i], 1, 0) for i in range(M)]) == N)
# OLD rule: counting requires only registry membership (every index is registered), NOT inV
s.add(Or(*[And(counts[i], Not(inV[i])) for i in range(M)]))
check("NEG-CTRL F3 N=5,M=7: the OLD (registry-membership) rule LETS a removed validator's key "
"count (proves the current-validator intersection is load-bearing for safety)", s,
expect_unsat=False, kind="NEG-CTRL")
# ---- FIX-F4 NEVER-WEAKER-THAN-ECDSA: hybrid-valid => ECDSA-valid, for ANY k. ----
# A hybrid block needs the ECDSA quorum over the FULL set N and the eligible Falcon
# quorum over k <= N. So even though the Falcon leg is over a subset, the block is
# never accepted without the full ECDSA quorum -> never weaker than ECDSA-only.
s = Solver()
e, fal, Nn, kk, qN, qk = Int('e'), Int('fal'), Int('N'), Int('k'), Int('qN'), Int('qk')
s.add(Nn >= 1, kk >= 0, kk <= Nn)
s.add(3 * qN >= 2 * Nn, 3 * qN <= 2 * Nn + 2) # qN = ceil(2N/3) (ECDSA)
s.add(3 * qk >= 2 * kk, 3 * qk <= 2 * kk + 2) # qk = ceil(2k/3) (eligible Falcon)
s.add(e >= qN, fal >= qk) # hybrid-valid
s.add(e < qN) # negate: NOT ecdsa-valid
check("FIX-F4 NEVER-WEAKER (all N,k): every hybrid-valid block (ECDSA quorum over N AND eligible "
"Falcon quorum over k) is ECDSA-valid -> the fixed rule is never weaker than ECDSA-only", s)
# NEG-CTRL F4: the Falcon eligible quorum is a REAL added gate (ecdsa-valid can be hybrid-invalid).
s = Solver()
e, fal, qN7, qk7 = Int('e'), Int('fal'), quorum(7), quorum(7)
s.add(e >= qN7, fal < qk7) # ECDSA ok, eligible Falcon short
check("NEG-CTRL F4 N=k=7: an ECDSA-valid block can be hybrid-INVALID when the eligible Falcon "
"quorum is short (proves the eligible Falcon leg is a genuine added gate)", s,
expect_unsat=False, kind="NEG-CTRL")
# ---- FIX-F5 FAIL-CLOSED on an empty eligible set (the fail-OPEN trap). ----
# With k=0 the naive threshold ceil(2*0/3)=0 is trivially met by valid=0 (fail-OPEN).
# NEG-CTRL shows the trap; the FIX guards it (k==0 -> REJECT in blocking mode).
s = Solver()
valid, q0, k0 = Int('valid'), Int('q0'), Int('k')
s.add(k0 == 0, 3 * q0 >= 2 * k0, 3 * q0 <= 2 * k0 + 2) # q0 = ceil(0) = 0
s.add(valid == 0, valid >= q0) # naive accept holds
check("NEG-CTRL F5 FAIL-OPEN trap: with k=0 the naive check valid(0) >= quorum(0)=0 ACCEPTS "
"(this is why the fixed rule must fail-closed on an empty eligible set)", s,
expect_unsat=False, kind="NEG-CTRL")
s = Solver()
accepted, k0 = Bool('accepted'), Int('k')
s.add(k0 == 0)
s.add(Implies(k0 == 0, Not(accepted))) # FIX guard: empty eligible -> reject
s.add(accepted) # negate: accepted anyway
check("FIX-F5 FAIL-CLOSED: with the empty-eligible guard, a k=0 (unbound / no-coverage) block "
"is never accepted in blocking mode (no fail-open)", s)
# ---- FIX-ARM: at ARM time the registry COVERS the validator set (k == N), so the ----
# eligible Falcon quorum equals the ECDSA quorum ceil(2N/3) -> full fault margin.
for N in (7, 9):
s = Solver()
s.add(Int('eq') == (ceil23(N) - quorum(N))) # eligible quorum(k=N) - ECDSA quorum(N)
s.add(Int('eq') != 0) # negate: they differ
check(f"FIX-ARM N={N}: with full coverage (k=N) the eligible quorum ceil(2N/3)={ceil23(N)} "
f"EQUALS the ECDSA quorum -> arming at full margin (the required arming invariant)", s)
# =============================================================================
print("\n=== SUMMARY (Falcon BLOCKING mode) ===")
allok = True
for name, tag, ok, kind in results:
print(f" {tag:9} [{kind}] {name}")
allok = allok and ok
print()
if allok:
print(" PROVED: In BLOCKING mode the Falcon quorum certificate is SAFE -- <= f")
print(" equivocating Falcon-faulty validators cannot certify two conflicting")
print(" blocks (N in {4,5,7}) -- and LIVE at the fault bound (N-f >= quorum, all")
print(" N). The N>=7 requirement is machine-checked: N=7 is the smallest set that")
print(" is BOTH safe (f>=2) and live (N-2>=quorum) against 2 Falcon faults. It is")
print(" also the next BFT-optimal 3f+1 size, and unlike the current N=5 (zero")
print(" single-fault margin) it leaves margin > 0 over quorum for a single fault. At")
print(" N in {4,5} two Falcon faults drop the correct set below quorum and the")
print(" post-fork chain STALLS (shown reachable). Negative controls FIRE: dropping")
print(" the <= f bound or lowering the quorum breaks blocking safety.")
print(" AUD-CONSENSUS-1/-2 FIX (registry size M != validator count N): the FIXED rule")
print(" binds BOTH the quorum AND the counted-seal set to eligible = currentValidators")
print(" INTERSECT registry (k=|eligible|, quorum=ceil(2k/3)). Machine-checked: the OLD")
print(" rule HALTS when N grows past M (BUG-DEMO fires at N=11,M=7); the fix stays LIVE")
print(" for all k (F1, k >= ceil(2k/3)); is SAFE over the eligible set (F2); EXCLUDES")
print(" removed-validator keys (F3, with the OLD-rule leak firing as a control); is")
print(" NEVER weaker than ECDSA (F4, hybrid-valid => ECDSA-valid over N); FAIL-CLOSES on")
print(" an empty eligible set (F5, with the fail-open trap firing as a control); and arms")
print(" at full margin under coverage k=N (FIX-ARM).")
print(" BOUNDARY: bounded per-N/per-k safety; unbounded liveness identity; DESIGN")
print(" combinatorics not Besu bytecode; mainnet Falcon layer is LOG-ONLY, not")
print(" blocking (blocking needs N>=7 + an ADDRESS-BOUND registry covering the set).")
else:
print(" NOT fully established (see FAILED / unexpected result above).")
import sys
sys.exit(0 if allok else 1)