|
1 | | -# 3SF-mini |
| 1 | +# 3SF-mini: Justification & Finalization |
2 | 2 |
|
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. |
4 | 6 |
|
5 | | -## Justifiable Slot Backoff |
| 7 | +## Concepts |
6 | 8 |
|
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) | |
12 | 16 |
|
13 | | -As an example, consider this scenario: |
| 17 | +## Justification via Supermajority |
14 | 18 |
|
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² | 3 | |
| 87 | +| 12 | 3 | 3×4 | 3 | |
| 88 | +| 16 | 2 | 4² | 4 | |
| 89 | +| 20 | 3 | 4×5 | 4 | |
| 90 | +| 25 | 2 | 5² | 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