The Blockchain Chief Bitcoin Book / Part III: Validation & Consensus
Chapter 07

Block Validation, Consensus & Mining

How blocks are validated and connected to the chain: ProcessNewBlock, ConnectBlock, the UTXO set, proof of work, difficulty adjustment, and block template creation.

Block Validation Pipeline

When a new block arrives, it goes through a multi-stage validation pipeline. Each stage is progressively more expensive:

CheckBlockHeader()Verify proof of work (cheap, O(1))
AcceptBlockHeader()Check it connects to a known block, not a duplicate
AcceptBlock()Check block body (tx count, sizes), store to disk
ActivateBestChain()Determine if this block extends the best chain
ConnectTip() → ConnectBlock()Execute every transaction, update UTXO set (most expensive)
💡 Why This Order?

Cheap checks come first. Verifying PoW is nearly free and rejects most spam. Storing to disk happens before full validation so blocks can be validated later without re-downloading. This design made headers-first sync possible.

ProcessNewBlock()

The entry point for all incoming blocks. Located in validation.cpp:

What It Does

  1. Check PoW: reject immediately if the block doesn't meet the difficulty target
  2. AcceptBlock(): validate header chain, check body, write block to disk (blk*.dat files)
  3. ActivateBestChain(): if this block creates a chain with more work, switch to it

Chainstate Manager

The ChainstateManager coordinates potentially two chainstates:

ConnectBlock()

This is the most critical function in all of Bitcoin Core. It applies a block to the UTXO set:

Step by Step

  1. Check BIP30: ensure no duplicate transaction IDs
  2. Determine script flags: which consensus rules apply at this height (soft forks)
  3. For each transaction:
    • Look up all input UTXOs from the CCoinsView
    • Check input values (no negative, no overflow)
    • Verify sequence locks (relative timelocks)
    • Add script checks to the verification queue
    • Spend inputs: mark UTXOs as spent
    • Create outputs: add new UTXOs to the set
  4. Run all script checks: verify signatures (parallelized)
  5. Verify block reward: coinbase output ≤ subsidy + total fees
  6. Update the UTXO database: flush changes
🔴 Critical Safety

If ConnectBlock fails after partially updating the UTXO set, the changes are rolled back using the CCoinsViewCache undo mechanism. Failed blocks never corrupt the UTXO database.

The UTXO Set

The UTXO set is the most important state in Bitcoin; it represents who owns what:

CCoinsView Hierarchy

Bitcoin Core uses a layered caching system for UTXO access:

CCoinsViewCache In-memory cache, fast reads, batched writes
CCoinsViewDB On-disk LevelDB storage, persistent
CCoinsViewErrorCatcher Error handling layer between cache and disk

Coin Structure

Each UTXO is stored as a Coin object:

The key for lookup is the COutPoint (txid + output index). The UTXO set currently holds ~180 million entries.

Proof of Work

A block's proof of work is verified by CheckProofOfWork():

bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params) {
    // 1. Decode compact target from nBits
    arith_uint256 bnTarget;
    bnTarget.SetCompact(nBits);

    // 2. Target must be positive and below powLimit
    if (bnTarget <= 0 || bnTarget > UintToArith256(params.powLimit))
        return false;

    // 3. Block hash must be below target
    if (UintToArith256(hash) > bnTarget)
        return false;

    return true;
}

The core idea: the block header hash (double SHA-256) must be numerically less than the target value encoded in nBits. A lower target = higher difficulty = more computational work required.

Difficulty Adjustment

Every 2,016 blocks (~2 weeks), the difficulty adjusts to maintain ~10-minute block times:

The Algorithm

  1. Measure how long the last 2,016 blocks actually took (actualTimespan)
  2. Compare to the target: 2,016 × 10 minutes = 20,160 minutes (targetTimespan)
  3. Calculate: newTarget = oldTarget × (actualTimespan / targetTimespan)
  4. Clamp: timespan is limited to range [3.5 days, 8 weeks] to prevent extreme swings

If blocks came faster than 10 minutes → target decreases (harder). If slower → target increases (easier).

💡 Compact Target Format (nBits)

The target is stored as a 4-byte compact representation. The first byte is the exponent (number of bytes), and the remaining 3 bytes are the mantissa. For example, 0x1d00ffff means a target that starts with 00ffff shifted left by (0x1d - 3) = 26 bytes.

Consensus Parameters

Key constants that define Bitcoin's rules:

powLimitMaximum (easiest) target allowed
nPowTargetTimespan14 days (1,209,600 seconds)
nPowTargetSpacing10 minutes (600 seconds)
nSubsidyHalvingInterval210,000 blocks (~4 years)
BIP34Height, BIP66Height, ...Activation heights for soft forks
nMinimumChainWorkMinimum total work for a valid chain (DoS protection)
defaultAssumeValidBlock hash below which scripts are skipped on IBD

Block Template & Mining

Mining is the process of creating new blocks. Bitcoin Core's BlockAssembler creates block templates:

CreateNewBlock() Flow

  1. Create coinbase transaction with subsidy + fees
  2. Sort mempool by ancestor fee rate (highest first)
  3. Greedily add transactions until the 4 MWU weight limit is reached
  4. Respect package (ancestor) relationships, a child can't be included without its parent
  5. Calculate Merkle root from final transaction list
  6. Fill in block header (prev hash, timestamp, nBits)
  7. Return the template for the miner to search for a valid nonce

Mining Loop (External)

The actual mining (finding a nonce that produces a valid PoW) is done externally, either by generateblock RPC for testing, or by specialized mining hardware (ASICs) via the Stratum protocol or getblocktemplate RPC.

Chain Reorganizations

When a competing chain has more total work, Bitcoin Core performs a reorganization:

Find fork pointWalk back from both chain tips to find the common ancestor
DisconnectTip() × NUndo blocks from current chain tip back to fork point (reverse UTXO changes)
ConnectTip() × MApply blocks from new chain (fork point to new tip)
Return disconnected txs to mempoolTransactions from the old chain that aren't in the new one go back to mempool

Each block stores an "undo" file (rev*.dat) containing the data needed to reverse its UTXO changes. This makes disconnection efficient.