用Python创建简单区块链
环境: Flask 0.12.2 request2.18.4 原文链接:https://xiaozhuanlan.com/blockchaincore?rel=5100037573 Github:https://github.com/xilibi2003/blockchain
可能出现的问题: AttributeError: ‘Request’ object has no attribute ‘is_xhr’ 解决方法: 降低Werkzeug版本为0.16版本如下: pip uninstall Werkzeug pip install Werkzeug==0.16.1 链接: Flask AttributeError ‘Request’ object has no attribute ‘is_xhr’ 报错的解决办法 源代码:
import hashlib
import json
import requests
from time import time
from textwrap import dedent
from typing import Any, Dict, List, Optional
from urllib.parse import urlparse
from uuid import uuid4
from flask import Flask, jsonify, request
class Blockchain(object):
def __init__(self):
self.current_transactions = []
self.chain = []
self.nodes = set()
self.new_block(previous_hash=1, proof=100)
def register_node(self, address):
"""
Add a new node to the list of nodes
:param address: <str> Address of node. Eg. 'http://192.168.0.5:5000'
:return: None
"""
parsed_url = urlparse(address)
self.nodes.add(parsed_url.netloc)
def valid_chain(self, chain):
"""
Determine if a given blockchain is valid
:param chain: <list> A blockchain
:return: <bool> True if valid, False if not
"""
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")
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 resolve_conflicts(self):
"""
共识算法解决冲突
使用网络中最长的链,
:return: <bool> True 如果链被取代,否则为False
"""
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: <int> The proof given by the Proof of Work algorithm
:param previous_hash: (Optional)<str> Hash of previous Block
:return: <dict> 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]),
}
self.current_transactions = []
self.chain.append(block)
return block
def new_transaction(self, sender, recipient, amount):
"""
生成新交易信息,信息将加入到下一个待挖的区块中
:param sender: <str> Address of the Sender
:param recipient: <str> Address of the Recipient
:param amount: <int> Amount
:return: <int> The index of the Block that will hold this transaction
"""
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount,
})
return self.last_block['index'] + 1
@property
def last_block(self):
return self.chain[-1]
@staticmethod
def hash(block):
"""
生成块的 SHA-256 hash值
:param block: <dict> Block
:return: <str>
"""
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
def proof_of_work(self, last_proof):
"""
简单的工作量证明:
- 查找一个 p' 是的 hash(pp') 以4个0开头
- p 是上一个块的证明, p' 是当前的证明
:param last_proof: <int>
:return: <int>
"""
proof = 0
while self.valid_proof(last_proof, proof) is False:
proof += 1
return proof
@staticmethod
def valid_proof(last_proof, proof):
"""
验证证明: 是否hash(last_proof, proof)以4个0开头?
:param last_proof: <int> Previous Proof
:param proof: <int> Current Proof
:return: <bool> True if correct,False if not
"""
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()
@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,
)
block = blockchain.new_block(proof)
response = {
'message': "New Block Forged",
'index': block['index'],
'transaction': block['transactions'],
'proof': block['proof'],
'previous_hash': block['previous_hash'],
}
return jsonify(response), 200
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
values = request.get_json()
required = ['sender', 'recipient', 'amount']
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'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 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 is authoritaat'
}
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000)
|