aere-research/formal-consensus/FalconQuorum.qnt
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

157 lines
7.2 KiB
Plaintext

// -----------------------------------------------------------------------------
// FalconQuorum.qnt
//
// FORMAL MODEL of the AERE post-fork Falcon quorum-certificate consensus rule.
//
// Models the DESIGN described in consensus-pqc/QUORUM-DESIGN.md and implemented
// by FalconSealValidationRule (besu-consensus-pqc-quorum.patch):
//
// * There are N QBFT validators, each holding a Falcon-512 keypair.
// * When a validator commits a block it ALSO Falcon-signs the same commit hash
// and gossips the seal (validatorIndex, signature).
// * A post-fork ("blocking") block is VALID iff its embedded certificate carries
// >= quorum(N) DISTINCT valid Falcon seals from registered validators over the
// correct commit hash. (Java: calculateRequiredValidatorQuorum(N) = ceil(2N/3).)
// * ECDSA committed seals still govern in parallel; this model isolates the
// ADDED Falcon-quorum gate — the new safety/liveness surface.
//
// Adversary model (Falcon layer):
// * An HONEST validator signs at most one block per height and only produces
// seals that verify -> it can be counted toward at most ONE of two conflicting
// blocks.
// * A FALCON-FAULTY validator is one whose seal does NOT count toward an honest
// quorum: either it emits a signature that fails verification (registry
// mismatch / crash / correlated impl defect) OR it equivocates. A faulty
// node that equivocates CAN place a valid seal on BOTH conflicting blocks
// (it holds its own key), so it counts toward both.
//
// This is a check of the DESIGN's combinatorics, NOT of the Besu Java code.
// It never asserts anything about mainnet consensus (chain 2800 consensus is
// classical ECDSA QBFT; the Falcon quorum is not activated there).
//
// Besu quorum: quorum(N) = ceil(2N/3) (fastDivCeiling(2N,3))
// BFT fault bound: f(N) = floor((N-1)/3)
// -----------------------------------------------------------------------------
module falcon_quorum {
// -------- configuration --------
const N: int // number of validators (instantiated to 4, 5, 7 below)
// Besu's quorum: ceil(2N/3) = floor((2N+2)/3) for the integers we use.
pure def quorum(n: int): int = (2 * n + 2) / 3
// Standard BFT fault bound f = floor((N-1)/3).
pure def faultBound(n: int): int = (n - 1) / 3
// Validator index set 0..N-1.
pure def validators(n: int): Set[int] = 0.to(n - 1)
// -------- state --------
// signA / signB : the DISTINCT valid-seal index sets that end up embedded in
// the certificate of two (possibly conflicting) candidate
// blocks A and B at the SAME height.
// faulty : the Falcon-faulty validator set chosen by the adversary.
var signA: Set[int]
var signB: Set[int]
var faulty: Set[int]
val honest: Set[int] = validators(N).exclude(faulty)
// A block's certificate meets the blocking rule iff it carries >= quorum seals.
val certifiedA: bool = signA.size() >= quorum(N)
val certifiedB: bool = signB.size() >= quorum(N)
// Well-formedness of a certificate assignment under the adversary model:
// - every seal index is a real validator;
// - an HONEST validator never appears on both conflicting blocks (no equivocation)
// and never appears unless it actually signed (captured by membership);
// - FAULTY validators may appear on either/both (equivocation allowed).
val wellFormed: bool =
signA.subseteq(validators(N)) and
signB.subseteq(validators(N)) and
faulty.subseteq(validators(N)) and
honest.forall(h => not(signA.contains(h) and signB.contains(h)))
// Are A and B conflicting? Two distinct candidate blocks at one height.
// We treat them as conflicting whenever both certificates are non-empty and
// the seal sets differ in a way only possible for two different blocks.
// For the safety theorem we conservatively assume A != B (distinct blocks).
val conflicting: bool = true
// -------- adversary: pick faulty, signA, signB nondeterministically --------
action init = all {
nondet fs = validators(N).powerset().oneOf()
nondet sa = validators(N).powerset().oneOf()
nondet sb = validators(N).powerset().oneOf()
all {
faulty' = fs,
signA' = sa,
signB' = sb,
}
}
action step = all {
faulty' = faulty,
signA' = signA,
signB' = signB,
}
// -------- SAFETY --------
// Under the BFT assumption (<= f faulty), no two conflicting blocks can both
// reach a Falcon quorum. Honest signers split disjointly; faulty (<= f) may
// double-sign. So |signA| + |signB| <= |honest| + 2|faulty| = N + |faulty|.
// Both >= quorum requires 2*quorum <= N + |faulty| <= N + f, which is false
// for N in {4,5,7}. Hence the AND is impossible => safety.
val bftAssumption: bool = faulty.size() <= faultBound(N)
// The invariant to check (must hold in ALL reachable states):
val safety: bool =
(wellFormed and bftAssumption and conflicting)
implies not(certifiedA and certifiedB)
// Non-vacuity / assumption is load-bearing: DROP the <= f bound and safety CAN
// break. Apalache REFUTES this and returns a state where > f equivocating
// faulty validators put valid seals on BOTH conflicting blocks so each reaches
// quorum. This proves the safety theorem genuinely depends on <= f faults.
val safetyNoAssumption: bool =
(wellFormed and conflicting) implies not(certifiedA and certifiedB)
// -------- LIVENESS / no-halt boundary --------
// With exactly f Falcon-faulty validators, the honest set (N - f) can still
// form a quorum. This is a CONFIG theorem (state-independent): N - f >= Q.
val livenessAtF: bool = (N - faultBound(N)) >= quorum(N)
// With f+1 Falcon-faulty validators, the honest set is BELOW quorum, so no
// post-fork block can be certified -> the chain HALTS. This is exactly why
// N >= 7 is needed to tolerate 2 faults.
val haltAtFplus1: bool = (N - (faultBound(N) + 1)) < quorum(N)
// Sanity invariant that always holds (used by `quint run` to confirm the
// config theorems for this N):
val configTheorems: bool = livenessAtF and haltAtFplus1
// LIVENESS invariant (Apalache VERIFIES this holds): when at most f validators
// are Falcon-faulty and every honest validator signs the single canonical
// block A, the block IS certified -> the chain does NOT halt.
val livenessInv: bool =
(wellFormed and bftAssumption and signA == honest and signB == Set())
implies certifiedA
// HALT WITNESS (Apalache REFUTES this -> prints the counterexample state):
// this predicate CLAIMS "an (f+1)-fault halt configuration never occurs".
// Apalache finds it DOES occur: with f+1 faulty and every honest validator
// signing block A, |honest| = N-(f+1) < quorum, so A is NOT certified.
// That refuting state is exactly the f+1-fault halt. This is the reason
// N>=7 is required to tolerate 2 faults.
val noHaltAtFplus1: bool =
not(wellFormed
and faulty.size() == faultBound(N) + 1
and signA == honest
and signB == Set()
and not(certifiedA))
}
// Concrete instances -----------------------------------------------------------
module n4 { import falcon_quorum(N = 4).* }
module n5 { import falcon_quorum(N = 5).* }
module n7 { import falcon_quorum(N = 7).* }