Skip to content

Commit 0911bc4

Browse files
claudehyperpolymath
authored andcommitted
proof(consensus): fix + model-check the BFT consensus spec; wire TLC into CI
The TLA+ consensus spec had never been model-checked and was invalid: * TypeOK violated -- Commit logs a vote snapshot incl "NONE" (commits fire once the approval threshold is met, before all agents vote), but the log type only allowed APPROVE/REJECT. * Agreement mis-stated -- it asserted all committed actions are globally DISTINCT (violated by a legitimate re-commit), not the consensus property. * ByzantineSafety vacuous -- a constant-level formula (no state vars), so TLC asserted nothing about the protocol. Rewritten as a genuine single-round Byzantine quorum model (design decision 2026-06-14): per-agent commit views, an equivocating proposer (honest agents may receive different values) + Byzantine voters. Safety now rests on QUORUM INTERSECTION: * Agreement -- no two honest agents commit different values for a round; * Validity -- an honest commit is backed by a Threshold-quorum; * ByzantineSafety -- a committed value cannot be forged by the Byzantine minority alone (>= Threshold-|Byzantine| honest senders; Threshold > F). Verified with TLC (N=4, F=1, Threshold=2F+1=3): positive run -- no error; NEGATIVE run (Threshold=2 < 2F+1) -- Agreement violated, proving the quorum threshold is load-bearing (invariants are not vacuous). Adds .github/workflows/tla-consensus.yml: downloads tla2tools v1.8.0 (sha256-pinned), runs the positive gate + asserts the negative test fails. https://claude.ai/code/session_01DQACj3RFmAPZaBPgR9SAaS
1 parent 7dd9f3a commit 0911bc4

5 files changed

Lines changed: 186 additions & 192 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# tla-consensus.yml — model-check the Byzantine consensus spec with TLC.
3+
#
4+
# Validates formal/PhronesisConsensus.tla (per-agent commit views, equivocating
5+
# proposer + Byzantine voters). Two gates:
6+
# * positive — at Threshold = 2F+1 all safety invariants hold (TypeOK,
7+
# Agreement, Validity, ByzantineSafety);
8+
# * negative — below the quorum bound (Threshold = 2) TLC MUST report an
9+
# Agreement violation, proving the quorum threshold is load-bearing.
10+
name: TLA+ Consensus
11+
12+
on:
13+
push:
14+
branches: [main, master]
15+
pull_request:
16+
workflow_dispatch:
17+
18+
# Estate guardrail: cancel superseded runs (read-only check, safe to cancel).
19+
concurrency:
20+
group: ${{ github.workflow }}-${{ github.ref }}
21+
cancel-in-progress: true
22+
23+
permissions:
24+
contents: read
25+
26+
jobs:
27+
tlc:
28+
name: TLC model-check (BFT safety)
29+
runs-on: ubuntu-latest
30+
timeout-minutes: 15
31+
steps:
32+
- name: Checkout
33+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
34+
35+
- name: Fetch tla2tools (pinned version + sha256-verified)
36+
run: |
37+
curl -fsSL -o tla2tools.jar \
38+
https://github.com/tlaplus/tlaplus/releases/download/v1.8.0/tla2tools.jar
39+
echo "237332bdcc79a35c7d26efa7b82c77c85c2744591c5598673a8a45085ff2a4fb tla2tools.jar" \
40+
| sha256sum -c -
41+
42+
- name: TLC positive — all safety invariants hold at Threshold = 2F+1
43+
working-directory: formal
44+
run: |
45+
java -XX:+UseParallelGC -cp "${GITHUB_WORKSPACE}/tla2tools.jar" \
46+
tlc2.TLC -config PhronesisConsensus.cfg PhronesisConsensus.tla
47+
48+
- name: TLC negative — Agreement must fail below the quorum bound
49+
working-directory: formal
50+
run: |
51+
if java -cp "${GITHUB_WORKSPACE}/tla2tools.jar" \
52+
tlc2.TLC -config PhronesisConsensus_neg.cfg PhronesisConsensus.tla; then
53+
echo "::error::Agreement held at Threshold < 2F+1 — the quorum bound is not load-bearing (invariant vacuous)."
54+
exit 1
55+
fi
56+
echo "Negative test OK: Agreement correctly violated below the quorum bound."

formal/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# TLC model-checker artifacts
2+
*_TTrace_*.tla
3+
*_TTrace_*.bin
4+
states/
5+
*.st
6+
MC_*.out

formal/PhronesisConsensus.cfg

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,20 @@
1-
\* TLC Configuration for PhronesisConsensus
1+
\* SPDX-License-Identifier: MPL-2.0
2+
\* TLC configuration for PhronesisConsensus (Byzantine quorum model).
3+
\* N = 4 agents, F = 1 Byzantine (a4), Threshold = 2F+1 = 3, N >= 3F+1.
24
\* Run with: tlc PhronesisConsensus.tla -config PhronesisConsensus.cfg
35

46
CONSTANTS
5-
\* 4 agents (N=4, tolerates F=1 Byzantine agent)
67
Agents = {a1, a2, a3, a4}
7-
8-
\* Two possible actions for model checking
98
Actions = {act1, act2}
10-
11-
\* Bound rounds for finite state space
12-
MaxRounds = 3
13-
14-
\* Threshold for consensus (2F+1 = 3 for N=4, F=1)
9+
MaxRounds = 1
1510
Threshold = 3
16-
17-
\* Null constant
11+
Byzantine = {a4}
1812
NULL = NULL
1913

20-
SPECIFICATION
21-
Spec
14+
SPECIFICATION Spec
2215

2316
INVARIANTS
2417
TypeOK
25-
Safety
2618
Agreement
2719
Validity
28-
NonRepudiation
2920
ByzantineSafety
30-
31-
PROPERTIES
32-
\* Enable for liveness checking (requires fairness)
33-
\* EventualDecision
34-
\* Progress

0 commit comments

Comments
 (0)