Python区块链仿真实现教程及源码
在区块链或领域,Python并不是主流的开发语⾔。但是如果你的⽬的是研究区块链技术的原理,或者需要在⾃⼰的笔记本上仿真⼀个区块链⽹络并进⾏⼀些研究性的实验,⽐如完成⾃⼰的毕业设计项⽬或科研课题,那么Python就是合适的。在这个教程⾥,我们将学习如何使⽤Python从零开发⼀个多节点的区块链⽹络,并基于这个仿真区块链⽹络,开发⼀个去中⼼化的数据分享应⽤。
相关教程链接: | | | | | | |
1、Python仿真区块链:⽤区块分批保存交易
我们⾸先要把数据以JSON格式存⼊区块链。JSON是⼀种常⽤的跨语⾔的数据交换格式,例如⼀篇博客的JSON表⽰看起来就像这样:
{
"author": "some_author_name",
"content": "Some thoughts that author wants to share",
"timestamp": "The time at which the content was created"
}
在区块链领域,我们经常使⽤ 交易 来代替上⾯说到的数据。因此,为了避免引起混乱并保持⼀致,在这个教程⾥我们将使⽤ 交易 这个术语来表⽰要存⼊区块链的数据。
交易被分批打包进区块,⼀个区块可以包含⼀个或多个交易。包含交易的区块会定期⽣成并加⼊区块链。因为会有很多区块,所以每个区块都应当有⼀个唯⼀的ID。下⾯是我们的Python仿真区块链的Block类定义代码:
class Block:
def __init__(self, index, transactions, timestamp):
"""
Constructor for the `Block` class.
:param index: Unique ID of the block.
:param transactions: List of transactions.
:
param timestamp: Time of generation of the block.
"""
self.index = index
self.timestamp = timestamp
2、Python仿真区块链:为区块添加抗篡改的数字指纹
区块链的⼀个特点就是存储在区块中的交易不可篡改,为了实现这个特性,⾸先需要能够检测出区块数据被篡改。为此⽬的,我们需要使⽤密码学中的哈希(Hash)函数。
哈希函数可以把任意⼤⼩的输⼊数据转换为固定⼤⼩的输出数据,也就是数据的哈希,⽽且不同的输⼊数据(基本上)会得到不同的输出数据,因此可以使⽤输出的哈希作为输⼊数据的标识。⼀个理想的哈希函数具有如下特点:
应当易于计算
应当是确定性的,对于相同的输⼊数据总是⽣成相同的哈希
应当具有均匀随机性,输⼊数据的⼀点变化也会导致输出哈希的显著改变
这样我们就可以保证:
从哈希猜测出输⼊数据是什么基本是不可能的,唯⼀的办法是尝试所有可能的组合
如果同时知道输⼊和输出,那么你可以通过简单地重算来验证哈希是否正确
显然,从输⼊数据推导出哈希很简单,然⽽从哈希推导出输⼊数据则是⼏乎不可能的,这⼀⾮对称性值就是区块链⽤来获取期望的抗篡改能⼒的关键。
⽬前有很多流⾏的哈希函数,下⾯是⼀个使⽤SHA-256哈希函数的Python⽰例:
>>> from hashlib import sha256
>>> data = b"Some variable length data"
>>> sha256(data).hexdigest()
'b919fbbcae38e2bdaebb6c04ed4098e5c70563d2dc51e085f784c058ff208516'
>>> sha256(data).hexdigest() # no matter how many times you run it, the result is going to be the same 256 character string
'b919fbbcae38e2bdaebb6c04ed4098e5c70563d2dc51e085f784c058ff208516'
>>> data = b"Some variable length data2" # Added one character at the end.
'9fcaab521baf8e83f07512a7de7a0f567f6eef2688e8b9490694ada0a3ddeec8'
注意在上⾯的⽰例中,输⼊数据的⼀点变化就得到完全不同的哈希!
在教程的Python仿真区块链项⽬中,我们将把区块哈希保存为区块的⼀个字段,⽤它作为区块数据的数字指纹(Digital Fingerprint),或者说签名(Signature)。
下⾯是计算区块哈希的Python实现代码:
from hashlib import sha256
import json
def compute_hash(block):
"""
Returns the hash of the block instance by first converting it
into JSON string.
"""
block_string = json.dumps(self.__dict__, sort_keys=True)
return sha256(de()).hexdigest()
注意:在⼤多数数字加密货币实现中,区块中的每个交易也需要计算哈希并利⽤⼀个树形结构(merkle树)来计算⼀组交易的根哈希。不过这对于区块链来说并不是必需的,因此我们暂时忽略这⼀特性。
3、Python仿真区块链:将区块⼀个个链接起来
好了,现在我们已经搞定区块类Block的Python实现了,现在来看看如何⽤Ptyhon实现区块链结构。
区块链就是区块的集合,我们可以使⽤Python列表来保存所有的区块。不过这还不够,因为如果有⼈
故意⽤⼀个较早的区块替换掉集合中的新区块还会导致数据被篡改。
我们需要⼀个办法来保证对较早的区块的修改会导致整条区块链的⽆效。⽐特币使⽤的办法是让后⾯区块的哈希依赖于前⾯较早的区块。为将区块链接起来,我们需要在区块结构中增加⼀个新的字段来保存前⼀个区块的哈希:previous_hash。
好了,如果每个区块都通过previous_hash字段链接到前⼀个区块,那么第⼀个区块怎么办?在区块链领域,第⼀个区块被称为创世区块(Genesis Block),可以⼿⼯⽣成创世区块或者使⽤⼀些特定的逻辑。现在让我们为Block类添加previous_hash字段并实现区块链结构定义,下⾯是Blockchain类的
Python实现代码:
from hashlib import sha256
import json
import time
class Block:
def__init__(self, index, transactions, timestamp, previous_hash):
"""
Constructor for the `Block` class.
:param index: Unique ID of the block.
:param transactions: List of transactions.
:param timestamp: Time of generation of the block.
:param previous_hash: Hash of the previous block in the chain which this block is part of.
"""
self.index = index
self.timestamp = timestamp
self.previous_hash = previous_hash # Adding the previous hash field
def compute_hash(self):
"""
Returns the hash of the block instance by first converting it
into JSON string.
"""
block_string = json.dumps(self.__dict__, sort_keys=True) # The string equivalent also considers the previous_hash field now return sha256(de()).hexdigest()
class Blockchain:
def __init__(self):
"""
Constructor for the `Blockchain` class.
"""
self.chain = []
def create_genesis_block(self):
"""
A function to generate genesis block and appends it to
the chain. The block has index 0, previous_hash as 0, and
a valid hash.
"""
genesis_block = Block(0, [], time.time(), "0")
genesis_block.hash = genesis_blockpute_hash()
self.chain.append(genesis_block)
@property
def last_block(self):
"""
A quick pythonic way to retrieve the most recent block in the chain. Note that
the chain will always consist of at least one block (i.e., genesis block)
"""
return self.chain[-1]
现在,如果任何较早的区块被修改,那么:
该较早区块的哈希会变化
这会导致与后⾯区块的previous_hash字段记录的内容不⼀致
由于计算区块哈希的输⼊数据包含了previous_hash字段的内容,因此下⼀个区块的哈希也会变化
最终,从被替换掉的区块开始的整条链都失效了,修复这⼀问题的唯⼀办法是重算整条链。
4、Python仿真区块链:实现⼯作量证明算法
不过还有⼀个问题。如果我们修改了之前的区块,如果重算后⾯的其他区块⾮常简单的话,那么篡改区块链也不是什么难事了。为了避免这⼀问题,我们可以利⽤前⾯提到的哈希函数的⾮对称性来加⼤区块哈希计算⼯作的难度和随机性。我们要做的是:只接受符合特定约束条件的区块哈希。现在让我们增加⼀个约束条件,要求区块哈希的开始部分⾄少有n个0,其中n是⼀个正整数。
我们知道,除⾮改变区块数据的内容,否则区块哈希不会变化,当然我们也不希望修改已有的数据。那么我们该怎么做?很简单!我们再增加⼀些我们可以随便修改的数据就是了。因此我们需要为Block类增加⼀个新的字段nonce,我们可以通过改变这个字段的值来得到不同的区块哈希,直到满⾜指定的约束条件,⽽这时的nonce值就是我们⼯作量的证明。
上⾯的这⼀过程就是⽐特币使⽤的hashcash算法的简化版本。约束条件中指定的前导0的数量决定了我们的⼯作量证明算法的难度:前导0的数量越多,就越难到合适的nonce。
同时,由于哈希函数的⾮对称性,⼯作量证明不容易计算,但是容易进⾏验证。
下⾯是⼯作量证明算法(PoW:Proof of Work)的Python实现代码:
class Blockchain:
# difficulty of PoW algorithm
python货币转换difficulty = 2
"""
Previous code contd..
"""
def proof_of_work(self, block):
"""
Function that tries different values of the nonce to get a hash
that satisfies our difficulty criteria.
"""
< = 0
computed_hash = blockpute_hash()
while not computed_hash.startswith('0' * Blockchain.difficulty):
< += 1
computed_hash = blockpute_hash()
return computed_hash
需要指出的是,没有简单的逻辑可以快速到满⾜约束条件的nonce值,因此只能进⾏暴⼒计算。
5、Python仿真区块链:将区块加⼊区块链
要将区块加⼊区块链,我们⾸先需要验证:
区块中的数据没有被篡改,所提供的⼯作量证明是正确的
交易的顺序是正确的,previous_hash字段指向我们链上最新区块的哈希
现在让我们看⼀下将区块上链的Python实现代码:
class Blockchain:
"""
Previous code contd..
"""
def add_block(self, block, proof):
"""
A function that adds the block to the chain after verification.
Verification includes:
* Checking if the proof is valid.
* The previous_hash referred in the block and the hash of a latest block
in the chain match.
"""
previous_hash = self.last_block.hash
if previous_hash != block.previous_hash:
return False
if not Blockchain.is_valid_proof(block, proof):
return False
block.hash = proof
self.chain.append(block)
return True
def is_valid_proof(self, block, block_hash):
"""
Check if block_hash is valid hash of block and satisfies
the difficulty criteria.
"""
return (block_hash.startswith('0' * Blockchain.difficulty) and
block_hash == blockpute_hash())
6、Python仿真区块链:挖矿
交易⼀开始是保存在未确认交易池中的。将未确认交易放⼊区块并计算⼯作量证明的过程,就是⼴为⼈知的挖矿。⼀旦出了满⾜指定约束条件的nonce,我们就可以说挖出了⼀个可以上链的区块。
在⼤多数数字加密货币中,包括⽐特币,矿⼯都会得到加密货币奖励,以回报其为计算⼯作量证明所投⼊的算⼒。下⾯是我们的挖矿函数的Python实现代码:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论