Skip to content

Commit 403f887

Browse files
committed
docs: add documentation about 3SF-mini and LMD-GHOST
1 parent 55ae96f commit 403f887

2 files changed

Lines changed: 1023 additions & 17 deletions

File tree

docs/3sf_mini.md

Lines changed: 269 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,274 @@
1-
# 3SF-mini
1+
# 3SF-mini: Justification & Finalization
22

3-
TODO: add 3SF-mini explanation
3+
ethlambda uses **3SF-mini** (Three-Stage Finality, minimal version) for justification
4+
and finalization. Unlike the Ethereum Beacon Chain's epoch-based Casper FFG, 3SF-mini
5+
operates at the **slot level**: any slot can be justified, not just epoch boundaries.
46

5-
## Justifiable Slot Backoff
7+
## Concepts
68

7-
The 3SF-mini algorithm introduces a backoff mechanism to increase finalization rate during periods of asynchrony.
8-
This is achieved by "diluting" the possible targets of a justification vote, through the `slot_is_justifiable_after` function (`Slot.is_justifiable_after` in the spec).
9-
The function marks only some slots as valid justification targets, with the distance between them increasing over time since the last finalization.
10-
This increases the period during which votes for a given slot can be included, improving the chances of achieving the required 2/3 majority for justification.
11-
Also, since two consecutive justified **justifiable** slots are needed to finalized a slot, this backoff isn't immediately reset after finalization occurs, only lowering over time when synchrony is restored.
9+
| Term | Meaning |
10+
|------|---------|
11+
| **Justified** | A checkpoint backed by ≥2/3 validator votes |
12+
| **Finalized** | A checkpoint that can never be reverted |
13+
| **Source** | The latest justified checkpoint (vote origin) |
14+
| **Target** | The checkpoint being voted for (vote destination) |
15+
| **Justifiable** | A slot that *could* become justified (per the 3SF-mini schedule) |
1216

13-
As an example, consider this scenario:
17+
## Justification via Supermajority
1418

