Blockchain technology has applications far beyond cryptocurrencies. At its core, a blockchain is a distributed ledger that ensures data integrity through cryptographic techniques, known as hashing. While the underlying concepts may seem complex, building a simple blockchain in Python is a great way to demystify the technology.

In this post, we’ll walk through the creation of a basic blockchain using Python, illustrating the foundational principles that make this technology so powerful. By the end, you’ll have a clearer understanding of how blockchains work and a hands-on example that you can expand upon in your own projects. Whether you’re a seasoned developer or just starting out, this guide will help you grasp the essentials of blockchain technology in a practical, approachable way.

The Code

As a note, this code could be utilized in any coding environment but I would recommend a Jupyter Notebook. A version of the code mentioned in this post can be viewed on GitHub.

Import a few packages that will be useful.

import hashlib
import json
from datetime import datetime

Start by defining the Block and Blockchain classes.

Create the Block Class

The Block class will represent a single block in the blockchain. Each block will contain the block’s index, previous block’s hash, timestamp, data (transactions), nonce, and it’s own hash. The compute_hash method calculates the SHA-256 hash of the block’s contents.

class Block:
    def __init__(self, index, previous_hash, timestamp, data, nonce=None):
        self.index = index
        self.previous_hash = previous_hash
        self.timestamp = timestamp
        self.data = data
        self.nonce = nonce
        self.hash = None

    def compute_hash(self):
        block_dict = self.__dict__
        if 'hash' in block_dict:
            del block_dict['hash']
        block_string = json.dumps(block_dict, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

Create the Blockchain Class

The Blockchain class will manage the chain of blocks and handle the consensus mechanism. The class defines the following methods:

  • create_genesis_block method initializes the chain with the genesis block.
  • last_block method returns the most recent block in the chain.
  • add_block method adds a new block to the chain after verifying its integrity and proof.
  • proof_of_work method implements the Proof of Work consensus mechanism by finding a nonce that produces a hash with a specified number of leading zeros.
  • add_new_transaction method adds a new transaction to the list of unconfirmed transactions.
  • mine method creates a new block from unconfirmed transactions and adds it to the chain after solving the proof of work puzzle.
  • is_valid_proof method verifies the validity of a block’s proof.
  • check_chain_validity method ensures the integrity of the entire blockchain by verifying each block’s hash and proof.
class Blockchain:
    difficulty = 2  # Proof of Work difficulty

    def __init__(self):
        self.unconfirmed_transactions = []
        self.chain = []
        self.create_genesis_block()

    def create_genesis_block(self):
        genesis_block = Block(0, None, str(datetime.now()), "Genesis Block", 0)
        genesis_block.hash = genesis_block.compute_hash()
        self.chain.append(genesis_block)

    def last_block(self):
        return self.chain[-1]

    def add_block(self, block, new_hash):
        previous_hash = self.last_block().hash

        if previous_hash != block.previous_hash:
            return False

        if not self.is_valid_proof(block, new_hash):
            return False

        block.hash = new_hash
        
        self.chain.append(block)
        return True
    
    def proof_of_work(self, block):
        block.nonce = 0
        computed_hash = block.compute_hash()
        while not computed_hash.startswith('0' * Blockchain.difficulty):
            block.nonce += 1
            computed_hash = block.compute_hash()
        
        return computed_hash

    def add_new_transaction(self, transaction):
        self.unconfirmed_transactions.append(transaction)

    def mine(self):
        if not self.unconfirmed_transactions:
            return False

        last_block = self.last_block()
        new_block = Block(index=last_block.index + 1,
                          previous_hash=last_block.hash,
                          timestamp=str(datetime.now()),
                          data=self.unconfirmed_transactions)

        proof = self.proof_of_work(new_block)
        self.add_block(new_block, proof)
        self.unconfirmed_transactions = []
        return new_block.index
    
    def is_valid_proof(self, block, block_hash):
        return (block_hash.startswith('0' * Blockchain.difficulty) and
                block_hash == block.compute_hash())

    def check_chain_validity(self):
        result = True
        previous_hash = None

        for block in self.chain:
            if block.index == 0:
                previous_hash = block.hash
                continue 
                
            block_hash = block.hash

            if not self.is_valid_proof(block, block_hash) or previous_hash != block.previous_hash:
                result = False
                break
                
            previous_hash = block_hash

        return result

Running the code

When running this example, it will create a simple Blockchain, add some transactions, mine new blocks, and print the Blockchain’s contents. It also checks the validity of the entire chain to ensure its integrity.

blockchain = Blockchain()
blockchain.add_new_transaction("James earned 10 LSU_tokens")
blockchain.mine()
blockchain.add_new_transaction("James pays Mike_the_tiger 5 LSU_tokens")
blockchain.mine()
for block in blockchain.chain:
    print(f"Block {block.__dict__}")
Block {'index': 0, 'previous_hash': None, 'timestamp': '2024-08-08 14:34:59.958990', 'data': 'Genesis Block', 'nonce': 0, 'hash': 'ea5ca59ee5ce305d73c929e46f87b8ff3ce6884284f0352091360bef4c617673'}
Block {'index': 1, 'previous_hash': 'ea5ca59ee5ce305d73c929e46f87b8ff3ce6884284f0352091360bef4c617673', 'timestamp': '2024-08-08 14:34:59.959075', 'data': ['James earned 10 LSU_tokens'], 'nonce': 685, 'hash': '00deffc551853b93135329f117b063045109977649556b0b3e6837fabade4d00'}
Block {'index': 2, 'previous_hash': '00deffc551853b93135329f117b063045109977649556b0b3e6837fabade4d00', 'timestamp': '2024-08-08 14:34:59.964583', 'data': ['James pays Mike_the_tiger 5 LSU_tokens'], 'nonce': 74, 'hash': '008066be505213f2523624625587d8096d7e4f1fe6ede3eb4e0976d1720c9cb6'}
print("Blockchain valid?", blockchain.check_chain_validity())
Blockchain valid? True

Conclusion

This basic implementation demonstrates how blockchain can achieve consensus and maintain a secure, tamper-evident record, effectively solving the Byzantine Generals Problem in a distributed network.