Computer competition python blockchain implementation – proof of work workload proof consensus algorithm

Article directory

  • 0 Preface
  • 1 Blockchain basics
    • 1.1 Internal structure of Bitcoin
    • 1.2 Implemented blockchain data structure
    • 1.3 Points to note
    • 1.4 The core of blockchain – workload proof algorithm
      • 1.4.1 The Byzantine Generals Problem
      • 1.4.2 Solutions
      • 1.4.3 Code implementation
  • 2 Quickly implement a blockchain
    • 2.1 What is blockchain
    • 2.2 What does a complete fast include?
    • 2.3 What is mining
    • 2.4 Proof-of-work algorithm:
    • 2.5 Implementation code
  • 3 finally

0 Preface

A series of high-quality competition projects, what I want to share today is

Python blockchain implementation – proof of work workload proof consensus algorithm

This project is relatively new and suitable as a competition topic. It is highly recommended by senior students!

More information, project sharing:

https://gitee.com/dancheng-senior/postgraduate

1 Blockchain Basics

The senior explained the components of the blockchain in detail using the structure of Bitcoin

1.1 Internal Structure of Bitcoin

  • previous hash (the hash of the previous block)
  • merkle root (Merkle tree root node, internally stores transaction data)
  • timestamp (time when the current block was generated)
  • nonce (number of times to calculate hash value)

1.2 Implemented blockchain data structure

  • index current block
  • timestamp The timestamp when the block was created
  • data transaction information
  • previousHash the hash of the previous block
  • hash the hash of the current block

1.3 Notes

The first block is called the genesis block. When the blockchain is created, it is produced by default. Here, a simple linked list is used instead of a Merkle tree for storage.

Sample code

?


    from hashlib import sha256
    //block schema
    class Block:
         
        def __init__(self,index,timestamp,data,previousHash=""):
            
            self.index = index
            self.timestamp = timestamp
            self.data = data
            self.previousHash = previousHash
            self.hash = self.calculateHash()
            
        //Calculate the hash value of the current block
        def calculateHash(self):
            plainData = str(self.index) + str(self.timestamp) + str(self.data)
            return sha256(plainData.encode('utf-8')).hexdigest()
        
        def __str__(self):
            return str(self.__dict__)
     //Blockchain schema
    classBlockChain:
        //Create the genesis block during initialization
        def __init__(self):
            self.chain = [self.createGenesisBlock()]
        //Build the genesis block
        def createGenesisBlock(self):
            return Block(0,"01/01/2018","genesis block","0")
        //Get the last block
        def getLatestBlock(self):
            return self.chain[len(self.chain)-1]
        //Add blocks to the blockchain
        def addBlock(self,newBlock):
            newBlock.previousHash = self.getLatestBlock().hash
            newBlock.hash = newBlock.calculateHash()
            self.chain.append(newBlock)
        
        def __str__(self):
            return str(self.__dict__)
        //Verify whether the blockchain is valid and has not been tampered with.
        def chainIsValid(self):
            for index in range(1,len(self.chain)):
                currentBlock = self.chain[index]
                previousBlock = self.chain[index-1]
                if (currentBlock.hash != currentBlock.calculateHash()):
                    return False
                if previousBlock.hash != currentBlock.previousHash:
                    return False
            return True


myCoin = BlockChain()
myCoin.addBlock(Block(1,"02/01/2018","{amount:4}"))
myCoin.addBlock(Block(2,"03/01/2018","{amount:5}"))

#print block info Print blockchain information
print("print block info ####:")
for block in myCoin.chain:
    print(block)
#check blockchain is valid Check whether the blockchain is valid
print("before tamper block,blockchain is valid ###")
print(myCoin.chainIsValid())
#tamper the blockinfo Tamper with the data of block 2
myCoin.chain[1].data = "{amount:1002}"
print("after tamper block,blockchain is valid ###")
print(myCoin.chainIsValid())


Output results

?

