* * *

This tutorial is in continuation with the previous blog on making your own blockchain here. Make sure to go through this very clearly as we would be using the code to make our very own cryptocurrency.

We know that the core idea behing blockchains is to decentralize the data. Everyone knows everything and everyone monitors that the data isn't tampered. This very crux can be used to implement blockchains in cryptocurrency, thus eliminating the needs of any third party intermediaries such as bank. Nodes or people would be connected to each other in a Peer to Peer fashion and transactions can be directly associated with them with each transaction being a part of a block in a blockchain.Thanks to the hashing, the data integrity will be maintained.

If we look at the cryptocurrency technology stack, we find that it has three layers:

Layer 3: It corresponds to the technology that is used in our cryptocurrency i.e. Blockchains, the implementation behind the making of our currency. This we have already covered in our previous tutorial.

Layer 2: It corresponds to the protocol or the rules that define how transactions are to be done. Bitcoin, Ethereum, Neo, Ripple all these are protocols. Every protocol has a coin associated with it. We are going to work on this layer in this blog.

Layer 1: It corresponds to any digital asset that was created using Blockchains. We would be focussing on this in coming blogs and see how they rely on one more popular term Smart Contracts.

So now let's get our hands dirty! We will be making our own cryptocurrency, wherein we would be executing transactions, sending coins to our friends, and also maintain consensus. Let's take a moment to understand consensus. We know that the idea behind our currency is that all our friends (or nodes) are connected to each other directly and transactions will take place without any intervention of any bank. Now if in a a group of 4 friends 2 of them transfer each other coins, adding transaction to the chain, the other 2 should know about this, meaning that their chain should also be up to date. That is how we say that everyone knows everything. This updation of the chain across all the nodes connected is known as consensus.

Moving on to coding, make sure the previous code is running. After all our currency is nothing but just another blockchain. So your blockchain should be working. We would be adding the extra code in that.

Import some extra libraries

import requests
from uuid import uuid4
from urllib.parse import urlparse

These will be helpful in estabilishing consensus and making our transactions more distributed. 

We now make some changes in the init function of our blockchain class, where we will be adding to more variables: a list of transactions to hold the transactions made and a set of nodes to keep record od the nodes connected in our distributed network.

def __init__(self):
        self.chain = []
        self.transactions=[]
        self.create_block(proof = 1, previous_hash = '0')
        self.nodes=set()

Similarily in your create_block function, add transactions to our dictionary. We also empty our transaction list before appending our block to the chain, so that duplicate transactions do not occur in more than one block.

    def create_block(self, proof, previous_hash):
        block = {'index': len(self.chain) + 1,
                 'timestamp': str(datetime.datetime.now()),
                 'proof': proof,
                 'previous_hash': previous_hash,
                 'transactions':self.transactions}
        self.transactions=[]
        self.chain.append(block)
        return block

Now let us make a new function add_transactions that will be responsible for appending sender, receiver and the amount transferred in a dictionary format to the list transaction. The function will return the index of the next block, the block in which the transaction should be added.

    def add_transactions(self,sender,receiver,amount):
        self.transactions.append({'sender':sender,
                                  'receiver':receiver,
                                  'amount':amount})
        previous_block=self.get_previous_block()
        return previous_block['index']+1

We also make a quick function that connects nodes in our network. This is where our urlparse library comes into use. We pass the complete url of the node, out of which we are only interested in the ip address and the port, which can be retrieved via the netloc. For eg, if you pass 'http://www.cwi.nl:80/%7Eguido/Python.html' to urlparse the results will be:

o = urlparse('http://www.cwi.nl:80/%7Eguido/Python.html')
ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html',
            params='', query='', fragment='')

Here we need just the netloc portion of the result.

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

We now move onto estabilishing the consensus. Once a transaction has taken place among some nodes, all the nodes should be updated i.e each one of them should have the longest chain. How do we do this? We can ask for the current chain from each of our nodes in the network and check if any of the nodes have a chain that is less than the length of the largest chain. If there is, it means there were some transactions in some nodes and the chain needs to be updated. So we parse over our set of nodes and with the help of the requests library we send a request to each of them to get their chains. Remember we did implement this in last blog via our flask app with the function get_chain which returned the chain and its length. We then check this length with the maximum length and update accordingly. Our longest chain initially is none and the maximum length is the length of the current chain.

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

