aere-research/formal-consensus/pqfinality_smt.py
Aere Network 6cb0140fae Republished from a clean root: the compiled artifact is gone from history, and the local line of work joins the sanitized public line
The public history carried kat/__pycache__/mlkem768_reference.cpython-314.pyc,
a compiled Python artifact embedding the operator's absolute local path. Text
secret scanners do not read compiled binaries, which is exactly how it slipped
through, and removing it from the tip would have left it reachable through the
old root commits. So this repository is republished from a single clean root.

This root also carries, from the previously unpublished line of work:
- corrected LICENSE year, LICENSING.md, VERIFY-POLICY.md, and
  CITATIONS-UNRESOLVED.md remeasured 2026-08-11 (101 paths, README aligned)
- O-018: run_consensus_verification.py ran 19 of 29 models and reported PASS;
  it now runs all 29, and computemarket_smt.py gains resolveByTimeout /
  reclaimUnsettled cases plus a negative control
- O-006: the word 'audited' removed from next to Bouncy Castle, twice, after a
  concurrent edit resurrected it
- O-014: prior art named and dated - Algorand's native falcon_verify shipped
  about ten months before AERE's precompiles; the primacy claim is withdrawn
  where it was implied
- bench/ scripts parametrized so they actually run for an outsider (the
  earlier textual sanitization left $STAGING unexpanded inside Python strings)
- AIP-2/AIP-3 errata with measured figures, spec remeasurements at 2026-08-01,
  and the spec-zk-stack retractions (owner is an operational key, not the
  Foundation; 'maximally sound' withdrawn; aggregator V1 deprecated)
The redacted bench-host environment files from the sanitized line are kept
exactly as published; the unredacted local variants are not carried.
2026-08-15 13:52:14 +03:00

263 lines
15 KiB
Python

