BoltDB是一个嵌入式key/value的数据库,即只需要将其链接到你的应用程序代码中即可使用BoltDB提供的API来高效的存取数据。而且BoltDB支持完全可序列化的ACID事务,让应用程序可以更简单的处理复杂操作。
BoltDB设计源于LMDB,具有以下特点:
最后,BoltDB使用Golang开发,而且被应用于influxDB项目作为底层存储。
go get github.com/boltdb/bolt
db, e := bolt.Open(dbname, 0644, nil)
open函数第一个参数为数据库名,dbname已经定义好了,这里直接引用的,第二个参数为权限,第三个参数为options
如果数据库没有则创建,如果有就打开。
db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketname))
//如果表为nil,则创建,不为nil则删除再创建
if bucket == nil {
//创建bucket
createBucket, i := tx.CreateBucket([]byte(bucketname))
HandleErr(i, "tx.createbucket")
} else {
//删除旧的bucket,新建一个空的bucket
tx.DeleteBucket([]byte(bucketname))
createBucket, i := tx.CreateBucket([]byte(bucketname))
HandleErr(i, "tx.createbucket")
}
return nil
})
//只读事务
err := db.View(func(tx *bolt.Tx) error {
...
return nil
})
实现区块链的持久化操作
blockchaing.go
package Block
import (
"github.com/bolt-master/bolt-master"
"fmt"
)
const bucketname = "blockchain"
const dbname = "bc2.db"
//定义区块链
type BlockChain struct {
Db *bolt.DB //存放区块的数据库
}
//创造区块链
func (bc *BlockChain) CreateBlockChain() {
//创造创世区块
firstBlock := new(Block)
block := CreateBlock(firstBlock, "创世区块")
fmt.Println("\nfirstBlock is created!\n", block)
//打开数据库
db, e := bolt.Open(dbname, 0644, nil)
HandleErr(e, "bolt.open err")
bc.Db = db
db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketname))
//如果表为nil,则创建,不为nil则删除再创建
if bucket == nil {
//创建bucket
createBucket, i := tx.CreateBucket([]byte(bucketname))
HandleErr(i, "tx.createbucket")
//将创始区块添加到数据库中
puterr := createBucket.Put([]byte(block.HashCode), block.BlockToByte())
HandleErr(puterr, "createBucket.put")
puterr2 := createBucket.Put([]byte("LastBlockHash"), []byte(block.HashCode))
HandleErr(puterr2, "createBucket.put")
} else {
//删除旧的bucket,新建一个空的bucket
tx.DeleteBucket([]byte(bucketname))
createBucket, i := tx.CreateBucket([]byte(bucketname))
HandleErr(i, "tx.createbucket")
//将创始区块添加到数据库中
puterr1 := createBucket.Put([]byte(block.HashCode), block.BlockToByte())
HandleErr(puterr1, "createBucket.put")
puterr2 := createBucket.Put([]byte("LastBlockHash"), []byte(block.HashCode))
HandleErr(puterr2, "createBucket.put")
}
return nil
})
}
//向区块链中添加区块
func (bc *BlockChain) AddBlock(data string) {
//连接数据库进行更改
bc.Db.Update(func(tx *bolt.Tx) error {
//查看表是否存在
bucket := tx.Bucket([]byte(bucketname))
//如果存在
if bucket != nil {
//获取lastblockhash
lastblockbyte := bucket.Get([]byte("LastBlockHash"))
//如果lastblock不存在则panic
if lastblockbyte == nil {
panic("bc.lastblockhash is worry !")
//如果存在则继续
} else {
byteblock := bucket.Get(lastblockbyte)
//格式转换
block := ByteToBlock(byteblock)
newblock := CreateBlock(block, data)
fmt.Println(data,newblock)
//存入数据库
puterr1 := bucket.Put([]byte(newblock.HashCode), newblock.BlockToByte())
HandleErr(puterr1, "createBucket.put")
//更改数据库中的lastblockhash
puterr2 := bucket.Put([]byte("LastBlockHash"), []byte(newblock.HashCode))
HandleErr(puterr2, "createBucket.put")
}
}else{
panic("bucket is not exist !" )
}
return nil
})
}
//循环输出block
func (bc *BlockChain) ShowBlockChain() {
bc.Db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketname))
if bucket != nil {
lastblockhash := bucket.Get([]byte("LastBlockHash"))
if lastblockhash == nil {
panic("lastblock is not exist !")
} else {
lastblock := bucket.Get([]byte(lastblockhash))
toBlock := ByteToBlock(lastblock)
fmt.Println()
fmt.Println(toBlock)
for {
hash:=toBlock.PreHash
get := bucket.Get([]byte(hash))
toBlock := ByteToBlock(get)
fmt.Println(toBlock)
if toBlock.PreHash==""{
break
}
}
}
} else {
panic("bucket is not exist !")
}
return nil
})
}
block.go
package Block
import (
"time"
"encoding/gob"
"bytes"
)
type Block struct {
Index int
HashCode string
PreHash string
Timestamp string
Data string
Nonce int
Diff int
}
//创造区块
func CreateBlock(oldBlock *Block,data string)*Block{
block := new(Block)
block.Data=data
block.PreHash=oldBlock.HashCode
block.Timestamp=time.Now().String()
block.Diff=4
block.Index=oldBlock.Index+1
Pow(block)
return block
}
func (b *Block) BlockToByte()[]byte{
var buffer bytes.Buffer
decoder := gob.NewEncoder(&buffer)
decode := decoder.Encode(b)
HandleErr(decode,"decoder worry !" )
return buffer.Bytes()
}
func ByteToBlock(bb []byte) *Block{
b := new(Block)
decoder:= gob.NewDecoder(bytes.NewReader(bb))
decoder.Decode(b)
return b
}
pow.go
package Block
import (
"strconv"
"crypto/sha256"
"encoding/hex"
"strings"
"fmt"
)
func Pow(block *Block){
for{
hash := GetHash(block)
fmt.Println(hash,"\t",block.Nonce)
repeat := strings.Repeat("0", block.Diff)
prefix := strings.HasPrefix(hash, repeat)
if prefix{
fmt.Println("挖矿成功!")
block.HashCode=hash
break
}else {
block.Nonce+=1
}
}
}
func GetHash(block *Block)string{
s:=strconv.Itoa(block.Index)+strconv.Itoa(block.Nonce)+
strconv.Itoa(block.Diff)+block.Timestamp+block.PreHash+
block.Data
hashed := sha256.New()
hashed.Write([]byte(s))
hash := hashed.Sum(nil)
return hex.EncodeToString(hash)
}
uitls.go
package Block
import "fmt"
func HandleErr(err error,why string){
if err!=nil{
fmt.Println(err,"\twhy=",why)
}
}
main.go
package main
import (
"go_demo/BlockChain2/Block"
"strconv"
)
func main() {
var blockchain Block.BlockChain
blockchain.CreateBlockChain()
defer blockchain.Db.Close()
blockchain.AddBlock("我是第" + strconv.Itoa(2) + "个区块")
blockchain.ShowBlockChain()
}
结果为: