How to Build Your Own Blockchain in Less Than 200 Lines of Python

When you hear the word “blockchain,” what comes to mind? Bitcoin? Ethereum? Complex cryptographic puzzles and million-dollar mining rigs? For most people, blockchain technology feels like a black box—powerful, revolutionary, but inaccessible to the average programmer.

Here is the secret that the hype machine doesn’t tell you: The core concept of a blockchain is surprisingly simple. At its heart, a blockchain is just a linked list—a chain of blocks—with a few clever cryptographic tricks to ensure that once data is written, it cannot be changed without everyone noticing.

In this guide, we will strip away the noise and build a fully functional blockchain from scratch using Python in fewer than 200 lines of code. You will learn about hashing, proof-of-work, and immutability by actually building it. Whether you are a budding developer, a crypto enthusiast, or just curious, by the end of this tutorial, you will have a working prototype that can mine blocks and validate its own integrity.

Let’s code.

What Exactly Is a Blockchain? (The 30-Second Refresher)

Before we open our code editor, let’s align on the vocabulary. A blockchain is a distributed digital ledger. It consists of a list of records (called blocks) linked together using cryptography.

Each block contains three essential ingredients:

  1. Data: The actual information (e.g., “Alice sent Bob 5 coins”).
  2. Hash: A unique digital fingerprint of the current block (like a thumbprint).
  3. Previous Hash: The fingerprint of the block that came before it.

This “previous hash” is the chain. If someone changes Block #2, its hash changes. Block #3 will immediately notice because it stores the old hash. The chain breaks.

To make this secure against tampering, blockchains often use a Proof-of-Work mechanism—a mathematical puzzle that makes creating a new block intentionally slow (about 10 minutes for Bitcoin). We will implement a simplified version of this puzzle.

Setting Up Your Python Environment

You don’t need any exotic libraries for this project. We will use only the Python standard library. This ensures that your code runs anywhere without installation headaches.

Requirements

  • Python 3.6 or higher: If you don’t have it, download it from python.org.
  • A text editor: VS Code, PyCharm, or even Notepad.

Importing the Necessary Modules

Create a new file called simple_blockchain.py. We need three imports:

python

import hashlib
import json
import time
  • hashlib: For creating SHA-256 hashes (the fingerprints).
  • json: To convert our block data into a string format that can be hashed.
  • time: To add timestamps to our blocks.

H2: Building the Block Class

Every chain needs blocks. We will define a Python class that represents a single block. This is the blueprint for every link in our chain.

The Block Structure

A block in our blockchain will have the following attributes:

  • index: The position of the block (0 for Genesis, 1, 2…).
  • timestamp: When the block was created.
  • data: The transactions or information.
  • previous_hash: The hash of the preceding block (a string).
  • nonce: A number used for the proof-of-work puzzle.
  • hash: The current block’s own fingerprint.

Here is the __init__ method:

python

class Block:
    def __init__(self, index, data, previous_hash):
        self.index = index
        self.timestamp = time.time()
        self.data = data
        self.previous_hash = previous_hash
        self.nonce = 0  # Starts at zero, we will increment this for mining
        self.hash = self.compute_hash()

The Compute Hash Method

The compute_hash method takes all the block’s data and scrambles it into a 64-character hexadecimal string.

Important: The data must be deterministic. If we hash the same data twice, we get the same hash. If we change even one comma, the hash changes completely.

python

    def compute_hash(self):
        # Convert the block contents to a JSON string
        block_string = json.dumps({
            "index": self.index,
            "timestamp": self.timestamp,
            "data": self.data,
            "previous_hash": self.previous_hash,
            "nonce": self.nonce
        }, sort_keys=True).encode()
        # Return the SHA-256 hash
        return hashlib.sha256(block_string).hexdigest()

Notice how we include the nonce in the hash. This is how we will solve the puzzle later.

Implementing Proof-of-Work (Mining)

In a real blockchain like Bitcoin, mining is hard. The network requires that the block’s hash starts with a certain number of zeros (e.g., 0000a3f5...).

