• 用Python构建区块链


    区块链

    区块链是在计算机网络的节点之间共享数据的分类账(分布式数据库)。作为数据库,区块链以电子格式储存信息。区块链的创新之处在于它保证了数据记录的安全性和真实性,可信性(不需要没有可信任的第三方)。

    区块链和典型数据库的区别是数据结构。区块链以block的方式收集信息。

    block

    block是一种能永久记录加密货币交易数据(或其他用途)的一种数据结构。类似于链表。一个block记录了一些火所有尚未被验证的最新交易。验证数据后,block将关闭,之后会创建一个新的block来输入和验证新的交易。因此,一旦写入,永久不能更改和删除。

    • block是区块链中存储和加密信息的地方
    • block由长数字标识,其中包括先前加密块的加密交易信息和新的交易信息
    • 在创建之前,block以及其中的信息必须由网络验证

    以下是一个简单的例子:

    block = {
        'index': 1,
        'timestamp': 1506057125.900785,
        'transactions': [
            {
                'sender': "8527147fe1f5426f9dd545de4b27ee00",
                'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
                'amount': 5,
            }
        ],
        'proof': 324984774000,
        'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    目标

    区块链的目标是允许数字信息被记录和分发,但不能编辑。通过这种方式,区块链成为了不可变分类账或无法更改、删除和销毁的交易记录的基础。

    去中心化

    想象一下,一家公司拥有10000台服务器,用于维护一个包含所有客户信息的数据库。公司的所有服务器都在一个仓库中,可以完全控制每台服务器。这就提供了单点故障。如果那个地方停电了怎么办?如果他的网络连接被切断了怎么办?在任何情况下,数据都会丢失或损坏。

    构建

    区块链类

    我们将创建一个BlockChain类,构造函数创建一个空列表来存储区块链,再创建一个空列表来存储交易。创建block_chain.py

    # block_chain.py
    
    class Blockchain:
        def __init__(self) -> None:
            self.chain = []
            self.current_transactions = []
            
        def new_block(self):
            # Creates a new Block and adds it to the chain
            pass
        
        def new_transaction(self):
            # Adds a new transaction to the list of transactions
            pass
        
        @staticmethod
        def hash(block):
            # Hashes a Block
            pass
    
        @property
        def last_block(self):
            # Returns the last Block in the chain
            pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    添加交易

    我们需要一种将交易添加到区块的方法。new_transaction负责这个

    class Blockchain(object):
        ...
        
        def new_transaction(self, sender, recipient, amount) -> int:
            self.current_transactions.append({
                'sender': sender,
                'recipient': recipient,
                'amount': amount,
            })
    
            return self.last_block['index'] + 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    new_transaction 将交易添加到列表后,它返回交易将被添加到的块的索引——下一个要挖掘的块。这将在以后对提交交易的用户有用。

    创建新blocks

    当我们的区块链被实例化时,我们需要为它播种一个创世块——一个没有前辈的块。我们还需要向我们的创世块添加一个“证明”,这是挖掘的结果(或工作量证明)。除了在我们的构造函数中创建创世块之外,我们还将充实 new_block()、new_transaction() 和 hash() 的方法:

    import hashlib
    import json
    from time import time
    
    
    class Blockchain:
        def __init__(self) -> None:
            self.chain = []
            self.current_transactions = []
            
            # Create the genesis block
            self.new_block(previous_hash=1, proof=100)
            
        def new_block(self, proof, previous_hash=None) -> dict:
            
            block = {
                'index': len(self.chain) + 1,
                'timestamp': time(),
                'transactions': self.current_transactions,
                'proof': proof,
                'previous_hash': previous_hash or self.hash(self.chain[-1]),
            }
            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_block['index'] + 1
        
        
        @property
        def last_block(self) -> dict:
            # Returns the last Block in the chain
            return self.chain[-1]
        
        @staticmethod
        def hash(block) -> str:       
            block_string = json.dumps(block, sort_keys=True).encode()
            return hashlib.sha256(block_string).hexdigest()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    到这里,我们几乎完成了代表我们的区块链。但此时,你一定想知道新区块是如何创建、锻造或开采的。

    POW

    工作量证明算法 (PoW) 是在区块链上创建或挖掘新块的方式,它的目标是发现一个解决问题的数字。这个数字必须很难找到但很容易被网络上的任何人验证。PoW广泛用于加密货币挖掘,用于验证交易和挖掘新代币。由于PoW,比特币和其他加密货币交易可以以安全的方式进行点对点处理,而无需受信任的第三方。
    让我们实现一个类似的算法:

    class Blockchain(object):
        def proof_of_work(self, last_proof) -> int:
            proof = 0
            while self.valid_proof(last_proof, proof) is False:
                proof += 1
            
            return proof
        
        @staticmethod
        def valid_proof(last_proof, proof) -> bool:
            guess = f'{last_proof}{proof}'.encode()
            guess_hash = hashlib.sha256(guess).hexdigest()
            return guess_hash[:4] == '0000'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    API

    为了使区块链能够交互,我们需要一个将其置于web服务器上。这里我们是用Flask框架。
    如果没有安装,需要安装flask

    pip install flask
    
    • 1

    我们的服务器将在我们的区块链网络中形成单一节点,在同级目录下创建一个app.py:

    from uuid import uuid4
    from time import time
    from textwrap import dedent
    from flask import Flask, jsonify, request
    from block_chain import Blockchain
    
    
    # 实例化应用
    app = Flask(__name__)
    
    # 创建随机节点名称
    node_identifier = str(uuid4()).replace('_', '')
    
    # 实例化block_chain类
    block_chain = Blockchain()
    
    # 创建/mine端点
    @app.route('/mine', methods=['GET'])
    def mine():
        block_chain.new_transaction(
            sender="0",
            recipient=node_identifier,
            amount=1,
        )
    
        last_block = block_chain.last_block
        last_proof = last_block['proof']
    
        proof = block_chain.proof_of_work(last_proof)
        previous_hash = block_chain.hash(last_block)
        block = block_chain.new_block(proof, previous_hash)
    
        response = {
            'message': "New Block Forged",
            'index': block['index'],
            'transactions': block['transactions'],
            'proof': block['proof'],
            'previous_hash': block['previous_hash'],
        }
        return jsonify(response), 200
    
    
    @app.route('/transactions/new', methods=['POST'])
    def new_transaction():
        return "We'll add a new transaction"
    
    
    @app.route('/chain', methods=['GET'])
    def full_chain():
        response = {
            'chain': block_chain.chain,
            'length': len(block_chain.chain),
        }
        return jsonify(response), 200
    
    # 修改端口号
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    然后运行

    flask run
    
    • 1

    通过api软件(本次使用的是api fox)来发送请求:
    调用mine

    调用new

    注册新节点

    区块链的全部意义在于它们应该去中心化。如果想要网络中有多个节点,必须采用共识算法。在我们可以实施共识算法之前,我们需要一种方法让节点知道网络上的相邻节点。我们网络上的每个节点都应该保留网络上其他节点的注册表。因此,我们需要更多的端点:

    ...
    from urllib.parse import urlparse
    ...
    
    
    class Blockchain:
        def __init__(self) -> None:
            ...
            self.nodes = set()
            ...
    
        def register_node(self, address) -> None:    
            parsed_url = urlparse(address)
            self.nodes.add(parsed_url.netloc)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    冲突

    冲突是指一个节点与另一个节点有不同的链。为了解决这个问题,我们将制定最长有效链为权威的规则。使用此算法,我们在网络中的节点之间达成共识。

    ...
    import requests
    
    
    class Blockchain:
        ...
        
        def valid_chain(self, chain):
    
            last_block = chain[0]
            current_index = 1
    
            while current_index < len(chain):
                block = chain[current_index]
                print(f'{last_block}')
                print(f'{block}')
                print("\n-----------\n")
                # Check that the hash of the block is correct
                if block['previous_hash'] != self.hash(last_block):
                    return False
    
                # Check that the Proof of Work is correct
                if not self.valid_proof(last_block['proof'], block['proof']):
                    return False
    
                last_block = block
                current_index += 1
    
            return True
    
        def resolve_conflicts(self):
            """
            This is our Consensus Algorithm, it resolves conflicts
            by replacing our chain with the longest one in the network.
            :return:  True if our chain was replaced, False if not
            """
    
            neighbours = self.nodes
            new_chain = None
    
            # We're only looking for chains longer than ours
            max_length = len(self.chain)
    
            # Grab and verify the chains from all the nodes in our network
            for node in neighbours:
                response = requests.get(f'http://{node}/chain')
    
                if response.status_code == 200:
                    length = response.json()['length']
                    chain = response.json()['chain']
    
                    # Check if the length is longer and the chain is valid
                    if length > max_length and self.valid_chain(chain):
                        max_length = length
                        new_chain = chain
    
            # Replace our chain if we discovered a new, valid chain longer than ours
            if new_chain:
                self.chain = new_chain
                return True
    
            return False
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    第一个方法 valid_chain() 负责通过遍历每个块并验证哈希和证明来检查链是否有效。resolve_conflicts() 是一种循环遍历我们所有相邻节点、下载它们的链并使用上述方法验证它们的方法。如果找到一个有效的链,其长度大于我们的,我们将替换我们的。

    让我们将两个端点注册到我们的 API,一个用于添加相邻节点,另一个用于解决冲突:

    @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 have 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在这一点上,如果你愿意,你可以拿一台不同的机器,并在你的网络上启动不同的节点。或者在同一台机器上使用不同的端口启动进程。比如创建两个端口5000和6000来进行尝试。

  • 相关阅读:
    Mysql 索引优化——Explain
    PLC信号发生器(余弦信号)
    【Mysql系列】05_JDBC使用
    员工档案,云进销存财务ERP批发零售超市商贸仓库出入库管理软件开发
    npm,yarn如何查看源和换源,删除node_modules
    vue03模板语法(下)
    k8s部署nacos集群(外接mysql数据库)
    ChatGPT自媒体创作秘籍:高效生成优质文章和视频
    手把手带你自定义 Gradle 插件 —— Gradle 系列(2)
    STM32-EXTI中断
  • 原文地址:https://blog.csdn.net/majiayu000/article/details/128029383