首页 > 区块链博客 > 以太坊(Ethereum ETH)是如何计算难度的

以太坊(Ethereum ETH)是如何计算难度的

2018-05-07 10:48:00 阅读量:

全文摘要:

什么是难度难度(Difficulty)一词来源于区块链技术的先驱比特币,用来度量挖出一个区块平均需要的运算次数。挖矿本质上就是在求解一个谜题,不同的电子币设置了不同的谜题。比如比特币使用SHA-256、莱特币使用Scrypt、以太坊使用Ethash。一个谜题的解的所有可能取值被称为解

什么是难度

难度(Difficulty)一词来源于区块链技术的先驱比特币,用来度量挖出一个区块平均需要的运算次数。挖矿本质上就是在求解一个谜题,不同的电子币设置了不同的谜题。比如比特币使用SHA-
256、莱特币使用Scrypt、以太坊使用Ethash。一个谜题的解的所有可能取值被称为解的空间,挖矿就是在这些可能的取值中寻找一个解。

这些谜题都有如下共同的特点:

  • 没有比穷举法更有效的求解方法
  • 解在空间中均匀分布,从而使每一次穷举尝试找到一个解的概率基本一致
  • 解的空间足够大,保证一定能够找到解

假设现在有一种电子币,解所在的空间为0-99共100个数字,谜题为x<100。这个谜题非常简单,空间中的任何一个数字都能满足。如果想让谜题更难以求解该怎么做呢?把谜题改成x<50,现在空间中只有一半的数字能满足了,也就是说,现在的难度比原来大了。并且我们还能知道难度大了多少,原来求解平均要尝试1次,现在求解平均要尝试2次了,也就是说,x<50的难度是x<100的2/1=2倍。同理,如果谜题变成x<10,难度就是x<100的100/10=10倍。

现在谜题多了个参数Difficulty,谜题变成了x<Difficulty,上面的
100、
50、10,都是Difficulty的取值,这个参数Difficulty就是我们常说的难度(Difficulty)。

难度(Difficulty)通过控制合格的解在空间中的数量来控制平均求解所需要尝试的次数,也就可以间接的控制产生一个区块需要的时间,这样就可以使区块以一个合理而稳定的速度产生。

当挖矿的人很多,单位时间能够尝试更多次时,难度就会增大,当挖矿的人减少,单位时间能够尝试的次数变少时,难度就降低。这样产生一个区块需要的时间就可以做到稳定。

有了难度,我们还需要一种算法,用来确定和调整合理的难度值是多少。

以太坊是如何计算难度的

以太坊的代码是完全开源的,官方使用Go语言实现了一个钱包,被称为Geth。

Geth可以从这里下载:Go Ethereum Downloads

源代码可以在这里找到:ethereum/go-ethereum

Geth的开发者对“共识”的理解相当深入,代码也很容易阅读。关于难度的代码在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)}}

有三种计算难度的规则,分别对应以太坊的三个主要版本:已经成为历史的Frontier、正在使用的Homestead和将要发布的Metropolis。

现在正在使用的Homestead的难度计算规则在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 & auditx := new(big.Int)y := new(big.Int)// 1 - (block_timestamp - parent_timestamp) // 10x.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 factorperiodCount := 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}

不过我不是来搬运代码的,我们说人话。

在解释具体计算过程之前,先来介绍一下,用到的几种运算符。

  • 整数除法,符号//

计算a//b时,先计算a/b,然后取不大于a/b的最大整数。

例如:

-11.3 // 5 = -3

11.3 // 5 = 2

  • 取整,符号INT

计算INT(a)时,仅仅取整数部分,丢弃小数。

例如:

INT(3.7) = 3

INT(-3.7) = -3

  • 最大值,符号MAX

计算MAX(a,b)时,结果为a和b中较大的那一个。

例如:

MAX(-1,0) = 0

MAX(7,10) = 10

计算一个区块的难度时,需要以下输入:

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,这是以太坊难度的下限。

来源:币乎


上一篇:【五黑有话说~第10篇】不好意思说中了:比特币带头大跌,接下来该怎么办?
下一篇:完全理解以太坊ETH(二)——智能合约

微信

微信

微信

微信