著者:Tia、Techub News

ブロックチェーンはその非中央集権的な設計により効率を犠牲にしているため、実行速度の向上は常に解決が急務となる問題の一つです。ブロックチェーンの「実行レイヤー」は、各取引を処理し、それをチェーンに追加する重要な部分です。処理能力を加速するために、実行レイヤーの向上がコア戦略の一つとなり、並行実行はその重要な突破口となります。

従来のブロックチェーンは通常、逐次的に取引を処理するため、特に取引が密集しているネットワークでは取引速度が大きく制限され、混雑を引き起こします。しかし、並行実行を通じて複数の取引を同時に処理できるため、実行効率が大幅に向上し、チェーン上の負担が軽減されます。

並行処理とは何かをよりよく理解するために、実行から始めて、マージ後のPBSモードでのイーサリアムを例にして、実行が何であるかを説明し、実行が全取引ライフサイクルの中でどの位置にあるかを示します。

取引実行の具体的な段階

  1. 取引がメモリプールに入り、フィルタリングとソートが行われます:これは取引が提出された後の前処理段階であり、Mempool、Searcher、Builderの相互作用を含み、取引のフィルタリングとソートを完了します。

  2. Builderがブロックを構築(ただし実行はしない):Builderは利益のある取引を並べて1つのブロックを構築し、取引のパッケージ化とソートを完了します。

  3. Proposerがブロックを検証し、提出します:ブロックの構築が完了した後、Builderはブロックの提案をProposerに送信します。Proposerはブロックの構造と取引内容を検証し、正式にブロックをネットワークに提出して実行を開始します。

  4. 取引を実行:ブロックが提出された後、ノードはブロック内の取引を逐次実行します。これは状態更新の重要なステージであり、各取引はスマートコントラクトの呼び出し、アカウント残高の変化、または状態の変更を引き起こします。

  5. 証人が証明:検証者はブロックの実行結果と状態の根を証明し、それを最終確認として扱います。これにより、ブロックの実行レイヤーにおける真実性と有効性が確保され、不一致を防ぎます。

  6. 状態の同期:各ノードはブロックの実行結果(アカウント残高、契約状態の更新など)を自身のローカル状態に同期します。各取引の実行後、ノードは新しい状態の根を計算し、次のブロックの初期状態として保存します。

もちろん、これはブロック単位の取引の状態同期に過ぎません。最新のチェーン上の状態を維持するために、通常、ノードは逐次ブロックのデータを同期し、ブロックと状態を継続的に検証します。しかし、POSメカニズム下での最終性を達成するためには、アグリゲーターが各スロットの証人の署名を集約して完全な署名を形成し、それを次のスロットの提案者に渡す必要があります。また、検証者はあるエポック後に投票数に基づいてそのエポック内のすべてのブロックの状態を確認し、一時的な合意状態チェックポイントを形成する必要があります。連続して2つのエポックが多数の検証者の証明を受けた後に、ブロックと取引が最終性を達成します。

取引の全ライフサイクルを通じて、実行はProposerがBuilderに送信されたブロックの構造と取引内容を検証した後に発生します。実際の実行プロセスでは、取引を逐一処理し、関連するアカウントや契約の状態を更新します。すべての取引が実行を完了した後、Proposerは新しい状態の根(メルクル根)を計算します。これは、現在のブロックのすべての取引の実行結果と最終的なグローバル状態の要約です。一般的に言えば、完全なブロック実行プロセスは、イーサリアムが前の状態から次の状態に変わる際に完了する必要がある一連の計算を含みます。各取引の実行からメルクル根の計算まで。

順次実行

並行処理とは対照的に、順次実行があります。これは現在のブロックチェーンで一般的な実行方法です。通常、取引は順序通りに逐次実行されます。1つの取引が実行を完了すると、イーサリアムはアカウントの状態及び関連情報(残高、契約ストレージデータなど)をアカウント状態ツリーに更新し、新しいアカウント状態ハッシュが生成されます。すべてのアカウント状態ツリーが更新されると、状態メルクルルートと呼ばれる状態ツリーの根ノードハッシュが形成されます。状態メルクルルート、取引メルクルルート、及び受領証メルクルルートが完成すると、ブロックヘッダーはハッシュ計算を行い、そのブロックのブロックハッシュを生成します。

その中で、取引の実行順序は非常に重要です。メルクルツリーはハッシュ値の二分木であるため、異なる順序で形成されたメルクル根の値は異なります。