print block info ####:
{<!-- -->'index': 0, 'timestamp': '01/01/2018', 'data': 'genesis block', 'previousHash' : '0', 'hash': 'd8d21e5ba33780d5eb77d09d3b407ceb8ade4e5545ef951de1997b209d91e264'}
{<!-- -->'index': 1, 'timestamp': '02/01/2018', 'data': '{amount:4}', ' previousHash': 'd8d21e5ba33780d5eb77d09d3b407ceb8ade4e5545ef951de1997b209d91e264', 'hash': '15426e32db30f4b26aa719ba5e573f372f41e27e4728eb9 e9ab0bea8eae63a9d'}
{<!-- -->'index': 2, 'timestamp': '03/01/2018', 'data': '{amount:5}', ' previousHash': '15426e32db30f4b26aa719ba5e573f372f41e27e4728eb9e9ab0bea8eae63a9d', 'hash': '75119e897f21c769acee6e32abcefc5e88e250a1f35cc959 46379436050ac2f0'}
before tamper block,blockchain is valid ###
True
after tamper block,blockchain is valid ###
False

1.4 The core of blockchain – workload proof algorithm

The senior introduced the basic structure of the blockchain above. Based on the previous work, I will briefly implement the proof of work algorithm (proof of work).
work), before introducing POW, first think about why we need a proof-of-work algorithm, or go one step further and think about why Bitcoin solves the problem of trust?

1.4.1 The Byzantine Generals Problem

Before the emergence of Bitcoin, there was the Byzantine Generals Problem. The main idea is, how to trust the information sent to you by others in a distributed system environment?

A group of Byzantine generals, each leading an army, jointly besieged a city. In order to simplify the problem, the action strategies of each army are limited to two types: attack or withdrawal. Because attacking with some armies while withdrawing could have disastrous consequences, the generals must vote to agree on a strategy of either all armies attacking together or all armies withdrawing together. Because the generals are located in different directions in the city, they
The problem with the system is that there can be traitors among the generals who not only vote for worse strategies but also selectively send voting information. Suppose there are 9 generals voting and 1 of them is a traitor. Among the eight loyal generals, four voted for attack and four voted for evacuation. At this time, the traitor may deliberately send letters to the four generals who voted to attack to express their vote to attack, and to the four generals who voted to evacuate to express their vote to evacuate. In this way, from the perspective of the four generals who voted for attack, the voting result was that five people voted for attack and thus launched an attack; while from the perspective of the four generals who voted for retreat, the result was that five people voted for retreat. In this way, the unified coordination of the various armies was destroyed.

1.4.2 Solution

The main problem of the Byzantine Generals Problem is that the middleman can intercept the message and modify it; the above-mentioned soldiers can be understood as some nodes in Bitcoin. Not all nodes can directly process the message after getting it. Let’s solve a mathematical problem first. It is proof of work. Only after having specific computing power to solve the problem can it be modified or verified (verified, packaged, and uploaded to the chain).


The above picture is a simple workload proof algorithm process. There is an x after a string of numbers. The number before x can be understood as transaction data. Then you need to find an If it is very uniform, then it is equally possible for each bit of the generated hash value to be 0 or 1, so the probability that the first n bits are all 0 is 2 to the nth power/2 number of hash value digits, as shown in the figure above All possibilities if the hash value is 5 bits

1.4.3 Code Implementation

