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.
157 lines
7.2 KiB
Plaintext
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).* }
|