区块链 — 原理与代码实现

import hashlib
import json
from uuid import uuid4
import requests
from time import time
from urllib.parse import urlparse
from flask import Flask, jsonify, request
from argparse import ArgumentParser


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]

            # 计算出的 hash 值与实际不符
            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:
            reponse = requests.get(f'http://{node}/chain')
            if reponse.status_code == 200:
                length = reponse.json()['length']
                chain = reponse.json()['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

    # 创建区块
    def new_block(self, proof, previous_hash=None):

        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transcations': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_lock)
        }

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

        return block

    # 新建交易
    def new_transaction(self, sender, recipient, amount) -> int:
        self.current_transactions.append(
            {
                'sender': sender,
                'recipient': recipient,
                'amount': amount
            }
        )
        return self.last_lock['index'] + 1

    # 计算 hash 值
    @staticmethod
    def hash(block):
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

    # 返回最后一个区块
    @property
    def last_lock(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
        return proof

    # hash 值前 4 位是否为 0
    def valid_proof(self, last_proof: int, proof: int) -> bool:
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[0:4] == '0000'


app = Flask(__name__)
blockchain = BlockChain()

node_identifier = str(uuid4()).replace('-', '')

# 创建新交易
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    values = request.get_json()
    required = ['sender', 'recipient', 'amount']
    if values is None or 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'Transcation will be added to Block {index}'}
    return jsonify(response), 201

# 挖矿
@app.route('/mine', methods=['GET'])
def mine():
    last_block = blockchain.last_lock
    last_proof = last_block['proof']
    proof = blockchain.proof_of_work(last_proof)

    blockchain.new_transaction(sender="0",
                               recipient=node_identifier,
                               amount=1)
    block = blockchain.new_block(proof, None)

    response = {
        "message": "New Block Forged",
        "index": block['index'],
        "transcations": block['transcations'],
        "proof": block['proof'],
        "previous_hash": block['previous_hash']
    }

    return jsonify(response), 200

# 获取整个区块链
@app.route('/chain', methods=['GET'])
def full_chain():

    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain)
    }
    return jsonify(response), 200

# 注册节点
# {"nodes": ["http://127.0.0.1:5001"]}
@app.route('/nodes/register', methods=['POST'])
def register_nodes():
    values = request.get_json()

    nodes = values.get('nodes')

    if nodes is None:
        return "Error: please supply a valid list of nodes", 400

    for node in nodes:
        blockchain.register_node(node)

    response = {
        "message": "New nodes has been added",
        "total_nodes": list(blockchain.nodes)
    }

    return jsonify(response), 201

# 解决冲突,切换到最长的链
@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 chain is authoritative",
            "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)