这章来看下HPB的挖矿流程。在节点启动的时候通过flag参数mine指定是否进行挖矿,当然也可以在节点启动后通过API来调用,启动流程代码调用顺序是go-hpb/cmd/ghpb/main.go-main->ghpb->startNode`。
在startNode方法最后部署代码可以看到,首先判断MiningEnabledFlag和RoleType两个参数后,设置交易的GasPrice后(系统默认为defaultGasPrice = 50 * config.Shannon),开启挖矿。
1 | if ctx.GlobalBool(utils.MiningEnabledFlag.Name) && (conf.Network.RoleType == "") { |
在StartMining方法中
- 设置挖矿地址coinbase,这个地址是在HpbConfig初始化的时候设置的,取的是钱包中第一个帐户地址
- 获挖矿引擎Prometheus,并把coinbase和所在钱包的签名方法传递给引擎。这个操作主要是为了从wallet中取出coinbase的私钥,然后进行挖矿签名
- 开启接收交易的flag
- miner协程启动
1
2
3
4
5if wallets := hpbnode.AccountManager().Wallets(); len(wallets) > 0 {
if account := wallets[0].Accounts(); len(account) > 0 {
hpbnode.hpberbase = account[0].Address
}
}
1 | func (s *Node) StartMining(local bool) error { |
在Start方法中,
- 执行miner.update()方法
- 设置状态为shouldStart==1,表示不允许再次挖矿了,因为已经开始了。
- 设置挖矿地址coinbase
- 判断能否进行挖矿,如果不能则返回,不启动挖矿
- worker.start()开使用挖矿,worker.startNewMinerRound()尝试再次启动挖工worker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18func (self *Miner) Start(coinbase common.Address) {
//
go self.update()
atomic.StoreInt32(&self.shouldStart, 1)
self.worker.setHpberbase(coinbase)
self.coinbase = coinbase
if atomic.LoadInt32(&self.canStart) == 0 {
log.Info("Network syncing, will start miner afterwards")
return
}
atomic.StoreInt32(&self.mining, 1)
log.Info("Starting mining operation")
self.worker.start()
self.worker.startNewMinerRound()
}
在miner.update()方法中,miner.mux订阅了三个事件,分别是下载开始事件,下载结束事件,下载失败事件,这个和以太坊的源码有些不同,以太坊是downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{},作用的是一样,在HPB源码中可以看下三个事件的发出都是和信息同步有关的,比如在synfast.go、synfull.go、synlight.go中的syncWithPeer方法中进行事件发布
- 如果收到下载开始事件,则停止当前的挖矿工作
- 如查收到下载完成或失败事件,则开启挖矿工作,同时取消事件订阅
可以看到update中也有可能启动挖矿行为的,所以使用atomic来进行状态管理,实现线程安全。
1 | // update keeps track of the synctrl events. Please be aware that this is a one shot type of update loop. |
在worker.start()方法中,首先设置了worker的状态,然后再启动producer.Start()。这里producer是接口类,其实现类只有一个,是CpuAgent。
producer绑定CpuAgent是通过这行代码进行注册的miner.Register(NewCpuAgent(bc.InstanceBlockChain(), engine))。代码调用顺序是
–>ghpb
–>startNode
–>utils.StartNode(stack)
–>stack.Start(stack.Hpbconfig)
–>hpbnode.WorkerInit(conf)
–>hpbnode.miner = worker.New(&conf.BlockChain, hpbnode.NewBlockMux(), hpbnode.Hpbengine, hpbnode.hpberbase)
–>miner.Register(NewCpuAgent(bc.InstanceBlockChain(), engine))
1 | func (self *worker) start() { |
接下来直接看下CpuAgent.Start()方法
- 首先进行CAS状态判断
- 协程启动update方法,这个类似于miner的update方法
1 | func (self *CpuAgent) Start() { |
update方法中,在死循环中不断从chan中获取数据,如果数据类型是stop,则退出循环,如果是workCh则开始挖矿。stop事件是在miner.update方法中会传递。work的传递是在miner.Start()方法中startNewMinerRound()<-makeCurrent()传递进去的。
1 | func (self *CpuAgent) update() { |
go self.mine(work, self.quitCurrentOp)才开始真正的挖矿计算,,下一章节再详细分析。