
【完全保存版】SolanaのPoHの徹底解説(初めから丁寧に)
この記事では、Solanaのホワイトペーパーをもとに、SolanaのPoHという仕組みについて、解説していきます。
https://solana.com/solana-whitepaper.pdf
1 PoHとは
PoH(Proof Of History)とは2つのイベントがあった時、どちらが先に起こったかを検証する仕組みです。

2 なぜ順番の検証が重要なの?
ブロックチェーンでは、「ノード」が世界中に分散しています。
そのため、この仕組みがないと、イベントが到着する時間がノードによって変わってしまうかもしれません。
そうなると、ノード毎に整合性が取れなくなってしまいます。

3 ブロックチェーンと同期処理
なお、多くのチェーンではその問題が発生しないように、ノード間で「同期」を行います。
それが、ブロック生成に時間がかかる要因の1つです。
一方、SolanaはPoHで順番が検証されるので、「同期」は行われません。(非同期です。また、後に出てきますが、一部行なっています。)
4 PoHの概要について
1 PoHの基本構成
PoHは「Index」「Operation(操作)」「Output Hush」(以下、Output)の3つから構成されます。
「Index」で順番がわかり、「Operation」で操作を行い、「Output」が出力される結果です。

2 Indexについて
まずは、Indexについて見ていきましょう。
これは、時系列になります。
なぜ、この時系列になると言い切れるかは、後ほど出てきます。

3 OperationとOutput Hash
次に、「Operation」と「Output」の関係性を見てみましょう。
下のように、「sha256」というハッシュによって「Output」が生成されています。

また、ハッシュ関数には、生成された値から、元の値を予測できないという性質があります。
そのため、逆方向には進みません。

次に、「Output」からの「Operation(操作)」の流れを見てみましょう。
下のようになっています。
つまり、できた結果が、その次の入力につながっています。

つまり、このように進んでいます。
「hash1」をハッシュ化したものが「hash2」
「hash2」をハッシュ化したものが「hash3」
・・・・

そして、逆方向に進まないという性質から、「hash3」は必ず「hash1」より後にできています。
そのため、Index:3はIndex:1より後であると言い切ることができます。

5 一定間隔毎の観測について
1 全部の突合は必要?
では、下のように、もう少し多い場合を考えてみましょう。

下の結果が正しいかを検証するには、
Index:2の計算結果がIndex:2の「Output」と合致
Index:3の計算結果がIndex:3の「Output」と合致
・・・
Index:300の計算結果がIndex:300の「Output」と合致
をすれば良いのですが、本当に全部必要でしょうか?

結論としては、全ての突合は必要ありません。
300回ハッシュ化した結果が、最終結果とあっていれば、必ず途中経過はあっています。
なぜなら、万一、どこかで違っていたら、その後もずれ続け、最終結果が一致しないからです。

2 ホワイトペーパーでの間隔
では、ホワイトペーパーの値を見てみましょう。
どうやら、1,048,576ごとにチェックをしているようです。
ちなみに、これは2の20乗の数です。

6 イベントを含めて考えよう
ここまではただのハッシュの順番の話でした。
では、実際にイベントを追加して見てみましょう。
下のように、前のハッシュにイベントのハッシュをつなげたものをハッシュ化しています。

こんな感じですね。

あとは前章と同じです。
下のように2つのイベントがあったとします。
Indes:336の時のイベントと、Index:600の時のイベントでは、Index:600の方が後に起こったことがわかります。

ちなみに、インプットの様子を図にするとこんな感じになります。
Inputを加えた状態でハッシュ化されます。
また、その結果が次のインプットになるので、それ以降の結果に影響を与えます。(赤色になっています。)

7 検証の並行処理について
1 検証処理について
まず、検証自体は単純な処理で行えます。
下の場合、100回進んでいます。
そのため「hash200」を100回ハッシュ化した結果が「hash300」になるかを確認すれば良いです。(イベントはない場合を想定)

一方、Indexが300から、400までについても全く同じことが言えます。
「hash300」を100回ハッシュ化して「hash400」になるかを確認すればいいですね。

2 並列処理について
そして、2つの処理は別々の独立した処理です。
無関係のものとして、検証をすることができます。
つまり、別々の検証者(Verifier)で独立して検証を行うことができます。

図にするとこんな感じです。
並列処理ができるため、「Verifier」を増やすことで、検証スピードを上げることができます。

