難易度アルゴリズムとフォーク選択ルール

Parallaxの難易度調整の歴史 — ジェネシスからのBTC型DAA、ブロック17,560でのASERTアップグレード (PIP-0002)、そして累積ワークに基づくNakamotoのフォーク選択ルール — を正確にたどります。

コンセンサスパラメータ
パラメータ記号備考
目標ブロック間隔
τ
600秒
Bitcoin型の目標値
未来方向のタイムドリフト上限
300秒
5分
MTPサンプル数
11ブロック
直近11個のタイムスタンプの中央値
難易度アルゴリズムの履歴
BTC型DAA → ASERT
ジェネシス〜17,559ブロックはBTC型2016ブロックウィンドウ、17,560ブロック以降はASERT (aserti3-2d、PIP-0002)。
概要
Parallaxは、初期のBTC型DAAフェーズを経て、XHash Proof of Workとブロック単位のASERT難易度アルゴリズムにより、10分ブロックを目標とします。
  • ジェネシスからブロック17,559までは、ParallaxはBitcoin型の2016ブロック難易度調整ウィンドウ (BTC型DAA) を使用していました。
  • PIP-0002 (BTC型難易度調整アルゴリズムからASERTへの移行) により、ブロック17,560からASERTが有効化されました。
  • ASERT (aserti3-2d、Bitcoin Cashで用いられているもの) は、固定アンカーに対してブロックごとに難易度を調整し、ハッシュレート変動のもとでも600秒目標へ滑らかに収束します。
  • Median-Time-Past (MTP、直近11個の中央値) がタイムスタンプの妥当性を担保し、ノードは未来方向に+300秒を超えるヘッダを拒否し、target = ⌊(2^256−1)/D⌋ でPoWを検証します。
ターゲット ↔ 難易度
XHash検証で用いるワーク閾値のマッピング (BTC-DAA時代とASERT時代で共通)。
  • TWO256M1 = 2^256 − 1 とする。ヘッダが有効なのは XHash(header) ≤ target のとき、ここで target = ⌊TWO256M1 / D⌋。
  • 難易度Dが高いほど ⇒ targetが小さくなり ⇒ ブロックは難しくなる。
  • Header.MixDigestは、hashimotoから計算されるdigest (light/fullパス) と等しくなければならない。
  • 難易度は厳密に正である必要があり、ゼロや負は無効です。
ターゲット計算 (概念)
疑似コード
// Given difficulty D (big integer)
TWO256M1 = (1n << 256n) - 1n
target   = TWO256M1 / D
valid    = BigIntFromBytes(result) <= target
難易度調整: BTC型DAA → ASERT
歴史的な2016ブロックウィンドウから、アンカー基準の現代的なブロック単位ASERTへ。
  • ジェネシス → ブロック17,559: CalcNakamotoDifficulty() はエポックアンカーを用いたBitcoin型2016ブロックウィンドウを実装します。
  • ブロック17,560以降: CalcAsertDifficulty() は固定アンカー {anchorHeight, anchorParentTime, anchorTarget} を用いてaserti3-2dを実装します。
  • ASERTはアンカーからの高さオフセット Δh と経過時間 Δt を用いて、2日の半減期で難易度を600秒目標へ押し進めます。
  • 歴史的なブロックは元のBTC型DAA難易度を保持し、それらのルールの下で引き続き検証されます。前向きの調整は完全にASERTベースです。
高さ依存の難易度選択 (概念)
疑似コード
// Difficulty selection by era
CalcDifficulty(config, anchor, parent, header):
  if height(header) < 17560:
    // BTC-style DAA: 2016-block window with epoch anchors
    return CalcNakamotoDifficulty(config, parent)
  else:
    // ASERT: per-block retarget relative to fixed anchor
    return CalcAsertDifficulty(config, anchor, parent, header)

// ASERT core idea (fixed-point, aserti3-2d)
CalcAsertDifficulty(config, anchor, parent, header):
  Δh = height(header) - anchor.height
  Δt = header.Time - anchor.parentTime   // seconds
  // τ = 600 s target, T_half = 172800 s (2 days)
  e  = ((Δt - Δh * τ) * RADIX) / T_half  // fixed-point exponent
  target = anchor.target * 2^e           // via cubic approximation in integer math
  target = clamp(target, 1, maxTarget)
  return DifficultyFromTarget(target)
Median-Time-Past (MTP)
有効性とタイムワープ耐性のためのタイムスタンプ健全性チェック。
  • MTP(parent) は、parentで終わる直近11個のブロックタイムスタンプの中央値。
  • 有効性には header.Time > MTP(parent) (厳密な不等式) が必要です。
  • 未来方向のドリフト上限: header.Time ≤ now + 300秒。
  • ASERTにおいては、アンカーからの経過時間はMTPで制約されたタイムスタンプによって駆動されるため、古典的な長期タイムワープ攻撃を難易度アルゴリズムに対して防ぎます。
MTPの計算
疑似コード
MTP(n):
  ts = timestamps(n, upTo=11) // back from n inclusive
  sort(ts)
  return ts[len(ts)//2]
フォーク選択ルール
累積ワークが最大の有効チェーン。アクティブな難易度時代とは無関係です。
  • ChainWork[tip] = ChainWork[parent] + Work(block) を維持します。
  • Work(block) は target/difficulty の単調関数であり、整合的な定義であれば同等の順序を与えます。
  • ChainWork最大の有効ティップを選択し、同点の場合はティップハッシュの辞書順で決着させます。
  • 無効なヘッダ (時間、PoW、BTC-DAA/ASERTルール) はフォーク選択前に除外されます。
累積ワーク (概念)
疑似コード
Work(block):
  target = TWO256M1 / Difficulty(block)
  // Use an approximation that preserves ordering; e.g.,
  return TWO256M1 / (target + 1)

SelectBest(tips):
  return argmax(tips, ChainWork[tip])
リオーグと確率的ファイナリティ
絶対的ファイナリティではなく、深さに基づく保証。
  • 確認深度kは、kに対して指数関数的にリオーグ確率を下げます。
  • ウォレット/UIは、リスクに応じてk (例: 高額ならデフォルト6確認) を選びます。
  • ノードは病的なピアからのDoSを避けるため、実用上のガード (例: 最大リオーグ深度) を実装できます。
  • ASERTのブロック単位での滑らかな難易度応答は、大きなウィンドウを持つBTC型DAAに比べ、振動駆動型や機会主義的な長期リオーグのインセンティブを低下させます。
リオーグ処理 (概念)
疑似コード
OnNewTip(candidate):
  if ChainWork[candidate] > ChainWork[currentTip]:
    currentTip = candidate
    ReorgTo(candidate)
攻撃と対策
Parallaxの難易度設計に関連する主要な攻撃ベクトル。
  • タイムワープ: 厳密なMTP強制と、ASERTのブロック単位フィードバック (大きな固定ウィンドウなし) により、古典的なBTC型2016ブロックのタイムワープ操作を緩和します。
  • 未来タイムスタンプのスキュー: ローカル時間から+300秒を超えるヘッダを拒否します。
  • MixDigestの詐称: header.MixDigestはhashimotoの出力 (light/full) と一致しなければなりません。
ヘッダ有効性のサブセット
疑似コード
ValidHeader(h, parent):
  require(len(h.Extra) <= MaximumExtraDataSize)
  require(h.Time <= now() + 300)
  require(h.Time > MTP(parent))
  require(h.Difficulty > 0)
  // difficulty rules depend on height:
  //   < 17560: BTC-style DAA epoch invariants
  //   ≥ 17560: ASERT anchor-based invariants
  // PoW: MixDigest match & XHash(h) <= targetFrom(D)
パイプライン

エンドツーエンドの選択フロー

ヘッダは、Extraサイズ、時間 (MTPと未来ドリフト)、時代固有の難易度ルール (初期ブロックはBTC型DAA、高さ17,560以降はASERT)、gasリミット/EIP、高さ増分、PoWシールをチェックされます。有効なブロックはChainWorkを延ばし、最重量のティップが正規となります。

1
検証
2
アンカー / 時代の確認
3
難易度の計算 (DAA/ASERT)
4
ワークの累積
5
最重量の選択