分析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
		}
	}
}