GO实现BTC-V3-存储
分析bolt存储区块的格式
key一定唯一:
把所有的区块都写到一个bucket中:key-> value : []byte->[]byte :
在bucket存储两种数据:
1. 区块,区块的哈希值作为key,区块的字节流作为value
block.Hash -> block.toBytes()
2. 最后一个区块的哈希值
key使用固定的字符串:[]byte(“lastHashKey”), value 就是最后一个区块的哈希
添加一个新区块要做两件事情:
1、添加区块
2、更新”lastHashKey“ 这个key对应的值,这个值就是最后一个区块的哈希值,用于新区块的创建添加。
改写区块链,获取区块链实例
//实现创建区块链的方法
func NewBlockChain() *BlockChain {
//功能分析:
//1. 获得数据库的句柄,打开数据库,读写数据
db, err := bolt.Open("blockChain.db", 0600, nil)
//向数据库中写入数据
//从数据库中读取数据
if err != nil {
log.Panic(err)
}
defer db.Close()
var tail []byte
db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("blockBucket"))
if b == nil {
//如果b1为空,说明名字为"buckeName1"这个桶不存在,我们需要创建之
fmt.Printf("bucket不存在,准备创建!\n")
b, err = tx.CreateBucket([]byte("blockBucket"))
if err != nil {
log.Panic(err)
}
//抽屉准备完毕,开始添加创世块
genesisBlock := NewBlock(genesisInfo, []byte{})
b.Put(genesisBlock.Hash, genesisBlock.toBytes() /*将区块序列化,转成字节流*/)
b.Put([]byte("lastHashKey"), genesisBlock.Hash)
tail = genesisBlock.Hash
} else {
tail = b.Get([]byte("lastHashKey"))
}
return nil
})
return &BlockChain{db, tail}
}
编码解码区块
//序列化, 将区块转换成字节流
func (block *Block) Serialize() []byte {
var buffer bytes.Buffer
//定义编码器
encoder := gob.NewEncoder(&buffer)
//编码器对结构进行编码,一定要进行校验
err := encoder.Encode(block)
if err != nil {
log.Panic(err)
}
return buffer.Bytes()
}
func Deserialize(data []byte) *Block {
fmt.Printf("解码传入的数据: %x\n", data)
var block Block
//创建解码器
decoder := gob.NewDecoder(bytes.NewReader(data))
err := decoder.Decode(&block)
if err != nil {
log.Panic(err)
}
return &block
}
更新AddBlcok
//添加区块
func (bc *BlockChain) AddBlock(data string) {
//1. 创建一个区块
bc.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("blockBucket"))
if b == nil {
fmt.Printf("bucket不存在,请检查!\n")
os.Exit(1)
}
block := NewBlock(data, bc.tail)
b.Put(block.Hash, block.Serialize() /*将区块序列化,转成字节流*/)
b.Put([]byte("lastHashKey"), block.Hash)
bc.tail = block.Hash
return nil
})
}
定义迭代器,创建迭代器
//定义一个区块链的迭代器,包含db,current
type BlockChainIterator struct {
db *bolt.DB
current []byte //当前所指向区块的哈希值
}
//创建迭代器,使用bc进行初始化
func (bc *BlockChain) NewIterator() *BlockChainIterator {
return &BlockChainIterator{bc.db, bc.tail}
}
Next函数实现
func (it *BlockChainIterator) Next() *Block {
var block Block
it.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockBucketName))
if b == nil {
fmt.Printf("bucket不存在,请检查!\n")
os.Exit(1)
}
//真正的读取数据
blockInfo /*block的字节流*/ := b.Get(it.current)
block = *Deserialize(blockInfo)
it.current = block.PrevBlockHash
return nil
})
return &block
}
使用迭代器更新main函数
package main
import (
"fmt"
//"time"
"time"
"bytes"
)
func main() {
fmt.Printf("helloworld\n")
//block := NewBlock(genesisInfo, []byte{0x0000000000000000})
bc := NewBlockChain()
defer bc.db.Close()
bc.AddBlock("hello itcast!!!")
it := bc.NewIterator()
for {
block := it.Next()
fmt.Printf("++++++++++++++++++++++++++++++++\n")
fmt.Printf("Version : %d\n", block.Version)
fmt.Printf("PrevBlockHash : %x\n", block.PrevBlockHash)
fmt.Printf("MerKleRoot : %x\n", block.MerKleRoot)
timeFormat := time.Unix(int64(block.TimeStamp), 0).Format("2006-01-02 15:04:05")
fmt.Printf("TimeStamp : %s\n", timeFormat)
fmt.Printf("Difficulity : %d\n", block.Difficulity)
fmt.Printf("Nonce : %d\n", block.Nonce)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
pow := NewProofOfWork(block)
fmt.Printf("IsValid: %v\n", pow.IsValid())
if bytes.Equal(block.PrevBlockHash, []byte{}) {
fmt.Printf("区块链遍历结束!\n")
break
}
}
}
命令行demo
package main
import (
"os"
"fmt"
)
func main() {
cmds := os.Args
for i, cmd := range cmds {
fmt.Printf("cmd[%d] : %s\n", i, cmd)
}
}
使用命令行分析
1. 所有的支配动作交给命令行来做
2. 主函数只需要调用命令行结构即可
3. 根据输入的不同命令,命令行做相应动作
1. addBlock
2. printChain
CLI : command line的缩写
type CLI struct {
bc *BlockChain
}
添加区块的时候: bc.addBlock(data), data 通过os.Args拿回来
打印区块链时候:遍历区块链,不需要外部输入数据
定义CLI结构, Run方法框架搭建
const Usage = `
./blockchain addBlock "xxxxxx" 添加数据到区块链
./blockchain printChain 打印区块链
`
type CLI struct {
bc *BlockChain
}
//给CLI提供一个方法,进行命令解析,从而执行调度
func (cli *CLI) Run() {
cmds := os.Args
if len(cmds) < 2 {
fmt.Printf(Usage)
os.Exit(1)
}
switch cmds[1] {
case "addBlock":
fmt.Printf("添加区块命令被调用, 数据:%s\n", cmds[2])
case "printChain":
fmt.Printf("打印区块链命令被调用\n")
default:
fmt.Printf("无效的命令,请检查\n")
fmt.Printf(Usage)
}
//添加区块的时候: bc.addBlock(data), data 通过os.Args拿回来
//打印区块链时候:遍历区块链,不需要外部输入数据
}
改写main函数
package main
func main() {
bc := NewBlockChain()
defer bc.db.Close()
cli := CLI{bc}
cli.Run()
}
更新Run函数
//给CLI提供一个方法,进行命令解析,从而执行调度
func (cli *CLI) Run() {
cmds := os.Args
if len(cmds) < 2 {
fmt.Printf(Usage)
os.Exit(1)
}
switch cmds[1] {
case "addBlock":
fmt.Printf("添加区块命令被调用, 数据:%s\n", cmds[2])
data := cmds[2]
cli.AddBlock(data)
case "printChain":
fmt.Printf("打印区块链命令被调用\n")
cli.PrintChain()
default:
fmt.Printf("无效的命令,请检查\n")
fmt.Printf(Usage)
}
//添加区块的时候: bc.addBlock(data), data 通过os.Args拿回来
//打印区块链时候:遍历区块链,不需要外部输入数据
}
添加commands.go 具体实现命令的文件
package main
import (
"fmt"
"time"
"bytes"
)
//实现具体的命令
func (cli *CLI) AddBlock(data string) {
cli.bc.AddBlock(data)
fmt.Printf("添加区块成功!\n")
}
func (cli *CLI) PrintChain() {
it := cli.bc.NewIterator()
for {
block := it.Next()
fmt.Printf("++++++++++++++++++++++++++++++++\n")
fmt.Printf("Version : %d\n", block.Version)
fmt.Printf("PrevBlockHash : %x\n", block.PrevBlockHash)
fmt.Printf("MerKleRoot : %x\n", block.MerKleRoot)
timeFormat := time.Unix(int64(block.TimeStamp), 0).Format("2006-01-02 15:04:05")
fmt.Printf("TimeStamp : %s\n", timeFormat)
fmt.Printf("Difficulity : %d\n", block.Difficulity)
fmt.Printf("Nonce : %d\n", block.Nonce)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
pow := NewProofOfWork(block)
fmt.Printf("IsValid: %v\n", pow.IsValid())
if bytes.Equal(block.PrevBlockHash, []byte{}) {
fmt.Printf("区块链遍历结束!\n")
break
}
}
}

