主页 > 钱包imtoken官网 > 从零开始用Python搭建区块链网络+共识

从零开始用Python搭建区块链网络+共识

钱包imtoken官网 2023-05-13 06:14:30

上一篇:用 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,尝试添加不同的后缀访问不同的功能。

区块链技术和比特币_比特币区块链长度_sitebishijie.com 比特币区块链

混沌币主页

访问区块链页面,发现只有创世区块:

sitebishijie.com 比特币区块链_比特币区块链长度_区块链技术和比特币

当前节点区块链

当我们访问挖矿界面时,会生成一个新的区块。

比特币区块链长度_sitebishijie.com 比特币区块链_区块链技术和比特币

挖矿快速产生新区

我们多次访问挖矿界面,然后返回到链页面,发现所有新挖出的区块都被添加到区块链中。

区块链技术和比特币_比特币区块链长度_sitebishijie.com 比特币区块链

多次挖矿后的链页

我们发现每个区块只有区块奖励的coinBaseTransaction,为什么呢? 因为我们没有产生其他交易。 接下来,模拟生成交易。 这时候就需要使用http工具来提交POST请求。

打开PostMan,按照交易要求的格式提交POST请求:

比特币区块链长度_sitebishijie.com 比特币区块链_区块链技术和比特币

提交 POST 请求

提交添加交易的请求后,我们看到提示将交易添加到区块5中。我们知道,交易首先被打包成区块,然后区块中的交易才会生效比特币区块链长度,直到挖出一个区块。 也就是说,交易信息只有在挖出第五个区块后才能在区块链中看到。

所以,这里需要再次访问挖矿页面。 然后查看block接口:

比特币区块链长度_区块链技术和比特币_sitebishijie.com 比特币区块链

第五个区块被开采

比特币区块链长度_sitebishijie.com 比特币区块链_区块链技术和比特币

加入交易后再次挖矿成功的区块链

多节点同步测试

单节点区块链的接入、挖矿、新增交易都没有问题后,我们就可以测试多节点之间的数据同步了。

新建两个文件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)

分别运行三个节点,通过人工模拟节点数据的差异。 我们通过访问他们各自的挖矿页面来使他们的区块链长度不同。 假设他们共同拥有的区块是相同的。

此时,三个节点的区块链为:

比特币区块链长度_区块链技术和比特币_sitebishijie.com 比特币区块链

节点 5005

区块链技术和比特币_比特币区块链长度_sitebishijie.com 比特币区块链

节点 5006

区块链技术和比特币_比特币区块链长度_sitebishijie.com 比特币区块链

节点 5007

我们以节点5007为例,节点5007向网络发起同步请求。 这个时候,他就需要了解身边的节点。 因此,我们通过POST请求将5005、5006添加到5007的节点列表中。

#节点5005  区块长度4
#节点5006  区块长度8
#节点5007  区块长度3
#预计同步后的数据应该以节点5006为准

区块链技术和比特币_比特币区块链长度_sitebishijie.com 比特币区块链

添加节点请求

此时节点5007可以同步区块链数据。 访问node_refresh页面,发现更新后的区块链确实是三个节点中最长的一条链:

sitebishijie.com 比特币区块链_比特币区块链长度_区块链技术和比特币

节点更新数据

当然,此时如果访问5007节点的链页,区块链数据也已经更新到最新的区块链。

这样,我们就通过代码实现了一个简单的区块链的网络层和共识层。 同时也加深了对区块链数据结构和网络同步原理的理解。

本文中的flask框架并不复杂,关键是了解区块链(ChaorsCoinBlockChain)的整体架构代码的实现。