关于以太坊的难度调整算法,从源代码里的block_validator.go
文件中可以找到相关表述。算法主要是让挖矿时间保持在10-19s这个区间内,如果挖矿时间在0到9s内,Geth会增大挖矿难度;如果挖矿时间大于20s,Geth会减小难度。
在block_validator.go
文件中,函数CalcDifficulty
用来调整挖矿难度,函数输入是以太坊的版本号、父区块的难度值、父区块的时间、当前时间以及父区块编号,函数返回的是将要被创建的下一个区块的难度值。
下表为一些相应的input parameters。
Input Parameters | Descriptions |
---|---|
time | Proposed time of formation of new block. |
parentTime | Time of formation of parent Block. |
parentNumber | Parent block, block number. |
parentDiff | Difficulty of parent block |
先根据版本号选择相应的难度计算函数,若为Frontier,则调用calcDifficultyFrontier
;若为Homestead,则调用calcDifficultyHomestead
。
在以太坊硬分叉时难度调整算法有变动。
- Change the difficulty adjustment algorithm from the current formula:
block_diff = parent_diff + parent_diff // 2048 * (1 if block_timestamp - parent_timestamp < 13 else -1) + int(2**((block.number // 100000) - 2))
(where the+ int(2**((block.number // 100000) - 2))
represents the exponential difficulty adjustment component) toblock_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) + int(2**((block.number // 100000) - 2))
, where//
is the integer division operator, eg.6 // 2 = 3
,7 // 2 = 3
,8 // 2 = 4
. TheminDifficulty
still defines the minimum difficulty allowed and no adjustment may take it below this. - The problem with the frontier formula and the reason for the change was that the frontier version doesn't take into account how far off from 13 seconds the block time was. A block mined 1 second after the previous one has the same effect on the difficulty as one mined after 12 seconds. This causes block difficulty to adjust to a median block time rather than a mean.
calcDifficultyHomestead函数:
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
}
calcDifficultyFrontier函数:
func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
diff := new(big.Int)
adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
bigTime := new(big.Int)
bigParentTime := new(big.Int)
bigTime.SetUint64(time)
bigParentTime.SetUint64(parentTime)
if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
diff.Add(parentDiff, adjust)
} else {
diff.Sub(parentDiff, adjust)
}
if diff.Cmp(params.MinimumDifficulty) < 0 {
diff.Set(params.MinimumDifficulty)
}
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount.Div(periodCount, ExpDiffPeriod)
if periodCount.Cmp(common.Big1) > 0 {
// diff = diff + 2^(periodCount - 2)
expDiff := periodCount.Sub(periodCount, common.Big2)
expDiff.Exp(common.Big2, expDiff, nil)
diff.Add(diff, expDiff)
diff = math.BigMax(diff, params.MinimumDifficulty)
}
return diff
}
Below is step by step process how difficulty of new block gets created.
First, difference between time of formation of parent block and new block is calculated.
Output of step 1 is then divided by 10 and integer of it is stored. This is done to create ranges. If output of step 1 is between 1 – 9 then output of this step will be 0. If output of step 1 is between 10 – 19 then output of this step will be 1. If output of step 1 is between 20 – 29 then output of this step will be 2 and so on.
From step above various ranges gets created. Now in order to create three ranges we will subtract 1 from output of step 2. The three ranges will be either +ve, zero or –ve. If you see it carefully then output of this step will be +ve when output of step 1 is between 0 – 9, zero when output of step 1 is between 10 – 19 and –ve when output of step 1 is anything more than 20.
Now compare output of previous step with -99 and if it is even lesser than -99 then set it as -99. This is done to limit the smallest possible value of step 3, otherwise keep the value of output of previous step as is.
Next we divide the parent block difficulty by the difficulty bound divisor, whose value is 2048.
Multiply output of step 4 with step 5. This will give the difference of difficulty of new block with old parent block. Depending is this value is +ve then difficulty will increase and if this value is –ve then then new difficulty will decrease.
Now add output of step 6 to parent difficulty and result will be the difficulty of the new block.
Once difficulty is calculated, a check is made to make sure that difficulty calculated is at least more than the minimum threshold value of 131072.
Before returning the difficulty, check is done that if block number is more than 200,000 then “The Bomb” logic is applied to increase the difficulty exponentially.
In order to increase the difficulty exponentially, new block number is calculated by adding one to the parent block number.
Now new block number is divided by 100,000.
If new block number is more than 200,000 then output of step 11 is subtracted from 2.
Now exponentially increased difficulty delta is calculated by doing following calculation: 2 ^ output of step 12.
And new difficulty is calculated by adding output of previous step to the difficulty calculated in step 7.
Summary
If the timestamp difference (block_timestamp - parent_timestamp)
is:
- < 10 seconds, the difficulty is adjusted upwards by
parent_diff // 2048 * 1
- 10 to 19 seconds, the difficulty is left unchanged
- >= 20 seconds, the difficulty is adjust downwards proportional to the timestamp difference, from
parent_diff // 2048 * -1
to a max downward adjustment ofparent_diff // 2048 * -99
This is consistent with the statement from ethdocs.org - Ethereum Homestead - The Homestead Release:
EIP-2/4 eliminates the excess incentive to set the timestamp difference to exactly 1 in order to create a block that has slightly higher difficulty and that will thus be guaranteed to beat out any possible forks. This guarantees to keep block time in the 10-20 range and according to simulations restores the target 15 second blocktime (instead of the current effective 17s).
And from Ethereum Network Status, the average block time currently is 13.86 seconds.
Details
The difficulty adjustment formula:
block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) + int(2**((block.number // 100000) - 2))
where // is the integer division operator, eg. 6 // 2 = 3, 7 // 2 = 3, 8 // 2 = 4.
can be broken down into the following parts:
Sub-formula B - The difficulty bomb part, which increases the difficulty exponentially every 100,000 blocks.
+ int(2**((block.number // 100000) - 2))
The difficulty bomb won't be discussed here as it is already covered in the following Q&As:
- What is the "difficulty bomb" and what is the goal of it?
- When will the difficulty bomb make mining impossible?
- What if difficulty bomb makes mining impossible before POS release?
Sub-formula A - The difficulty adjustment part, which increases or decreases the block difficulty depending on the time between the current block timestamp and the parent block timestamp:
+ parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
Subformula A1 - Lets separate out part of Subformula A
+ max(1 - (block_timestamp - parent_timestamp) // 10, -99)
and consider what the adjustment effect is due to the timestamp difference between the current block and the parent block:
When (block_timestamp - parent_timestamp)
is
- 0, 1, 2, ..., 8, 9 seconds
- A1 evaluates to
max(1 - 0, -99) = 1
- A evaluates to
+parent_diff // 2048 * 1
- A1 evaluates to
- 10, 11, 12, ..., 18, 19 seconds
- A1 evaluates to
max(1 - 1, -99) = 0
- A evaluates to
+parent_diff // 2048 * 0
- A1 evaluates to
- 20, 21, 22, ..., 28, 29 seconds
- A1 evaluates to
max(1 - 2, -99) = -1
- A evaluates to
+parent_diff // 2048 * -1
- A1 evaluates to
- 30, 31, 32, ..., 38, 39 seconds
- A1 evaluates to
max(1 - 3, -99) = -2
- A evaluates to
+parent_diff // 2048 * -2
- A1 evaluates to
- 1000, 1001, 1002, ..., 1008, 1009 seconds
- A1 evaluates to
max(1 - 100, -99) = -99
- A evaluates to
+parent_diff // 2048 * -99
- A1 evaluates to
- > 1009 seconds
- A1 evaluates to
max(1 - {number greater than 100}, -99) = -99
- A evaluates to
+parent_diff // 2048 * -99
- A1 evaluates to
So, if the timestamp difference (block_timestamp - parent_timestamp)
is:
- < 10 seconds, the difficulty is adjusted upwards by
parent_diff // 2048 * 1
- 10 to 19 seconds, the difficulty is left unchanged
- >= 20 seconds, the difficulty is adjust downwards proportional to the timestamp difference, from
parent_diff // 2048 * -1
to a max downward adjustment ofparent_diff // 2048 * -99
So, this is basically how Ethereum tries to keep the mining time difference between 10 – 19 seconds. Now, if we carefully take a look to step 2, then we will observe that division by 10 helps to create three ranges. If the value falls in first range then difficulty is increased, if value falls in second range then difficult is kept constant and finally, if the range falls in third range then difficulty is reduced.
Now if we want to change the ranges of difficulty, we can do it by dividing it by some another number in step 2. That means, if we want to keep mining time difference in between 0 – 4 sec. then increase the difficulty, if mining time difference is between 5 – 9 sec. then keep the difficulty constant and if mining time is 10 or more sec. then try to reduce the difficulty, then instead of dividing by 10 you can divide the value by 5 in step 2. This way you can easily manage the ranges of mining difficulty.
Below is the graph showing Ethereum main network block difficulty growth.
*Source – *https://etherscan.io/chart/difficulty