使用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)

开始挖矿:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • 小时候不知天高地厚 免不了父亲的鞭子 鞭子还不曾落到身上 眼泪就伴着哀嚎滚落下来 本以为能逃过一劫 却不曾想结果更...
    丧少女的励志生活阅读 236评论 0 7
  • 自从上了大学才知道什么是雾霾,原来天也可以这样晦暗,所以我格外珍惜好天气,每次蓝天就想出去郊游,可是要学习一扎进图...
    梓田小可爱阅读 384评论 0 1
  • 我总是想象一切都是美好的,然而美好却总是与我无缘,是我太过刻意的追逐而迷失了方向呢?还是所有美好都虚假的害怕我审视...
    独孤琳阅读 149评论 0 0