How the Bitcoin network reaches consensus — mining, proof of work, and difficulty adjustment

Lesson 318min3,751 chars

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.

ItemGHash.IO Incident Data
DateJanuary 9, 2014
Peak hash rate share~51%
BTC price at the time~$850
Price drop after incident~10%
ResolutionVoluntary hash rate reduction
LessonReaffirmed 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.

HalvingBlock heightYearBlock rewardDaily issuance (approx.)
Genesis0200950 BTC7,200 BTC
1st210,000201225 BTC3,600 BTC
2nd420,000201612.5 BTC1,800 BTC
3rd630,00020206.25 BTC900 BTC
4th840,00020243.125 BTC450 BTC
......~21400 BTC0 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:

  1. 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.

  2. 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 itemEstimated 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 gainedDouble-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:

  1. Copy the CSV output section and paste it into the 'Mining Economics' tab in Google Sheets
  2. Data → Split text to columns → Separator: comma
  3. Select the 'cumulative_supply_btc' column and create a line chart
  4. 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

MetricValueMeaning
Block time~10 minutesTarget for difficulty adjustment
Difficulty adjustment interval2,016 blocks (~2 weeks)Ensures stable sample size
Halving interval210,000 blocks (~4 years)Supply reduction schedule
Total supply21,000,000 BTCAn absolute cap that can never be exceeded
Current issuance rate96.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% attackerPractically 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:

  1. Proof of Work = a computational lottery that repeats SHA-256 while changing the nonce to find a target hash
  2. Difficulty adjustment = auto-correction every 2,016 blocks to maintain the 10-minute target
  3. 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.

Code Playground

Python아래 코드는 비트코인 채굴의 핵심 루프를 가장 단순한 형태로 재현한다. 논스를 0부터 1씩 올려가며 SHA-256을 돌리고, 해시값이 목표 조건(앞자리 0의 개수)을 충족하면 "블록 발견"이다. 실행해보면 난이도가 올라갈수록 논스가 기하급수적으로 커지는 걸 직접 확인할 수 있다.
Python그렇다면 0의 개수와 시도 횟수 사이에는 정확히 어떤 관계가 있을까? 아래 코드로 0이 1개일 때부터 6개일 때까지 시도 횟수와 소요 시간을 측정해본다.
Python이 흐름에서 가장 주목할 지점은 5단계와 7단계의 **비대칭성**이다. **채굴은 어렵지만 검증은 쉽다.** 채굴자가 수십억 번 해시를 계산해서 정답을 찾았어도, 다른 노드는 **딱 한 번** 해시를 계산하면 맞는지 틀리는지 알 수 있다. 아래 코드로 이 비대칭성을 직접 확인해보자.
Python아래 코드로 이 공식을 시뮬레이션한다. 네 가지 시나리오 — 정상, 빠름, 느림, 극단적으로 빠른 경우 — 에서 난이도가 어떻게 조절되는지 확인해보자.
Python아래 코드는 반감기가 반복될 때마다 보상이 어떻게 줄고, 누적 발행량이 2,100만 BTC에 수렴하는지를 보여준다.
Python아래 시뮬레이션은 공격자의 해시레이트 비율과 되돌리려는 블록 수에 따라 공격 성공 확률이 어떻게 변하는지 보여준다. 특히 "왜 6컨펌이 안전한가"를 수치로 확인할 수 있다.
Python이전 수업까지 만든 코드(Python으로 미니 블록체인)에 이어서, 이번엔 **반감기 경제학 데이터를 생성하는 스크립트**를 추가한다. 아래 코드는 세 단계로 구성된다: 2번 수업의 미니 블록체인 복습, 반감기 CSV 데이터 생성, 텍스트 기반 발행량 곡선 시각화.

Q&A