freecoding.school100% FREE · NO SIGNUP
Proof PeaksISSUE #28 of 38

the ghost protocol · heaviest subtree

ZeekaVSThe Collision
Zeeka saysGHOST chooses the chain head by walking down the heaviest subtree at every fork — counting work in all descendants, not just one branch.

Longest-chain wastes every block on a "stale" sibling. GHOST (Greedy Heaviest Observed SubTree) reuses that work: at each fork, pick the child whose subtree contains the most cumulative work, not the longest single chain. Pre-Merge Ethereum used a GHOST variant that even rewarded uncles, redistributing some of the reward to stale siblings to keep small miners viable.

The trade-off is more bookkeeping for the fork-choice rule — you have to know the subtree weight of every candidate — but the payoff is less wasted work at high block rates. The demo builds a tree where the longest single chain points one way (depth 3) while the heaviest subtree points the other (4 leaves), and shows the two rules diverge on the same data.

Power-ups you unlock

The Collision attacks — common mistakes

Boss battleBuild a small block tree where longest-chain picks one head but GHOST picks another.

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>
// GHOST vs longest-chain on the same tree.
const tree = { A:['B','C'], B:['D'], D:['H'], H:[], C:['E','F','G'], E:[], F:[], G:[] };
const subtreeW = (n)=> 1 + tree[n].reduce((s,c)=> s + subtreeW(c), 0);
const depth = (n)=> tree[n].length === 0 ? 0 : 1 + Math.max(...tree[n].map(depth));
function walk(start, score){
  const path = [start];
  while(tree[path[path.length-1]].length){
    const cur = path[path.length-1]; let best = tree[cur][0];
    for(const c of tree[cur]) if(score(c) > score(best)) best = c;
    path.push(best);
  }
  return path;
}
const ghostPath  = walk('A', subtreeW);
const longestP   = walk('A', depth);
document.getElementById('o').textContent = [
  'subtree weights:  A=' + subtreeW('A') + '  B=' + subtreeW('B') + '  C=' + subtreeW('C') + '  D=' + subtreeW('D'),
  'longest-chain head: ' + longestP.join(' → ') + '  (depth ' + (longestP.length-1) + ')',
  'GHOST       head: ' + ghostPath.join(' → '),
  'GHOST picks C (subtree=4) over B (subtree=3); longest-chain picks the depth-3 branch B→D→H'
].join('\n');
</script></body></html>
▶ Open the interactive comic issue
‹ Nakamoto Consensus · Longest-Chain AnalysisCasper Ffg · The Finality Gadget ›