python区块链框架_从零开始创建一个区块链应用(Python版)

本文详细介绍了如何使用Python从零开始创建一个区块链应用,包括创建Blockchain类、区块、交易、工作量证明算法,并通过成语接龙作为工作量证明的验证方式。此外,还实现了基于Flask的用户界面,允许用户参与挖矿。最后,文章讨论了一致性算法,确保全网节点的区块链同步。
摘要由CSDN通过智能技术生成

2018年什么最火?非区块链莫属!

一时间网上各种介绍区块链的文章层出不穷,但大多数都是从概念层面进行解释的,本文则从技术层面讲解,如何从零开始创建一个区块链应用。

本文使用Python开发,读者需要了解一些Python语言的基础知识。

首先创建一个Python文件blockchain.py,所有源代码都保存在该文件中。

接着创建一个

Blockchain 类,在构造函数中创建两个列表,一个用于储存区块链,另一个用于储存交易。

---------------------------------------------------------------------------------------------------

class

Blockchain(object):

def

__init__(self):

self.chain = [] #

存储区块链

self.current_transactions =

[] # 存储交易

self.init_chain()

# 初始化区块链

---------------------------------------------------------------------------------------------------

接下来我们创建一个区块,每个区块包含属性:索引(index)、Unix

时间戳(timestamp)、交易列表(transactions)、工作量证明(proof)和前一个区块的 hash

值(previous_hash)。

---------------------------------------------------------------------------------------------------

def

new_block(self, proof, previous_hash=None):

"""

创建新区块

:param proof:

工作量

:param previous_hash:

前一个区块的 hash 值

:return: 新区块

"""

block = {

'index': len(self.chain) +

1, # 索引

'timestamp': time(), #

时间戳

'transactions':

self.current_transactions, # 交易列表

'proof': proof, #

工作量证明

'previous_hash':

previous_hash or self.hash(self.chain[-1]), # 前一个区块的 hash

}

self.current_transactions =

[] # 清空当前的交易列表

self.chain.append(block) #

新区块添加到链尾

return block

def hash(block):

"""

生成块的

SHA-256 hash值

:param

block: Block

:return:

"""

#

确保块字典是排好序的, 否则不能得到一致性hash值

block_string = json.dumps(block,

sort_keys=True).encode()

return

hashlib.sha256(block_string).hexdigest()

---------------------------------------------------------------------------------------------------

每一个区块都包含前一个区块的

hash

值,这是关键的一点,它保障了区块链的不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的 hash 值都会变得不正确。

接下来我们加入交易模块

---------------------------------------------------------------------------------------------------

def

new_transaction(self, sender, recipient, amount):

"""

生成新的交易记录,

将其加入到下一个区块中

:param sender:

发送者

:param recipient:

接受者

:param amount:

数量

:return:

记录此交易的区块索引

"""

self.current_transactions.append({

'sender':

sender,

'recipient':

recipient,

'amount':

amount,

})

return

self.last_block['index'] + 1

---------------------------------------------------------------------------------------------------

理解工作量证明

新的区块依赖工作量证明算法(PoW)来构造。PoW

的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。

在比特币中,使用称为

