使用Python从零开始开发区块链应用程序您将能够使用Flask微框架Word文档格式.docx
- 文档编号:19535554
- 上传时间:2023-01-07
- 格式:DOCX
- 页数:13
- 大小:146.06KB
使用Python从零开始开发区块链应用程序您将能够使用Flask微框架Word文档格式.docx
《使用Python从零开始开发区块链应用程序您将能够使用Flask微框架Word文档格式.docx》由会员分享,可在线阅读,更多相关《使用Python从零开始开发区块链应用程序您将能够使用Flask微框架Word文档格式.docx(13页珍藏版)》请在冰豆网上搜索。
我们将通过实现一个区块链来深入剖析它。
让我们开始吧。
关于该应用程序
首先定义一下我们将要构建的应用程序的用途。
我们的目的是构建一个允许用户共享信息的简单网站。
因为内容将存储在区块链中,所以它无法更改且会永远存在。
我们将采用自下而上的实现方式。
首先定义我们将存储在区块链中的数据的结构。
一篇帖子(任何用户在我们的应用程序上发布的一条消息)将由3个基本要素来标识:
.1内容
2.作者
3.时间戳
将事务存储到区块中
我们将采用一种广泛使用的格式来将数据存储在区块链中:
JSON。
以下是一篇存储在区块链中的帖子的格式:
{
"
author"
:
"
some_author_name"
content"
Somethoughtsthatauthorwantstoshare"
timestamp"
Thetimeatwhichthecontentwascreated"
}
术语“数据”通常在互联网上被“事务”一词所取代。
所以,为了避免混淆并保持一致,我们将使用术语“事务”来表示在我们的示例应用程序中发布的数据。
事务被打包到区块中。
一个区块可以包含一个或许多个事务。
包含事务的区块频繁地生成并添加到区块链中。
因为可能有多个区块,所以每个区块都应有一个唯一ID:
classBlock:
def__init__(self,index,transactions,timestamp):
self.index=[]
self.transactions=transactions
self.timestamp=timestamp
让区块不可更改
我们希望检测出对区块内存储的数据的任何篡改。
在区块链中,这是使用一个哈希函数来实现的。
哈希函数接受任何大小的数据并生成固定大小的数据,该结果通常用于识别输入。
下面是Python中的一个使用sha256哈希函数的示例:
>
fromhashlibimportsha256
data="
Somevariablelengthdata"
sha256(data).hexdigest()
'
b919fbbcae38e2bdaebb6c04ed4098e5c70563d2dc51e085f784c058ff208516'
sha256(data).hexdigest()#nomatterhowmanytimesyourunit,the
resultisgoingtobethesame256characterstring
一个理想的哈希函数包括以下特征:
它应该很容易计算。
哪怕只更改数据中的一个位,哈希值也应该完全发生变化。
应该无法根据输出哈希值猜出输入。
您现在知道哈希函数是什么了吧。
我们将每个区块的哈希值都存储在Block对象内的一个字段中,其作用类似于它所包含的数据的数字指纹:
fromhashlibimportsha256
importjson
defcompute_hash(block):
Afunctionthatcreatesthehashoftheblock.
block_string=json.dumps(self.__dict__,sort_keys=True)
returnsha256(block_string.encode()).hexdigest()
备注:
在大多数加密货币中,甚至对区块中的各个事务也进行了哈希运算,从而形成一棵哈希树(也称为二进制哈希树),这棵树的根可以用作区块的哈希值。
它不是区块链正常运作的必要条件,所以我们将省略它,以保持代码简洁。
链接区块
我们已设置了区块。
区块链应该是一个区块集合。
我们可以将所有区块都存储在Python列表中(等效于数组)。
但这还不够,因为如果有人故意替换了集合中的一个区块该怎么办?
用修改过的事务创建一个新的区块,计算哈希值,然后替换任何旧区块,这在我们的当前实现中并不是什么难事,因为我们会保持区块的不可更改性和顺序。
我们需要采用某种途径来确保对过去的区块的任何更改都会造成整个链的失效。
一种方法是通过哈希值将区块链接起来。
谈到链接,我们指的是将前一个区块的哈希值包含在当前区块中。
所以,如果任何以前的区块的内容发生更改,该区块的哈希值也会发生更改,导致与下一个区块中的previous_hash字段不匹配。
每个区块都通过previous_hash字段链接到前一个区块,但是第一个区块该如何处理?
第一个区块称为创始区块,大多数情况下,它是手动生成或通过某种独特逻辑生成的。
让我们将previous_hash字段添加到Block类中,并实现我们的Blockchain类的初始结构(参见清单1)。
清单1.我们的Blockchain类的初始结构
importtime
def__init__(self,index,transactions,timestamp,previous_hash):
self.index=index
self.previous_hash=previous_hash
defcompute_hash(self):
这是我们的Blockchain类:
classBlockchain:
def__init__(self):
self.unconfirmed_transactions=[]#datayettogetintoblockchain
self.chain=[]
self.create_genesis_block()
defcreate_genesis_block(self):
Afunctiontogenerategenesisblockandappendsitto
thechain.Theblockhasindex0,previous_hashas0,and
avalidhash.
genesis_block=Block(0,[],time.time(),"
0"
)
genesis_block.hash=genesis_pute_hash()
self.chain.append(genesis_block)
@property
deflast_block(self):
returnself.chain[-1]
实现工作量证明算法
但这里存在一个问题。
如果我们更改前一个区块,我们可以非常轻松地重新计算所有后续区块的哈希值,并创建一个不同的有效区块链。
为了预防这种情况,我们必须让计算哈希值的任务变得困难和随机化。
以下是我们实现此操作的方式。
我们不会接受任何区块哈希值,而是对它添加某种约束。
让我们来添加一种约束:
哈希值应以两个前导零开始。
另外我们知道,除非更改区块的内容,否则哈希值不会发生更改。
所以我们将在区块中引入一个称为随机数的新字段。
随机数会不断变化,直到我们获得满足约束条件的哈希值。
前导零的数量(在我们的例子中为值2)决定了工作量证明算法的“难度”。
您可能还注意到,我们的工作量证明很难计算,但在我们确定随机数后很容易验证(对于验证,您只需要再次运行哈希函数即可):
#difficultyofPoWalgorithm
difficulty=2
Previouscodecontd..
defproof_of_work(self,block):
Functionthattriesdifferentvaluesofnoncetogetahash
thatsatisfiesourdifficultycriteria.
block.nonce=0
computed_hash=pute_hash()
whilenotcomputed_hash.startswith('
0'
*Blockchain.difficulty):
block.nonce+=1
returncomputed_hash
请注意,没有明确的逻辑来快速确定随机数;
只能通过暴力破解。
将区块添加到链中
要将区块添加到链中,首先需要验证所提供的工作量证明是否正确,以及要添加的区块的previous_hash字段是否指向链中的最新区块的哈希值。
让我们看看将区块添加到链中的代码:
defadd_block(self,block,proof):
Afunctionthataddstheblocktothechainafterverification.
previous_hash=self.last_block.hash
ifprevious_hash!
=block.previous_hash:
returnFalse
ifnotself.is_valid_proof(block,proof):
block.hash=proof
self.chain.append(block)
returnTrue
defis_valid_proof(self,block,block_hash):
Checkifblock_hashisvalidhashofblockandsatisfies
thedifficultycriteria.
return(block_hash.startswith('
*Blockchain.difficulty)and
block_hash==pute_hash())
挖矿
事务最初存储在一个未确认事务池中。
将未确认事务放入区块中并计算工作量证明的过程被称为区块挖矿。
一旦找到满足我们的约束条件的随机数,我们就可以说挖到了一个区块,这个区块就会放入区块链中。
在大多数加密货币(包括比特币)中,作为对耗费算力来计算工作量证明的奖励,矿工可以获得一些加密货币。
以下是我们的挖矿函数的格式:
Previouscodecontd...
defadd_new_transaction(self,transaction):
self.unconfirmed_transactions.append(transaction)
defmine(self):
Thisfunctionservesasaninterfacetoaddthepending
transactionstotheblockchainbyaddingthemtotheblock
andfiguringoutProofofWork.
ifnotself.unconfirmed_transactions:
last_block=self.last_block
new_block=Block(index=last_block.index+1,
transactions=self.unconfirmed_transactions,
timestamp=time.time(),
previous_hash=last_block.hash)
proof=self.proof_of_work(new_block)
self.add_block(new_block,proof)
self.unconfirmed_transactions=[]
returnnew_block.index
好了,我们的工作差不多完成了。
您可以在GitHub上查看截至目前的合并代码。
创建接口
现在为我们的节点创建接口,以便与其他对等节点以及我们将要构建的应用程序进行交互。
我们将使用Flask创建一个REST-API来与我们的节点进行交互。
以下是它的代码:
fromflaskimportFlask,request
importrequests
app=Flask(__name__)
#thenode'
scopyofblockchain
blockchain=Blockchain()
我们的应用程序需要一个端点来提交新事务。
我们的应用程序将使用此端点将新数据(帖子)添加到区块链中:
@app.route('
/new_transaction'
methods=['
POST'
])
defnew_transaction():
tx_data=request.get_json()
required_fields=["
"
]
forfieldinrequired_fields:
ifnottx_data.get(field):
return"
Invlaidtransactiondata"
404
tx_data["
]=time.time()
blockchain.add_new_transaction(tx_data)
Success"
201
下面是返回节点的链副本的端点。
我们的应用程序将使用此端点来查询要显示的所有帖子:
/chain'
GET'
defget_chain():
chain_data=[]
forblockinblockchain.chain:
chain_data.append(block.__dict__)
returnjson.dumps({"
length"
len(chain_data),
chain"
chain_data})
下面是请求节点挖掘未确认事务(如果有)的端点。
我们将使用此端点从我们的应用程序自身发起一个挖矿命令:
/mine'
defmine_unconfirmed_transactions():
result=blockchain.mine()
ifnotresult:
Notransactionstomine"
Block#{}ismined."
.format(result)
#endpointtoqueryunconfirmedtransactions
/pending_tx'
defget_pending_tx():
returnjson.dumps(blockchain.unconfirmed_transactions)
app.run(debug=True,port=8000)
现在,您可以体验一下我们的区块链,创建一些事务,然后使用诸如cURL或Postman之类的工具来挖掘它们。
建立共识和去中心化
目前为止,我们实现的代码只能在单个计算机上运行。
即使通过哈希值链接了区块,我们仍然不能信任单个实体。
我们需要多个节点来维护我们的区块链。
所以让我们创建一个端点,以便让一个节点了解网络中的其他对等节点:
#theaddresstootherparticipatingmembersofthenetwork
peers=set()
#endpointtoaddnewpeerstothenetwork.
/add_nodes'
defregister_new_peers():
nodes=request.get_json()
ifnotnodes:
Invaliddata"
400
fornodeinnodes:
peers.add(node)
您可能已经认识到,在多节点方面存在一个问题。
由于故意操纵或意外的原因,一些节点的链副本可能有所不同。
在这种情况下,我们需要商定采用链的某个版本,以维持整个系统的完整性。
我们需要达成共识。
一种简单的共识算法可能是,在网络中的不同参与者构成的链出现分歧时,商定采用最长的有效链。
选择此方法的理由是,最长的链是对已完成的最多工作量的有效估算:
defconsensus():
Oursimpleconsensusalgorithm.如果找到一个更长的有效链,则用它替换我们的链。
globalblockchain
longest_chain=None
current_len=len(blockchain)
fornodeinpeers:
response=requests.get('
http:
//{}/chain'
.format(node))
length=response.json()['
length'
chain=response.json()['
chain'
iflength>
current_lenandblockchain.check_chain_validity(chain):
current_len=length
longest_chain=chain
iflongest_chain:
blockchain=longest_chain
最后,我们需要开发一种方法,让任何节点向网络宣布它已经挖到一个区块,以便每个人都能更新他们的区块链,并继续挖掘其他事务。
其他节点可以轻松地验证工作量证明,并将它添加到各自的链中:
#endpointtoaddablockminedbysomeoneelsetothenode'
schain.
/add_block'
defvalidate_and_add_block():
block_data=request.get_json()
block=Block(block_data["
index"
],block_data["
transactions"
],
block_data["
block_data["
previous_hash"
]])
proof=block_data['
hash'
added=blockchain.add_block(block,proof)
ifnotadded:
Theblockwasdiscardedbythenode"
Blockaddedtothechain"
defannounce_new_block(block):
forpeerinpeers:
url="
//{}/add_block"
.format(peer)
requests.post(url,data=json.dumps(block.__dict__,sort_keys=True))
announce_new_block方法应在节点挖到每个区块后调用,以便对等节点能将该区块添加到自己的链中。
构建应用程序
好了,后端都设置好了。
您可以在GitHub上查看到目前为止的代码。
现在,是时候创建应用程序的接口了。
我们使用了Jinja2模板来呈现网页和一些CSS,让页面看起来美观一些。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用 Python 从零开始 开发 区块 应用程序 将能 Flask 框架