Solia saysIn production Ethereum, LMD-GHOST walks the actual block tree by counting each validator’s latest attestation as one vote for that branch — head is the heaviest subtree.
Theoretical LMD-GHOST said "walk the heaviest subtree by latest messages." In production the implementation is concrete: for every active validator, find their most recent attestation; add 1 to the weight of every block on the path from that attestation back to genesis. The head is then the leaf reachable by always picking the heaviest child.
Only the latest attestation counts — old ones do not accumulate. That property is why it is "Latest-Message-Driven" GHOST. Reorgs happen when a late block arrives after some validators have already attested to a sibling; the latest-message rule then converges on whichever branch gathers more recent votes. The demo walks a small tree given a snapshot of latest attestations.
Power-ups you unlock
Only the LATEST attestation per validator counts
Each latest attestation adds 1 to every ancestor block on its path
Head = follow heaviest child down to a leaf
Reorgs happen when attestation weights shift quickly
Equivocations (two latest atts) are slashable
The Reentrancy Reaper attacks — common mistakes
Summing all of a validator’s attestations — only the latest counts
Forgetting subtree weight includes descendants
Assuming head changes are instant — they require attestation arrival
Ignoring the slashing rules that keep latest messages honest
Boss battleGiven 7 latest attestations on a small fork tree, compute subtree weights and find the LMD-GHOST head.
Example code
<!doctype html><html><head><meta charset="utf-8"></head>
<body style="background:#06040d;color:#e6e0ff;font-family:monospace;padding:20px"><pre id="o"></pre>
<script>
// production LMD-GHOST: each LATEST attestation adds 1 to every ancestor on its path.
const tree = {
A:{children:['B','C']},
B:{children:['D']}, D:{children:[]},
C:{children:['E']}, E:{children:['F']}, F:{children:[]}
};
const parent = {};
for(const p in tree) for(const c of tree[p].children) parent[c] = p;
const latestAtts = { v1:'D', v2:'D', v3:'F', v4:'F', v5:'F', v6:'E', v7:'C' };
function weight(node){
let w = 0;
for(const v in latestAtts){
let cur = latestAtts[v];
while(cur){ if(cur === node){ w++; break; } cur = parent[cur]; }
}
return w;
}
function walk(start){
const path = [start];
while(tree[path[path.length-1]].children.length){
const cur = path[path.length-1];
let best = tree[cur].children[0];
for(const c of tree[cur].children) if(weight(c) > weight(best)) best = c;
path.push(best);
}
return path;
}
document.getElementById('o').textContent = [
'latest attestations: ' + Object.entries(latestAtts).map(([k,v]) => k + '→' + v).join(', '),
'subtree weights: A=' + weight('A') + ' B=' + weight('B') + ' C=' + weight('C') + ' D=' + weight('D') + ' E=' + weight('E') + ' F=' + weight('F'),
'LMD-GHOST head path: ' + walk('A').join(' → '),
'C branch (5 latest atts) beats B branch (2) → head = ' + walk('A').slice(-1)[0]
].join('\n');
</script></body></html>