フォーク選択と累積難易度
ネットワークが数分間分断されたときなど、2 つのバージョンのチェーンがしばらく競合することがあります。そのとき ZooBC のすべてのノードは、累積難易度の大きいブランチに従います。累積難易度とはブロックの重みの累計で、早く抽選された生成者のブロックは遅く抽選された生成者のブロックより重くなります。このルールはすべてのノードで同じ算術なので、誰かが勝者を選ばなくてもネットワークは収束します。
長さではなく重さ
各ブロックは累計に一定の量を加えますが、その量は、生成者がその高さの抽選で何番目だったかが下がるほど小さくなります。最も単純な形では、ブロックは 1,000,000 を抽選における生成者の順位で割った値を加えます。最初に抽選された生成者のブロックは 1,000,000 を、3 番目の生成者のブロックは約 333,333 を加えます。
したがって、時間どおりのブロックからなるチェーンは、生成者が番を逃し続けたチェーンより重くなります。後者の方が長い場合でもそうです。ラウンド 1 のブロック 3 つは 3,000,000、ラウンド 3 のブロック 4 つは約 1,333,332 の重みです。
他の方法ではうまくいきません。トランザクション数を数えるなら、生成者はブロックを水増しして勝ててしまいます。「先に見た方」はネットワークの遅延によってノードごとに異なるため、ノードが永遠に分裂したままになりかねません。そして、あるブランチを正式なものと定める権威は存在しません。
ブロックの正確な重み
バージョン 0.5.8 以降、ブロックの増分は次のとおりです。
increment = floor(1,000,000 × |S| / (k × |A|))
- S は直前のブロック時点の抽選集合、つまりアクティブなノードから一時停止中のノードを除いたものです。
- A は直前のブロック時点のアクティブなレジストリ全体です。割合 |S| / |A| が 1 を超えることはありません。
- k は、ブロックのラウンドまでのラウンドにおいて、生成者が最後に現れた位置までの抽選回数です。時間どおりにタイムスタンプを押した最初に抽選された生成者なら 1、3 回目の抽選で指名された生成者なら 3 です。(そこに生成者が見つからない場合は 2 × n 回(n は抽選集合のノード数)の抽選の中で最初に現れた位置を使い、それでも見つからなければハッシュ順の位置を使います。)
2 つの下限によって、ブロックが本来以上の重みを主張することを防ぎます。k はブロックのタイムスタンプが属するラウンドより小さくなることはないため、遅いタイムスタンプで早いラウンドの重みを保つことはできません(ラウンド、タイミング、時間枠 を参照)。また直前のブロックの生成者については、k は降格後のラウンドである 10 より小さくなることはありません(優先ラウンド、空のブロック、直前の生成者 を参照)。
抽選集合の割合は、ネットワークが分裂したときに重要になります。分裂した両側がそれぞれ連絡の取れないノードを一時停止にすると、割合がなければ双方が時間どおりのブロックごとに 1,000,000 を加え、同じ重さに見えてしまいます。割合があれば、11 席のうち 3 席で動く側はブロックごとに最大 272,727 しか加えられず、8 席の側は 727,272 を加えます。ブロック数が同じなら、より多くの席を持つ側が重くなります。
すべてのブロックは自身の累積難易度を 10 進数の文字列として持っています。すべてのバリデーターがそれを再計算し、誤った値を記したブロックを拒否します。2 つの合計は、まず桁数で、次に 1 桁ずつ比較されます。
参加スコアはこのいずれにも関係しません。重みが読み取るのは抽選での位置と抽選集合であって、スコアではありません。
完全な同点
完全な同点は、たとえば同じ長さの 2 つのブランチがどちらも時間どおりのラウンド 1 のブロックだけでできている場合に起こります。バージョン 0.5.8 以降、2 つのブランチの先端ブロックのハッシュをバイト列として比較し、小さい方のハッシュが勝ちます。ノードは、先端ハッシュが自分のものより小さい同じ重みのブランチへ切り替え、そうでなければ自分のブランチを保持し、誰にも問い合わせません。すべてのノードが同じ 2 つのハッシュに同じ比較を適用するので、ネットワーク全体が一斉に収束します。
切り替える前にブランチの重みを量る
あるピアが自分のブランチの方が重いと主張しても、それだけでは何の証明にもなりません。ノードは自分のチェーンに手を付ける前に、競合するブランチの重みを量ります。
- 2 つのチェーンが共有する最後のブロックを見つけます。
- そのブロック以降のブランチをページ単位で取得し、すべてのヘッダーの署名とブロック間のリンクを確認します。
- 各ブロックの重みを再計算します。その際、共通ブロック以降のそのブランチ自身のレジストリと一時停止を再生します。分裂の向こう側ではそれらが異なることがあるからです。
- ブランチが自分のチェーンより重くなった時点で打ち切り、そのときにはじめてロールバックしてそのブランチを適用します。最終的に軽いブランチや、限られたブロック数のうちに追い越せないブランチは、何もロールバックせずに拒否されます。
主張した難易度が再計算値と異なるピアは、嘘をついたものとして記録されます。
ロールバックには厳格な制限があります。mainnet では 720 ブロック(3 時間)を超えることはなく、スパインチェーン上で共同署名された最後のチェックポイントより下に戻ることもありません。ファイナリティとロールバック を参照してください。
遅れたノードとピアへの問い合わせ
先端ブロックが 7,200 秒(2 時間)より古いノードは、先にいると主張するピアを無条件には信用しません。登録済みピアのサンプルの少なくとも 3 分の 2 が報告する先端に従います。より重いブランチの確認や自身の健全性の確認のためにノードが行うその他のピアへの問い合わせは、ローカルなポリシーであってコンセンサスではありません。