#!/usr/bin/env python3
# -----------------------------------------------------------------------------
# pqfinality_smt.py
#
# SMT proof (z3) of the FAIL-CLOSED acceptance guards of Aere Network's ADDITIVE
# Post-Quantum Finality Certificate: AereFinalityCertificateVerifier bound to the
# live AerePQAttestationKeyRegistry. Plus load-bearing NEGATIVE CONTROLS for the
# quorum, the validator-set-root binding, and the attestation-domain binding.
#
# Contracts: contracts/contracts/pqfinality/AereFinalityCertificateVerifier.sol
# contracts/contracts/pqfinality/AerePQAttestationKeyRegistry.sol
#
# A certificate is ONE SP1 proof that a quorum of validators produced a valid
# hash-based (post-quantum) attestation over a finalized block. verifyCertificate()
# ACCEPTS a certificate ONLY when every fail-closed guard holds (else it reverts):
# G0 registry validator set non-empty: n >= 1 (EmptyValidatorSet)
# G1 root binding: pvRoot == REGISTRY.validatorSetRoot() (ValidatorSetRootMismatch)
# G2 size binding: pvSize == n (SizeMismatch)
# G3 domain binding: pvDomain == attestationDomain (DomainMismatch)
# attestationDomain = keccak(DOMAIN_PREFIX, chainId, registry)
# G4 quorum: pvQuorum >= ceil(2N/3) = (2N+2)/3 (BelowQuorum)
# G5 non-zero block: pvBlockHash != 0 (ZeroBlockHash)
# G6 SP1 proof verifies through the pinned vkey + gateway (verifyProof reverts on bad proof)
#
# We model the guards as first-order constraints over the decoded public-values
# fields and prove each safety property by asserting its NEGATION under the guards
# and showing z3 returns UNSAT (no counterexample). Each NEG-CTRL removes exactly
# one guard and shows the corresponding bypass becomes satisfiable (SAT).
#
# Roots / domains / block hashes are modelled as Int identities (the proof needs
# equality / disequality, not byte layout). keccak256 is modelled as an INJECTIVE
# uninterpreted function (collision-resistance, marked [VERIFY]) exactly where the
# binding argument rests on "a different pre-image gives a different commitment".
# This checks the DESIGN-level guard logic, NOT the compiled EVM bytecode.
# -----------------------------------------------------------------------------
from z3 import (Int, Bool, Function, IntSort, BoolSort, Solver, And, Or, Not,
Implies, Distinct, ForAll, sat, unsat)
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()
print(" witness:", {str(d): m[d] for d in m.decls()})
return ok
def required_quorum(n):
# _quorumThreshold(n) = (2*n + 2) / 3, Solidity floor division for n >= 0.
return (2 * n + 2) / 3
def add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain,
pvQuorum, pvBlockHash,
g_quorum=True, g_root=True, g_domain=True):
"""Assert verifyCertificate()'s fail-closed guards. Flags let a NEG-CTRL drop one."""
s.add(n >= 1) # G0 EmptyValidatorSet
if g_root:
s.add(pvRoot == regRoot) # G1
s.add(pvSize == n) # G2
if g_domain:
s.add(pvDomain == expDomain) # G3
if g_quorum:
s.add(pvQuorum >= required_quorum(n)) # G4
s.add(pvBlockHash != 0) # G5
print("### AereFinalityCertificateVerifier -- FAIL-CLOSED certificate acceptance\n")
# ---- Q0 The threshold formula IS ceil(2N/3): required=(2N+2)/3 satisfies the
# ceiling characterization 3*required >= 2N AND 3*(required-1) < 2N, for all
# N >= 1. Negation UNSAT => the on-chain quorum is exactly ceil(2N/3). --------
s = Solver()
n = Int('n')
req = required_quorum(n)
s.add(n >= 1)
s.add(Not(And(3 * req >= 2 * n, 3 * (req - 1) < 2 * n))) # NEGATION of the ceil law
check("Q0 quorum threshold (2N+2)/3 == ceil(2N/3) for all N>=1", s)
# ---- Q0b Concrete anchor values: N=7 -> 5, N=9 -> 6, N=4 -> 3 (matches doc). The
# Solidity floor division (2N+2)/3 is asserted equal to the doc's quorum for each
# anchor N; the negation (any anchor wrong) is UNSAT. -----------------------
s = Solver()
bad = Bool('bad')
s.add(bad == Or((2*4+2)//3 != 3, (2*5+2)//3 != 4, (2*7+2)//3 != 5, (2*9+2)//3 != 6))
s.add(bad)
check("Q0b concrete quorum anchors N in {4,5,7,9} -> {3,4,5,6}", s)
# ---- P1 QUORUM: an accepted certificate has pvQuorum >= ceil(2N/3). Negation:
# all guards hold yet pvQuorum < required. UNSAT under G4. -------------------
s = Solver()
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash)
s.add(pvQuorum < required_quorum(n)) # NEGATION: sub-quorum accepted
check("P1 accepted certificate meets the ceil(2N/3) quorum (no sub-quorum accept)", s)
# ---- P1b Concrete N=7: a 4-of-7 certificate (below the 5 quorum) is rejected. ---
s = Solver()
pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
s.add(pvSize == 7, pvRoot == regRoot, pvDomain == expDomain, pvBlockHash != 0)
s.add(pvQuorum >= (2*7+2)//3) # G4 with N=7 => >= 5
s.add(pvQuorum == 4) # NEGATION: a 4-of-7 cert
check("P1b N=7: a 4-of-7 (sub-quorum) certificate cannot be accepted", s)
# ---- P2 ROOT BINDING: an accepted certificate's validatorSetRoot equals the LIVE
# registry root. Negation: accepted yet pvRoot != registeredRoot. UNSAT (G1). -
s = Solver()
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash)
s.add(pvRoot != regRoot) # NEGATION: stale / forged root accepted
check("P2 accepted certificate is bound to the live registry validatorSetRoot", s)
# ---- P2b SIZE BINDING: pvSize == N. Negation UNSAT (G2). ---------------------
s = Solver()
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash)
s.add(pvSize != n) # NEGATION
check("P2b accepted certificate's validatorSetSize equals the registry N", s)
# ---- P3 DOMAIN BINDING (anti cross-context replay): attestationDomain is
# keccak(DOMAIN_PREFIX, chainId, registry), modelled as an INJECTIVE function
# D(chainId, registry). A certificate produced for a DIFFERENT chain or
# registry has pvDomain = D(chainId2, registry2) with (chainId2,registry2) !=
# (chainId,registry), so by injectivity pvDomain != expDomain and G3 rejects.
# Negation: such a cross-context certificate is accepted. UNSAT. ------------
D = Function('D', IntSort(), IntSort(), IntSort()) # keccak(prefix, chainId, registry)
s = Solver()
chainId, registry = Int('chainId'), Int('registry')
chainId2, registry2 = Int('chainId2'), Int('registry2')
# collision-resistance / injectivity of the domain hash [VERIFY: keccak256]:
s.add(ForAll([chainId, registry, chainId2, registry2],
Implies(D(chainId, registry) == D(chainId2, registry2),
And(chainId == chainId2, registry == registry2))))
cid, reg = Int('cid'), Int('reg')
cid2, reg2 = Int('cid2'), Int('reg2')
expDomain = D(cid, reg) # this verifier's bound domain
pvDomain = D(cid2, reg2) # domain baked into the foreign cert
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash)
s.add(Or(cid2 != cid, reg2 != reg)) # a DIFFERENT chain or registry
check("P3 a certificate from another chain/registry is rejected (domain binding)", s)
# ---- P3b DOMAIN BINDING, direct form: accepted yet pvDomain != expected. UNSAT.
s = Solver()
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash)
s.add(pvDomain != expDomain) # NEGATION
check("P3b accepted certificate's attestationDomain equals the bound domain", s)
# ---- P4 KEY ROTATION invalidates an old-root certificate. The registry root
# commits to each validator's (address, keyEpoch, keyHash); a rotation bumps
# the epoch and changes the key hash, so the recomputed root changes. Model the
# root as an INJECTIVE function R(epochVec) [VERIFY: keccak256]. After a rotation
# the live root is newRoot = R(afterEpochs) != R(beforeEpochs) = oldRoot. A
# certificate carrying pvRoot == oldRoot then fails the (now-newRoot) G1 binding.
# Negation: an old-root certificate is still accepted after the rotation. UNSAT.
R = Function('R', IntSort(), IntSort()) # root as a function of the epoch commitment
s = Solver()
beforeEpochs, afterEpochs = Int('beforeEpochs'), Int('afterEpochs')
s.add(ForAll([beforeEpochs, afterEpochs],
Implies(R(beforeEpochs) == R(afterEpochs), beforeEpochs == afterEpochs)))
be, ae = Int('be'), Int('ae')
s.add(be != ae) # rotation changed the epoch commitment
oldRoot = R(be) # root the old cert was proven against
newRoot = R(ae) # live root after rotation
n = Int('n'); pvSize = Int('pvSize')
pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
# The live registry now returns newRoot; the old certificate carries pvRoot = oldRoot.
add_guards(s, n, oldRoot, newRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash)
check("P4 a key rotation (root changes) makes an old-root certificate unaccept-able", s)
# ---- P5 EMPTY SET fail-closed: with n == 0 no certificate is accepted (G0). ----
s = Solver()
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
# reuse the guards but force the empty set; G0 (n>=1) then contradicts n==0.
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash)
s.add(n == 0) # NEGATION of the non-empty guard
check("P5 empty validator set is rejected (EmptyValidatorSet, fail-closed)", s)
# ============================ NEGATIVE CONTROLS ==============================
# ---- NEG-CTRL 1 (drop the quorum guard G4): without pvQuorum >= ceil(2N/3), a
# sub-quorum certificate (e.g. 1 attestation of 7) is accepted. z3 finds it. --
s = Solver()
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash,
g_quorum=False) # DROP G4
s.add(n == 7, pvQuorum == 1) # a 1-of-7 certificate
check("no-quorum-check verifier CAN accept a sub-quorum certificate", s,
expect_unsat=False, kind="NEG-CTRL")
# ---- NEG-CTRL 2 (drop the root binding G1): without pvRoot == registeredRoot, a
# certificate proven against a STALE or FORGED validator set is accepted. -----
s = Solver()
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash,
g_root=False) # DROP G1
s.add(pvRoot != regRoot) # forged / stale root accepted
check("no-root-binding verifier CAN accept a stale/forged validator-set root", s,
expect_unsat=False, kind="NEG-CTRL")
# ---- NEG-CTRL 3 (drop the domain binding G3): without pvDomain == attestationDomain,
# a certificate from a DIFFERENT chain or registry replays here. z3 finds it. --
s = Solver()
n = Int('n'); pvRoot, regRoot = Int('pvRoot'), Int('regRoot')
pvSize = Int('pvSize'); pvDomain, expDomain = Int('pvDomain'), Int('expDomain')
pvQuorum, pvBlockHash = Int('pvQuorum'), Int('pvBlockHash')
add_guards(s, n, pvRoot, regRoot, pvSize, pvDomain, expDomain, pvQuorum, pvBlockHash,
g_domain=False) # DROP G3
s.add(pvDomain != expDomain) # cross-context certificate replays
check("no-domain-binding verifier CAN accept a cross-chain/registry certificate", s,
expect_unsat=False, kind="NEG-CTRL")
# ------------------------------------------------------------------------------
print("\n=== SUMMARY ===")
allok = True
for name, tag, ok, kind in results:
print(f" {tag:9} [{kind}] {name}")
allok = allok and ok
print()
if allok:
print("AereFinalityCertificateVerifier acceptance safety:")
print(" PROVED -- the on-chain quorum threshold is EXACTLY ceil(2N/3) (Q0/Q0b), and an")
print(" accepted certificate necessarily meets that quorum (P1/P1b), is bound to the live")
print(" registry root and size (P2/P2b), is bound to the chainId+registry attestation domain")
print(" so cross-context replay is rejected (P3/P3b), stops matching once a key rotation")
print(" changes the root (P4), and is rejected on an empty validator set (P5). Three NEG-CTRLs")
print(" fire: dropping the quorum, the root binding, or the domain binding each opens a real")
print(" bypass (sub-quorum accept / forged-root accept / cross-chain replay).")
print(" [VERIFY] SP1 GATEWAY SOUNDNESS: acceptance also requires SP1_GATEWAY.verifyProof to")
print(" succeed against the pinned PROGRAM_VKEY; verifyProof reverting on an invalid proof is a")
print(" trusted primitive, not re-proved here. The OFF-CHAIN zkVM aggregation circuit (that N")
print(" distinct hash-based attestations over the domain-bound block exist under the root) is")
print(" [MEASURE], NOT implemented, proven only against a MOCK gateway in the contract tests.")
print(" keccak256 injectivity (root / domain collision-resistance) is [VERIFY], modelled here.")
print(" DESIGN: additive PQ finality, Aere consensus stays classical ECDSA QBFT; this checks")
print(" the guard logic, not the compiled EVM bytecode.")
else:
print(" NOT fully established (see FAILED / unexpected CEX above).")
import sys
sys.exit(0 if allok else 1)