Hashcash(https://en.wikipedia.org/wiki/Hashcash)

的工作量证明算法,矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。当然,一旦计算出来,很容易验证这个结果。

实现我们自己的工作量证明

我们的工作量证明非常简单,就是验证用户输入的成语是否有效以及该成语开始汉字和区块链最后一个区块中成语的结束汉字是否相同(即成语接龙规则)。

---------------------------------------------------------------------------------------------------

def

valid_proof(last_proof, proof):

"""

验证工作量证明

:param last_proof:

前一个工作量

:param proof:

当前工作量

:return: True:有效 ,

False:无效.

"""

params = {

'q': proof,

't': 'ChengYu'

}

# 验证是否是成语

url =

"http://chengyu.t086.com/chaxun.php?" + urlencode(params,

encoding="GBK")

try:

r = requests.get(url,

timeout=10)

r.raise_for_status()

r.encoding =

"GBK"

content = r.text

except:

content = None

if content is None or

content.find("没有找到与您搜索相关的成语") > -1 or content.find("搜索词太长") >

-1:

return False

# 验证是否满足成语接龙的规则

return last_proof[-1] ==

proof[0]

---------------------------------------------------------------------------------------------------

到目前为止,Blockchain

类已经基本完成了,接下来我们创建用户挖矿的界面。

我们使用 Python

Flask 框架,这是一个轻量级 Web 应用框架,它方便将网络请求映射到 Python 函数,现在我们来让 Blockchain

运行在 Flask Web 上。

创建节点

我们的“Flask

服务器”将扮演区块链网络中的一个节点,首先我们添加一些框架代码:

---------------------------------------------------------------------------------------------------

from flask

import Flask, jsonify, request, session, render_template

#

创建我们的节点

app =

Flask(__name__)

app.config['SECRET_KEY'] =

os.urandom(32)

blockchain =

Blockchain()

# 欢迎页

@app.route('/')

def

index():

return

render_template('index.html')

# 挖矿页

@app.route('/chain',

methods=['POST'])

def

chain():

phone =

request.form['phone']

if phone

is None or phone == "":

return '缺少参数[phone]',

400

session["phone"] = phone

blockchain.resolve_conflicts()

proofs =

[block["proof"] for block in blockchain.chain]

return

render_template('chain.html', phone=phone,

proofs=proofs)

# 挖矿

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

def

mine():

# 一致性算法解决冲突,后面会讲到

blockchain.resolve_conflicts()

last_block

= blockchain.last_block

last_proof

= last_block['proof']

proof =

request.form['answer'].strip()

if

last_proof[-1] != proof[0]:

proof_chain = ""

for block in

blockchain.chain:

proof_chain += block["proof"] + " ->

"

response = {

'message':

"非常遗憾!你晚了一步,已经有其他用户回答了这个成语",

'proof_chain': proof_chain

}

return jsonify(response),

200

if

blockchain.valid_proof(last_proof, proof):

blockchain.new_transaction(

sender="*", # "*"

表示新挖出的币

recipient=session["phone"],

amount=1,

)

# 在区块链的末尾加入新块

block =

blockchain.new_block(proof)

blockchain.write()

proof_chain = ""

for block in

blockchain.chain:

proof_chain += block["proof"] + " ->

"

response = {

'message': "恭喜你接龙成功!",

'proof_chain': proof_chain

}

else:

response = {

'message': "你输入的不是成语,请重新输入..."

}

return

jsonify(response), 200

---------------------------------------------------------------------------------------------------

这是节点的欢迎页

sg_trans.gif

在欢迎页我们需要输入手机号作为个人的身份认证。

接下来是挖矿页

在这个页面你将会看到所有区块的工作量证明,输入你将要接龙的成语进行挖矿。

sg_trans.gif

挖矿成功后,你将会拥有该成语(即工作量证明)。

sg_trans.gif

至此,我们的区块链应用已经可以挖矿了,但是区块链系统是分布式的。既然是分布式的,那么我们究竟该如何保证所有节点拥有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性算法。

在一致性算法中我们规定最长且有效的链才是最终链,其它链将会被抛弃。

---------------------------------------------------------------------------------------------------

def

resolve_conflicts(self):

"""

一致性算法解决冲突

使用网络中最长的链.

:return: True:链被取代,

False:链未被取代

"""

new_chain =

self.chain

# 寻找全网链长最大的区块链

max_length =

len(self.chain)

replaced = False

#

遍历全网所有节点获取区块链,在中本聪的《比特币:一种点对点的电子现金系统》论文中没有给出该如何获取全网所有节点,我们这里采用遍历区域网的方式实现

for i in range(1,

256):

try:

response =

requests.get(f'http://192.168.0.{i}:5000/gainBlockChain',

timeout=30)

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

replaced = True

# 链长等于当前最大且有效的区块链,

对比最后一个block的创建时间

elif length == max_length and

self.valid_chain(chain):

if float(chain[-1]["timestamp"]) <

float(new_chain[-1]["timestamp"]):

new_chain = chain

replaced = True

except Exception as

e:

print(e)

#

如果发现新的链长大于自己且有效的区块链则替换自己的

if replaced:

self.chain =

new_chain

self.write()

return replaced

# 获取节点的区块链

@app.route('/gainBlockChain',

methods=['GET'])

def

full_chain():

response =

{

'chain':

blockchain.chain,

'length':

len(blockchain.chain),

}

return

jsonify(response), 200

def

valid_chain(self, chain):

"""

验证区块链的有效性

:param chain:

待验证的区块链

:return: True:有效,

False:无效

"""

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")

# 验证块的hash值是否正确

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

data_dir =

"/opt/blockchain"

def

write(self):

'''

将区块链信息写入本地磁盘

:return:

'''

f = open(data_dir +

"/data.txt", "w", encoding="utf-8")

for block in

self.chain:

f.write(str(block["index"]))

f.write("#")

f.write(block["proof"])

f.write("#")

f.write(str(block["previous_hash"]))

f.write("#")

f.write(str(block["timestamp"]))

f.write("#")

transactions =

block["transactions"]

if transactions and

len(transactions) > 0:

f.write(transactions[-1]["sender"])

f.write("#")

f.write(transactions[-1]["recipient"])

f.write("#")

f.write(str(transactions[-1]["amount"]))

else:

f.write("*#*#1")

f.write("\n")

f.close()

---------------------------------------------------------------------------------------------------

在创建新的节点时,我们会调用resolve_conflicts()方法来解决冲突。

这是创建节点时初始化区块链的方法,里面包含了创世块的创建,这是非常关键的一步。

---------------------------------------------------------------------------------------------------

def

init_chain(self):

# 读取本地存储的区块链

if len(self.chain) ==

0:

try:

f = open(data_dir + "/data.txt", "r",

encoding="utf-8")

for line in f.readlines():

items = line.split("#")

self.current_transactions = []

transaction = {

'sender': items[4],

'recipient': items[5],

'amount': int(items[6]),

}

self.current_transactions.append(transaction)

block = {

'index': int(items[0]),

'timestamp': float(items[3]),

'transactions':

self.current_transactions,

'proof': items[1],

'previous_hash': items[2],

}

self.chain.append(block)

f.close()

except:

pass

#

遍历全网下载链长最长且有效的区块链

self.resolve_conflicts()

# 创建创世快

if len(self.chain) ==

0:

self.new_block(proof="海阔天空",

previous_hash="*")

#

self.new_transaction(sender="*", recipient="*",

amount=1)

if not

os.path.exists(data_dir):

os.mkdir(data_dir)

self.write()

---------------------------------------------------------------------------------------------------

好啦,我们的区块链应用可以挖矿了,你可以邀请小伙伴们一起来测试了。

尚未解决的问题点:

1. 时间戳服务器: 在中本聪的《比特币:一种点对点的电子现金系统》论文中给出了时间戳服务器的概念,不过在我们的实现代码中,仅采用了各个节点自身的时间生成的时间戳,这是一个需要解决的问题点。

2. 区块链全网所有节点: 在我们的实现代码中,我们是采用遍历区域网的方式实现的,而在真实的场景下,需要记录所有的节点用于实现一致性算法。

参考: http://blog.51cto.com/wuwenhua/2084615

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值