Golangによるブロックチェーンの実装(3)—データ持続化(1)BoltDBの使用
27586 ワード
データ持続性
前述の論文では,poW掘削可能なブロック鎖を実現した.しかし、私たちの前のブロック情報はキャッシュに保存されており、実行するたびに創世ブロックから開始する必要があります.これは明らかに重要な欠陥であり、本章では永続化を実現します.ビットコインではLevelDBを用いてデータ永続化を行い,ビットコインシステムとLevelDBはいずれもC++で実現される.私たちのブロックチェーンはGoで実現されているので、Go言語で書かれたBoltDBも探しています.
BoltDBの追加
BoltDBはkey/valueベースのストレージであり、SQLリレーショナル・データベース(MySQL、PG)のようなテーブルも行、列もない.データはKey-value構造にのみ存在する(Golangのmapsに似ている).Key-valueはSQLのテーブル機能とあまり差のないバケツ(buckets)に格納されているので、値を得るには「バケツ」と「key」を知る必要があります
BoltDBには次のような特性があります.小さくて簡潔な Goによる の実装は、 を個別に配置する必要はありません.は、EMCのデータ構造 をサポートします.
BoltDBの紹介と使用については、私の前の文章を参考にすることができますが、本稿では検討していません.
https://blog.csdn.net/yang731227/article/details/82974575
2つの定数を定義し、dbNameはデータベース名、bkNameはバケツ/テーブル名、バケツはブロック情報を格納します.
Blockchain構造体最新のブロックのハッシュ値を格納するためにtipを追加した.
データシーケンス化処理
BlotDBを使用する前提は、そのK-Vはbyte配列しか記憶できないので、まず
シーケンス化
データのシーケンス化
逆シーケンス化
再実現シーケンス化方法
データ持続性の実現
ブロックチェーンは創世ブロックによって導かれていることを知っているので、まず創世ブロックを改造しなければならない.前の文章では
書き込みDB
データを書き込む前に、ビットコインに2つのテーブルでデータを格納する方法を明らかにしなければなりません. blocksは、チェーン内のすべてのブロックのメタデータ を格納する. chainstateストレージチェーンのステータス.現在完了していないトランザクション情報およびその他のメタデータが格納されます.
blocksでは、k->vペアは、「b」+32-byteブロックのhashコード->ブロックインデックスレコード「f」+4-byteファイル番号->ファイル情報レコード「l」->4-byteファイル番号:最後のブロックの番号「R」->1-byteブール値:タグがインデックス「F」+1-byteタグ名長さ+タグ名->1 byte booleanをリセットしているかどうかトランザクションのhash値->トランザクションのインデックスレコード
私たちはまだ取引していないので、今はblocksだけを検討しています.chainstate私たちは検討しないことを示しています.また,ブロックをそれぞれ独立したファイルに存在させず,DB全体を1つのファイルとしてBlocksを格納する.ファイルに関連付けられた数字は必要ありません
次のk->vペアだけが必要です 32-byteこのブロックハッシュ->シーケンス化後ブロック情報 'l->チェーン内の最後のブロックのhash値
チェーン内の創世ブロックを変更した後、関数
1.バケツの最後のブロックのHash 2を取り出す.Hashを逆シーケンス化し、最後のブロック情報3を得る.取り出した情報に基づいて、新しいブロック4を作成する.新しいブロックをDBにシーケンス化して格納する
ブロックの読み込み
書き込みDBは改造されていますので、読み取りを実現します
うんてん
まとめ
本論文では,BoltDBを用いてデータを永続化することを実現したが,実行するたびに創世ブロックから始まるのはBUGではないか.はい、あなたの考えは間違いありません.本文はデータベースの存在を判断していません.ブロックデータをクエリーする機能も欠けています.心配しないでください.次の章では、データの永続化について説明し続けます.ブロックチェーンの反復とコマンドラインのインタラクティブインタフェースを追加して、私たちの機能をさらに改善します.
前述の論文では,poW掘削可能なブロック鎖を実現した.しかし、私たちの前のブロック情報はキャッシュに保存されており、実行するたびに創世ブロックから開始する必要があります.これは明らかに重要な欠陥であり、本章では永続化を実現します.ビットコインではLevelDBを用いてデータ永続化を行い,ビットコインシステムとLevelDBはいずれもC++で実現される.私たちのブロックチェーンはGoで実現されているので、Go言語で書かれたBoltDBも探しています.
BoltDBの追加
BoltDBはkey/valueベースのストレージであり、SQLリレーショナル・データベース(MySQL、PG)のようなテーブルも行、列もない.データはKey-value構造にのみ存在する(Golangのmapsに似ている).Key-valueはSQLのテーブル機能とあまり差のないバケツ(buckets)に格納されているので、値を得るには「バケツ」と「key」を知る必要があります
BoltDBには次のような特性があります.
BoltDBの紹介と使用については、私の前の文章を参考にすることができますが、本稿では検討していません.
https://blog.csdn.net/yang731227/article/details/82974575
const dbName = "blockchain.db" //
const bkName = "blocks" //
type Blockchain struct {
tip [] byte //
Db *bolt.DB //
}
2つの定数を定義し、dbNameはデータベース名、bkNameはバケツ/テーブル名、バケツはブロック情報を格納します.
Blockchain構造体最新のブロックのハッシュ値を格納するためにtipを追加した.
データシーケンス化処理
BlotDBを使用する前提は、そのK-Vはbyte配列しか記憶できないので、まず
Block
を[]byte
、すなわちデータシーケンス化に変換し、逆にブロックデータを読み取る必要がある場合は[]byte
をBlock
、すなわちデータ逆シーケンス化に復元しなければならない.シーケンス化
データのシーケンス化
func (b *Block) Serialize() []byte {
var result bytes.Buffer
encoder:=gob.NewEncoder(&result)
err :=encoder.Encode(b)
if err!=nil{
log.Panicf("serialize the block to byte failed %v
",err)
}
return result.Bytes()
}
逆シーケンス化
再実現シーケンス化方法
func DeserilizeBlock (blockBytes []byte) *Block{
var block Block
decoder:= gob.NewDecoder(bytes.NewReader(blockBytes))
err:= decoder.Decode(&block)
if err !=nil{
log.Panicf("deserialize the block to byte failed %v
",err)
}
return &block
}
データ持続性の実現
ブロックチェーンは創世ブロックによって導かれていることを知っているので、まず創世ブロックを改造しなければならない.前の文章では
NewBlockchain()
を使用しています.これは創世ブロックをチェーンに追加したものです.明らかに今、この方法は私たちのニーズを満たしていません.他の方法で置き換える必要があります.ここでは、Blockchain_GenesisBlokc()
を再作成します.この関数は実現する必要があります.1.DBファイルを作成し、2を開きます.ブロック情報を格納バケツ3を作成する.創世ブロックの情報をシーケンス化し、DB中4を記憶する.創世ブロックをチェーンに追加書き込みDB
データを書き込む前に、ビットコインに2つのテーブルでデータを格納する方法を明らかにしなければなりません.
blocksでは、k->vペアは、「b」+32-byteブロックのhashコード->ブロックインデックスレコード「f」+4-byteファイル番号->ファイル情報レコード「l」->4-byteファイル番号:最後のブロックの番号「R」->1-byteブール値:タグがインデックス「F」+1-byteタグ名長さ+タグ名->1 byte booleanをリセットしているかどうかトランザクションのhash値->トランザクションのインデックスレコード
私たちはまだ取引していないので、今はblocksだけを検討しています.chainstate私たちは検討しないことを示しています.また,ブロックをそれぞれ独立したファイルに存在させず,DB全体を1つのファイルとしてBlocksを格納する.ファイルに関連付けられた数字は必要ありません
次のk->vペアだけが必要です
func Blockchain_GenesisBlokc() *Blockchain {
db, err := bolt.Open(dbName, 0600, nil)
if err != nil {
log.Panicf("open the Dbfailed! %v
", err)
}
//defer db.Close()
var tip []byte //
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bkName))
if b == nil {
b, err = tx.CreateBucket([]byte(bkName))
if err != nil {
log.Panicf("create the bucket [%s] failed! %v
", bkName, err)
}
}
if b != nil {
genesisBlock := NewGenesisBlock()
//
err = b.Put(genesisBlock.Hash, genesisBlock.Serialize())
if err != nil {
log.Panicf("put the data of genesisBlock to Dbfailed! %v
", err)
}
//
err = b.Put([]byte("l"), genesisBlock.Hash)
if err != nil {
log.Panicf("put the hash of latest block to Dbfailed! %v
", err)
}
tip = genesisBlock.Hash
}
return nil
})
if err != nil {
log.Panicf("update the data of genesis block failed! %v
", err)
}
return &Blockchain{tip, db}
}
チェーン内の創世ブロックを変更した後、関数
AddBlock
を改造しました.以前は、新しいブロックをチェーンに簡単に追加しただけでしたが、次のようにする必要があります.1.バケツの最後のブロックのHash 2を取り出す.Hashを逆シーケンス化し、最後のブロック情報3を得る.取り出した情報に基づいて、新しいブロック4を作成する.新しいブロックをDBにシーケンス化して格納する
func (bc *Blockchain) AddBlock(data string) {
err := bc.Db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bkName))
if b != nil {
blockBytes := b.Get(bc.tip)
latest_block := DeserilizeBlock(blockBytes)
newBlock := NewBlock(latest_block.Index+1, data, latest_block.Hash)
err := b.Put(newBlock.Hash, newBlock.Serialize())
if nil != err {
log.Panicf("put the data of new block into Dbfailed! %v
", err)
}
err = b.Put([]byte("l"), newBlock.Hash)
if nil != err {
log.Panicf("put the hash of the newest block into Dbfailed! %v
", err)
}
bc.tip = newBlock.Hash
}
return nil
})
if nil != err {
log.Panicf("update the Dbof block failed! %v
", err)
}
}
ブロックの読み込み
書き込みDBは改造されていますので、読み取りを実現します
func (bc *Blockchain) PrintChain() {
fmt.Println("—————————————— ———————————————————————")
var curBlock *Block
var curHash []byte = bc.tip
for {
fmt.Println("—————————————————————————————————————————————")
bc.Db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bkName))
if b != nil {
blockBytes := b.Get(curHash)
curBlock = DeserilizeBlock(blockBytes)
fmt.Printf("\tHeigth : %d
", curBlock.Index)
fmt.Printf("\tTimeStamp : %d
", curBlock.TimeStamp)
fmt.Printf("\tPrevBlockHash : %x
", curBlock.PrevBlockHash)
fmt.Printf("\tHash : %x
", curBlock.Hash)
fmt.Printf("\tData : %s
", string(curBlock.Data))
fmt.Printf("\tNonce : %d
", curBlock.Nonce)
}
return nil
})
//
var hashInt big.Int
hashInt.SetBytes(curBlock.PrevBlockHash)
if big.NewInt(0).Cmp(&hashInt) == 0 {
break //
}
curHash = curBlock.PrevBlockHash
}
}
うんてん
func main() {
blockChain := BLC.Blockchain_GenesisBlokc()
defer blockChain.Db.Close();
blockChain.AddBlock("Send 100 btc to Jay")
blockChain.AddBlock("Send 50 btc to Clown")
blockChain.AddBlock("Send 20 btc to Bob")
blockChain.PrintChain()
}
まとめ
本論文では,BoltDBを用いてデータを永続化することを実現したが,実行するたびに創世ブロックから始まるのはBUGではないか.はい、あなたの考えは間違いありません.本文はデータベースの存在を判断していません.ブロックデータをクエリーする機能も欠けています.心配しないでください.次の章では、データの永続化について説明し続けます.ブロックチェーンの反復とコマンドラインのインタラクティブインタフェースを追加して、私たちの機能をさらに改善します.