开发环境: win10+Anaconda+PyCharm
测试工具:Postman
代码如下:
import hashlib
import json
import time
import uuid
from flask import Flask, jsonify, request
from urllib.parse import urlparse
from argparse import ArgumentParser
class Blockchain:
def __init__(self):
self.chain = []
# 交易信息
self.current_transactions = []
self.nodes = set()
self.new_block(proof=100, previous_hash=1)
#节点注册
def register_node(self, address: str):
#http://127.0.0.1:5001
parsed_url = urlparse(address)
self.nodes.add(parsed_url.netloc)
#共识机制
def resolve_conflicts(self):
neighbours = self.nodes
max_length = len(self.chain)
new_chain = None
for node in neighbours:
response = request.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 valid_chain(self, chain)->bool:
last_block = chain[0]
current_index = 1
while current_index < len(chain):
block = chain[current_index]
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
pass
# 建立新的区块
def new_block(self, proof, previous_hash=None):
block = {
'index': len(self.chain) + 1,
'timestamp': time.time(),
'transaction': self.current_transactions,
'proof': proof,
'previous_hash': previous_hash or self.hash(self.last_block)
}
self.current_transactions = []
self.chain.append(block)
return block
# 建立新的交易,参数:1.发送者 2.接收者 3.金额
def new_transaction(self, sender, recipient, amount):
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount
})
return self.last_block['index'] + 1
# 计算区块hash值,静态方法
@staticmethod
def hash(block):
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: int) -> int:
proof = 0
while self.valid_proof(last_proof, proof) is False:
proof += 1
return proof
def valid_proof(self, last_proof: int, proof: int) -> bool:
guess = f'{last_proof}{proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
if guess_hash[0:4] == "0000":
return True
else:
return False
#创建一个 FLASK对象
app = Flask(__name__)
blockchain = Blockchain()
node_indentifier = str(uuid.uuid4()).replace("-", "")
#添加一个新的交易
@app.route('/transaction/new', methods=['POST'])
def new_transaction():
values = request.get_json()
required = ["sender", "recipient", "amount"]
if values is None:
return "MISSING VALUES", 400
#检查请求中是否已经带好了三个参数
if not all(k in values for k in required):
return "MISSING VALUES", 400
index = blockchain.new_transaction(values["sender"], values["recipient"], values["amount"])
response = {"message": f'Transcation will be add to Block{index}'}
return jsonify(response), 201
#挖矿/打包
@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_indentifier, amount=1)
block = blockchain.new_block(proof, None)
response = {
"message": "new block forged",
"index": block['index'],
"transaction": block["transaction"],
"proof": block["proof"],
"previous_hash": block["previous_hash"]
}
return jsonify(response), 200
#返回整个区块链信息
@app.route("/chain", methods=['GET'])
def full_chain():
response = {
"chain": blockchain.chain,
"length": len(blockchain.chain)
}
return jsonify(response), 200
#节点注册
@app.route("/node/register", methods=['post'])
def register_node():
values = request.get_json()
nodes = values.get("nodes")
if nodes is None:
return "Error :please supply a valid list of nodes"
for node in nodes:
blockchain.register_node(node)
response = {
"message": "new nodes have benn 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 was authoritative",
"new_chain": blockchain.chain
}
return jsonify(response), 200
if __name__ == '__main__':
parser = ArgumentParser()
#-p --port
parser.add_argument('-p', '--port', default=5000, type=int, help="port to listen to")
args = parser.parse_args()
port = args.port
app.run(host='127.0.0.1', port=5000)
部分测试用例
1.测试链接 :http://127.0.0.1:5000/transaction/new
{
"sender":"bysen",
"recipient":"lucy",
"amount":5
}
2.测试链接:http://127.0.0.1:5000/mine
{
"sender":"bysen",
"recipient":"lucy",
"amount":5
}
3.测试链接:http://127.0.0.1:5000/node/register
{
"nodes":["http://127.0.0.2:5001"]
}