Agave v2.2 アップデートについて知っておくべきこと
Alexander Meißnerさん、0xIchigoさん、そしてWill Hickeyさんには、本稿の初期バージョンをレビューしていただき、心より感謝いたします。
Agaveバリデータクライアント v2.2 のリリースは、Solanaがより強靭でマルチクライアントなエコシステムを目指していく中で、大きな節目となるアップデートです。今回のアップデートでは、ネットワークのパフォーマンスや開発者体験をさらに向上させるための重要な改善が盛り込まれています。
Agave 2.2 の主要な更新内容
- パフォーマンスの大幅な最適化
- ブロックごとのCompute Unit上限が50Mから60Mへ20%増加
- Accounts Lattice Hash(ALH)の導入
- Secp256r1署名の検証(Agave 2.1から延期)
- SBPF v1, v2, v3プログラムのデプロイと実行
- CPI 呼び出しに関する制限の緩和
- Loader-v4 の導入
- Greedy Scheduler がデフォルト化
この記事は各セクションごとに完結しているため、ご自分の興味のあるトピックだけを選んで読むことができます。バリデータ運用者、開発者、あるいは熱心なユーザーのいずれにとっても、このAgave 2.2に関する網羅的なガイドが、最新の改良点を最大限に活用するための重要な視点を提供することを願っています。
新機能のロールアウト
本記事の執筆時点で、全体のステークのうち約19.8%がAgave v2.2を利用しており、ネットワーク全体ですでに広く採用されていることが分かります。現在、メインネットでの新機能のアクティベーション(有効化)は、アップグレード期間中のため一時的に停止されていますが、予定されている有効化シーケンスに従ってまもなく再開される見込みです。
Agave 2.2で導入された主要機能の多くは、現時点ではまだメインネット上で有効化されていませんが、Solanaの機能ゲートシステムによって、2.2リリースサイクル中に徐々に有効化されていく予定です。アクティベーションのタイミングは、機能の優先度や Testnet, Devnet での有効化順序に基づいて決まります。最新状況については、Agaveの機能ゲートトラッカーを参照してください。
ブロック上限を 60M CUに引き上げ
Anzaは2025年にSolana上のブロックスペースを倍増させるという意欲的な目標を掲げています。その一環として、「SIMD-0256: ブロック上限を60Mに引き上げ」が、Solana 2.2リリースに含まれる予定です。これは、現在のブロック上限50M CUから20%の引き上げとなります。
以前のアップグレードでは、ブロック上限が48M CUから50M CUに引き上げられましたが、それ以降もブロックタイムは安定して目標としている400ミリ秒以下に保たれてきました。最近のデータでも、ブロックは50M CUの上限に頻繁に達しており、さらなるスケーリングへの準備が整っていることを示しています。
Heliusバリデータの直近のフルブロック(データソース: Firedancer ダッシュボード)
このアップデートでは、その他のプロトコル上の制限には変更がありません:
- 1アカウントあたりのブロック内上限は、12M CUのままです。
- トランザクション1件あたりの上限は、引き続き1.4M CUです。
- ブロック内の投票トランザクションの上限も36M CUです。
- ブロックあたりに新規で割り当て可能なアカウントデータの最大サイズも、100MBのままで据え置きです。
ブロック上限の引き上げは、ユーザー体験に直接影響します。ネットワークの処理能力が向上することで、平均的な取引手数料が下がり、混雑時でもトランザクションがブロックに取り込まれる確率が高くなります。
ブロック上限引き上げに伴う課題
スループットを高める際には、2つの大きな課題に対処する必要があるため、慎重かつ段階的なアプローチが適切です。
- インフラの準備
クライアントの最適化に合わせて、エコシステム全体のインフラも進化していく必要があります。特に注意すべきは、書き込みレイヤーが読み取りレイヤーの速度を上回ってしまうと、ユーザー体験が損なわれるという点です。ここでいう読み取りレイヤーとは、たとえばRPCノード、インデクサー、アーカイブサービスなどを指しています。
- ブロックの迅速な伝播
リーダーノードから他のノード群へブロックを配信する処理がもう一つのボトルネックになります。ブロックが大きくなりすぎると、その配信にかかる時間が、特に高負荷の条件下において、目標の400ミリ秒を超えてしまう可能性があります。
現在、特に深刻なのは、TVU(トランザクション検証ユニット)の再送ステージです。このステージでは、クラスタ全体にシュレッド(分割データ)を配信しています。Turbineのファンアウト係数が200と非常に高い(つまり各ノードが他の200個のノードにシュレッドを中継する)ため、大規模なバリデータでは秒間15万パケット近くのアウトバウンド通信が発生しています。これを非効率に処理すると、シュレッドの伝達が停滞し、リーダー交代の乱れやネットワーク全体での状態の不一致といった問題を引き起こしかねません。
この課題に対処するため、Anzaのエンジニアたちは現在、ブロック配信パイプラインの全面的な改良、具体的には、カーネルをバイパスしてユーザー空間から直接ネットワークパケットを処理できるXDP (eXpress Data Path) への移行を進めています。これにより、中間コピーのコストを排除し、バリデータソフトウェアがNIC(ネットワークインターフェースカード)と直接データをやり取りできるようになります。
ユーザー空間/システムコールとカーネル (Run your program in the kernel space with eBPFより)
Accounts Lattice Hash (ALH)
Solanaが数十億のアカウントを扱えるようにするためには、グローバルなアカウント状態のハッシュを、よりスケーラブルに処理できる方法が必要です。新たに導入された Accounts Lattice Hash (ALH) では、これまでのMerkleツリーベースのアカウントハッシュ方式に代わり、準同型ハッシュに基づいた、より効率的で拡張性の高いアプローチを採用することで、すべてを一から再計算することなく、既存のハッシュから新しいハッシュを構築できるようになります。
現在Solanaでは、次の2種類のアカウント状態ハッシュが使われています:
- Epoch Accounts Hash(EAH):エポックごとに1回計算される、すべてのアカウント状態を含むMerkleルート。
- Accounts Delta Hash(ADH):1ブロック内で変更されたアカウントだけを対象に毎ブロック再計算されるMerkleルート。
これらはいずれも、アカウントをパブリックキー順に並べてMerkleツリーを構築する必要があるため、アカウント数が増えるとともにパフォーマンスやスケーラビリティの課題が顕在化します。そのため、EAHとADHの二重ハッシュ構成という折衷案が使われていました。EAHは正確ではあるものの頻度が少なく、ADHは頻度は高いものの一部の変更されたアカウントしか反映しません。Merkleツリーの再構築というオーバーヘッドなしで、すべてのアカウント状態を毎ブロック完全かつ最新の形でハッシュ化できれば理想的です。
Accounts Lattice Hashの仕組み
ALHは、この課題を解決します。ALHは準同型ハッシュ関数を使用して、アカウントごとの変更を加減算するだけで、ハッシュ全体を更新できるという特徴があります。毎回Merkleツリーを作り直すのではなく、変更のあったアカウントのハッシュ(LtHashes)だけを足したり引いたりすることで、最終的なハッシュを更新していきます。その結果として得られる2048バイトの単一ハッシュは、全アカウントの状態を集約したものです。しかも、このハッシュは、毎ブロック更新可能でありながら、一から再計算する負荷が削減されています。
この手法は計算量が O(n) であり、Merkleツリーの O(n log n) に比べて大きく改善されています。そのため、毎ブロック全アカウントの状態をハッシュとして含めることができ、再計算による高いコストも不要になります。
O(n)とO(n log n)の増加率の違い (ソース: Stack Overflow)統合と旧ハッシュ方式の廃止
この機能は、Agave 2.2 のリリースサイクル全体を通じて、3つのSIMDに対応する機能ゲートを段階的に有効化する形でロールアウトされます。
- SIMD-0215: Accounts Lattice Hash
- SIMD-0220: Snapshots use Accounts Lattice Hash
- SIMD-0223: Removes Accounts Delta Hash
これらの変更により、Accounts Lattice Hash(ALH)が、従来のADHとEAHの両方に取って代わることになります。今後は各ブロックのバンクハッシュにALHが組み込まれ、エポック単位ではなく、ブロック単位で全アカウント状態のハッシュが可能になります。
パフォーマンスとトレードオフ
ALHへの切り替えによって、ブロックごとにMerkleベースのハッシュ処理を行う必要がなくなるため、バリデータのパフォーマンスは大きく向上します。これにより、コンセンサス処理やスナップショット生成が効率化されます。たとえば、ブロックのファイナライズ時やスナップショット作成時に、アカウントをソートしたりツリーを再構築したりする必要がなくなり、ALHの更新はシンプルな加算処理だけで済みます。
ただし、ALHはMerkleやVerkleツリーのような「含まれていること/含まれていないことの証明(inclusion/exclusion proof)」には対応していません。これにより、ライトクライアントやSimple Payment Verification(SPV)など、一部の暗号学的な検証用途には制約が生じます。しかし、この点は、得られる大きな効率向上を考慮すれば、十分に妥当なトレードオフとされています。代替の証明手法についての議論はこちらをご参照ください。
スナップショットとの統合
初回のAccounts Lattice Hashの計算には時間がかかるため、バリデータのスナップショットにはALHの値が保存され、起動時にそれが存在すれば復元されるようになりました。これにより、今後のスナップショットは、従来のMerkleベースのハッシュではなく、ALHフォーマットを反映した形となり、Solanaのストレージおよびハッシュモデルがこの新しい設計により一層統一されていきます。
ネイティブ Secp256r1 署名検証の対応
secp256r1 楕円曲線による署名検証をネイティブでサポートする機能が追加されました。これは、Passkeys や WebAuthn 標準、さらには2要素認証(2FA)などを含む高度なアカウント抽象化モデルとのオンチェーン互換性を実現する重要なアップグレードです。
この機能によって、Web2ではすでに一般的となっているパスワード不要の認証(パスワードレス認証)を、Web3の世界に導入することができ、オンチェーンアプリケーションのセキュリティとユーザビリティの両面が強化されます。
当初はAgave 2.1での導入が予定されていましたが、実装は2.2に延期されました。詳細については、Agave 2.1アップデートでのSecp256r1署名検証の解説をご覧ください。
プリコンパイル命令のアラインメントバグ
Agave 2.2の初期リリース直後に、Secp256r1およびEd25519のプリコンパイルプログラムの実装に重大なバグが発見されました。このバグは、新しく導入された--transaction-structureビューフラグを有効にしたときに発生します。このフラグはデータのアラインメントを保証せずに生のトランザクション構造を表示するものです。プリコンパイル側では、命令データが2バイト境界に整列されている前提で処理を行っていたため、ブロック生成者とバリデータの間で実行結果が不一致となり、バンクハッシュの不整合が発生しました。これによりリーダーがブロック生成を中断し、一時的にネットワークの可用性が損なわれる事態となりました。この問題は、4月9日に最初の報告があり、4月11日には修正パッチが適用されました。なお、このバグによるユーザー資産への影響は一切ありません。詳細は、後日公開されたRoot Cause Analysisにて確認できます。
SBPFv1, v2, v3 プログラムのデプロイと実行
Solana Berkeley Packet Filter(SBPF)は、Solanaのプログラムを効率的かつ安全に実行するために設計されたカスタム仮想マシンです。これは、Linux向けに開発されたRustベースの拡張BPF (eBPF) がフォークされたものです。
SolanaにおけるSBPFのアップグレードは、パフォーマンスの向上、セキュリティの強化、開発者が利用できる新機能の拡充に不可欠です。Agave 2.2では、SIMD-0161で初めて提案されたSBPF仮想マシンのバージョン管理の仕組みを導入することで、将来的な保守性と性能改善の基盤を構築します。これにより、SBPFv1 (SIMD-0166), SBPFv2 (SIMD-0173, SIMD-0174), SBPFv3 (SIMD-0178, SIMD-0179, SIMD-0189) のプログラムが、ネットワーク全体を巻き込むような再デプロイをすることなく、それぞれ独立してデプロイ・実行できるようになり、段階的な進化が可能になりました。
これまでSBPFのアップグレードは、グローバルな機能ゲート経由でしか導入できず、ランタイムの進化が非常に煩雑で、デプロイの調整も極めて困難でした。
今回のバージョニングの導入により、プログラムの挙動をグローバルランタイムから分離し、プログラムごとにバージョンタグを持たせることが可能になりました。このタグは、ELFファイル(実行ファイル)のヘッダー内にある e_flagsフィールドにエンコードされます。この仕組みによって、以下のような改善が実現されます:
- 明確なランタイム動作:各プログラムがどの命令セット(=どのSBPFバージョン)で動作するかを明示でき、それに応じてランタイムが動作を切り替える。
- 制御されたロールアウト:新しいSBPFバージョンは対応する機能ゲートごとに個別に有効化され、古いバージョンを時間をかけて段階的に廃止することができる。
- 計画的な非推奨化:古いバージョンのサポートを将来的に打ち切ることが可能となり、VMのロジックがシンプルになる。廃止までに十分な猶予期間が確保されるため、開発者は安心して移行作業を進められる。
バージョン識別子
現在は、 e_flagsが 0x0020以外であればSBPF v0として扱われるという仕様になっていますが、この方法では将来的な複数バージョンサポートに対応できません。
新しいSBPFバージョンを有効にする最初の機能ゲートがアクティベートされると、プロトコルの e_flags解釈方式が切り替わり、値をそのままSBPFバージョン番号にマッピングするようになります。たとえば 0x0000 → SBPF v0, 0x0001 → SBPF v1, 0x0002 → SBPF v2, …といった形で明確にバージョンを区別します。
CPI呼び出しの制限緩和
Agave 2.2 では、長らく待たれていたCPI(Cross-Program Invocation)の制限解除がついに実現されました。これまでSolanaでは、CPI経由で呼び出されるすべてのプログラムアカウントを、呼び出し元が明示的に指定する必要がありました。この制約により、トランザクションの構築が煩雑になり、特にCPIが多層にネストされた場合には計算コストも大きく増加していました。しかし、こうした制限は歴史的な事情によるものであり、現在のプロトコルには本来不要です。
SIMD-0163 により、CPIで呼び出されるプログラムアカウントは、トランザクションのトップレベルアカウントリストから直接参照できるようになります。これにより、毎回すべての呼び出し階層でプログラムアカウントを再帰的に渡す必要がなくなり、以下のような利点が得られます:
- プログラムアカウント(特に約10MBもの大きなバイナリを持つもの)を、毎回シリアライズ/デシリアライズするコストを削減することでCUの大幅な削減(loader-v3のように実行ファイルが別アカウントにある場合は除きます)。
- 呼び出されるプログラムのアカウントをスタック経由で明示的に渡す必要がなくなり、トランザクションの構築がシンプルになります。
- モジュール性が高く、深くネストされたプログラムアーキテクチャの構築がより簡単になり、コンポーザビリティーが向上します。
後方互換性について
これらの変更は既存のプログラムには影響しませんが、もし、変更の恩恵を受けたい場合には、以下のいずれかの対応が必要になります:
呼び出すプログラムを静的に決め打ちしている場合、以前はランタイムの制約を満たすために実際には使わないダミーのプログラムアカウントを渡していたケースがあります。そうした場合には、 NativeLoader1111111111111111111111111111111
のようなプレースホルダーを挿入すれば、他のインストラクションアカウントのインデックスをずらすことなく対応可能です。
呼び出すプログラムを動的に特定のアカウントから取得しているようなプログラムは、この最適化を活用するにはコードの修正と再デプロイが必要です。
この最適化は安全性を損なうことなく導入されています。プログラムが同一トランザクション内で新規に追加・変更・削除されたアカウントを呼び出すことはできないように、「可視性の遅延(delay visibility)」という仕組みで保護されています。
Loader-v4
Agave 2.2 では、Loader-v4 のサポートが導入されました。これは、従来の Loader-v3 に代わる、よりシンプルで柔軟なプログラムデプロイ手法です。SIMD-0167 によって有効化されており、プログラムアカウントの管理を簡素化し、アップグレードの運用を改善、さらに DeFiのような高リスク環境で稼働するプログラム向けに重要な安全機能も追加されています。
Loader-v4 の主な特徴
単一アカウントモデル - Loader-v4では、プロキシアカウントや外部のプログラムデータバッファが不要になり、1つのアカウントだけでプログラムを格納できます。
メンテナンスモード - プログラムを一時的に「実行不可」のメンテナンス状態に切り替えることが可能です。これにより、たとえば脆弱性が発見された際には、再デプロイやクローズは不要で、元のプログラムアドレスを破棄することなく、プログラムの実行を一時停止して対応に当たることができます。
サイズ変更の柔軟性 - プログラムバイナリの後からの拡張・縮小が可能になり、より柔軟なリソース割り当てや、ロックされたSOLの一部返還なども実現できます。
バッファアカウント利用の任意化 - Loader-v3では再デプロイ時に常に外部バッファアカウントが必要でしたが、Loader-v4ではバッファを使うかどうかが任意になりました。メインのプログラムアカウントに直接再デプロイできるため、アップロード時に必要なSOLのロック量を半分に削減できます。
部分的な再デプロイが可能 - Loader-v3ではアップグレード時に毎回全バイナリを再アップロードする必要がありましたが、Loader-v4では必要な部分だけをアップロードしてパッチ的に更新できます。これは特に軽微な修正の時に便利です。
Loader-v3からのスムーズな移行 - Loader-v3でデプロイされたプログラムは、プログラムアドレスを変更することなくLoader-v4に移行可能です。これにはLoader-v3で新たに追加された Migrate インストラクションを使用します。移行時には「可視性の遅延 (delay visibility)」が発生し、移行したスロット内では一時的にそのプログラムは使用不可になります。
Loader-v4が有効化された後は、Loader-v3への新規デプロイが無効化される機能ゲートが別途アクティブになります。既存のLoader-v3プログラムはそのまま動作しますが、今後の新規デプロイは原則Loader-v4が推奨されることになります。
Loader-v4プログラムをファイナライズする際には、将来のバージョンアドレスを「次のバージョン」としてヒント付きで指定することができ、プログラムを再デプロイせずにバージョンを連結リストのように扱うことが可能となります。これにより、ユーザーインターフェースは「どのファイナライズされたバージョンを使うか」という選択肢を提示できるようになります。
なお、権限管理やアカウントのクローズ処理の仕組みは、Loader-v4でも変化ありません。これらはすべて、新しく追加される program-v4 CLIサブコマンドを通じて利用できるようになります。
まとめ
Agave 2.2は、Solanaプロトコルにとって重要な節目となるリリースで、ネットワークの技術的な可能性を広げる、重要なランタイムの強化と新機能が数多く導入されています。
このリリースには、以下のような主要な改善が含まれます:
- Compute Unitのブロック容量の20%拡大
- スケーラブルな状態ハッシュを実現するAccounts Lattice Hash(ALH)の統合
- プログラム開発まわりの複数のアップグレード
- 一般的な暗号方式との統合に不可欠なSecp256r1署名検証のサポート
これらの改良により、ネットワークのスループットが向上し、開発者体験が改善され、より幅広い用途のアプリケーションを支える力が強化されます。
Solana上でプログラムを構築している方、バリデータを運用している方、あるいはネットワークとやり取りしているユーザーにとっても、Agave 2.2 はパフォーマンス、柔軟性、耐障害性のいずれも大きく前進させてくれるリリースといえます。
Discussion