11_BitCoin全攻略十一_数据库
我们引入bolt数据库
关于bolt数据库的使用入门,可以看一下我的另一篇博客
bolt数据库入门
https://blog.youkuaiyun.com/qq_33781658/article/details/87948815
1.我们使用一个bucket来存储所有的区块
2.bucket中的数据
key: 区块hash值
value: 区块的字节流(block转成字节流)
3.我们使用一个特殊的key记录上一个区块的hash值
key: "lastHash"
value: 最后一个区块的hash值
每次添加区块之后
就更新这个lastHash
我们先来看blockchain.go文件
type BlockChain struct {
Blocks []*Block
}
我们现在的这个BlockChain结构体
里面放的是数组
现在我们要改成数据库存储了
type BlockChain struct {
db *bolt.DB
}
我们再加一个属性
用来记录最后一个区块的hash值
type BlockChain struct {
db *bolt.DB
tail []byte
}
然后我们看NewBlockChain()方法
func NewBlockChain() *BlockChain {
genesisInfo := []byte("Hello world! Hello blockchain!")
genesisBlock := NewBlock(genesisInfo, []byte{})
bc := BlockChain{
Blocks: []*Block{genesisBlock},
}
return &bc
}
这里我们把创世区块放到数组里面
那么要修改成,把创始区块放到数据库里
const blockChainDB = "blockChain.db"
const blockBucket = "blockBucket"
func NewBlockChain() *BlockChain {
var bc BlockChain
genesisInfo := []byte("Hello world! Hello blockchain!")
//创建db
db, err := bolt.Open(blockChainDB, 0600, nil)
if err != nil {
panic(err)
}
//操作数据库
db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockBucket))
//第一次
if b == nil {
b, err = tx.CreateBucket([]byte(blockBucket))
if err != nil {
panic(err)
}
//将创世区块写入bucket
genesisBlock := NewBlock(genesisInfo, []byte{})
b.Put(genesisBlock.Hash, genesisBlock.toBytes())
b.Put([]byte("lastHashKey"), genesisBlock.Hash)
bc.tail = genesisBlock.Hash
} else {
lastHash := b.Get([]byte("lastHashKey"))
bc.tail = lastHash
}
return nil
})
bc.db = db
return &bc
}
然后我们来写一个Serialize方法
func (block Block) Serialize() []byte {
}
把block转换成字节流
然后还需要一个Deserialize方法反序列化
func Deserialize(data []byte) Block{
}
把字节流转换成Block
那么我们使用gob包来进行序列化和反序列化
func (b *Block) Serialize() []byte{
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(b)
if err!=nil{
panic(err)
}
return buffer.Bytes()
}
func Deserialize(data []byte) *Block{
var block Block
decoder := gob.NewDecoder(bytes.NewReader(data))
err := decoder.Decode(&block)
if err!=nil{
panic(err)
}
return &block
}
然后我们再改一下AddBlock方法
我们看一下
func (bc *BlockChain) AddBlock(data []byte) {
//获取前一个区块
lastBlock := bc.Blocks[len(bc.Blocks)-1]
//获取前一个区块的hash值
prevHash := lastBlock.Hash
//创建当前区块
newBlock := NewBlock(data, prevHash)
//添加到区块链
bc.Blocks = append(bc.Blocks, newBlock)
}
然后我们也修改成与数据库交互
func (bc *BlockChain) AddBlock(data []byte) bool {
err := bc.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockBucket))
if b == nil {
fmt.Println("错误,bucket为空")
os.Exit(1)
}
prevHash := bc.tail
newBlock := NewBlock(data, prevHash)
err := b.Put(newBlock.Hash, newBlock.Serialize())
if err != nil {
return err
}
err = b.Put([]byte(lastHashKey), newBlock.Hash)
if err != nil {
return err
}
bc.tail = newBlock.Hash
return nil
})
if err != nil {
return false
}
return true
}
然后我们看下完整代码
package main
import (
"GolangDemo/blockchain/lib/bolt-master"
"bytes"
"encoding/gob"
"fmt"
"os"
)
type BlockChain struct {
db *bolt.DB
tail []byte
}
func (bc *BlockChain) AddBlock(data []byte) bool {
err := bc.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockBucket))
if b == nil {
fmt.Println("错误,bucket为空")
os.Exit(1)
}
prevHash := bc.tail
newBlock := NewBlock(data, prevHash)
err := b.Put(newBlock.Hash, newBlock.Serialize())
if err != nil {
return err
}
err = b.Put([]byte(lastHashKey), newBlock.Hash)
if err != nil {
return err
}
bc.tail = newBlock.Hash
return nil
})
if err != nil {
return false
}
return true
}
const blockChainDB = "blockChain.db"
const blockBucket = "blockBucket"
const lastHashKey = "lastHashKey"
func NewBlockChain() *BlockChain {
var bc BlockChain
genesisInfo := []byte("Hello world! Hello blockchain!")
//创建db
db, err := bolt.Open(blockChainDB, 0600, nil)
if err != nil {
panic(err)
}
//操作数据库
db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockBucket))
//第一次
if b == nil {
b, err = tx.CreateBucket([]byte(blockBucket))
if err != nil {
panic(err)
}
//将创世区块写入bucket
genesisBlock := NewBlock(genesisInfo, []byte{})
b.Put(genesisBlock.Hash, genesisBlock.Serialize())
b.Put([]byte(lastHashKey), genesisBlock.Hash)
bc.tail = genesisBlock.Hash
} else {
lastHash := b.Get([]byte(lastHashKey))
bc.tail = lastHash
}
return nil
})
bc.db = db
return &bc
}
func (b *Block) Serialize() []byte {
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(b)
if err != nil {
panic(err)
}
return buffer.Bytes()
}
func Deserialize(data []byte) *Block {
var block Block
decoder := gob.NewDecoder(bytes.NewReader(data))
err := decoder.Decode(&block)
if err != nil {
panic(err)
}
return &block
}