當前位置:
首頁 > 最新 > 【Golang區塊鏈開發003】區塊序列化存儲

【Golang區塊鏈開發003】區塊序列化存儲

目錄

區塊序列化

BoltDB資料庫使用

通過BoltDB存儲區塊

區塊鏈基於BoltDB存儲區塊

遍歷區塊鏈區塊信息

通過迭代器遍歷區塊信息

一.區塊序列化


import(

"encoding/gob"

)


4.1 區塊定義

//定義區塊

typeBlockstruct{

//1.區塊高度,也就是區塊的編號,第幾個區塊

Heightint64

//2.上一個區塊的Hash值

PreBlockHash[]byte

//3.交易數據(最終都屬於transaction 事務)

Data[]byte

//4.創建時間的時間戳

TimeStampint64

//5.當前區塊的Hash值

Hash[]byte

//6.Nonce 隨機數,用於驗證工作量證明

Nonceint64

}4.2 序列化區塊

// 定義Block的方法Serialize(),將區塊序列化成位元組數組

func(block*Block)Serialize() []byte{

//1.定義result的位元組buffer,用於存儲序列化後的區塊

varresultbytes.Buffer

//2.初始化序列化對象encoder

encoder:=gob.NewEncoder(&result)

//3.通過Encode()方法對區塊進行序列化

err:=encoder.Encode(block)

iferr!=nil{

log.Panic(err)

}

//4.返回result的位元組數組

returnresult.Bytes()

}4.3 反序列化位元組數組

//定義函數DeserializeBlock(),傳入參數為位元組數組,返回值為Block

funcDeserializeBlock(blockBytes[]byte)*Block{

//1.定義一個Block指針對象

varblockBlock

//2.初始化反序列化對象decoder

decoder:=gob.NewDecoder(bytes.NewReader(blockBytes))

//3.通過Decode()進行反序列化

err:=decoder.Decode(&block)

iferr!=nil{

log.Panic(err)

}

//4.返回block對象

return&block

}

二.BoltDB資料庫使用


使用Go語言編寫

不需要伺服器即可運行

支持數據結構

直接使用API存取數據,沒有查詢語句;

支持完全可序列化的ACID事務,這個特性比LevelDB強;

數據保存在內存映射的文件里。沒有wal、線程壓縮和垃圾回收;

通過COW技術,可實現無鎖的讀寫並發,但是無法實現無鎖的寫寫並發,這就註定了讀性能超高,但寫性能一般,適合與讀多寫少的場景。

BoltDB是一個Key/Value(鍵/值)存儲,這意味著沒有像SQL RDBMS(MySQL,PostgreSQL等)中的表,沒有行,沒有列。相反,數據作為鍵值對存儲(如在Golang Maps中)。鍵值對存儲在Buckets中,它們旨在對相似的對進行分組(這與RDBMS中的表類似)。因此,為了獲得Value(值),需要知道該Value所在的桶和鑰匙。


//通過go get下載並import

import"github.com/boltdb/bolt"3.1 打開或創建資料庫

db,err:=bolt.Open("my.db",0600,nil)

iferr!=nil{

log.Fatal(err)

}

deferdb.Close()

執行注意點

如果通過goland程序運行創建的my.db會保存在$GOPATH /src/Project目錄下如果通過go build main.go ; ./main 執行生成的my.db,會保存在當前目錄$GOPATH /src/Project/package下3.2 資料庫操作3.2.1 創建資料庫表與數據寫入操作

//1. 調用Update方法進行數據的寫入

err=db.Update(func(tx*bolt.Tx)error{

//2.通過CreateBucket()方法創建BlockBucket(表),初次使用創建

b,err:=tx.CreateBucket([]byte("BlockBucket"))

iferr!=nil{

returnfmt.Errorf("Create bucket :%s",err)

}

//3.通過Put()方法往表裡面存儲一條數據(key,value),注意類型必須為[]byte

ifb!=nil{

err:=b.Put([]byte("l"), []byte("Send $100 TO Bruce"))

iferr!=nil{

log.Panic("數據存儲失敗..")

}

}

returnnil

})

//數據Update失敗,退出程序

iferr!=nil{

log.Panic(err)

}3.2.2 數據寫入

//1.打開資料庫

db,err:=bolt.Open("my.db",0600,nil)

