




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
第python區(qū)塊鏈持久化和命令行接口實(shí)現(xiàn)簡版目錄說明引言選擇數(shù)據(jù)庫couchdbcouchdb的安裝數(shù)據(jù)庫結(jié)構(gòu)序列化持久化區(qū)塊鏈迭代器CLI測試一下
說明
本文根據(jù)/liuchengxu/blockchain-tutorial的內(nèi)容,用python實(shí)現(xiàn)的,但根據(jù)個人的理解進(jìn)行了一些修改,大量引用了原文的內(nèi)容。文章末尾有本節(jié)完整源碼實(shí)現(xiàn)地址。
引言
到目前為止,我們已經(jīng)構(gòu)建了一個有工作量證明機(jī)制的區(qū)塊鏈。有了工作量證明,挖礦也就有了著落。雖然目前距離一個有著完整功能的區(qū)塊鏈越來越近了,但是它仍然缺少了一些重要的特性。在今天的內(nèi)容中,我們會將區(qū)塊鏈持久化到一個數(shù)據(jù)庫中,然后會提供一個簡單的命令行接口,用來完成一些與區(qū)塊鏈的交互操作。本質(zhì)上,區(qū)塊鏈?zhǔn)且粋€分布式數(shù)據(jù)庫,不過,我們暫時先忽略分布式這個部分,僅專注于存儲這一點(diǎn)。
選擇數(shù)據(jù)庫
目前,我們的區(qū)塊鏈實(shí)現(xiàn)里面并沒有用到數(shù)據(jù)庫,而是在每次運(yùn)行程序時,簡單地將區(qū)塊鏈存儲在內(nèi)存中。那么一旦程序退出,所有的內(nèi)容就都消失了。我們沒有辦法再次使用這條鏈,也沒有辦法與其他人共享,所以我們需要把它存儲到磁盤上。
那么,我們要用哪個數(shù)據(jù)庫呢?實(shí)際上,任何一個數(shù)據(jù)庫都可以。在比特幣原始論文中,并沒有提到要使用哪一個具體的數(shù)據(jù)庫,它完全取決于開發(fā)者如何選擇。BitcoinCore,最初由中本聰發(fā)布,現(xiàn)在是比特幣的一個參考實(shí)現(xiàn),它使用的是LevelDB。而我們將要使用的是
couchdb
因?yàn)樗?
簡單易用有一個web的UI界面,方便我們查看豐富的查詢支持良好的python支持
couchdb的安裝
直接安裝,參考/article/202914.htm
docker版couchdb安裝,使用docker-compose安裝couchdb
#couchdb.yaml
version:'2'
services:
couchdb:
image:hyperledger/fabric-couchdb
ports:
-5984:5984
執(zhí)行docker-compose-fcouchdb.yamlup-d即可安裝。
使用http://ip:5984/_utils即可訪問couchdb的后臺管理系統(tǒng)。
數(shù)據(jù)庫結(jié)構(gòu)
在開始實(shí)現(xiàn)持久化的邏輯之前,我們首先需要決定到底要如何在數(shù)據(jù)庫中進(jìn)行存儲。為此,我們可以參考BitcoinCore的做法:
簡單來說,BitcoinCore使用兩個bucket來存儲數(shù)據(jù):
其中一個bucket是blocks,它存儲了描述一條鏈中所有塊的元數(shù)據(jù)另一個bucket是chainstate,存儲了一條鏈的狀態(tài),也就是當(dāng)前所有的未花費(fèi)的交易輸出,和一些元數(shù)據(jù)
此外,出于性能的考慮,BitcoinCore將每個區(qū)塊(block)存儲為磁盤上的不同文件。如此一來,就不需要僅僅為了讀取一個單一的塊而將所有(或者部分)的塊都加載到內(nèi)存中。而我們直接使用couchdb。
在blocks中,key-value為:
keyvalueb+32字節(jié)的blockhashblockindexrecordf+4字節(jié)的filenumberfileinformationrecordl+4字節(jié)的filenumberthelastblockfilenumberusedR+1字節(jié)的boolean是否正在reindexF+1字節(jié)的flagnamelength+flagnamestring1byteboolean:variousflagsthatcanbeonorofft+32字節(jié)的transactionhashtransactionindexrecord
在chainstate,key-value為:
keyvaluec+32字節(jié)的transactionhashunspenttransactionoutputrecordforthattransactionB32字節(jié)的blockhash:theblockhashuptowhichthedatabaserepresentstheunspenttransactionoutputs
詳情可見這里。
因?yàn)槟壳斑€沒有交易,所以我們只需要blocksbucket。另外,正如上面提到的,我們會將整個數(shù)據(jù)庫存儲為單個文件,而不是將區(qū)塊存儲在不同的文件中。所以,我們也不會需要文件編號(filenumber)相關(guān)的東西。最終,我們會用到的鍵值對有:
32字節(jié)的block-hash(轉(zhuǎn)換為16進(jìn)制字符串)-block結(jié)構(gòu)l-鏈中最后一個塊的hash(轉(zhuǎn)換為16進(jìn)制字符串)
這就是實(shí)現(xiàn)持久化機(jī)制所有需要了解的內(nèi)容了。
序列化
為了方便我們查看,這里我們不直接使用二進(jìn)制數(shù)據(jù),而將其轉(zhuǎn)換為16進(jìn)制字符串。所以我們需要對區(qū)塊內(nèi)容進(jìn)行序列化。
讓我們來實(shí)現(xiàn)Block的Serialize方法:
#classBlock
defserialize(self):
return{
"magic_no":self._magic_no,
"block_header":self._block_header.serialize(),
"transactions":self._transactions
直接返回我們需要的數(shù)據(jù)構(gòu)成的字典即可,而block_header則需要進(jìn)一步序列化。它的序列化同樣也只需要返回具體的數(shù)據(jù)字典即可,如下:
#classBlockHeader
defserialize(self):
returnself.__dict__
反序列化則是把信息轉(zhuǎn)換為區(qū)塊對象。
#classBlock
@classmethod
defdeserialize(cls,data):
block_header_dict=data['block_header']
block_header=BlockHeader.deserialize(block_header_dict)
transactions=data["transactions"]
returncls(block_header,transactions)
首先反序列化塊,然后構(gòu)造成一個對象,反序列化Header:
#classBlockHeader
@classmethod
defdeserialize(cls,data):
timestamp=data.get('timestamp','')
prev_block_hash=data.get('pre_block_hash','')
#hash=data.get('hash','')
hash_merkle_root=data.get('hash_merkle_root','')
height=data.get('height','')
nonce=data.get('nonce','')
block_header=cls(hash_merkle_root,height,prev_block_hash)
block_header.timestamp=timestamp
block_header.nonce=nonce
returnblock_header
持久化
持久化要做的事情就是把區(qū)塊數(shù)據(jù)寫入到數(shù)據(jù)庫中,則我們要做的事情有:
檢查數(shù)據(jù)庫是否已經(jīng)有了一個區(qū)塊鏈如果沒有則創(chuàng)建一個,創(chuàng)建創(chuàng)世塊并將l指向這個塊的哈希添加一個區(qū)塊,將l指向新添加的區(qū)塊哈希
創(chuàng)建創(chuàng)世塊如下:
#classBlockChain:
defnew_genesis_block(self):
if'l'notinself.db:
genesis_block=Block.new_genesis_block('genesis_block')
genesis_block.set_header_hash()
self.db.create(genesis_block.block_header.hash,genesis_block.serialize())
self.set_last_hash(genesis_block.block_header.hash)
添加一個區(qū)塊如下:
defadd_block(self,transactions):
addablocktoblock_chain
last_block=self.get_last_block()
prev_hash=last_block.get_header_hash()
height=last_block.block_header.height+1
block_header=BlockHeader('',height,prev_hash)
block=Block(block_header,transactions)
block.mine()
block.set_header_hash()
self.db.create(block.block_header.hash,block.serialize())
last_hash=block.block_header.hash
self.set_last_hash(last_hash)
對couchdb的操作的簡單封裝如下:
classDB(Singleton):
def__init__(self,db_server_url,db_name='block_chain'):
self._db_server_url=db_server_url
self._server=couchdb.Server(self._db_server_url)
self._db_name=db_name
self._db=None
@property
defdb(self):
ifnotself._db:
try:
self._db=self._server[self._db_name]
exceptcouchdb.ResourceNotFound:
self._db=self._server.create(self._db_name)
returnself._db
defcreate(self,id,data):
self.db[id]=data
returnid
def__getattr__(self,name):
returngetattr(self.db,name)
def__contains__(self,name):
returnself.db.__contains__(name)
def__getitem__(self,key):
returnself.db[key]
def__setitem__(self,key,value):
self.db[key]=value
區(qū)塊鏈迭代器
由于我們現(xiàn)在使用了數(shù)據(jù)庫存儲,不再是數(shù)組,那么我們便失去了迭代打印區(qū)塊鏈的特性,我們需要重寫__getitem__以獲得該特性,實(shí)現(xiàn)如下:
#classBlockChain(object):
def__getitem__(self,index):
last_block=self.get_last_block()
height=last_block.block_header.height
ifindex=height:
returnself.get_block_by_height(index)
else:
raiseIndexError('Indexisoutofrange')
#classBlockChain(object):
defget_block_by_height(self,height):
Getablockbyheight
query={"selector":{"block_header":{"height":height}}}
docs=self.db.find(query)
block=Block(None,None)
fordocindocs:
block.deserialize(doc)
break
returnblock
根據(jù)區(qū)塊高度獲取對應(yīng)的區(qū)塊,此處是利用了couchdb的mongo_query的富查詢來實(shí)現(xiàn)。
CLI
到目前為止,我們的實(shí)現(xiàn)還沒有提供一個與程序交互的接口。是時候加上交互了:
這里我們使用argparse來解析參數(shù):
defnew_parser():
parser=argparse.ArgumentParser()
sub_parser=parser.add_subparsers(help='commands')
#Aprintcommand
print_parser=sub_parser.add_parser(
'print',help='Printalltheblocksoftheblockchain')
print_parser.add_argument('--print',dest='print',action='store_true')
#Aaddcommand
add_parser=sub_parser.add_parser(
'addblock',help='Printalltheblocksoftheblockchain')
add_parser.add_argument(
'--data',type=str,dest='add_data',help='blockdata')
returnparser
defprint_chain(bc):
forblockinbc:
print(block)
defadd_block(bc,data):
bc.add_block(data)
print("Success!")
defmain():
parser=new_parser()
args=parser.parse_args()
bc=BlockChain()
ifhasattr(args,'print'):
print_chain(bc)
ifhasattr(args,'add_data'):
add_block(bc,args.add_data)
if__name__=="__main__":
main()
測試一下
#創(chuàng)世塊創(chuàng)建
$python3main.py
Mininganewblock
Foundnonce==19ash_hex==047f213bcb01f1ffbcdfafad57ffeead0e86924cf439594020da47ff2508291c
Document'l'@'191-2f44a1493638684d9e000d8dd105192a'{'hash':'e4f7adac65bcbb304af21be52a1b52bb28c0205a3746d63453d9e8c182de927a'}
Mininganewblock
Foundnonce==1ash_hex==0df1ac18c84a8e524d6fe49cb04aae9af02dd85addc4ab21ac13f9d0d7ffe769
Document'l'@'192-168ff7ea493ca53c66690985deb5b7ac'{'hash':'01015004e21d394b1a6574eb81896e1c800f18aa22997e96b79bca22f7821a67'}
Block(_block_header=BlockHeader(timestamp='1551317137.2814202',hash_merkle_root='',prev_block_hash='',hash='f20f3c74c831d03aaa2291af23e607896a61809b5ced222483b46795a456a1c5',nonce=None,height=0))
Block(_block_header=BlockHeader(timestamp='1551317137.358466',hash_merkle_root='',prev_block_hash='f20f3c
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 康養(yǎng)聯(lián)合體合同協(xié)議書
- 2025年教育信息化2.0時代教師信息技術(shù)與課程建設(shè)研究報告
- 2025年氫燃料電池汽車關(guān)鍵零部件國產(chǎn)化關(guān)鍵技術(shù)突破與產(chǎn)業(yè)鏈升級策略報告
- 合同協(xié)議書概念界定
- 總包合同協(xié)議書范本
- 放學(xué)接孩子合同協(xié)議書
- 河堤承包合同協(xié)議書
- 感情合同協(xié)議書
- 廚師臨時聘用合同協(xié)議書
- 合同協(xié)議書錯了
- 工商企業(yè)管理專業(yè)模擬實(shí)訓(xùn)報告
- 煙花爆竹事故應(yīng)急處置
- 專利法全套ppt課件(完整版)
- 八年級英語15篇完形填空(附答案)
- 《馬克思主義與社會科學(xué)方法論》課件第四講 社會矛盾研究方法
- 會寶嶺選礦廠集中控制技術(shù)方案
- 第13講巧解弦圖與面積
- 畢業(yè)設(shè)計(論文)-CK6150總體及縱向進(jìn)給和尾座部件的設(shè)計
- 初中物理新課程標(biāo)準(zhǔn)學(xué)生必做的20個實(shí)驗(yàn)
- 天津市公路工程工地試驗(yàn)室管理規(guī)定
- 小學(xué)心理健康《欣賞我自己》教學(xué)設(shè)計
評論
0/150
提交評論