§ 11 · Blockchain
The most-work chain
Which branch wins when the chain splits, and by how much it must win
On this page 7 sections
Mining is a lottery, and sometimes two people win almost at once. Two miners each find a valid block on top of the same parent, a few seconds apart, and send them out. Half the network hears about one first, half about the other. For a moment, the chain has two tips.
Nodes need a rule to settle this without talking it over. The rule is the same idea as Bitcoin’s: follow the branch with the most work. Pearl adds one twist, a safety margin before switching.
Try it
Build a short chain, then edit a block and watch every block after it break. The heavier unbroken chain is the one nodes keep.
Which branch do nodes follow?
The valid one with the most total workChain work: The total expected work behind a chain, added up from each block's target. On Pearl it is roughly a count of multiply-accumulates.. Each block’s work is 2²⁵⁶ ÷ (target + 1), the expected effort to win it, and a branch’s work is the sum over all its blocks, back to genesis.
Note “most work”, not “most blocks”. Because work grows with difficulty, a long chain of easy blocks can lose to a short chain of hard ones. In practice, with WTEMA keeping neighboring blocks at almost the same difficulty, the longer branch nearly always wins.
The newest block of the branch a node follows is its tipChain tip: The newest block of the chain a node currently follows. New blocks are built on top of it.. Everything below is “the chain” as far as that node is concerned.
What is Pearl’s quarter-block margin?
A node switches from its current tip to a competing one only if both of these hold:
- the competing branch has strictly more total work, and
- its lead is at least a quarter of the work of the smaller of the two tip blocks.
At block 130,827’s difficulty, one block’s work is 11,303,469,090,474,696,447,598, so the margin is 2,825,867,272,618,674,111,899.
Pearl’s code was run on this rule with competing branches whose lead was a fraction of one block’s work:
| Lead of the competing branch | Switch? |
|---|---|
| 0 (a tie) | no |
| 1/16 of a block | no |
| 3/16 of a block | no |
| 4/16 = 1/4 of a block | yes |
| 5/16 of a block | yes |
| one whole block | yes |
What happens in a tie?
Nothing: equal work fails the first condition, so a node keeps the tip it already has. This is the first-seen ruleFirst-seen rule: When two competing branches carry exactly the same work, a node keeps the one it received first instead of switching., as in Bitcoin.
On Pearl, a fresh split is always an exact tie. A block’s required bits depend only on its parent’s bits and its parent’s solve time (that’s WTEMA’s one-block lag). Two blocks on the same parent therefore need identical bits and carry identical work. Whichever one a node hears first, it keeps.
The tie breaks when the next block arrives. Whichever branch it extends now leads by a whole block of work, far beyond the quarter-block margin, and every node moves there.
Why add a margin at all?
The code’s own comment gives the reason: to prevent “trivial-margin reorgs”. Without it, a branch could win by a crumb of work.
Where would a crumb come from? From timestamps. Two branches of the same length, split two or more blocks back, can carry slightly different bits, because each branch’s timestamps steer its own WTEMA steps.
What is a reorg?
When a node does switch, it performs a reorganizationReorg: When a node drops the newest block or blocks of its chain because a competing branch with more work appeared. Transactions from the dropped blocks go back to waiting.: it disconnects blocks from its old tip back to the point where the branches split, then connects the new branch’s blocks in order, checking each one.
- Transactions in the disconnected blocks that aren’t in the new branch go back to the mempoolMempool: Each node's waiting room for valid transactions that aren't in a block yet. Miners pick from it, usually the best-paying ones first., to be mined again.
- The disconnected blocks become staleStale block: A valid block that lost a race: a different block at the same height ended up in the winning chain, so its reward can never be spent.. Their coinbase rewards never existed as far as the chain is concerned, which is why coinbase outputs wait 100 blocks before they can be spent.
- A reorg can never reach below the latest checkpoint: nodes refuse blocks that fork the chain before it.
Pearl’s light client uses the same rule, with the same quarter-block margin, when its headers disagree.
How many confirmations are enough?
There’s no protocol answer; it depends on how much is at stake. Each confirmationConfirmation: The block holding your transaction counts as one confirmation, and each block added on top counts as one more. The more confirmations, the harder it is to undo. is another block of work an attacker would have to redo, and then outrun the honest network by a quarter of a block. Waiting longer makes undoing a payment rapidly harder.
Make a guess
A node follows branch A, 5 blocks above the split. Branch B appears with 5 blocks too, and slightly more total work. Does the node switch?
Think of your answer first, then check it.
Answer Only if B's lead is at least a quarter of a block's work.
Five blocks each, built from the same starting difficulty a few minutes apart, typically differ in total work by well under one percent of a block. That’s far below the quarter-block margin, so the node stays on A. When either branch gains a sixth block, it leads by about a whole block and wins.
Code: ShouldChangeTip
From chain.go (node/blockchain/chain.go:1166-1188 at v1.5.2, on GitHub):
func ShouldChangeTip(currentWork, proposedWork, currentTipBlockWork, proposedTipBlockWork *big.Int) bool {
if currentWork.Cmp(proposedWork) >= 0 {
return false // not strictly more work: keep the current tip
}
diff := new(big.Int).Sub(proposedWork, currentWork)
minBlockWork := currentTipBlockWork
if proposedTipBlockWork.Cmp(currentTipBlockWork) < 0 {
minBlockWork = proposedTipBlockWork
}
threshold := new(big.Int).Rsh(minBlockWork, 2) // ÷ 4, despite the comment's "half"
return diff.Cmp(threshold) >= 0
} The light client calls the same function in
spv/blockmanager.go (spv/blockmanager.go:2669-2672 at v1.5.2, on GitHub).