That's all the changes we need to make in our class. We now move on to mining the blockchain and working with the flask app.

Now everytime, a block is mined, we want the miner to get a reward of may be 10 coins. So the receiver would be the miner, the sender would be our node and amount would be 10. We make use of the uuid4 library, which assigns a unique uuid to a node. We also want to remove the '-', from our uuid.

app = Flask(__name__)
    
node_address=str(uuid4()).replace('-','')

We change our existing mine_block function so that we can have our list transactions in the response and also a transaction of 10 coins for the block has been mined.

@app.route('/mine_block', methods = ['GET'])
def mine_block():
    previous_block = blockchain.get_previous_block()
    previous_proof = previous_block['proof']
    proof = blockchain.proof_of_work(previous_proof)
    previous_hash = blockchain.hash(previous_block)
    blockchain.add_transactions(sender=node_address,receiver='Juhi',amount=10)
    block = blockchain.create_block(proof, previous_hash)
    
    response = {'message': 'Mission Accomplished!',
                'index': block['index'],
                'timestamp': block['timestamp'],
                'proof': block['proof'],
                'previous_hash': block['previous_hash'],
                'transactions': block['transactions']
                }
    return jsonify(response), 200

We also add an add_transaction function which will take a JSON in the body section. This JSON will contain the sender, receiver and the amount involved in transaction and using our class function add_transactions it returns the block in which the transaction has been appended.

@app.route('/add_transaction', methods = ['POST'])
def add_transaction():
    json=request.get_json()
    transaction_keys=['sender','receiver','amount']
    if not all (key in json for key in transaction_keys):
        return 'elements of transaction missing',400
    index=blockchain.add_transactions(json['sender'],json['receiver'],json['amount'])
    response={'message':f'transaction added in the block {index}'}
    return jsonify(response),201

We create a connect_node function that again takes a JSON in the body section. This JSON has all the nodes that needs to be connected in the network. With the help of the add_node fucntion we add all the nodes in the JSON.

@app.route('/connect_node', methods = ['POST'])
def connect_node():
    json=request.get_json()
    nodes=json.get('node')
    if nodes is None:
        return 'no node',400
    for node in nodes:
        blockchain.add_node(node)
    response={'message':'all nodes connected with following nodes:',
              'nodes':list(blockchain.nodes)}
    return response,201

We now quickly add a function replace_chain for handling consenus and updating the largest chain to all the nodes. 

@app.route('/replace_chain', methods = ['GET'])
def replace_chain():
    is_chain_replaced = blockchain.replace_chain()
    if is_chain_replaced:   
        response = {'message': 'nodes updated.',
                    'new_chain':blockchain.chain}
    else:
        response = {'message': 'already largest chain',
                    'new_chain':blockchain.chain}
    return jsonify(response), 200

Now go ahead and copy the entire code in 3 different files to create three different nodes. Make sure to change the ports in the line app.run(host = '0.0.0.0', port = 5001) with 5001, 5002 and 5003, making 3 different nodes to work with in our network. And that's it! Run these 3 different python scripts in 3 different consoles.

Let's head over to Postman and begin the action! Run the 3 node address in 3 different tabs. 

Now connect these nodes to each other. For every node do a POST request and supply a JSON of all the the other nodes.

Now let's mine a block and see what happens. On mining a block I transferred 10 coins to Jaroli Sir. Thank me later Sir!

To add a transaction, we give a JSON containing the sender, receiver and amount. The transaction will be added to the block after I mine the blocks.

However, if you now see the chains in other nodes through get_chain, it will still show the old nodes and not these new transactions. To get all the nodes updated we perform our replace_chain function

And that's how all the nodes will now get to the transactions.

In a practical scenario, we wont be lisiting sender or a receiver in plain names, instead we will be using their public keys. This is something we will leave for our next tutorial. Also a brownie task for you can be instead of copy pasting the code in n scripts to make n nodes, can we make use of a graph?

In the next blogs we would be taking one step further with smart contracts but until then happy blockchaining!