为了能够永久保存区块链数据,这里引入bolt数据库。
(1)改写BlockChain结构体
type BlockChain struct {
//Blocks []*Block // 保存所有区块
Db *bolt.DB
Tail []byte // 最后区块的hash
}
(2)改写NewBlockChain方法,把创世块数据以及最后区块的哈希写入数据库中。
const dbName = "blockchain.db"
const bucketName = "bucket"
const last = "lastBlockHash"
// 创建方法
func NewBlockChain() *BlockChain {
/*// 创建创世块
genericBlock := NewBlock([]byte(genesisInfo), []byte{})
// 创建BlockChain
bc := BlockChain{[]*Block{genericBlock}}
return &bc*/
var db *bolt.DB
var lastHash []byte
// 1. 打开数据库
db, err := bolt.Open(dbName, 0600, nil)
if err != nil {
panic("bolt.Open err!")
}
db.Update(func(tx *bolt.Tx) error {
// 2. 打开抽屉Bucket
bucket := tx.Bucket([]byte(bucketName))
// 3. 如果Bucket是否为nil,则创建一个Bucket
if bucket == nil {
bucket, err = tx.CreateBucket([]byte(bucketName))
if err != nil {
panic("tx.CreateBucket err!")
}
// 创建创世块
genericBlock := NewBlock([]byte(genesisInfo), []byte{})
// 把创世块保存在bucket中
bucket.Put(genericBlock.Hash, genericBlock.Serialize())
// 把创世块的hash保存在last中
bucket.Put([]byte(last), genericBlock.Hash)
// 记录lastHash
lastHash = genericBlock.Hash
} else {
// 4. 如果Bucket不为nil,记录lastHash
lastHash = bucket.Get([]byte(last))
}
return nil
})
return &BlockChain{db, lastHash}
}
(3)定义序列化和反序列化函数
// 序列化block
func (block *Block) Serialize() []byte {
// 创建Encoder
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
// 加密操作
err := encoder.Encode(block)
if err != nil {
panic("encoder.Encode err!")
}
return buffer.Bytes()
}
// 反序列化
func Deserialize(data []byte) *Block {
var block *Block
var buffer bytes.Buffer
_, err := buffer.Write(data)
if err != nil {
panic("buffer.Write err!")
}
// 创建Decoder
decoder := gob.NewDecoder(&buffer)
// 加密操作
err = decoder.Decode(&block)
if err != nil {
panic("decoder.Decode err!")
}
return block
}
(4)修改AddBlock方法,把新区块序列化后保存到数据库中,并且更新最后区块的哈希。
// 添加区块
func (bc *BlockChain) AddBlock(data string) {
/*// 获取最后区块
lastBlock := bc.Blocks[len(bc.Blocks)-1]
// 创建一个新区块
block := NewBlock([]byte(data), lastBlock.Hash)
// 添加新区块
bc.Blocks = append(bc.Blocks, block)*/
// 获取最后区块hash
lastHash := bc.Tail
// 创建区块
block := NewBlock([]byte(data), lastHash)
// 更新操作
bc.Db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
if bucket == nil {
panic("bucket should not be nil!")
}
// 向bolt数据库添加新区块
bucket.Put(block.Hash, block.Serialize())
// 更新数据库的last
bucket.Put([]byte(last), block.Hash)
// 更新bc.Tail
bc.Tail = block.Hash
return nil
})
}
(5)修改main函数,把打印语句注释。
func main() {
bc := NewBlockChain()
bc.AddBlock("1111")
bc.AddBlock("2222")
/*for i, block := range bc.Blocks {
fmt.Println("======== block height : ", i, "=======")
fmt.Printf("Version : %d\n", block.Version)
fmt.Printf("PreHash : %x\n", block.PrevHash)
fmt.Printf("MerKleRoot : %x\n", block.MerKleRoot)
fmt.Printf("TimeStamp : %d\n", block.TimeStamp)
fmt.Printf("Difficulty : %d\n", block.Difficulty)
fmt.Printf("Nonce : %d\n", block.Nonce)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
}*/
}
(6)编译运行,并查看数据库文件大小。
> go build -o block.exe .
> block
> dir/a blockchain.db
(1)定义迭代器结构体
type BlockChainIterator struct {
Db *bolt.DB
Index []byte // 迭代器指针的索引值
}
(2)提供创建方法
// 创建迭代器
func (bc *BlockChain) NewIterator() *BlockChainIterator {
itr := BlockChainIterator{
bc.Db,
bc.Tail,
}
return &itr
}
(3)定义迭代函数
// 遍历上一个区块
func (itr *BlockChainIterator) Prev() *Block {
var block *Block
itr.Db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
if bucket == nil {
panic("bucket shoud not be nil!")
}
// 获取最后区块
blockBytes := bucket.Get(itr.Index)
// 反序列化
block = Deserialize(blockBytes)
// 移动指针
itr.Index = block.PrevHash
return nil
})
return block
}
(4)改写main函数
func main() {
bc := NewBlockChain()
bc.AddBlock("1111")
bc.AddBlock("2222")
// 定义迭代器
itr := bc.NewIterator()
for {
// 调用迭代器函数,返回当前block,并且向左移动
block := itr.Prev()
fmt.Println("========================")
fmt.Printf("Version : %d\n", block.Version)
fmt.Printf("PreHash : %x\n", block.PrevHash)
fmt.Printf("MerKleRoot : %x\n", block.MerKleRoot)
fmt.Printf("TimeStamp : %d\n", block.TimeStamp)
fmt.Printf("Difficulty : %d\n", block.Difficulty)
fmt.Printf("Nonce : %d\n", block.Nonce)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
// 退出条件
if len(block.PrevHash) == 0 {
break
}
}
}
(1)定义命令行结构体
type Cli struct {
bc *BlockChain
}
(2)定义Run函数,用于与用户进行交互。
const Usage = `
block addBlock --data DATA "添加新区块"
block printChain "打印区块链"
block getBalance --address ADDRESS "查询账户余额"
block send FROM TO AMOUNT MINER DATA "由FROM给TO转AMOUNT个比特币,并指定MINER为矿工"
block newWallet "创建一个钱包"
block listAddress "列出所有钱包地址"
`
func (cli *Cli) Run() {
args := os.Args
if len(args) < 2 {
fmt.Println(Usage)
return
}
// 获取命令名称
command := args[1]
switch command {
case "addBlock":
if len(os.Args) == 4 && os.Args[2] == "--data" {
data := os.Args[3]
if data == "" {
fmt.Println("data should not be empty!")
return
}
cli.addBlock(data)
}
case "printChain":
cli.PrintChain()
default:
fmt.Println(Usage)
}
}
(3)提供AddBlock函数
// 添加区块
func (cli *Cli) addBlock(data string) {
cli.bc.AddBlock(data)
}
(4)提供PrintChain函数
// 打印区块
func (cli *Cli) printChain() {
bc := cli.bc
// 定义迭代器
itr := bc.NewIterator()
for {
// 调用迭代器函数,返回当前block,并且向左移动
block := itr.Prev()
fmt.Println("========================")
fmt.Printf("Version : %d\n", block.Version)
fmt.Printf("PreHash : %x\n", block.PrevHash)
fmt.Printf("MerKleRoot : %x\n", block.MerKleRoot)
fmt.Printf("TimeStamp : %d\n", block.TimeStamp)
fmt.Printf("Difficulty : %d\n", block.Difficulty)
fmt.Printf("Nonce : %d\n", block.Nonce)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
// 退出条件
if len(block.PrevHash) == 0 {
break
}
}
}
(5)修改main函数
func main() {
bc := NewBlockChain()
cli := Cli{bc}
cli.Run()
}
(6)创建运行脚本
@del blockchain.db
@go build -o block.exe .
@block