?


    from hashlib import sha256
    import time
    class Block:
         
        def __init__(self,index,timestamp,data,previousHash=""):
            
            self.index = index
            self.timestamp = timestamp
            self.data = data
            self.previousHash = previousHash
            self.nonce = 0 //Represents how many hash calculations have been calculated currently
            self.hash = self.calculateHash()


        def calculateHash(self):
            plainData = str(self.index) + str(self.timestamp) + str(self.data) + str(self.nonce)
            return sha256(plainData.encode('utf-8')).hexdigest()
        #Mining difficulty represents complexity, which means success will only be considered if the first difficulty bit is 0.
        def minerBlock(self,difficulty):
            while(self.hash[0:difficulty]!=str(0).zfill(difficulty)):
                self.nonce + =1
                self.hash = self.calculateHash()
        
        def __str__(self):
            return str(self.__dict__)


    classBlockChain:
        
        def __init__(self):
            self.chain = [self.createGenesisBlock()]
            self.difficulty = 5
    
        def createGenesisBlock(self):
            return Block(0,"01/01/2018","genesis block")
        
        def getLatestBlock(self):
            return self.chain[len(self.chain)-1]
        #Before adding a block, you need to do a calculation question, and then you can add the block to the chain.
        def addBlock(self,newBlock):
            newBlock.previousHash = self.getLatestBlock().hash
            newBlock.minerBlock(self.difficulty)
            self.chain.append(newBlock)

        def __str__(self):
            return str(self.__dict__)
        
        def chainIsValid(self):
            for index in range(1,len(self.chain)):
                currentBlock = self.chain[index]
                previousBlock = self.chain[index-1]
                if (currentBlock.hash != currentBlock.calculateHash()):
                    return False
                if previousBlock.hash != currentBlock.previousHash:
                    return False
            return True
           

    myCoin = BlockChain()
    
    # The time required for each block mining is printed below. Bitcoin controls a block to be produced in 10 minutes through a certain mechanism.
    # In fact, it is to adjust the difficulty value above based on the current network computing power. If you are
    # Locally adjust the difficulty value of the above code to a large value. You will see that the calculation result will not be produced for a long time.
    startMinerFirstBlockTime = time.time()
    print("start to miner first block time:" + str(startMinerFirstBlockTime))
    
    myCoin.addBlock(Block(1,"02/01/2018","{amount:4}"))
    
    print("miner first block time completed" + ",used " + str(time.time()-startMinerFirstBlockTime) + "s")
    
    startMinerSecondBlockTime = time.time()
    
    print("start to miner first block time:" + str(startMinerSecondBlockTime))
    
    myCoin.addBlock(Block(2,"03/01/2018","{amount:5}"))
    
    print("miner second block time completed" + ",used " + str(time.time()-startMinerSecondBlockTime) + "s\\
")
    
    #print block info
    print("print block info ####:\\
")
    for block in myCoin.chain:
        print("\\
")
        print(block)
        
    #check blockchain is valid
    print("before tamper block,blockchain is valid ###")
    print(myCoin.chainIsValid())
    
    #tamper the blockinfo
    myCoin.chain[1].data = "{amount:1002}"
    print("after tamper block,blockchain is valid ###")
    print(myCoin.chainIsValid())


output

2 Quickly implement a blockchain

2.1 What is blockchain

A blockchain is an immutable, ordered chain of records called blocks. They can contain transactions, files, or any data you like, but most importantly, they are linked together with hashes.

2.2 What does a complete fast contain

An index, a timestamp, a transaction list, a check, and a fast hash linked list

2.3 What is mining

Mining is actually very simple, just do the following three things:

1. Calculate proof of work PoW
2. Give the miner (self) a coin by adding a new transaction.
3. Construct a new block and add it to the chain

2.4 Proof-of-work algorithm:

Using this algorithm to prove how new blocks are created and mined on top of blocks, the goal of POW is to calculate a number that meets certain conditions. This number must be computationally very difficult for everyone to verify, but easy to verify. This is the core idea behind proof of work that the computational difficulty is directly proportional to the specific string that the target string needs to satisfy.

2.5 Implementation code

?


    import hashlib
    import json
    import requests
    from textwrap import dedent
    from time import time
    from uuid import uuid4
    from urllib.parse import urlparse
    from flask import Flask, jsonify, request
    
    class Blockchain(object):
        def __init__(self):
            ...
            self.nodes = set()
            # Use set to store nodes to avoid adding nodes repeatedly.
            ...
            self.chain = []
            self.current_transactions = []
    
            #Create genesis block
            self.new_block(previous_hash=1,proof=100)
    
        def reister_node(self,address):
            """
            Add a new node to the node list
            :param address:
            :return:
            """
            prsed_url = urlparse(address)
            self.nodes.add(prsed_url.netloc)
    
        def valid_chain(self,chain):
            """
            Determine whether a given blockchain is valid
            :param chain:
            :return:
            """
            last_block = chain[0]
            current_index = 1
    
            while current_index<len(chain):
                block = chain[current_index]
                print(f'{<!-- -->last_block}')
                print(f'{<!-- -->block}')
                print("\\
______\\
")
                # Check whether the hash of the block is correct
                if block['previous_hash'] != self.hash(last_block):
                    return False
                # Check if 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 ressolve_conflicts(self):
            """
            Consensus Algorithm
            :return:
            """
            neighbors = self.nodes
            new_chain = None
            # Find the longest chain
            max_length = len(self.chain)
    
            # Get and verify the chains of all nodes in the network
            for node in neighbors:
                response = requests.get(f'http://{<!-- -->node}/chain')
    
                if response.status_code == 200:
                    length = response.json()['length']
                    chain = response.json()['chain']
    
                    # Check whether the length is long and whether the chain is valid
                    if length > max_length and self.valid_chain(chain):
                        max_length = length
                        new_chain = chain
    
            # If a new valid chain is found to be longer than the current one, replace the current chain
            if new_chain:
                self.chain = new_chain
                return True
            return False
    
        def new_block(self,proof,previous_hash=None):
            """
            Create a new block and add it to the chain
            :param proof: Proof generated by proof-of-work algorithm
            :param previous_hash: hash value of the previous block
            :return: 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]),
            }
    
            #Reset current transaction records
            self.current_transactions = []
    
            self.chain.append(block)
            return block
    
        def new_transaction(self,sender,recipient,amount):
            #Add new transaction to transaction list
            """
            Creates a new transaction to go into the next mined Block
            :param sender: sender’s address
            :param recipient:recipient address
            :param amount: quantity
            :return:The index of the block that holds the transaction
            """
            self.current_transactions.append({<!-- -->
                'sender':sender,
                'recipient':recipient,
                'amount':amount,
            })
    
            return self.last_block['index'] + 1

        @staticmethod
        def hash(block):
            """
            Generate SHA-256 value for a block
            :param block:
            :return:
            """
            # You must ensure that this dictionary (block) is sorted, otherwise you will get inconsistent hashes
            block_string = json.dumps(block,sort_keys=True).encode()
            return hashlib.sha256(block_string).hexdigest()
    
        @property
        def last_block(self):
            # Return the last block in the chain
            return self.chain[-1]

        def proof_of_work(self,last_proof):
            # Simple proof of working algorithm
            proof=0
            while self.valid_proof(last_proof,proof)is False:
                proof + =1
            return proof
    
        @staticmethod
        def valid_proof(last_proof,proof):
            # Verify the proof
            guess = f'{<!-- -->last_proof}{<!-- -->proof}'.encode()
            guess_hash = hashlib.sha256(guess).hexdigest()
            return guess_hash[:4] =="0000"


    # Instantiate node
    app = Flask(__name__)
    
    # Generate a globally unique address for the node
    node_identifier = str(uuid4()).replace('-','')
    
    # Instantiate the Blockchain class
    blockchain = Blockchain()
    
    # Make a mining request
    @app.route('/mine',methods=['GET'])
    def mine():
        # Run the proof of the working algorithm to get the next proof.
        last_block = blockchain.last_block
        last_proof = last_block['proof']
        proof = blockchain.proof_of_work(last_proof)
    
        # There must be a reward for finding the evidence.
        blockchain.new_transaction(
            sender="0",
            recipient=node_identifier,
            amount=1,
        )
    
        # Build a 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
    
    #Create transaction request
    @app.route('/transactions/new',methods=['POST'])
    def new_transactions():
        values = request.get_json()
    
        # Check whether the required fields are located in the POST data
        required = ['seder','recipient','amount']
        if not all(k in values for k in request):
            return 'Missing values',400
    
        #Create a new thing
        index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
        response = {<!-- -->'message': f'Transaction will be added to Block {<!-- -->index}'}
        return jsonify(response), 201
    
    # Get all quick information
    @app.route('/chain',methods=['GET'])
    def full_chain():
        response = {<!-- -->
            'chain':blockchain.chain,
            'length':len(blockchain.chain),
        }
        return jsonify(response),200
    
    #Add node
    @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
    
    # Resolve conflicts
    @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
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0',port=5000)

After the code is ready, start your project and open Postman to complete the following operations

The senior conducts mining by requesting http://localhost:5000/mine

3 Finally

More information, project sharing:

https://gitee.com/dancheng-senior/postgraduate