How Ethereum (ETH) Calculates Difficulty

Original address: https://zhuanlan.zhihu.com/p/28830859

what is difficulty

The term Difficulty comes from Bitcoin, the pioneer of blockchain technology, and is used to measure the average number of operations required to mine a block. Mining is essentially solving a puzzle, and different electronic coins set up different puzzles. For example, Bitcoin uses SHA-256, Litecoin uses Scrypt, and Ethereum uses Ethash. All possible values ​​of the solution of a puzzle are called the solution space, and mining is to find a solution among these possible values.

These puzzles have the following characteristics in common:

  • There is no more efficient solution than the exhaustive method
  • The solutions are uniformly distributed in the space, so that the probability of finding a solution is basically the same for each exhaustive attempt
  • The solution space is large enough to ensure that a solution can be found

Suppose there is an electronic currency now, the solution space is 0-99, a total of 100 numbers, and the puzzle is x<100. The puzzle is so simple that any number in the space will suffice. What if you want to make the puzzle more difficult to solve? Change the puzzle to x<50, and now only half of the numbers in the space can be satisfied, that is to say, the difficulty is now greater than before. And we can also know how much more difficult it is. Originally, it took an average of 1 attempt to solve the problem, but now it takes an average of 2 attempts to solve it. That is to say, the difficulty of x<50 is 2/1=2 times that of x<100. Similarly, if the puzzle becomes x<10, the difficulty is 100/10=10 times that of x<100.

Now the puzzle has a parameter Difficulty , and the puzzle becomes x<Difficulty . The above 100, 50, and 10 are all values ​​of Difficulty. This parameter Difficulty is what we often call Difficulty .

Difficulty controls the number of attempts required to solve the average solution by controlling the number of qualified solutions in the space, and it can also indirectly control the time required to generate a block, so that the block can be made in a reasonable and stable manner. speed is generated.

When there are many mining people and more attempts per unit time, the difficulty will increase. When the number of mining people decreases and the number of attempts per unit time decreases, the difficulty will decrease. In this way, the time required to generate a block can be stabilized.

With difficulty, we also need an algorithm to determine and adjust what a reasonable difficulty value is.

How Ethereum Calculates Difficulty

The code of Ethereum is completely open source, and the official implementation of a wallet in Go language is called Geth.

Geth can be downloaded from here: Go Ethereum Downloads

The source code can be found here: ethereum/go-ethereum

The developers of Geth have a fairly deep understanding of "consensus" and the code is easy to read. The code for difficulty is in consensus/ethash/consensus.go .

func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
	next := new(big.Int).Add(parent.Number, big1)
	switch {
	case config.IsMetropolis(next):
		return calcDifficultyMetropolis(time, parent)
	case config.IsHomestead(next):
		return calcDifficultyHomestead(time, parent)
	default:
		return calcDifficultyFrontier(time, parent)
	}
}

There are three rules for calculating difficulty, corresponding to the three major versions of Ethereum: Frontier, which is already history, Homestead, which is in use, and Metropolis, which is about to be released.

Homestead's difficulty calculation rules, which are currently in use, are implemented in calcDifficultyHomestead.

func calcDifficultyHomestead(time uint64, parent *types.Header) *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).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)
	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
}

But I'm not here to carry the code, we're talking people.

Before explaining the specific calculation process, let's introduce several operators used.

  • Integer division, signed //

When calculating a//b, first calculate a/b, and then take the largest integer not greater than a/b.

E.g:

-11.3 // 5 = -3

11.3 // 5 = 2

  • round, notation INT

When calculating INT(a), only the integer part is taken and the decimals are discarded.

E.g:

INT(3.7) = 3

INT(-3.7) = -3

  • maximum value, symbol MAX

When MAX(a,b) is calculated, the result is the larger of a and b.

E.g:

MAX(-1,0) = 0

MAX(7,10) = 10


When calculating the difficulty of a block, the following inputs are required:

parent_timestamp:上一个区块产生的时间

parent_diff:上一个区块的难度

block_timestamp:当前区块产生的时间

block_number:当前区块的序号


block_diff = parent_diff + 难度调整 + 难度炸弹

