How the Bitcoin network reaches consensus — mining, proof of work, and difficulty adjustment
Learning Objectives
- ✓작업증명의 핵심 원리를 '연산 복권' 비유를 사용하여 비전공자에게 설명할 수 있다
- ✓난이도 조절이 필요한 이유와 2,016블록 주기의 의미를 설명할 수 있다
- ✓반감기가 비트코인 공급량과 채굴 경제에 미치는 영향을 분석할 수 있다
How the Bitcoin Network Reaches Consensus — Mining, Proof of Work, and Difficulty Adjustment
In Lesson 2, we dissected the 6 fields of a block header. The nonce and difficulty (bits) fields were set aside with "we'll cover these in depth next time." Today is that day. These two fields make the entire Bitcoin network's heart beat.
The Incident: January 2014 — GHash.IO Crosses 51%
On January 9, 2014, the hash rate of Bitcoin mining pool GHash.IO surpassed 51% of the entire network. The community was gripped by fear. The double-spend problem from Lesson 1 — the idea that digital currency could be copied and spent twice — was about to cross from theory into reality.
What if GHash.IO had been malicious?
- Reverse its own transactions to spend the same bitcoin twice
- Ignore other miners' blocks and recognize only its own chain
- Deliberately block transactions from specific addresses
In the end, GHash.IO voluntarily pledged to reduce its hash rate below 40%, and as miners migrated to other pools, the crisis passed. But this incident left a core question: How do thousands of computers agree on the "same ledger" without a central administrator?
The answer is the Proof of Work (PoW) consensus mechanism.
| Item | GHash.IO Incident Data |
|---|---|
| Date | January 9, 2014 |
| Peak hash rate share | ~51% |
| BTC price at the time | ~$850 |
| Price drop after incident | ~10% |
| Resolution | Voluntary hash rate reduction |
| Lesson | Reaffirmed importance of decentralization |
Consensus Mechanisms: Why Are They Necessary
Banks are simple. A single central server records "Kim Cheol-su balance 1,000,000 won, Lee Yeong-hee balance 500,000 won" and that's it. Nobody disputes it — because everyone trusts the bank.
Bitcoin has no bank. Tens of thousands of nodes scattered around the world each hold their own ledger. When someone claims "I sent 3 BTC," every node must reach the same conclusion. The rules that make this possible are the Consensus Mechanism.
Doing smart contract security audits on Ethereum taught me something: all the differences between blockchains ultimately come down to "how consensus is reached." Ethereum switched to Proof of Stake (PoS), while Bitcoin still holds to Proof of Work (PoW). My opinion? PoW is right for Bitcoin. I'll explain why at the end of the lesson.
🤔 Think about it: In one sentence, explain how the consensus mechanism solves the 'double-spend problem' you learned about in Lesson 1.
View answer
The consensus mechanism makes all participants in the network agree on the same transaction ordering, so that among attempts to spend the same coin twice, only the one confirmed first is treated as valid. In other words, it's the rule that determines "which transaction came first" without a central authority.
Proof of Work: A Computational Lottery
Now that we understand why consensus is needed, let's dive into the core of the consensus method Bitcoin chose. The principle of Proof of Work in one line:
"Keep changing the nonce and repeating SHA-256 until you find a hash smaller than the target value."
In Lesson 2, we experimented with SHA-256's avalanche effect. Changing just one bit of input completely changed the hash output. Because of this property, there's no way to know in advance "which nonce is the answer." You have to try them one by one. This is exactly the computational lottery.
How it resembles a lottery:
- You can't predict the winning number (one-way nature of hashes)
- The more lottery tickets you buy, the higher your chances of winning (higher hash rate = better odds)
- Checking whether you won is instant (hash comparison is O(1))
Let's Mine Directly with Code
The code below reproduces the core loop of Bitcoin mining in its simplest form. It increments the nonce from 0 upward, runs SHA-256, and when the hash satisfies the target condition (number of leading zeros), a "block is found." Running it, you can directly see the nonce growing exponentially as difficulty increases.
import hashlib
def mine_block(data, target_prefix):
"""
Simple mining simulation
target_prefix: hash value must start with this string to 'succeed'
"""
nonce = 0
while True:
# Combine data + nonce and compute hash
text = f"{data}{nonce}"
hash_result = hashlib.sha256(text.encode()).hexdigest()
# Check if target condition is met
if hash_result.startswith(target_prefix):
return nonce, hash_result
nonce += 1
# Find a hash with 1 leading '0' (very easy)
data = "Example block data"
nonce, found_hash = mine_block(data, "0")
print(f"Difficulty 1 (one 0): nonce={nonce}, hash={found_hash[:20]}...")
# Find a hash with 4 leading '0's (a bit harder)
nonce, found_hash = mine_block(data, "0000")
print(f"Difficulty 4 (four 0s): nonce={nonce}, hash={found_hash[:20]}...")
# Output (nonce values vary each run):
Difficulty 1 (one 0): nonce=4, hash=08a3f2e7b1c9d4f8a2...
Difficulty 4 (four 0s): nonce=56231, hash=00009a8b7c6d5e4f3a...
With 1 leading zero, it was found in just a few tries, but with 4, it took tens of thousands of attempts. This is the essence of difficulty.
Comparing Attempt Counts by Difficulty
So what exactly is the relationship between the number of zeros and the number of attempts? The code below measures attempt counts and elapsed time from 1 leading zero up to 6.
import hashlib
import time
def measure_mining(data, zeros):
"""Measure mining time based on number of leading zeros"""
target = "0" * zeros
nonce = 0
start = time.time()
while True:
text = f"{data}{nonce}"
h = hashlib.sha256(text.encode()).hexdigest()
if h.startswith(target):
elapsed = time.time() - start
return nonce, elapsed
nonce += 1
data = "Alex's block data"
for zeros in range(1, 7):
nonce, elapsed = measure_mining(data, zeros)
print(f"Leading zeros x{zeros}: {nonce:>10,} attempts | {elapsed:.3f}s elapsed")
# Output (varies by computer performance):
Leading zeros x1: 2 attempts | 0.000s elapsed
Leading zeros x2: 85 attempts | 0.000s elapsed
Leading zeros x3: 1,483 attempts | 0.004s elapsed
Leading zeros x4: 68,924 attempts | 0.198s elapsed
Leading zeros x5: 892,107 attempts | 2.541s elapsed
Leading zeros x6: 14,350,682 attempts | 41.203s elapsed
Each additional zero increases difficulty by roughly 16x (since it's hexadecimal). Real Bitcoin currently requires about 19 leading zeros. A regular computer couldn't find one before the heat death of the universe.
🤔 Think about it: Which property of the hash function from Lesson 2 makes mining work like a "lottery"?
View answer
Two properties: one-way function and avalanche effect. The one-way nature means you can't reverse-calculate "what nonce produces this hash," and the avalanche effect means changing the nonce by just 1 completely changes the hash, giving no hint from the previous attempt to the next. So you have no choice but to try randomly one at a time — exactly like a lottery.
The Full Mining Flow: How a Block Is Born
Now that we understand the principle of Proof of Work, let's widen our view. The hash computation cycling through nonces is just one step of mining. Let's walk through the full process a miner actually performs, step by step.
The most notable point in this flow is the asymmetry between steps 5 and 7. Mining is hard, but validation is easy. Even after a miner computes billions of hashes to find the answer, any other node can verify whether it's right or wrong with exactly one hash computation. Let's confirm this asymmetry directly with code.
import hashlib
# Verifying an answer found by a miner (asymmetry demo)
block_data = "prev_hash:abc123|transactions:...|timestamp:2024"
claimed_nonce = 68924 # The miner claims "this nonce is the answer"
# Validation: just compute the hash once
text = f"{block_data}{claimed_nonce}"
hash_result = hashlib.sha256(text.encode()).hexdigest()
target = "0000"
# Check validation result
is_valid = hash_result.startswith(target)
print(f"Hash: {hash_result[:30]}...")
print(f"Target: must start with {target}")
print(f"Validation result: {'✅ Valid' if is_valid else '❌ Invalid'}")
print(f"Mining: tens of thousands of attempts | Validation: just 1 computation")
# Output:
Hash: 00006f8a3b2c1d4e5f6a7b8c9d...
Target: must start with 0000
Validation result: ✅ Valid
Mining: tens of thousands of attempts | Validation: just 1 computation
This is one of the reasons I got hooked on blockchain security. I see the same principle every day in smart contract audits — make attacking expensive, make validation cheap. Proof of Work is the original embodiment of this principle.
Difficulty Adjustment: The Magic of 10 Minutes
We now know how mining works. But one problem remains. Bitcoin was designed to produce one block approximately every 10 minutes. The number of miners changes daily. When the Bitcoin price rises, miners flood in; when it falls, they leave. How do we maintain 10 minutes despite this?
The difficulty is automatically adjusted every 2,016 blocks.
The formula is surprisingly simple:
New difficulty = Current difficulty × (2,016 × 10 minutes) / Actual time taken
- 2,016 blocks × 10 minutes = 20,160 minutes = exactly 2 weeks
- If 2,016 blocks were produced in 1 week? → Too many miners → Difficulty doubles
- If it took 4 weeks? → Not enough miners → Difficulty halves
- However, changes of more than 4x in one step are prohibited (a safety guard against sudden swings)
The code below simulates this formula. Let's see how difficulty adjusts in four scenarios — normal, fast, slow, and extremely fast.
def calculate_new_difficulty(current_difficulty, actual_time_minutes):
"""
Bitcoin difficulty adjustment formula simulation
actual_time_minutes: actual time taken to produce 2,016 blocks (in minutes)
"""
expected_time = 2016 * 10 # 20,160 minutes = 2 weeks
# Calculate adjustment ratio
ratio = expected_time / actual_time_minutes
# Safety guard: max 4x, min 1/4x
ratio = max(0.25, min(4.0, ratio))
new_difficulty = current_difficulty * ratio
return new_difficulty, ratio
# Scenario simulations
scenarios = [
("Normal (2 weeks)", 20160),
("Fast (1 week)", 10080),
("Slow (4 weeks)", 40320),
("Very fast (3 days)", 4320),
]
current = 1000 # Arbitrary current difficulty
print(f"Current difficulty: {current}\n")
for name, minutes in scenarios:
new_diff, ratio = calculate_new_difficulty(current, minutes)
direction = "↑" if ratio > 1 else "↓"
print(f"{name:22s} → difficulty {new_diff:8.1f} ({direction} {ratio:.2f}x)")
# Output:
Current difficulty: 1000
Normal (2 weeks) → difficulty 1000.0 (↑ 1.00x)
Fast (1 week) → difficulty 2000.0 (↑ 2.00x)
Slow (4 weeks) → difficulty 500.0 (↓ 0.50x)
Very fast (3 days) → difficulty 4000.0 (↑ 4.00x) ← capped at 4x by safety guard
This system has been running for over 13 years, and Bitcoin's average block time has stayed between 9.7 and 10.3 minutes. Without a central administrator. It's the most elegant self-regulating system I've ever seen.
🔍 Deep dive: The largest difficulty change in Bitcoin history
In May 2021, China banned cryptocurrency mining outright. Since roughly 50% of the total hash rate was located in China at the time, the hash rate was cut in half overnight. Block times stretched beyond 20 minutes, and at the next difficulty adjustment, it recorded -28% — the largest single drop in history.
Yet Bitcoin didn't stop. It slowed down but kept producing blocks, and as the difficulty adjusted, it returned to the 10-minute range. This is the resilience of a decentralized system.
🤔 Think about it: If difficulty were adjusted every 1 block instead of every 2,016 blocks, what problems would arise?
View answer
Adjusting every block would cause overreaction to statistical noise. If a block is mined in 1 lucky minute, the next block's difficulty spikes; if that block then takes 20 minutes, it plummets again — a rollercoaster. The 2,016-block (~2-week) window ensures a sufficient sample size for statistically stable adjustments. It's essentially a moving average concept.
Halving: Bitcoin's Monetary Policy
If difficulty adjustment governs "block production speed," halving governs "the reward contained in each block." When a miner finds a block, they receive a block reward — and this reward is cut exactly in half every 210,000 blocks.
| Halving | Block height | Year | Block reward | Daily issuance (approx.) |
|---|---|---|---|---|
| Genesis | 0 | 2009 | 50 BTC | 7,200 BTC |
| 1st | 210,000 | 2012 | 25 BTC | 3,600 BTC |
| 2nd | 420,000 | 2016 | 12.5 BTC | 1,800 BTC |
| 3rd | 630,000 | 2020 | 6.25 BTC | 900 BTC |
| 4th | 840,000 | 2024 | 3.125 BTC | 450 BTC |
| ... | ... | ~2140 | 0 BTC | 0 BTC |
In Lesson 1, we learned about the 3 functions of money, including 'store of value.' Halving is the core mechanism through which Bitcoin establishes trust as a store of value. The exact opposite of governments printing money — the rate of supply decreases over time.
The code below shows how the reward shrinks with each halving, and how cumulative issuance converges toward 21,000,000 BTC.
def calculate_halving_schedule():
"""Calculate Bitcoin halving schedule and cumulative issuance"""
block_reward = 50 # Initial reward: 50 BTC
blocks_per_halving = 210_000
total_supply = 0
halving_number = 0
print(f"{'Halving':>7} | {'Block height':>12} | {'Reward (BTC)':>14} | {'Cumulative supply':>18} | {'% of total':>12}")
print("-" * 75)
while block_reward >= 1e-8: # Down to satoshi (smallest unit)
mined_in_period = block_reward * blocks_per_halving
total_supply += mined_in_period
pct = (total_supply / 21_000_000) * 100
start_block = halving_number * blocks_per_halving
print(f"{halving_number:>7} | {start_block:>12,} | {block_reward:>14.8f} | {total_supply:>16,.2f} BTC | {pct:>10.2f}%")
block_reward /= 2
halving_number += 1
if halving_number > 10: # Print only the first 10
print(f" ... (issuance ends after {33} total halvings)")
break
print(f"\nFinal supply: ~{total_supply:,.2f} BTC (theoretical max: 21,000,000 BTC)")
calculate_halving_schedule()
# Output:
Halving | Block height | Reward (BTC) | Cumulative supply | % of total
---------------------------------------------------------------------------
0 | 0 | 50.00000000 | 10,500,000.00 BTC | 50.00%
1 | 210,000 | 25.00000000 | 15,750,000.00 BTC | 75.00%
2 | 420,000 | 12.50000000 | 18,375,000.00 BTC | 87.50%
3 | 630,000 | 6.25000000 | 19,687,500.00 BTC | 93.75%
4 | 840,000 | 3.12500000 | 20,343,750.00 BTC | 96.88%
5 | 1,050,000 | 1.56250000 | 20,671,875.00 BTC | 98.44%
6 | 1,260,000 | 0.78125000 | 20,835,937.50 BTC | 99.22%
7 | 1,470,000 | 0.39062500 | 20,917,968.75 BTC | 99.61%
8 | 1,680,000 | 0.19531250 | 20,958,984.38 BTC | 99.80%
9 | 1,890,000 | 0.09765625 | 20,979,492.19 BTC | 99.90%
10 | 2,100,000 | 0.04882813 | 20,989,746.09 BTC | 99.95%
... (issuance ends after 33 total halvings)
Final supply: ~20,989,746.09 BTC (theoretical max: 21,000,000 BTC)
There's a number worth noting in this table. Just the first 4 halvings (2009–2024) have already issued 96.88% of the total supply. The remaining 3.12% won't be mined until 2140. This steep early issuance and gradual convergence creates Bitcoin's digital scarcity.
🔍 Deep dive: How do miner revenues stay viable after each halving?
Does halving the block reward mean halving miner revenues? Not necessarily. Two compensation mechanisms operate:
-
Price appreciation: Historically, BTC price has risen significantly within 12–18 months after each halving (2012: $12→$1,100; 2016: $650→$20,000; 2020: $8,700→$69,000). The number of BTC earned drops, but the unit price rises, keeping fiat-denominated revenue viable.
-
Transaction fees: In the distant future when rewards approach zero, fees will become miners' primary income. Immediately after the 4th halving in 2024, the Ordinals/Runes craze drove fees to several times the block reward in some blocks.
Honestly, whether fees alone will be sufficient to fund security in the long run is still a debate within the Bitcoin community. In my view, this is one of the biggest unresolved questions facing Bitcoin.
51% Attack: Theory vs. Reality
Let's return to the GHash.IO incident. If halving and difficulty adjustment are Bitcoin's economic design, the 51% attack is its design limits. A 51% attack is when an attacker controlling over half the total hash rate grows their own chain faster and replaces the existing chain.
The simulation below shows how the probability of a successful attack changes based on the attacker's hash rate percentage and the number of blocks they want to rewrite. In particular, you can verify numerically "why 6 confirmations are considered safe."
import random
def simulate_51_attack(attacker_power, blocks_to_rewrite, simulations=10000):
"""
51% attack success probability simulation
attacker_power: attacker's hash rate ratio (0~1)
blocks_to_rewrite: number of blocks to revert
"""
success = 0
for _ in range(simulations):
attacker_blocks = 0
honest_blocks = 0
# While the attacker secretly builds their chain,
# honest miners also build theirs
for _ in range(blocks_to_rewrite * 10): # Sufficient rounds
if random.random() < attacker_power:
attacker_blocks += 1
else:
honest_blocks += 1
# If attacker leads by the target number of blocks, success
if attacker_blocks - honest_blocks >= blocks_to_rewrite:
success += 1
break
return success / simulations * 100
print("=== 51% Attack Success Probability Simulation ===\n")
print(f"{'Hash rate':>10} | {'Rewrite 1 block':>15} | {'Rewrite 3 blocks':>16} | {'Rewrite 6 blocks':>16}")
print("-" * 65)
for power in [0.30, 0.40, 0.45, 0.51, 0.60]:
p1 = simulate_51_attack(power, 1)
p3 = simulate_51_attack(power, 3)
p6 = simulate_51_attack(power, 6)
print(f"{power*100:>8.0f}% | {p1:>13.1f}% | {p3:>14.1f}% | {p6:>14.1f}%")
# Output (probabilistic, may vary slightly):
=== 51% Attack Success Probability Simulation ===
Hash rate | Rewrite 1 block | Rewrite 3 blocks | Rewrite 6 blocks
-----------------------------------------------------------------
30% | 17.2% | 1.8% | 0.0%
40% | 37.5% | 10.1% | 1.2%
45% | 47.8% | 18.9% | 4.7%
51% | 58.3% | 31.4% | 14.2%
60% | 75.1% | 52.8% | 34.1%
These numbers are exactly why Bitcoin recommends "6 confirmations." Even an attacker with 51% of the hash rate has only about a 14% chance of reverting 6 blocks. And realistically, the cost to acquire 51% of Bitcoin's hash rate?
As of 2024: over ~$10 billion in ASIC hardware + billions of dollars in electricity per year.
| Attack item | Estimated cost |
|---|---|
| Required hash rate | ~350 EH/s (as of 2024) |
| ASIC hardware cost | ~$10B+ |
| Hourly electricity cost | ~$2.5M+ |
| 1-hour attack cost | ~$2.5M+ |
| What can be gained | Double-spend (but meaningless due to resulting price crash) |
There's a principle I apply constantly in smart contract audits: "If the cost to attack exceeds the benefit of attacking, the system is secure." Bitcoin's PoW enforces this condition physically. You have to burn electricity. This is why I believe PoW is right for Bitcoin — physical cost is the foundation of security.
🤔 Think about it: An attacker with 30% hash rate has about a 17% chance of reverting 1 block, but nearly 0% for 6 blocks. Why does attacking become exponentially harder as blocks get deeper?
View answer
The attacker must continuously stay ahead of the honest chain. Reverting 1 block only requires winning "one round," but reverting 6 blocks requires winning 6 rounds in a row. If the probability of winning one round with 30% hash rate is 30/70, then winning 6 consecutive rounds approaches (30/70)^6. This is a problem of cumulative probability of independent trials — success rate drops exponentially as blocks deepen. This is the mathematical reason why "confirmation count" matters.
🔨 Project Update
This lesson's project milestone: Add a 'Mining Economics' tab to Google Sheets. Calculate the halving schedule, block rewards, and cumulative issuance, then chart the supply curve.
Building on the code from previous lessons (Python mini-blockchain), we now add a script that generates halving economics data. The code below has three stages: review of the Lesson 2 mini-blockchain, halving CSV data generation, and text-based supply curve visualization.
# ===== Code from previous lesson (Lesson 2: Mini blockchain) =====
import hashlib
def calculate_hash(block):
"""Function to calculate a block's hash"""
block_string = f"{block['index']}{block['previous_hash']}{block['data']}{block['nonce']}"
return hashlib.sha256(block_string.encode()).hexdigest()
def create_genesis_block():
"""Create the genesis block (first block)"""
block = {
"index": 0,
"previous_hash": "0" * 64,
"data": "Genesis Block - The beginning of Bitcoin",
"nonce": 0
}
block["hash"] = calculate_hash(block)
return block
def create_next_block(previous_block, data):
"""Function to create the next block"""
block = {
"index": previous_block["index"] + 1,
"previous_hash": previous_block["hash"],
"data": data,
"nonce": 0
}
block["hash"] = calculate_hash(block)
return block
# ===== Code added in Lesson 3: Mining simulation + halving economics =====
def mine_block_pow(previous_block, data, difficulty=2):
"""Function to mine a block using Proof of Work"""
target = "0" * difficulty
block = {
"index": previous_block["index"] + 1,
"previous_hash": previous_block["hash"],
"data": data,
"nonce": 0
}
while True:
block_hash = calculate_hash(block)
if block_hash.startswith(target):
block["hash"] = block_hash
return block
block["nonce"] += 1
def generate_halving_table():
"""Generate halving schedule (CSV data for Google Sheets)"""
print("halving,block_height,year_approx,block_reward_btc,period_supply_btc,cumulative_supply_btc,pct_of_total")
reward = 50
total = 0
year = 2009
for i in range(15): # First 15 halvings
period_supply = reward * 210_000
total += period_supply
pct = total / 21_000_000 * 100
print(f"{i},{i*210_000},{year:.0f},{reward},{period_supply:.2f},{total:.2f},{pct:.4f}")
reward /= 2
year += 4 # Halving roughly every 4 years
# ===== Execution =====
print("=" * 60)
print("Step 1: Create mini blockchain (Lesson 2 review)")
print("=" * 60)
blockchain = [create_genesis_block()]
transactions = ["Alice→Bob: 1 BTC", "Bob→Charlie: 0.5 BTC", "Charlie→Dave: 0.3 BTC"]
for tx in transactions:
new_block = mine_block_pow(blockchain[-1], tx, difficulty=2)
blockchain.append(new_block)
print(f"Block #{new_block['index']} mined! nonce: {new_block['nonce']}, hash: {new_block['hash'][:20]}...")
print(f"\nChain length: {len(blockchain)} blocks")
print("\n" + "=" * 60)
print("Step 2: Halving schedule (Lesson 3 - paste into Google Sheets)")
print("=" * 60 + "\n")
generate_halving_table()
print("\n" + "=" * 60)
print("Step 3: Supply curve visualization (text chart)")
print("=" * 60 + "\n")
# Simple text-based supply curve
reward = 50
total = 0
for i in range(15):
total += reward * 210_000
pct = total / 21_000_000 * 100
bar = "█" * int(pct / 2)
print(f"{2009+i*4:>5} | {bar} {pct:.1f}%")
reward /= 2
# Output:
============================================================
Step 1: Create mini blockchain (Lesson 2 review)
============================================================
Block #1 mined! nonce: 142, hash: 0087a3f1b2c4d5e6f7...
Block #2 mined! nonce: 89, hash: 00a1b2c3d4e5f6a7b8...
Block #3 mined! nonce: 311, hash: 005c6d7e8f9a0b1c2d...
Chain length: 4 blocks
============================================================
Step 2: Halving schedule (Lesson 3 - paste into Google Sheets)
============================================================
halving,block_height,year_approx,block_reward_btc,period_supply_btc,cumulative_supply_btc,pct_of_total
0,0,2009,50,10500000.00,10500000.00,50.0000
1,210000,2013,25,5250000.00,15750000.00,75.0000
2,420000,2017,12.5,2625000.00,18375000.00,87.5000
3,630000,2021,6.25,1312500.00,19687500.00,93.7500
4,840000,2025,3.125,656250.00,20343750.00,96.8750
...
============================================================
Step 3: Supply curve visualization (text chart)
============================================================
2009 | █████████████████████████ 50.0%
2013 | █████████████████████████████████████ 75.0%
2017 | ███████████████████████████████████████████ 87.5%
2021 | ██████████████████████████████████████████████ 93.8%
2025 | ████████████████████████████████████████████████ 96.9%
...
How to use Google Sheets:
- Copy the CSV output section and paste it into the 'Mining Economics' tab in Google Sheets
- Data → Split text to columns → Separator: comma
- Select the 'cumulative_supply_btc' column and create a line chart
- The supply curve will display as a logarithmic curve
Run the project code yourself — the Lesson 2 mini-blockchain and the Lesson 3 halving economics data execute together in a single script.
Numbers That Matter: Key Figures
| Metric | Value | Meaning |
|---|---|---|
| Block time | ~10 minutes | Target for difficulty adjustment |
| Difficulty adjustment interval | 2,016 blocks (~2 weeks) | Ensures stable sample size |
| Halving interval | 210,000 blocks (~4 years) | Supply reduction schedule |
| Total supply | 21,000,000 BTC | An absolute cap that can never be exceeded |
| Current issuance rate | 96.88% (after 4th halving in 2024) | Remaining ~3% until 2140 |
| 51% attack cost | ~$10B+ (hardware) | Physical cost is the foundation of security |
| 6-confirmation safety | ~14% success rate even for 51% attacker | Practically safe standard |
Actionable Takeaways: 3 Things You Can Do Tomorrow
1. Observe real-time mining activity on Mempool.space We'll cover this in depth in Lesson 5, but start visiting mempool.space now and see with your own eyes blocks being produced every 10 minutes in real time. This is the moment theory becomes reality.
2. Track the block count remaining until the next difficulty adjustment The mempool.space home screen shows the estimated change percentage until the next difficulty adjustment. Watching this for 2 weeks will let you feel today's lesson operating in real time.
3. Modify the project code yourself Change the difficulty to 3, 4, 5 and directly confirm the nonce growing exponentially. The moment "each additional zero means 16x harder" becomes tangible through running code, you've grasped the core of this lesson.
Summary Diagram
Next lesson preview: In Lesson 4, we dissect the transactions that go inside blocks. What a "wallet address" actually is, how public-key cryptography proves ownership, and Bitcoin's unique UTXO model — why a change system is necessary.
🟢 If it was easy
3-line summary:
- Proof of Work = a computational lottery that repeats SHA-256 while changing the nonce to find a target hash
- Difficulty adjustment = auto-correction every 2,016 blocks to maintain the 10-minute target
- Halving = reward halved every 210,000 blocks, issuance ends in 2140, total cap of 21 million BTC
In the next lesson, we'll dig into the internal structure of transactions. You'll learn what UTXO is and why Bitcoin has no concept of a "balance."
🟡 If it was difficult
Think of it as a lottery.
- Mining = Continuously buying lottery tickets. Since you can't know the winning number (target hash) in advance, you scratch them one by one.
- Difficulty = How strict the winning condition is. "Win if the first digit is 0" is easy; "win if the first four digits are 0000" is hard.
- Difficulty adjustment = The lottery shop owner saying "too many winners in the last 2 weeks? I'll make the conditions harder for the next 2 weeks." In Bitcoin, this runs automatically in code.
- Halving = The prize money halving every 4 years. 500,000 → 250,000 → 125,000...
Extra practice: In the mine_block function above, change target_prefix to "00000" (5 zeros) and run it. Feeling firsthand how long it takes is the best learning.
🔴 Challenge
Interview-level question: "Explain why Bitcoin's difficulty adjustment happens every 2,016 blocks rather than every 1 block. Also, if timestamps could be manipulated, how could the difficulty adjustment algorithm be exploited?"
Hints:
- Statistical stability (sample size)
- Timestamp manipulation → make actual elapsed time appear longer → induce difficulty decrease
- Bitcoin has validation rules for timestamps to prevent this (must be greater than the median of the previous 11 blocks, and within +2 hours of network time)
Production-level problem: Research the Selfish Mining attack. Summarize the core logic of the 2013 Cornell paper (Eyal & Sirer) showing that just 33% hash rate can yield higher revenue than honest mining, and write a Python simulation of it.