以太坊 miner worker 導(dǎo)讀

代碼版本:1.8.27贸典,此版本的Miner模塊有較大改變配乓,取消了原來的agent模塊以及work對象,但是基本邏輯還是一樣的巢株。Miner模塊的主要執(zhí)行部分在worker中槐瑞,Miner對象及其方法主要控制著模塊的開關(guān)和外部接口。

Miner模塊

// Miner creates blocks and searches for proof-of-work values.
type Miner struct {
    mux      *event.TypeMux
    worker   *worker
    coinbase common.Address
    eth      Backend
    engine   consensus.Engine
    exitCh   chan struct{}
    startCh  chan common.Address
    stopCh   chan struct{}
}

miner.update()方法

監(jiān)聽downloader事件纯续,控制著canStart和shouldStart這兩個(gè)開關(guān)随珠,用于抵擋DOS攻擊。

1猬错、當(dāng)監(jiān)聽到downloader的StartEvent事件時(shí),canStart設(shè)置為0茸歧,表示downloader同步時(shí)不可進(jìn)行挖礦倦炒,如果正在挖礦(miner.mining == ture),停止挖礦软瞎,同時(shí)將shouldStart設(shè)置為1逢唤,以便下次直接開始挖礦;

2涤浇、當(dāng)監(jiān)聽到downloader的DoneEvent事件或者FailedEvent事件鳖藕,判斷shouldStart是否打開。如果是打開的只锭,則再打開canStart著恩,將shouldStart關(guān)閉。此時(shí)蜻展,將挖礦的控制權(quán)完全交給miner.Start()方法喉誊。

miner.start()方法很簡單,打開shouldstart,設(shè)置coinbase纵顾,然后啟動(dòng)worker

func (miner *Miner) Start(coinbase common.Address) {
    miner.startCh <- coinbase
}

func (miner *Miner) update() {
...
        case addr := <-miner.startCh:
            miner.SetEtherbase(addr)
            if canStart {
                miner.worker.start()
            }
            shouldStart = true
...
}

Worker模塊

下圖是miner主要的流程圖伍茄,清晰的說明了worker的工作原理

miner

Worker的數(shù)據(jù)結(jié)構(gòu)如下,比較重要的已經(jīng)注釋:

// worker is the main object which takes care of submitting new work to consensus engine
// and gathering the sealing result.
type worker struct {
    config      *Config
    chainConfig *params.ChainConfig
    engine      consensus.Engine //共識引擎
    eth         Backend                    //以太坊終端
    chain       *core.BlockChain   //區(qū)塊鏈對象

    // Feeds
    pendingLogsFeed event.Feed

    // Subscriptions
    mux          *event.TypeMux
    txsCh        chan core.NewTxsEvent //交易池更新事件
    txsSub       event.Subscription
    chainHeadCh  chan core.ChainHeadEvent //區(qū)塊頭更新事件
    chainHeadSub event.Subscription
    chainSideCh  chan core.ChainSideEvent //區(qū)塊鏈分叉事件
    chainSideSub event.Subscription

    // Channels
    newWorkCh          chan *newWorkReq
    taskCh             chan *task
    resultCh           chan *types.Block
    startCh            chan struct{}
    exitCh             chan struct{}
    resubmitIntervalCh chan time.Duration
    resubmitAdjustCh   chan *intervalAdjust

    current      *environment                 // An environment for current running cycle.當(dāng)前挖礦生命周期的執(zhí)行環(huán)境
    localUncles  map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks. 本地分叉區(qū)塊作為潛在叔塊
    remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks. 分叉區(qū)塊鏈中潛在的叔塊
    unconfirmed  *unconfirmedBlocks           // A set of locally mined blocks pending canonicalness confirmations. 本地產(chǎn)生但尚未被確認(rèn)的區(qū)塊

    mu       sync.RWMutex // The lock used to protect the coinbase and extra fields
    coinbase common.Address
    extra    []byte

    pendingMu    sync.RWMutex
    pendingTasks map[common.Hash]*task //挖礦任務(wù)map

    snapshotMu       sync.RWMutex // The lock used to protect the snapshots below
    snapshotBlock    *types.Block//快照的區(qū)塊
    snapshotReceipts types.Receipts
    snapshotState    *state.StateDB//快照的狀態(tài)

    // atomic status counters
    running int32 // The indicator whether the consensus engine is running or not. 判斷引擎是否啟動(dòng)
    newTxs  int32 // New arrival transaction count since last sealing work submitting. 記錄上次遞交任務(wù)后新來的區(qū)塊數(shù)量

    // noempty is the flag used to control whether the feature of pre-seal empty
    // block is enabled. The default value is false(pre-seal is enabled by default).
    // But in some special scenario the consensus engine will seal blocks instantaneously,
    // in this case this feature will add all empty blocks into canonical chain
    // non-stop and no real transaction will be included.
    noempty uint32

    // External functions
    isLocalBlock func(block *types.Block) bool // Function used to determine whether the specified block is mined by local miner.

    // Test hooks
    newTaskHook  func(*task)                        // Method to call upon receiving a new sealing task.
    skipSealHook func(*task) bool                   // Method to decide whether skipping the sealing.
    fullTaskHook func()                             // Method to call before pushing the full sealing task.
    resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval.
}

