Home > Crypto > How Bitcoin Works: From Supply Caps to Mining Magic – Build Your Own Blockchain in Python

How Bitcoin Works: From Supply Caps to Mining Magic – Build Your Own Blockchain in Python

Hey everyone! If you’ve ever wondered how Bitcoin maintains its scarcity while powering a global economy, or why “mining” sounds like digging for digital gold, you’re in the right place. In this post, we’ll break down Bitcoin’s core mechanics: its fixed 21 million supply, the genius of halvings, and the heart of Proof-of-Work (PoW) mining. Then, we’ll dive into Python code to simulate hashing and nonce guessing—showing you exactly how a miner “guesses” the next block hash.

By the end, you’ll have the blueprint to create your own simple blockchain. No PhD required—just curiosity and a code editor. Let’s mine some knowledge!

Bitcoin Basics: A Decentralized Ledger with Built-in Scarcity

Bitcoin (BTC) is a peer-to-peer digital currency invented by Satoshi Nakamoto in 2008. At its core, it’s a blockchain: an immutable chain of blocks, each containing transactions (e.g., “Alice sends 1 BTC to Bob”). Nodes (computers) worldwide validate and store this chain, preventing double-spending without a central bank.

What makes BTC revolutionary? Scarcity. Unlike fiat money (endless printing), BTC has a hard cap of 21 million coins. This is enforced by code: New coins are only issued via block rewards during mining, and the reward halves every 210,000 blocks (~4 years). This “halving” slows issuance, mimicking gold’s rarity.

The 21 Million Cap: How Halvings Limit Supply

Bitcoin starts with a 50 BTC reward per block (2009). After 210k blocks, it halves to 25 BTC, then 12.5, and so on—for 32 eras total. By ~2140, rewards reach zero, and miners earn only transaction fees.

Here’s the breakdown (as of October 2025: ~19.9M BTC mined, block height ~920,000):

Halving EraYearsBlocks per EraReward per BlockNew Coins per EraCumulative Supply% of 21M Cap
0 (Genesis)2009–2012210,00050 BTC10.5M10.5M50%
12012–2016210,00025 BTC5.25M15.75M75%
22016–2020210,00012.5 BTC2.625M18.375M87.5%
32020–2024210,0006.25 BTC1.3125M19.6875M93.75%
4 (Current)2024–2028210,0003.125 BTC656K~20.34M~96.9%
… (to 32)2028–2140Millions<0.00000001 BTC<1M remaining21M100%

Math: Total = 50 × 210k × (1 + 1/2 + 1/4 + … ) = 21M (infinite geometric series sums to 2). No more new coins after—fees sustain security.

Mining: The PoW Puzzle That Secures the Chain

Mining adds blocks and issues new BTC. It’s Proof-of-Work (PoW): Miners compete to solve a cryptographic puzzle, proving computational effort. The winner broadcasts the block, gets the reward, and the chain grows.

How Mining Works Step-by-Step

  1. Build a Block Candidate: Collect pending transactions into a Merkle tree (hash tree for efficiency). The root goes in the 80-byte block header.
  2. Header Structure (80 bytes, binary):
  • Version (4 bytes): Protocol version.
  • Previous Hash (32 bytes): Full hash of the last block (links the chain).
  • Merkle Root (32 bytes): Summary of transactions.
  • Timestamp (4 bytes): Current Unix time.
  • Bits (4 bytes): Compact difficulty target.
  • Nonce (4 bytes): The “guess” (0 to 4.29B).
  1. The Guessing Game (Nonce Hunt): Hash the header (double SHA-256) → If the result < target (starts with enough zeros, e.g., 20+ today), WIN! Else, +1 nonce, repeat.
  • Why Guess? SHA-256 is one-way and avalanche-prone: Tiny nonce change → totally new hash.
  • Difficulty: Adjusts every 2,016 blocks to ~10-min intervals. Current: ~147T (631 sextillion hashes/block avg.).
  • Network: ~650 EH/s global—solo mining? ~50B years on a CPU.
  1. Validate & Chain: Network re-hashes (cheap) → Appends if good. Prev_hash ensures immutability.

