现在我们想要把区块存储进数据库中并且根据需要遍历某个区块的信息
3.1区块的存储这里我们采用Go语言自带的数据库,如果在安装Go语言时没有安装,则需要通过如下链接安装
go get github.com/boltdb/bolt
安装成功后才能实现区块的存储和遍历
我们用键值对来实现对区块的存储,key:当前区块的哈希,value:当前区块序列化后的数组。当然遍历时我们就需要进行反序列化以还原区块信息,现在我们一并构建这它们
/Users/xxx/go/src/publicChain/part15-persistence/BLC/Block.go
//SerializeBlock
func (block *Block) Serialize() []byte {
var result bytes.Buffer
encoder := gob.NewEncoder(&result)
err := encoder.Encode(block)
if err != nil {
log.Panic(err)
}
return result.Bytes()
}
//DeserializeBlock
func DeserializeBlock(blockBytes []byte) *Block {
var block Block
decoder := gob.NewDecoder(bytes.NewReader(blockBytes))
err := decoder.Decode(&block)
if err != nil {
log.Panic()
}
return &block
}
在此之前,我们会将新产生的区块加入到链上,现在我们重新改写一下:在这里我们将会把新产生的区块存入数据库中的数据表里。事实上,只要一个区块的产生依赖于上一个区块,那这些区块自动就是一条“区块链”,所以并不需要我们再重构一条链,我们只需要通过一定的方式处理这些区块即可。前面所说的把一个个区块加入到“链”上其实就是一种处理方式,现在我们把这种方式改变一下,也就是说直接把区块装入数据库中。下面是实现代码:
/Users/xxx/go/src/publicChain/part15-persistence/BLC/Blockchain.go
const dbName = "blockChain.db"
const blockTableName = "blocks"
type Blockchain struct {
Tip []byte //the hash of current block
DB *bolt.DB //database
}
//add a new block to blockchain
func (blc *Blockchain) AddBlockToBlockchain(data string) {
err := blc.DB.Update(func(tx *bolt.Tx) error {
//get a table
b := tx.Bucket([]byte(blockTableName))
//crate a new block
if b != nil {
//Get the latest block
blockBytes := b.Get(blc.Tip)
//Deserialize
block := DeserializeBlock(blockBytes)
//Serialize the block and store it in the database
newBlock := Newblock(block.Height+1, block.Hash, data)
err := b.Put(newBlock.Hash, newBlock.Serialize())
if err != nil {
log.Panic(err)
}
//Update the hash corresponding to l in the database
err = b.Put([]byte("l"), newBlock.Hash)
if err != nil {
log.Panic(err)
}
//Update blockchain tip
blc.Tip = newBlock.Hash
}
return nil
})
if err != nil {
log.Panic(err)
}
}
//1 creat a blockchain with Genesis Block
func CreatBlockchainWithGenesisBlock() *Blockchain {
//creat a database
db, err := bolt.Open(dbName, 0600, nil)
if err != nil {
log.Fatal(err)
}
var blockHash []byte
err = db.Update(func(tx *bolt.Tx) error {
//creat a table
b, err := tx.CreateBucket([]byte(blockTableName))
if err != nil {
log.Panic()
}
if b != nil {
genesisBlock := CrateGenesisBlock("Genesis Data......")
//Store the genesis block into a table
err := b.Put(genesisBlock.Hash, genesisBlock.Serialize())
if err != nil {
log.Panic(err)
}
//Store the hash of current block
err = b.Put([]byte("l"), genesisBlock.Hash)
if err != nil {
log.Panic(err)
}
blockHash = genesisBlock.Hash
}
return nil
})
return &Blockchain{blockHash, db}
}
3.2区块的遍历
在上面的代码中,我们已经可以将新产生的区块装入数据库中的数据表中,现在我们遍历打印
/Users/xxx/go/src/publicChain/part16-persistence-iteration/BLC/Blockchain.go
//iterate over all blocks
func (blc *Blockchain) PrintChain() {
var block *Block
var currentHash []byte = blc.Tip
for {
err := blc.DB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blockTableName))
if b != nil {
// Get the byte array of the current block
blockBytes := b.Get(currentHash)
//Deserialize
block = DeserializeBlock(blockBytes)
fmt.Printf("Height:%d\n", block.Height)
fmt.Printf("PreBlockHash:%x\n", block.PreBlockHash)
fmt.Printf("Data:%s\n", block.Data)
fmt.Printf("Timestamp:%d\n", block.Timestamp)
fmt.Printf("Hash:%x\n", block.Hash)
fmt.Printf("Nonce:%d\n", block.Nonce)
}
return nil
})
fmt.Println()
if err != nil {
log.Panic(err)
}
var hashInt big.Int
hashInt.SetBytes(block.PreBlockHash)
if big.NewInt(0).Cmp(&hashInt) == 0 {
break
}
currentHash = block.PreBlockHash
}
}
我们在main函数中稍作修改,然后运行,非常棒,我们能够成功的遍历区块并且打印它们
func main() {
//creat a Genesis Block
Blockchain := BLC.CreatBlockchainWithGenesisBlock()
defer Blockchain.DB.Close()
// new block
Blockchain.AddBlockToBlockchain("Send 100RMB To Zhangqiang")
Blockchain.AddBlockToBlockchain("Send 300RMB To Xinyi")
Blockchain.AddBlockToBlockchain("Send 200RMB To Gaojian")
Blockchain.AddBlockToBlockchain("Send 500RMB To Xiaochu")
Blockchain.PrintChain()
}
0000b5430784409081e82c0de2b08267f1a803ccfb066fdc526f223ede70799d
000008339b4489740c7bbb2d57b72cba32744359feab79b9ef0e0ef587cb0e24
0000d68e0482e967652f34a3a26be66920d3444851764a4abdb5a16e76742114
0000e513daca4c4148da99343e036e7d77c673ff6039e7f8420483de0c63db45
0000f6bcd5f300d6ebd557a9a10128baea0a89c79b03e4614a0b9898aece1a40
Height:5
PreBlockHash:0000e513daca4c4148da99343e036e7d77c673ff6039e7f8420483de0c63db45
Data:Send 500RMB To Xiaochu
Timestamp:1645877202
Hash:0000f6bcd5f300d6ebd557a9a10128baea0a89c79b03e4614a0b9898aece1a40
Nonce:74522
Height:4
PreBlockHash:0000d68e0482e967652f34a3a26be66920d3444851764a4abdb5a16e76742114
Data:Send 200RMB To Gaojian
Timestamp:1645877201
Hash:0000e513daca4c4148da99343e036e7d77c673ff6039e7f8420483de0c63db45
Nonce:48490
Height:3
PreBlockHash:000008339b4489740c7bbb2d57b72cba32744359feab79b9ef0e0ef587cb0e24
Data:Send 300RMB To Xinyi
Timestamp:1645877201
Hash:0000d68e0482e967652f34a3a26be66920d3444851764a4abdb5a16e76742114
Nonce:7929
Height:2
PreBlockHash:0000b5430784409081e82c0de2b08267f1a803ccfb066fdc526f223ede70799d
Data:Send 100RMB To Zhangqiang
Timestamp:1645877201
Hash:000008339b4489740c7bbb2d57b72cba32744359feab79b9ef0e0ef587cb0e24
Nonce:38449
Height:1
PreBlockHash:0000000000000000000000000000000000000000
Data:Genesis Data......
Timestamp:1645877201
Hash:0000b5430784409081e82c0de2b08267f1a803ccfb066fdc526f223ede70799d
Nonce:36943
3.3 时间格式格式化输出
更改一下上述PrintChain方法中的时间打印方式
fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 03:04:05 PM"))
部分输出结果如下,我们看到时间被格式化打印而出了
Height:5
PreBlockHash:0000d41c2f482e9f8885e31065368da0ddbe0b0aa6d4797cab09a8d0c42eb0d2
Data:Send 500RMB To Xiaochu
Timestamp:2022-02-26 08:39:34 PM
Hash:00001c26642bbf01f1974a5067cf0dbc0aa62726a89e26059bc0fa3ff7033459
Nonce:13611
3.4 迭代器优化
现在我们来对迭代器所在部分的代码进行优化。让我们先来分析一下思路:第一步,根据最新区块的哈希找到最新的区块,第二步,根据区块数据寻找上一个区块,层层回溯,具体代码实现如下:
/Users/xxx/go/src/publicChain/part16-persistence-iteration/BLC/Blockchain.go
func (blc *Blockchain) Iterator() *BlockChainIterator {
return &BlockChainIterator{blc.Tip, blc.DB}
}
type BlockChainIterator struct {
CurrentHash []byte
DB *bolt.DB
}
func (blockChainIterator *BlockChainIterator) Next() *Block {
var block *Block
err := blockChainIterator.DB.View(func(tx *bolt.Tx) error {
//get a table
b := tx.Bucket([]byte(blockTableName))
if b != nil {
currentBlockBytes := b.Get(blockChainIterator.CurrentHash)
//get current block
block = DeserializeBlock(currentBlockBytes)
//update the hash in Iterator
blockChainIterator.CurrentHash = block.PreBlockHash
}
return nil
})
if err != nil {
log.Panic(err)
}
return block
}
//iterate over all blocks
func (blc *Blockchain) PrintChain() {
blockChainIterator := blc.Iterator()
for {
block := blockChainIterator.Next()
fmt.Printf("Height:%d\n", block.Height)
fmt.Printf("PreBlockHash:%x\n", block.PreBlockHash)
fmt.Printf("Data:%s\n", block.Data)
fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 03:04:05 PM"))
fmt.Printf("Hash:%x\n", block.Hash)
fmt.Printf("Nonce:%d\n", block.Nonce)
fmt.Println()
var hashInt big.Int
hashInt.SetBytes(block.PreBlockHash)
if big.NewInt(0).Cmp(&hashInt) == 0 {
break
}
}
}
上述代码能够正常运行,部分结果如下:
Height:2
PreBlockHash:00004fe9eae1cd4285edcc5d112eb1df4c1cf68a214c612a3afb024ab4f35d43
Data:Send 100RMB To Zhangqiang
Timestamp:2022-02-27 08:54:29 AM
Hash:0000bebc7cb0c274731969cf2ca83c45eb4cb490ed65475cb62fc8f2a2825b6d
Nonce:70176
并且我们可以把上述迭代器部分的代码放到独立文件中
/Users/xxx/go/src/publicChain/part16-persistence-iteration/BLC/BlockchainIterator.go
package BLC
import (
"github.com/boltdb/bolt"
"log"
)
type BlockChainIterator struct {
CurrentHash []byte
DB *bolt.DB
}
func (blockChainIterator *BlockChainIterator) Next() *Block {
var block *Block
err := blockChainIterator.DB.View(func(tx *bolt.Tx) error {
//get a table
b := tx.Bucket([]byte(blockTableName))
if b != nil {
currentBlockBytes := b.Get(blockChainIterator.CurrentHash)
//get current block
block = DeserializeBlock(currentBlockBytes)
//update the hash in Iterator
blockChainIterator.CurrentHash = block.PreBlockHash
}
return nil
})
if err != nil {
log.Panic(err)
}
return block
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)