關(guān)于以太坊的難度調(diào)整算法拦赠,從源代碼里的block_validator.go
文件中可以找到相關(guān)表述巍沙。算法主要是讓挖礦時(shí)間保持在10-19s這個(gè)區(qū)間內(nèi),如果挖礦時(shí)間在0到9s內(nèi)荷鼠,Geth會(huì)增大挖礦難度句携;如果挖礦時(shí)間大于20s,Geth會(huì)減小難度允乐。
在block_validator.go
文件中务甥,函數(shù)CalcDifficulty
用來(lái)調(diào)整挖礦難度,函數(shù)輸入是以太坊的版本號(hào)喳篇、父區(qū)塊的難度值、父區(qū)塊的時(shí)間态辛、當(dāng)前時(shí)間以及父區(qū)塊編號(hào)麸澜,函數(shù)返回的是將要被創(chuàng)建的下一個(gè)區(qū)塊的難度值。
下表為一些相應(yīng)的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 |
先根據(jù)版本號(hào)選擇相應(yīng)的難度計(jì)算函數(shù)炊邦,若為Frontier,則調(diào)用calcDifficultyFrontier
熟史;若為Homestead馁害,則調(diào)用calcDifficultyHomestead
。
在以太坊硬分叉時(shí)難度調(diào)整算法有變動(dòng)蹂匹。
- 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函數(shù):
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函數(shù):
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