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