Creating a Blockchain in Python

Understanding Through Implementation

Digital currencies are a hot topic right now. I wanted to ensure that I have a grasp of the core principles that govern this technology before it somehow impacts my life hence I set out on a mission to learn them.

After spending a a few hours studying the fundamentals of blockchain and its architecture, I embarked on a project where I aimed to create a simplified blockchain in python, in order to cement my understanding and consolidate my learning.

It is always a good idea to not spend too much time engrossed in theory and to reach the practical stage as soon as possible. This way, by actively engaging the brain in problem solving linked to concept that we are trying to solidify, we form neural connections must faster and are able to visualise concepts much clearer.

Moving from a natural language explanation of blockchain to a python implementation has granted me a modular and robust understanding of blockchain architecture and functionality, and has allowed me to build a strong foundation as I continue to learn more about blockchain technology.

In this blog I will detail the process took, the blockchain concepts demonstrated and how this project has benefited me.

Structural Overview

I adopted an object-oriented programming approach where each piece of the blockchain, including the blockchain itself, is a separate class. In Python, classes are like blueprints, and each time we use them, we’re cutting out a unique instance, like cookies from a cookie cutter. When you see self, think of it as “this specific cookie” that was cut from the mould.

I began with the core components of a blockchain, building each individually, before integrating them all together into a working chain. Each class lives in a separate Python file and is imported into the final blockchain script.

The Block

In blockchain, a block is essentially a storage container that stores transactions. The architecture of a block as shown below consists of various elements.

class Block:

    def __init__(self, Height, Blocksize, BlockHeader, TxCount, Txs): 
        self.Height = Height  # height of the block
        self.Blocksize = Blocksize # size of the block
        self.BlockHeader = BlockHeader
        self.Txcount = TxCount # nunmber of transactions
        self.Txs = Txs # the transaction itself, likely a list/array/..
  • self.Height: This is like a block's street number it tells you where it sits in the chain (1st, 2nd, 3rd…).

  • self.Blocksize: Think of this as how much data the block can hold — like the size of your storage box.

  • self.BlockHeader: This is the “label” on the outside of the box — it contains key info about what’s inside and where it came from.

  • self.TxCount: The number of individual transactions inside the block — like how many letters are inside an envelope.

  • self.Txs: These are the actual transactions, the “contents” of the block.

The Block Header

The block header is one of the properties of the Block class that we created above and it forms a very crucial part of the blockchain architecture.

class BlockHeader:
    def __init__(self, version, prevBlockHash, merkleRoot, timestamp, bits):
        self.version = version
        self.prevBlockHash = prevBlockHash
        self.merkleRoot = merkleRoot
        self.timestamp = timestamp
        self.bits = bits
        self.nonce = 0
        self.blockHash = ' ' ## to be updated via mine function

We notice that the self.blockHash property of the Block is initially blank. I will cover this after discussing what hashing is and how it is implemented in our python project.

  • self.version: Think of this as the rulebook version, which set of instructions this block follows.

  • self.prevBlockHash: This is a fingerprint (hash) of the previous block. It’s like a link in a chain

  • self.merkleRoot: his is a single hash that represents all the transactions in the block.

  • self.timestamp: When the block was created, essentially its date of birth.

  • self.bits: Represents the target difficulty for mining the block.

  • self.nonce: A number miners keep changing to find a hash that fits the difficulty rule.

  • self.blockHash: The unique identifier for this specific block, calculated by hashing the entire header (updated during the mining process).

Hashing is like feeding a sentence into a magical blender that always produces a fixed-length smoothie. No matter how big or small your input is, the blender gives you the same-sized result. More importantly, even the tiniest change in the input gives you a completely different smoothie.

And once it's blended, there's no way to unblend it — you can't reverse a hash to get the original message.

In Python, I used the hashlib library to create hashes:

import hashlib ## hashlib module provides a common interface to many different secure hash algorithms
def hash256(s):  ## hash256 performs a specific type of cryptographic hashing on input 's'. 
    """Our hash256 function does Two round of SHA256"""
    return hashlib.sha256(hashlib.sha256(s).digest()).digest() ## This creates a SHA-256 hash object.
     ## digest() finalizes the hash computation and returns the hash value

This is our hashing function that will be used repeatedly in our blockchain implementation.

Returning to our BlockHeader class, we now wish to implement the mining process. In order for a block to be successfully mined, miners must satisfy a difficulty requirement. Every time they try to solve it with a different ‘solution’ , a new blockHash value is obtained.

from Blockchain.Backend.util.util import hash256
class BlockHeader:
    def __init__(self, version, prevBlockHash, merkleRoot, timestamp, bits):
        self.version = version
        self.prevBlockHash = prevBlockHash
        self.merkleRoot = merkleRoot
        self.timestamp = timestamp
        self.bits = bits
        self.nonce = 0
        self.blockHash = ' ' ## to be updated via mine function

    def mine(self):
        while (self.blockHash[0:4]) != '0000': ## difficulty requirement
            self.blockHash = hash256((str(self.version) + self.prevBlockHash + self.merkleRoot + str(self.timestamp)
                                     + self.bits + str(self.nonce)).encode()).hex()
            self.nonce += 1 # to satisfy the difficulty requirement we can only change the nonce and then hash again
            print(f"Mining Started {self.nonce}", end = '\\r') #end = '\\r' makes the output update on only one line

The mining function creates a blockHash by concatenating all of the properties of the block header and then running it through the hash function that we created.