並行実行

並行実行の環境下では、ノードはブロック内の取引を並行処理しようとします。取引を順番に1つずつ実行するのではなく、取引を異なる「実行パス」に割り当てて同時に実行できるようにします。並行実行を通じて、システムはブロック内の取引をより効率的に処理し、スループットを向上させることができます。

すべての取引が実行を完了した後、ノードは実行結果(すなわち取引が影響を及ぼした状態の更新)を集約し、1つの新しいブロック状態を形成します。この状態はブロックチェーンに追加され、チェーン上の最新のグローバル状態を代表します。

状態競合

並行処理が異なるパスで同時に取引を処理するため、並行処理の大きな難しさは状態競合です。つまり、同じ時間帯に複数の取引がブロックチェーン上の同じ部分のデータ(状態)に対して読み取りまたは書き込み操作を行う可能性がある状況です。この状況を適切に処理しないと、実行結果が不確定になります。状態の更新順序が異なるため、最終的な計算結果も異なります。例えば、

2つの取引、取引Aと取引Bがあり、両方が同じアカウントの残高を更新しようとしています:

  • 取引A:アカウント残高を10増加させる。

  • 取引B:アカウント残高を20増加させる。

アカウントの初期残高は100です。

もし順次実行すれば、実行順序の結果は確定的です:

1. 取引Aを先に実行し、その後取引Bを実行します:

  • アカウント残高が最初に10増加し、110になります。

  • さらに20増加し、最終的に130になります。

2. 取引Bを先に実行し、次に取引Aを実行する:

  • アカウントの残高が最初に20増加し、120になります。

  • さらに10増加し、最終的に130になります。

この2つの順序では、最終的な残高はどちらも130です。これは、システムが取引実行の順序の一貫性を確保しているためです。

しかし、並行実行環境下では、取引Aと取引Bが同時に初期残高100を読み取り、それぞれの計算を行う可能性があります:

  1. 取引Aは残高が100であることを読み取り、計算後に残高を110に更新します。

  2. 取引Bも残高が100であることを読み取り、計算後に残高を120に更新します。

この場合、取引が同時に実行されるため、最終的な残高は120にしか更新されず、130にはなりません。これは、取引Aと取引Bの操作が互いの結果を「上書き」し、状態競合が発生したからです。

この種の状態競合問題は通常「データの上書き」と呼ばれます。つまり、取引が同じデータを同時に変更しようとすると、互いの計算結果を上書きし、最終的な状態が不正確になる可能性があります。もう一つの状態競合が引き起こす可能性がある問題は、実行順序を保証できないことです。複数の取引が異なる時間帯に操作を完了すると、異なる実行順序が発生します。順序が異なれば、計算結果も異なる可能性があり、結果が不確定になります。

この不確実性を回避するために、ブロックチェーンの並行実行システムは通常、いくつかの競合検出やロールバックメカニズムを導入するか、取引の依存関係分析を事前に行い、最終状態の一貫性に影響を与えないように並行実行を保証します。

楽観的並行と確定的並行

潜在的な状態競合問題に対処する方法は2つあります:確定的並行と楽観的並行。この2つのモードは、効率と設計の複雑性においてそれぞれトレードオフがあります。

確定的並行は状態アクセスを事前に宣言する必要があり、検証者やシーケンサーは取引の順序付け時に宣言された状態アクセスをチェックします。同じ状態への書き込みを試みる複数の取引がある場合、これらの取引は競合としてマークされ、同時実行が避けられます。異なるチェーンは状態アクセスを事前に宣言する形式が異なりますが、一般的には以下の方法が含まれます:

  • 契約の仕様による制約:開発者はスマートコントラクト内で直接状態アクセスの範囲を定めます。例えば、ERC-20トークンの転送には送信者と受信者の残高フィールドへのアクセスが必要です。

  • 取引の構造化データ宣言:取引に専用のフィールドを追加して状態アクセスをマークします。

  • コンパイラによる分析:高水準言語のコンパイラはコントラクトコードを静的に分析し、状態アクセスの集合を自動的に生成できます。

  • フレームワークによる強制的な宣言:特定のフレームワークでは、開発者が関数を呼び出す際にアクセスする必要のある状態を明示的に指定することが求められます。

