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 of parent_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:
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
- 10, 11, 12, ..., 18, 19 seconds
- A1 evaluates to
max(1 - 1, -99) = 0
- A evaluates to
+parent_diff // 2048 * 0
- 20, 21, 22, ..., 28, 29 seconds
- A1 evaluates to
max(1 - 2, -99) = -1
- A evaluates to
+parent_diff // 2048 * -1
- 30, 31, 32, ..., 38, 39 seconds
- A1 evaluates to
max(1 - 3, -99) = -2
- A evaluates to
+parent_diff // 2048 * -2
- 1000, 1001, 1002, ..., 1008, 1009 seconds
- A1 evaluates to
max(1 - 100, -99) = -99
- A evaluates to
+parent_diff // 2048 * -99
- > 1009 seconds
- A1 evaluates to
max(1 - {number greater than 100}, -99) = -99
- A evaluates to
+parent_diff // 2048 * -99
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 of parent_diff // 2048 * -99
The Source Code
From Go Ethereum - core/block_validator.go, lines 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
}