python多節點同步測試

上一篇只做到單節點的功能測試,這裏繼續多節點。

import hashlib
import json
import time
from urllib.parse import urlparse
from uuid import uuid4
import requests
from flask import Flask, jsonify, request
from typing import Any, Dict, List, Optional  # 數據結構

'''
typing.Optional
可選類型。

Optional[X] 等價於 Union[X, None]。也就是要麼是X,要麼是None

請注意,這不是一個可選參數的概念,它是一個具有默認值的參數。具有默認值的可選參數不需要在其類型註釋上使用 Optional 限定符(雖然如果默認值爲 None,則推斷)。
如果允許 None 的顯式值,則強制參數可以仍然具有 Optional 類型。

typing.Any
表示無約束類型的特殊類型。

每種類型都與 Any 兼容。

Any 兼容各種類型。

'''


class DadaCoinBlockChain:
    def __init__(self):
        self.current_transactions = []  # 交易列表
        self.chain = []  # 區塊鏈
        self.nodes = set()  # 保存網絡節點
        self.new_block(proof=100, preHash="1")  # 創世區塊

    ##創建區塊
    def new_block(self,
                  proof: int,  # 指定參數爲int
                  preHash: Optional[str]  # 指定上一塊的哈希數據類型爲str,默認爲None
                  ) -> Dict[str, Any]:  # 指定返回數據類型
        block = {
            "index": len(self.chain) + 1,  # 索引
            "timestamp": time.time(),  # 當前時間
            "transactions": self.current_transactions,
            "proof": proof,
            "preHash": preHash or self.hash(self.chain[-1])

        }
        self.current_transactions = []
        self.chain.append(block)

        return block

    @staticmethod
    def hash(block: Dict[str, Any]) -> str:
        block_str = json.dumps(block, sort_keys=True).encode("utf-8")
        return hashlib.sha256(block_str).hexdigest()

    def new_transaction(self, sender: str, receiver: str, amount: int) -> int:  # 創建交易
        transaction = {
            "sender": sender,
            "receiver": receiver,
            "amount": amount,
        }
        self.current_transactions.append(transaction)
        return self.last_block["index"] + 1  # 索引標記交易的數量

    @property
    def last_block(self) -> Dict[str, Any]:
        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

    @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[-4:] == "1234"

    def register_node(self, addr: str) -> None:  # 加入網絡的其他節點,用於更新
        now_url = urlparse(addr)
        self.nodes.add(now_url.netloc)  # 增加網絡節點
        pass

    def valid_chain(self, chain: List[Dict[str, Any]]) -> bool:  # 區塊鏈校驗
        last_block = self.chain[0]
        current_index = 1
        while current_index < len(self.chain):
            block = self.chain[current_index]
            # 哈希校驗
            if block["preHash"] != 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_conflict(self) -> bool:  # 同步區塊鏈
        neighbours = self.nodes
        new_chain = None
        max_length = len(self.chain)
        for node in neighbours:
            response = requests.post(f'http://{node}/chain')
            if response.status_code == 200:
                chain = response.json()["chain"]
                length = response.json()["length"]
                if length > max_length and self.valid_chain(chain):
                    max_length = length
                    new_chain = chain
        if new_chain:
            self.chain = new_chain
            return True
        else:
            return False


datacoin = DadaCoinBlockChain()  # 創建一個網絡節點
node_id = str(uuid4()).replace("-", "")  # 生成錢包地址
print("當前節點錢包地址:", node_id)
app = Flask(__name__)  # 初始化


# 首頁
@app.route("/", methods=["POST"])
def index_page():
    return "歡迎來到大大幣系統,你好小韭菜"


# 獲取區塊鏈
@app.route("/chain", methods=["POST"])
def index_chain():
    response = {
        "chain": datacoin.chain,  # 區塊鏈
        "length": len(datacoin.chain),
    }
    return jsonify(response), 200


# 創建新的交易
@app.route("/new_transaction", methods=["POST"])
def new_transaction():
    values = request.get_json()
    print("values:", values)
    required = ["sender", "receiver", "amount"]
    if not all(key in values for key in required):
        return "數據不完整", 400
    index = datacoin.new_transaction(values["sender"], values["receiver"], values["amount"])
    response = {
        "message": f'交易加入到區塊{index}',
    }

    return jsonify(response), 200


# 挖礦
@app.route("/mine", methods=["POST"])
def index_mine():
    last_block = datacoin.last_block
    last_proof = last_block["proof"]
    proof = datacoin.proof_of_work(last_proof)  # 挖礦計算

    # 系統獎勵,挖礦產生交易
    datacoin.new_transaction(sender="0", receiver=node_id, amount=10)
    block = datacoin.new_block(proof, None)  # 增加一個區塊
    response = {
        "message": "新的區塊創建",
        "index": block["index"],
        "transactions": block["transactions"],
        "proof": block["proof"],
        "preHash": block["preHash"],
    }

    return jsonify(response), 200


# 新增節點
@app.route("/new_node", methods=["POST"])
def new_node():
    values = request.get_json()
    nodes = values.get("nodes")
    if nodes is None:
        return "你妹的啥數據", 400
    for node in nodes:
        datacoin.register_node(node)
    response = {
        "message": f'網絡節點已被追加',
        "nodes": list(datacoin.nodes),
    }
    return jsonify(response), 200


# 刷新節點
@app.route("/node_refresh", methods=["POST"])
def node_refresh():
    replaced = datacoin.resolve_conflict()  # 共識算法進行最長替換
    if replaced:
        response = {
            "message": "區塊鏈已被替換爲最長",  # 區塊鏈
            "new_chain": datacoin.chain,
        }
        pass
    else:
        response = {
            "message": "當前區塊鏈已是最長,無需替換",  # 區塊鏈
            "new_chain": datacoin.chain,
        }

    return jsonify(response), 200

if __name__ == "__main__":
    app.run("127.0.0.1", port=5000)

這裏複製三份代碼,端口分別是5000,5002,5003,代表三個節點,全部啓動。
1,5000節點,挖礦6個區塊,5002挖礦2個區塊,5003挖礦3個區塊
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
2,5002添加兩個節點,

 {
    "nodes": ["http://127.0.0.1:5000","http://127.0.0.1:5003"]
 }

5003添加兩個節點

 {
    "nodes": ["http://127.0.0.1:5000","http://127.0.0.1:5002"]
 }

這裏寫圖片描述

3,節點同步,
這裏寫圖片描述

順利完成前面的功能測試,這部分的開發就完成了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章