1.前言
比特币和区块链技术是现在非常火的技术,但是我们对其的原理并没有深究,今天我们通过 python 代码来实现一个简单的区块链,通过代码来了解区块链技术
需要的工具和环境:
工具:
1. pycharm
2. postman
环境:
1. python3
2. flask
3. requests
环境和工具的安装和搭建不做赘述,不懂的可以自己 google 或者百度,下面我们进入正题
2.搭建区块链
2.1 初始化类和需要的属性
我们新建一个类并初始化区块链需要的数据类型, 一个 chain 数组来存放整个链,一个 transactions 来记录没有防区区块链中的交易,一个 nodes 来存放整个网络中的节点,初始化区块 new_block(创世块)
class Blockchain:
def __init__(self):
self.chain = []
self.current_transactions = []
self.nodes = set()
self.new_block(proof=100, previous_hash=1)
2.2 创建一个新的块
新建一个区块并存放到 chain 数组里面,每一个块里面要有基本的数据
def new_block(self, proof, previous_hash=None):
block = {
'index': len(self.chain) + 1, //block的坐标
'timestamp': time(), //时间戳
'transactions': self.current_transactions, //当前的所有交易
'proof': proof, //工作量证明
'previous_hash': previous_hash or self.hash(self.last_block) //上一个数据的hash值
}
self.current_transactions = []
self.chain.append(block)
return block
2.3 新的交易记录
将新的交易记录到 current_transactions 中
def new_transaction(self, sender, recipient, amount):
self.current_transactions.append(
{
'sender': sender,
'recipent': recipient,
'amount': amount
}
)
return self.last_block['index'] + 1
@property
def last_block(self):
return self.chain[-1]
2.4 挖矿
挖矿是很关键的部分,他将当前所有的交易打包生成一个新的 block,加入区块链中,挖矿需要工作量证明,一般通过不断将上一个工作量证明加上一个自增的常数不断 hash 直到前几位等于规定的数字的时候才可以获取挖矿的权限,这需要大量的计算
def proof_of_work(self, last_proof: int) -> int:
proof = 0
while self.valid_proof(last_proof, proof) is False:
proof += 1
# print(proof)
return proof
def valid_proof(self, last_proof: int, proof: int) -> bool:
guess = f'{last_proof}{proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
# print(guess_hash)
# sleep(1)
return guess_hash[0:4] == "0000"
2.5 区块分岔
有的时候区块会分岔,这时候我们就需要验证区块,选择最长的一个有效的链为主链
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
def resolve_conflicts(self) -> bool:
neighbours = self.nodes
max_length = len(self.chain)
new_chain = None
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):
new_chain = chain
if new_chain:
self.chain = new_chain
return True
return False
到此一个基本的区块链的核心就完毕了,我们下面加入 flask 通过 postman 来访问,来体验一下吧
运行区块链
加入 flask 之后完整的代码
import hashlib
import json
from argparse import ArgumentParser
from time import time
from urllib.parse import urlparse
from uuid import uuid4
import requests
from flask import Flask, app, jsonify, request
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):
parsed_url = urlparse(address)
self.nodes.add(parsed_url.netloc)
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
def resolve_conflicts(self) -> bool:
neighbours = self.nodes
max_length = len(self.chain)
new_chain = None
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):
new_chain = chain
if new_chain:
self.chain = new_chain
return True
return False
def new_block(self, proof, previous_hash=None):
block = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.current_transactions,
'proof': proof,
'previous_hash': previous_hash or self.hash(self.last_block)
}
self.current_transactions = []
self.chain.append(block)
return block
def new_transaction(self, sender, recipient, amount):
self.current_transactions.append(
{
'sender': sender,
'recipent': recipient,
'amount': amount
}
)
return self.last_block['index'] + 1
@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
# print(proof)
return proof
def valid_proof(self, last_proof: int, proof: int) -> bool:
guess = f'{last_proof}{proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
# print(guess_hash)
# sleep(1)
return guess_hash[0:4] == "0000"
# testPow = Blockchain()
# testPow.proof_of_work(100)
app = Flask(__name__)
blockchain = Blockchain()
node_indentifier = str(uuid4()).replace('-', '')
@app.route("/transactions/new", methods=['POST'])
def new_transcation():
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'Transacation will be added to Block {index}'}
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 "nodes not null",400
for node in nodes:
blockchain.register_node(node)
response = {
"message" : "node is add",
"nodes" : list(blockchain.nodes)
}
return jsonify(response),200
@app.route("/mine", methods=['GET'])
def mine():
last_block = blockchain.last_block
proof_work = blockchain.proof_of_work(last_block['proof'])
blockchain.new_transaction(
sender="0",
recipient=node_indentifier,
amount=1
)
block = blockchain.new_block(proof_work, None)
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("/chain", methods=['GET'])
def chain():
response = {
'chain': blockchain.chain,
'length': len(blockchain.chain)
}
return jsonify(response), 200
@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 chian is auth',
'new_chain': blockchain.chain
}
return jsonify(response),200
if __name__ == '__main__':
parser = ArgumentParser()
parser.add_argument('-p','--port',default=5000,type=int,help='port to listen to')
args = parser.parse_args()
port = args.port
app.run(host='0.0.0.0', port=port)
postman 调用对应的连接
初始的区块链
新添加一个交易
挖矿
查看区块链,新的交易已经被打包成了一个新的区块
我们启动一个新的节点,加入区块链,并查看他的区块链
节点注册
节点数据同步,之后再查看区块链
我们发现数据已经同步,区块链的基本原理到这里就结束了,有兴趣的可以进一步研究
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于