Bitcoin Blocks
Understanding Bitcoin block structure for agents. Headers, merkle trees, proof-of-work, and block propagation.
Bitcoin Blocks
Blocks are the fundamental unit of Bitcoin’s blockchain. Each block contains a batch of transactions and links to the previous block, forming an immutable chain.
Block Structure
┌─────────────────────────────────────────────────────────┐
│ Block Header (80 bytes) │
│ ├── Version (4 bytes) │
│ ├── Previous Block Hash (32 bytes) │
│ ├── Merkle Root (32 bytes) │
│ ├── Timestamp (4 bytes) │
│ ├── Difficulty Target (4 bytes) │
│ └── Nonce (4 bytes) │
├─────────────────────────────────────────────────────────┤
│ Transaction Count (varint) │
├─────────────────────────────────────────────────────────┤
│ Transactions │
│ ├── Coinbase Transaction │
│ ├── Transaction 1 │
│ ├── Transaction 2 │
│ └── ... │
└─────────────────────────────────────────────────────────┘
Block Header Fields
| Field | Size | Description |
|---|---|---|
| Version | 4 bytes | Block version for protocol upgrades |
| Previous Hash | 32 bytes | SHA256d of previous block header |
| Merkle Root | 32 bytes | Root of transaction merkle tree |
| Timestamp | 4 bytes | Unix timestamp (seconds) |
| Bits | 4 bytes | Compact difficulty target |
| Nonce | 4 bytes | Value miners adjust for PoW |
Version Field
Version bits signal protocol support:
- Bit 0: Reserved
- Bit 1: BIP-341 (Taproot) signaling
- Bits 2-28: Available for future soft forks
Block Hash
The block hash is computed by double-SHA256 of the 80-byte header:
import hashlib
def block_hash(header_bytes):
return hashlib.sha256(
hashlib.sha256(header_bytes).digest()
).digest()[::-1] # Reverse for display
Note: Block hashes are displayed in reverse byte order by convention.
Merkle Tree
Transactions are organized in a binary hash tree:
Merkle Root
/ \
H(AB) H(CD)
/ \ / \
H(A) H(B) H(C) H(D)
| | | |
Tx A Tx B Tx C Tx D
Merkle Proof
Agents can verify transaction inclusion without downloading the full block:
def verify_merkle_proof(tx_hash, proof, merkle_root):
current = tx_hash
for (sibling, is_left) in proof:
if is_left:
current = sha256d(sibling + current)
else:
current = sha256d(current + sibling)
return current == merkle_root
Proof of Work
Mining requires finding a nonce such that the block hash is below the target:
hash(header) < target
Difficulty Calculation
# Target from compact "bits" format
def bits_to_target(bits):
exponent = bits >> 24
mantissa = bits & 0x007fffff
return mantissa * (256 ** (exponent - 3))
# Difficulty relative to genesis
def difficulty(bits):
genesis_target = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
return genesis_target / bits_to_target(bits)
Difficulty Adjustment
Every 2016 blocks (~2 weeks), difficulty adjusts:
new_target = old_target * (actual_time / expected_time)
# Clamped to max 4x increase or 4x decrease
Block Size and Weight
Legacy Size Limit
Pre-SegWit: 1 MB maximum block size
SegWit Weight
Post-SegWit uses “weight units”:
weight = base_size * 3 + total_size
max_weight = 4,000,000 WU
Effectively allows ~2-4 MB blocks depending on transaction types.
Coinbase Transaction
First transaction in every block:
- No inputs (creates new bitcoins)
- Output includes block reward + fees
- Contains arbitrary data in coinbase field
{
"txid": "...",
"vin": [{
"coinbase": "03a5b8170456...", // Block height + arbitrary data
"sequence": 4294967295
}],
"vout": [{
"value": 3.125, // Current block reward
"scriptPubKey": {...}
}]
}
Block Reward Schedule
| Era | Block Range | Reward |
|---|---|---|
| 1 | 0 - 209,999 | 50 BTC |
| 2 | 210,000 - 419,999 | 25 BTC |
| 3 | 420,000 - 629,999 | 12.5 BTC |
| 4 | 630,000 - 839,999 | 6.25 BTC |
| 5 | 840,000+ | 3.125 BTC |
Block Timing
- Target interval: 10 minutes average
- Adjustment period: 2016 blocks (~2 weeks)
- Variance: Individual blocks can take seconds to hours
Timestamp Rules
- Must be greater than median of last 11 blocks
- Must be less than network time + 2 hours
API Access
Get Block by Hash
curl https://mempool.space/api/block/{hash}
Get Block by Height
# Get hash at height
curl https://mempool.space/api/block-height/{height}
# Then get block
curl https://mempool.space/api/block/{hash}
Get Latest Block
curl https://mempool.space/api/blocks/tip/hash
curl https://mempool.space/api/blocks/tip/height
Get Block Transactions
curl https://mempool.space/api/block/{hash}/txs
Agent Considerations
Block Confirmation
| Confirmations | Security Level | Use Case |
|---|---|---|
| 0 | Unconfirmed | Small, trusted |
| 1 | Tentative | Low value |
| 3 | Reasonable | Medium value |
| 6 | Standard | High value |
| 100 | Coinbase maturity | Mining rewards |
Monitoring New Blocks
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
if 'block' in data:
print(f"New block: {data['block']['height']}")
ws = websocket.WebSocketApp(
"wss://mempool.space/api/v1/ws",
on_message=on_message
)
ws.send(json.dumps({"action": "want", "data": ["blocks"]}))
ws.run_forever()
Reorg Handling
Blocks can be orphaned during chain reorganizations:
def handle_reorg(old_tip, new_tip):
# Find common ancestor
# Revert transactions from orphaned blocks
# Apply transactions from new chain
pass
Best practice: Wait for confirmations before considering transactions final.
Machine-Readable Summary
{
"topic": "bitcoin-blocks",
"header_size": 80,
"max_weight": 4000000,
"target_interval_seconds": 600,
"adjustment_period_blocks": 2016,
"current_reward_btc": 3.125,
"apis": {
"block": "https://mempool.space/api/block/{hash}",
"height": "https://mempool.space/api/block-height/{height}",
"tip": "https://mempool.space/api/blocks/tip/hash"
}
}