iferr!=nil{

log.Fatal(err)

}

deferdb.Close()

err=db.Update(func(tx*bolt.Tx)error{

//2.通過Bucket()方法打開BlockBucket表

b:=tx.Bucket([]byte("BlockBucket"))

//3.通過Put()方法往表裡面存儲數據

ifb!=nil{

err:=b.Put([]byte("l"), []byte("Send $200 TO Fengyingcong"))

err=b.Put([]byte("ll"), []byte("Send $100 TO Bruce"))

iferr!=nil{

log.Panic("數據存儲失敗..")

}

}

returnnil

})

//更新失敗

iferr!=nil{

log.Panic(err)

}3.2.3 數據讀取

//1.打開資料庫

db,err:=bolt.Open("my.db",0600,nil)

iferr!=nil{

log.Fatal(err)

}

deferdb.Close()

//2.通過View方法獲取數據

err=db.View(func(tx*bolt.Tx)error{

//3.打開BlockBucket表,獲取表對象

b:=tx.Bucket([]byte("BlockBucket"))

//4.Get()方法通過key讀取value

ifb!=nil{

data:=b.Get([]byte("l"))

fmt.Printf("%s
",data)

data=b.Get([]byte("ll"))

fmt.Printf("%s
",data)

}

returnnil

})

iferr!=nil{

log.Panic(err)

}

三.通過BoltDB存儲區塊


//1.創建一個區塊對象block

block:=BLC.NewBlock("Send $500 to Tom",1, []byte{,,,,,,,,,,,,,,,})

//2. 列印區塊對象相關信息

fmt.Printf("區塊的Hash信息為: %x
",block.Hash)

fmt.Printf("區塊的數據信息為: %v
",string(block.Data))

fmt.Printf("區塊的隨機數為: %d
",block.Nonce)

//3. 打開資料庫

db,err:=bolt.Open("my.db",0600,nil)

iferr!=nil{

log.Fatal(err)

}

deferdb.Close()

//4. 更新數據

err=db.Update(func(tx*bolt.Tx)error{

//4.1 打開BlockBucket表對象

b:=tx.Bucket([]byte("blocks"))

//4.2 如果表對象不存在,創建表對象

ifb==nil{

b,err=tx.CreateBucket([]byte("blocks"))

iferr!=nil{

log.Panic("Block Table Create Failed")

}

}

//4.3 往表裡面存儲一條數據(key,value)

err=b.Put([]byte("l"),block.Serialize())

iferr!=nil{

log.Panic("數據存儲失敗..")

}

returnnil

})

//更新失敗,返回錯誤

iferr!=nil{

log.Panic("數據更新失敗")

}

//5. 查看數據

err=db.View(func(tx*bolt.Tx)error{

//5.1打開BlockBucket表對象

b:=tx.Bucket([]byte("blocks"))

ifb!=nil{

//5.2 取出key=「l」對應的value

blockData:=b.Get([]byte("l"))

//5.3反序列化

block:=BLC.DeserializeBlock(blockData)

//6. 列印區塊對象相關信息

fmt.Printf("區塊的Hash信息為: %x
",block.Hash)

fmt.Printf("區塊的數據信息為: %v
",string(block.Data))

fmt.Printf("區塊的隨機數為: %d
",block.Nonce)

}

returnnil

})

//數據查看失敗

iferr!=nil{

log.Panic("數據更新失敗")

}

四.區塊鏈基於BoltDB存儲區塊


import(

"time"

"bytes"

"encoding/gob"//編碼解碼庫

"log"

)1.2 定義區塊屬性

//定義區塊屬性

typeBlockstruct{

//1.區塊高度,也就是區塊的編號,第幾個區塊

Heightint64

//2.上一個區塊的Hash值

PreBlockHash[]byte

//3.交易數據(最終都屬於transaction 事務)

Data[]byte

//4.創建時間的時間戳

TimeStampint64

//5.當前區塊的Hash值

Hash[]byte

//6.Nonce 隨機數,用於驗證工作量證明

Nonceint64

}1.3 序列化區塊

// 序列化區塊(Block類對象轉成位元組數組)

func(block*Block)Serialize() []byte{

varresultbytes.Buffer

encoder:=gob.NewEncoder(&result)

err:=encoder.Encode(block)

iferr!=nil{

log.Panic(err)

}

returnresult.Bytes()

}1.4 反序列化

//反序列化

funcDeserializeBlock(blockBytes[]byte)*Block{

varblockBlock

decoder:=gob.NewDecoder(bytes.NewReader(blockBytes))

err:=decoder.Decode(&block)

iferr!=nil{

log.Panic(err)

}

return&block

}1.5 創建區塊

//傳入參數data,height,PreBlockHash,返回值*Block

funcNewBlock(datastring,heightint64,PreBlockHash[]byte)*Block{

//根據傳入參數創建區塊

block:=&Block{

height,

PreBlockHash,

[]byte(data),

time.Now().Unix(),

nil,

,

}

//調用工作量證明的方法,並且返回有效的Hash和Nonce值

//創建pow對象

pow:=NewProofOfWork(block)

//通過Run()方法進行挖礦驗證

hash,nonce:=pow.Run(height)

//將Nonce,Hash賦值給類對象屬性

block.Hash=hash[:]

block.Nonce=nonce

returnblock

}1.6 創建創世區塊

//單獨為創世區塊定義的函數

funcCreateGenesisBlock(datastring)*Block{

returnNewBlock(data,1, []byte{,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,})

}1.7 代碼整合

packageBLC

import(

"time"

"bytes"

"encoding/gob"

"log"

)

//定義區塊

typeBlockstruct{

//1.區塊高度,也就是區塊的編號,第幾個區塊

Heightint64

//2.上一個區塊的Hash值

PreBlockHash[]byte

//3.交易數據(最終都屬於transaction 事務)

Data[]byte

//4.創建時間的時間戳

TimeStampint64

//5.當前區塊的Hash值

Hash[]byte

//6.Nonce 隨機數,用於驗證工作量證明

Nonceint64

}

// 序列化區塊(Block類對象轉成位元組數組)

func(block*Block)Serialize() []byte{

varresultbytes.Buffer

encoder:=gob.NewEncoder(&result)

err:=encoder.Encode(block)

iferr!=nil{

log.Panic(err)

}

returnresult.Bytes()

}

//反序列化,位元組數組==>區塊

funcDeserializeBlock(blockBytes[]byte)*Block{

varblockBlock

decoder:=gob.NewDecoder(bytes.NewReader(blockBytes))

err:=decoder.Decode(&block)

iferr!=nil{

log.Panic(err)

}

return&block

}

//1. 創建新的區塊

funcNewBlock(datastring,heightint64,PreBlockHash[]byte)*Block{

//創建區塊

block:=&Block{

height,

PreBlockHash,

[]byte(data),

time.Now().Unix(),

nil,

,

}

//創建pow對象

pow:=NewProofOfWork(block)

//調用工作量證明的方法,並且返回有效的Hash和Nonce值

hash,nonce:=pow.Run(height)

block.Hash=hash[:]

block.Nonce=nonce

returnblock

}

//2.生成創世區塊

funcCreateGenesisBlock(datastring)*Block{

returnNewBlock(data,1, []byte{,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,})

}


2.1 導入相關庫

import(

"math/big"//導入big包

"bytes"

"crypto/sha256"//導入sha256密碼庫

"fmt"

"time"

)

關於big包的說明

使用Go語言中的float64類型進行浮點運算,返回結果將精確到15位,足以滿足大多數的任務。當對超出int64或者uint64 類型這樣的大數進行計算時,如果對精度沒有要求,float32 或者 float64 可以勝任,但如果對精度有嚴格要求的時候,我們不能使用浮點數,在內存中它們只能被近似的表示。對於整數的高精度計算 Go 語言中提供了 big 包。其中包含了 math 包:有用來表示大整數的 big.Int 和表示大有理數的 big.Rat 類型(可以表示為 2/5 或 3.1416 這樣的分數,而不是無理數或 π)。這些類型可以實現任意位類型的數字,只要內存足夠大。缺點是更大的內存和處理開銷使它們使用起來要比內置的數字類型慢很多。大的整型數字是通過 big.NewInt(n) 來構造的,其中 n 為 int64 類型整數。而大有理數是用過 big.NewRat(N,D) 方法構造。N(分子)和 D(分母)都是 int64 型整數。因為 Go 語言不支持運算符重載,所以所有大數字類型都有像是 Add() 和 Mul() 這樣的方法。它們作用於作為 receiver 的整數和有理數,大多數情況下它們修改 receiver 並以 receiver 作為返回結果。因為沒有必要創建 big.Int 類型的臨時變數來存放中間結果,所以這樣的運算可通過內存鏈式存儲。2.2 定義POW結構體屬性

typeProofOfWorkstruct{

Block*Block//當前要驗證的區塊

target*big.Int//大數存儲,區塊難度

}2.3 定義常量 TargetBit

//256位Hash裡面至少要有16個零0000 0000 0000 0000

constTargetBit=162.4 拼接區塊屬性

func(pow*ProofOfWork)prePareData(nonceint) []byte{

data:=bytes.Join(

[][]byte{

pow.Block.PreBlockHash,

pow.Block.Data,

IntToHex(pow.Block.TimeStamp),

IntToHex(int64(TargetBit)),

IntToHex(int64(nonce)),

IntToHex(int64(pow.Block.Height)),

},

[]byte{},

)

returndata

}2.5 創建工作量證明對象

funcNewProofOfWork(block*Block)*ProofOfWork{

//1.創建初始值為1的target

target:=big.NewInt(1)

//2.左移256-TargetBit

target=target.Lsh(target,256-TargetBit)

return&ProofOfWork{block,target}

}2.6 通過POW類對象Run方法挖礦

func(proofOfWork*ProofOfWork)Run(numint64) ([]byte,int64) {

//初始化隨機數nonce為0

nonce:=

//存儲新生成的hash值

varhashIntbig.Int

//存儲hash值

varhash[32]byte

for{

//1. 將Block的屬性拼接成位元組數組,注意,參數為Nonce

databytes:=proofOfWork.prePareData(nonce)

//2.將拼接後的位元組數組生成Hash

hash=sha256.Sum256(databytes)

//3. 將hash存儲至hashInt

hashInt.SetBytes(hash[:])

//4.判斷hashInt是否小於Block裡面的Target

// Cmp compares x and y and returns:

//

// -1 if x

// 0 if x == y

// +1 if x > y

//此處需要滿足hashInt(y)小於設置的target(x)即 x > y,則挖礦成功

ifproofOfWork.target.Cmp(&hashInt)==1{

fmt.Printf("第%d個區塊,挖礦成功:%x
",num,hash)

fmt.Println(time.Now())

time.Sleep(time.Second*2)

//挖礦成功,退出循環

break

}

nonce++

}

returnhash[:],int64(nonce)

}2.7 代碼整合

packageBLC

import(

"math/big"

"bytes"

"crypto/sha256"

"fmt"

"time"

)

typeProofOfWorkstruct{

Block*Block//當前要驗證的區塊

target*big.Int//大數存儲,區塊難度

}

//數據拼接,返回位元組數組

func(pow*ProofOfWork)prePareData(nonceint) []byte{

data:=bytes.Join(

[][]byte{

pow.Block.PreBlockHash,

pow.Block.Data,

IntToHex(pow.Block.TimeStamp),

IntToHex(int64(TargetBit)),

IntToHex(int64(nonce)),

IntToHex(int64(pow.Block.Height)),

},

[]byte{},

)

returndata

}

//256位Hash裡面至少要有16個零0000 0000 0000 0000

constTargetBit=16

//判斷挖礦得到的區塊是否有效

func(proofOfWork*ProofOfWork)IsValid()bool{

//1.proofOfWork.Block.Hash

//2.proofOfWork.Target

varhashIntbig.Int

hashInt.SetBytes(proofOfWork.Block.Hash)

ifproofOfWork.target.Cmp(&hashInt)==1{

returntrue

}

returnfalse

}

//創建新的工作量證明對象

funcNewProofOfWork(block*Block)*ProofOfWork{

/*1.創建初始值為1的target

0000 0001

8 - 2

*/

target:=big.NewInt(1)

//2.左移256-targetBit

target=target.Lsh(target,256-TargetBit)

return&ProofOfWork{block,target}

}

func(proofOfWork*ProofOfWork)Run(numint64) ([]byte,int64) {

nonce:=

varhashIntbig.Int//存儲新生成的hash值

varhash[32]byte

for{

//1. 將Block的屬性拼接成位元組數組

databytes:=proofOfWork.prePareData(nonce)

//2.生成Hash

hash=sha256.Sum256(databytes)

//fmt.Printf("挖礦中..%x
", hash)

//3. 將hash存儲至hashInt

hashInt.SetBytes(hash[:])

//4.判斷hashInt是否小於Block裡面的target

// Cmp compares x and y and returns:

//

// -1 if x

// 0 if x == y

// +1 if x > y

//需要hashInt(y)小於設置的target(x)

ifproofOfWork.target.Cmp(&hashInt)==1{

//fmt.Println("挖礦成功", hashInt)

fmt.Printf("第%d個區塊,挖礦成功:%x
",num,hash)

fmt.Println(time.Now())

time.Sleep(time.Second*2)

break

}

nonce++

}

returnhash[:],int64(nonce)

}


3.1 導入相關庫

import(

"github.com/boltdb/bolt"//導入bolt庫,用於操作DB

"log"

)3.2 創建常量

constdbName="blockchain.db"//資料庫名

constblockTableName="blocks"//表名3.3 創建區塊鏈屬性

typeBlockChainstruct{

Tip[]byte//區塊鏈裡面最後一個區塊的Hash

DB*bolt.DB//資料庫

}3.4 創建帶有創世區塊的區塊鏈

funcCreateBlockChainWithGenesisBlock()*BlockChain{

//1. 創建或者打開資料庫

db,err:=bolt.Open(dbName,0600,nil)

iferr!=nil{

log.Fatal(err)

}

//2.創建存儲

varblockHash[]byte

//3.更新數據

err=db.Update(func(tx*bolt.Tx)error{

//獲取表

b:=tx.Bucket([]byte(blockTableName))

ifb==nil{//如果不存在block表,則進行創建

b,err=tx.CreateBucket([]byte(blockTableName))

iferr!=nil{

log.Panic(err)

}

}

//4.調用Block.go中的函數CreateGenesisBlock創建創世區塊

genesisBlock:=CreateGenesisBlock("Genesis Data..")

//5.將創世區塊存儲至表中(key=當前區塊的Hash值,value=當前區塊的序列化位元組數組)

err:=b.Put(genesisBlock.Hash,genesisBlock.Serialize())

iferr!=nil{

log.Panic(err)

}

//6.存儲最新的區塊鏈的hash(key="l",value=當前區塊的Hash值)

err=b.Put([]byte("l"),genesisBlock.Hash)

iferr!=nil{

log.Panic(err)

}

//7.定義該區塊鏈的Tip值為最新區塊的Hash值

blockHash=genesisBlock.Hash

returnnil

})

//返回區塊鏈對象(Tip:blockHash,DB:進行Update操作的db對象)

return&BlockChain{blockHash,db}

}3.5 添加新區塊至區塊鏈中

func(blc*BlockChain)AddBlockChain(datastring) {

//獲取db對象blc.DB

err:=blc.DB.Update(func(tx*bolt.Tx)error{

//1.獲取表

b:=tx.Bucket([]byte(blockTableName))

//2.創建新區塊

ifb!=nil{

//通過Key:blc.Tip獲取Value(區塊序列化位元組數組)

byteBytes:=b.Get(blc.Tip)

//反序列化出最新區塊(上一個區塊)對象

block:=DeserializeBlock(byteBytes)

//3.通過NewBlock進行挖礦生成新區塊newBlock

newBlock:=NewBlock(data,block.Height+1,block.Hash)

//4.將最新區塊序列化並且存儲到資料庫中(key=新區塊的Hash值,value=新區塊序列化)

err:=b.Put(newBlock.Hash,newBlock.Serialize())

iferr!=nil{

log.Panic(err)

}

/*5.更新資料庫中"l"對應的Hash為新區塊的Hash值

用途:便於通過該Hash值找到對應的Block序列化,從而找到上一個Block對象,為生成新區塊函數NewBlock提供高度Height與上一個區塊的Hash值PreBlockHash

*/

err=b.Put([]byte("l"),newBlock.Hash)

iferr!=nil{

log.Panic(err)

}

//6. 更新Tip值為新區塊的Hash值

blc.Tip=newBlock.Hash

}

returnnil

})

iferr!=nil{

log.Panic(err)

}

}3.6 代碼整合

packageBLC

import(

"github.com/boltdb/bolt"

"log"

)

constdbName="blockchain.db"//資料庫名

constblockTableName="blocks"//表名

typeBlockChainstruct{

Tip[]byte//區塊鏈裡面最後一個區塊的Hash

DB*bolt.DB//資料庫

}

//1.創建帶有創世區塊的區塊鏈

funcCreateBlockChainWithGenesisBlock()*BlockChain{

//創建或者打開資料庫

db,err:=bolt.Open(dbName,0600,nil)

iferr!=nil{

log.Fatal(err)

}

varblockHash[]byte

err=db.Update(func(tx*bolt.Tx)error{

//獲取表

b:=tx.Bucket([]byte(blockTableName))

ifb==nil{

b,err=tx.CreateBucket([]byte(blockTableName))

iferr!=nil{

log.Panic(err)

}

}

//創建創世區塊

genesisBlock:=CreateGenesisBlock("Genesis Data..")

//將創世區塊存儲至表中

err:=b.Put(genesisBlock.Hash,genesisBlock.Serialize())

iferr!=nil{

log.Panic(err)

}

//存儲最新的區塊鏈的hash

err=b.Put([]byte("l"),genesisBlock.Hash)

iferr!=nil{

log.Panic(err)

}

blockHash=genesisBlock.Hash

returnnil

})

//返回區塊鏈對象

return&BlockChain{blockHash,db}

}

func(blc*BlockChain)AddBlockChain(datastring) {

err:=blc.DB.Update(func(tx*bolt.Tx)error{

//1.獲取表

b:=tx.Bucket([]byte(blockTableName))

//2.創建新區塊

ifb!=nil{

//獲取最新區塊

byteBytes:=b.Get(blc.Tip)

//反序列化

block:=DeserializeBlock(byteBytes)

//3. 將區塊序列化並且存儲到資料庫中

newBlock:=NewBlock(data,block.Height+1,block.Hash)

err:=b.Put(newBlock.Hash,newBlock.Serialize())

iferr!=nil{

log.Panic(err)

}

//4.更新資料庫中"l"對應的Hash

err=b.Put([]byte("l"),newBlock.Hash)

iferr!=nil{

log.Panic(err)

}

//5. 更新blockchain的Tip

blc.Tip=newBlock.Hash

}

returnnil

})

iferr!=nil{

log.Panic(err)

}

}


packageBLC

import(

"bytes"

"encoding/binary"

"log"

)

//將int64轉換為位元組數組

funcIntToHex(numint64) []byte{

buff:=new(bytes.Buffer)

err:=binary.Write(buff,binary.BigEndian,num)

iferr!=nil{

log.Panic(err)

}

returnbuff.Bytes()

}


packagemain

import(

"publicChain/BLC"//引用BLC包

"fmt"

)

funcmain() {

fmt.Println("開始挖礦")

//創建創世區塊

blockChain:=BLC.CreateBlockChainWithGenesisBlock()

deferblockChain.DB.Close()

//創建新的區塊

blockChain.AddBlockChain("Send $100 to Bruce")

blockChain.AddBlockChain("Send $200 to Apple")

blockChain.AddBlockChain("Send $300 to Alice")

blockChain.AddBlockChain("Send $400 to Bob")

}

挖礦成功,數據已經存入blockchain.db中

五.遍歷區塊鏈區塊信息


func(blc*BlockChain)PrintChain() {

//1.定義區塊對象block

varblock*Block

//2.定義當前區塊Hash值對象currentHash

varcurrentHash=blc.Tip

//3.從資料庫對象blc.DB中循環取值

for{

err:=blc.DB.View(func(tx*bolt.Tx)error{

//4. 打開表 blockTableName

b:=tx.Bucket([]byte(blockTableName))

ifb!=nil{

//5.通過獲取Key:currentHash獲得對應的Value:當前區塊的序列化位元組數組

blockBytes:=b.Get(currentHash)

//6.反序列化位元組數組,獲取最新區塊對象

block=DeserializeBlock(blockBytes)

//7. 列印獲取到的最新區塊對象的相關屬性

fmt.Printf("Height:%d
",block.Height)

fmt.Printf("PreBlockHash:%x
",block.PreBlockHash)

fmt.Printf("Data:%s
",block.Data)

fmt.Printf("TimeStamp:%s
",time.Unix(block.TimeStamp,).Format("2006-01-02 03:04:05 PM"))

fmt.Printf("Hash:%x
",block.Hash)

fmt.Printf("Nonce:%d
",block.Nonce)

}

returnnil

})

iferr!=nil{

log.Panic("資料庫查詢失敗",err)

}

//8.定義存儲最新區塊對應的上一個區塊Hash值

varhashIntbig.Int

hashInt.SetBytes(block.PreBlockHash)

//9.判斷該區塊是否為創世區塊,如果是,則退出循環遍歷

ifbig.NewInt().Cmp(&hashInt)=={

fmt.Println("該區塊為創世區塊,退出程序")

break

}

/*10.如果該區塊不是創世區塊,則將用於表數據查詢的key的值currentHash的值用上一個區塊Hash賦值,繼續進行取值循環,直至找到創世區塊

*/

currentHash=block.PreBlockHash

fmt.Println()

}

}2.測試代碼main.go

新增blockChain.PrintChain()

packagemain

import(

"publicChain/part17-數據迭代/BLC"

"fmt"

)

funcmain() {

fmt.Println("開始挖礦")

//創建創世區塊

blockChain:=BLC.CreateBlockChainWithGenesisBlock()

deferblockChain.DB.Close()

////創建新的區塊

blockChain.AddBlockChain("Send $100 to Bruce")

blockChain.AddBlockChain("Send $200 to Apple")

blockChain.AddBlockChain("Send $300 to Alice")

blockChain.AddBlockChain("Send $400 to Bob")

blockChain.PrintChain()

}

通過遍歷函數,列印出從最後一個區塊至創世區塊的所有信息。

六.通過迭代器遍歷區塊信息


import(

"github.com/boltdb/bolt"//導入BoltDB庫

"log"

)1.2 定義屬性

typeBlockChainIteratorstruct{

CurrentHash[]byte// 保存當前的區塊Hash值

DB*bolt.DB//DB對象

}1.3 迭代方法

func(blockchainIterator*BlockChainIterator)Next()*Block{

//1.定義Block對象block

varblock*Block

//2.操作DB對象blockchainIterator.DB

err:=blockchainIterator.DB.View(func(tx*bolt.Tx)error{

//3.打開表對象blockTableName

b:=tx.Bucket([]byte(blockTableName))

ifb!=nil{

//Get()方法通過Key:當前區塊的Hash值獲取當前區塊的序列化信息

currentBlockBytes:=b.Get(blockchainIterator.CurrentHash)

//反序列化出當前的區塊

block=DeserializeBlock(currentBlockBytes)

//更新迭代器裡面的CurrentHash

blockchainIterator.CurrentHash=block.PreBlockHash

}

returnnil

})

iferr!=nil{

log.Panic(err)

}

returnblock

}

2.修改遍歷方法 BlockChain.go


func(blockchain*BlockChain)Iterator()*BlockChainIterator{

return&BlockChainIterator{blockchain.Tip,blockchain.DB}

}2.2 修改PrintChain()方法

func(blc*BlockChain)PrintChain() {

//創建迭代類對象blockchainIterator

blockchainIterator:=blc.Iterator()

for{

//通過Next()方法獲取當前區塊,並更新區塊鏈對象保存的Hash值為上一個區塊的Hash值

block:=blockchainIterator.Next()

fmt.Printf("Height:%d
",block.Height)

fmt.Printf("PreBlockHash:%x
",block.PreBlockHash)

fmt.Printf("Data:%s
",block.Data)

fmt.Printf("TimeStamp:%s
",time.Unix(block.TimeStamp,).Format("2006-01-02 03:04:05 PM"))

fmt.Printf("Hash:%x
",block.Hash)

fmt.Printf("Nonce:%d
",block.Nonce)

varhashIntbig.Int

hashInt.SetBytes(block.PreBlockHash)

ifbig.NewInt().Cmp(&hashInt)=={

break

}

}

}通過迭代器的方式其實與直接遍歷區塊信息的結果沒有任何差別,只是對數據操作的代碼進行了封裝,也能從一定程度上對程序進行了簡單的解耦合設計,推薦使用。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 區塊鏈學習聯盟 的精彩文章:

TAG:區塊鏈學習聯盟 |