楽観的並行は、まず取引を楽観的に処理し、競合が発生した場合に影響を受けた取引を順番に再実行します。競合の発生をできるだけ回避するために、楽観的並行設計の核心は、歴史データ、静的分析などを通じて状態を迅速に予測し、仮定することです。つまり、システムは完全に検証されていない状態で、特定の操作や状態更新が有効であると仮定し、すべての検証プロセスを待たずに実行し、パフォーマンスとスループットを向上させるのです。

楽観的並行は、状態に対する迅速な予測と仮定を通じて、競合の発生を可能な限り回避しようとしますが、それでも避けられない課題がいくつかあります。特に契約の実行やクロスチェーン取引に関与する場合、競合が頻繁に発生すると、再実行がシステムパフォーマンスを著しく低下させ、計算リソースの消費を増加させる可能性があります。

確定的並行は、取引前に状態依存性チェックを行うことで楽観的並行が引き起こす可能性のある競合を回避しますが、取引が提出される前に状態依存性を正確に宣言する必要があるため、開発者には高い要件が課され、実装の複雑性が増加します。

EVM並行のジレンマ

状態の競合に対するアプローチには確定性と楽観性の違いがあるだけでなく、並行処理の具体的な実装過程においては、チェーンデータベースアーキテクチャの観点からも考慮する必要があります。並行処理における状態競合の問題は、メルクルツリーアーキテクチャに基づくEVMでは特に難しいです。メルクルツリーは階層的なハッシュ構造であり、特定の状態データの変更が行われるたびに、メルクルツリーの根ハッシュ値も更新される必要があります。この更新プロセスは再帰的であり、葉ノードから根ノードまで階層的に計算されます。ハッシュは不可逆であるため、下層のデータの変更が完了した後でないと上層を計算できません。この特性により、並行更新が非常に困難になります。

もし2つの取引が並行して実行され、同じ状態(例えば、アカウント残高)にアクセスすると、メルクルツリーノードの競合が発生します。この競合を解決するには、通常は追加のトランザクション管理メカニズムが必要であり、複数のブランチで一貫した根ハッシュ値を保証する必要があります。これはEVMにとって実現が容易ではなく、並行化と状態の一貫性の間で妥協を強いられます。

非EVM並行ソリューション

ソラナ

イーサリアムのグローバル状態ツリーとは異なり、ソラナはアカウントモデルを採用しています。各アカウントは独立したストレージスペースであり、帳簿に保存されているため、パス競合問題を回避しています。

ソラナは確定的並行を採用しています。ソラナでは、各取引を提出する際にアクセスするアカウントと必要なアクセス権(読み取り専用または読み書き)を明示的に宣言する必要があります。この設計により、ブロックチェーンノードは取引実行前に各取引がアクセスするリソースを事前に分析できます。取引が実行を開始する前にすべてのアカウント依存関係が明確にされているため、ノードはどの取引が同じアカウントにアクセスするか、どの取引が安全に並行実行できるかを判断でき、競合を回避することで並行スケジューリングの基盤を実現します。

各取引が実行前にアクセスする必要のあるアカウントと権限を宣言しているため、ソラナは取引間にアカウントの依存関係が存在するかどうかを確認できます(シーレベルモデル)。取引間に共有の読み書きアカウントがなければ、システムはそれらを異なるプロセッサに割り当てて並行実行できます。

Aptos

Aptosの並行実行設計はイーサリアムとは大きく異なり、アーキテクチャとメカニズムにおいていくつかの重要な革新を行い、主にアカウントモデルと状態ストレージにおいて表れています。

イーサリアムは取引を実行する際にグローバル状態ツリー(MPT)を頻繁に更新する必要があります。すべてのアカウントと契約の状態は共有された状態ツリーに保存されており、どの取引もこの状態ツリーの一部にアクセスし、更新する必要があります。一方、Aptosはアカウントを独立した状態ユニットに分割し、各オブジェクトが独立したキーと値のペアであり、オブジェクト間は独立して存在し、互いに影響しないため、明示的な参照関係がある場合のみ関連付けられます。オブジェクト間に共通のツリーパスはなく、ロック競合は発生せず、完全に並行処理が可能です。

Aptosの基盤データ構造はジェリーフィッシュメルクルツリーです。各オブジェクトの状態は最終的にJMTに保存され、独立したキーと値のペアとして扱われます。イーサリアムのMPTとは異なり、ジェリーフィッシュメルクルツリーは完全な二分木構造の形式をとっており、この形式によりノードの保存パスとクエリパスが簡略化され、検証時間が大幅に短縮されます。また、各アカウントのツリー内での位置は固定されており、ツリー内のノードは独立して保存されるため、複数のアカウントの更新と検索を並行して行うことができます。