在初始化miner的時(shí)候施逾,會(huì)新建worker敷矫,調(diào)用newWorker( ),該方法首先配置了worker對象汉额,然后訂閱了交易池事件曹仗、規(guī)范鏈更新事件和分叉事件,啟動(dòng)4個(gè)goroutine闷愤,最后通過向startCh中傳入一個(gè)struct{}{},直接進(jìn)入newWorkerLoop的邏輯整葡。

func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(*types.Block) bool, init bool) *worker {
    worker := &worker{
        config:             config,
        chainConfig:        chainConfig,
        engine:             engine,
        eth:                eth,
        mux:                mux,
        chain:              eth.BlockChain(),
        isLocalBlock:       isLocalBlock,
        localUncles:        make(map[common.Hash]*types.Block),
        remoteUncles:       make(map[common.Hash]*types.Block),
        unconfirmed:        newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth),
        pendingTasks:       make(map[common.Hash]*task),
        txsCh:              make(chan core.NewTxsEvent, txChanSize),
        chainHeadCh:        make(chan core.ChainHeadEvent, chainHeadChanSize),
        chainSideCh:        make(chan core.ChainSideEvent, chainSideChanSize),
        newWorkCh:          make(chan *newWorkReq),
        taskCh:             make(chan *task),
        resultCh:           make(chan *types.Block, resultQueueSize),
        exitCh:             make(chan struct{}),
        startCh:            make(chan struct{}, 1),
        resubmitIntervalCh: make(chan time.Duration),
        resubmitAdjustCh:   make(chan *intervalAdjust, resubmitAdjustChanSize),
    }
    // Subscribe NewTxsEvent for tx pool
    worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)
    // Subscribe events for blockchain
    worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
    worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)

    // Sanitize recommit interval if the user-specified one is too short.
    recommit := worker.config.Recommit
    if recommit < minRecommitInterval {
        log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval)
        recommit = minRecommitInterval
    }

    go worker.mainLoop()
    go worker.newWorkLoop(recommit)
    go worker.resultLoop()
    go worker.taskLoop()

    // Submit first work to initialize pending state.
    if init {
        worker.startCh <- struct{}{}
    }
    return worker
}

NewWorkLoop

newWorkLoop主要監(jiān)聽兩個(gè)重要的通道,一個(gè)是startCh通道讥脐,一個(gè)是chainHeadCh遭居,這兩個(gè)通道均用于清理特定父區(qū)塊的pengding tasks列表啼器,然后遞交基于父區(qū)塊的挖礦task。區(qū)別在于startCh通道啟動(dòng)是基于當(dāng)前的currentBlock俱萍,而chainHeadCh是基于新傳來的區(qū)塊頭端壳。

// newWorkLoop is a standalone goroutine to submit new mining work upon received events.
func (w *worker) newWorkLoop(recommit time.Duration) {
...
    for {
        select {
        case <-w.startCh:
            clearPending(w.chain.CurrentBlock().NumberU64())
            timestamp = time.Now().Unix()
            commit(false, commitInterruptNewHead)

        case head := <-w.chainHeadCh:
            clearPending(head.Block.NumberU64())
            timestamp = time.Now().Unix()
            commit(false, commitInterruptNewHead)

        case <-timer.C: ...

        case interval := <-w.resubmitIntervalCh: ...

        case adjust := <-w.resubmitAdjustCh: ...

        case <-w.exitCh:
            return
        }
    }
}

