This is a part of research for an ongoing project, so this is more of
a braindump. Read the original Gasper paper here.
⚠ NOTE: This is a WIP ⚠
Gasper is the consensus protocol used by Ethereum. It’s composed of 2 gadgets:
Validators have 2 jobs: propose blocks and make attestations
The dynamic availability vs. finality-favoring ledgers under network partition. Source: Ebb-and-Flow Protocols: A Resolution of the Availability-Finality Dilemma
V, return single leaf
block B to propose
BC slots, used to checkpoint for finality
(Casper!)
C=64 in production(B, i) and (B, j) for different
epochs i and jB and epoch
j, define EBB(B, j) as function that gets
block in highest slot by epoch j in chain(B)
j in
chain(B)
LEBB(B) = max([EBB(B, j) for j in epochs]) (latest
EBB in chain(B))B, EBB(B, 0) = genesis()slot(B) = jC for some j, B
is EBB in all chains that include B
B is last block in epoch j,
it’s EBB for all chains that include BB to be EBB for blocks in different
epochs → why we have (B, j) notation
Epoch boundary blocks under forks, showing how epoch boundary pairs can be across epochs. Source: Gasper
def lmdGhost(G: view) -> block:
B = genesis() # genesis block
M = latestAttestations() # one attestation per validator
while B not in G.leaves:
B = argmax([weight(G, child, M) for child in B.children])
return B
An example of LMD GHOST tree. Source: Gasper
i = jC+kP picked randomly for each slot and does runs
HLMD(view(P, i)) = B’ to propose B’
B’ has slot(B) = i,
B’.parent = B, newattests(B), and other
metadata
newattests(B) is all attestations for B
and not included in newattests(B*) for ancestor
B* of B
B seen but parents not,
newattests(B) ignored
(i + ½), validator V finds
B’ = HLMD(view(V, i+½)) and make attestation a
slot(a) = jC + kblock(a) = B’ ⇒ a attests to
block(a)
slot(block(a)) ≤ slot(a)
Different checkpoint edges and supermajority links. Source: Gasper
(A, j’) → (B, j) (via checkpoint
edge)G, a justified pair is
J(·) = justified pairs for view(A, j’) in J(G) and checkpoint edge
from (A, j’) → (B, j) ⇒ (B, j) in
J(G)
(B_gen, 0) in J(G)def protoHLMD(G: view) -> block:
(B_j, j) = getHighestEpochJustifiedPair(G)
B = B_j
M = latestAttestations()
while B not in G.leaves:
B = argmax([weight(G, child, M) for child in B.children])
return B
def HLMD(G: view) -> block:
(B_j, j) = max([J(l) for l in G.leaves]) # only leaves
B’ = [(B_j, j) in J(l) for l in G.leaves]
B = B_j, G’ = union([chain(b) for b in B’])
M = latestAttestations()
while B not in G’.leaves:
B = argmax([weight(G’, child, M) for child in B.children])
return B
B_l stores state of its last justified
pairM but NOT Casper attestations (frozen!)