We will implement a simple puzzle: Find a hash that starts with four zeros ("0000") .

The Proof-of-Work Logic

We will add a mine method to our Block class. It will try nonce values (0, 1, 2, 3…) until it finds a hash that meets the difficulty requirement.

python

    def mine(self, difficulty):
        # Create a target string of zeros (e.g., "0000")
        target = "0" * difficulty
        
        # Keep looping until the hash starts with the target
        while self.hash[:difficulty] != target:
            self.nonce += 1
            self.hash = self.compute_hash()
        
        print(f"Block mined! Nonce: {self.nonce}, Hash: {self.hash}")
  • Why this works: Because the nonce is part of the hash calculation, changing it changes the hash. The computer brute-forces numbers until it gets “luck” and finds a hash starting with zeros.
  • Difficulty: In production, difficulty adjusts. For our tutorial, we will use difficulty = 4.

Building the Blockchain Class

Now we need a class to manage the chain. This class will handle creating new blocks, validating the chain, and storing the list.

Initializing the Chain

Python

class Blockchain:
    def __init__(self):
        self.chain = []
        self.difficulty = 4  # Number of leading zeros required
        self.create_genesis_block()
    
    def create_genesis_block(self):
        # The first block has no previous hash
        genesis_block = Block(0, "Genesis Block", "0")
        genesis_block.mine(self.difficulty)
        self.chain.append(genesis_block)

The Genesis Block is the first block. Its previous_hash is just a placeholder string ("0").

Adding New Blocks

To add a new block, we get the hash of the current last block, create a new block pointing to it, mine it, and append it.

python

    def add_block(self, data):
        previous_block = self.chain[-1]
        new_block = Block(len(self.chain), data, previous_block.hash)
        new_block.mine(self.difficulty)
        self.chain.append(new_block)

Validating the Chain (Immutability Check)

This is the most important function. It ensures that no one has tampered with the data.

python

    def is_chain_valid(self):
        # Start from block 1 (skip genesis)
        for i in range(1, len(self.chain)):
            current = self.chain[i]
            previous = self.chain[i-1]
            
            # 1. Check if the current block's hash is correct
            if current.hash != current.compute_hash():
                print(f"Invalid hash at block {current.index}")
                return False
            
            # 2. Check if the current block points to the previous block's hash
            if current.previous_hash != previous.hash:
                print(f"Broken link at block {current.index}")
                return False
            
            # 3. Check if the proof-of-work is valid (starts with zeros)
            if current.hash[:self.difficulty] != "0" * self.difficulty:
                print(f"Invalid proof-of-work at block {current.index}")
                return False
        
        return True

Putting It All Together (The Main Function)

Let’s run our blockchain! In the same file, add the following code outside the classes:

python

if __name__ == "__main__":
    # Create the blockchain
    my_chain = Blockchain()
    
    print("Mining block 1...")
    my_chain.add_block("Alice sent Bob 10 BTC")
    
    print("\nMining block 2...")
    my_chain.add_block("Bob sent Charlie 5 BTC")
    
    # Print the entire chain
    print("\n--- Blockchain Contents ---")
    for block in my_chain.chain:
        print(f"Index: {block.index}")
        print(f"Data: {block.data}")
        print(f"Hash: {block.hash}")
        print(f"Prev: {block.previous_hash}")
        print("-" * 40)
    
    # Validate the chain
    print(f"\nIs chain valid? {my_chain.is_chain_valid()}")
    
    # Attempt to tamper with the data
    print("\n--- Attempting Tampering ---")
    my_chain.chain[1].data = "Alice sent Bob 1000 BTC"
    
    # Recompute hash (the attacker forgets to do this in reality)
    # But even if they do, the next block's previous hash breaks.
    my_chain.chain[1].hash = my_chain.chain[1].compute_hash()
    
    print(f"Is chain valid after tampering? {my_chain.is_chain_valid()}")

Expected Output

