This article is mainly translated from:learn-blockchains-by-building-oneFlask (flask) : flask (flask) : flask (flask) : flask (Flask) Code article under the web disk link.

We are curious about the rise of digital money and want to know how the technology behind it, blockchain, works.

However, it is not easy to fully understand blockchain. I like to learn by doing, and I can master the technology more firmly by writing codes. The understanding of blockchain can be deepened by building a blockchain.

The preparatory work

This article requires a basic understanding of Python, the ability to read and write basic Python, and a basic understanding of HTTP requests.

We know that blockchains are immutable, ordered chains of records of blocks, which can be transactions, files, or whatever data you want, but importantly they are linked by hashes.

Environment to prepare

Make sure Python3.5, PIP, Django, requests, urllib, json, hashlib are installed.

pip install django requests
Copy the code

You also need an HTTP client, such as Postman, cURL, or another client. Postman is used as an example in this article.

Start creating Blockchain

Django-admin startProject demo django-admin startProject demo django-admin startProject demo django-admin startProject demo


Blockchain class

Create a Blockchain class in Views and create two lists in the constructor, one for storing the Blockchain and one for storing transactions.

Here is the framework for the Blockchain class:

class Blockchain(object):
    def __init__(self):
        self.chain = []
        self.current_transactions = []
        
    def new_block(self):
        # Creates a new Block and adds it to the chain
        pass
    
    def new_transaction(self):
        # Adds a new transaction to the list of transactions
        pass
    
    @staticmethod
    def hash(block):
        # Hashes a Block
        pass

    @property
    def last_block(self):
        # Returns the last Block in the chain
        pass
Copy the code

The Blockchain class is used to manage chains, store transactions, add new blocks, etc. Let’s further refine these methods.

Block structure

Each block contains attributes: index (index), Unix timestamp (timestamp), list of transactions (transactions), proof of work (explained later), and Hash value for the previous block.

Here is the structure of a block:

Block = {'index': 1, 'timestamp': 1506057125.900785, 'transactions': [{'sender': "8527147fe1f5426f9dd545de4b27ee00", 'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f", 'amount': 5, } ], 'proof': 324984774000, 'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" }Copy the code

At this point, the concept of blockchain is clear. Each new block contains the Hash of the previous block. This is the key point, which guarantees the immutability of the blockchain. If an attacker breaks one of the previous blocks, all subsequent blocks will Hash incorrectly. If you don’t understand, digest it slowly.

Join the deal

Next we need to add a transaction to complete the new_transaction method

class Blockchain(object): ... Def new_transaction(self, sender, Recipient, amount): """ def new_transaction(self, sender, Recipient, amount): <str> Address of the Sender :param recipient: <str> Address of the Recipient :param amount: <int> Amount :return: <int> The index of the Block that will hold this transaction """ self.current_transactions.append({ 'sender': sender, 'recipient': recipient, 'amount': amount, }) return self.last_block['index'] + 1Copy the code

Method adds a transaction record to the list and returns an index of the block to which the record will be added (the next block to be mined), which will be useful later when the user submits a transaction.

Create a new block

After Blockchain is instantiated, we need to construct a creation block (the first block with no previous blocks) and add a proof of work to it. Each block has to go through proof of work, commonly known as mining, which we’ll talk about later.

To construct the creation block, we also need to refine the new_block(), new_transaction(), and hash() methods:

class Blockchain(object):
    def __init__(self):
        self.chain = []
        self.current_transactions = []
        self.new_block(previous_hash=1, proof=100)
        self.nodes = set()
    def new_block(self,proof,previous_hash= None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,
            'proof':proof ,
            'previous_hash': previous_hash or self.hash(self.chain[-1]),
        }
        self.current_transactions = []
        self.chain.append(block)
        return block

    def new_transaction(self,sender,recipient,amount):
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })
        return self.last_block['index']+1

    @staticmethod
    def hash(block):
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()
Copy the code

With the above code and comments to get an intuitive understanding of the blockchain, let’s take a look at how blocks are dug up.

Understand proof of workload

New blocks rely on proof-of-work algorithms (PoW) to construct them. The goal of PoW is to find a number that meets certain criteria, a number that is difficult to calculate but easy to verify. That’s the core idea of proof of work.

To make it easier to understand, here’s an example:

Suppose the Hash value of the product of an integer x times another integer y must end in 0, i.e. Hash (x * y) = ac23dc… 0. Let’s say x is equal to 5, what is y?

The Python implementation is as follows:

from hashlib import sha256 x = 5 y = 0 while sha256(str(x*y).encode()).hexdigest()[:4] ! = "0000": y += 1 print(y,sha256(str(x*y).encode()).hexdigest()[:4]) print(y)Copy the code

In Bitcoin, a proof-of-work algorithm called Hashcash is used, which is similar to the above problem. Miners jostle for the right to create blocks to calculate the results. Typically, the difficulty of the calculation is proportional to the number of specific characters the target string needs to satisfy, and the miner is rewarded with Bitcoin for the result. Of course, this result is very easy to verify on the web.

Implement proof of work

Let’s implement a similar PoW algorithm that looks for a number p that starts with four zeros in the Hash value of the string concatenated with proof of the previous block.

import hashlib
import json

from time import time
from uuid import uuid4


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

    def proof_of_work(self, last_proof):
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        guess = str(last_proof*proof).encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:5] == "00000"
Copy the code

