程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

[design tutorial] Python blockchain implementation - proof of work workload proof consensus algorithm

編輯:Python

List of articles

  • 0 Preface
  • 1 Blockchain Foundation
    • 1.1 Bitcoin internal structure
    • 1.2 Implemented blockchain data structure
    • 1.3 Be careful
    • 1.4 The core of blockchain - Workload proof algorithm
      • 1.4.1 General Byzantine question
      • 1.4.2 terms of settlement
      • 1.4.3 Code implementation
  • 2 Quickly implement a blockchain
    • 2.1 What is a blockchain
    • 2.2 What does a complete fast contain
    • 2.3 What is mining
    • 2.4 Workload proof algorithm :
    • 2.5 Implementation code
  • 3 Last

0 Preface

Hi, Hello everyone , This is senior Dan Cheng , Today, I'd like to introduce to the students how to build a blockchain system , The principle of blockchain

🧿 Topic selection guidance , Project sharing :

https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md

1 Blockchain Foundation

Senior students explain the components of blockchain in detail with the structure of bitcoin

1.1 Bitcoin internal structure

  • previous hash( Of the previous block hash)
  • merkle root( Merkel tree root node , Store transaction data internally )
  • timestamp( The time when the current block was generated )
  • nonce( Absenteeism calculation hash Value times )

1.2 Implemented blockchain data structure

  • index The current number of blocks
  • timestamp The timestamp when the block was created
  • data Transaction information
  • previousHash Of the previous block hash
  • hash Of the current block hash

1.3 Be careful

The first block is called the genesis block (genesis block), When the blockchain is created, it is produced by default. Here, a simple linked list is used , Not with the Merkel tree

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 of the current block hash value
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
class BlockChain:
// When initializing establish Genesis block
def __init__(self):
self.chain = [self.createGenesisBlock()]
// Build 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 Has anyone 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 effective 
print("before tamper block,blockchain is valid ###")
print(myCoin.chainIsValid())
#tamper the blockinfo Tampering with blocks 2 The data of 
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': '15426e32db30f4b26aa719ba5e573f372f41e27e4728eb9e9ab0bea8eae63a9d'}
{
'index': 2, 'timestamp': '03/01/2018', 'data': '{amount:5}', 'previousHash': '15426e32db30f4b26aa719ba5e573f372f41e27e4728eb9e9ab0bea8eae63a9d', 'hash': '75119e897f21c769acee6e32abcefc5e88e250a1f35cc95946379436050ac2f0'}
before tamper block,blockchain is valid ###
True
after tamper block,blockchain is valid ###
False

1.4 The core of blockchain - Workload proof algorithm

The above senior introduced the basic structure of blockchain , I'll simply implement the workload proof algorithm based on the previous work (proof of work), Introducing pow Let's think about why we need the workload proof algorithm , Or think about why bitcoin solves the problem of trust ?

1.4.1 General Byzantine question

Before bitcoin, there was the Byzantine general 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 led an army to besiege a city . To simplify the problem , The operational strategy of each army is limited to attack or withdrawal . Because part of the army attacking and part of the army evacuating may have disastrous consequences , So the generals have to vote to agree on a strategy , That is, all the troops attack together or all the troops withdraw together . Because the generals are in different directions in different cities , They can only communicate with each other by courier . During the voting process, each general will inform all other generals of the information about whether to vote for the attack or retreat by courier , In this way, each general can determine the action strategy according to his vote and the information sent by all other generals
The problem with the system is , There may be traitors among the generals , Not only are they likely to vote for a worse strategy , It is also possible to selectively send voting information . Suppose there is 9 The generals voted , among 1 A traitor .8 Among the loyal generals 4 People attack ,4 The evacuation of people . At this time, the traitor may deliberately give 4 The general who voted to attack sent a letter to show that he voted to attack , And give 4 The general who withdrew from mingtou sent a letter to indicate that he was withdrawing . In this way 4 It seems that the leader of the famous attack team , The vote was 5 People attack , To attack ; And in the 4 The generals who voted to evacuate seemed to be 5 Human evacuation . In this way, the unity and coordination of the various armies will be destroyed .

