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

这是一篇写得非常好的关于难度的文章。

来源并参考: https://zhuanlan.zhihu.com/p/28830859

什么是难度
难度(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

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 & 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
}

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

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

整数除法,符号//
计算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,这是以太坊难度的下限。

猜你喜欢

转载自blog.csdn.net/wowotuo/article/details/78887617