0 前言
🔥 优质角逐项目系列,本日要分享的是
python区块链实现 - proof of work工作量证明共识算法
该项目较为新奇,适相助为角逐课题方向,学长非常保举!
🧿 更多资料, 项目分享:
https://gitee.com/dancheng-senior/postgraduate
1 区块链底子
学长以比特币的结构向各人详解区块链的构成部门
1.1 比特币内部结构
- previous hash(前一个区块的hash)
- merkle root(默克尔树根节点,内部存储买卖业务数据)
- timestamp(当前区块天生的时间)
- nonce(旷工盘算hash值次数)
1.2 实现的区块链数据结构
- index 当前第几个区块
- timestamp 该区块创建时的时间戳
- data 买卖业务信息
- previousHash 前一个区块的hash
- hash 当前区块的hash
1.3 注意点
第一个区块叫做创世区块(genesis block),区块链创建的时间默认生产的这里用的是单纯的链表,不是用默克尔树存储
示例代码
- from hashlib import sha256
- //区块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()
-
- //计算当前区块的hash值
- 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__)
- //区块链schema
- class BlockChain:
- //初始化的时候 创建 创世区块
- def __init__(self):
- self.chain = [self.createGenesisBlock()]
- //构建创世区块
- def createGenesisBlock(self):
- return Block(0,"01/01/2018","genesis block","0")
- //获取最后一个区块
- def getLatestBlock(self):
- return self.chain[len(self.chain)-1]
- //往区块链里面添加区块
- def addBlock(self,newBlock):
- newBlock.previousHash = self.getLatestBlock().hash
- newBlock.hash = newBlock.calculateHash()
- 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()
- myCoin.addBlock(Block(1,"02/01/2018","{amount:4}"))
- myCoin.addBlock(Block(2,"03/01/2018","{amount:5}"))
- #print block info 打印区块链信息
- print("print block info ####:")
- for block in myCoin.chain:
- print(block)
- #check blockchain is valid 检查区块链是不是有效的
- print("before tamper block,blockchain is valid ###")
- print(myCoin.chainIsValid())
- #tamper the blockinfo 篡改区块2的数据
- myCoin.chain[1].data = "{amount:1002}"
- print("after tamper block,blockchain is valid ###")
- print(myCoin.chainIsValid())
复制代码 输出效果
- 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 区块链的焦点-工作量证明算法
上面学长先容了区块链的根本结构,我在之前的底子上来简朴实现一下工作量证明算法(proof of
work),在先容pow之前先思索一下为什么要工作量证明算法,大概再往前想一步为什么比特币怎样办理信托的题目?
1.4.1 拜占庭将军题目
比特币出现之前就有了拜占庭将军题目,重要头脑是,如安在分布式体系环境里去信赖其他人发给你的信息?
一组拜占庭将军分别各向导一支部队共同围困一座都会。为了简化题目,将各支部队的举措计谋限定为打击或撤离两种。由于部门部队打击部门部队撤离大概会造成灾难性结果,因此各位将军必须通过投票来告竣同等计谋,即全部部队一起打击或全部部队一起撤离。由于各位将军分处都会差别方向,他
体系的题目在于,将军中大概出现叛徒,他们不光大概向较为糟糕的计谋投票,还大概选择性地发送投票信息。假设有9位将军投票,此中1名叛徒。8名忠诚的将军中出现了4人投打击,4人投撤离的环境。这时间叛徒大概故意给4名投打击的将领送信表现投票打击,而给4名投撤离的将领送信表现投撤离。如许一来在4名投打击的将领看来,投票效果是5人投打击,从而发起打击;而在4名投撤离的将军看来则是5人投撤离。如许各支部队的同等协同就遭到了粉碎。
1.4.2 办理办法
拜占庭将军题目重要题目是,中央人可以拦截消息,举行修改;上述的那些士兵可以明确成比特币中的一些节点,不是全部节点拿到消息后都是可以直接处理惩罚的,先去办理一个数学题目,就是工作量证明,只有拥有特定的盘算本领办理了题目之后才气去修改大概校验(验证,打包,上链)。
上图就是简朴的工作量证明算法流程,一串数字反面有个x,x之前的数可以明确成买卖业务数据,然后必要找到一个x,让整个数的hash值的开头有n个0,如果hash是很匀称的话,那么天生的hash值每一位为0大概1都是等大概的,以是前n个都为0的概率就是2的n次方/2的hash值位数,上图给出了如果hash值是5个bit的环境下的全部大概
1.4.3 代码实现
- 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 //代表当前计算了多少次hash计算
- 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()
- #挖矿 difficulty代表复杂度 表示前difficulty位都为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__)
- 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]
- #添加区块前需要 做一道计算题😶,坐完后才能把区块加入到链上
- 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()
-
- # 下面打印了每个区块挖掘需要的时间 比特币通过一定的机制控制在10分钟出一个块
- # 其实就是根据当前网络算力 调整我们上面difficulty值的大小,如果你在
- # 本地把上面代码difficulty的值调很大你可以看到很久都不会出计算结果
- 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())
复制代码 输出

