區塊鏈持久化--golang實現區塊鏈系列之三

本篇使用boltdb的簡易資料庫將區塊鏈的資料持久化到一個檔案中。boltdb是kv形式儲存的。
下面程式碼:
package main import ( "github.com/boltdb/bolt" "os" ) const dbfile= "blockchain.db" const blockBucket= "block_demo" const lastHashKey= "genesis" type BlockChian struct { //blocks []*Block db *bolt.DB lastHash []byte } func NewBlockChain() *BlockChian { //return &Blo ckChian{[]*Block{NewGenesisBlock()}} //func Open(path string, mode os.FileMode, options *Options) (*DB, error) { //var db = &DB{opened: true} db, err := bolt.Open(dbfile, 7777, nil) CheckErr(err) var lasthash []byte //db.View() db.Update(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(blockBucket)) if bucket != nil { //讀取lasthash lasthash = bucket.Get([]byte(lastHashKey)) } else { // 1.建立bucket // 2. 創世區塊 genesis := NewGenesisBlock() bucket, err := tx.CreateBucket([]byte(blockBucket)) CheckErr(err) err = bucket.Put(genesis.Hash, genesis.Serialize()) CheckErr(err) lasthash = genesis.Hash err = bucket.Put([]byte(lastHashKey), genesis.Hash) CheckErr(err) } return nil }) return &BlockChian{db:db, lastHash:lasthash} } func (bc *BlockChian)AddBlock(data string) { //var prevBlockHash[]byte /*err := bc.db.View(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(blockBucket)) lasthash := bucket.Get([]byte(lastHashKey)) prevBlockHash = lasthash return nil })*/ block := NewBlock(data, bc.lastHash) err := bc.db.Update(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(blockBucket)) err := bucket.Put(block.Hash, block.Serialize()) CheckErr(err) err = bucket.Put([]byte(lastHashKey), block.Hash) CheckErr(err) return nil }) CheckErr(err) } type BlockChainIterator struct { db *bolt.DB currentHash []byte } func (bc *BlockChian)Iterator() *BlockChainIterator { return &BlockChainIterator{bc.db, bc.lastHash} } func (it *BlockChainIterator)Next() *Block{ var block *Block err := it.db.View(func(tx *bolt.Tx) error { bucket := tx.Bucket([]byte(blockBucket)) if bucket == nil { os.Exit(1) } blockTmp := bucket.Get(it.currentHash) block = Deserialize(blockTmp) it.currentHash = block.PrevBlockHash return nil }) CheckErr(err) return block }
boltdb是key-value形式的資料庫, 所以,區塊鏈儲存hash當做key, 區塊鏈的內容當做value進行儲存。
上面有一個特殊不變的key儲存最後一個區塊的hash。
先獲取最後區塊的hash,就可以獲取到區塊的資料,獲取資料就可以獲取前一個區塊的hash,以此遞推,可以獲取所有區塊的資料。
命令列工具:
package main import ( "flag" "fmt" "os" ) const Usage= ` addBlock --data DATA"add a block to block chain" printChain"print all blocks" ` type CLI struct { bc *BlockChian } func (cli *CLI)Run(){ if len(os.Args) < 2 { fmt.Println("too few parameters!\n", Usage) os.Exit(1) } addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError) printChainCmd := flag.NewFlagSet("printChain", flag.ExitOnError) addBlockCmdPara := addBlockCmd.String("data", "", "block info") switch os.Args[1] { case "addBlock": err := addBlockCmd.Parse(os.Args[2:]) CheckErr(err) if addBlockCmd.Parsed() { if *addBlockCmdPara == "" { fmt.Println("data is empty") os.Exit(1) } cli.AddBlock(*addBlockCmdPara) } case "printChain": err := printChainCmd.Parse(os.Args[2:]) CheckErr(err) if printChainCmd.Parsed() { cli.PrintChain() } default: fmt.Println("invalid cmd\n", Usage) os.Exit(1) } } package main import "fmt" func (cli *CLI)AddBlock(data string){ cli.bc.AddBlock(data) } func (cli *CLI)PrintChain(){ bc := cli.bc it := bc.Iterator() for { // 取回當前hash指定的block,並且將當前的hash指向上一個區塊的hash block := it.Next() fmt.Println("data:", string(block.Data)) fmt.Println("Version:", block.Version) fmt.Printf("Hash:%x\n", block.Hash) fmt.Printf("TimeStamp:%d\n", block.TimeStamp) fmt.Printf("MerkeRoot:%x\n", block.MerkeRoot) fmt.Printf("Nonce:%d\n", block.Nonce) fmt.Printf("preblock Hash:%x\n", block.PrevBlockHash) println("") pow := NewProofOfWork(block) fmt.Println("Vaild:", pow.IsVaild()) if len(block.PrevBlockHash) == 0 { break } } }
終端執行效果:
