持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
区块链数据如何遍历
要想做到区块链数据遍历,就应该搞清楚数据的存储形式。我们知道,可以从最新的块hash值获得该区块数据,在区块数据内可以找到前一块的hash值,然后以此类推,就可以一直遍历到创世块。
1、利用gob将区块数据还原
利用gon.NewDecoder来构造解码器,将之前序列化数据传入就可以还原为区块数据 go //区块数据还原为Block func DeserializeBlock(d []byte) *Block { var block Block //创建解码器 decoder := gob.NewDecoder(bytes.NewReader(d)) //解析区块数据 decoder.Decode(&block) return &block }
2、设计迭代器
因为遍历时涉及数据赋值变化,为了避免遍历对整个区块链影响,增加一个迭代器实现遍历。 ```go //迭代器 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方法,获取当前区块的值,并告知是否还有前块 ```go //获取前一个区块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函数中添加代码开始遍历。 go 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 } }
总结
我们可以在网上搜一下区块链持久化的示意图,这能够更深层次的帮助我们理解。