BitKhan: Creating my own cryptocurrency with Python

with Blockchain on a decentralized network

Posted on May 24, 2018

Created a cryptocurrency using the Blockchain Technology with just Python and a few libraries. Built a working model to transfer the currency from one node to another on the decentralized network. The transactions are tamper-proof and uses the same sha256 hash encryption that Bitcoin uses.

Getting Started

The Blockchain was built with Python 3.6 and uses Flask to run the app.

The Script

Importing Libraries
import datetime
import json
from flask import Flask, jsonify, request
import hashlib
import requests
from uuid import uuid4
from urllib.parse import urlparse

These are all the libraries that we need to make the cryptocurrency.

We’ll create the blockchain in three parts: Creating the Blockchain, Mining the Blockchain and Decentralizing the Blockchain

Part 1: Creating the Blockchain

Let’s create a class Blockchain, under which we’ll define all the basic functions needed for a blockchain. In the __init__ function of the Blockchain, we initiate all the global variables needed.

class Blockchain:
    def __init__(self):
        self.chain = []
        self.transactions = []
        self.create_block(nonce = 1, previous_hash = '0')
        self.nodes = set()
        self.difficulty = 4

Note: All functions in this part of the tutorial will be under the main class Blockchain.

The ‘create_block’ function, as the name suggests, creates a block on the blockchain. It is called when the Blockchain is initialised and the ‘genesis block’ is created with the predefined dictionary, i.e., when the length of the chain is 0.

def create_block(self, nonce, previous_hash):
        block = {'block_id': len(self.chain)+1,
                 'timestamp': str(datetime.datetime.now()),
                 'nonce': nonce,
                 'previous_hash': previous_hash,
                 'hash': '0',
                 'transactions': self.transactions}
        self.transactions = []
        if len(self.chain) == 0:
            self.chain.append(block)
        return block

And,

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

This function is for returning the last block on the chain.

def proof_of_work(self, block):
        encoded_block = json.dumps(block, sort_keys = True).encode()
        nonce = 1
        valid_hash = False
        while valid_hash is False:
            hash_operation = hashlib.sha256(encoded_block + str(nonce).encode()).hexdigest()
            if hash_operation[:self.difficulty] == self.difficulty*'0':
                valid_hash = True
            else:
                nonce += 1
        return hash_operation, nonce

The ‘proof_of_work’ function creates the sha256 hash for the block that is passed through it. It is used to mine the block and to check the integrity of the block.

def add_to_chain(self, block):
        block['hash'], block['nonce'] = self.proof_of_work(block)
        self.chain.append(block)
        return block

This function is called when the ‘proof_of_work’ is called to mine the block. Once the hash is created for the mined block, this function appends the mined block to the Blockchain.

def is_chain_valid(self, chain):
        previous_block = chain[0].copy()
        block_index = 1
        while block_index < len(chain):
            block = chain[block_index].copy()
            if block_index == 1:
                previous_block = block
                block_index += 1
            else:
                previous_block['hash'], previous_block['nonce'] = '0', 1
                hash_operation, _ = self.proof_of_work(previous_block)
                if block['previous_hash'] != hash_operation:
                    return False
                block['hash'], block['nonce'] = '0', 1
                hash_operation, _ = self.proof_of_work(block)
                if hash_operation[:self.difficulty] != self.difficulty*'0':
                    return False
                previous_block = block
                block_index += 1
        return True

The above code is to check if the chain is a valid chain or not. It will check if the hash of the previous block matches with the hash generated for the previous block. The sha256 has is deterministic and hence this function can tell us if the block is tampered with or not.

def add_transaction(self, sender, receiver, bitkhan):
        self.transactions.append({'sender': sender, 'receiver': receiver, 'bitkhan': bitkhan})
        previous_block = self.get_previous_block()
        return previous_block['block_id'] + 1

'add_transaction’ adds the transaction details to the ‘transaction’ list, which later gets added to the ‘transaction’ key of the block when the block is mined.

def add_node(self, address):
        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)

There is more than one node in a decentralized network and ‘add_node’ gets the address of the node which wants to connect to our Blockchain network.

def replace_chain(self):
        network = self.nodes
        longest_chain = None
        max_length = len(self.chain)
        for node in network:
            try:
                response = requests.get(f'http://{node}/get_chain')
                if response.status_code == 200:
                    length = response.json()['length']
                    chain = response.json()['chain']
                    if length > max_length and self.is_chain_valid(chain):
                        max_length = length
                        longest_chain = chain
            except Exception:
                continue
        if longest_chain:
            self.chain = longest_chain
            return True
        return False

