This is a community collaborative translation of the article, has been translated, more information please click
Introduction to collaborative translation 。

Are you, like me, very interested in the blockchain technology at the bottom of cryptocurrencies and particularly interested in how they work?

However, learning blockchain technology is not plain sailing. I watched a lot of video tutorials and various courses, and finally felt that there are too few practical courses available.

I like to learn by doing, especially on a code basis to understand how the whole thing works. If you like this way of learning as much as I do, by the time you finish this tutorial, you will know how blockchain technology works.

Write it before you start

Remember, a blockchain is an immutable, ordered chain of records called a block. They can contain transactions, files, or any data you like. But the important thing is, they’re hashed together.

If you’re not familiar with hashing, here’s an explanation.

What is the purpose of this guide? You’ll be comfortable reading and writing basic Python, and since we’ll be talking to blockchain over HTTP, you’ll also want to understand how HTTP works.

What do I need to prepare? To make sure you have Python 3.6+ installed (and PIP), you also need to install the Flask, Requests libraries:

` PIP install Flask = = 0.12.2 requests = = 2.18.4 `Copy the code

By the way, you’ll also need an HTTP-enabled client, like Postman or cURL, or whatever. Where is the source code? You can click here

Step 1: Create a blockchain

Open your favorite text editor or IDE. I personally prefer PyCharm. Create a new file named blockchain.py. We’re just going to use this one file. But if you’re not sure, you can also refer to the source code.

Describing blockchain

We are creating a Blockchain class whose constructor creates an initialized empty list (to store our Blockchain) and another to store transactions. Here is an example of our class:

blockchain.py

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

Our Blockchain class is responsible for managing the chained data, which stores transactions and has methods that add new blocks to the chained data. Let’s start adding more methods

What does the block look like?

Each block has an index, a timestamp (Unix timestamp), a list of transactions, a checksum (more on that later), and a hash of the previous block.

Here is an example of a Block:

blockchain.py

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 a blockchain should be obvious — each new block contains a hash of the previous block within it. This is crucial, because it’s what makes blockchains immutable: If an attacker corrupts an earlier block in the blockchain, all subsequent blocks will contain incorrect hashes.

Does that make sense? If you haven’t figured it out yet, take a moment to think it over — this is the core idea behind blockchain.

Add transactions to blocks

We will need a way to add transactions to blocks. Our new_transaction() method does just that, and it’s pretty simple:

blockchain.py