2 快速实现一个区块链
2.1 什么是区块链
区块链是一个不可变得,有序的被称之为块的纪录链,它们可以包罗买卖业务、文件大概任何你喜欢的数据,但最告急的是,它们用hash毗连在一起。
2.2 一个完备的快包罗什么
一个索引,一个时间戳,一个事物列表,一个校验, 一个前快的散链表
2.3 什么是挖矿
挖矿着实非常简朴就做了以下三件事:
1、盘算工作量证明poW
2、通过新增一个买卖业务赋予矿工(自已)一个币
3、构造新区块并将其添加到链中
2.4 工作量证明算法:
使用该算法来证明是如安在区块上创建和发掘新的区块,pow的目的是盘算出一个符合特定条件的数字,这个数字对于全部人而言必须在盘算上非常困难,但易于验证,这就是工作证明背后的焦颔首脑盘算难度与目的字符串必要满意的特定字符串成正比。
2.5 实当代码
- 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()
- # 用 set 来储存节点,避免重复添加节点.
- ...
- self.chain = []
- self.current_transactions = []
-
- #创建创世区块
- self.new_block(previous_hash=1,proof=100)
-
- def reister_node(self,address):
- """
- 在节点列表中添加一个新节点
- :param address:
- :return:
- """
- prsed_url = urlparse(address)
- self.nodes.add(prsed_url.netloc)
-
- def valid_chain(self,chain):
- """
- 确定一个给定的区块链是否有效
- :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")
- # 检查block的散列是否正确
- if block['previous_hash'] != self.hash(last_block):
- return False
- # 检查工作证明是否正确
- if not self.valid_proof(last_block['proof'], block['proof']):
- return False
-
- last_block = block
- current_index += 1
- return True
- def ressolve_conflicts(self):
- """
- 共识算法
- :return:
- """
- neighbours = self.nodes
- new_chain = None
- # 寻找最长链条
- max_length = len(self.chain)
-
- # 获取并验证网络中的所有节点的链
- for node in neighbours:
- response = requests.get(f'http://{node}/chain')
-
- if response.status_code == 200:
- length = response.json()['length']
- chain = response.json()['chain']
-
- # 检查长度是否长,链是否有效
- if length > max_length and self.valid_chain(chain):
- max_length = length
- new_chain = chain
-
- # 如果发现一个新的有效链比当前的长,就替换当前的链
- if new_chain:
- self.chain = new_chain
- return True
- return False
-
- def new_block(self,proof,previous_hash=None):
- """
- 创建一个新的块并将其添加到链中
- :param proof: 由工作证明算法生成证明
- :param previous_hash: 前一个区块的hash值
- :return: 新区块
- """
- 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):
- # 将新事务添加到事务列表中
- """
- Creates a new transaction to go into the next mined Block
- :param sender:发送方的地址
- :param recipient:收信人地址
- :param amount:数量
- :return:保存该事务的块的索引
- """
- self.current_transactions.append({
- 'sender':sender,
- 'recipient':recipient,
- 'amount':amount,
- })
-
- return self.last_block['index'] + 1
- @staticmethod
- def hash(block):
- """
- 给一个区块生成 SHA-256 值
- :param block:
- :return:
- """
- # 必须确保这个字典(区块)是经过排序的,否则将会得到不一致的散列
- block_string = json.dumps(block,sort_keys=True).encode()
- return hashlib.sha256(block_string).hexdigest()
-
- @property
- 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 = f'{last_proof}{proof}'.encode()
- guess_hash = hashlib.sha256(guess).hexdigest()
- return guess_hash[:4] =="0000"
- # 实例化节点
- app = Flask(__name__)
-
- # 为该节点生成一个全局惟一的地址
- node_identifier = str(uuid4()).replace('-','')
-
- # 实例化Blockchain类
- blockchain = Blockchain()
-
- # 进行挖矿请求
- @app.route('/mine',methods=['GET'])
- def mine():
- # 运行工作算法的证明来获得下一个证明。
- last_block = blockchain.last_block
- last_proof = last_block['proof']
- proof = blockchain.proof_of_work(last_proof)
-
- # 必须得到一份寻找证据的奖赏。
- blockchain.new_transaction(
- sender="0",
- recipient=node_identifier,
- amount=1,
- )
-
- # 通过将其添加到链中来构建新的块
- 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
-
- # 创建交易请求
- @app.route('/transactions/new',methods=['POST'])
- def new_transactions():
- values = request.get_json()
-
- # 检查所需要的字段是否位于POST的data中
- required = ['seder','recipient','amount']
- if not all(k in values for k in request):
- return 'Missing values',400
-
- #创建一个新的事物
- index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
- response = {'message': f'Transaction will be added to Block {index}'}
- return jsonify(response), 201
-
- # 获取所有快信息
- @app.route('/chain',methods=['GET'])
- def full_chain():
- response = {
- 'chain':blockchain.chain,
- 'length':len(blockchain.chain),
- }
- return jsonify(response),200
-
- # 添加节点
- @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
-
- if __name__ == '__main__':
- app.run(host='0.0.0.0',port=5000)
复制代码 代码弄好启动你的项目以后打开Postman 完成以下操纵
学长通过哀求 http://localhost:5000/mine举行采矿
3 末了
🧿 更多资料, 项目分享:
https://gitee.com/dancheng-senior/postgraduate
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |