教程区块链区块链技术ch1616.5 简易 P2P 网络与节点发现

本页目录

单节点 = 数据库。多节点 + 交叉验证 = 区块链。本节用 HTTP API 模拟节点间通信。


16.5.1 节点架构

graph TD
    N1[节点 1:3001] --> |广播新交易| N2[节点 2:3002]
    N1 --> |广播新块| N3[节点 3:3003]
    N2 --> |/resolve| N1
    N3 --> |/resolve| N2
    
    style N1 fill:#e3f2fd
    style N2 fill:#e8f5e9
    style N3 fill:#fff3e0

消息类型

消息方法用途
新区块POST /blocks/broadcast挖矿后广播
新交易POST /transactions/new接受广播/用户提交
请求链GET /chain获取完整链
共识GET /nodes/resolve最长链规则

16.5.2 Flask 节点实现

python
# mini_node.py — 极简节点
from flask import Flask, request, jsonify
import requests, time

app = Flask(__name__)

# 每个节点运行独立的区块链实例
blockchain = Blockchain(difficulty=4)

# 已知的对等节点列表
peers = set()  # { "http://localhost:3002", ... }

@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    data = request.get_json()
    tx = blockchain.create_transaction(
        data['sender'], data['recipient'], data['amount']
    )
    # 广播给所有同伴
    broadcast_transaction(data)
    return jsonify({"message": "交易已添加至 pending", "nonce": tx.nonce}), 201

def broadcast_transaction(tx_data):
    for peer in peers:
        try:
            requests.post(f"{peer}/transactions/new", json=tx_data, timeout=1)
        except: pass  # 节点离线 — 不影响本次提交

@app.route('/mine', methods=['GET'])
def mine():
    miner = request.args.get('address', '0')
    block = blockchain.mine_pending_transactions(miner)
    broadcast_block(block)
    return jsonify({
        "message": "已挖出新区块",
        "index": block.index,
        "hash": block.hash,
        "nonce": block.nonce,
    })

def broadcast_block(block):
    data = {
        "index": block.index,
        "timestamp": block.timestamp,
        "transactions": [tx.to_dict() for tx in block.transactions],
        "previous_hash": block.previous_hash,
        "nonce": block.nonce,
        "hash": block.hash,
        "difficulty": block.difficulty,
    }
    for peer in peers:
        try:
            requests.post(f"{peer}/blocks/broadcast", json=data, timeout=1)
        except: pass

@app.route('/blocks/broadcast', methods=['POST'])
def receive_block():
    """其他节点挖出的新块"""
    data = request.get_json()
    # 验证后追加
    # 简化:信任网络,仅检查 prevHash
    new_block = Block(**data)
    if new_block.previous_hash == blockchain.chain[-1].hash:
        blockchain.chain.append(new_block)
    return jsonify({"message": "区块已接收"}), 200

@app.route('/chain', methods=['GET'])
def full_chain():
    return jsonify({
        "chain": [asdict(b) for b in blockchain.chain],
        "length": len(blockchain.chain),
    })

@app.route('/nodes/resolve', methods=['GET'])
def consensus():
    """最长链规则:发现更长的有效链时替换"""
    replaced = False
    for peer in peers:
        try:
            res = requests.get(f"{peer}/chain", timeout=2).json()
            other_chain = res['chain']
            
            if len(other_chain) > len(blockchain.chain):
                # 验证整条链
                if is_valid_chain(external_chain_to_blocks(other_chain)):
                    # 回滚本地状态
                    rebuild_from_chain(other_chain)
                    replaced = True
        except: pass
    
    return jsonify({"replaced": replaced, "length": len(blockchain.chain)})

@app.route('/register', methods=['POST'])
def register_node():
    node = request.get_json()['node']
    peers.add(node)
    return jsonify({"message": f"节点 {node} 已注册", "peers": list(peers)})

# 启动
if __name__ == '__main__':
    import sys
    port = int(sys.argv[1]) if len(sys.argv) > 1 else 3001
    app.run(host='0.0.0.0', port=port, debug=False)

16.5.3 三重启动与测试

bash
# 启动 3 个节点
cd chapter16 && python mini_node.py 3001 &
cd chapter16 && python mini_node.py 3002 &
cd chapter16 && python mini_node.py 3003 &

# 互相注册
curl -X POST http://localhost:3001/register -H 'Content-Type: application/json' -d '{"node":"http://localhost:3002"}'
curl -X POST http://localhost:3001/register -H 'Content-Type: application/json' -d '{"node":"http://localhost:3003"}'

# 节点1 挖矿
curl http://localhost:3001/mine?address=Alice

# 节点2 发起交易
curl -X POST http://localhost:3002/transactions/new -H 'Content-Type: application/json' -d '{"sender":"Alice","recipient":"Bob","amount":20}'

# 节点3 发起交易后挖矿
curl -X POST http://localhost:3003/transactions/new ...
curl http://localhost:3003/mine?address=Bob

# 检查所有节点是否同步
curl http://localhost:3001/chain | jq'.length'
curl http://localhost:3002/chain | jq'.length'
curl http://localhost:3003/chain | jq'.length'
# 输出应相同!

16.5.4 共识演示:分叉与恢复

sequenceDiagram
    N1[节点1] ->> N2: 开始挖矿
    N3[节点3] ->> N2: 开始挖矿
    
    N1 ->> N1: 挖出块 #2A
    N3 ->> N3: 挖出块 #2B(同时)
    
    N1 ->> N2: 广播 #2A
    N3 ->> N2: 广播 #2B
    
    N2 ->> N2: 收到两个 #2,先到达者采纳
    
    N1 ->> N2: 挖出 #3A(基于 #2A)
    Note right of N2: #3A 更长,N2 回滚到 #2A
    
    style N1 fill:#e3f2fd
    style N3 fill:#fff3e0

, 前往 → 16.6 HTTP API 与区块链浏览器 |*

评论

0

评论加载中…

发表评论

0/2000