‘The Longest Chain is King’ is the golden rule for the Bitcoin Blockchain and we use the same to create our cryptocurrency. The ‘replace_chain’ function replaces the Blockchain on the nodes of the network with the longest chain found.

Note: All the functions defined above come under the class Blockchain

Part 2: Mining the Blockchain
# Creating a WebApp
app = Flask(__name__)

# Creating a address for the node on the Port 5000
node_address = str(uuid4()).replace('-','')

# Initiating a Blockchain
blockchain = Blockchain()

Creating the Flask WebApp and Initializing the Blockchain.

# Mining a new block
@app.route('/mine_block', methods = ['GET'])
def mine_block():
    previous_block = blockchain.get_previous_block()
    blockchain.add_transaction(sender = node_address, receiver = 'laptop_server', bitkhan = 12.5)
    previous_hash = previous_block['hash']
    block = blockchain.create_block(previous_hash = previous_hash, nonce = 1)
    block = blockchain.add_to_chain(block)
    response = {'message': 'Congrats, you just a mined a block!',
                'block_id': block['block_id'],
                'timestamp': block['timestamp'],
                'nonce': block['nonce'],
                'previous_hash': block['previous_hash'],
                'hash': block['hash'],
                'transactions': block['transactions']}
    return jsonify(response), 200

We use ‘GET’ and ‘POST’ requests to execute our functions. We define the url and the function to mine a new block in the Blockchain. It uses the ‘proof_of_work’ function to encrypt the transactions in the block with the cryptographic hash.

# Getting the full Blockchain    
@app.route('/get_chain', methods = ['GET'])
def get_chain():
    response = {'chain': blockchain.chain,
                'length': len(blockchain.chain)}
    return jsonify(response), 200

The ‘get_chain’ request is to get the current state of the Blockchain. It returns all blocks of the Blockchain.

# Checking the validity of Blockchain
@app.route('/is_valid', methods = ['GET'])
def is_valid():
    is_valid = blockchain.is_chain_valid(blockchain.chain)
    if is_valid:
        response = {'message': 'The Blockchain is valid!'}
    else:
        response = {'message': 'The Blockchain is not valid!'}
    return jsonify(response), 200

This request checks the validity of the Blockchain by executing the ‘is_chain_valid’ function which compares the hashes on the Blockchain

# Adding a transaction to the Blockchain
@app.route('/add_transaction', methods = ['POST'])
def add_transaction():
    json = request.get_json()
    transaction_keys = ['sender','receiver','bitkhan']
    if not all (key in json for key in transaction_keys):
        return 'Some elements are missing in the transaction', 400
    block_id = blockchain.add_transaction(json['sender'], json['receiver'], json['bitkhan'])
    response = {'message': f'This transaction will be added to Block {block_id}'}
    return jsonify(response), 201

This is a ‘POST’ request and it’ll add the transaction details to the ‘transaction’ list.

Part 3: Decentralizing the Blockchain
# Connecting new nodes
@app.route('/connect_node', methods = ['POST'])
def connect_node():
    json = request.get_json()
    nodes = json.get('nodes')
    if nodes is None:
        return "No node", 400
    for node in nodes:
        blockchain.add_node(node)
    response = {'message': 'All the nodes are now connected. The Bitkhan blockchain contains the following nodes:',
                'total_nodes': list(blockchain.nodes)}
    return jsonify(response), 201

Here, we post the addresses of the nodes to which you want to connect with. Our node plus the other nodes passed through this request form the Blockchain network

# Replacing the blockchain with the longest chain if needed 
@app.route('/replace_chain', methods = ['GET'])
def replace_chain():
    is_chain_replaced = blockchain.replace_chain()
    if is_chain_replaced:
        response = {'message': 'The Blockchain was replaced by the longest chain',
                    'new_chain': blockchain.chain}
    else:
        response = {'message': 'All good. No changes needed to the chain',
                    'current_blockchain': blockchain.chain}
    return jsonify(response), 200

This request runs the ‘replace_chain’ function and replaces your Blockchain with the longest Blockchain available in the network

# Running the App 
app.run(host = '0.0.0.0', port = 5000)

And finally we are ready to run the app

Blockchain Demo

I ran the app on my laptop and also my smartphone. The below is a live demo of how these two nodes interact with each other to transfer the cryptocurrency that we just created.

I used ngrok to get a public url of my localhost.