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

区块链的文章看了很多是不是还是有些云里雾里的感觉 ,在对区块链概念有了基本了解之后,笔者建议有基础的同学可以动手写一个区块链,这样才能更深刻的理解区块链的本质。本文是写给有相应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)

开始挖矿:

使用python构建完整底层区块链_第1张图片
1. mine交易

启动第二个节点:

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

python blockchain.py 5001


使用python构建完整底层区块链_第2张图片
2. second node

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

解决冲突:

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

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


使用python构建完整底层区块链_第3张图片
3. register


使用python构建完整底层区块链_第4张图片
4. resolve

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

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

参考

Learn Blockchain by Building OneDaniel van Flymen

你可能感兴趣的:(使用python构建完整底层区块链)