清理殘留挖礦任務(wù)后,構(gòu)建新的挖礦任務(wù)枪蘑,這時(shí)候調(diào)用commit函數(shù)损谦,構(gòu)建一個(gè)newWorkReq對象,傳入newWorkCh通道岳颇,進(jìn)入MainLoop協(xié)程照捡。MainLoop()監(jiān)聽三個(gè)重要的通道,newWorkCh(新work請求通道)话侧、txsCh(交易池更新事件通道)以及chainSideCh(區(qū)塊鏈分叉事件通道)

MainLoop

// mainLoop is a standalone goroutine to regenerate the sealing task based on the received event.
func (w *worker) mainLoop() {
    defer w.txsSub.Unsubscribe()
    defer w.chainHeadSub.Unsubscribe()
    defer w.chainSideSub.Unsubscribe()

    for {
        select {
                //task1:直接啟動(dòng)commitNetwork,  進(jìn)一步提交挖礦task
        case req := <-w.newWorkCh:
            w.commitNewWork(req.interrupt, req.noempty, req.timestamp)
                // task2:出現(xiàn)分叉后栗精,處理叔塊
        case ev := <-w.chainSideCh:
            // Short circuit for duplicate side blocks 檢驗(yàn)該hash的區(qū)塊是否已經(jīng)被當(dāng)作潛在叔塊,如果是瞻鹏,則忽略
            if _, exist := w.localUncles[ev.Block.Hash()]; exist {
                continue
            }
            if _, exist := w.remoteUncles[ev.Block.Hash()]; exist {
                continue
            }
            // Add side block to possible uncle block set depending on the author.
                        //將該區(qū)塊作為潛在叔塊加入叔塊map悲立,key為該區(qū)塊的礦工地址
            if w.isLocalBlock != nil && w.isLocalBlock(ev.Block) {
                w.localUncles[ev.Block.Hash()] = ev.Block
            } else {
                w.remoteUncles[ev.Block.Hash()] = ev.Block
            }
            // If our mining block contains less than 2 uncle blocks,
            // add the new uncle block if valid and regenerate a mining block.
                        // 如果我們正在mining的區(qū)塊少于兩個(gè)叔塊,則添加新的叔塊并從新生成mining blocks
            if w.isRunning() && w.current != nil && w.current.uncles.Cardinality() < 2 {
...
            }
                //task3: 交易池更新后
        case ev := <-w.txsCh: ...

        // System stopped
        case <-w.exitCh:
            return
        case <-w.txsSub.Err():
            return
        case <-w.chainHeadSub.Err():
            return
        case <-w.chainSideSub.Err():
            return
        }
    }
}

接著上面的的流程新博,newWorkCh通道傳出req后薪夕,直接啟動(dòng)commitNewWork()函數(shù)。

commitNewWork()

方法主要的功能是遞交一個(gè)新的task:

  1. 初始化一個(gè)新區(qū)塊頭給待挖礦的區(qū)塊赫悄;
  2. 為當(dāng)前挖礦周期初始化一個(gè)工作環(huán)境work原献;
  3. 獲取交易池中每個(gè)賬戶地址的交易列表中的第一個(gè)交易后排序,然后應(yīng)用這些交易涩蜘;
  4. 獲取兩個(gè)叔塊嚼贡;
  5. 將區(qū)塊遞交給commit,用于生成task同诫;
  6. 更新狀態(tài)快照粤策,供前端查詢;

最后是commit方法計(jì)算挖礦獎(jiǎng)勵(lì)误窖,更新block叮盘,將上面生成的block遞交到一個(gè)挖礦task,最后將task傳入taskCh通道霹俺。

// commit runs any post-transaction state modifications, assembles the final block
// and commits new work if consensus engine is running.
func (w *worker) commit(uncles []*types.Header, interval func(), update bool, start time.Time) error {
    // Deep copy receipts here to avoid interaction between different tasks.
    receipts := copyReceipts(w.current.receipts)
    s := w.current.state.Copy()
        // 計(jì)算挖礦獎(jiǎng)勵(lì)(包括叔塊獎(jiǎng)勵(lì))
    block, err := w.engine.FinalizeAndAssemble(w.chain, w.current.header, s, w.current.txs, uncles, receipts)
    if err != nil {
        return err
    }
    if w.isRunning() {
        if interval != nil {
            interval()
        }
        select {
                // 生成task柔吼,傳入taskCh通道:
        case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now()}:
            w.unconfirmed.Shift(block.NumberU64() - 1)
...
        case <-w.exitCh:
            log.Info("Worker has exited")
        }
    }
    if update {
        w.updateSnapshot()
    }
    return nil
}

TaskLoop

task進(jìn)入taskLoop后,被加入pendingTasks列表:

    for {
        select {
        case task := <-w.taskCh:
            if w.newTaskHook != nil {
                w.newTaskHook(task)
            }
            // Reject duplicate sealing work due to resubmitting.
                        // 計(jì)算header數(shù)據(jù)的RLP hash值丙唧,判斷是否有相同的塊已經(jīng)在挖礦中了愈魏,如果是則放棄,否則終止之前的挖礦
            sealHash := w.engine.SealHash(task.block.Header())
            if sealHash == prev {
                continue
            }
            // Interrupt previous sealing operation
            interrupt()
            stopCh, prev = make(chan struct{}), sealHash

            if w.skipSealHook != nil && w.skipSealHook(task) {
                continue
            }
            w.pendingMu.Lock()
            w.pendingTasks[sealHash] = task
            w.pendingMu.Unlock()
                        // 最后執(zhí)行挖礦,結(jié)果會(huì)通過resuletCh傳入resultLoop
            if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil {
                log.Warn("Block sealing failed", "err", err)
            }
        case <-w.exitCh:
            interrupt()
            return
        }
    }

resultLoop

最后是resultLoop培漏,挖礦結(jié)果傳入resultLoop溪厘,先從pengdingTasks列表中取出剛執(zhí)行挖礦的task,更新收據(jù)日志中的blockHash牌柄,然后將區(qū)塊存入數(shù)據(jù)庫畸悬,最后將區(qū)塊廣播出去。

commitTransaction() 交易執(zhí)行

  1. 設(shè)置gaspool:
  2. 進(jìn)入交易執(zhí)行循環(huán)

在for循環(huán)中珊佣,會(huì)有三種情況會(huì)被打斷:a蹋宦、交易還在執(zhí)行,但是新的區(qū)塊已經(jīng)經(jīng)過廣播到達(dá)本地咒锻,interrupt信號為1冷冗;b、worker start 或者restart虫碉,interrupt信號為1贾惦;c、worker重新構(gòu)造區(qū)塊敦捧,包含了新到的交易,interrupt信號為2碰镜。

對于前兩種兢卵,worker的本次執(zhí)行就終止,當(dāng)對于第三種情況绪颖,本次執(zhí)行依然會(huì)被提交到consensus engine

  1. 如果區(qū)塊工作環(huán)境剩余gas小于21000秽荤,則推出循環(huán),否則從排好序的列表離取出交易柠横;
  2. 執(zhí)行交易并處理錯(cuò)誤:w.commitTransaction()
        // Start executing the transaction
                // 首先準(zhǔn)備當(dāng)前的世界狀態(tài)
        w.current.state.Prepare(tx.Hash(), w.current.tcount)
                // 調(diào)用交易執(zhí)行的方法窃款,core.ApplyTransaction,得到收據(jù)并放入當(dāng)前的執(zhí)行環(huán)境
        logs, err := w.commitTransaction(tx, coinbase)
        switch {
        case errors.Is(err, core.ErrGasLimitReached):
                        // gasPool不夠執(zhí)行交易,則當(dāng)前交易從trxs中移除
            // Pop the current out-of-gas transaction without shifting in the next from the account
            log.Trace("Gas limit exceeded for current block", "sender", from)
            txs.Pop()

        case errors.Is(err, core.ErrNonceTooLow):
                        // 交易nonce太低牍氛,則取下一個(gè)交易替換處理列表中的第一個(gè)交易
            // New head notification data race between the transaction pool and miner, shift
            log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
            txs.Shift()

        case errors.Is(err, core.ErrNonceTooHigh):
                        // 交易nonce太高晨继,則將當(dāng)前交易從trxs列表中移除
            // Reorg notification data race between the transaction pool and miner, skip account =
            log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
            txs.Pop()

        case errors.Is(err, nil):
                        // 一切正常,收集日志搬俊,統(tǒng)計(jì)執(zhí)行成功的交易計(jì)數(shù)
            // Everything ok, collect the logs and shift in the next transaction from the same account
            coalescedLogs = append(coalescedLogs, logs...)
            w.current.tcount++
            txs.Shift()

        case errors.Is(err, core.ErrTxTypeNotSupported):
            // Pop the unsupported transaction without shifting in the next from the account
            log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
            txs.Pop()

        default:
            // Strange error, discard the transaction and get the next in line (note, the
            // nonce-too-high clause will prevent us from executing in vain).
            log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
            txs.Shift()
        }

Core.ApplyTransaction 執(zhí)行交易的入口紊扬,將交易送入太坊虛擬機(jī)執(zhí)行

