TeachMeBitcoin

Block height examples

From TeachMeBitcoin, the free encyclopedia Reading time: 5 min

8. Block Height vs Unix Time in Timelocks

Two Timekeeping Systems in Bitcoin

Bitcoin uses two different units for measuring time in timelocks — block height and Unix timestamp — and they are not interchangeable. Both CLTV and CSV support both modes, but the rules about which is used and how they interact are nuanced and have historically been sources of confusion and bugs.

Block Height Mode

Block height is Bitcoin's most fundamental unit of time. Each block mined increments the chain height by one, and roughly one block is found every 10 minutes on average (though in practice this varies significantly due to the probabilistic nature of mining).

For nLockTime (CLTV):

For nSequence (CSV, block mode):

# Block height examples
current_block = 850_000
one_week_blocks = 1008    # 7 days × 144 blocks/day
one_month_blocks = 4320   # 30 days × 144 blocks/day
one_year_blocks = 52560   # 365 days × 144 blocks/day

# CLTV: lock until 6 months from now
cltv_expiry = current_block + (one_month_blocks * 6)
print(f"CLTV expiry: block {cltv_expiry}")  # block 875920

# CSV: lock for 1 week relative
csv_value = one_week_blocks
print(f"CSV value: {csv_value} (0x{csv_value:04x})")  # 1008 (0x03f0)

Unix Timestamp Mode

Unix timestamps measure seconds since January 1, 1970 (UTC). For nLockTime, any value >= 500,000,000 is treated as a Unix timestamp.

For nLockTime (CLTV):

For nSequence (CSV, time mode):

import time

# Timestamp examples
now = int(time.time())  # Current Unix timestamp
one_hour = 3600
one_day = 86400
one_week = 604800

# CLTV: lock until 3 months from now (absolute timestamp)
three_months = 90 * one_day  # 7,776,000 seconds
cltv_timestamp = now + three_months
print(f"CLTV timestamp: {cltv_timestamp}")
# Must be >= 500,000,000 to be a timestamp (it will be, we're in 2024+)

# CSV: lock for 2 weeks using time (relative)
CSV_TYPE_FLAG = 1 << 22
two_weeks_seconds = 14 * one_day  # 1,209,600 seconds
units = (two_weeks_seconds + 511) // 512  # = 2366 units
csv_time_value = CSV_TYPE_FLAG | units
print(f"CSV time value: 0x{csv_time_value:08x}")  # 0x0040093e

Median Time Past (MTP) — Why It Matters

The MTP is a critical concept for time-based timelocks. Rather than using the current block's header timestamp for validation, Bitcoin uses the median timestamp of the last 11 blocks. This is defined by BIP 113 (activated alongside BIP 68 and BIP 112).

def compute_mtp(recent_block_timestamps):
    """
    Compute Median Time Past from the last 11 block timestamps.
    recent_block_timestamps: list of 11 timestamps, most recent last.
    """
    if len(recent_block_timestamps) < 11:
        raise ValueError("Need at least 11 block timestamps")

    last_11 = sorted(recent_block_timestamps[-11:])
    return last_11[5]  # Median of 11 = index 5

# Why MTP? Miners can set their block timestamp up to 2 hours in the future.
# Without MTP, a miner could craft a block with a future timestamp to
# unlock time-based timelocks prematurely. MTP prevents this.

The 500,000,000 Boundary: Current State

As of 2024, Unix time is approximately 1,700,000,000+ seconds, which is well above 500,000,000. This means all current Unix timestamps are unambiguously in the "timestamp" range for nLockTime. The 500,000,000 threshold translates to approximately January 5, 1985 — safely in the past.

For block heights: Bitcoin's current height is around 850,000–900,000, which is safely below 500,000,000, so there's no ambiguity between block height and timestamp modes in nLockTime at present.

Mixing Types: A Fatal Error

CLTV (and CSV) explicitly fail if you try to compare a block-height value with a timestamp value. This is the "type mismatch" check:

# This will FAIL at validation time:
# Script encodes block height 800000 (< 500,000,000)
# But tx.nLockTime = 1700000000 (>= 500,000,000, a timestamp)
# CLTV detects the mismatch and fails the script

LOCKTIME_THRESHOLD = 500_000_000

def check_locktime_type_match(script_value, tx_locktime):
    script_is_height = script_value < LOCKTIME_THRESHOLD
    tx_is_height = tx_locktime < LOCKTIME_THRESHOLD

    if script_is_height != tx_is_height:
        raise ScriptError("Locktime type mismatch: cannot mix block height and timestamp")

    return True

Choosing Between Block Height and Timestamp

Use block height when:

Use timestamp when:

For most programmatic use cases (Lightning, vaults, escrow), block height is preferred because it's deterministic and doesn't depend on miner timestamp behavior.

☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!