Glossary

Block Locator

A block locator is a data structure with a sparse list of block hashes that helps Bitcoin nodes efficiently synchronize their chain state.

Key Takeaways

  • A block locator is a compact list of block hashes that a Bitcoin node sends to a peer so they can find the most recent block both nodes share, known as the fork point.
  • The list uses exponential spacing: the first 10 hashes are consecutive, then the step size doubles for each subsequent entry (2nd, 4th, 8th, 16th block back), always ending with the genesis block. This keeps the locator to roughly 30 to 40 hashes regardless of chain height.
  • Block locators are carried inside the getheaders and getblocks P2P messages, making them the starting point for headers-first sync and chain synchronization between nodes.

What Is a Block Locator?

A block locator is a data structure used in Bitcoin's peer-to-peer protocol to help two nodes quickly identify where their copies of the blockchain diverge. When a node wants to synchronize with a peer, it cannot simply ask "send me everything after block X" because it does not know which blocks the peer has. The two nodes might share the same chain, or they might have diverged at some point due to a chain reorganization or simply being at different heights.

Instead of sending its entire chain (which would be enormous), the node constructs a block locator: a short, strategically chosen list of block hashes that gives the peer enough information to figure out the most recent ancestor they share. From that common point, the peer can respond with the headers or blocks the requesting node is missing.

In Bitcoin Core, the block locator is represented by the CBlockLocator class. It consists of a protocol version number and a vector of block hashes ordered from newest to oldest. The structure is defined in the chain header files and used throughout the networking code whenever a node needs to request chain data from a peer.

How It Works

The key insight behind the block locator is that most chain divergences are short. Two well-connected nodes typically agree on recent history and only differ by a few blocks at the tip. Long-range forks are extremely rare. The locator exploits this by providing dense coverage of recent blocks (where forks are likely) and sparse coverage of older blocks (where forks are unlikely).

Building the Locator

Bitcoin Core builds a block locator using the following algorithm:

  1. Start at the node's current chain tip and add its hash to the list
  2. Walk backward through the chain, adding each block's hash. For the first 10 entries, add every consecutive block
  3. After 10 entries, double the step size with each subsequent hash: skip 2, then 4, then 8, then 16 blocks, and so on
  4. Always include the genesis block hash as the final entry

The result is an ordered list that looks something like this for a node at block height 870,000:

Block 870,000  (tip)
Block 869,999  (tip - 1)
Block 869,998  (tip - 2)
...
Block 869,991  (tip - 9)     ← first 10 are consecutive
Block 869,989  (tip - 11)    ← step = 2
Block 869,985  (tip - 15)    ← step = 4
Block 869,977  (tip - 23)    ← step = 8
Block 869,961  (tip - 39)    ← step = 16
Block 869,929  (tip - 71)    ← step = 32
Block 869,865  (tip - 135)   ← step = 64
...
Block 0        (genesis)     ← always included

Logarithmic Efficiency

Because the step size doubles after the first 10 entries, the total number of hashes grows logarithmically with chain height. For a chain of height h, the locator contains approximately 10 + log2(h) entries. At a chain height of 870,000, this works out to roughly 10 + 20 = 30 hashes. Even if Bitcoin ran for centuries and reached a chain height of billions, a locator would still contain fewer than 50 hashes.

Each hash is 32 bytes, so the entire locator fits within roughly 1 to 1.5 KB of data: a trivially small message that can pinpoint any divergence point in a chain containing hundreds of thousands of blocks.

Finding the Fork Point

When a peer receives a block locator (inside a getheaders or getblocks message), it processes it as follows:

  1. Iterate through the locator hashes from newest to oldest
  2. For each hash, check whether it exists in the peer's own chain
  3. The first matching hash is the most recent common ancestor (fork point)
  4. Respond with headers or block inventory starting from the block after that fork point

If none of the locator hashes match (an unlikely case meaning the chains share nothing in common except genesis), the peer responds starting from block 1. The genesis block hash at the end of every locator guarantees that at least one match is always found.

Locator Precision

The exponential spacing means the locator might not identify the exact fork point on the first round trip. For example, if two chains diverged 500 blocks back, the locator's nearest hash might be at 512 blocks back due to the exponential step. The peer would respond with headers starting from that point, and the requesting node can then send a more precise locator if needed. In practice, the getheaders response of up to 2,000 headers per message covers any gap introduced by the sparse tail of the locator.

Use in P2P Messages