image

ApplyTransaction函數(shù)

該函數(shù)的調(diào)用有兩種情況:

  1. 是在將區(qū)塊插入?yún)^(qū)塊鏈前需要驗(yàn)證區(qū)塊合法性
    bc.insertChain-->bc.processor.Process-->stateProcessor.Process -->ApplyTransaction

  2. 是worker挖礦過程中執(zhí)行交易時(shí)
    Worker.commitTransaction ——> ApplyTransaction

主要功能是:將交易轉(zhuǎn)化成Message,創(chuàng)建EVM對象唉擂,調(diào)用ApplyMessage執(zhí)行交易餐屎,生成日志對象;

  1. 將交易轉(zhuǎn)換成Message玩祟;
  2. 初始化一個(gè)EVM的執(zhí)行環(huán)境腹缩;
  3. 執(zhí)行交易,改變stateDB世界狀態(tài),然后生成收據(jù);
func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
    // Create a new context to be used in the EVM environment.
    txContext := NewEVMTxContext(msg)
    evm.Reset(txContext, statedb)

    // Apply the transaction to the current state (included in the env).
    result, err := ApplyMessage(evm, msg, gp)
    if err != nil {
        return nil, err
    }

    // Update the state with pending changes.
    var root []byte
    if config.IsByzantium(blockNumber) {
        statedb.Finalise(true)
    } else {
        root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
    }
    *usedGas += result.UsedGas

    // Create a new receipt for the transaction, storing the intermediate root and gas used
    // by the tx.
    receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
    if result.Failed() {
        receipt.Status = types.ReceiptStatusFailed
    } else {
        receipt.Status = types.ReceiptStatusSuccessful
    }
    receipt.TxHash = tx.Hash()
    receipt.GasUsed = result.UsedGas

    // If the transaction created a contract, store the creation address in the receipt.
    if msg.To() == nil {
        receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
    }

    // Set the receipt logs and create the bloom filter.
    receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
    receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
    receipt.BlockHash = blockHash
    receipt.BlockNumber = blockNumber
    receipt.TransactionIndex = uint(statedb.TxIndex())
    return receipt, err
}

ethash 挖礦

POW的本質(zhì)是基于算力解決一個(gè)數(shù)學(xué)上困難的問題藏鹊,解決問題的關(guān)鍵點(diǎn)除了暴力枚舉润讥,沒有任何辦法可以找到我們所需要的nonce值,但對于驗(yàn)證輸出的結(jié)果是非常簡單容易的伙判。

經(jīng)典的比特幣POW的算法原理是對block的header加上循環(huán)更新的nonce去進(jìn)行hash運(yùn)算象对,運(yùn)算的target是hash值的前n位為0,這個(gè)計(jì)算只能通過暴力枚舉來進(jìn)行宴抚,驗(yàn)證也很容易勒魔,只要使用最終的nonce打入header按照之前的算法驗(yàn)證即可。

以太坊采用的ethash算法與比特幣不同菇曲,但基本類似冠绢,都是找到一個(gè)nonce值輸入到算法中,得到的結(jié)果低于一個(gè)基于特定困難值的閾值常潮。

RAND(h,n) <= M/d

RAND 是一系列復(fù)雜的運(yùn)算
h:header 不飽和nonce
n:nonce

M:2^256
d: 難度值 弟胀,該難度值給予父區(qū)塊的時(shí)間戳和難度而得到

如上所示,我們用header和nonce經(jīng)過復(fù)雜的計(jì)算喊式,如果得到的結(jié)果小于或者等于M/d孵户,該nonce就是可用的,意味著挖礦成功岔留。

下圖是ethash算法在以太坊源碼中的實(shí)現(xiàn)


aa

Eth.Seal方法

