基于bolt数据库实现简单的区块链 day(2)
- 一、blot数据库
- 1.1 安装Boltdb数据库
- 1.2 存储数据
- 1.3 读取数据
- 二、基于bolt数据库实现简单的区块链
- 2.1区块链结构体
- 2.2 创建带有创世块的区块链
- 三、gob包
- 3.1 介绍
- 3.2 编码解码实例
- 3.3 block文件
- 3.4 blockChain文件
- 3.5 proofOfWork文件
- 四、迭代器
- 4.1 迭代器的数据结构
- 4.2 创建迭代器函数
- 4.3 迭代函数
- 4.4 使用迭代器进行迭代
- 4.5 结果
- 五、命令行
- 5.1 获取命令行
- 5.2 通过命令行得到要运行的方法
- 5.3 执行命令行
一、blot数据库
1.1 安装Boltdb数据库
- 他会下载到gopath下的路径
go get github.com/boltdb/bolt
- 我下载好的路径就是
C:\Users\Administrator\go\src\github.com\boltdb\bolt
1.2 存储数据
import (
"fmt"
"github.com/boltdb/bolt"
"log"
)
func main(){
//打开数据库,打开不了则创建数据库
db, err := bolt.Open("BitCoin.db",0600,nil)
if err != nil {
log.Panicln("打开数据库出错")
}
updateErr := db.Update(func(tx *bolt.Tx) error {
//打开Bucket
bucket := tx.Bucket([]byte("BitCoin"))
if bucket == nil{
fmt.Println("当前桶不存在")
bucket, err = tx.CreateBucket([]byte("BitCoin"))
if err != nil {
log.Panicln("创建桶失败")
}
}
bucket.Put([]byte("name"), []byte("jack"))
bucket.Put([]byte("age"), []byte("12"))
bucket.Put([]byte("address"), []byte("西安"))
//返回Nil,以便数据库进行操作
return nil
})
if updateErr != nil{
log.Panicln("往数据库存储数据失败")
}
}
1.3 读取数据
db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("BitCoin"))
if bucket == nil{
log.Panicln("查找桶失败")
}
name := string(bucket.Get([]byte("name")))
age := string(bucket.Get([]byte("age")))
address := string(bucket.Get([]byte("address")))
fmt.Println("name:",name, "age:",age, "address:",address)
return nil
})
二、基于bolt数据库实现简单的区块链
2.1区块链结构体
- 替换原来的指针数组
- Block的hash作为key
- Block节点的二进制流作为value
- 需要一个常量记录最后一个节点的hash值,不然无法创建新节点时记录上一节点的哈希值
// BlockChain 区块链结构体
//将区块链存储在数据库中
type BlockChain struct {
db *bolt.DB
//存储最后一个区块的hash
lastBlockHash []byte
}
2.2 创建带有创世块的区块链
// CreateBlockChain 创建带有一个创世块的区块链
func CreateBlockChain() *BlockChain{
var blockChain *BlockChain
//创世块
genesisBlock := GenesisBlock()
db, err := bolt.Open("BlockCoin", 0600, nil)
if err != nil{
log.Panicln("打开数据库出错")
}
db.Update(func(tx *bolt.Tx) error {
//取到bucket
bucket := tx.Bucket([]byte("BlockCoin"))
if bucket == nil{
bucket, err := tx.CreateBucket([]byte("BlockCoin"))
if err != nil{
log.Panicln("创建bucket出错")
}
//存储创世块的信息
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.blockToByte())
//存储最后一个区块的hash信息
lashBlockHash = genesisBlock.Hash
}else {
//存储创世块的信息
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.blockToByte())
//存储最后一个区块的hash信息
lashBlockHash = genesisBlock.Hash
}
blockChain.db = db
blockChain.lastBlockHash = lashBlockHash
return nil
})
return blockChain
}
三、gob包
3.1 介绍
golang自带的一个数据结构编码/解码的工具。
3.2 编码解码实例
type Teacher struct {
Name string
Age int
Address string
}
func main(){
var mrsLi Teacher = Teacher{
Name: "jack",
Age: 59,
Address: "xian",
}
//1.编码的数据放到buffer中
var buffer bytes.Buffer
//2.定义一个编码器
encoder := gob.NewEncoder(&buffer)
//3.使用编码器进行编码
err := encoder.Encode(&mrsLi)
if err != nil{
fmt.Println("编码失败")
}
fmt.Println("编码后的李老师:",buffer.Bytes())
//1.定义一个解码器
decoder := gob.NewDecoder(bytes.NewReader(buffer.Bytes()))
//2.定义解码后承接的变量
var mrsWu Teacher
err1 := decoder.Decode(&mrsWu)
if err1 != nil{
fmt.Println("解码出错")
}
fmt.Println(mrsWu.Age,mrsWu.Address,mrsWu.Name)
}
3.3 block文件
package main
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/gob"
"fmt"
"log"
"time"
)
// Block 区块结构体
type Block struct{
//1.区块版本号
Version uint64
//2.前区块Hash
PreBlockHash []byte
//3.merkel根
MerkelRoot []byte
//4.时间戳
TimeStamp uint64
//5.难度值
Difficulty uint64
//6.随机数,也就是挖矿要找的数据
Nonce uint64
//a.当前区块的Hash值,在现实比特币中没有这个
Hash []byte
Data []byte
}
// Uint64ConvertByte 将uint64类型转换为[]byte{}类型
func Uint64ConvertByte(data uint64)[]byte{
var buffer bytes.Buffer
err := binary.Write(&buffer, binary.BigEndian,data)
if err != nil {
log.Panicln(err)
}
return buffer.Bytes()
}
// CreateBlock 创建block
func CreateBlock(preBlockHash []byte, data string) *Block{
block := Block{
Version: 00,
PreBlockHash: preBlockHash,
MerkelRoot: []byte{},
TimeStamp: uint64(time.Now().Unix()),
Difficulty: 0,//随便填一个
Nonce: 0,//随便填一个
Data: []byte(data),
Hash: []byte{},
}
//进行挖矿,得到挖矿成功后的hash和随机值
pow := CreatePOW(&block)
//挖矿模拟,不断改变随机数,计算hash,直到找到合适的hash
hash,nonce := pow.Run()
//根据挖矿结果对区块数据不断进行更新
block.Hash = hash
block.Nonce = nonce
//block.SetHash()
return &block
}
// GenesisBlock 创建创世块,即第一个区块
func GenesisBlock() *Block{
genesisBlock := CreateBlock([]byte{},"第一个创世块,牛逼")
return genesisBlock
}
// SetHash 计算当前区块的hash
func (block *Block)SetHash(){
var blockInfo []byte
//...的作用是把data数组打散,一个个装进preBlockHash切片中
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Version)...)
//blockInfo = append(blockInfo, block.PreBlockHash...)
//blockInfo = append(blockInfo, block.MerkelRoot...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.TimeStamp)...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Difficulty)...)
//blockInfo = append(blockInfo, Uint64ConvertByte(block.Nonce)...)
//blockInfo = append(blockInfo, block.Data...)
// 创建一个二维数组
tem := [][]byte{
Uint64ConvertByte(block.Version),
block.PreBlockHash,
block.MerkelRoot,
Uint64ConvertByte(block.TimeStamp),
Uint64ConvertByte(block.Difficulty),
Uint64ConvertByte(block.Nonce),
block.Data,
}
//将二维byte数组连接成一维byte数组
bytes.Join(tem, []byte{})
//hash是一个字节数组
Hash := sha256.Sum256(blockInfo)
//block.Hash作为Hash的切片
block.Hash = Hash[:]
}
func (block *Block) blockToByte()[]byte{
return []byte{}
}
func (block *Block)Serialize() []byte{
//1.编码的数据放到buffer中
var buffer bytes.Buffer
//2.定义一个编码器
encoder := gob.NewEncoder(&buffer)
//3.使用编码器进行编码
err := encoder.Encode(block)
if err != nil{
fmt.Println("编码失败")
}
return buffer.Bytes()
}
func (block *Block) DeSerialize(buffer []byte) *Block{
//1.定义一个解码器
decoder := gob.NewDecoder(bytes.NewReader(buffer))
//2.定义解码后承接的变量
err1 := decoder.Decode(block)
if err1 != nil{
fmt.Println("解码出错")
}
return block
}
3.4 blockChain文件
package main
import (
"fmt"
"github.com/boltdb/bolt"
"log"
)
// BlockChain 区块链结构体
//将区块链存储在数据库中
type BlockChain struct {
db *bolt.DB
//存储最后一个区块的hash
lastBlockHash []byte
}
var lashBlockHash []byte
// CreateBlockChain 创建带有一个创世块的区块链
func CreateBlockChain() *BlockChain{
var blockChain *BlockChain = &BlockChain{}
//创世块
genesisBlock := GenesisBlock()
db, err := bolt.Open("BlockCoin", 0600, nil)
if err != nil{
log.Panicln("打开数据库出错")
}
db.Update(func(tx *bolt.Tx) error {
//取到bucket
bucket := tx.Bucket([]byte("BlockCoin"))
if bucket == nil{
bucket, err := tx.CreateBucket([]byte("BlockCoin"))
if err != nil{
log.Panicln("创建bucket出错")
}
//存储创世块的信息
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.Serialize())
//存储最后一个区块的hash信息
lashBlockHash = genesisBlock.Hash
}else {
//存储创世块的信息
bucket.Put([]byte(genesisBlock.Hash), genesisBlock.Serialize())
//存储最后一个区块的hash信息
lashBlockHash = genesisBlock.Hash
}
//试着输出一下genesisBlock的信息
//fmt.Println(genesisBlock.Serialize())
blockChain.db = db
blockChain.lastBlockHash = lashBlockHash
return nil
})
return blockChain
}
// AddBlock 当前区块的前一区块哈希值字段从区块链中获取
func (blockChain *BlockChain)AddBlock(data string){
db := blockChain.db
lastBlockChain := blockChain.lastBlockHash
block := CreateBlock(lastBlockChain, data)
//更新区块链条最后一个区块的hash信息
blockChain.lastBlockHash = block.Hash
fmt.Printf("当前区块的hash%d:\n",block.Hash)
fmt.Println("当前区块的数据:",string(block.Data))
fmt.Println("当前区块的随机值:",block.Nonce)
db.Update(func(tx *bolt.Tx) error {
//取到bucket
bucket := tx.Bucket([]byte("BlockCoin"))
if bucket == nil {
bucket, err := tx.CreateBucket([]byte("BlockCoin"))
if err != nil {
log.Panicln("创建bucket出错")
}
//存储区块的信息
bucket.Put([]byte(block.Hash), block.Serialize())
} else {
//存储区块的信息
bucket.Put([]byte(block.Hash), block.Serialize())
}
return nil
})
}
3.5 proofOfWork文件
package main
import (
"bytes"
"crypto/sha256"
"math/big"
)
// ProofOfWork 挖矿模块
// ProofOfWork 工作量证明机制
type ProofOfWork struct {
block *Block
target *big.Int
}
//CreatePOW 创建ProofOfWork
func CreatePOW(block *Block) *ProofOfWork{
pow := ProofOfWork{
block: block,
}
target := "0000100000000000000000000000000000000000000000000000000000000000"
bigNum := big.Int{}
//res是指针类型的
res, _ := bigNum.SetString(target, 16)
pow.target = res
return &pow
}
//Run 返回一个Hash值和随机数
func (pow *ProofOfWork) Run()([]byte, uint64){
tmpBigInt := &big.Int{}
//与给定的目标哈希值进行比较,小于则挖矿成功
var nonce uint64 = 0
var hash [32]byte
for{
block := pow.block
tem := [][]byte{
Uint64ConvertByte(block.Version),
block.PreBlockHash,
block.MerkelRoot,
Uint64ConvertByte(block.TimeStamp),
Uint64ConvertByte(block.Difficulty),
Uint64ConvertByte(nonce),
block.Data,
}
// blockInfo 拼装好的数据
blockInfo := bytes.Join(tem, []byte(""))
hash = sha256.Sum256(blockInfo)
tmpBigInt.SetBytes(hash[:])
res := tmpBigInt.Cmp(pow.target)
if res == -1{
break
}
nonce ++
}
return hash[:],nonce
}
四、迭代器
4.1 迭代器的数据结构
type BlockChainIterator struct {
DB *bolt.DB
//当前hash的指针
CurrentHashPtr []byte
}
4.2 创建迭代器函数
- 迭代器函数是属于区块链的函数
func (blockChain *BlockChain) NewBlockChainIterator() *BlockChainIterator{
//如何判断blockChain是否存在??
blockChainIterator := BlockChainIterator{
DB: blockChain.db,
CurrentHashPtr: blockChain.lastBlockHash,
}
return &blockChainIterator
}
4.3 迭代函数
- 迭代函数是属于迭代器结构体
// nextBlock 进行迭代: 1.返回一个Block 2.迭代指针往前移
func (blockChainIterator *BlockChainIterator)nextBlock() *Block{
var block *Block
db := blockChainIterator.DB
db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("BlockCoin"))
if bucket == nil{
log.Panicln("迭代时出错")
}
currentHash := bucket.Get(blockChainIterator.CurrentHashPtr)
//得到Block对象
block = DeSerialize(currentHash)
//指针往前移动
blockChainIterator.CurrentHashPtr = block.PreBlockHash
return nil
})
return block
}
4.4 使用迭代器进行迭代
func main() {
//创建第一个区块链
firstBlockChain := CreateBlockChain()
//fmt.Println(firstBlockChain.lastBlockHash)
firstBlockChain.AddBlock("jack向sheep转了50比特币")
firstBlockChain.AddBlock("jack向sheep转了80比特币")
//得到迭代器
iterator := firstBlockChain.NewBlockChainIterator()
//进行迭代
for {
block := iterator.nextBlock()
fmt.Println("-------------------------------")
fmt.Printf("当前区块的hash:%x\n",block.Hash)
fmt.Printf("上一区块的hash:%x\n",block.PreBlockHash)
fmt.Printf("当前区块的数据:%s\n",string(block.Data))
//判断是否迭代完毕
if len(iterator.CurrentHashPtr) == 0{
fmt.Println("迭代结束")
break
}
}
}
4.5 结果
当前区块的hash[0 0 11 253 51 145 39 252 74 72 27 243 138 170 24 86 207 130 33 24 47 50 34 172 2 182 220 230 77 77 38 33]:
当前区块的数据: jack向sheep转了50比特币
当前区块的随机值: 1784998
当前区块的hash[0 0 0 30 7 7 134 66 43 201 134 147 176 39 52 67 42 89 53 28 162 81 219 229 62 109 158 31 180 75 191 110]:
当前区块的数据: jack向sheep转了80比特币
当前区块的随机值: 1897998
-------------------------------
当前区块的hash:0000001e070786422bc98693b02734432a59351ca251dbe53e6d9e1fb44bbf6e
上一区块的hash:00000bfd339127fc4a481bf38aaa1856cf8221182f3222ac02b6dce64d4d2621
当前区块的数据:jack向sheep转了80比特币
-------------------------------
当前区块的hash:00000bfd339127fc4a481bf38aaa1856cf8221182f3222ac02b6dce64d4d2621
上一区块的hash:00000dac365228f51b7ca6063757ea3aa0458612a47048e7779f47edd2a6a6bc
当前区块的数据:jack向sheep转了50比特币
-------------------------------
当前区块的hash:00000dac365228f51b7ca6063757ea3aa0458612a47048e7779f47edd2a6a6bc
上一区块的hash:
当前区块的数据:第一个创世块,牛逼
迭代结束
Process finished with the exit code 0
五、命令行
5.1 获取命令行
list := os.Args
5.2 通过命令行得到要运行的方法
- 比如go cmd addBlock --data “添加新的交易”,通过参数得知需要执行添加区块的功能。
type Cmd struct {
bChain *BlockChain
}
const Usage = `addBlock --data 添加区块
printBlock 输出区块`
func (cmd *Cmd) Run(){
list := os.Args
if len(list) < 2{
fmt.Println(Usage)
}else{
switch list[1] {
case "addBlock":
fmt.Println("添加区块")
fmt.Println(list[2], list[3])
//cmd.bChain.AddBlock()
case "printBlock":fmt.Println("输出区块")
default:
fmt.Println(Usage)
}
}
}
5.3 执行命令行
go build *.go
./cmd addBlock --data "添加一笔新的交易"