ETH-难度调整-20

147 阅读3分钟
  • 概述:

    • 前⽂介绍了⽐特币每隔2016个区块调整难度,从⽽达到维持出块时间10min的⽬标。以太坊与之不同,每个区块都有可能会进⾏难度调整。以太坊难度调整较为复杂,存在多个版本,⽹络上存在诸多不⼀致,这⾥遵循以代码逻辑为准的原则,从代码中查看以太坊难度调整算法
    • 区块难度D(H):
      • difficulty_adjustment1.png
      • D(H)为本区块的难度,由基础部分和难度炸弹部分相加得到
      • P(H)为⽗区块的难度,每个区块的难度都在⽗区块难度的基础上进⾏调整
      • ⽤于⾃适应调节出块难度,维持稳定的出块速度
        • diff_adjust.png
      • x是调整的单位,西格玛是调整的系数
      • y和⽗区块的uncle数有关
        • 如果⽗区块中包括了uncle,则y为2,否则为1
        • ⽗块包含uncle时难度会⼤⼀个单位,因为包含uncle时新发⾏的货币量⼤,需要适当提⾼难度以保持货币发⾏量稳定
      • 难度降低的上届设置为-99,主要是应对被⿊客攻击或其它⽬前想不到的⿊天鹅事件
      • Hs为本区块的时间戳,P(H)hs为⽗区块的时间戳,均以秒为单位,并规定Hs>P(H)hs
        • 此部分是稳定出块速度的最重要部分:出块时间过短则调⼤难度,出块时间过⻓则调⼩难度
      • 基础部分有下界,为最⼩值D0=131072
    • 炸弹部分epsilon:
      • 根据以上以太坊难度调整算法可以看到,该算法可以很好地动态调整挖矿难度,从⽽保障系统整体出块时间维持在15s左右
      • 然⽽,以太坊在设计之初就计划要逐步从POW(⼯作量证明)转向POS(权益证明),⽽权益证明不需要挖矿,这种转变需要硬分叉实现
      • 以太坊本身为⼀个分布式系统,其转⼊POS必须经过系统中⼤多数矿⼯认可才⾏,如果届时矿⼯联合起来抗议转⼊POS,那么这⼀设计初衷就失效了
      • 设置难度炸弹的原因是要降低迁移到POS协议时发⽣分叉的⻛险,因为到时挖矿难度⾮常⼤,所以矿⼯有意愿迁移到POS协议
    • 具体代码实现,源码在/go-ethereum/consensus/ethash/consensus.go
      // 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)
          x := new(big.Int)
          y := new(big.Int)
          
          // (2 if len (parent uncles) else 1) - (timestamp - parent timestamp) // 9
          x.Sub(bigTime, bigParentTime)
          x.Div(x, big9)
          if parent.UncleHash == types.EmptyUncleHash {
              x.Sub(bigl, X)
          } else {
              x.Sub(big2, X)
          }
          // max((2 if len (parent uncles) else 1) - (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))
          // DifficultyBoundDivisor = big.NewInt(131072)
          y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
          x.Mul(y, X)
          x.Add(parent.Difficulty, x)
          // minimum difficulty can ever be (before exponential factor)
          // MinimumDifficulty = big.NewInt(131072)
          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)
      }
      // for the exponential factor
      periodCount := fakeBlockNumber
      // expDiffPeriod = big.NewInt(100000)
      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)
      }
      
  • 以太坊发展:

    1. Frontier
    2. Homestead
    3. Metropolis,分为2个⼦阶段:
      1. Byzantium:难度炸弹回调发⽣在这⼀阶段,在EIP (Ethereum Improvement Proposal)中决定
      2. Constantinople
    4. Serenity