主要任務(wù)是:

  1. 獲得種子seed夏哭;
  2. 基于seed獲得Rand對象,rand值將作為初始化nonce進(jìn)行挖礦献联;
  3. 啟動(dòng)mine方法竖配,執(zhí)行挖礦;
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
// the block's difficulty requirements.
func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
    // If we're running a fake PoW, simply return a 0 nonce immediately
    if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
        header := block.Header()
        header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{}
        select {
        case results <- block.WithSeal(header):
        default:
            ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header()))
        }
        return nil
    }
    // If we're running a shared PoW, delegate sealing to it
    if ethash.shared != nil {
        return ethash.shared.Seal(chain, block, results, stop)
    }
    // Create a runner and the multiple search threads it directs
    abort := make(chan struct{})

    ethash.lock.Lock()
        // 挖礦的線程
    threads := ethash.threads
    if ethash.rand == nil {
        seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
        if err != nil {
            ethash.lock.Unlock()
            return err
        }
                // 生成rand對象
        ethash.rand = rand.New(rand.NewSource(seed.Int64()))
    }
    ethash.lock.Unlock()
    if threads == 0 {
        threads = runtime.NumCPU()
    }
    if threads < 0 {
        threads = 0 // Allows disabling local mining without extra logic around local/remote
    }
    // Push new work to remote sealer
    if ethash.remote != nil {
        ethash.remote.workCh <- &sealTask{block: block, results: results}
    }
    var (
        pend   sync.WaitGroup
        locals = make(chan *types.Block)
    )
        // 閉包多線程處理
    for i := 0; i < threads; i++ {
        pend.Add(1)
        go func(id int, nonce uint64) {
            defer pend.Done()
            ethash.mine(block, id, nonce, abort, locals)
        }(i, uint64(ethash.rand.Int63()))
    }
    // Wait until sealing is terminated or a nonce is found
    go func() {
        var result *types.Block
        select {
        case <-stop:
            // Outside abort, stop all miner threads
            close(abort)
        case result = <-locals:
            // One of the threads found a block, abort all others
            select {
            case results <- result:
            default:
                ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header()))
            }
            close(abort)
        case <-ethash.update:
            // Thread count was changed on user request, restart
            close(abort)
            if err := ethash.Seal(chain, block, results, stop); err != nil {
                ethash.config.Log.Error("Failed to restart sealing after update", "err", err)
            }
        }
        // Wait for all miners to terminate and return the block
        pend.Wait()
    }()
    return nil
}

mine方法

主要任務(wù):

  1. 取出block的header里逆;
  2. 取出沒有nonce時(shí)的區(qū)塊hash进胯;
  3. 設(shè)置目標(biāo)target,M/td原押;
  4. 獲得dataset數(shù)據(jù)集胁镐;
  5. 開啟無限循環(huán),計(jì)算每一輪的nonce值的POW結(jié)果班眯,直到獲得滿足條件的解希停;

補(bǔ)充:DAG和epoch

  1. 上面的dataset就來自內(nèi)存中的一組數(shù)據(jù)或者硬盤里的DAG。
  2. DAG是有向無環(huán)圖署隘,以太坊的DAG是基于區(qū)塊高度生成的宠能。
  3. 以太坊中每3萬個(gè)塊會(huì)生成一代DAG,這一代就成稱為一個(gè)epoch磁餐。
  4. 挖礦的時(shí)候需要從DAG中隨機(jī)選取dataset违崇,所以挖礦工作只能在現(xiàn)世DAG創(chuàng)建以后才能開始阿弃。

參考:
1.以太坊源碼解讀
2.go-ethereum-code-analysis

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市羞延,隨后出現(xiàn)的幾起案子渣淳,更是在濱河造成了極大的恐慌,老刑警劉巖伴箩,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件入愧,死亡現(xiàn)場離奇詭異,居然都是意外死亡嗤谚,警方通過查閱死者的電腦和手機(jī)棺蛛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巩步,“玉大人旁赊,你說我怎么就攤上這事∫我埃” “怎么了终畅?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長竟闪。 經(jīng)常有香客問我离福,道長,這世上最難降的妖魔是什么炼蛤? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任术徊,我火速辦了婚禮,結(jié)果婚禮上鲸湃,老公的妹妹穿的比我還像新娘。我一直安慰自己子寓,他們只是感情好暗挑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斜友,像睡著了一般炸裆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鲜屏,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天烹看,我揣著相機(jī)與錄音,去河邊找鬼洛史。 笑死惯殊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的也殖。 我是一名探鬼主播土思,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了己儒?” 一聲冷哼從身側(cè)響起崎岂,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闪湾,沒想到半個(gè)月后冲甘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡途样,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年江醇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娘纷。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫁审,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赖晶,到底是詐尸還是另有隱情律适,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布遏插,位于F島的核電站捂贿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏胳嘲。R本人自食惡果不足惜厂僧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望了牛。 院中可真熱鬧颜屠,春花似錦、人聲如沸鹰祸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛙婴。三九已至粗井,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間街图,已是汗流浹背浇衬。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留餐济,地道東北人耘擂。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像颤介,于是被迫代替她去往敵國和親梳星。 傳聞我的和親對象是個(gè)殘疾皇子赞赖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354