Block locators appear in two key P2P protocol messages:

getheaders

The getheaders message is the primary mechanism for headers-first synchronization. A node sends its block locator along with a stop hash (or a zero hash to request the maximum batch). The peer responds with a headers message containing up to 2,000 block headers starting from the fork point.

getheaders message format:
┌──────────────────────────┐
│ version (uint32)         │
│ hash_count (varint)      │
│ block_locator_hashes     │
│   [32 bytes × count]     │
│ hash_stop (32 bytes)     │
└──────────────────────────┘

The node repeats getheaders requests, updating the locator each time with the latest received header, until the peer has no more headers to send. This iterative process downloads the entire header chain before any full blocks are fetched: a key part of initial block download.

getblocks

The older getblocks message uses the same locator format but triggers a different response: the peer replies with an inv (inventory) message listing block hashes rather than full headers. This was the original synchronization mechanism before headers-first sync was introduced. Modern nodes prefer getheaders because downloading headers first lets a node validate the chain's proof-of-work before committing to full block downloads.

Why It Matters

Without block locators, nodes would face a difficult choice during synchronization: send their entire chain of hashes (wasteful) or guess a common block and risk being wrong (fragile). The locator solves this elegantly by compressing the chain into a logarithmic-sized summary that handles both common cases (recent forks of a few blocks) and rare cases (deep reorganizations) efficiently.

This mechanism is especially critical during initial block download, where a new node must synchronize hundreds of thousands of blocks from peers. The locator ensures the node can find where to start downloading efficiently, even when connecting to peers that may be on slightly different chain tips. For a deeper look at how Bitcoin nodes discover and communicate with each other, see the Bitcoin P2P network topology research article.

Layer-2 protocols like Lightning and Spark rely on the underlying Bitcoin node's ability to stay synchronized with the network. The block locator is a small but essential part of the infrastructure that keeps Bitcoin nodes in consensus about the current state of the chain.

Code Example

The following pseudocode illustrates how Bitcoin Core constructs a block locator. The actual implementation lives in the chain module and uses skip pointers for performance, but the core logic is straightforward:

function buildBlockLocator(tip):
    hashes = []
    step = 1
    height = tip.height

    while height >= 0:
        hashes.append(getBlockHash(height))
        // After the first 10 entries, double the step
        if hashes.length > 10:
            step = step * 2
        height = height - step

    // Always end with the genesis block
    if hashes[hashes.length - 1] != GENESIS_HASH:
        hashes.append(GENESIS_HASH)

    return hashes

And the peer's processing logic:

function processLocator(locatorHashes, stopHash):
    forkPoint = null

    for hash in locatorHashes:
        if myChain.contains(hash):
            forkPoint = myChain.getBlock(hash)
            break

    if forkPoint == null:
        forkPoint = genesisBlock

    // Return up to 2,000 headers after the fork point
    headers = []
    current = forkPoint.next
    while current != null and headers.length < 2000:
        headers.append(current.header)
        if current.hash == stopHash:
            break
        current = current.next

    return headers

Risks and Considerations

No Protocol-Level Size Limit

Bitcoin's original protocol specification does not impose an explicit maximum on the number of hashes in a block locator. In practice, the exponential spacing algorithm in Bitcoin Core produces at most about 42 hashes (given a 32-bit block height limit and a doubling exponent after 10 entries). However, a malicious node could theoretically send a locator with thousands of hashes, consuming processing time on the receiving node. Proposals on the Bitcoin development mailing list have suggested capping locator size at 64 entries to formalize this practical limit.

Eclipse Attack Considerations

If an attacker controls all of a node's peer connections (an eclipse attack), they could respond to locator requests with a fabricated chain. The node would sync headers from the attacker and potentially follow a malicious fork. This is why Bitcoin Core validates chain work during headers-first sync: the node only follows the chain with the most cumulative proof-of-work, making it expensive for an attacker to produce a convincing fake chain.

Locator Accuracy vs. Chain Depth

The exponential spacing trades precision for compactness. For forks deeper than about 10 blocks, the locator will not pinpoint the exact divergence block on the first attempt. The requesting node may receive some redundant headers it already has. This overhead is minimal in practice: the first round trip narrows the gap to within a factor of two, and subsequent exchanges can refine further. For the vast majority of real-world synchronization scenarios, a single locator round trip is sufficient.

This glossary entry is for informational purposes only and does not constitute financial or investment advice. Always do your own research before using any protocol or technology.