1.4.2 terms of settlement

The main problem with Byzantine Generals is , Middlemen can intercept messages , Make changes ; The soldiers mentioned above can be understood as some nodes in bitcoin , Not all nodes can process messages directly after they get them , Let's solve a math problem first , It's proof of work , Only when you have specific computing power and solve the problem can you modify or verify ( verification , pack , Upper chain ).


The above figure is a simple workload proof algorithm flow , A string of numbers is followed by x,x The previous number can be understood as transaction data , Then you need to find one x, Let the whole number hash The value starts with n individual 0, If hash If it's very uniform , So the generated hash Each bit of the value is 0 perhaps 1 It's all possible , So before n All for 0 The probability of 2 Of n Power /2 Of hash Number of values , The figure above shows if hash The value is 5 individual bit All possibilities in the case of

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 calculations have been made hash Calculation
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()
# dig difficulty Represents complexity Before presentation difficulty All for 0 Success is considered. 
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__)
class BlockChain:
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 blocks, you need Do a calculation problem , After sitting down, 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 is controlled in... Through a certain mechanism 10 Every minute comes a block 
# In fact, according to the current network computing power Adjust our top difficulty Value size , If you are in the 
# Put the above code locally difficulty You can see that the calculation results will not come out 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\n")
#print block info
print("print block info ####:\n")
for block in myCoin.chain:
print("\n")
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 a blockchain

A blockchain is an inexorable , An ordered chain of records called blocks , They can contain transactions 、 Files or any data you like , But most of all , They use hash come together .

2.2 What does a complete fast contain

An index , A timestamp , A list of things , A check , A fast scattered list

2.3 What is mining

Mining is actually very simple. It does the following three things :

1、 Proof of calculation work poW
2、 Give miners... By adding a new transaction ( Oneself ) One coin
3、 Construct new blocks and add them to the chain

2.4 Workload proof algorithm :

This algorithm is used to prove how to create and mine new blocks on blocks ,pow The goal is to calculate a number that meets certain conditions , This figure must be very difficult to calculate for everyone , But easy to verify , This is the core idea behind the work proof. The calculation difficulty is directly proportional to the specific string that the target string needs to meet .

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 , Avoid adding nodes repeatedly .
...
self.chain = []
self.current_transactions = []
# Create the 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("\n______\n")
# Check block Whether the hash of is correct 
if block['previous_hash'] != self.hash(last_block):
return False
# Check whether the work certificate 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: """
neighbours = self.nodes
new_chain = None
# Look for the longest chain 
max_length = len(self.chain)
# Obtain and verify the chain of all nodes in the 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 the length , Is the chain effective 
if length > max_length and self.valid_chain(chain):
max_length = length
new_chain = chain
# If a new effective chain is found to be longer than the current one , Just 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: The proof is generated by the working proof algorithm :param previous_hash: Of the previous block hash value :return: New area 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 
self.current_transactions = []
self.chain.append(block)
return block
def new_transaction(self,sender,recipient,amount):
# Add a new transaction to the transaction list 
""" Creates a new transaction to go into the next mined Block :param sender: The address of the sender :param recipient: The address of the addressee :param amount: Number :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... For a block SHA-256 value :param block: :return: """
# It must be ensured that this dictionary ( block ) It's 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):
# Returns the last block in the chain 
return self.chain[-1]
def proof_of_work(self,last_proof):
# A simple proof of the 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 that 
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('-','')
# Instantiation 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)
# You must get a reward for finding evidence .
blockchain.new_transaction(
sender="0",
recipient=node_identifier,
amount=1,
)
# Build new blocks by adding them 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 that the required fields are in POST Of data in 
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 the quick information 
@app.route('/chain',methods=['GET'])
def full_chain():
response = {

'chain':blockchain.chain,
'length':len(blockchain.chain),
}
return jsonify(response),200
# Add a 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)

When the code is ready, start your project and open Postman Do the following

The senior student passed the request http://localhost:5000/mine Mining

🧿 Topic selection guidance , Project sharing :

https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md

3 Last


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved