代碼版本: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的工作原理
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:
- 初始化一個(gè)新區(qū)塊頭給待挖礦的區(qū)塊赫悄;
- 為當(dāng)前挖礦周期初始化一個(gè)工作環(huán)境work原献;
- 獲取交易池中每個(gè)賬戶地址的交易列表中的第一個(gè)交易后排序,然后應(yīng)用這些交易涩蜘;
- 獲取兩個(gè)叔塊嚼贡;
- 將區(qū)塊遞交給commit,用于生成task同诫;
- 更新狀態(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í)行
- 設(shè)置gaspool:
- 進(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
- 如果區(qū)塊工作環(huán)境剩余gas小于21000秽荤,則推出循環(huán),否則從排好序的列表離取出交易柠横;
- 執(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í)行
ApplyTransaction函數(shù)
該函數(shù)的調(diào)用有兩種情況:
是在將區(qū)塊插入?yún)^(qū)塊鏈前需要驗(yàn)證區(qū)塊合法性
bc.insertChain-->bc.processor.Process-->stateProcessor.Process -->ApplyTransaction
是worker挖礦過程中執(zhí)行交易時(shí)
Worker.commitTransaction ——> ApplyTransaction
主要功能是:將交易轉(zhuǎn)化成Message,創(chuàng)建EVM對象唉擂,調(diào)用ApplyMessage執(zhí)行交易餐屎,生成日志對象;
- 將交易轉(zhuǎn)換成Message玩祟;
- 初始化一個(gè)EVM的執(zhí)行環(huán)境腹缩;
- 執(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)
Eth.Seal方法
主要任務(wù)是:
- 獲得種子seed夏哭;
- 基于seed獲得Rand對象,rand值將作為初始化nonce進(jìn)行挖礦献联;
- 啟動(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ù):
- 取出block的header里逆;
- 取出沒有nonce時(shí)的區(qū)塊hash进胯;
- 設(shè)置目標(biāo)target,M/td原押;
- 獲得dataset數(shù)據(jù)集胁镐;
- 開啟無限循環(huán),計(jì)算每一輪的nonce值的POW結(jié)果班眯,直到獲得滿足條件的解希停;
補(bǔ)充:DAG和epoch
- 上面的dataset就來自內(nèi)存中的一組數(shù)據(jù)或者硬盤里的DAG。
- DAG是有向無環(huán)圖署隘,以太坊的DAG是基于區(qū)塊高度生成的宠能。
- 以太坊中每3萬個(gè)塊會(huì)生成一代DAG,這一代就成稱為一個(gè)epoch磁餐。
- 挖礦的時(shí)候需要從DAG中隨機(jī)選取dataset违崇,所以挖礦工作只能在現(xiàn)世DAG創(chuàng)建以后才能開始阿弃。
參考:
1.以太坊源碼解讀
2.go-ethereum-code-analysis