はじめに
2024年、Solanaは飛躍的な成長を遂げました。その預かり資産残高(TVL)は年初の10億米ドルから50億米ドル近くまで急増し、第4位のパブリックブロックチェーンとなりました。
イーサリアムと比較して、Solanaはより高速かつ低コストで、優れたユーザー体験を提供します。Proof of History(歴史の証明)に基づくコンセンサスメカニズムと非同期のトランザクション処理モデルにより、開発者に高いトランザクションスループットと低レイテンシを実現しており、あらゆる種類の分散型アプリケーションにとって選ばれるプラットフォームとなっています。
BlockSecは、**「Solana Simplified(Solanaをわかりやすく)」**シリーズを計画しました。このシリーズでは、Solanaの基本概念から、Solanaのトランザクションを分析するための実践ガイド、そしてSolanaスマートコントラクトを記述するためのチュートリアルまでを網羅します。
本シリーズの第1弾として、本稿ではSolanaネットワーク内の主要な概念(動作メカニズム、アカウントモデル、トランザクションなど)を深く掘り下げ、Solanaで正確かつ効率的なスマートコントラクトを記述するための基礎を固めます。
eBPF:Solanaトランザクション実行の要
スマートコントラクトを記述および実行するために、ブロックチェーンは通常、プログラミング言語とチューリング完全な計算環境を必要とします。
イーサリアム上のスマートコントラクトは、通常Solidityと呼ばれるハイレベル言語で記述されます。コンパイラはそれをバイトコードに変換し、イーサリアム仮想マシン(EVM)で実行します。Solanaは、全く新しい仮想環境や言語を開発する代わりに、既存の高度な技術を完全に活用することを選択しました。元々Linuxカーネルの機能を拡張するために設計されたeBPF(extended Berkeley Packet Filter)仮想マシンが、Solanaの基礎的な実行環境として採用されています。
では、eBPFはEVMと比べてどのような利点があるのでしょうか?
解釈実行に限定されるEVMとは異なり、eBPFはJust-In-Time(JIT)コンパイルをサポートしており、バイトコードをプロセッサが直接実行可能なマシン命令に変換できます。このような機能により、プログラムの効率が大幅に向上します。
さらに、eBPFは効率的な命令セットと成熟したインフラストラクチャを備えています。開発者はRust言語を使用してスマートコントラクトを作成できます。LLVMコンパイラフレームワークのeBPFバックエンドを活用することで、Rustプログラムを直接eBPFバイトコードにコンパイルすることが可能です。
Solanaのアカウントモデル
Solanaアカウントの構造
Solanaではデータはアカウントの形式で保存されます。下図に示すように、Solana内のすべてのデータは巨大なキーバリューストア(データベース)と考えることができます。このデータベースのキーはアカウントアドレスです。「ウォレット」アカウント(ユーザーが公開鍵・秘密鍵のペアで直接制御するアカウント)の場合、これらのアドレスはEd25519署名システムを使用して生成された公開鍵です。データベースの値は、残高やその他の関連情報を含む各アカウントの詳細で構成されています。
Solanaは、アカウントを説明するためにAccountInfo(アカウント情報)という以下の構造を使用します。
各アカウントのAccountInfoには4つのフィールドが含まれています。それぞれの説明は以下の通りです。
-
Dataフィールド:このフィールドにはアカウントに関連するデータが保存されます。アカウントがプログラム(つまりスマートコントラクト)である場合、そこにはeBPFバイトコードが格納されます。それ以外の場合、データの形式は通常、アカウント作成者によって定義されます。 -
Executableフィールド:このフィールドは、そのアカウントがプログラムであるかどうかを示します。重要な点として、イーサリアムとは異なり、Solanaのプログラムは更新可能です。 -
Lamportsフィールド:このフィールドは、アカウント内のSolanaトークンの残高を記録します。Lamportは、実際にはSOLトークンの最小単位です(1 SOL = 10億 Lamports)。 -
Ownerフィールド:このフィールドはアカウントの所有者を示します。Solanaでは、すべてのアカウントに「所有者(Owner)」が存在します。例えば、すべての「ウォレット」アカウントの所有者は、ネットワーク上のアカウント作成などの機能を担う特別なアカウントであるSystem Programです。アカウントデータや残高からのLamport減額を変更できるのは所有者のみです(ただし、誰でもアカウントに資金を転送することで残高を増やすことは可能です)。
定義済みSolanaアカウント
Solanaには、固定アドレスでデプロイされたNative Programs(ネイティブプログラム)として知られる一連の定義済み実行可能プログラムがあります。Solanaネットワークがアップグレードされるにつれ、これらの定義済みプログラムも更新される可能性があります。これらはAPIやライブラリ関数として機能し、Solanaネットワーク内で特定の機能を提供します。
ネイティブプログラムの中で、開発者が頻繁に対話するのがSystem Programです。System Programは、それぞれが独立したタスクを実行する一連の指示を開発者に提供します。例えば、開発者はCreateAccount命令を使用して新しいアカウントを作成したり、Transfer命令を使用して他のアカウントにLamportを転送したりできます。
もう一つの一般的なネイティブプログラムはBPF Loaderプログラムです。これは他のすべてのプログラムアカウントの所有者であり、カスタムプログラムのデプロイ、更新、実行を担当します。「ウォレット」アカウントがデプロイしたプログラムを更新する必要がある場合、それは実際にはBPF Loaderプログラムへの委任によって行われます。なぜなら、プログラムのデータの変更権限は所有者のみが直接持っているからです。
ネイティブプログラムに加えて、SolanaはSysvars(システム変数)として知られる一連のアカウントを提供しています。これらのアカウントは、現在の時間や最新のブロックハッシュなど、Solanaネットワークの現在の状態に関連する情報やグローバル変数をSolana上のプログラムに提供します。
アカウントのレント(保管料)
Solanaブロックチェーンでは、各アカウントは「レント(Rent)」と呼ばれる最低残高を保持する必要があります。現実の家賃とは異なり、Solana上のレントは返還可能です。アカウント内のデータがチェーン上で利用可能であることを保証するために、アカウントは適切な量のLamportを保持しなければなりません。レントの額は、そのアカウントが占有するデータのサイズに関連しています。
アカウント残高をレントの額未満に減らそうとするトランザクションは、残高を完全にゼロにする場合を除き、失敗します。残高がゼロになった場合、そのアカウントのレントが回収されたことを意味し、トランザクション終了時にSolanaのガベージコレクションプロセスによって、該当するアカウントデータが削除されます。
🧐 Solana ScanでSolanaアカウントを確認する
前述の概念をより深く理解するために、Solanaの"Hello World"プロジェクトを使用してプログラムアカウントをデプロイしました。これは、SolanaのブロックチェーンエクスプローラーであるSolscanを使用して確認できます。