Hence we see again that the self.blockHash is still bank. This is because this value is eventually found by miners during the mining process. They iterate over many nonce values self.nonce ; changing the nonce value changes the header, and every time the header is hashed, we obtain a completely different blockHash value. When the nonce value is so that the newly created blockHash value satisfies the difficulty level, the final blockHash is established.

In my implementation, it is so that the difficulty requirement is reached when the first 4 digits of the newly created blockHash are all 0.

We recall that the header is composed of self.prevBlockHash , hence with our new blockHash, we form a chain to the previous block.

Putting it all together: The Blockchain

We begin by importing all the required modules that we created and other python modules:

import sys
sys.path.append('/Users/Myself/Documents/Blockhain In Python') # We are guiding python to necessary modules etc..

from Blockchain.Backend.core.block import Block 
from Blockchain.Backend.core.blockheader import BlockHeader     # here we are importing all of our classes and functions
from Blockchain.Backend.util.util import hash256
import time
import json

The main features of a a newly instantiated blockchain are the instantiation of the Genesis block (the first block created on the blockchain) and also a mechanism to add mined blocks to the blockchain. This is all exemplified below.

ZERO_HASH = '0' * 64
VERSION = 1

class Blockchain:
    def __init__(self): # Recall that 'self' is the current instance of the class. Here we initialise the object which is 1 new blockchain.
        self.chain = [] # List that holds all of our blocks - Due to be populated
        self.GenesisBlock()
        

    def GenesisBlock(self): #when we pass in self to a function (the current instance) it allows us to acess instance (object) variables and call instance methods
        BlockHeight = 0
        prevBlockHash = ZERO_HASH
        self.addBlock(BlockHeight, prevBlockHash) # Adding the genesis block to the blockchain via addBlock function, recall addBlock is an instance method of self

    def addBlock(self, BlockHeight, prevBlockHash):
        timestamp = int(time.time())
        Transaction = f"T sent {BlockHeight} Bitcoins to Z" # Creates unique transactions
        merkleRoot = hash256(Transaction.encode()).hex() #.hex() converts into hex form
        bits = 'ffff001f'
        blockheader = BlockHeader(VERSION, prevBlockHash, merkleRoot, timestamp, bits) #creating an instance immediately calls the __init__ function in blockheader.py
        blockheader.mine()
        self.chain.append(Block(BlockHeight, 1, blockheader.__dict__, 1, Transaction).__dict__)  # Appending our new instance of Block to the chain. 
        # We can access self.chain because we passed in self.
        # __dict__ means that we convert the class into a dictionary, we convert it into it the class properties
                                                                                                                                               
        print(json.dumps(self.chain, indent = 4))  

if __name__ == "__main__":
    blockchain = Blockchain()

When we run this we see the following:

This is the generation of our Genesis Block. We see all the properties of the block.

We now wish to add a structure to allow us to add more blocks. The full implementation is shown below. I have chosen to create 4 blocks.

import sys
sys.path.append('/Users/tomis/Documents/Blockhain In Python') # We are guiding python to necessary modules etc..

from Blockchain.Backend.core.block import Block 
from Blockchain.Backend.core.blockheader import BlockHeader     # here we are importing all of our classes and functions
from Blockchain.Backend.util.util import hash256
import time
import json

ZERO_HASH = '0' * 64
VERSION = 1

class Blockchain:
    def __init__(self): # Recall that 'self' is the current instance of the class. Here we initialise the object which is 1 new blockchain.
        self.chain = [] # List that holds all of our blocks - Due to be populated
        self.block_count = 0
        self.GenesisBlock()
        

    def GenesisBlock(self): #when we pass in self to a function (the current instance) it allows us to acess instance (object) variables and call instance methods
        BlockHeight = 0
        prevBlockHash = ZERO_HASH
        self.addBlock(BlockHeight, prevBlockHash) # Adding the genesis block to the blockchain via addBlock function, recall addBlock is an instance method of self

    def addBlock(self, BlockHeight, prevBlockHash):
        timestamp = int(time.time())
        Transaction = f"T sent {BlockHeight} Bitcoins to Z" # Creates unique transactions
        merkleRoot = hash256(Transaction.encode()).hex() #.hex() converts into hex form
        bits = 'ffff001f'
        blockheader = BlockHeader(VERSION, prevBlockHash, merkleRoot, timestamp, bits) #creating an instance immediately calls the __init__ function in blockheader.py
        blockheader.mine()
        self.chain.append(Block(BlockHeight, 1, blockheader.__dict__, 1, Transaction).__dict__)  # Appending our new instance of Block to the chain. 
        # We can access self.chain because we passed in self.
        # 
        # __dict__ means that we convert the class into a dictionary, we convert it into it the class properties
                                                                                   
                                                                    
        print(json.dumps(self.chain, indent = 4))
        self.block_count += 1   

    def main(self):
        while self.block_count < 4:
            lastBlock = self.chain[::-1] #creates a reverse copy of the self chain list
            BlockHeight = lastBlock[0]["Height"] + 1 #here we access a key named height from the last block. One of the properties of block class
            prevBlockHash = lastBlock[0]['BlockHeader']['blockHash'] #We are fetching the blockhash from the library Blockheader from the last block
            self.addBlock(BlockHeight, prevBlockHash)
  

if __name__ == "__main__":
    blockchain = Blockchain()
    blockchain.main()

The output is shown below:

and so on….

This project has given me a strong understanding of how Blockchain works at a fundamental level. As I now start to research use cases of this technology in applications as well as build my own , I can do so with a strong understanding of the underlying concepts.

I recommend to anyone who is interested in getting into blockchain development to do the same.

Adios

Tomisin Ajeneye

Tomisin Ajeneye

Tomisin Ajeneye