python网络构建_Python从零开始构建区块链之网络+共识(上)

(上)准备工作

前言

上篇文章用代码简单构造了区块链数据层各个数据类的实现,一个简陋的区块链基本实现。今天开始,在其基础上简单实现区块链的网络层和共识层。

本次系列文章将从实际代码出发,来加深你对区块链网络同步和区块共识的理解。

准备工作

由于用到了网络,我们需要借助一个网络框架flask实现网络节点,同时一致性共识的算法建立在挖矿的基础上,我们需要对挖矿的原理有所了解。

1.Flask网络框架

Flask是一个使用 Python 编写的轻量级 Web 应用框架。我们在这里使用flask框架模拟实现区块链网络中的各个节点,以实现节点同步和节点中的最长链选择。

打开Pycharm,新建一个flask项目。我们来初步了解一下,我们将怎么使用它来构造节点。

新建后,默认的flask项目代码是:

from flask import Flask

app = Flask(__name__)

@app.route('/') #映射根目录

def hello_world():

return 'Hello World!' #返回网页信息

if __name__ == '__main__':

app.run()

我们运行一下项目,将会在consle看到:

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

在浏览器输入网址:http://127.0.0.1:5000/:

http://127.0.0.1:5000/

我们一看就明白了,flask通过app.route映射网页链接,然后返回网页内容。我们对上述代码稍作修改:

from flask import Flask

app = Flask(__name__)

from flask import Flask

app = Flask(__name__)

@app.route('/') #映射根目录

def hello_world():

return 'this is a chainNode!' #返回网页信息

@app.route('/zhangsan')

def node_zhangsan():

return '张三的区块链节点'

@app.route('/lisi')

def node_lisi():

return '李四的区块链节点'

@app.route('/wangwu')

def node_wangwu():

return '王五的区块链节点'

if __name__ == '__main__':

app.run()

运行结果依然是:

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

我们访问网址发现:

节点根目录:http://127.0.0.1:5000/

张三的节点:http://127.0.0.1:5000/zhangsan

王五的节点: http://127.0.0.1:5000/wangwu

这样,我们就通过flask框架构造了网络中不同的网络节点:

http://127.0.0.1:5000/zhangsan #张三的节点

http://127.0.0.1:5000/lisi #李四的节点

http://127.0.0.1:5000/wangwu #王五的节点

...

2.区块链中挖矿概念的简单理解

在区块链中,挖矿其实是一个数学问题。通俗地讲,就是在不断计算一个区块的哈希值,直到计算出的结果符合系统设定的条件,我们就说“成功地挖到一个区块”。

忽略其他因素,我们通过代码简单地模拟一下挖矿的过程:

#通过哈希来挖掘区块

from hashlib import sha256

if __name__ == '__main__':

x = 11

y = 0

#f'{x*y}' 将x*y的结果转化为字符串 encode("utf-8") 默认utf-8,可以不写

#当x*y的结果的前三位是=都是0的时候找到目标哈希值

#hexdigest:哈希结果的二进制字符串

while sha256(f'{x*y}'.encode("utf-8")).hexdigest()[-3:] != "000":

# while sha256(f'{x*y}'.encode("utf-8")).hexdigest()[-5:] != "01010":

# while sha256(f'{x*y}'.encode("utf-8")).hexdigest()[:8] != "00001111":

y += 1 #不符合条件,计算次数统计加1

print(y)

print("y = {}".format(y)) #找到目标值时候的计算次数

我们运行程序,看一下结果如何:

sha256(f'{x*y}'.encode("utf-8")).hexdigest()[-3:] == "000"

sha256(f'{x*y}'.encode("utf-8")).hexdigest()[-5:] == "01010"

sha256(f'{x*y}'.encode("utf-8")).hexdigest()[:8] == "00001111"

我们依次增大目标条件的难度值(根据改变匹配的位数),发现计算的次数越来越大。甚至第三种条件我的Mac本跑了五分钟都没有出结果。。。

#计算了10383次得到目标值

sha256(f'{x*y}'.encode("utf-8")).hexdigest()[-3:] == "000"

#计算了874020次得到目标值

sha256(f'{x*y}'.encode("utf-8")).hexdigest()[-5:] == "01010"

#Mac本计算了5分钟,91517163次仍然没有得到目标值!!!这个好难哦...

sha256(f'{x*y}'.encode("utf-8")).hexdigest()[:8] == "00001111"

这就是区块链“挖矿”的一个基本原理,挖矿难度值也在于上述代码中的目标条件设定。因此,我们可以通过这个条件设置来更改挖矿难度。

3.区块链架构整合与代码完善

前面,为了更直观清楚地理解区块链数据层的结构,我们构造了交易类,交易记录类,区块类,区块链类等来分层分模块详细理解各项功能。

现在,我们将主要的区块链功能整合到一个区块链类里,然后加上必要的工作量证明,区块校验,节点同步,最长链一致性算法等核心代码。并且对之前的Python代码做一个规范化,以后我们谈到的区块链代码框架也一般用今天写的这个。

引入必要模块

import hashlib #信息安全加密

import json #网络数据传递格式

import datetime #时间

from typing import Optional, Dict, Any, List #必要数据类型

from urllib.parse import urlparse #url编码解码