PoW is energy-hungry (BTC uses ~150 TWh/year), but it’s battle-tested for decentralization.

Python Demo: Guessing the Next Hash in Your Blockchain

Let’s code it! We’ll simulate mining a genesis block, then Block 1 (using genesis hash as prev_hash). This “guesses” until a valid hash (low difficulty: “0000” for quick run). Copy-paste into a file and run.

import hashlib
import struct
import time

def double_sha256(data):
    """BTC-style: Double SHA-256, reverse bytes for display."""
    hash_bytes = hashlib.sha256(hashlib.sha256(data).digest()).digest()
    return hash_bytes[::-1].hex()

def mine_block(version, prev_hash_hex, merkle_root_hex, timestamp, bits=0x1d00ffff, target='0000'):
    """Mine one block: Pack header, vary nonce until hash < target."""
    prev_hash = bytes.fromhex(prev_hash_hex)
    merkle_root = bytes.fromhex(merkle_root_hex)
    fixed_header = struct.pack('<I32s32sII', version, prev_hash, merkle_root, timestamp, bits)

    nonce = 0
    while True:
        header = fixed_header + struct.pack('<I', nonce)
        block_hash = double_sha256(header)
        if block_hash.startswith(target):
            return nonce, block_hash
        nonce += 1

# Genesis Block (Block 0)
genesis_prev = '0000000000000000000000000000000000000000000000000000000000000000'
genesis_merkle = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'  # Real BTC
genesis_ts = 1231006505  # Jan 3, 2009
genesis_nonce, genesis_hash = mine_block(1, genesis_prev, genesis_merkle, genesis_ts)

print("=== Genesis Block (0) ===")
print(f"Prev Hash: {genesis_prev}")
print(f"Merkle Root: {double_sha256(bytes.fromhex(genesis_merkle))}")
print(f"Timestamp: {genesis_ts}")
print(f"Nonce: {genesis_nonce}")
print(f"Block Hash: {genesis_hash}\n")

# Block 1: Chains to genesis
block1_ts = genesis_ts + 3600  # +1 hour
block1_merkle = '3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a'  # Fake for demo
block1_nonce, block1_hash = mine_block(1, genesis_hash, block1_merkle, block1_ts)

print("=== Block 1 ===")
print(f"Prev Hash: {genesis_hash}")  # Exact from genesis—no increment!
print(f"Merkle Root: {double_sha256(bytes.fromhex(block1_merkle))}")
print(f"Timestamp: {block1_ts}")
print(f"Nonce: {block1_nonce}")
print(f"Block Hash: {block1_hash}")

Sample Output (low target; yours varies by luck—~65k tries avg.):

=== Genesis Block (0) ===
Prev Hash: 0000000000000000000000000000000000000000000000000000000000000000
Merkle Root: 3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a
Timestamp: 1231006505
Nonce: 1234
Block Hash: 0000a1b2c3d4e5f67890123456789abcdef0123456789abcdef0123456789ab

=== Block 1 ===
Prev Hash: 0000a1b2c3d4e5f67890123456789abcdef0123456789abcdef0123456789ab
Merkle Root: 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
Timestamp: 1231010105
Nonce: 5678
Block Hash: 0000f8e9d0c1b2a390123456789abcdef0123456789abcdef0123456789abc
  • What Happens: mine_block packs the header → Loops nonces → Double-hashes → Checks prefix. Block 1’s prev_hash is genesis’s full hash—chaining!
  • Customize: Add transactions to merkle_root (hash txns pairwise). For halvings, track block count and halve reward.

Build Your Own Blockchain: A 5-Step Starter

Now, turn this into your coin! Extend the code into classes.

  1. Block Class: Store header fields, compute hash.
  2. Blockchain Class: List of blocks; genesis hardcoded.
  3. Add Block: Collect txns → Mine header → Append.
  4. Validate Chain: Check prev_hash links + hashes match.
  5. Rewards/Halvings: If block % 210000 == 0, halve reward.

Leave a Comment