python區(qū)塊鏈持久化和命令行接口實(shí)現(xiàn)簡版_第1頁
python區(qū)塊鏈持久化和命令行接口實(shí)現(xiàn)簡版_第2頁
python區(qū)塊鏈持久化和命令行接口實(shí)現(xiàn)簡版_第3頁
python區(qū)塊鏈持久化和命令行接口實(shí)現(xiàn)簡版_第4頁
python區(qū)塊鏈持久化和命令行接口實(shí)現(xiàn)簡版_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論