3 検証時間の計算方法について(1コアだけの場合)
では、かかる時間の計算式を見てみましょう。
もし、並列処理でない場合をまずは考えます。
下のように、全ハッシュ数を1つのコアが1秒でいくつのハッシュを検証するかを割ることで求められます。

下のような感じです。
仮に、ハッシュが100万あり、検証スピードが1秒あたり10万であれば、10秒ですね(100万➗10万)

4 検証時間の計算方法について(並列処理の場合)
では、並列処理の場合を見てみましょう。
こんな感じですね。

下のように、コアの数でかけることができます。
ということは、仮に、ハッシュが100万あり、検証スピードが1秒あたり10万のコアが5個あれば、2秒ですね(100万➗10万➗5コア)

このようにして、Verifierの数を増やすことで検証スピードを上げることができます。
8 ジェネレータの水平スケーリングについて
次は、ジェネレータを増やすことで、生成スピードを上げることができるかという話についてです。
1 ジェネレータとは
まず、ジェネレータとは、今まで扱っていた、連続したハッシュを生成してくれるものです。
今までは一つの前提でしたが、複数同時に生成することはできるのでしょうか?

2 独立したハッシュの生成
まずは、下のようにハッシュを見てみると、ジェネレータAとBでは別のハッシュが生成されています。
つまり、この二つは(次の同期をやらないと)独立しており、無関係です。
ということは、ジェネレータAとBでは時間の前後関係がわかりません。

3 同期と順番の確定について
この2つを関連させるのが同期です。
下のように、Bの「hash1b」をAに渡しています。
そして、その結果、「hash3」が作られています。

つまり、独立していた、2つのジェネレータに順序関係が生まれました。
下のように、「hash1b」は「hash3a」よりも前に発生していることがわかります。

4 図解で考えよう
では、図で理解してみましょう。
下のように、2つのジェネレータがあります。

まずは、下のように、AからBにインプットが行われ、同期が行われています。

次に、BからAにも同期が行われています。
これで、AとBのジェネレータがつながりました。

5 3つ以上のジェネレータの場合
では、下のように、3つのジェネレータで考えてみましょう。

この場合、同期は、下のように行えば済みます。
この状態で、追加でAとCを同期させる必要はありません。

6 同期と可用性について
同期を行う以上、「可用性」の問題が生じてしまいます。
「可用性」とは、アクセスが可能で、使用可能な状態の割合のことです。
仮に、「99.9%」だとします。

では、ジェネレータが3つの場合を考えましょう。
この場合、システムを全体で見たときの可用性は「99.9% × 99.9%」になります。

ということは、ジェネレータが10個ある場合は、99.9%の10乗、つまり、99%にまで下がってしまいます。
このように、スケーリングと可用性の間には、トレードオフの関係性があります。
9 悪意のあるジェネレータへの対処について
1 悪意のあるジェネレータについて
では、下のBのように、悪意のあるジェネレータがイベントの順番を故意に変えた場合に備えて、どう防げば良いでしょう。

2 イベント生成時による解決方法
この解決方法としては、イベント生成時に、「最近のハッシュを組み込む」ことで対応しています。
たとえば、下のように、イベント3では、直前の「hash30a」をつなげて作っています。

では、このイベント3を悪意のあるものが下の位置に移動させようとしたらどうなるでしょう?
この場合、イベント3は「hash30a」を含んでいますが、この時点では、そもそも「hash30a」は存在していません。
これにより、順番が変えられたことを検知することができます。

3 図解で見てみよう
図でも見てみましょう。
このように、イベントを挿入するときは、直近のハッシュを組み込んだ上で、挿入しています。

4 短期間の移動について
しかし、これは短期間の移動では有効ではありません。
たとえば、Index:40をIndex:35に移動しようとしたらどうでしょう?
この場合、「hash30a」はIndex:35の前なので、矛盾は生じません。

そのため、短期間では、ハッシュの順序が正しくない可能性があるという前提でソフトウェアを設計することが推奨されています。
5 秘密鍵によるサイン
ちなみに、悪意のあるジェネレータが、下のように組み込むハッシュを改ざんできてしまえば、矛盾が生じなくなってしまいます。

そのため、書き換えることができないように、秘密鍵を使ってサインを行います。
これによって、勝手に書き換えることができなくなります。

10 最後に
いかがだったでしょうか?
なるべく丁寧にPoHについて説明してきました。
皆さんの理解の一助になれば幸いです。
いいなと思ったら応援しよう!
