ethereum/EIPs-100 挖礦難度計算

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

以太坊挖礦過程:

這個地方沒有太懂爲何,以後再多多查查資料

這裏主要是看:

以太坊挖礦難度調整

以太坊中的區塊的難度調整公式以下圖所示。 


參數說明

  1. 區塊鏈難度調整中,創始塊的難度被設置爲D0=131072 ,此後每一個區塊的難度都與其父區塊的難度相關。D(H)是本區塊的難度,由P(H)Hd+x×ζ2和難度炸彈ϵ構成。

  2. P(H)Hd爲父區塊的難度,每一個區塊的難度都是在父區塊難度的基礎上進行調整。

  3. x×ζ2用於自適應調節出塊難度,維持穩定的出塊速度(15秒)。

  4. ϵ表示難度炸彈。
  5. 難度有最低下限,即不能低於D0=131072

根據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的計算方式以下圖所示。 

  • x 是父區塊難度的2048的取整,是調整的單位
  • ζ2調整係數,其最小隻能是-99。
  • y的取值依賴於父區塊是否包含叔父區塊,若是包含,則y=2,不然y=1。y = (2 if len(parent.uncles) else 1)
  • HS是本區塊的時間戳,P(H)Hs是父區塊的時間戳,單位爲秒,而且HS>P(H)Hs
  • 難度下降的上界設置爲−99 ,主要是應對被黑客攻擊或其餘目前想不到的黑天鵝事件

假設當父區塊不帶叔父區塊的時候(y=1),出塊時間 = (timestamp - parent.timestamp),調整過程以下:

  • 出塊時間在[1,8]之間,出塊時間太短,難度調大一個單位
  • 出塊時間在[9,17]之間,出塊時間能夠接受,難度保持不變
  • 出塊時間在[18,26]之間,出塊時間過長,難度調小一個單位
  •  
    這裏發現,出塊時間變長,區塊的總體難度就會調小,倘若有的礦工,故意將區塊的時間戳改的比較晚(礦工是能夠更改時間戳的),那麼是否是就能夠搶先發布區塊呢?好比說將時間戳延遲寫15秒,會怎麼樣呢?這樣就會致使該礦工計算出來的難度比別的礦工計算的難度低,其餘礦工15秒發佈一個區塊,而該礦工能夠在10秒內發佈區塊,能夠拿到區塊獎勵。可是問題在於假如恰好也有別的區塊在10秒內發佈了區塊,此時根據POW的規則,另一個礦工發佈的區塊難度更大,所以其餘礦工會以最大工做量標準,選擇15秒內挖出的區塊所在的鏈做爲主鏈,而該礦工發佈的區塊便成了叔父區塊

難度炸彈計算公式以下圖所示。 

  • ϵ是2的指數函數,每十萬個塊擴大一倍,後期增加很是快,這就是難度「炸彈」的由來。

  • Hi稱爲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的版本:

以太坊(Ethereum ETH)是如何計算難度的

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
}
相關文章
相關標籤/搜索