上の画像に示すように、まずこのアカウントが「Program」とラベル付けされていることがわかります。このアカウントのレントとして送信者の残高から一定のLamportが差し引かれたため、SOL Balanceフィールドは空ではありません。さらに、作成したアカウントはプログラムであるため、そのExecutableフィールドは「Yes」に設定されています。
Executable DataフィールドがeBPFプログラムそのものではなく、アドレスを格納していることに困惑するかもしれません。前述の通り、Solanaはプログラムの更新を許可しており、これは実際には「プロキシ」パターンを介して実装されています。プログラムアカウントの直接的な変更は許可されていないため、SolanaはeBPFプログラムを格納するための別のデータアカウントを作成し、プログラムアカウント内のDataフィールドにはこのデータアカウントのアドレスのみを格納します。
プログラムの更新が必要な場合は、データアカウントのDataフィールドのみを修正すれば済みます。Solscanを使用してデータアカウントを表示すると、「Program Executable Data Account」とマークされており、そのDataフィールドに実際のプログラムが格納されていることがわかります。

「More Info」セクションのOwnerフィールドはBPF Loaderとなっており、先述の説明と一致します。
「Overview」の最後のフィールドにUpgrade Authorityという項目が表示され、AccountInfoには存在しないことに気づくかもしれません。これは何を意味するのでしょうか?
前述の通り、「ウォレット」アカウントはプログラムの更新をBPF Loaderに委任します。更新前に、BPF Loaderは委任者がプログラムを最初にデプロイしたアカウントであるかどうかを検証します。プログラムアカウントの所有者はすでにBPF Loaderに設定されているため、この情報を保持する余地はありません。そのため、SolanaはそれをデータアカウントのDataフィールドに格納します。これが「Overview」にUpgrade Authorityフィールドが存在する理由であり、これは実際にはプログラムをデプロイしたウォレットアドレスです。
下の画像は、プログラムアカウントとデータアカウントの関係を示しています。データアカウントのDataフィールドには、ウォレットアドレスとeBPFコードの両方が含まれていることに注意してください。

