ブロックチェーンはその非中央集権的な設計のために効率を犠牲にしているため、実行速度の向上は常に解決が急がれる問題の一つです。ブロックチェーンの「実行レイヤー」は、各トランザクションを処理し、それをチェーンに追加する重要な部分です。処理能力を加速するために、実行レイヤーの向上が核心戦略の一つとなっており、並行実行はこの方面での重要な突破口です。
従来のブロックチェーンは通常、トランザクションを逐次的に処理するため、トランザクション速度が大きく制限され、特にトランザクションが密集しているネットワークでは混雑を引き起こします。しかし、並行実行を通じて、複数のトランザクションを同時に処理できるため、実行効率を大幅に向上させ、チェーン上の圧力を軽減します。
並行についてより良く理解するために、まず実行から始め、Merge後のPBSモデルのイーサリアムを例にして実行が何であるかを説明し、実行がトランザクションライフサイクル全体の中でどのような位置にあるかを示します。
トランザクション実行の具体的な段階
トランザクションがメモリプールに入り、フィルタリングおよびソートされる:これはトランザクションが提出された後の前処理段階であり、Mempool、Searcher、Builderの相互作用を含み、トランザクションのフィルタリングおよびソートを完了します。
Builderがブロックを構築(ただし実行しない):Builderは利益のあるトランザクションをブロックに配置し、トランザクションのパッケージ化とソートを完了します。
Proposerがブロックを検証して提出する:ブロックが構築された後、Builderはブロックの提案をProposerに送信します。Proposerはブロックの構造とトランザクションの内容を検証し、正式にブロックをネットワークに提出して実行を開始します。
トランザクションを実行する:ブロックが提出された後、ノードはブロック内のトランザクションを1つずつ実行します。これは状態更新の重要な段階であり、各トランザクションはスマートコントラクトの呼び出し、アカウント残高の変化、または状態の変更を引き起こします。
証人の証明:検証者はブロックの実行結果と状態根を証明し、それを最終確認として記録します。これにより、ブロックの実行レイヤーにおける信頼性と有効性が保証され、不整合を防ぎます。
状態の同期:各ノードはブロックの実行結果(アカウント残高、契約状態の更新など)を自分のローカル状態に同期し、各トランザクションを実行した後、ノードは新しい状態根を計算し保存します。次のブロックにおける初期状態として使用されます。
もちろん、これはブロック単位でのトランザクションの状態同期に過ぎません。最新のチェーン上の状態を維持するために、通常、ノードは各ブロックのデータを逐次同期し、ブロックと状態を継続的に検証します。しかし、POSメカニズムでの最終性を達成するためには、集約者が各Slot内の証人の署名を集約して完全な署名を生成し、それを次のSlotの提案者に渡す必要があります。そして、検証者はエポックを経た後、投票数に基づいてそのエポック内のすべてのブロックの状態を確認し、一時的な合意状態のチェックポイントを形成します。連続して2つのエポックが多数の検証者の証人の支持を得た後、ブロックとトランザクションのみが最終性を達成します。
トランザクションの全ライフサイクルを通じて、実行はProposerがBuilderから送信されたブロックの構造とトランザクション内容を検証した後に発生します。実際の実行プロセスでは、トランザクションを1つずつ処理し、対応するアカウントまたは契約状態を更新する必要があります。すべてのトランザクションが実行された後、Proposerは新しい状態根(マーケルルート)を計算します。これは現在のブロックにおけるすべてのトランザクションの実行結果と最終的なグローバル状態の要約です。簡単に言えば、完全なブロック実行プロセスは、イーサリアムが前の状態から次の状態に変わる過程で必要な計算の一連のプロセスを含み、各トランザクションの実行からマーケルルートの計算までを含みます。
順次実行
並行の対極は順次実行であり、現在のブロックチェーンで一般的な実行方式です。通常、トランザクションは順番に逐次実行されます。1つのトランザクションが実行を完了すると、イーサリアムはアカウント状態および関連情報(残高、契約ストレージデータなど)をアカウント状態ツリーに更新し、新しいアカウント状態ハッシュが生成されます。すべてのアカウント状態ツリーが更新された後、状態マーケルルートと呼ばれる状態ツリーのルートノードハッシュが形成されます。状態マーケルルート、トランザクションマーケルルート、及びレシートマーケルルートが完成した後、ブロックヘッダーはハッシュ計算を行い、そのブロックのブロックハッシュを生成します。
その中で、トランザクションの実行順序は非常に重要です。マーケルツリーはハッシュ値の二分木であるため、異なる順序で形成されるマーケルルート値は異なります。
並行実行
並行実行の環境では、ノードはブロック内のトランザクションを並行して処理しようとします。トランザクションを順番に1つずつ実行するのではなく、トランザクションを異なる「実行パス」に割り当て、それらが同時に実行できるようにします。並行実行により、システムはブロック内のトランザクションをより効率的に処理し、スループットを向上させることができます。
すべてのトランザクションの実行が完了した後、ノードは実行結果(トランザクションによる状態更新)を集約し、新しいブロック状態を形成します。この状態はブロックチェーンに追加され、チェーン上の最新のグローバル状態を表します。
状態の競合
並行して異なるパスでトランザクションを同時に処理するため、並行の大きな難点は状態競合です。つまり、同じ時間にブロックチェーン上の同じデータ(状態)に対して複数のトランザクションが読み取りまたは書き込みを行う可能性があります。この状況を適切に処理しないと、実行結果が不確定になります。状態の更新順序が異なるため、最終的な計算結果も異なります。例えば、
仮にトランザクションAとトランザクションBの2つがあり、両方とも同じアカウントの残高を更新しようとしています:
トランザクションA:アカウント残高を10増加させる。
トランザクションB:アカウント残高を20増加させる。
アカウントの初期残高は100です。
もし私たちが直列に実行すれば、実行順序の結果は確定的です:
1. トランザクションAを先に実行し、その後トランザクションBを実行する:
アカウント残高が先に10増加し、110になります。
再増加20、最終的に130になります。
2. トランザクションBを先に実行し、その後トランザクションAを実行する:
アカウント残高が先に20増加し、120になります。
さらに10増加し、最終的に130になります。
これらの2つの順序では、最終的な残高はどちらも130になります。なぜなら、システムがトランザクション実行の順序の一貫性を保証しているからです。
しかし、並行実行環境では、トランザクションAとトランザクションBが同時に初期残高100を読み取り、それぞれの計算を行う可能性があります:
トランザクションAは残高100を読み取り、計算後に残高を110に更新します。
トランザクションBも残高100を読み取り、計算後に残高を120に更新します。
この状況では、トランザクションが同時に実行されるため、最終的な残高は120にしか更新されません。なぜなら、トランザクションAとトランザクションBの操作が「上書き」してしまい、状態の競合が発生したからです。
この種の状態競合の問題は通常「データの上書き」と呼ばれます。つまり、トランザクションが同時に同じデータを変更しようとすると、互いに計算結果を上書きし、最終的な状態が正しくなくなる可能性があります。もう一つの状態競合が引き起こす可能性のある問題は、実行順序の保証ができないことです。複数のトランザクションが異なる時間に操作を完了するため、実行順序が異なる場合があり、異なる計算結果をもたらす可能性があります。
このような不確定性を避けるために、ブロックチェーンの並行実行システムは通常、衝突検出やロールバックメカニズムを導入したり、事前にトランザクションの依存性分析を行ったりして、最終的な状態の一貫性に影響を与えずに並行実行を確保します。
楽観的並行と確定的並行
状態競合の可能性がある問題に対処する方法は2つあります:確定的並行と楽観的並行です。これらの2つのモデルは、効率性と設計の複雑さにおいてそれぞれトレードオフがあります。
確定的並行は、状態アクセスを事前に宣言する必要があります。検証者またはシーケンサーは、トランザクションの順序付け時に宣言された状態アクセスをチェックします。同じ状態に書き込みを試みる複数のトランザクションがある場合、これらのトランザクションは競合としてマークされ、同時に実行されないようにします。異なるチェーンは、状態アクセスを事前に宣言する形式が異なりますが、一般的には以下のような方法が含まれます。
契約の規範により制約:開発者はスマートコントラクト内で直接状態アクセス範囲を規定します。例えば、ERC-20トークンの転送には送信者と受信者の残高フィールドへのアクセスが必要です。
構造化データの宣言によるトランザクションの実行:トランザクションに専用のフィールドを追加して状態アクセスをマークします。
コンパイラ分析を通じて:高級言語のコンパイラは契約コードを静的に分析し、状態アクセスコレクションを自動生成できます。
フレームワークによる強制宣言:特定のフレームワークは、開発者が関数を呼び出すときに明示的にアクセスが必要な状態を指定することを要求します。
楽観的並行は、トランザクションを先に楽観的に処理し、衝突が発生した場合には影響を受けたトランザクションを順次再実行します。衝突の発生をできるだけ避けるために、楽観的並行の設計の核心は、過去のデータ、静的分析などを通じて状態を迅速に予測し仮定することです。つまり、システムは完全に検証されていない状況で、特定の操作または状態の更新が有効であると仮定し、すべての検証プロセスを待つことなく性能とスループットを向上させようとします。
楽観的並行は、状態の迅速な予測と仮定を通じて、競合の発生をできるだけ回避しようとしますが、契約の実行やクロスチェーントランザクションが関与する場合、回避できない課題がいくつかあります。競合が頻繁に発生する場合、再実行はシステムパフォーマンスを著しく低下させ、計算リソースの消費を増加させることになります。
確定的並行は、トランザクション前に状態依存性チェックを行い、楽観的並行で発生する可能性のある競合を回避しますが、トランザクション提出前に状態依存性を正確に宣言する必要があるため、開発者に対してより高い要求を課し、実装の複雑さを増加させます。
EVM並行のジレンマ
状態競合の処理には、確定的および楽観的の区別だけでなく、並行を実現する具体的なプロセスにおいて、チェーンデータベースアーキテクチャの観点から考慮する必要があります。並行における状態競合の問題は、マーケルツリーアーキテクチャの下にあるEVMにおいて特に困難です。マーケルツリーは階層的なハッシュ構造であり、トランザクションが特定の状態データを変更するたびに、マーケルツリーのルートハッシュも更新する必要があります。この更新プロセスは再帰的であり、葉ノードから上に向かって階層的に計算され、ルートノードに至ります。ハッシュは不可逆的であるため、下層のデータ変更が完了しない限り上層を計算できず、この特性により並行更新が非常に困難になります。
もし2つのトランザクションが並行して実行され、同じ状態(アカウント残高など)にアクセスすると、マーケルツリーのノードの競合が発生します。この競合を解決するためには、通常、追加のトランザクション管理メカニズムが必要であり、複数のブランチで一貫したルートハッシュ値を確保する必要があります。これはEVMにとって簡単ではなく、並行化と状態の一貫性の間でトレードオフを行う必要があります。
非EVM並行解決策
Solana
イーサリアムのグローバル状態ツリーとは異なり、Solanaはアカウントモデルを採用しています。各アカウントは独立したストレージスペースであり、帳簿に格納されているため、パス競合の問題を回避しています。
Solanaは確定的並行です。Solanaでは、各トランザクションは提出時に明確にアクセスするアカウントと必要なアクセス権(読み取り専用または読み書き)を宣言する必要があります。この設計により、ブロックチェーンノードはトランザクション実行前に、各トランザクションがアクセスする必要のあるリソースを事前に分析できます。トランザクションが実行を開始する前にすべてのアカウント依存関係が明確にされているため、ノードはどのトランザクションが同じアカウントにアクセスするか、どのトランザクションが安全に並行実行できるかを判断でき、スマートスケジューリングを実現し、競合を避けることができます。
各トランザクションは実行前にアクセスが必要なアカウントと権限を宣言しているため、Solanaはトランザクション間にアカウント依存関係が存在するかをチェックできます(Sealevelモデル)。トランザクション間に共有の読み取りおよび書き込みアカウントがなければ、システムはそれらを異なるプロセッサに割り当てて並行実行できます。
Aptos
Aptosの並行実行設計はイーサリアムとは大きく異なり、アーキテクチャとメカニズムにおいていくつかの重要な革新が行われており、主にアカウントモデルと状態ストレージに反映されています。
イーサリアムはトランザクションを実行する際に、グローバル状態ツリー(MPT)を頻繁に更新する必要があります。すべてのアカウント、契約の状態は共有の状態ツリーに保存され、いかなるトランザクションもこの状態ツリーの一部にアクセスして更新する必要があります。一方、Aptosはアカウントを独立した状態単位に分け、各オブジェクトは独立したキーと値のペアとして存在し、互いに影響を与えず、明確な参照関係がある場合にのみ関連付けられます。オブジェクト間には公共のツリーパスがなく、ロック競合は発生せず、完全に並行して処理できます。
Aptosの底層データ構造はJellyfish Merkle Treeです。各オブジェクトの状態は最終的にJMTに保存され、独立したキーと値のペアとして扱われます。イーサリアムのMPTとは異なり、Jellyfish Merkle Treeは完全な二分木構造であり、この形式によりノードの保存パスとクエリパスが簡素化され、検証時間が大幅に短縮されます。また、各アカウントはツリー内で固定された位置を持ち、ツリー内のノードは独立して保存され、複数のアカウントの更新と検索が並行して行えるようになります。
Aptosは楽観的並行であり、事前に宣言されたすべてのアカウントの依存関係を提供する必要はありません。そのため、AptosはBlock-STMを使用し、Block-STMは事前に設定されたトランザクション順序を利用して依存関係を推定し、中止の回数を減らします。
並行EVM
非EVM並行と比較して、並行EVMは状態依存性、衝突検出、ガス管理、ロールバックメカニズムなどの問題を処理する際に、より大きな技術的難しさを抱えています。これをより良く理解するために、いくつかの並行EVMプロジェクト(Sui、Monad、Cantoなど)がこれらの問題をどのように解決しているかを参考にできます。
Sui
SuiはAptosと同様にオブジェクトモデルを使用して状態を処理し、各オブジェクト(アカウント、スマートコントラクトの状態など)を独立したリソースとして扱います。これらのオブジェクトはオブジェクトの一意の識別子によって区別されます。トランザクションが異なるオブジェクトに関与する場合、これらのトランザクションは並行して処理でき、異なる状態に対して操作を行うので、直接的な競合は発生しません。
Suiはオブジェクトモデルを使用して状態を管理していますが、EVMとの互換性を保つために、Suiのアーキテクチャは追加の適応レイヤーや抽象メカニズムを通じてオブジェクトモデルとEVMのアカウントモデルを橋渡ししています。
Suiでは、トランザクションのスケジューリングに楽観的並行戦略を使用し、トランザクション間に競合がないと仮定します。競合が発生した場合、システムはロールバックメカニズムを使用して状態を復元します。
Suiはオブジェクトモデルと状態隔離技術を使用しており、状態依存性の問題を効果的に回避できます。各オブジェクトは独立したリソースとして機能し、異なるトランザクションは並行して実行できるため、スループットと効率が向上します。しかし、この方法のトレードオフはオブジェクトモデルの複雑さとロールバックメカニズムのオーバーヘッドです。トランザクション間で競合が発生した場合、部分的な状態をロールバックする必要があり、これによりシステムに負担がかかり、並行処理の効率に影響を与える可能性があります。非EVM並行システム(Solanaなど)と比較して、Suiは効率的な並行性を維持するために、より多くの計算およびストレージリソースを必要とします。
Monad
Suiと同様に、Monadも楽観的並行を採用しています。しかし、Monadの楽観的並行は具体的なトランザクション実行の前に、依存関係のあるトランザクションに対して予測を行います。予測は主にMonadの静的コード分析器を通じて行われます。予測は状態にアクセスする必要があり、イーサリアムデータベースにおける状態の保存方法により、状態へのアクセスが非常に困難になります。状態読み取りのプロセスでの並行性をより効率的にするために、Monadはデータベースを再構築しました。
Monadの状態ツリーはパーティションによって分割され、各パーティションは独自の状態サブツリーを維持します。更新時には関連するシャードのみを変更すればよく、全体の状態ツリーを再構築する必要はありません。状態インデックステーブルを通じてパーティション内の状態を迅速に特定し、パーティション間の相互作用を減少させます。
小結
並行の核心は、多経路実行の方法で実行レイヤーの実行効率を向上させることであり、多経路実行を実現するためには、チェーンは衝突検出やロールバックメカニズムなどの一連の処理を行い、最終的な状態の一貫性に影響を与えずに並行実行を確保し、データベースに一定の改善を加える必要があります。
もちろん、実行レイヤーの効率の向上は並行だけに限定されず、実行段階の最適化は、トランザクションがデータベースに必要な読み取りおよび書き込み操作を減らすことによっても達成できます。また、チェーン全体の速度向上に関連する範囲はさらに広く、合意レイヤーの効率向上も含まれます。
各技術の背後には特定の制限条件があります。並行は効率を向上させる方法の一つに過ぎず、最終的にこの技術を使用するかどうかは、開発者にとっての使いやすさや、非中央集権を損なうことなく実現できるかどうかを考慮する必要があります。技術のスタックは多ければ多いほど良いわけではなく、少なくともイーサリアムにとっては、並行はそれほど魅力的ではありません。効率を向上させる観点から見れば、並行を追加することはイーサリアムにとって最適解ではなく、簡潔性の観点やイーサリアムの現在のRollup中心のロードマップを考慮してもそうです。