When you run python simple_blockchain.py, you will see the mining process (a slight delay as the computer searches for nonces). The final tampering test should return False, proving the security of the chain.

Advanced Concepts (Where to Go Next)

You have just built the skeleton of Bitcoin! However, a production blockchain needs a few more features. Here is how to extend your project:

Networking and Consensus

Currently, our blockchain lives in one file. Real blockchains run on thousands of computers (nodes). To make your blockchain a “network”:

  • P2P Communication: Use socket or web3.py to broadcast new blocks to peers.
  • Consensus Algorithm: If two miners mine a block at the same time, you get a “fork.” The network follows the Longest Chain Rule (the chain with the most proof-of-work).

Transactions Instead of “Data”

Replace the data string with a list of Transaction objects.

python

class Transaction:
    def __init__(self, sender, receiver, amount):
        self.sender = sender
        self.receiver = receiver
        self.amount = amount

Then, collect pending transactions into a Mempool before mining.

Digital Signatures (ECDSA)

To prove that Alice authorized the transaction, you need cryptography.

  • Use the cryptography library or ecdsa to generate public/private keys.
  • Sign the transaction hash with the private key.
  • Nodes verify the signature using the public key.

Flask API

Turn your blockchain into a REST API using Flask. This allows you to send transactions via HTTP POST requests and view the chain via HTTP GET.

python

from flask import Flask, jsonify, request
# ... (integrate your blockchain class)

Conclusion

In less than 200 lines of Python, you have built a tamper-proof, self-validating blockchain. You learned that:

  • Hashing creates unique fingerprints.
  • Proof-of-Work prevents spam and makes tampering expensive.
  • The Chain links blocks via previous_hash references, ensuring integrity.

This is not just a coding exercise; this is the foundation of a technological revolution. Whether you want to build a supply chain tracker, a voting system, or a cryptocurrency, you now understand the engine under the hood.

Your challenge: Run the code. Try changing the difficulty from 4 to 5. Watch how much longer it takes to mine. That is the economics of blockchain in action.

Now, go build something revolutionary.


Full Source Code (Summary)

Here is the complete script (approx 130 lines) for your reference:

python

import hashlib
import json
import time

class Block:
    def __init__(self, index, data, previous_hash):
        self.index = index
        self.timestamp = time.time()
        self.data = data
        self.previous_hash = previous_hash
        self.nonce = 0
        self.hash = self.compute_hash()
    
    def compute_hash(self):
        block_string = json.dumps({
            "index": self.index,
            "timestamp": self.timestamp,
            "data": self.data,
            "previous_hash": self.previous_hash,
            "nonce": self.nonce
        }, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()
    
    def mine(self, difficulty):
        target = "0" * difficulty
        while self.hash[:difficulty] != target:
            self.nonce += 1
            self.hash = self.compute_hash()
        print(f"Mined Block {self.index}: Nonce={self.nonce} Hash={self.hash}")

class Blockchain:
    def __init__(self):
        self.chain = []
        self.difficulty = 4
        self.create_genesis_block()
    
    def create_genesis_block(self):
        genesis = Block(0, "Genesis Block", "0")
        genesis.mine(self.difficulty)
        self.chain.append(genesis)
    
    def add_block(self, data):
        prev = self.chain[-1]
        new_block = Block(len(self.chain), data, prev.hash)
        new_block.mine(self.difficulty)
        self.chain.append(new_block)
    
    def is_chain_valid(self):
        for i in range(1, len(self.chain)):
            curr, prev = self.chain[i], self.chain[i-1]
            if curr.hash != curr.compute_hash():
                return False
            if curr.previous_hash != prev.hash:
                return False
            if curr.hash[:self.difficulty] != "0" * self.difficulty:
                return False
        return True

# Run it
if __name__ == "__main__":
    bc = Blockchain()
    bc.add_block("Send 10 BTC")
    bc.add_block("Send 5 BTC")
    print("Chain valid?", bc.is_chain_valid())

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *