Miner层是在共识引擎之上的一层逻辑,以太坊的共识引擎已经实现了插件化,无论共识引擎是ethash或者clique,miner的逻辑还是一样的。
实现
Miner层的逻辑是复杂琐碎的,完整地陈述它意义不大,我们可以抓住我们期待它应该有的核心功能和对应的实现方式,就能很好地把握整个逻辑。
- 当网络的区块已经被抢先挖走的时候,应该能及时退出当前的挖矿逻辑,在最新的链上重新开始工作。
- 总是追求利益最大化:交易是按照gas-price从高到低挑选入块,避免一定程度的空块而尽量交易包含入块而获取较高的gas fee,尽可能挑选叔块是自己出得,而获取更多的叔块奖励,尽可能让本地的tx优先入块。
- 能够和外部良好地互动:管理员能自己启动和停止挖矿,更换coinbase等。在sync的时候自动停止挖矿,而sync done的时候自动开启挖矿。
就这么简单的要求。。看下他的实现:
- Miner 分成两个层次,miner和worker,其中miner就负责实现上述3的功能。
- worker可以看作是一个状态机器,他的外部输入包括 新的block,分叉block,新的tx,整个挖矿任务只有seal阶段可以看作是串行,而其他阶段(prepare,commitTransactions,FinalizeAndAssemble)都是并行的。
- worker启动了4个协程:
- newWorkLoop. 挖矿任务是从这里触发的,有三个触发源头,一个是miner调用的start(比如sync结束启动miner),收到网络中新的块,resubmit定时器超时,收到这些时间的时候,会把上一个任务的interupt信号置位(可以打断其他任务在commitTransactions阶段),推送新的任务信号到mainLoop. 还负责更新resubmit的时间间隔(是根据上一个任务被打断时已经消耗的gas占比来算的,倾向于不要总是尚未 完成commitTransactions就被打断)。
- mainloop. 接受到任务信号,准备区块头,准备current env,把uncle 块 commit到env中,按照最大利益规则把tx安排好,然后commitTransactions(会同时监听interupt信号),FinalizeAndAssemble做后处理和奖励啥的(根据共识引擎不同),把receipts拷贝出来一起放进一个task对象推送给taskLoop。 如果检测到新的分叉块,重新组装uncle,再次调用FinalizeAndAssemble,直接把task推送给taskLoop.
- taskLoop. 接受到task对象,把上一个任务停掉(close stop chan),然后执行共识引擎的sealHash 和seal(实际上seal也是异步的,通过resultChannel把结果放出来).
- resultLoop. 从resultChannel里拿出挖矿的结果啦,把block和state写入到db中,广播一下新block事件。