难度调整 = parent_diff // 2048 * MAX(1 - (block_timestamp - parent_timestamp) // 10, -99)

难度炸弹 = INT(2**((block_number // 100000) - 2))


另外,区块难度不能低于以太坊的创世区块,创世区块的难度为131072,这是以太坊难度的下限。


实践一下

让我们来计算一个区块的难度。

以太坊的区块链是公开的,可以在这里查看:The ethereum blockchain explorer


现在我们将根据4212371区块的难度来计算4212372区块的难度。

先查看下4212371区块的难度和时间戳,难度为2,117,963,098,883,076,时间戳是2017-08-28 11:24:23。


再来看下4212372区块的时间戳,时间戳是2017-08-28 11:25:43。


现在需要的信息都有了。

parent_timestamp = 2017-08-28 11:24:23

parent_diff = 2,117,963,098,883,076

block_timestamp = 2017-08-28 11:25:43

block_number = 4212372

先算难度炸弹:

INT(2**((4212372 // 100000) - 2)) =

INT(2**(42 - 2)) = INT(2**40) = 2**40 = 1099511627776

再算难度调整:

2117963098883076 // 2048 * MAX(1 - (2017-08-28 11:25:43 - 2017-08-28 11:24:23) // 10, -99) =

2117963098883076 // 2048 * MAX(1 - (2017-08-28 11:25:43 - 2017-08-28 11:24:23) // 10, -99) =

2117963098883076 // 2048 * MAX(1 - 80 // 10, -99) =

2117963098883076 // 2048 * MAX(1 - 8, -99) =

2117963098883076 // 2048 * MAX(-7, -99) =

2117963098883076 // 2048 * -7 =

1034161669376 * -7 = -7239131685632

最终的难度为:

block_diff = 2117963098883076 - 7239131685632 + 1099511627776 = 2,111,823,478,825,220

检查下是不是大于创世区块的难度131072,显然满足条件。

是不是和查到的一模一样?4212372区块的难度为2,111,823,478,825,220。


以太坊难度的特点

以太坊的区块难度以单个区块为单位进行调整,可以非常迅速的适应算力的变化,正是这种机制,使以太坊在硬分叉出以太坊经典(ETC)以后没有出现比特币分叉出比特币现金(BCC)后的算力“暴击”问题。同时,以太坊的新区块难度在老区块的基础上有限调整的机制也使区块难度不会出现非常大的跳变。

下面是BTC和BCC的难度变化:


这是以太坊的难度变化:


可以看出以太坊难度的递增比较平滑,出现的几个跳变是由于难度炸弹造成的。


关于难度炸弹

其实大家谈之色变的难度炸弹只是难度计算公式中的一部分而已,也就是下面的这部分。

INT(2**((block_number // 100000) - 2))

难度炸弹每100000个区块就会翻倍,目前只有1T左右。但是由于是指数级递增,还记得用铜钱铺满棋盘的故事么,指数递增是很迅速的,用炸弹来形容倒是非常贴切。



可以看到,到5400000区块时,难度将达到4500T,会使挖矿变得极为困难,甚至不能收回电费成本。

为什么要这样设置呢?

以太坊的开发者认为,现在的版本都不是最终的状态,未来将会使用PoS方式分配新产生的以太坊而不是PoW。PoS又被称为虚拟挖矿,可以理解为根据目前拥有的以太坊的多少来分配新产生的以太坊,届时将不再需要矿工消耗大量电力挖矿,这将会使矿工们“失业”。

比特币社区出现的矿工和用户对立的情况警示了以太坊的开发者,为了避免矿工拥有过多的话语权,阻碍新技术的应用,于是设立了难度炸弹,到一定的时间点之后,矿工自然会因为无利可图自动退场。不得不说,这个设定是非常英明的。

因为每个新区块的难度包含上一区块的难度,而上一区块的难度又包含了一个难度炸弹,也就是说难度炸弹的难度会在每个区块的难度中逐渐累积起来。这将使难度炸弹除了指数跳变的影响之外,又增加了较为平缓的长期影响。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325444225&siteId=291194637