class Blockchain(object):
    ...

    def new_transaction(self, sender, recipient, amount):
        """
        Creates a new transaction to go into the next mined Block
        :param sender: <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'] + 1
Copy the code

The new_TRANSACTION () method adds the transaction to the list, which returns an index of the block to which the transaction will be added – mining the next one is useful later for users who submit the transaction.

Create a new block

After our Blockchain is instantiated, we need to add trands (a block with no lead blocks) to it. We also need to add a proof to our origin block, which is the result of mining (or proof of work). We’ll talk more about mining later.

In addition to creating the creation block in the constructor, we also complete the new_block(), new_transaction(), and hash() functions:

blockchain.py

import hashlib import json from time import time class Blockchain(object): def __init__(self): Self.current_transactions = [] self.chain = [] self.new_block(previous_hash=1, proof=100) def new_block(self, Proof, previous_hash=None): """ Creates a new block to the blockchain :param proof: <int> Proof generated by the working proof algorithm :param previous_hash: (Optional) < STR > Hash value of previous block :return: <dict> new block "" 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): """ Sender: < STR > Sender: < STR > Sender: < STR > Self.current_transactions. append({'sender': sender, 'Recipient' : recipient, 'amount': amount, }) return self.last_block['index'] + 1 @property def last_block(self): Return self.chain[-1] @staticMethod def hash(block): """ To generate a sha-256 value for a block: param block: <dict> block: return: < STR > """ # We must ensure that the dictionary (block) is sorted, Otherwise we will get inconsistent hashes block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest()Copy the code

The code above should be straightforward – I’ve added some comments and documentation for clarity. We’re almost done with our blockchain. But at this point you must be wondering how new blocks are created, forged, or mined.

Proof of work algorithm

Use proof-of-work (PoW) algorithms to demonstrate how new blocks are created or mined on the blockchain. The goal of PoW is to calculate a number that meets certain conditions, which must be computationally difficult for all but easy to verify. This is the core idea behind proof of work.

We’ll see a simple example to help you understand:

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’s y? With Python:

from hashlib import sha256 x = 5 y = 0 # We don't know what y should be yet... while sha256(f'{x*y}'.encode()).hexdigest()[-1] ! = "0": y += 1 print(f'The solution is y = {y}')Copy the code

The result is: y = 21. The generated Hash value must end in 0.

hash(5 * 21) = 1253e9373e... 5e3600155e860Copy the code

In Bitcoin, the proof-of-work algorithm is called Hashcash, and it is similar to the above problem, except it is very difficult to calculate. That’s where the miners scramble to calculate for the right to create blocks. Usually, the difficulty of the calculation is proportional to the number of specific characters that the target string needs to satisfy, and the miner will be rewarded with a certain amount of Bitcoin (through a transaction) after the result is calculated.

Verifying the results is, of course, very easy.

Implement proof of work

Let’s implement a similar PoW algorithm. The rule is similar to the above example:

Find a number P that starts with four zeros in the Hash value of the string concatenated with the previous block’s proof.

blockchain.py

import hashlib
import json

from time import time
from uuid import uuid4

class Blockchain(object):
    ...

    def proof_of_work(self, last_proof):
        """
        Simple Proof of Work Algorithm:
         - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
         - p is the previous proof, and p' is the new proof
        :param last_proof: <int>
        :return: <int>
        """

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

        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        """
        Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
        :param last_proof: <int> Previous Proof
        :param proof: <int> Current Proof
        :return: <bool> True if correct, False if not.
        """

        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"
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.

Step 2: Blockchain as API interface

We’ll use the Python Flask framework, a lightweight Web application framework that makes it easy to map network requests to Python functions, and now let’s get Blockchain running on Flask Web.

We will create three interfaces:

  • /transactions/newCreate a transaction and add it to the block
  • /mineTell the server to mine new blocks
  • /chainReturns the entire blockchain

Create a node

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

blockchain.py

import hashlib import json from textwrap import dedent from time import time from uuid import uuid4 from flask import Flask class Blockchain(object): ... # Instantiate our Node app = Flask(__name__) # Generate a globally unique address for this node node_identifier = str(uuid4()).replace('-', '') # Instantiate the Blockchain blockchain = Blockchain() @app.route('/mine', methods=['GET']) def mine(): return "We'll mine a new Block" @app.route('/transactions/new', methods=['POST']) def new_transaction(): return "We'll add a new transaction" @app.route('/chain', methods=['GET']) def full_chain(): response = { 'chain': blockchain.chain, 'length': len(blockchain.chain), } return jsonify(response), 200 if __name__ == '__main__': = '0.0.0.0' app. The run (host, port = 5000)Copy the code

A brief explanation of the above code:

  • Line 15: Instantiate the node. Read more about Flask.
  • Line 18: Create a random name for the node. .
  • Line 21: Instantiate the Blockchain class.
  • Lines 24–26: Create /mine interface, GET request.
  • Line 28–30: Create /transactions/new interface, POST request, can send transaction data to the interface.
  • Lines 32–38: Create the /chain interface and return the entire blockchain.
  • Lines 40–41: The server runs on port 5000.

Send a deal

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

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

Because we already have a way to add transactions, it’s easy to add transactions based on the interface. Let’s write a function for adding transactions:

blockchain.py

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

...

@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    values = request.get_json()

    # Check that the required fields are in the POST'ed data
    required = ['sender', 'recipient', 'amount']
    if not all(k in values for k in required):
        return 'Missing values', 400

    # Create a new Transaction
    index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])

    response = {'message': f'Transaction will be added to Block {index}'}
    return jsonify(response), 201 
Copy the code

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

blockchain.py

import hashlib
import json

from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

...

@app.route('/mine', methods=['GET'])
def mine():
    # We run the proof of work algorithm to get the next proof...
    last_block = blockchain.last_block
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    # We must receive a reward for finding the proof.
    # The sender is "0" to signify that this node has mined a new coin.
    blockchain.new_transaction(
        sender="0",
        recipient=node_identifier,
        amount=1,
    )

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

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }
    return jsonify(response), 200
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.

Step 3: Run blockchain

You can use cURL or Postman to interact with the API

Start the Server:

$python blockchain.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)Copy the code

Let’s dig through request http://localhost:5000/mine (GET) :


Make a GET request with Postman.

Create a trade request, request http://localhost:5000/transactions/new (POST), as shown in figure


If you do not use Postman, use the following cURL statement:

$ curl -X POST -H "Content-Type: application/json" -d '{
 "sender": "d4ee26eee15148ee92c6cd394edd974e",
 "recipient": "someone-other-address",
 "amount": 5
}' "http://localhost:5000/transactions/new"
Copy the code

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

{"chain": [{"index": 1, "previous_hash": 1, "proof": 100, "timestamp": 1506280650.770839, "transactions": []}, {"index": 2, "previous_hash": "c099bc...bfb7", "proof": 35293, "timestamp": 1506280664.717925, "transactions": [ { "amount": 1, "recipient": "8bbcb347e0634905b0cac7955bae152b", "sender": "0" } ] }, { "index": 3, "previous_hash": "Eff91a...10f2", "proof": 35089, "timestamp": 1506280666.1086972, "transactions": [{"amount": 1," Recipient ": "8bbcb347e0634905b0cac7955bae152b", "sender": "0" } ] } ], "length": 3 }Copy the code

Step 4: 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. /nodes/registerReceives a new node list in the form of a URL.
  2. /nodes/resolvePerform a consistency algorithm to resolve any conflicts and ensure that the nodes have the correct chain.

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

blockchain.py

. from urllib.parse import urlparse ... class Blockchain(object): def __init__(self): ... self.nodes = set() ... def register_node(self, address): """ Add a new node to the list of nodes :param address: < STR > Address of node.eg. 'http://192.168.0.5:5000' :return: None """ 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 earlier, conflicts occur when one node has a different chain from another. To solve this problem, we will make the longest effective chain is the most authoritative rule. In other words: the longest chain in the network is the most authoritative. We’re going to use this algorithm to reach consensus between nodes in the network.

blockchain.py

. import requests class Blockchain(object) ... def valid_chain(self, chain): """ Determine if a given blockchain is valid :param chain: <list> A blockchain :return: <bool> True if valid, False if not """ last_block = chain[0] current_index = 1 while current_index < len(chain): block = chain[current_index] print(f'{last_block}') print(f'{block}') print("\n-----------\n") # Check that the hash of the block is correct 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): """ This is our Consensus Algorithm, it resolves conflicts by replacing our chain with the longest one in the network. :return: <bool> True if our chain was replaced, False if not """ neighbours = self.nodes new_chain = None # We're only looking for chains longer than ours max_length = len(self.chain) # Grab and verify the chains from all the nodes in our network for node in neighbours: response = requests.get(f'http://{node}/chain') if response.status_code == 200: length = response.json()['length'] chain = response.json()['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 to see if a chain is valid by iterating through each block and verifying hashes and proofs.

Resolve_conflicts () is a method that loops through all of our neighbor nodes, downloads their chains, and validates them using the method above. If we find a valid chain that is longer than ours, we replace our chain.

We registered two endpoints with our API, one for adding adjacent nodes and one for resolving conflicts:

blockchain.py

@app.route('/nodes/register', methods=['POST'])
def register_nodes():
    values = request.get_json()

    nodes = values.get('nodes')
    if nodes is None:
        return "Error: Please supply a valid list of nodes", 400

    for node in nodes:
        blockchain.register_node(node)

    response = {
        'message': 'New nodes have been added',
        'total_nodes': list(blockchain.nodes),
    }
    return jsonify(response), 201

@app.route('/nodes/resolve', methods=['GET'])
def consensus():
    replaced = blockchain.resolve_conflicts()

    if replaced:
        response = {
            'message': 'Our chain was replaced',
            'new_chain': blockchain.chain
        }
    else:
        response = {
            'message': 'Our chain is authoritative',
            'chain': blockchain.chain
        }

    return jsonify(response), 200
Copy the code

At this point, if you like, you can use a different machine and start different nodes on your network. Or start the process using a different port on the same machine. I created another node on a different port on my machine and registered it with the current node. Therefore, I have two nodes: http://localhost:5000 and http://localhost:5001. Register a new node:


I then mined some new blocks on node 2 to make sure the chain was longer. After that, I call GET/Nodes /resolve on node 1, where the chain is replaced by a consistency algorithm:


It’s a package to go get some friends together to help test your blockchain.

I hope this article inspires you to create something new. I’m fascinated by digital currencies because I believe blockchain will soon change the way we look at things, including the economy, government, archives management, and more.

Update: I plan to continue my discussion of blockchain transaction validation mechanisms and discuss some of the ways in which blockchain can be produced in an upcoming part 2.

Please go to:
Build your own blockchain step by step in Python