15-
- The last finalized slot is 0.
16-
- Slot 1 is justified.
17-
- During the next 14 slots (2 to 15), only some votes with differing targets are included, so no new justification occurs.
18-
- At slots 16, 17, 18, and 19, the last justifiable slot is 16, so enough votes are included to justify slot 16 (with slot 1 as source).
19-
- Since there are multiple justifiable slots between 1 and 16, slot 1 isn't finalized yet.
20-
- Slot 20 is reached, and in the following slots, enough votes are included to justify it, with slot 16 as source.
21-
- Since slots 16 and 20 are consecutive justifiable slots, slot 16 is now finalized (and past slots too).
22-
- The backoff is effectively reduced, since the next justifiable slots after 20 are 21, 22, 25, 28, and so on.
19+
A checkpoint becomes **justified** when ≥2/3 of validators attest to it as a target:
20+
21+
```text
22+
JUSTIFICATION
23+
─────────────
24+
25+
Validators: V0 V1 V2 V3 V4 V5 V6 V7 V8
26+
│ │ │ │ │ │ │
27+
└───┴───┴───┴───┴───────┴───┘
28+
29+
7 out of 9 votes
30+
(3×7=21 ≥ 2×9=18) ✓
31+
32+
33+
┌──────────────┐
34+
│ Checkpoint C │
35+
│ JUSTIFIED ✓ │
36+
└──────────────┘
37+
```
38+
39+
The threshold is computed as: `3 × vote_count ≥ 2 × validator_count`
40+
41+
Attestations must also pass validity checks before they count:
42+
- Source checkpoint must already be justified
43+
- Target must not already be justified
44+
- Source slot < Target slot (time flows forward)
45+
- Both checkpoints must reference known blocks
46+
- Target slot must be **justifiable** per the 3SF-mini schedule (see below)
47+
48+
## The Justifiability Schedule
49+
50+
Not every slot can be justified, only slots at specific distances from the last
51+
finalized slot. This is the novel part of 3SF-mini.
52+
53+
A slot is **justifiable** if `delta = slot - finalized_slot` matches any rule:
54+
55+
```text
56+
┌─────────────────────────────────────────────────────────┐
57+
│ JUSTIFIABILITY RULES │
58+
│ │
59+
│ Rule 1: delta ≤ 5 (first 5 always OK) │
60+
│ │
61+
│ Rule 2: delta = n² (perfect squares) │
62+
│ 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, ... │
63+
│ │
64+
│ Rule 3: delta = n(n+1) (pronic numbers) │
65+
│ 2, 6, 12, 20, 30, 42, 56, 72, 90, 110, ... │
66+
│ │
67+
└─────────────────────────────────────────────────────────┘
68+
```
69+
70+
Visualizing the first 40 slots after finalization (✓ = justifiable):
71+
72+
```text
73+
delta: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
74+
✓ ✓ ✓ ✓ ✓ ✓ ✓ · · ✓ · · ✓ · · · ✓ · · · ✓
75+
╰──────── R1 ────────╯ R3 R2 R3 R2 R3
76+
77+
delta: 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
78+
· · · · ✓ · · · · ✓ · · · · · ✓ · · · ·
79+
R2 R3 R2+R3
80+
```
81+
82+
| delta | Rule | Formula | Gap since previous |
83+
|-------|------|---------|--------------------|
84+
| 0–5 | 1 | ≤ 5 ||
85+
| 6 | 3 | 2×3 | 1 |
86+
| 9 | 2 || 3 |
87+
| 12 | 3 | 3×4 | 3 |
88+
| 16 | 2 || 4 |
89+
| 20 | 3 | 4×5 | 4 |
90+
| 25 | 2 || 5 |
91+
| 30 | 3 | 5×6 | 5 |
92+
| 36 | 2+3 | 6²=6×6 | 6 |
93+
94+
**Key property:** Gaps between justifiable slots grow, but never become infinite.
95+
As more time passes since finalization, the network gets progressively wider windows
96+
to accumulate votes. This creates a natural backpressure: if the network is struggling
97+
to reach 2/3 consensus (e.g., due to partitions or validator dropouts), the increasing
98+
gaps give more time for the supermajority to form.
99+
100+
### Justifiable Slot Backoff
101+
102+
The justifiability schedule acts as a backoff mechanism to increase finalization rate
103+
during periods of asynchrony. By "diluting" the possible targets of a justification
104+
vote (via the `slot_is_justifiable_after` function), the protocol increases the window
105+
during which votes for a given slot can be included, improving the chances of achieving
106+
the required 2/3 majority.
107+
108+
Since two consecutive justified **justifiable** slots are needed to finalize, this
109+
backoff isn't immediately reset after finalization occurs; it only lowers over time
110+
when synchrony is restored.
111+
112+
**Example:**
113+
114+
```
115+
Finalized slot = 0. Justifiable slots (✓):
116+
117+
slot: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
118+
F ✓ ✓ ✓ ✓ ✓ ✓ · · ✓ · · ✓ · · · ✓ · · · ✓
119+
```
120+
121+
**Phase 1: Slot 1 justified, but no progress for a while.**
122+
123+
```
124+
slot: 0 1 2 3 4 5 6 7 8 9 ... 15
125+
F J · · · · · · · · ·
126+
127+
└── Justified (source=0, 2/3 votes)
128+
129+
Slots 2–15: votes arrive with differing targets,
130+
no single slot accumulates 2/3 → no new justification.
131+
```
132+
133+
**Phase 2: Backoff helps. Slot 16 justified (gap = 4 slots since last justifiable).**
134+
135+
```
136+
slot: 0 1 ... 12 13 14 15 16 17 18 19
137+
F J ✓ · · · ✓ · · ·
138+
139+
┌───────────┘
140+
Justified (source=1)
141+
142+
Slot 16 is built. Validators begin voting for it (source=1, target=16).
143+
During slots 17–19 there is no new justifiable target to compete with
144+
slot 16, so all votes funnel toward it, giving 2/3 enough time to
145+
converge on a single target.
146+
147+
Can we finalize slot 1?
148+
Justifiable slots between 1 and 16: 2, 3, 4, 5, 6, 9, 12
149+
These are unjustified gaps → slot 1 NOT finalized yet.
150+
```
151+
152+
**Phase 3: Slot 20 justified, slot 16 finalized.**
153+
154+
```
155+
slot: 0 1 ... 16 17 18 19 20
156+
F J J · · · J
157+
▲ ▲
158+
source ────────▶ target
159+
160+
Justified (source=16, target=20).
161+
162+
Can we finalize slot 16?
163+
Slots between 16 and 20: 17, 18, 19
164+
Justifiable? 17: delta=17 → ✗ 18: delta=18 → ✗ 19: delta=19 → ✗
165+
But wait: are 16 and 20 consecutive *justifiable* slots?
166+
Next justifiable after 16: 20 (delta=20 = 4×5 ✓)
167+
Yes! No justifiable gaps → slot 16 FINALIZED ✓
168+
(and all slots before it)
169+
```
170+
171+
**After finalization of slot 16, backoff resets.**
172+
173+
```
174+
New finalized slot = 16. Justifiable slots shift:
175+
176+
slot: 16 17 18 19 20 21 22 23 24 25 26
177+
F ✓ ✓ ✓ ✓ ✓ ✓ · · ✓ ·
178+
╰──────── delta ≤ 5 ────────╯
179+
180+
The gaps shrink back to 1 slot apart; fast finalization
181+
resumes as long as the network stays synchronous.
182+
```
183+
184+
## Finalization
185+
186+
A justified checkpoint becomes **finalized** when there are **no unjustifiable gaps**
187+
between its source and target. The intuition: if every slot between source and target
188+
*could have been* justified, then the chain of justifications is unbroken, and the
189+
source is safe to finalize.
190+
191+
```text
192+
FINALIZATION CHECK
193+
──────────────────
194+
195+
Finalized Source (justified) Target (justified)
196+
│ │ │
197+
▼ ▼ ▼
198+
┌───┬───┬───┬──────┬───┬───┬───┬───┬────┐
199+
│ F │ │ │ S │ │ │ │ │ T │
200+
└───┴───┴───┴──────┴───┴───┴───┴───┴────┘
201+
slot ◄─── check ───►
202+
10 11 12 13 14 15 16 17 18
203+
204+
For each slot between S and T (exclusive: 14, 15, 16, 17):
205+
Is it justifiable after F (slot 10)?
206+
14: delta=4 ≤ 5 → justifiable ✓
207+
15: delta=5 ≤ 5 → justifiable ✓
208+
16: delta=6 = 2×3 → justifiable ✓
209+
17: delta=7 → NOT justifiable ✗ ← gap found!
210+
211+
Result: S is NOT finalized (unjustifiable gap at slot 17)
212+
```
213+
214+
The logic behind this: if slot 17 *cannot* be justified, then an attacker could
215+
potentially create an alternative justified chain that diverges at slot 17, undermining
216+
the source's finality. Only when every intermediate slot *could have* been justified
217+
is the chain of justifications considered airtight.
218+
219+
```text
220+
Alternative scenario (no gaps):
221+
222+
Source: slot 13 (delta from F=10: 3)
223+
Target: slot 16 (delta from F=10: 6)
224+
225+
Check slots 14, 15:
226+
14: delta=4 ≤ 5 → justifiable ✓
227+
15: delta=5 ≤ 5 → justifiable ✓
228+
229+
No unjustifiable gaps → S is FINALIZED ✓
230+
```
231+
232+
## Worked Example: Justification and Finalization
233+
234+
```text
235+
Setup: 4 validators (V0–V3), finalized at slot 100
236+
237+
Slot 100: [FINALIZED] ← anchor
238+
Slot 101: Block proposed, justifiable? delta=1 ≤ 5 → YES
239+
Slot 102: Block proposed, justifiable? delta=2 ≤ 5 → YES
240+
```
241+
242+
**Round 1: Justify slot 101.**
243+
244+
Validators attest with `source=100, target=101`. 3 of 4 vote:
245+
246+
```text
247+
source target
248+
(slot 100) (slot 101)
249+
│ │
250+
▼ ▼
251+
[ 100 ]─────▶[ 101 ] 3/4 votes → 3×3=9 ≥ 2×4=8 → JUSTIFIED ✓
252+
```
253+
254+
**Round 2: Justify slot 102 and finalize slot 101.**
255+
256+
Validators attest with `source=101, target=102`:
257+
258+
```text
259+
source target
260+
(slot 101) (slot 102)
261+
│ │
262+
▼ ▼
263+
[ 101 ]─────▶[ 102 ] 3/4 votes → JUSTIFIED ✓
264+
265+
└── Finalization check:
266+
Slots between 101 and 102 (exclusive): NONE
267+
No unjustifiable gaps → slot 101 FINALIZED ✓
268+
```
269+
270+
**After finalization of slot 101:**
271+
- `justified_slots` window shifts forward (old slots pruned)
272+
- LiveChain entries for slots ≤101 are pruned
273+
- Gossip signatures and aggregation proofs for finalized blocks are cleaned up
274+
- Future fork choice runs start from slot 101's successor, never looking further back

0 commit comments

Comments
 (0)