下面的代码都是基于go-ethereum v1.6.7。
Q:以太坊go-ethereum中,当一个miner找到一个新区块时,会将结果Result发送到管道returnCh中。那接下来的逻辑是怎样的呢?
//miner/agent.go
func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
if result, err := self.engine.Seal(self.chain, work.Block, stop); result != nil {
log.Info("Successfully sealed new block", "number", result.Number(), "hash", result.Hash())
//send操作
self.returnCh <- &Result{work, result}
} else {
//...
}
}
那是否有miners监听该管道returnCh呢?答案是:Yes!worker的wait方法一直迭代管道recv。只要拿到不为nil的数据,则将新区块写入本地数据库中,同时发送事件NewMinedBlockEvent。
下面负责持久化,发送相应事件等功能的协程一直处于阻塞之中。
//miner/worker.go
func (self *worker) wait() {
for {
mustCommitNewWork := true
for result := range self.recv { //从self实例的管道中接收结果
atomic.AddInt32(&self.atWork, -1)
if result == nil {
continue
}
block := result.Block
work := result.Work
if self.fullValidation {
//...
go self.mux.Post(core.NewMinedBlockEvent{Block: block}) //发送事件NewMinedBlockEvent
} else {
//...
stat, err := self.chain.WriteBlock(block) //持久化新区块到本地数据库中
//...
}
//...
// broadcast before waiting for validation
go func(block *types.Block, logs []*types.Log, receipts []*types.Receipt) {
self.mux.Post(core.NewMinedBlockEvent{Block: block}) //发送NewMinedBlockEvent事件,/eth/handler.go#(pm *ProtocolManager) Start()方法会订阅该事件并同时开一个协程【go pm.minedBroadcastLoop()】监听该事件
self.mux.Post(core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
if stat == core.CanonStatTy {
self.mux.Post(core.ChainHeadEvent{Block: block}) //发送ChainHeadEvent事件,/miner/worker.go#(self *worker) update() 方法会读取该事件
self.mux.Post(logs)
}
//...
}(block, work.state.Logs(), work.receipts) //匿名函数调用
}
// Insert the block into the set of pending ones to wait for confirmations
//矿工将自己开采的区块放进一个循环列表中,待达到五个确认后,再移除