One way to measure the complexity of an algorithm is to change the number that starts with zero. Using four for the demonstration, you’ll see that each additional zero greatly increases the time it takes to compute the result.

Now that the Blockchain class is almost complete, use HTTP Requests for interaction.

Blockchain as an API interface

We’ll use the Python Django framework, a lightweight Web application framework that makes it easy to map network requests to Python functions. Now let’s try it out:

We will create three interfaces:

  • /transactions/new Creates a transaction and adds it to the block
  • /mine tells the server to mine a new block
  • /chain returns the entire blockchain

Create a node

Our “Django Web server” will act as a node in the blockchain network. Let’s add some framework code:

node_identifier = str(uuid4()).replace('-', '')

# Instantiate the Blockchain
blockchain = Blockchain()
def mine(request):
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)
    print(proof)
    blockchain.new_transaction(
         sender="0",
         recipient=node_identifier,
         amount=1,
     )

     # Forge the new Block by adding it to the chain
    block = blockchain.new_block(proof)

    response = {
         'message': "New Block Forged",
         'index': block['index'],
         'transactions': block['transactions'],
         'proof': block['proof'],
         'previous_hash': block['previous_hash'],
    }
    print(response)
    return HttpResponse(json.dumps(response))
def new_transaction(request):
    values = json.loads(request.body.decode('utf-8'))
    required = ['sender', 'recipient', 'amount']
    if not all(k in values for k in required):
        return 'Missing values'
    index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
    print(index)
    response = {'message': 'Transaction will be added to Block %s'%index}
    return HttpResponse(json.dumps(response))
def full_chain(request):
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain),
    }
    return HttpResponse(json.dumps(response))
Copy the code

Add A URL routing node: Run the service

from demo import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^mine', views.mine),
    url(r'^transactions/new/', views.new_transaction),
    url(r'^chain/', views.full_chain),
    url(r'^register', views.register_nodes),
    url(r'^resolve', views.consensus),
]
Copy the code

Run the service

Python manage. Py runserver then executes 127.0.0.1:8000Copy the code

Send a deal

The transaction data sent to the node is structured as follows:

{
 "sender": "my address",
 "recipient": "someone else's address",
 "amount": 5
}
Copy the code

Add a transaction to the service


dig

Mining is the magic. It’s simple. It does three things:

  1. Calculate the workload to prove PoW
  2. Grant the miner (himself) one coin through a new transaction
  3. Construct a new block and add it to the chain
  def proof_of_work(self, last_proof):
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        guess = str(last_proof*proof).encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:5] == "00000"
Copy the code

Note that the recipient of the transaction is our own server node, and most of the work we do is just interacting around the Blockchain class methods. At this point, our blockchain is complete, let’s actually run it

Running blockchain

Use Postman to interact with the API

Let’s dig through request to http://127.0.0.1:8000/mine


After dug two mines, there are three block, by requesting http://localhost:8000/chain can get all the pieces of information.

