https://github.com/ethereum/EIPs/blob/master/EIPS/eip-100.mdgit
創世紀區塊的難度是131,072,有一個特殊的公式用來計算以後的每一個塊的難度。若是某個區塊比前一個區塊驗證的更快,以太坊協議就會增長區塊的難度。github
區塊的難度影響nonce,它是在挖礦時必需要使用proof-of-work算法來計算的一個hash值。算法
區塊難度和nonce之間的關係用數學形式表達就是:less
Hd表明的是難度。函數
找到符合難度閾值的nonce惟一方法就是使用proof-of-work算法來列舉全部的可能性。找到解決方案預期時間與難度成正比—難度越高,找到nonce就越困難,所以驗證一個區塊也就越難,這又相應地增長了驗證新塊所需的時間。因此,經過調整區塊難度,協議能夠調整驗證區塊所需的時間。(挖礦的過程就是要找出正確的nonce值)區塊鏈
另外一方面,若是驗證時間變的愈來愈慢,協議就會下降難度。這樣的話,驗證時間自我調節以保持恆定的速率—平均每15s一個塊。ui
這就是以太坊的出塊時間可以保持15秒的緣由,由於當驗證時間變長,就會下降難度;驗證時間變短,則會增強難度,那麼難度是怎麼計算的呢?atom
eip | title | author | type | category | status | created |
---|---|---|---|---|---|---|
100
|
Change difficulty adjustment to target mean block time including uncles
|
Vitalik Buterin
|
Standards Track
|
Core
|
Final
|
2016-04-28
|
adj_factor = max(1 - ((timestamp - parent.timestamp) // 10), -99) child_diff = int(max(parent.difficulty + (parent.difficulty // BLOCK_DIFF_FACTOR) * adj_factor, min(parent.difficulty, MIN_DIFF)))
而後若是block.number >= BYZANTIUM_FORK_BLKNUM(拜占庭硬分叉),則難度的計算的第一個公式變爲:spa
adj_factor = max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)
即Byzantium版本.net
diff = (parent_diff +(parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99))) + 2^(periodCount - 2)
有關上面的拜占庭版本的詳細介紹看:https://blog.csdn.net/t46414704152abc/article/details/81538361
這個地方沒有太懂爲何,以後再多多查查資料
這裏主要是看:
以太坊中的區塊的難度調整公式以下圖所示。
區塊鏈難度調整中,創始塊的難度被設置爲D0=131072 ,此後每一個區塊的難度都與其父區塊的難度相關。D(H)是本區塊的難度,由P(H)Hd+x×ζ2和難度炸彈ϵ構成。
P(H)Hd爲父區塊的難度,每一個區塊的難度都是在父區塊難度的基礎上進行調整。
x×ζ2用於自適應調節出塊難度,維持穩定的出塊速度(15秒)。
根據EIP-100給出的式子,能夠得出:
P(H)Hd = parent_diff
x = parent_diff / 2048
ζ2 = max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)
ϵ = 2^(periodCount - 2)
其中x和ζ2的計算方式以下圖所示。
假設當父區塊不帶叔父區塊的時候(y=1),出塊時間 = (timestamp - parent.timestamp),調整過程以下:
難度炸彈計算公式以下圖所示。
ϵ是2的指數函數,每十萬個塊擴大一倍,後期增加很是快,這就是難度「炸彈」的由來。
H′i稱爲fake block number,由真正的block number Hi減小三百萬獲得。之因此減小三百萬,是由於目前proof of stake的工做量證實方式還存在一些問題,pos協議涉及不夠完善,可是難度炸彈已經致使挖礦時間變成了30秒左右,爲了減少難度,就會減去三百萬。
設置難度炸彈的緣由是要下降遷移到PoS協議時發生fork的風險,倘若礦工聯合起來抵制POS的工做量證實模式,那就會致使以太坊產生硬分叉;有了難度炸彈,挖礦難度愈來愈大,礦工就有意願遷移到PoS協議上了。難度炸彈的威力,能夠經過下圖看出。
區塊數量到370萬以後,挖礦難度忽然遞增,到430萬時,難度已經很是之大了,這時候挖礦時間已經變爲爲30秒,可是POS協議尚未完善,因而以太坊將挖礦難度公式進行調整,使得每次計算時,當前區塊號減去三百萬,這樣就下降了挖礦難度,而且在這個時期,對以太坊出塊獎勵進行了調整,從原來的5個ETH變爲3個ETH。
// CalcDifficulty is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { return CalcDifficulty(chain.Config(), time, parent) } // CalcDifficulty is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { next := new(big.Int).Add(parent.Number, big1) switch { case config.IsByzantium(next): return calcDifficultyByzantium(time, parent) case config.IsHomestead(next): return calcDifficultyHomestead(time, parent) default: return calcDifficultyFrontier(time, parent) } } // Some weird constants to avoid constant memory allocs for them. var ( expDiffPeriod = big.NewInt(100000) big1 = big.NewInt(1) big2 = big.NewInt(2) big9 = big.NewInt(9) big10 = big.NewInt(10) bigMinus99 = big.NewInt(-99) big2999999 = big.NewInt(2999999) )
以太坊中難度計算公式以下圖所示,因爲目前處於以太坊發展的Metropolis中的Byzantium階段,因此難度計算公式的函數名稱爲calcDifficultyByzantium
// calcDifficultyByzantium is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time given the // parent block's time and difficulty. The calculation uses the Byzantium rules. func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int { // https://github.com/ethereum/EIPs/issues/100. // algorithm: // diff = (parent_diff + // (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)) // ) + 2^(periodCount - 2) bigTime := new(big.Int).SetUint64(time) bigParentTime := new(big.Int).Set(parent.Time) // holds intermediate values to make the algo easier to read & audit x := new(big.Int) y := new(big.Int) // (2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9 x.Sub(bigTime, bigParentTime) // x = block_timestamp - parent_timestamp x.Div(x, big9) // x = (block_timestamp - parent_timestamp) //9,整除9 if parent.UncleHash == types.EmptyUncleHash {//判斷有無叔父區塊 x.Sub(big1, x) //無爲1, x = 1 - (block_timestamp - parent_timestamp) //9 } else { x.Sub(big2, x)//有爲2 , x = 2 - (block_timestamp - parent_timestamp) //9 } // max((2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9, -99) if x.Cmp(bigMinus99) < 0 { x與-99比,看誰大,小於0則說明-99大 x.Set(bigMinus99) //x的值設爲-99,此時x = ζ2 } // parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)) y.Div(parent.Difficulty, params.DifficultyBoundDivisor) //y = parent_diff / 2048 x.Mul(y, x) // x = y * ζ2 x.Add(parent.Difficulty, x) // x = parent_diff + y * ζ2 // minimum difficulty can ever be (before exponential factor) if x.Cmp(params.MinimumDifficulty) < 0 { //與創世區塊的難度比較,創世區塊的難度是難度的最低值,若是算出來的難度低於它,那就設置爲創世區塊的難度 x.Set(params.MinimumDifficulty) } // calculate a fake block number for the ice-age delay: // https://github.com/ethereum/EIPs/pull/669 // fake_block_number = max(0, block.number - 3_000_000) fakeBlockNumber := new(big.Int) if parent.Number.Cmp(big2999999) >= 0 { //當父區塊號 >= 2999999,說明本區塊 >= 3000000,因此要減去3百萬,以此來下降難度,因此用父區塊數 - 2999999便可 fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, big2999999) // Note, parent is 1 less than the actual block number } // for the exponential factor periodCount := fakeBlockNumber periodCount.Div(periodCount, expDiffPeriod) //periodCount = periodCount / 100000 // the exponential factor, commonly referred to as "the bomb" // diff = diff + 2^(periodCount - 2) if periodCount.Cmp(big1) > 0 { y.Sub(periodCount, big2) // y.Exp(big2, y, nil) x.Add(x, y) } return x }
frontier版本已經不使用了,它存在的問題是沒有考慮到到13秒的距離。出塊1秒和12秒有一樣的難度計算值
而後還找到了另外一個很好地解釋了難度的帖子,可是它講的是Homestead的版本:
https://blog.csdn.net/Metal1/article/details/80151535
這個是Homestead版本的計算方法,與Byzantium版本的不一樣之處就在於adj_factor = max(1 - ((timestamp - parent.timestamp) // 10), -99)
block_diff = parent_diff + 難度調整 + 難度炸彈
難度調整 = parent_diff // 2048 * MAX(1 - (block_timestamp - parent_timestamp) // 10, -99)
即難度調整 = parent_diff // 2048 * adj_factor (BLOCK_DIFF_FACTOR在這裏設置爲2048)
難度炸彈 = INT(2**((block_number // 100000) - 2)) (向下取整)
該版本是在區塊數尚未到達三百萬時候的版本,想在超過三百萬後,爲了下降難度,減小出塊的時間,都用上面的Byzantium版本了
https://github.com/ethereum/go-ethereum/blob/master/consensus/ethash/consensus.go
// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time given the // parent block's time and difficulty. The calculation uses the Homestead rules. func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int { // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md // algorithm: // diff = (parent_diff + // (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) // ) + 2^(periodCount - 2) bigTime := new(big.Int).SetUint64(time) bigParentTime := new(big.Int).Set(parent.Time) // holds intermediate values to make the algo easier to read & audit x := new(big.Int) y := new(big.Int) // 1 - (block_timestamp - parent_timestamp) // 10 x.Sub(bigTime, bigParentTime) x.Div(x, big10) x.Sub(big1, x) // max(1 - (block_timestamp - parent_timestamp) // 10, -99) if x.Cmp(bigMinus99) < 0 { x.Set(bigMinus99) } // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) y.Div(parent.Difficulty, params.DifficultyBoundDivisor) x.Mul(y, x) x.Add(parent.Difficulty, x) // minimum difficulty can ever be (before exponential factor) if x.Cmp(params.MinimumDifficulty) < 0 { x.Set(params.MinimumDifficulty) } // for the exponential factor periodCount := new(big.Int).Add(parent.Number, big1) //這裏與Byzantium版本不一樣處是不減三百萬,直接父區塊數加1得現在的區塊數便可 periodCount.Div(periodCount, expDiffPeriod) // the exponential factor, commonly referred to as "the bomb" // diff = diff + 2^(periodCount - 2) if periodCount.Cmp(big1) > 0 { y.Sub(periodCount, big2) y.Exp(big2, y, nil) x.Add(x, y) } return x }