主页 > 钱包imtoken官网 > 从零开始用Python搭建区块链网络+共识
从零开始用Python搭建区块链网络+共识
上一篇:用 Python 从零开始构建区块链网络+共识(上)
在上一篇文章中,我们做了节点同步的准备工作。 本次使用flask框架实现区块链的多节点,通过共识实现节点间的数据同步。
介绍区块链类
首先,新建一个Python文件ChaosCoinBlockNode,导入上一个区块链类ChaosCoinBlockChain。 在这里生成一个网络节点和节点钱包地址。
#节点的数据更新和网络公示
from uuid import uuid4 #签名
import requests #网络请求
from flask import Flask, jsonify, request #flask网络框架
from ChaorsCoinBlockChain import ChaorsCoinBlockChain
chaorsCoin = ChaorsCoinBlockChain() #创建一个网络节点
node_id = str(uuid4()).replace("-", "") #生成节点秘钥,即钱包地址
print("当前节点钱包地址:", node_id)
初始化烧瓶框架
之前基本了解了flask映射网页的基本使用。 这里的首页打印一条信息,表示网络运行正常。
app = Flask(__name__) #初始化flask框架
@app.route("/")
def index_page():
return "welcome to ChaorsCoin..."
查看区块链
新建一个网页查看当前节点区块链,构造一个response(其实是一个字典,因为网络上传输的数据格式是Json)显示区块链长度和所有区块
@app.route("/chain") #查看所有区块链
def index_chain():
response = {
"chain":chaorsCoin.chain, #区块链
"length":len(chaorsCoin.chain) #区块链长度
}
return jsonify(response), 200
矿业
新区块的快速挖掘依赖于之前的合法区块。 每当出块时,系统都会产生奖励。 每个区块的第一笔交易是系统奖励给矿工的交易,称为CoinBaseTransaction。
同样的,我们将我们要展示的区块信息封装成一个json字典展示在网页上。
@app.route("/mine") #挖矿
def index_mine():
last_block = chaorsCoin.last_block
proof = chaorsCoin.proof_of_work(last_block)
#系统奖励比特币
chaorsCoin.new_transaction(
sender="0", #0代表系统奖励,即coinBaseTransaction
recipient=node_id,
amount=12.5
)
block = chaorsCoin.new_block(proof, chaorsCoin.hash(last_block)) #新增区块
response = {
"message":"new block created...",
"index":block["index"],
"transactions":block["transactions"],
"proof":block["proof"],
"hash":chaorsCoin.hash(block),
"prev_hash":block["prev_hash"]
}
return jsonify(response), 200
每次调用该页面时,都会生成一个新的区块并添加到当前区块链中。 所以要注意,我们这里测试设置的工作量不要太难,否则生成新区块的时间会很长。
@staticmethod
def valid_proof(last_proof:int, proof:int)->bool: #验证工作量证明
guess = f'{last_proof}{proof}'.encode("utf-8")
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:3] == "000" #计算难度,这里的计算难度不要设置的太高。不然等好久才会生成一个新区块
# return guess_hash[-5:] == "24689"
创建交易
事务的创建需要传入参数,所以这里使用POST请求。
@app.route("/new_transcations", methods=["POST"]) # 创建一个新的交易
def index_new_transcations():
values = request.get_json() #抓取网络传输的信息
required = ["sender", "recipient", "amount"]
#判断提交的json数据key值是否合法
if not all(key in values for key in required):
return "数据不完整或格式错误", 400
index = chaorsCoin.new_transaction(values["sender"],
values["recipient"],
values["amount"]) #新增交易
response = {
"message":f"交易加入到区块{index}"
}
return jsonify(response), 200
新节点
在区块链上,每个用户都是一个独立的节点。 当整个网络的数据发生变化时,每个节点都需要连接到周围的节点来同步最新的数据。 因此,需要将周围的节点加入到当前区块链的节点集中。
@app.route("/new_node", methods=["POST"]) #新增节点
def index_new_node():
values = request.get_json()
nodes = values.get("nodes") #获取所有节点
if nodes is None:
return "怎么是空节点"
for node in nodes:
chaorsCoin.register_node(node)
response = {
"message": "网络节点加入到区块",
"nodes":list(chaorsCoin.nodes)
}
return jsonify(response), 200
这里我们只是使用flask网络请求来模拟区块链上的节点同步原理。 至于真正的节点同步可以很复杂,我们只是想更好地理解它的原理。
节点更新
可能全网不同节点的区块链是不一样的。 张三的区块链高度可能是100比特币区块链长度,李四的可能是105,那么王五请求全网同步数据应该选择哪个区块链呢? 我们都知道长度较长的链和合法的区块链将被选为最长链。
@app.route("/node_refresh") #刷新节点
def index_node_refresh():
replaced = chaorsCoin.resolve_conflicts() #一致性算法进行最长链选择
print(replaced)
if replaced:
response = {
"message": "区块链被替换为最长有效链",
"new chain": chaorsCoin.chain
}
else:
response = {
"message": "当前区块链为最长无需替换",
"chain": chaorsCoin.chain
}
return jsonify(response), 200
共识算法回顾
当区块中的节点进行数据同步时,当前节点会获得一个周围节点的列表。 遍历每个节点保存的区块链。 如果其他区块链的高度大于自己的区块链,且区块链验证合法,则将其替换为最长链。 通俗地说,原理和找一堆数组的最大值没什么区别。 只是具体的实现细节可能会有些不同和复杂。 这不属于今天的讨论范围。 我们只谈原理。
def resolve_conflicts(self)->bool: #冲突,一致性算法的一种
#取得互联网中最长的链来替换当前的链
neighbours = self.nodes #备份节点 eg:127.0.0.1是一个节点,另一个不同的节点192.168.1.
new_chain = None
max_length = len(self.chain) #先保存当前节点的长度
for node in neighbours: #刷新每个网络节点,获取最长跟新
response = requests.get(f"http://{node}/chain") #访问网络节点
print(response.status_code)
if response.status_code == 200:
length = response.json()["length"] #取得邻节点长度
chain = response.json()["chain"] #取得邻节点区块链
# print(max_length, length, self.valid_chain(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
这样,我们就使用flask框架搭建了一个具有主要功能的区块链节点,然后运行测试。
if __name__ == '__main__':
app.run("127.0.0.1", port=5005) #当提示address被占用的时候,更改一下port即可
单节点网络测试
程序运行结果:
当前节点钱包地址: 2a35a1b48e0843269ffc5d7cd6b684e1
* Running on http://127.0.0.1:5005/ (Press CTRL+C to quit)
打开控制台输出的URL,尝试添加不同的后缀访问不同的功能。
混沌币主页
访问区块链页面,发现只有创世区块:
当前节点区块链
当我们访问挖矿界面时,会生成一个新的区块。
挖矿快速产生新区
我们多次访问挖矿界面,然后返回到链页面,发现所有新挖出的区块都被添加到区块链中。
多次挖矿后的链页
我们发现每个区块只有区块奖励的coinBaseTransaction,为什么呢? 因为我们没有产生其他交易。 接下来,模拟生成交易。 这时候就需要使用http工具来提交POST请求。
打开PostMan,按照交易要求的格式提交POST请求:
提交 POST 请求
提交添加交易的请求后,我们看到提示将交易添加到区块5中。我们知道,交易首先被打包成区块,然后区块中的交易才会生效比特币区块链长度,直到挖出一个区块。 也就是说,交易信息只有在挖出第五个区块后才能在区块链中看到。
所以,这里需要再次访问挖矿页面。 然后查看block接口:
第五个区块被开采
加入交易后再次挖矿成功的区块链
多节点同步测试
单节点区块链的接入、挖矿、新增交易都没有问题后,我们就可以测试多节点之间的数据同步了。
新建两个文件ChaosCoinBlockNode_1和ChaosCoinBlockNode_2,将ChaosCoinBlockNode的代码复制到这两个文件中。 修改节点地址,使它们是三个不同的节点。
#ChaorsCoinBlockNode
app.run("127.0.0.1", port=5005)
#ChaorsCoinBlockNode_1
app.run("127.0.0.1", port=5006)
#ChaorsCoinBlockNode_2
app.run("127.0.0.1", port=5007)
分别运行三个节点,通过人工模拟节点数据的差异。 我们通过访问他们各自的挖矿页面来使他们的区块链长度不同。 假设他们共同拥有的区块是相同的。
此时,三个节点的区块链为:
节点 5005
节点 5006
节点 5007
我们以节点5007为例,节点5007向网络发起同步请求。 这个时候,他就需要了解身边的节点。 因此,我们通过POST请求将5005、5006添加到5007的节点列表中。
#节点5005 区块长度4
#节点5006 区块长度8
#节点5007 区块长度3
#预计同步后的数据应该以节点5006为准
添加节点请求
此时节点5007可以同步区块链数据。 访问node_refresh页面,发现更新后的区块链确实是三个节点中最长的一条链:
节点更新数据
当然,此时如果访问5007节点的链页,区块链数据也已经更新到最新的区块链。
这样,我们就通过代码实现了一个简单的区块链的网络层和共识层。 同时也加深了对区块链数据结构和网络同步原理的理解。
本文中的flask框架并不复杂,关键是了解区块链(ChaorsCoinBlockChain)的整体架构代码的实现。