proof(lean4): mechanize BFT quorum-intersection safety (Theorem 3) - #45
Merged
Merged
Conversation
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
🔍 Hypatia Security ScanFindings: 30 issues detected
View findings[
{
"reason": "Action trufflesecurity/trufflehog@main needs attention",
"type": "unpinned_action",
"file": "secret-scanner.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Issue in secret-scanner.yml",
"type": "missing_timeout_minutes",
"file": "secret-scanner.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in scorecard-enforcer.yml",
"type": "scorecard_publish_with_run_step",
"file": "scorecard-enforcer.yml",
"action": "split_scorecard_publish_job",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "binary_to_term without :safe option -- deserialization attack (1 occurrences, CWE-502)",
"type": "elixir_send_unsanitised",
"file": "/home/runner/work/phronesis/phronesis/lib/phronesis/compiler.ex",
"action": "flag",
"rule_module": "code_safety",
"severity": "high"
},
{
"reason": "Nominal-only SAST in phronesis: codeql.yml language matrix contains no language present in the repo and lacks `actions`, so CodeQL records zero results on every commit. Remediation: set the CodeQL matrix to `language: actions`.",
"type": "StaticAnalysis",
"file": "/home/runner/work/phronesis/phronesis",
"action": "auto_fix",
"rule_module": "scorecard",
"severity": "medium",
"remediation": "Add CodeQL or equivalent SAST workflow.",
"scorecard_check": "SAST"
},
{
"reason": "Repository has 3 non-main remote branch(es). Policy: single main branch only.",
"type": "GS007",
"file": ".",
"action": "delete_remote_branches",
"rule_module": "git_state",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD007 -- Hypatia structural_drift: SD007 -- 2 day(s) old",
"type": "CSA001",
"file": ".claude/CLAUDE.md",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD007 -- Hypatia structural_drift: SD007 -- 2 day(s) old",
"type": "CSA001",
"file": ".claude/CLAUDE.md",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD007 -- Hypatia structural_drift: SD007 -- 2 day(s) old",
"type": "CSA001",
"file": ".claude/CLAUDE.md",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD007 -- Hypatia structural_drift: SD007 -- 2 day(s) old",
"type": "CSA001",
"file": ".claude/CLAUDE.md",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Mechanizes BFT Safety (Theorem 3) in Lean 4 as a real-proof companion to the existing TLA+ model-check. Where
formal/PhronesisConsensus.tlaexhaustively checks Agreement for the instance N = 4, F = 1, this proves the underlying quorum-intersection invariant for all N, F — and every cardinality fact is proved, not assumed.What's proved (
academic/formal-verification/lean4/Phronesis.lean)Model: agents = a universe
List, votes / Byzantine =Boolpredicates, cardinalities =countP.Cardinality lemmas (core-Lean inductions +
omega):countP_incl_excl—countP(p‖q) + countP(p&q) = countP p + countP qcountP_or_le_length— union boundcountP_mono— monotonicity under pointwise implicationHeadlines:
bft_no_two_quorums— withn ≤ 3f+1and threshold2f+1, two distinct values cannot both reach a quorum. The contradiction isf+1 ≤ |overlap| ≤ f: inclusion–exclusion forces 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 reaches2f+1; honest agents vote for at most one value; Byzantine may equivocate). This is exactly the TLA+Agreementinvariant, proved for all N, F.Verification
lake buildexit 0, no warnings, on Lean 4.12.0 core (no Mathlib).sorry-free.#print axiomsreports only Lean's standardpropext/Quot.sound(andClassical.choiceforbft_agreement, viaClassical.em—by_contraisn't a core tactic, so I usedClassical.em+cases).Docs
safety_proofs.md§3.4 gains a "Verified two ways" note (TLA+ model-check + Lean proof); the §5 status table marks BFT Safety = Model-checked (TLA+/TLC) + Mechanized (Lean 4).Status of the proof estate after this PR
Still recommended (deferred, noted on #44): a SHA-pinned Lean CI gate so these proofs are guarded against regression (blocked this session only by the GitHub API rate limit).
https://claude.ai/code/session_01DQACj3RFmAPZaBPgR9SAaS
Generated by Claude Code