{
    "chain": [
        {
            "transactions": [],
            "proof": 100,
            "timestamp": 1520314374.7261052,
            "index": 1,
            "previous_hash": 1
        },
        {
            "transactions": [
                {
                    "sender": "0",
                    "recipient": "27d4aae55b2848dcae52bc722d86e0c3",
                    "amount": 1
                }
            ],
            "proof": 1771087,
            "timestamp": 1520314389.5019505,
            "index": 2,
            "previous_hash": "32fa73f48240160257e95fdf8422c6df734b5d7e8ceb69a41a5578643c1d36fb"
        },
        {
            "transactions": [
                {
                    "sender": "d4ee26eee15148ee92c6cd394edd9705",
                    "recipient": "5",
                    "amount": 500
                },
                {
                    "sender": "0",
                    "recipient": "27d4aae55b2848dcae52bc722d86e0c3",
                    "amount": 1
                }
            ],
            "proof": 100,
            "timestamp": 1520314592.4745598,
            "index": 3,
            "previous_hash": "e6b1be488e0ed20fe3ec51135e5fafb4dfffaa28a190967106a5dd3e89e4b3aa"
        }
    ],
    "length": 3
}
Copy the code

Consistency (consensus)

We already have a basic blockchain that accepts transactions and mines. But blockchain systems are supposed to be distributed. Since it’s distributed, how on earth can we guarantee that all nodes have the same chain? This is the problem of consistency, and if we want to have multiple nodes on the network, we have to implement a consistent algorithm.

Registered nodes

Before implementing the consistency algorithm, we need to find a way for a node to know its neighbors. Each node needs to keep a record of other nodes in the network. So let’s add some new interfaces:

  1. /register receives a list of new nodes in URL form
  2. /resolve Performs a consistency algorithm to resolve any conflicts and ensure that nodes have the correct chain

Let’s modify the init function of the Blockchain and provide a method to register nodes:

from urllib.parse import urlparse
...


class Blockchain(object):
    def __init__(self):
        ...
        self.nodes = set()
        ...
    def register_node(self, address):
        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)
Copy the code

We use sets to store nodes, which is an easy way to avoid adding nodes repeatedly.

Implement consensus algorithm

As mentioned above, conflict refers to the fact that different nodes have different chains. In order to solve this problem, it is stipulated that the longest and effective chain is the final chain. In other words, the longest and effective chain in the network is the actual chain.

We use the following algorithm to achieve consensus in the network

class Blockchain(object): def __init__(self): ... def valid_chain(self, chain): last_block = chain[0] current_index = 1 while current_index < len(chain): block = chain[current_index] if block['previous_hash'] ! = self.hash(last_block): return False # Check that the Proof of Work is correct if not self.valid_proof(last_block['proof'], block['proof']): return False last_block = block current_index += 1 return True def resolve_conflicts(self): neighbours = self.nodes new_chain = None max_length = len(self.chain) for node in neighbours: response = requests.get('http://%s/chain' %node) if response.status_code == 200: length = json.loads(response)['length'] chain = json.loads(response)['chain'] # Check if the length is longer and the chain is valid if length > max_length and self.valid_chain(chain): max_length = length new_chain = chain # Replace our chain if we discovered a new, valid chain longer than ours if new_chain: self.chain = new_chain return True return FalseCopy the code

The first method, valid_chain(), checks for a valid chain, traversing each block to verify hash and proof.

The second method, resolve_conflicts(), resolves conflicts by traversing all neighboring nodes, checking the validity of the chain with the previous method, and replacing your own chain if it is found to be longer

Add two routes to the URL, one to register the node and one to resolve conflicts.

from demo import views
urlpatterns = [
    url(r'^register', views.register_nodes),
    url(r'^resolve', views.consensus),
]
Copy the code

You can simulate a multi-node network by running nodes on different machines, or by opening different network ports on one machine. Here is a demonstration of opening different ports on the same machine. Run a command on different terminals to start two nodes: http://127.0.0.1:8000 and http://127.0.0.1:8100


Then cut two blocks on node 8100 to make sure they are longer chains, and access interface /resolve on node 8000, where the chain on node 8100 will be replaced by the chain on node 8000 via consensus algorithm.


The Python code provided with this article can run natively and upload the entire project as a packageBaidu cloud web disk, attention and private message me, access to the network disk password.

Address: pan.baidu.com/s/1bZSSyQJY…


Advertising time: finally give yourself a salt ~~ welcome to turn over my brand when you are free (Zhihu No. : Cloth Road) to seeDevelopment operation and MaintenanceColumn article, hope more attention and praise is to give the author the best encouragement!