Aptosは楽観的並行を採用しており、すべてのアカウントの依存関係を事前に提供する必要はありません。そのため、AptosはBlock-STMを使用し、Block-STMは事前に設定された取引順序を利用して依存関係を推定し、停止回数を減らします。

並行EVM

非EVM並行と比較して、並行EVMは状態依存性、競合検出、ガス管理およびロールバックメカニズムなどの問題に直面した際、技術的な難易度が高くなります。これをよりよく理解するために、Sui、Monad、Cantoなどの並行EVMプロジェクトがこれらの問題をどのように解決しているかを参考にできます。

Sui

SuiもAptosと同様にオブジェクトモデルを使用して状態を処理し、各オブジェクト(アカウント、スマートコントラクトの状態など)を独立したリソースとして扱います。これらのオブジェクトはオブジェクトのユニークな識別子によって区別されます。取引が異なるオブジェクトに関与する場合、これらの取引は並行して処理することができ、異なる状態に対して操作を行うため、直接的な競合が発生しません。

Suiはオブジェクトモデルを使用して状態を管理していますが、EVMとの互換性を持たせるため、Suiのアーキテクチャは追加の適応層または抽象メカニズムを通じてオブジェクトモデルとEVMのアカウントモデルを橋渡ししています。

Suiでは、トランザクションのスケジューリングに楽観的並行戦略を使用し、トランザクション間に競合がないと仮定します。競合が発生した場合、システムはロールバックメカニズムを使って状態を復元します。

Suiはオブジェクトモデルと状態隔離技術を使用しており、状態依存性問題を効果的に回避できます。各オブジェクトは独立したリソースとして扱われ、異なる取引が並行して実行できるため、スループットと効率が向上します。しかし、この方法のトレードオフは、オブジェクトモデルの複雑性とロールバックメカニズムのオーバーヘッドです。取引間に競合が発生した場合、一部の状態をロールバックする必要があり、システムに負担をかけ、並行処理の効率に影響を与える可能性があります。非EVM並行システム(例えばソラナ)に比べて、Suiは高効率の並行性を維持するためにより多くの計算およびストレージリソースを必要とします。

モナド

Suiと同様に、Monadも楽観的並行を採用しています。しかし、Monadの楽観的並行は具体的な取引実行前に、依存関係のある取引に対して予測を行います。この予測は主にMonadの静的コード分析器を通じて行われます。予測には状態へのアクセスが必要であり、イーサリアムデータベースにおける状態の保存方法により、状態へのアクセスは非常に困難です。並行処理の際の状態読み取りプロセスをより効率的にするために、Monadはデータベースを再構築しました。

Monad状態ツリーはパーティションに分割され、各パーティションは独自の状態サブツリーを管理します。更新の際は、関連するシャードのみを変更すればよく、全体の状態ツリーを再構築する必要はありません。状態インデックステーブルを通じて迅速にパーティション内の状態を特定し、パーティション間の相互作用を減らします。

并行区块链全解:执行原理、代表项目及所处周期

小結

並行の核心は、多パス実行の方式で実行レイヤーの実行効率を向上させることであり、多パス実行を実現するために、チェーンは衝突検出やロールバックメカニズムなどの一連の処理を行い、最終的な状態の一貫性に影響を与えずに並行実行を保証し、データベースに一定の改良を加える必要があります。

もちろん、実行レイヤーの効率向上は並行処理のみに限らず、実行段階の最適化は、取引がデータベースに必要な読み書き操作を減らすことでも達成できます。そして、全体のチェーン速度の向上には、合意層の効率向上も含まれます。

各技術の背後には特有の制限条件があります。並行処理は効率を向上させる方法の一つに過ぎず、技術を使用するかどうかの最終的な決定は、開発者にとって友好的かどうか、非中央集権的な方法で達成できるかどうかなどを考慮する必要があります。技術のスタックが多ければ良いというわけではなく、少なくともイーサリアムにとっては、並行処理はそれほど魅力的ではありません。効率を向上させる観点から考えると、並行処理を追加することはイーサリアムにとって最適解ではありません。シンプルさを考慮しても、イーサリアムの現在のロールアップ中心のロードマップに照らしてもそうです。