Block height examples
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):
-
Values
0to499,999,999are interpreted as block heights. -
A transaction with
nLockTime = 850000cannot be included until block 850,000 is the chain tip.
For nSequence (CSV, block mode):
-
The type flag (bit 22) must be CLEAR.
-
Bits 0–15 contain the number of blocks.
-
Maximum: 65,535 blocks ≈ 455 days at 10 min/block.
# 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):
-
Values >= 500,000,000 are Unix timestamps.
-
Validation uses the Median Time Past (MTP): the median of the last 11 block timestamps, not the current block's timestamp. This prevents miners from manipulating time-based locks by setting a future timestamp in their block.
For nSequence (CSV, time mode):
-
The type flag (bit 22) must be SET.
-
Bits 0–15 contain the number of 512-second intervals.
-
Maximum: 65,535 × 512 seconds ≈ 388 days.
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:
-
You need predictable confirmation counting (Lightning, payment channels).
-
You want the lock measured in "mining events" not wall time.
-
You're building scripts where other participants need to count confirmations easily.
Use timestamp when:
-
The lock must correspond to a real-world time (e.g., "this unlocks on January 1, 2026 at midnight UTC").
-
You're building inheritance or estate contracts tied to calendar dates.
-
The 10-minute block interval approximation introduces unacceptable variance.
For most programmatic use cases (Lightning, vaults, escrow), block height is preferred because it's deterministic and doesn't depend on miner timestamp behavior.
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: