在源码consensus(共识)模块中,ethash的实现,而pow的难度调整算法的实现位于consensus/ethash/consensus.go中
入口方法:
// CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
return CalcDifficulty(chain.Config(), time, parent)
}
// CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, big1)
switch {
case config.IsByzantium(next):
return calcDifficultyByzantium(time, parent)
case config.IsHomestead(next):
return calcDifficultyHomestead(time, parent)
default:
return calcDifficultyFrontier(time, parent)
}
}
可以看出在eth的不同阶段,他的调整算法是不一样的,但算法的输入参数是一致的
time 新区块的生成时间
parent 上一个区块的参数
1.以太坊的四个阶段
以太坊的创始人们为它设定了4个发展阶段:Frontier,Homestead,Metropolis,Serenity,阶段之间的转换需要通过硬分叉的方式实现。
Frontier是2015年7月以太坊发行初期的试验阶段,那个时候的软件还不太成熟,但是可以进行基本的挖矿,学习,试验。系统运行之后,吸引了更多的人关注并参与到开发中来,以太坊作为一个应用平台,需要更多的人去开发自己的去中心化应用来实现以太坊本身的价值。随着人气渐旺,以太坊的价值也水涨船高。
Homestead是以太坊第一个正式的产品发行版本,于2016年3月发布。目前以太坊仍运行在该阶段,100%采用PoW挖矿,但是挖矿的难度除了因为算力增长而增加之外,还有一个额外的难度因子呈指数级增加,这就是难度炸弹(Difficulty Bomb)。由于PoS的运用将会降低挖矿的门槛,因为你不需要再去购买价格高昂的硬件矿机,只需要购买一定数量的ETH,将其作为保证金通过权益证明的方式验证交易有效性,即可拿到一定的奖励。因此,对矿工来说他们花高价购买的矿机将无用武之地,这势必会引起矿工的不满。为了防止PoW转PoS的过程中矿工联合起来抵制,从而分叉出两条以太坊区块链,难度炸弹被引入。难度炸弹指的是计算难度时除了根据出块时间和上一个区块难度进行调整外,加上了一个每十万个区块呈指数型增长的难度因子。计算公式如下,最右的相加项即为难度炸弹:
block_diff = parent_diff + parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) / 10, -99) + int(2^((block.number / 100000) - 2))
这有点像一个温水煮青蛙的过程,一开始附加的难度并不引人注意,但是随着区块高度的增加,呈指数增长的难度因子比重将会显著提高,使得出块难度大大增加,矿工将难以挖出新的区块。目前以太坊的区块高度超过420万,难度炸弹已经开始发挥威力,出块时间从之前很长一段时间维持的平均15秒左右渐渐增加到了25秒,每天新产生的ETH降到了19000以下(2017年9月2日数据)。由于出块越来越艰难,到最后区块将被完全冻结,这个过程又被称作“冰川时代”(Ice Age)。有了这个预期,那么转PoS引起的硬分叉就不会是一个困难的选择,毕竟没有人会继续待在那条将要走向凛冬的区块链。
然而PoS的机制设计中有很多问题需要解决,开发时间比原本计划的要长。根据最近的以太坊改进建议EIP-649(2017年8月26日被接受 ), 转换到权益证明(PoS)的时间节点将被延迟约一年半,工作量证明(PoW)将会继续担当大任。为了不堵塞交易,维持系统稳定运行,难度炸弹也需要被相应地延迟,实现方式是将挖矿难度按照回退300万个区块的高度去计算,因此出块时间又将回到15秒左右,如果不采取任何行动,则ETH的供应量会明显超出按原本难度炸弹时间表规划的供应量,这会导致通货膨胀,降低ETH的价值,为了使ETH的供应量与原本计划的数量相当,于是需要减少每个区块的奖励,从原本的5个ETH减少为3个ETH,叔块的奖励也将相应减少。
Homestead的下一阶段Metropolis又被分成了两个阶段:Byzantium和Constantinople。Byzantium预计在9月下旬发布,届时难度炸弹延迟和区块奖励减少将被执行。Constantinople的规划与开发预计将在今年晚些时候进行。
Byzantium的区块高度计算调整:
fake_block_number = max(0, block.number - 3_000_000) if block.number >= BYZANTIUM_FORK_BLKNUM else block.number
Byzantium的区块奖励计算调整,3*10e18 wei,即3ETH:
new_block_reward = 3_000_000_000_000_000_000 if block.number >= BYZANTIUM_FORK_BLKNUM else block.reward
乐观估计,以太坊的最后一个阶段Serenity,即转成PoS的软件版本至少要到2018年底发布了,具体实施要到2019年春季后。到时PoS的实行将会吸引更多分布式节点的加入,为各种分布式应用(Dapp)的运行打下物理基础,以太坊将有希望成为去中心化领域的app store,互联网的新时代也将到来。从软件开发的性质来说,总是会有各种意想不到的问题和难度出现,所以谁也不知道以太坊设想的乌托邦Serenity最终将在何时到来,不过这是一个值得期待的事件。如果说比特币是数字加密货币开创性的先驱,那么以太坊就是继往开来的主将,它有着更宏伟的蓝图。
***简单来说,
Frontier(前沿)实验阶段
Homestead(家园)加入了难度炸弹
Byzantium(大都会)拆除了难度炸弹,修改区块奖励
Serenity(宁静) POS
现在正处于Byzantium阶段
我们只看该阶段的难度调整算法
Byzantium难度调整算法
// calcDifficultyByzantium is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Byzantium rules.
func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int {
// https://github.com/ethereum/EIPs/issues/100.
// algorithm:
// diff = (parent_diff +
// (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -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)
// (2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9
x.Sub(bigTime, bigParentTime)
x.Div(x, big9)
if parent.UncleHash == types.EmptyUncleHash {
x.Sub(big1, x)
} else {
x.Sub(big2, x)
}
// max((2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9, -99)
if x.Cmp(bigMinus99) < 0 {
x.Set(bigMinus99)
}
// parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -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)
}
// calculate a fake block number for the ice-age delay:
// https://github.com/ethereum/EIPs/pull/669
// fake_block_number = min(0, block.number - 3_000_000
fakeBlockNumber := new(big.Int)
if parent.Number.Cmp(big2999999) >= 0 {
fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, big2999999) // Note, parent is 1 less than the actual block number
}
// for the exponential factor
periodCount := fakeBlockNumber
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
}