持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情
区块链数据如何遍历
要想做到区块链数据遍历,就应该搞清楚数据的存储形式。我们知道,可以从最新的块hash值获得该区块数据,在区块数据内可以找到前一块的hash值,然后以此类推,就可以一直遍历到创世块。
1、利用gob将区块数据还原
利用gon.NewDecoder来构造解码器,将之前序列化数据传入就可以还原为区块数据
//区块数据还原为Block
func DeserializeBlock(d []byte) *Block {
var block Block
//创建解码器
decoder := gob.NewDecoder(bytes.NewReader(d))
//解析区块数据
decoder.Decode(&block)
return &block
}
2、设计迭代器
因为遍历时涉及数据赋值变化,为了避免遍历对整个区块链影响,增加一个迭代器实现遍历。
//迭代器
type BlockchainIterator struct {
currentHash []byte //当区块hash
db *bolt.DB //已经打开的数据库
}
//通过Blockchain构造迭代器
func (bc *Blockchain) Iterator() *BlockchainIterator {
bci := &BlockchainIterator{bc.tip, bc.db}
return bci
}
3、使用迭代获取区块并将hash指向前一区块
实现PreBlock方法,获取当前区块的值,并告知是否还有前块
//获取前一个区块hash,返回当区块数据
func (i *BlockchainIterator) PreBlock() (*Block, bool) {
var block *Block
//根据hash获取块数据
i.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(blocksBucket))
encodedBlock := b.Get(i.currentHash)
//解码当前块数据
block = DeserializeBlock(encodedBlock)
return nil
})
//当前hash变更为前块hash
i.currentHash = block.PrevBlockHash
//返回区块
return block, len(i.currentHash) > 0
}
main函数开始遍历
准备完成,在main函数中添加代码开始遍历。
bci := bc.Iterator()
for {
block, next := bci.PreBlock()
fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)
fmt.Printf("Data: %s\n", block.Transactions[0].Vin[0].FromAddr)
fmt.Printf("Hash: %x\n", block.Hash)
fmt.Printf("Nonce: %d\n", block.Nonce)
pow := NewProofOfWork(block)
fmt.Printf("Pow: %t\n", pow.Validate())
fmt.Println()
if !next {
//next为假代表已经到创世块了
break
}
}
总结
我们可以在网上搜一下区块链持久化的示意图,这能够更深层次的帮助我们理解。