美文网首页
使用python构建完整底层区块链

使用python构建完整底层区块链

作者: 安福院长 | 来源:发表于2018-03-27 16:18 被阅读0次

    区块链的文章看了很多是不是还是有些云里雾里的感觉 ,在对区块链概念有了基本了解之后,笔者建议有基础的同学可以动手写一个区块链,这样才能更深刻的理解区块链的本质。本文是写给有相应python和区块链基础的同学教给大家如何通过python从零开始构建区块链。

    前导知识储备

    1. Python

    2. 区块链基础

    测试环境

    #此为本机环境,代码在本机环境已调试 运行无误

    1. python3.6.3

    2. flask0.12.2

    3. requests2.18.4

    4. postman

    代码结构

    整个区块链主要包括两个方面,一块是区块链核心代码,另外一块是节点服务器相关的接口,接下来分别就这两部分进行介绍。

    1. 区块链核心代码

    2. 节点服务器接口

    区块链核心代码

    首先我们看下最核心的区块链核心代码结构,对整体框架有个感性认识,之后我会分块进行完善。方便展示我调整了下格式。

    class BlockChain(object):

        def __init__(self):pass

        def new_block(self):pass

        def new_transaction(self):pass

        def proof_of_work(self):pass

        def register_node(self):pass

        def valid_chain(self, chain):pass

        def resolve_conflicts(self):pass

        def hash(block):pass

        def valid_proof(last_proof, proof):pass

        def last_block(self): pass

    __init__: 初始化

    def __init__(self):

        self.chain = []

        self.current_transactions = []

        self.nodes = set()

        #创世区块构建

        self.new_block(previous_hash=1, proof=100)

    初始化函数中,定义了两个列表,chain用来存储区块链,current_transactions用来存储交易信息。集合nodes用来存储节点信息。实例化类的时候,同时初始化产生第一个区块,即创世区块。

    new_block: 生成新的区块

    def new_block(self, proof, previous_has=None):

        block = {

            'index': len(self.chain),

            'timestamp': time.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

    new_block主要用来生成一个新快,并添加到区块链的尾部,这里我们可以看到一个区块的结构被定义为一个字典。区块结构包括5类关键信息

    1. index:当前区块的编号,区块编号从创世区块开始递增

    2. timestamp: 当前时间戳,生成该区块时候的时间

    3. transactions: 未加到区块的交易信息列表

    4. proof:工作量证明

    5. previous_hash: 前一个区块的哈希值

    这是简单的模拟一个区块的结构,与比特币区块链有些差别。更详细的区块结构信息请参考我之前写过的文章浅出区块链,需要 注意的是区块哈希值并不在区块的数据结构里

    new_transaction: 产生新的交易

    def new_transaction(self, sender, receiver, amount):

        self.current_transactions.append({

            'sender': sender,

            'receiver': receiver,

            'amount': amount

        })

        return self.last_block['index']+1

    一个交易的数据结构包括三部分

    1. sender:发送发地址

    2. receiver:接收方地址

    3. amount:数量

    函数里将交易数据加入到交易列表里,并返回即将通过挖矿产生的记录当前交易的下一个区块的index。

    proof_of_work: 工作量证明

    def proof_of_work(self, last_proof):

        proof = 0

        while(self.valid_proof(last_proof,proof) is False):

            proof += 1

        return proof

    工作量证明算法是为了找出一个符合特定条件的数字,作为节点获取当前区块记账权的工作量证明

    valid_proof: 工作量有效证明

    @staticmethod

    def valid_proof(last_proof, proof):

        guess = '{0}{1}'.format(last_proof, proof).encode()

        guess_hash = hashlib.sha256(guess).hexdigest()

        return guess_hash[:4] == '0000'

    当前规则是:基于上一个区块工作量和当前区块工作量所生成的哈希值前四位为'0000'即为有效工作量。通过改变这一条件,可以调整难度。在比特币区块链中有动态调节机制,保证平均10分钟左右生成一个区块。

    register_node: 注册节点

    def register_node(self, address):

        parsed_url = urlparse(address)

        return nodes.add(parsed_url.netloc)

    urlparse函数将地址进行解析,结果示例如下

    In    [1]: urlparse('http://192.168.1.1:5000')

    Out [1]: ParseResult(scheme='http', netloc='192.168.1.1:5000', path='', params='', query='', fragment='')

    valid_chain: 区块有效性验证

    def valid_chain(self, chain):

        last_block = chain[0]

        current_index = 1

        while current_index < len(chain):

            block = chain[current_index]

            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

    区块链的检查主要是针对‘previous_hash' 和 ’proof' 两个字段进行

    创世区块的index为0,其previous_hash 为1,不需要 检查,针对其后的每个区块检查以下两点

    1. 区块所存储'previous_hash'的字段的值和前一个区块hash出来的值是否一致;

    2. 并检查基于该区块'proof'字段的值和上一个区块的'proof'值的哈希值是否满足条件。

    resolve_conflicts: 冲突检查

    def resolve_conflicts(self):

        neighbours = self.nodes

        new_chain = None

        max_length = len(self.chain)

        for node in neighbours:

            response = requests.get('http://{0}/chain'.format(node))

            if(response.status_code == 200):

                length = response.json()['length']

                chain = response.json()['chain']

                if(length > max_length and self.valid_chain(chain)):

                    max_length = length

                    new_chain = chain

            if(new_chain):

                return True

            return False

    此函数会遍历其相邻节点寻找更长的区块链,如果 发现更长的区块链则取代当前节点的区块链

    hash:生成区块哈希值

    @staticmethod

    def hash(block):

        block_string = json.dumps(block,sort_keys=True).encode()

        return hashlib.sha256(block_string).hexdigest()

    调用hashlib相应函数生成区块的sha256哈希值

    last_block: 返回最后一个区块

    @property

    def last_block(self):

        return self.chain[-1]

    恭喜你,看到这里区块链底层最核心的部分已经完成,休息 一会,我们继续完成后续关于节点服务器的代码

    节点服务器接口

    这部分的代码主要来模拟区块链挖矿、交易、注册新节点、冲突解决等行为。

    节点实例化:

    import uuid,time,hashlib,json,requests

    from urllib.parse import urlparse

    from flask import Flask,jsonify,request

    app = Flask(__name__)

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

    blockchain = BlockChain()

    if __name__ == '__main__':

        import sys

        try:

            port = int(sys.argv[1])

        except:

            port = 5000

        app.run(host='0.0.0.0',port=port)

    UUID(Universally Unique Identifier)通用唯一标识符,对于 所有的UUID可以保证在空间上和时间上的唯一性。

    服务器接口:

    @app.route('/mine',methods=['GET'])

    @app.route('/chain', methods=['GET'])

    @app.route('/transactions/new', methods=['POST'])

    @app.route('/nodes/register', methods=['POST'])

    @app.route('/nodes/resolve', methods=['GET'])

    后续我们将分别对接口做进一步的介绍

    挖矿接口:

    @app.route('/mine',methods=['GET'])

    def mine():

        last_block = blockchain.last_block

        last_proof = last_block['proof']

        proof = blockchain.proof_of_work(last_proof)

        blockchain.new_transaction(

            sender = '0',

            receiver= node_identifier,

            amount = 1,

            )

        block = blockchain.new_block(proof)

        response = {

            'message': 'New block created',

            'index': blick['index'],

            'transactions': block['transactions'],

            '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

    发送交易数据接口:

    @app.route('/transactions/new', methods=['POST'])

    def new_transactions():

        values = request.get_json()

        required = ['sender', 'receiver', 'amount']

        if not all(k in values for k in required):

            return('Missing values',400)

        index = blockchain.new_transaction(values['sender'],values['receiver'],values['amount'])

        response = {'message':f'Transaction will be added to block {index}'}

        return jsonify(response),201

    注册新节点接口:

    @app.route('/nodes/register', methods=['POST'])

    def register_nodes():

        values = request.get_json()

        nodes = values.get('nodes')

        if(nodes is None):

            return('Error: pls 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 has been replaced',

                'new_chain': blockchain.chain

            }

        else:

            response = {

                'message': 'Our chain is authoritative',

                'chain': blockchain.chain

            }

        return jsonify(response),200

    到此代码层面已经全部完成,接下来我们将区块链部署,来实际的模拟挖矿,交易等操作。

    测试区块链

    启动server:

    python blockchain.py 5000

    * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

    开始挖矿:

    1. mine交易

    启动第二个节点:

    通过在一台机器上开启不同的网络端口模拟多节点网络

    python blockchain.py 5001

    2. second node

    启动了第二个节点,并且目前第二个节点挖了两次框,区块链的长度为3,两个节点产生冲突。这时可以通过共识机制去处理冲突

    解决冲突:

    1. 通过接口/nodes/register进行注册

    2. 通过接口/nodes/resolve解决节点冲突

    3. register 4. resolve

    可以看到目前节点(5000端口)的区块链已被替换为节点(5001端口)的数据

    至此一个简单的完整的区块链就构建完成,需要了解其他更深入的信息,可以参考我的另外一篇区块链系列文章,里面收集了一些我认为比较不错的资料。

    参考

    Learn Blockchain by Building OneDaniel van Flymen

    相关文章

      网友评论

          本文标题:使用python构建完整底层区块链

          本文链接:https://www.haomeiwen.com/subject/abwjcftx.html