一直想找个机会把这部分写一下的,今天刚好有空,希望用最简单方式把这部分逻辑讲清楚,老规矩还是看图说话吧
图1 挖矿功能核心对象之间的关系
挖矿的核心功能涉及到了Miner、worker、CpuAgent、RemoteAgent、Work、Clipue、Engine多个对象的协作,它们之间的关系如下 :
图2 挖矿功能核心对象的定义
从上面的类图也可以看出来,一个Miner只有一个助理worker, Miner只负责一些最高级的工作如启停挖矿。一个助理worker,可以雇佣多个工人(一般是多种类型的工人),新建任务Work,下发任务Work给工人(CpuAgent),并接收工人(CpuAgent)的成果,接收成果后的处理代码在worker.wait()中实现:
func (self *worker) wait() {
for {
mustCommitNewWork := true
for result := range self.recv { //self.recv保存了工人成果(其实就是成功爆破出满足规则的区块hash)
atomic.AddInt32(&self.atWork, -1)
if result == nil {
continue
}
block := result.Block
work := result.Work
// Update the block hash in all logs since it is now available and not when the
// receipt/log of individual transactions were created.
for _, r := range work.receipts {
for _, l := range r.Logs {
l.BlockHash = block.Hash()
}
}
for _, log := range work.state.Logs() {
log.BlockHash = block.Hash()
}
//把这个新区块写入本地leveldb
stat, err := self.chain.WriteBlockWithState(block, work.receipts, work.state)
if err != nil {
log.Error("Failed writing block to chain", "err", err)
continue
}
// check if canon block and write transactions
if stat == core.CanonStatTy {
// implicit by posting ChainHeadEvent
mustCommitNewWork = false
}
// Broadcast the block and announce chain insertion event
//通过设置事件把自己挖的新区块广播出来,已经达到宣告的目的(兄弟们,我挖到了,快来看,帮见证一下)
self.mux.Post(core.NewMinedBlockEvent{Block: block})
var (
events []interface{}
logs = work.state.Logs()
)
events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
if stat == core.CanonStatTy {
//如果上面写入了规范链,这里添加一个事件,通过这个事件来开启下一次的挖矿任务(commitNewWork)
events = append(events, core.ChainHeadEvent{Block: block})
}
self.chain.PostChainEvents(events, logs)
// Insert the block into the set of pending ones to wait for confirmations
self.unconfirmed.Insert(block.NumberU64(), block.Hash())
if mustCommitNewWork {
self.commitNewWork()
}
}
}
}