Skip to content

Commit d2c1d03

Browse files
claudehyperpolymath
authored andcommitted
proof(lean4): mechanize BFT quorum-intersection safety (Theorem 3)
Prove the Byzantine-safety invariant that formal/PhronesisConsensus.tla model-checks (for N=4, F=1) as a Lean theorem for ALL N, F. Model: agents are a universe list, votes/Byzantine are Bool predicates, cardinalities are countP. Every set fact is PROVED here, nothing about finite sets is assumed: * countP_incl_excl -- inclusion/exclusion: countP(p||q) + countP(p&&q) = countP p + countP q; * countP_or_le_length, countP_mono -- union bound + monotonicity. Headlines (sorry-free; #print axioms reports only Lean's standard propext / Quot.sound, plus Classical.choice for bft_agreement via Classical.em): * bft_no_two_quorums -- with n ≤ 3f+1 and a quorum threshold of 2f+1, two DISTINCT values cannot both reach a quorum. The contradiction is f+1 ≤ |overlap| ≤ f: inclusion/exclusion forces the overlap ≥ f+1, while honest-vote-once forces it ≤ f. * bft_agreement -- any two COMMITTED values are equal (a value is committed when its vote set reaches 2f+1; honest agents vote for at most one value; Byzantine may equivocate). This is the Agreement invariant the TLA+ spec checks, here proved for all N, F. Core Lean only (no Mathlib): by_contra is not core, so bft_agreement uses Classical.em + cases. Builds clean, no warnings. Docs: safety_proofs.md §3.4 gains a "Verified two ways" note (TLA+ model-check + Lean proof) and the §5 table marks BFT Safety "Model-checked (TLA+/TLC) + Mechanized (Lean 4)". https://claude.ai/code/session_01DQACj3RFmAPZaBPgR9SAaS
1 parent a088b15 commit d2c1d03

2 files changed

Lines changed: 125 additions & 1 deletion

File tree

academic/formal-verification/lean4/Phronesis.lean

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,109 @@ theorem bestMatch_maximal (m : PhrPolicy → Situation → Bool) :
748748

749749
end Arbitration
750750

751+
/-! # 19c. Byzantine Fault Tolerance — quorum intersection
752+
(companion to `formal/PhronesisConsensus.tla`)
753+
754+
The TLA+ spec model-checks Agreement (no two honest agents commit different
755+
values) for the instance N = 4, F = 1. Here the underlying combinatorial
756+
invariant is proved for ALL N, F: with `N ≤ 3F+1` and a quorum threshold of
757+
`2F+1`, two distinct values cannot both reach a quorum — because honest
758+
agents vote at most once and any two quorums overlap in more than F agents
759+
(hence in ≥ 1 honest agent).
760+
761+
Agents and votes are modelled by a universe list and `Bool` predicates; every
762+
cardinality fact (inclusion–exclusion, union bound, monotonicity) is *proved*
763+
here via `countP` inductions — nothing about finite sets is assumed. -/
764+
765+
section BFT
766+
767+
variable {Agent : Type}
768+
769+
/-- Inclusion–exclusion for `countP`: counting `p` and `q` separately equals
770+
counting their disjunction and their conjunction. -/
771+
theorem countP_incl_excl (p q : Agent → Bool) :
772+
∀ l : List Agent,
773+
l.countP (fun a => p a || q a) + l.countP (fun a => p a && q a)
774+
= l.countP p + l.countP q := by
775+
intro l
776+
induction l with
777+
| nil => rfl
778+
| cons a l ih =>
779+
simp only [List.countP_cons]
780+
cases hpa : p a <;> cases hqa : q a <;> simp [hpa, hqa] <;> omega
781+
782+
/-- A disjunction is counted no more than the size of the whole universe. -/
783+
theorem countP_or_le_length (p q : Agent → Bool) (l : List Agent) :
784+
l.countP (fun a => p a || q a) ≤ l.length :=
785+
List.countP_le_length _
786+
787+
/-- Monotonicity of `countP` under pointwise implication. -/
788+
theorem countP_mono (p q : Agent → Bool) (h : ∀ a, p a = true → q a = true) :
789+
∀ l : List Agent, l.countP p ≤ l.countP q := by
790+
intro l
791+
induction l with
792+
| nil => exact Nat.zero_le _
793+
| cons a l ih =>
794+
simp only [List.countP_cons]
795+
cases hpa : p a with
796+
| false => cases hqa : q a <;> simp [hpa, hqa] <;> omega
797+
| true => have hqa := h a hpa; simp [hpa, hqa] <;> omega
798+
799+
/-- **BFT Safety (quorum intersection).** With `n = agents.length` agents of
800+
whom `f = agents.countP byz` are Byzantine, a quorum threshold of `2f+1`,
801+
and `n ≤ 3f+1`, two *distinct* values cannot both reach a quorum. The two
802+
values are represented by vote predicates `vote1`, `vote2`; their
803+
distinctness is encoded by `honestVoteOnce` — any agent voting for both is
804+
Byzantine (an honest agent votes at most once). The contradiction is
805+
`f+1 ≤ |overlap| ≤ f`. -/
806+
theorem bft_no_two_quorums
807+
(agents : List Agent) (byz vote1 vote2 : Agent → Bool)
808+
(honestVoteOnce : ∀ a, vote1 a = true → vote2 a = true → byz a = true)
809+
(hn : agents.length ≤ 3 * agents.countP byz + 1)
810+
(hq1 : 2 * agents.countP byz + 1 ≤ agents.countP vote1)
811+
(hq2 : 2 * agents.countP byz + 1 ≤ agents.countP vote2) :
812+
False := by
813+
have hincl := countP_incl_excl vote1 vote2 agents
814+
have hunion := countP_or_le_length vote1 vote2 agents
815+
have hinter : agents.countP (fun a => vote1 a && vote2 a) ≤ agents.countP byz :=
816+
countP_mono (fun a => vote1 a && vote2 a) byz
817+
(by intro a h
818+
cases hv1 : vote1 a with
819+
| false => simp [hv1] at h
820+
| true =>
821+
cases hv2 : vote2 a with
822+
| false => simp [hv1, hv2] at h
823+
| true => exact honestVoteOnce a hv1 hv2)
824+
agents
825+
omega
826+
827+
/-- **BFT Agreement.** Any two *committed* values are equal. A value `v` is
828+
committed when its vote set reaches the `2f+1` threshold. Honest agents vote
829+
for at most one value (`honestSingleVote`); Byzantine agents may equivocate.
830+
Under `n ≤ 3f+1` two committed values must coincide — the safety invariant
831+
that `formal/PhronesisConsensus.tla` model-checks, here proved for all N, F. -/
832+
theorem bft_agreement
833+
{Value : Type} (agents : List Agent) (byz : Agent → Bool)
834+
(voteFor : Value → Agent → Bool)
835+
(honestSingleVote :
836+
∀ a v₁ v₂, byz a = false → voteFor v₁ a = true → voteFor v₂ a = true → v₁ = v₂)
837+
(hn : agents.length ≤ 3 * agents.countP byz + 1)
838+
(v₁ v₂ : Value)
839+
(hc1 : 2 * agents.countP byz + 1 ≤ agents.countP (voteFor v₁))
840+
(hc2 : 2 * agents.countP byz + 1 ≤ agents.countP (voteFor v₂)) :
841+
v₁ = v₂ := by
842+
cases Classical.em (v₁ = v₂) with
843+
| inl heq => exact heq
844+
| inr hne =>
845+
exact (bft_no_two_quorums agents byz (voteFor v₁) (voteFor v₂)
846+
(by intro a h1 h2
847+
cases hb : byz a with
848+
| true => rfl
849+
| false => exact absurd (honestSingleVote a v₁ v₂ hb h1 h2) hne)
850+
hn hc1 hc2).elim
851+
852+
end BFT
853+
751854
/-! # 20. Summary
752855
753856
Main theorems (all machine-checked on Lean 4 core; no `sorry`, only Lean's
@@ -768,6 +871,12 @@ end Arbitration
768871
13. bestMatch_maximal — the deciding policy has maximal priority among
769872
matches (higher-priority override; a high-priority
770873
REJECT is never undercut by a lower ACCEPT)
874+
14. bft_no_two_quorums — with n ≤ 3f+1 and threshold 2f+1, two distinct
875+
values cannot both reach a quorum (quorum
876+
intersection; all cardinality facts proved)
877+
15. bft_agreement — any two committed values are equal
878+
(BFT Agreement for all N, F; companion to
879+
formal/PhronesisConsensus.tla's model-check)
771880
Mirrors ../coq/Phronesis.v (preservation, eval_deterministic, totality).
772881
-/
773882

docs/safety_proofs.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,21 @@ Therefore, only valid actions can commit. ∎
368368

369369
**QED: Byzantine Safety holds.**
370370

371+
**Verified two ways:**
372+
373+
- **Model-checked (TLA+/TLC):** [`../formal/PhronesisConsensus.tla`](../formal/PhronesisConsensus.tla)
374+
exhaustively checks `Agreement`/`Validity`/`ByzantineSafety` for the instance
375+
N = 4, F = 1, with a negative test confirming the `2F+1` threshold is
376+
load-bearing (Agreement fails below it).
377+
- **Mechanized (Lean 4):** [`../academic/formal-verification/lean4/Phronesis.lean`](../academic/formal-verification/lean4/Phronesis.lean)
378+
proves the quorum-intersection argument for **all** N, F:
379+
`bft_no_two_quorums` (with `n ≤ 3f+1` and threshold `2f+1`, two distinct
380+
values cannot both reach a quorum) and `bft_agreement` (any two committed
381+
values are equal). Every cardinality fact — inclusion–exclusion
382+
(`countP_incl_excl`), the union bound, and monotonicity — is *proved* over
383+
`countP`, not assumed. All `sorry`-free (Lean's standard
384+
`propext`/`Quot.sound`/`Classical.choice`; confirm with `#print axioms`).
385+
371386
### 3.5 Liveness Under Partial Synchrony
372387

373388
**Theorem 4 (Eventual Liveness):**
@@ -437,7 +452,7 @@ Layer 5: Audit Log
437452
| Sandbox Isolation | Mechanized (Lean 4) | `academic/formal-verification/lean4/Phronesis.lean` |
438453
| Capability Soundness | Mechanized (Lean 4) | `academic/formal-verification/lean4/Phronesis.lean` |
439454
| Ethical Verdict Consistency | Mechanized (Lean 4) | `academic/formal-verification/lean4/Phronesis.lean` |
440-
| BFT Safety | Model-checked (TLA+/TLC) | `formal/PhronesisConsensus.tla` |
455+
| BFT Safety | Model-checked (TLA+/TLC) + Mechanized (Lean 4) | `formal/PhronesisConsensus.tla`, `academic/formal-verification/lean4/Phronesis.lean` |
441456
| BFT Liveness | Manual proof | This document |
442457
| Termination | Proven | Semantics doc |
443458
| Type Safety | Sketch | Semantics doc |

0 commit comments

Comments
 (0)