- 从交易扔到tx的pending池开始分析,不言而喻现在该是矿工登场的时候了
- 该方法就是挖矿的开始处,其中
start()方法是调用所有代理cpu或者远程代理开始挖矿工作,让我们看核心的commitNewWork()代码 - 该方法根据上一个块的信息构造出了本次出块的header信息,本次出块的难度,如果上一个块的时间是未来的某个时间,那么就需要一直sleep直到对应的时间,然后
work := self.current构建了当前出块任务的对象, - 该方法就是验证当前work中的每一笔交易是不是合法的,如果合法就加入到当前work的交易列表中,接着看
commitNewWork()方法下的Finalize方法 - 该方法其实就是计算好该块的出块奖励,接着看
commitNewWork()方法下的最后一个方法 - 该方法就是把当前出块的任务推送到每一个代理,通过管道的形式写入到每个代理的work管道,到此为止,下个块的信息已经发送给每个代理了,那么接着看代理如何出块,首先看结构
- 上面的分析当前块的任务以及信息已经通过管道写入到work中了,那么让我们看
work方法,谁来接收并处理呢?让我们将目光放到/miner/agent.go/update方法 - 可以看到刚才写入到管道的当前块任务在这里并接收并且处理,调用
self.mine(work, self.quitCurrentOp)进行挖矿,谁先计算出符合该块上面的难度hash,谁就能够产块,至此和共识包下的pow的分析第二篇形式闭环
- 该方法就是挖矿的开始处,其中
从交易扔到tx的pending池开始分析,不言而喻现在该是矿工登场的时候了
/miner/miner.go/Start
func (self *Miner) Start(coinbase common.Address) {atomic.StoreInt32(&self.shouldStart, 1)self.worker.setEtherbase(coinbase)self.coinbase = coinbaseif 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.commitNewWork()}
该方法就是挖矿的开始处,其中start()方法是调用所有代理cpu或者远程代理开始挖矿工作,让我们看核心的commitNewWork()代码
func (self *worker) commitNewWork() {self.mu.Lock()defer self.mu.Unlock()self.uncleMu.Lock()defer self.uncleMu.Unlock()self.currentMu.Lock()defer self.currentMu.Unlock()tstart := time.Now()parent := self.chain.CurrentBlock()tstamp := tstart.Unix()if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {tstamp = parent.Time().Int64() + 1}// this will ensure we're not going off too far in the futureif now := time.Now().Unix(); tstamp > now+1 {wait := time.Duration(tstamp-now) * time.Secondlog.Info("Mining too far in the future", "wait", common.PrettyDuration(wait))time.Sleep(wait)}num := parent.Number()header := &types.Header{ParentHash: parent.Hash(),Number: num.Add(num, common.Big1),GasLimit: core.CalcGasLimit(parent),GasUsed: new(big.Int),Extra: self.extra,Time: big.NewInt(tstamp),}// Only set the coinbase if we are mining (avoid spurious block rewards)if atomic.LoadInt32(&self.mining) == 1 {header.Coinbase = self.coinbase}if err := self.engine.Prepare(self.chain, header); err != nil {log.Error("Failed to prepare header for mining", "err", err)return}// If we are care about TheDAO hard-fork check whether to override the extra-data or notif daoBlock := self.config.DAOForkBlock; daoBlock != nil {// Check whether the block is among the fork extra-override rangelimit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 {// Depending whether we support or oppose the fork, override differentlyif self.config.DAOForkSupport {header.Extra = common.CopyBytes(params.DAOForkBlockExtra)} else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) {header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data}}}// Could potentially happen if starting to mine in an odd state.err := self.makeCurrent(parent, header)if err != nil {log.Error("Failed to create mining context", "err", err)return}// Create the current work task and check any fork transitions neededwork := self.currentif self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {misc.ApplyDAOHardFork(work.state)}pending, err := self.eth.TxPool().Pending()if err != nil {log.Error("Failed to fetch pending transactions", "err", err)return}txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending)work.commitTransactions(self.mux, txs, self.chain, self.coinbase)// compute uncles for the new block.var (uncles []*types.HeaderbadUncles []common.Hash)for hash, uncle := range self.possibleUncles {if len(uncles) == 2 {break}if err := self.commitUncle(work, uncle.Header()); err != nil {log.Trace("Bad uncle found and will be removed", "hash", hash)log.Trace(fmt.Sprint(uncle))badUncles = append(badUncles, hash)} else {log.Debug("Committing new uncle to block", "hash", hash)uncles = append(uncles, uncle.Header())}}for _, hash := range badUncles {delete(self.possibleUncles, hash)}// Create the new block to seal with the consensus engineif work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil {log.Error("Failed to finalize block for sealing", "err", err)return}// We only care about logging if we're actually mining.if atomic.LoadInt32(&self.mining) == 1 {log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart)))self.unconfirmed.Shift(work.Block.NumberU64() - 1)}self.push(work)}
该方法根据上一个块的信息构造出了本次出块的header信息,本次出块的难度,如果上一个块的时间是未来的某个时间,那么就需要一直sleep直到对应的时间,然后work := self.current构建了当前出块任务的对象,
然后就是从pending交易池里面获取交易进行创建交易,然后就是提交交易,接着看work.commitTransactions方法下的env.commitTransaction方法
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) {snap := env.state.Snapshot()receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})if err != nil {env.state.RevertToSnapshot(snap)return err, nil}env.txs = append(env.txs, tx)env.receipts = append(env.receipts, receipt)return nil, receipt.Logs}
该方法就是验证当前work中的每一笔交易是不是合法的,如果合法就加入到当前work的交易列表中,接着看commitNewWork()方法下的Finalize方法
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,// setting the final state and assembling the block.func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {// Accumulate any block and uncle rewards and commit the final state rootAccumulateRewards(chain.Config(), state, header, uncles)header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))// Header seems complete, assemble into a block and returnreturn types.NewBlock(header, txs, uncles, receipts), nil}
该方法其实就是计算好该块的出块奖励,接着看commitNewWork()方法下的最后一个方法
// push sends a new work task to currently live miner agents.func (self *worker) push(work *Work) {if atomic.LoadInt32(&self.mining) != 1 {return}for agent := range self.agents {atomic.AddInt32(&self.atWork, 1)if ch := agent.Work(); ch != nil {ch <- work}}}
该方法就是把当前出块的任务推送到每一个代理,通过管道的形式写入到每个代理的work管道,到此为止,下个块的信息已经发送给每个代理了,那么接着看代理如何出块,首先看结构
// Agent can register themself with the workertype Agent interface {Work() chan<- *WorkSetReturnCh(chan<- *Result)Stop()Start()GetHashRate() int64}
上面的分析当前块的任务以及信息已经通过管道写入到work中了,那么让我们看work方法,谁来接收并处理呢?让我们将目光放到/miner/agent.go/update方法
func (self *CpuAgent) update() {out:for {select {case work := <-self.workCh:self.mu.Lock()if self.quitCurrentOp != nil {close(self.quitCurrentOp)}self.quitCurrentOp = make(chan struct{})go self.mine(work, self.quitCurrentOp)self.mu.Unlock()case <-self.stop:self.mu.Lock()if self.quitCurrentOp != nil {close(self.quitCurrentOp)self.quitCurrentOp = nil}self.mu.Unlock()break out}}}