from uuid import uuid4 #签名

import requests #网络请求

from flask import Flask, jsonify, request #flask网络框架

构造方法

创建一个区块链类ChaorsCoinBlockChain,首先我们需要实现init方法,init方法创造一个区块链时需要有一个创世区块,需要包含一个区块列表和当前交易列表,同时一个区块链必须还有节点信息。

def __init__(self):

self.chain = [] #区块列表

self.current_transactions = [] #交易列表

self.nodes = set() #结点

self.new_block(proof=100, prev_hash=1) #创建创世区块

新增区块

上篇文章我们已经知道,每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明以及前一个区块的Hash值。

block = {

'index': 1,

'timestamp': 1506057125.900785,

'transactions': [

...

],

'proof': 324984774000,

'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

}

因此,新增一个区块需要以上各项信息

#新增区块

def new_block(self,

proof:int, #指定工作量类型

prev_hash:Optional[str] #默认是字符串

)->Dict[str, Any]: #指定返回值类型为字典

block = {

"index":len(self.chain) + 1, #新增区块,原有区块索引加1

"timestamp":datetime.datetime.now(), #时间戳

"transactions":self.current_transactions.clear(), #交易

"proof":proof, #工作量证明

"prev_hash":prev_hash or self.hash(self.chain[-1]) #上个区块的哈希

}

self.current_transactions = [] #开辟新的区块,即交易记录加入区块,当前交易需要被清空!!!

self.chain.append(block) #将区块追加到区块链表中

return block

新增交易

一个交易信息包括发起者,接受者和交易数额。

#交易的数据结构

[

{

'sender': "8527147fe1f5426f9dd545de4b27ee00",

'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",

'amount': 5,

}

...

]

#新增交易

def new_transaction(self, sender:str, recipient:str, amount:int)->int:

#生成交易信息

self.current_transactions.append({

"sender":sender, #付款方

"recipient":recipient, #收款方

"amount":amount #交易数额

})

return self.last_block["index"] + 1 #索引标记交易的数量

求哈希值

接下来,我们需要定义一个静态方法来求区块的哈希值。

@staticmethod

def hash(block:Dict[str, Any])->str: #传入一个字典类型,返回一个字符串

#对模块进行哈希处理 json.dumps:将区块处理为字符串

block_str = json.dumps(block, sort_keys=True).encode("utf-8")

return hashlib.sha256(block_str).hexdigest() #返回哈希值

上个区块

我们还需要一个属性用来访问区块链上一个区块

@property

def last_block(self)->Dict[str, Any]:

return self.chain[-1]

工作量证明

这里就需要用到,我们上面讲到的挖矿原理(回顾请看上述准备工作2)。由于区块链挖矿需要依赖于上一个区块,所以这里需要传入上个区块作为参数。

#挖矿依赖于上一个模块

def proof_of_work(self, last_block)->int: #挖矿获取工作量证明

last_proof = last_block["proof"] #取出算力证明

last_hash = self.hash(last_block)

proof = 0 #循环求解符合条件的合法哈希

#valid_proof:用于验证工作量证明是否符合条件

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

proof += 1

return proof

工作量证明验证

这个方法用于验证矿工的工作量证明是否满足系统条件,挖矿的难度也是在这里设置的。

@staticmethod

def valid_proof(last_proof:int, proof:int)->bool: #验证工作量证明

guess = f'{last_proof}{proof}'.encode("utf-8")

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

return guess_hash[:6] == "000000" #计算难度

# return guess_hash[-5:] == "24689" #这里的条件更改可以改变工作量证明计算的难度(即挖矿难度)

节点注册

区块链网络中的各个节点需要加入到区块链中,才能完成全网的共识同步。

def register_node(self, addr:str)->None: #加入网络中的其他节点,用于更新

parsed_url = urlparse(addr) #url解析

if parsed_url.netloc: #可以连接网络

self.nodes.add(parsed_url.netloc) # 增加网络节点

elif parsed_url.path:

self.nodes.add(parsed_url.path)

else:

raise ValueError("url无效")

区块链合法验证

区块链合法性校验需要满足两个条件:1.区块哈希合法性;2.区块工作量证明合法性。二者缺一不可,必须同时满足才能说明该区块合法。而链上所有区块都合法,才能说明区块链合法。

def valid_chain(self, chain:List[Dict[str, Any]])->bool: #区块链校验

last_block = chain[0] #从第一块开始校验

current_index = 1

while current_index < len(chain): #循环校验

block = chain[current_index]

if block["prev_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 #备份节点 eg:127.0.0.1是一个节点,另一个不同的节点192.168.1.

new_chain = None #更新后的最长区块链

max_length = len(self.chain) #先保存当前节点的长度

for node in neighbours: #刷新每个网络节点,获取最长跟新

response = requests.get(f'http://{node}/chain') #取得其他节点的区块链

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

else:

return False

这样,我们就简单搭建了一个简陋但是功能还比较齐全的区块链架构。

网络共识实现代码的准备工作到此就告一段落,由于实现网络共识涉及的内容比较多,这一篇暂且讲到这。下一篇文章开始正式解析网络与共识的具体实现与调试。

互联网颠覆世界,区块链颠覆互联网!

---------------------------------------------------------20180406晨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值