配合代碼食用(Geth v1.9.0 stable)
以太坊目前有ethash和clique兩個共識引擎,其中ethash是用于正式網絡的PoW(proof-of-work)共識引擎,clique是用于測試網絡的PoA(proof-of-authority)共識引擎
代碼流程
1.eth/backend.go
- New方法創建Ethereum對象時,會調用miner.New方法創建礦工對象,作為Ethereum的一個字段
- Ethereum.StartMining方法會根據給定的cpu線程數開啟挖礦Ethereum.miner.Start
2.miner/miner.go
- Miner,礦工類型,其字段包括worker(執行挖礦工作),eth(所關聯的以太坊節點),engine(共識引擎)等
- New方法會創建Miner對象,并開啟協程go miner.update()處理downloader同步相關事件(在同步開始時停止挖礦,在同步結束或失敗后重新開始挖礦)和退出事件
- Miner.Start方法會調用Miner.worker.start開始挖礦
3.miner/worker.go
- worker,負責提交新的工作(new work)給共識引擎(進行hash運算或其他方式得到滿足條件的block)并收集打包好的block
- newWorker方法,創建worker并協程啟動四個loop:
- go worker.newWorkLoop(recommit)
- startCh收到挖礦開始信號,移除pendingTasks中過時的task,提交新的挖礦任務請求(發送newWorkReq到newWorkCh)
- chainHeadCh收到ChainHeadEvent,移除pendingTasks中過時的task,提交新的挖礦任務請求
- <-timer.C,default流程,判斷如果正在挖礦則重新提交挖礦工作(周期性地拉取價格更高的交易)
- exitCh收到退出信號,return
- go worker.mainLoop()
- newWorkCh收到的新的挖礦任務請求(newWorkReq),執行worker.commitNewWork提交新的task(發送task到taskCh)
- chainSideCh收到ChainSideEvent,在叔區塊未知/正在挖礦/當前叔區塊數小于2時,添加該叔區塊并執行worker.commit提交新的task
- txsCh收到NewTxsEvent時,判斷如果不在挖礦,執行worker.commitTransactions處理交易并更新快照,否則不做操作
- exitCh收到退出信號或txsSub/chainHeadSub/chainSideSub收到Error,return
- go worker.taskLoop()
- taskCh收到commit方法中提交的task(receipts,state,block,createdAt),將task加入pendingTasks中,并調用worker.engine.Seal方法進行hash運算直到找到一個nonce使得區塊的難度值滿足要求(即挖礦成功)
- exitCh收到退出信號,return
- go worker.resultLoop()
- resultCh收到共識引擎找到nonce后發送的block:
- 刪除pendingTasks中的記錄
- worker.chain.WriteBlockWithState(block, receipts, task.state)提交block,receipts和state到數據庫
- 廣播block(NewMinedBlockEvent{Block: block})
- 廣播鏈的插入事件(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}, ChainHeadEvent{Block: block})
- 將block插入unconfirmed以進行后續的確認
- exitCh收到退出信號,return
- resultCh收到共識引擎找到nonce后發送的block:
- go worker.newWorkLoop(recommit)
- worker.commitNewWork具體邏輯:
- 判斷時間戳,要保證timestamp大于上一個塊的時間戳,如果timestamp>now+1要等待避免timestamp太超前
- 構造Header對象
- 調用worker.engine.Prepare方法,根據parent計算難度值給header的Difficulty字段賦值
- 提交叔區塊
- 取出交易池pending中的交易并執行worker.commitTransactions,該方法提交交易給evm執行并更新狀態
- 調用worker.commit方法
- worker.commit具體邏輯:
- 調用worker.engine.Finalize方法,該方法給礦工計算并加上礦工獎勵,計算header.Root并賦值,返回最終定稿的block
- 判斷如果在挖礦,提交新的task(發送task到taskCh)
4.consensus/ethash/sealer.go
- Ethash.Seal方法,核心邏輯是調用Ethash.mine方法,一直做hash運算直到符合難度值要求,把挖出的block放入worker.resultCh,在worker.resultLoop中處理
流程總結
worker:
newWorkLoop:發送newWorkReq到newWorkCh
mainLoop:newWorkCh收到newWorkReq,發送task到taskCh
taskLoop:taskCh收到task,worker.engine.Seal進行hash運算
consensus:
- hash解題成功,發送block到worker.resultCh
worker:
- resultLoop:resultCh收到block,廣播和寫入