TECHNICAL_DOC // BLOCK / BLOCK-VALIDATION
BLOCK
VALIDATION
VALIDATION
When a node receives a new block, it performs an exhaustive series of independent
validation checks before accepting it and relaying it to peers. No block is trusted
simply because a miner produced it — every node verifies every rule, every time. This is
what makes Bitcoin trustless.
VALIDATION_PIPELINE
PHASE 1 — HEADER CHECKS (CHEAP, DONE FIRST)
1. Block hash < current target (proof of work valid)
2. Version field is acceptable (not lower than required)
3. Timestamp > median of last 11 blocks (MTP rule)
4. Timestamp < node's local time + 2 hours
5. Previous block hash matches known chain tip
6. Merkle root field is present and correct length
THEN
PHASE 2 — TRANSACTION CHECKS (EXPENSIVE)
7. Block size ≤ 4,000,000 weight units
8. First transaction is coinbase, no others are
9. Coinbase reward ≤ block subsidy + sum of all fees
10. Merkle root matches SHA256d tree of all TxIDs
11. For every non-coinbase transaction:
a. All inputs reference existing, unspent UTXOs
b. No UTXO is double-spent within the block
c. input amounts ≥ output amounts
d. All signatures are cryptographically valid
e. All scripts execute to TRUE
f. Sequence/locktime rules satisfied
VALIDATION_CHECKS_IN_DETAIL
Proof of Work Check
FIRST CHECK
The fastest check — if the block hash doesn't meet the target, the entire block is discarded immediately with no further processing.
block_hash = SHA256(SHA256(header_bytes))
assert block_hash < current_target
// target derived from block header's "bits" field:
target = coefficient × 256^(exponent-3)
where bits = 0x1d00ffff → target = 0x00ffff × 2^208
Median Time Past (MTP)
TIMESTAMP RULE
A block's timestamp must be strictly greater than the median of the previous 11 block timestamps. This prevents miners from backdating blocks and manipulating timelocks.
MTP = median(timestamps of blocks: N-1, N-2, ..., N-11)
assert block.time > MTP
Upper bound: block.time < node_local_time + 7200 seconds
(blocks too far in the future are rejected as stale)
Merkle Root Verification
INTEGRITY CHECK
The Merkle root in the header is recomputed from all transactions in the block. If it doesn't match the header value, the block is invalid — even one altered transaction is detectable.
computed_merkle_root = build_merkle_tree(block.transactions)
assert computed_merkle_root == block.header.merkle_root
// Any single bit change in any transaction produces a completely
// different root — tamper evident for the entire transaction set
UTXO Validation
FULL NODE ONLY
Every input in every transaction must reference an existing, output/">unspent output. The node checks its UTXO set — a database of all spendable coins — for each reference.
for each tx in block.transactions[1:]: // skip coinbase
for each input in tx.inputs:
utxo = utxo_set.get(input.txid, input.vout)
assert utxo exists // not spent
assert utxo.value > 0
assert verify_script(utxo.scriptPubKey, input.scriptSig, input.witness)
mark_as_spent(utxo)
// SPV nodes cannot perform this check — they rely on PoW chain length
This is why a full node requires ~6 GB RAM for the UTXO set. Every unspent output must be instantly queryable for validation.
TERMINOLOGY_INDEX
MTP
Median Time Past. The median timestamp of the 11 previous blocks. A block's time must exceed this value.
Merkle Root
A single 32-byte hash committing to all transactions in the block. Recomputed during validation.
UTXO Set
The database of all spendable outputs. Full nodes check every input against this set during validation.
Weight Limit
Blocks must not exceed 4,000,000 weight units (SegWit era limit replacing the old 1 MB byte limit).
Coinbase Maturity
Coinbase outputs cannot be spent until 100 blocks have been built on top of the block containing them.