1. 程式人生 > >以太坊挖礦原理

以太坊挖礦原理

開啟 方法 比較 agen 存在 icu hash算法 info see

以太坊的共識機制是 PoW(Proof of Work 工作量證明機制),使用的算法是Ethash,這種算法是對 Dagger-Hashimoto算法的改良版本,流程大概如下

1.對於每一個塊,首先計算一個種子(seed),該種子只和當前塊的信息有關;然後根據種子生成一個32M的隨機數據集(cache)

2. 根據Cache生成一個1GB大小的數據集合DAG(有向非循環圖),它是一個完整的搜索空間,挖礦的過程就是從DAG中隨機選擇元素(類似於比特幣挖礦中查找合適Nonce)再進行哈希運算,可以從Cache快速計算DAG指定位置的元素,進而哈希驗證


要求對Cache和DAG進行周期性更新,每1000個塊更新一次,並且規定DAG的大小隨著時間推移線性增長,從1G開始,每年大約增長7G左右。

為了更好的了解這部分。我們可以簡單的看下 go-ethereum 的代碼

1. 在 miner.go裏調用 New方法生成一個礦工。

/**
   利用區塊鏈創建時候的一些配置,以及共識引擎consensus.Engine等參數先是生成一個礦工,然後
   讓礦工註冊一個cpu運算引擎,同時通過 update 來監聽同步狀態並更新挖礦狀態
**/
func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner {
	miner := &Miner{
		eth:      eth,
		mux:      mux,
		engine:   engine,
		worker:   newWorker(config, engine, common.Address{}, eth, mux),
		canStart: 1,
	}
	miner.Register(NewCpuAgent(eth.BlockChain(), engine))
	go miner.update()

	return miner
}

在update方法裏有一個需要註意:

case downloader.StartEvent:
			atomic.StoreInt32(&self.canStart, 0)
			if self.Mining() {
				self.Stop()
				atomic.StoreInt32(&self.shouldStart, 1)
				log.Info("Mining aborted due to sync")
			}

 可以看到如果當前處於 區塊的同步中,則挖礦的操作需要停止,直到同步操作結束(同步成功或是失敗),如果原來已經執行了挖礦操作的,則繼續開啟挖礦操作。

2.在 Register方法中調用worker的Agent接口裏的Start方法,該方法在agent.go裏實現。在agent.go裏調用 mine進行挖礦操作。

func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
       //調用Seal接口,在sealer.go裏實現,進行Ethash算法的實現
	if result, err := self.engine.Seal(self.chain, work.Block, stop); result != nil {
		log.Info("Successfully sealed new block", "number", result.Number(), "hash", result.Hash())
		self.returnCh <- &Result{work, result}
	} else {
		if err != nil {
			log.Warn("Block sealing failed", "err", err)
		}
		self.returnCh <- nil
	}
}

3. 在sealer.go的miner進行挖礦和結果比對,查找是否挖礦成功。

通過256/difficulty 生成一個target值,該值用於後面和計算出來的隨機數比較,如果計算出來的隨機數比target更小,則挖礦成功。同時通過當前所在的區塊號,生成一個完整的dataset。

      var (
               
	        //target的計算方法是 256/difficulty 的一個int值
		target = new(big.Int).Div(maxUint256, header.Difficulty)

                //當前是第幾塊
		number  = header.Number.Uint64()
                //生成一個dataset,也就是我們說的搜索或是匹配空間
		dataset = ethash.dataset(number)
	)

 具體過程如下:

1)通過number號得到當前塊處於第幾個epoch.(每30000個區塊為一個epoch,時間窗口為125小時,大約5.2天),通過所在的epoch為索引獲取當前內存中是否有dataset

2)如果沒有,先會看內存裏的總dataset是否大於 dagsinmemory(默認為1),如果大於,則需要把最早的一個dataset刪除

3)同時查看是否有pre-generated dataset cache,該數據存在與磁盤空間中。如果有這個數據,並且和當前區塊在同一個 epoch. 就用這個pre-generated dataset作為當前dataset.

4)如果上述不符合,則重新生成一個dataset. 如果在這個過程中,發現原來pre-generated dataset為空,或是它的epoch和當前所在區塊的epoch不一致,則需要用新生成的dataset作為pre-generated dataset,賦值給它

5) 生成dataset後,通過dataset,利用keccak512算法,生成一個1GB大小的數據集合DAG

6) 接下來就是不停循環的利用hashimoto算法(基於Keccak256算法)計算出一個結果值,然後和target進行比較。如果比target小則成功,否則就繼續

以太坊挖礦原理