1. 程式人生 > >以太坊原始碼分析(16)挖礦分析

以太坊原始碼分析(16)挖礦分析

### 從交易扔到tx的pending池開始分析,不言而喻現在該是礦工登場的時候了
/miner/miner.go/Start
```func (self *Miner) Start(coinbase common.Address) {    atomic.StoreInt32(&self.shouldStart, 1)    self.worker.setEtherbase(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.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 future    if now := time.Now().Unix(); tstamp > now+1 {        wait := time.Duration(tstamp-now) * time.Second        log.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 not    if daoBlock := self.config.DAOForkBlock; daoBlock != nil {        // Check whether the block is among the fork extra-override range        limit := 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 differently            if 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 needed    work := self.current    if 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.Header        badUncles []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 engine    if 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 root    AccumulateRewards(chain.Config(), state, header, uncles)    header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
    // Header seems complete, assemble into a block and return    return 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<- *Work    SetReturnCh(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        }    }}
```

##### 可以看到剛才寫入到管道的當前塊任務在這裡並接收並且處理,呼叫`self.mine(work, self.quitCurrentOp)`進行挖礦,誰先計算出符合該塊上面的難度hash,誰就能夠產塊,至此和共識包下的pow的分析第二篇形式閉環




網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN