乙太坊 Homestead 難度調整算法是如何工作的?
從EIP 2開始,Homestead 難度調整算法為:
block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) + int(2**((block.number // 100000) - 2))
其中 // 是整數除法運算符,例如。6 // 2 = 3, 7 // 2 = 3, 8 // 2 = 4。
這是如何運作的?
(這個問題是由Homestead 挖出的第一個區塊是什麼問題提出的?)
其他相關問答:
概括
如果時間戳差異
(block_timestamp - parent_timestamp)
是:
- < 10 秒,難度向上調整
parent_diff // 2048 * 1
- 10到19秒,難度不變
- >= 20 秒,難度與時間戳差成正比向下調整,從
parent_diff // 2048 * -1
到最大向下調整parent_diff // 2048 * -99
這與ethdocs.org - Ethereum Homestead - The Homestead Release的聲明一致:
EIP-2/4 消除了將時間戳差精確設置為 1 的過度激勵,以創建一個難度稍高的區塊,從而保證擊敗任何可能的分叉。這保證了將塊時間保持在 10-20 範圍內,並根據模擬恢復目標 15 秒塊時間(而不是目前有效的 17 秒)。
而從乙太坊網路狀態來看,目前的平均出塊時間是 13.86 秒。
細節
難度調整公式:
block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) + int(2**((block.number // 100000) - 2))
其中 // 是整數除法運算符,例如。6 // 2 = 3, 7 // 2 = 3, 8 // 2 = 4。
可以分解為以下幾個部分:
子公式 B - 難度炸彈部分,每 100,000 個區塊以指數方式增加難度。
+ int(2**((block.number // 100000) - 2))
難度炸彈不會在這裡討論,因為它已經在以下問答中介紹過:
子公式 A - 難度調整部分,根據目前區塊時間戳和父區塊時間戳之間的時間來增加或減少區塊難度:
+ parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
子公式 A1 - 讓我們分離出子公式 A 的一部分
+ max(1 - (block_timestamp - parent_timestamp) // 10, -99)
並考慮由於目前區塊和父區塊的時間戳差異導致的調整效果:
(block_timestamp - parent_timestamp)
什麼時候
0, 1, 2, …, 8, 9 秒
- A1 評估為
max(1 - 0, -99) = 1
- A 評估為
+parent_diff // 2048 * 1
10, 11, 12, …, 18, 19 秒
- A1 評估為
max(1 - 1, -99) = 0
- A 評估為
+parent_diff // 2048 * 0
20, 21, 22, …, 28, 29 秒
- A1 評估為
max(1 - 2, -99) = -1
- A 評估為
+parent_diff // 2048 * -1
30, 31, 32, …, 38, 39 秒
- A1 評估為
max(1 - 3, -99) = -2
- A 評估為
+parent_diff // 2048 * -2
1000, 1001, 1002, …, 1008, 1009 秒
- A1 評估為
max(1 - 100, -99) = -99
- A 評估為
+parent_diff // 2048 * -99
> 1009 秒
- A1 評估為
max(1 - {number greater than 100}, -99) = -99
- A 評估為
+parent_diff // 2048 * -99
因此,如果時間戳差異
(block_timestamp - parent_timestamp)
為:
- < 10 秒,難度向上調整
parent_diff // 2048 * 1
- 10到19秒,難度不變
- >= 20 秒,難度與時間戳差成正比向下調整,從
parent_diff // 2048 * -1
到最大向下調整parent_diff // 2048 * -99
原始碼
來自Go Ethereum - core/block_validator.go,第 264-311 行:
func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki // 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).SetUint64(parentTime) // 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(common.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(parentDiff, params.DifficultyBoundDivisor) x.Mul(y, x) x.Add(parentDiff, 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(parentNumber, common.Big1) periodCount.Div(periodCount, ExpDiffPeriod) // the exponential factor, commonly referred to as "the bomb" // diff = diff + 2^(periodCount - 2) if periodCount.Cmp(common.Big1) > 0 { y.Sub(periodCount, common.Big2) y.Exp(common.Big2, y, nil) x.Add(x, y) } return x }