一直想找個機會把這部分寫一下的,今天剛好有空,希望用最簡單方式把這部分邏輯講清楚,老規矩還是看圖說話吧
圖1 挖礦功能核心對象之間的關係
挖礦的核心功能涉及到了Miner、worker、CpuAgent、RemoteAgent、Work、Clipue、Engine多個對象的協作,它們之間的關係如下 :
- Miner:總包工頭(管大局,發號命令的)
- worker:總包工頭助理(代替總包統管所有事務)
- CpuAgent/RemoteAgent: 工人(真正幹活的人)
- Work: 代表了要乾的事情
- ethash/Clique: 幹活的工具
圖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()
}
}
}
}