本章介绍bytom代码孤块管理
作者使用MacOS操作系统,其他平台也大同小异
Golang Version: 1.8
孤块介绍
什么是孤块
当节点收到了一个有效的区块,而在现有的主链中却未找到它的父区块,那么这个区块被认为是“孤块”。父区块是指当前区块的PreviousBlockHash字段指向上一区块的hash值。
接收到的孤块会被存储在孤块池中,直到它们的父区块被节点收到。一旦收到了父区块,节点就会将孤块从孤块池中取出,并且连接到它的父区块,让它作为区块链的一部分。
孤块出现的原因
当两个或多个区块在很短的时间间隔内被挖出来,节点有可能会以不同的顺序接收到它们,这个时候孤块现象就会出现。
我们假设有三个高度分别为100、101、102的块,分别以102、101、100的颠倒顺序被节点接收。此时节点将102、101放入到孤块管理缓存池中,等待彼此的父块。当高度为100的区块被同步进来时,会被验证区块和交易,然后存储到区块链上。这时会对孤块缓存池进行递归查询,根据高度为100的区块找到101的区块并存储到区块链上,再根据高度为101的区块找到102的区块并存储到区块链上。
孤块源码分析
孤块管理缓存池结构体
protocol/orphan_manage.go
type OrphanManage struct {
orphan map[bc.Hash]*types.Block
prevOrphans map[bc.Hash][]*bc.Hash
mtx sync.RWMutex
}
func NewOrphanManage() *OrphanManage {
return &OrphanManage{
orphan: make(map[bc.Hash]*types.Block),
prevOrphans: make(map[bc.Hash][]*bc.Hash),
}
}
- orphan 存储孤块,key为block hash,value为block结构体
- prevOrphans 存储孤块的父块
- mtx 互斥锁,保护map结构在多并发读写状态下保持数据一致
添加孤块到缓存池
func (o *OrphanManage) Add(block *types.Block) {
blockHash := block.Hash()
o.mtx.Lock()
defer o.mtx.Unlock()
if _, ok := o.orphan[blockHash]; ok {
return
}
o.orphan[blockHash] = block
o.prevOrphans[block.PreviousBlockHash] = append(o.prevOrphans[block.PreviousBlockHash], &blockHash)
log.WithFields(log.Fields{"hash": blockHash.String(), "height": block.Height}).Info("add block to orphan")
}
当一个孤块被添加到缓存池中,还需要记录该孤块的父块hash。用于父块hash的查询