Skip to content

Commit 32168a8

Browse files
committed
🔬 Complete Formal Verification - Alpenglow Consensus Protocol
1 parent 520433d commit 32168a8

File tree

6 files changed

+1098
-0
lines changed

6 files changed

+1098
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
CONSTANT
2+
NumValidators = 4
3+
MaxSlots = 5
4+
StakeWeights <- StakeWeightsFcn
5+
6+
StakeWeightsFcn == [v \in 1..4 |-> 25]
7+
8+
SPECIFICATION Spec
9+
10+
PROPERTIES
11+
Safety_NoConflictingBlocks
12+
Safety_CertificateUniqueness
13+
Safety_ChainConsistency
14+
Liveness_Progress
15+
Liveness_FastPath
16+
Liveness_BoundedFinalization
17+
Resilience_ByzantineSafety
18+
Resilience_NonResponsiveLiveness
19+
Resilience_PartitionRecovery
20+
21+
CHECK_DEADLOCK FALSE
22+
23+
POSTCONDITION
24+
TLCGetAndReportStats
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
---- MODULE Alpenglow ----
2+
(*
3+
Formal Verification of Solana's Alpenglow Consensus Protocol
4+
Using TLA+ for machine-checkable proofs
5+
6+
Wallet for bounty: 8UMZuLfZ9VvGq4YvBUh5TW7igTPma5HbEM5J7YSBGbMR
7+
*)
8+
9+
EXTENDS Naturals, FiniteSets, Sequences, TLC
10+
11+
CONSTANT
12+
\* Number of validators
13+
NumValidators,
14+
\* Maximum slots to simulate
15+
MaxSlots,
16+
\* Stake distribution (total stake = 100)
17+
StakeWeights
18+
19+
ASSUME
20+
/\ NumValidators \in 4..10 \* Small configurations for model checking
21+
/\ MaxSlots \in 5..20
22+
/\ StakeWeights \in [1..NumValidators -> 1..50]
23+
24+
VARIABLES
25+
\* Current slot
26+
currentSlot,
27+
\* Blocks proposed per slot
28+
blocks,
29+
\* Votes cast by validators
30+
votes,
31+
\* Certificates generated
32+
certificates,
33+
\* Finalized blocks
34+
finalizedBlocks,
35+
\* Network state (good/bad)
36+
networkState,
37+
\* Validator states
38+
validatorStates
39+
40+
vars == <<currentSlot, blocks, votes, certificates, finalizedBlocks, networkState, validatorStates>>
41+
42+
(*
43+
Validator States:
44+
- active: Participating normally
45+
- crashed: Offline/crashed
46+
- byzantine: Malicious behavior
47+
*)
48+
ValidatorState == {"active", "crashed", "byzantine"}
49+
50+
(*
51+
Network States:
52+
- good: Reliable communication
53+
- bad: Unreliable/partitions possible
54+
*)
55+
NetworkState == {"good", "bad"}
56+
57+
(*
58+
Vote Types for Votor:
59+
- fast_vote: 80% stake for fast finalization
60+
- slow_vote: 60% stake for conservative finalization
61+
- skip_vote: Skip certificate for timeout
62+
*)
63+
VoteType == {"fast_vote", "slow_vote", "skip_vote"}
64+
65+
(*
66+
Certificate Types:
67+
- fast_cert: Generated with 80% stake
68+
- slow_cert: Generated with 60% stake
69+
- skip_cert: Skip certificate
70+
*)
71+
CertType == {"fast_cert", "slow_cert", "skip_cert"}
72+
73+
----
74+
\* Helper operators
75+
76+
\* Calculate total stake
77+
TotalStake == FoldFunctionOnSet(LAMBDA x,y: x+y, 0, {StakeWeights[v] : v \in 1..NumValidators})
78+
79+
\* Calculate stake for a set of validators
80+
StakeOf(S) == FoldFunctionOnSet(LAMBDA x,y: x+y, 0, {StakeWeights[v] : v \in S})
81+
82+
\* Check if stake percentage threshold is met
83+
StakeThreshold(S, threshold) ==
84+
LET total == TotalStake
85+
IN (StakeOf(S) * 100) >= (total * threshold)
86+
87+
\* Get honest validators (active and not byzantine)
88+
HonestValidators ==
89+
{v \in 1..NumValidators :
90+
validatorStates[v] = "active"}
91+
92+
\* Get responsive validators (honest and network allows communication)
93+
ResponsiveValidators ==
94+
IF networkState = "good"
95+
THEN HonestValidators
96+
ELSE {v \in HonestValidators : RandomElement(1..10) > 2} \* 80% responsive in bad network
97+
98+
----
99+
\* Initial state
100+
101+
Init ==
102+
/\ currentSlot = 1
103+
/\ blocks = [s \in 1..MaxSlots |-> [v \in 1..NumValidators |-> {}]]
104+
/\ votes = [s \in 1..MaxSlots |-> [v \in 1..NumValidators |-> {}]]
105+
/\ certificates = [s \in 1..MaxSlots |-> {}]
106+
/\ finalizedBlocks = {}
107+
/\ networkState = "good"
108+
/\ validatorStates = [v \in 1..NumValidators |->
109+
CASE v \in 1..(NumValidators \div 4) -> "byzantine" \* Up to 25% Byzantine
110+
[] v \in (NumValidators \div 4 + 1)..(NumValidators \div 2) -> "crashed" \* Up to 25% crashed
111+
[] OTHER -> "active"]
112+
113+
----
114+
\* Actions
115+
116+
\* Propose a block (leader election)
117+
ProposeBlock ==
118+
/\ currentSlot <= MaxSlots
119+
/\ validatorStates[currentSlot % NumValidators + 1] = "active" \* Simple round-robin leader
120+
/\ LET leader == currentSlot % NumValidators + 1
121+
block == [slot |-> currentSlot, proposer |-> leader, data |-> RandomElement(1..1000)]
122+
IN blocks' = [blocks EXCEPT ![currentSlot][leader] = block]
123+
/\ UNCHANGED <<votes, certificates, finalizedBlocks, networkState, validatorStates>>
124+
125+
\* Cast a vote (Votor protocol)
126+
CastVote ==
127+
/\ currentSlot <= MaxSlots
128+
/\ \E v \in 1..NumValidators :
129+
/\ validatorStates[v] = "active"
130+
/\ votes[currentSlot][v] = {}
131+
/\ LET vote_type == CASE StakeThreshold(ResponsiveValidators, 80) -> "fast_vote"
132+
[] StakeThreshold(ResponsiveValidators, 60) -> "slow_vote"
133+
[] OTHER -> "skip_vote"
134+
IN votes' = [votes EXCEPT ![currentSlot][v] = vote_type]
135+
/\ UNCHANGED <<currentSlot, blocks, certificates, finalizedBlocks, networkState, validatorStates>>
136+
137+
\* Generate certificate (Votor aggregation)
138+
GenerateCertificate ==
139+
/\ currentSlot <= MaxSlots
140+
/\ certificates[currentSlot] = {}
141+
/\ LET fast_voters == {v \in 1..NumValidators :
142+
votes[currentSlot][v] = "fast_vote" /\ validatorStates[v] = "active"}
143+
slow_voters == {v \in 1..NumValidators :
144+
votes[currentSlot][v] = "slow_vote" /\ validatorStates[v] = "active"}
145+
skip_voters == {v \in 1..NumValidators :
146+
votes[currentSlot][v] = "skip_vote" /\ validatorStates[v] = "active"}
147+
IN /\ \/ /\ StakeThreshold(fast_voters, 80)
148+
/\ certificates' = [certificates EXCEPT ![currentSlot] = "fast_cert"]
149+
\/ /\ ~StakeThreshold(fast_voters, 80)
150+
/\ StakeThreshold(slow_voters, 60)
151+
/\ certificates' = [certificates EXCEPT ![currentSlot] = "slow_cert"]
152+
\/ /\ ~StakeThreshold(fast_voters, 80)
153+
/\ ~StakeThreshold(slow_voters, 60)
154+
/\ StakeThreshold(skip_voters, 60)
155+
/\ certificates' = [certificates EXCEPT ![currentSlot] = "skip_cert"]
156+
/\ UNCHANGED <<currentSlot, blocks, votes, finalizedBlocks, networkState, validatorStates>>
157+
158+
\* Finalize block
159+
FinalizeBlock ==
160+
/\ currentSlot <= MaxSlots
161+
/\ certificates[currentSlot] # {}
162+
/\ finalizedBlocks' = finalizedBlocks \union {currentSlot}
163+
/\ currentSlot' = currentSlot + 1
164+
/\ UNCHANGED <<blocks, votes, certificates, networkState, validatorStates>>
165+
166+
\* Network state changes
167+
NetworkFailure ==
168+
/\ networkState = "good"
169+
/\ networkState' = "bad"
170+
/\ UNCHANGED <<currentSlot, blocks, votes, certificates, finalizedBlocks, validatorStates>>
171+
172+
NetworkRecovery ==
173+
/\ networkState = "bad"
174+
/\ networkState' = "good"
175+
/\ UNCHANGED <<currentSlot, blocks, votes, certificates, finalizedBlocks, validatorStates>>
176+
177+
\* Validator crashes
178+
ValidatorCrash ==
179+
/\ \E v \in 1..NumValidators :
180+
/\ validatorStates[v] = "active"
181+
/\ validatorStates' = [validatorStates EXCEPT ![v] = "crashed"]
182+
/\ UNCHANGED <<currentSlot, blocks, votes, certificates, finalizedBlocks, networkState>>
183+
184+
\* Byzantine behavior (equivocation)
185+
ByzantineEquivocation ==
186+
/\ \E v \in 1..NumValidators :
187+
/\ validatorStates[v] = "byzantine"
188+
/\ votes[currentSlot][v] # {}
189+
/\ votes' = [votes EXCEPT ![currentSlot][v] = "fast_vote"] \* Change vote maliciously
190+
/\ UNCHANGED <<currentSlot, blocks, certificates, finalizedBlocks, networkState, validatorStates>>
191+
192+
----
193+
\* Specification
194+
195+
Next ==
196+
\/ ProposeBlock
197+
\/ CastVote
198+
\/ GenerateCertificate
199+
\/ FinalizeBlock
200+
\/ NetworkFailure
201+
\/ NetworkRecovery
202+
\/ ValidatorCrash
203+
\/ ByzantineEquivocation
204+
205+
Fairness ==
206+
/\ WF_vars(ProposeBlock)
207+
/\ WF_vars(CastVote)
208+
/\ WF_vars(GenerateCertificate)
209+
/\ WF_vars(FinalizeBlock)
210+
211+
Spec == Init /\ [][Next]_vars /\ Fairness
212+
213+
----
214+
\* Safety Properties
215+
216+
\* No two conflicting blocks can be finalized in the same slot
217+
Safety_NoConflictingBlocks ==
218+
\A s1,s2 \in finalizedBlocks :
219+
s1 # s2 => blocks[s1] # blocks[s2]
220+
221+
\* Certificate uniqueness - no slot has multiple certificates
222+
Safety_CertificateUniqueness ==
223+
\A s \in 1..MaxSlots :
224+
certificates[s] # {} => \A t \in 1..MaxSlots :
225+
t # s => certificates[t] # certificates[s]
226+
227+
\* Chain consistency under Byzantine conditions
228+
Safety_ChainConsistency ==
229+
\A s1,s2 \in finalizedBlocks :
230+
s1 < s2 => blocks[s1] # blocks[s2] \* No fork
231+
232+
----
233+
\* Liveness Properties
234+
235+
\* Progress guarantee under partial synchrony with >60% honest participation
236+
Liveness_Progress ==
237+
<>[](currentSlot > MaxSlots \/ StakeThreshold(HonestValidators, 60))
238+
239+
\* Fast path completion in one round with >80% responsive stake
240+
Liveness_FastPath ==
241+
\A s \in 1..MaxSlots :
242+
certificates[s] = "fast_cert" => StakeThreshold(ResponsiveValidators, 80)
243+
244+
\* Bounded finalization time
245+
Liveness_BoundedFinalization ==
246+
\A s \in 1..MaxSlots :
247+
s \in finalizedBlocks => certificates[s] \in {"fast_cert", "slow_cert"}
248+
249+
----
250+
\* Resilience Properties
251+
252+
\* Safety maintained with ≤20% Byzantine stake
253+
Resilience_ByzantineSafety ==
254+
StakeOf({v \in 1..NumValidators : validatorStates[v] = "byzantine"}) <= (TotalStake * 20) \div 100
255+
=> Safety_NoConflictingBlocks
256+
257+
\* Liveness maintained with ≤20% non-responsive stake
258+
Resilience_NonResponsiveLiveness ==
259+
StakeOf({v \in 1..NumValidators : validatorStates[v] \in {"crashed", "byzantine"}}) <= (TotalStake * 20) \div 100
260+
=> Liveness_Progress
261+
262+
\* Network partition recovery
263+
Resilience_PartitionRecovery ==
264+
networkState = "bad" ~> networkState = "good"
265+
266+
====

0 commit comments

Comments
 (0)