Solanaにおけるトランザクションと命令(Instructions)
Solanaでは、ユーザーはトランザクションを発行することでプログラムを実行します。Solanaのユニークな点は、これらのトランザクションを並列に実行できる能力であり、これが超高速なトランザクション速度を支える鍵となっています。それでは、Solanaのトランザクションがどのように設計されているか詳しく見ていきましょう。
Solanaのトランザクションは、署名とメッセージで構成されています。1つのトランザクションには複数の署名を含めることができます。トランザクションのメッセージは、下図に示すように4つのパーツで構成されています。

HeaderとCompact Array of Account Addresses(アカウントアドレスのコンパクト配列)は、トランザクションに関与するすべてのアカウントと、トランザクション実行中のその特性(署名者であるかどうか、実行中に書き込み可能かどうかなど)を指定します。この情報により、Solanaは署名者アカウントが提供する署名を検証し、同じ状態に書き込むアカウントが含まれていない限り、トランザクションを並列で実行できます。
Recent Blockhashは、トランザクションのタイムスタンプとして機能します。トランザクションのブロックハッシュが最新のブロックハッシュよりも150ブロック以上古い場合、期限切れとみなされ、実行されません。
Compact Array of Instructions(命令のコンパクト配列)は、トランザクションの中で最も重要な部分であり、1つ以上の命令を含みます。命令は本質的に、プログラムアカウントによって提供されるルーチンの実行をトリガーします。各命令は、下図に示す3つのフィールドで構成されています。

最初のフィールドであるProgram ID Indexは、命令の受信者を指定します。これはその命令を処理する必要があるオンチェーンプログラムです。32バイトのアドレスを格納する代わりに、このアドレスはトランザクションメッセージの「アカウントアドレスのコンパクト配列」内に配置され、このフィールドには配列内のそのアドレスを指すu8インデックスのみが格納されます。
最初のフィールドと同様に、2番目のフィールドにはアカウントアドレスインデックスが格納され、Compact Array of Account Address Indexesと呼ばれます。この配列は、その命令に関与するすべてのアカウントを指定します。
最後のフィールドは、プログラムが命令を処理するために必要な追加データ(関数引数など)を含むバイト配列です。
Solanaはトランザクション内のすべての命令を順次処理し、そのトランザクションがアトミックに実行されることを保証することに注意してください。これは、すべての命令が正常に処理され完了するか、あるいはすべてが失敗するか、どちらかであることを意味します。一部の命令のみが処理され、他が処理されないという状況は発生しません。
🧐 Solana ScanでSolanaトランザクションを確認する
別のSolanaエクスプローラーを使用して、先ほどプログラムアカウントを作成したトランザクションを見てみましょう。Overviewセクションでは、Solanaのトランザクション署名、最近のブロックハッシュ、その他の情報を確認できます。

Account Inputセクションには、現在処理中のトランザクションに関与するすべてのアカウントと、そのトランザクション内での特性がリストされています。送信者とプログラムアカウントのアドレスに加え、2つのNative ProgramsとSysvarアカウントも含まれていることがわかります。

これは単純なプログラム作成トランザクションであるため、命令は2つしか含まれていません。最初の命令の受信者はSystem Programであり、プログラムアカウントの作成を担当します。2番目の命令の受信者はBPF Loaderであり、デプロイされたeBPFコードを保存するためのデータアカウントを作成し、そのアドレスをプログラムアカウントのDataフィールドに書き込みます。

結論
Solana上のスマートコントラクトはRustで開発され、eBPF仮想マシン上で実行されます。Solanaはアカウントモデルに従っており、データが削除されないようにオンチェーンアカウントは十分なレントを保持する必要があります。トランザクションは、必要なすべてのアカウントを定義する1つ以上の命令で構成され、これにより並列処理が可能となり、スループットの向上と応答レイテンシの削減を実現しています。これらの特徴が相まってSolanaの飛躍的な発展に寄与しており、最も選ばれるブロックチェーンの一つとなっています。



