コンテンツへスキップ
0%

Solana virtual machine(SVM)とは何か、どのように機能するのか

Usman Asim headshot

執筆者 Usman Asim

2025年12月18日 公開読了時間 6 分

仮想マシンを可視化した図

Solanaは、暗号資産業界で最も活発に利用されているブロックチェーンの一つとして台頭しており、デイリーアクティブアドレス数や取引量で常に上位5位以内にランクインしている。2025年10月だけでも、Solanaネットワークは約7,000万件の日次トランザクションを処理し、1,430億ドルのDEX取引量を記録した。ピーク時のスループットは、他の多くのチェーンなら停止に追い込まれるレベルだ。

これを可能にしているのは、単に高速なコンセンサスや強力なハードウェア要件だけではない。Solanaのパフォーマンスの核心には、根本的に異なる実行エンジンがある。それがSolana Virtual Machine(SVM)だ。

Solana Virtual Machineは、eBPFバイトコード上に構築されたレジスタベースのランタイムであり、すべての状態依存関係を事前に宣言させることでトランザクションを並列実行する。Ethereum Virtual Machineの逐次的なトランザクションモデルとは異なり、SVMは並列実行を前提として一から設計されており、CPUコア全体で数千ものトランザクションを一つずつではなく同時に処理する。

このアーキテクチャ上の選択は、あらゆる側面に波及する。プログラムがどのように状態を保存するか、トランザクションがどのように構築されるか、手数料がどのように計算されるか、そして開発者がオンチェーンアプリケーション構築をどう捉えるかにまで及ぶ。

本ガイドでは、SVMを基礎から解説する。まずコアコンポーネントから始め、それぞれがどのように機能するかを説明した上で、それらがどう組み合わさってSolanaのパフォーマンスを実現しているかを示す。読み終える頃には、SVMが_何を_行うかだけでなく、_なぜ_このように構築されているかが理解できるはずだ。

仮想マシンとは何か

SVMそのものに入る前に、仮想マシンとは実際には何なのか、そしてなぜブロックチェーンにそれが必要なのかを確認しておこう。

仮想マシン(VM)とは、あるコンピュータの中で動作するソフトウェアベースのコンピュータのことだ。ホストシステムのリソースに直接アクセスすることなくコードを実行できる、制御された環境を提供する。VMはあらゆる場所に存在する。Java Virtual MachineはJavaバイトコードを実行し、ブラウザはVM内でJavaScriptを実行し、クラウドサーバーもしばしばVM内で稼働している。

ブロックチェーンがVMを必要とする理由は明確だ。信頼できないコードを決定論的に実行するためである。スマートコントラクトをデプロイすると、世界中の数千のバリデータがそのコードを実行し、_全く同じ_結果に到達する必要がある。VMは以下を提供する。

  • サンドボックス化:信頼できないコードは、ホストシステムや他のプログラムのメモリにアクセスできない
  • 決定論性:同じ入力は常に同じ出力を生む。ハードウェアに依存しない
  • 計測:実行を計測・制限できる(ガス/compute units)ことで、悪用を防ぐ
  • 移植性:異なるマシンやOS間でコードが同一に動作する

**Ethereum Virtual Machine(EVM)**は、広く採用された最初のブロックチェーンVMだった。スタックベースのアーキテクチャを採用し、トランザクションを逐次実行し、コードと状態の両方をコントラクト内にまとめて保存する。これは機能するが、その設計上の選択が根本的なスループットの制約を生んでいる。

**Solana Virtual Machine(SVM)**は異なるアプローチを取る。レジスタベースのアーキテクチャ(物理的なCPUにより近い)を採用し、コードと状態を分離し、そして何より重要なのは、トランザクションを並列実行する点だ。

Solana Virtual Machine(SVM)とは何か

その核心において、SVMはSolanaの実行エンジンであり、プログラムロジックの実行、トランザクションの処理、状態の更新を担うシステムだ。Ethereumから来た人であれば、EVMに対するSolanaの答えだと考えればよいが、アーキテクチャ的には重要な点で異なっている。

SVMを理解するには、まずそれが何を対象に動作しているのかを理解する必要がある。

  • Accounts(アカウント): Solanaの汎用データ構造。すべてがアカウントである。ユーザーのウォレット、トークン残高、プログラムの状態、さらにはプログラム自体もそうだ。アカウントはデータを保持し、所有者(そのアカウントを変更できるプログラム)を持つ。コードとストレージを一体化するEVMのコントラクトとは異なり、Solanaはこれらを完全に分離しており、すべての状態はアカウント内に存在する。
  • Programs(プログラム): Solanaにおけるスマートコントラクトの呼称。プログラムは_ステートレス_であり、実行可能なコードのみを含み、sBPFと呼ばれるバイトコード形式にコンパイルされる。ほとんどのSolanaプログラムはRustで書かれる(CやC++もサポートされている)。プログラムが実行されると、渡されたアカウントの読み書きを行うが、内部には何も保持しない。
  • Transactions(トランザクション): 1つ以上のinstructionの束であり、それぞれが特定のプログラムを対象とし、読み書きが必要なアカウントを指定する。この状態依存関係の事前宣言こそが、すべての鍵となる。これによって並列実行が可能になっている。

これを踏まえると、SVMを理解する最もシンプルな方法は次のとおりだ。コンパイル済みのプログラムバイトコードを受け取り、一連のアカウントに対して実行し、結果として生じる状態変化を決定する、サンドボックス化されたランタイム環境である。Solana上のすべてのトランザクションはSVMを通過する。

SVMがEVMと異なる点は、単なる実装の詳細ではなく、根本的な設計にある。SVMは当初から並列実行を前提として構築されている。トランザクションは状態依存関係を事前に宣言し、ランタイムはそれによって競合しない処理を識別し、CPUコア全体で同時に処理できる。EVMはトランザクションを逐次処理するが、SVMは数千を同時に処理する。

用語についての補足:「SVM」という用語は、文脈によって意味が異なる。狭義では、プログラムコードを実行するバイトコードインタプリタとJITコンパイラそのものを指す(バイトコード形式であるsBPFについては後述する)。広義では、Anza(Solanaのコアバリデータチーム)などのチームが公式に用いる意味であり、スケジューリング、compute budget管理、プログラムのロード、実行、状態更新までを含む、トランザクション実行パイプライン全体を指す。開発者が「SVM」と言うとき、通常はこの広義のシステムを指している。本ガイドでは両方を扱い、どちらの意味かはその都度明示する。

SVMアーキテクチャを理解する

ここまでで、SVMが_何であるか_、そして他のブロックチェーンVMとどう異なるかを見てきた。次は、それが実際に_どう動作するか_を理解しよう。このセクションでは、トランザクションが到着してから最終的な状態変化に至るまで、完全なアーキテクチャを解説する。

以下の4つの相互に関連する要素を扱う。

  1. トランザクションがシステムをどう流れるか(パイプライン)
  2. コードを実行する実行エンジン(eBPF、コンパイル、VM自体)
  3. プログラムが操作するデータモデル(アカウント、PDA、rent、CPI)
  4. 並列実行と、それを可能にするSealevel

各要素は前の要素の上に構築される。最後まで読めば、個々のコンポーネントだけでなく、それらがどう組み合わさってSolanaのパフォーマンス特性を実現しているかが理解できるはずだ。

Part 1: トランザクションがSVMをどう流れるか

SVMを理解する最初のステップは、トランザクションの旅路をたどることだ。「送信」を押した瞬間からブロック確定に至るまでを追う。このパイプラインは、_なぜ_Solanaプログラムがこのように構造化されているのか、_なぜ_アカウントを事前に宣言しなければならないのか、そして_どこで_実際に並列性が生じているのかを説明する。

Solanaにトランザクションを送信すると、単純にキューに入って順番を待つわけではない。それぞれが特定の問題を解決する、複数の専門化されたサブシステムを通過していく。

markdown
Copied
Transaction submitted ↓ Banking Stage (conflict detection, parallel scheduling) ↓ The Bank (state snapshot for this slot) ↓ BPF Loader (provisions sBPF VM instance) ↓ Program executes (reads/writes accounts within budget) ↓ State updates committed

各段階を見ていこう。

Banking Stage:並列性が生まれる場所

これは交通整理役だ。検証済みのトランザクションが到着すると、Banking Stageはそれぞれを分析する。どのアカウントを読む必要があるか、どのアカウントに書き込む必要があるか。

この情報をもとに、どのトランザクションを同時に安全に実行できるかを判断する。

  • 完全に異なるアカウントを扱うトランザクション → 並列実行可能
  • 同じアカウントを両方が読み取るだけのトランザクション → 並列実行可能(読み取り同士は競合しない)
  • 同じアカウントに両方が書き込むトランザクション → 逐次実行が必須

スケジューラはアカウント単位のロックを用いてこれらのルールを強制する。ワーカースレッドは、競合しないトランザクションのバッチを同時に処理する。これがSolanaの並列実行モデル、Sealevelの核心である(Sealevelについてはパート4で詳しく掘り下げる)。

The Bank:ある時点における状態

Banking Stageが_スケジューリング_を担う一方、Bankは_状態_を担う。これは、特定のスロット(Solanaの時間単位で、約400ms)におけるSolana全体の状態のスナップショットだと考えればよい。

Bankはアカウントデータを管理し、実行を調整し、どのバージョンの状態が正統かを追跡する。各Bankは3つのライフサイクル段階を経る。

Stage
What's Happening

Active

このスロットのトランザクションを現在処理中

Frozen

スロット完了:これ以上の変更は許可されない

Rooted

確定し、永続ストレージにコミット済み

Banking Stageがトランザクションを実行するとき、それは特定のBank、つまり世界の特定のバージョンに対して実行される。これが、数千のトランザクションを並列処理しながらSolanaが整合性を維持する仕組みだ。

BPF Loader:プログラム実行の起動

トランザクションがプログラムを呼び出すとき、そのプログラムのコードを実際に_実行_する何かが必要になる。それがBPF Loaderの仕事だ。

BPF Loaderはプログラムのライフサイクル全体を扱う。

  • デプロイ:新しいプログラムのバイトコードをネットワークにアップロードする
  • JITコンパイル:バイトコードをネイティブマシンコードに変換し、実行を高速化する
  • アップグレード:既存プログラムの新バージョンをプッシュする
  • 実行:プログラムが呼び出された際に、隔離されたVMインスタンスをプロビジョニングする

各プログラムの呼び出しは、それぞれ独自のサンドボックス化されたsBPF VMインスタンスを持ち、以下を備える。

  • 専用のメモリ領域
  • compute budget(ガスリミットに類似)
  • 他のプログラムからの厳格な隔離

プログラムがcompute budgetを超えたら?実行は停止する。アクセスすべきでないメモリにアクセスしようとしたら?実行は停止する。この隔離は設計上厳格である。これにより、Solanaは数千人の開発者による信頼できないコードを安全に実行できる。

典型的なトランザクションの流れ

典型的なトランザクションの完全な流れを示す。

  1. どのプログラムを呼び出し、どのアカウントが必要かを指定してトランザクションを送信する
  2. Banking Stageがそれを受け取り、他の保留中のトランザクションとの競合をチェックし、適切にスケジューリングする
  3. Bankがこのスロットの現在の状態スナップショットを提供する
  4. BPF Loaderが対象プログラム用のsBPF VMインスタンスをプロビジョニングする
  5. プログラムが実行され、指定されたアカウントの読み書きを行う
  6. 状態変化がBankにコミットされる
  7. 最終的にBankがfreezeとrootを経て、変更が永続化される

各コンポーネントは特定の問題を解決している。Banking Stageは並列性を可能にし、Bankは一貫した状態を提供し、BPF Loaderは安全な実行を保証する。これらが合わさって、広義の「SVM」を構成している。

トランザクションについてさらに深く知りたい場合は、以下のリソースを参照してほしい。

Part 2: 実行エンジン

BPF Loaderはプログラムコードを実行するためにVMインスタンスをプロビジョニングする。だが、そのVMインスタンスの内部では実際に何が起きているのか。

ほとんどのブロックチェーンVMはゼロから設計されてきた。Solanaは異なる方向に進んだ。eBPF(extended Berkeley Packet Filter)という、もともとLinuxカーネル向けに構築された技術を採用したのだ。

BPFは1992年、Lawrence Berkeley Laboratoryで効率的なネットワークパケットフィルタリングのために生まれた。時間の経過とともに、Linuxカーネル内で安全に動作する汎用的なサンドボックス化されたVM、eBPFへと進化した。bpftraceのような可観測性ツールを使ったことがある、あるいはeBPFベースのネットワーキングに携わったことがあるなら、この技術にはすでに触れているはずだ。大規模な重要システムを支える、実績のあるインフラである。

Solanaの創業者Anatoly Yakovenkoは、オペレーティングシステム分野での経歴(Qualcommで13年以上)から、ある重要な洞察に至った。既存のVMがすでに難しい問題を解決しているのに、なぜ新しいVMをゼロから作る必要があるのか、というものだ。

eBPFはSolanaが必要としていたものをまさに提供していた。

  • オーバーヘッドのない安全性: eBPFプログラムは、ホストをクラッシュさせたりメモリを破損させたりできない制限された環境で実行される。バイトコードはロード前に静的検証される。無効なメモリアクセス、範囲外へのジャンプ、許可されていない操作は一切許されない。この安全性は、ガベージコレクションを必要とするマネージドランタイムとは異なり、実行時のオーバーヘッドなしに実現される。
  • ネイティブに近いパフォーマンス: レジスタベースのアーキテクチャ(EVMのスタックベース設計とは異なる)により、ネイティブマシンコードへのJITコンパイルが可能になる。プログラムはネイティブに近い速度で動作する。
  • 成熟したツールチェーン: LLVMにはすでにeBPFバックエンドが含まれている。Rust、C、C++、その他LLVM対応言語は、eBPFバイトコードに直接コンパイルされる。既知の言語で書けば、あとはツールチェーンが処理してくれる。

sBPF:Solanaによるカスタム版

Solanaはバニラのままのeベースのeepでない、標準のeBPFをそのまま使っているわけではない。標準eBPFはカーネル空間での動作を前提とした厳しい制約を持ち、それはブロックチェーンの実行にはそぐわない。そこでSolanaはこれをフォークした。

その結果生まれたのがsBPF(Solana Bytecode Format)であり、オンチェーンのプログラム実行向けに調整された改変版だ。

Feature
Standard eBPF
Solana sBPF

Environment

Kernel-space

User-space

Stack size

512 bytes

4KB per frame

Loops

Restricted (must provably terminate)

Allowed (bounded by compute units)

Custom syscalls

No

Yes (logging, CPI, crypto)

eBPFをカーネル空間で安全にしている制約、小さなスタック、ループの禁止は、Solanaにおけるスマートコントラクト開発を著しく制限してしまうだろう。sBPFはこれらを緩和しつつ、compute unit予算によって安全性を維持している。ループは可能だが、いずれcomputeを使い果たす。

カスタムsyscallも重要だ。プログラムはメッセージをログ出力したり、他のプログラムを呼び出したり(Cross-Program Invocation)、暗号処理を行ったりする必要がある。標準のeBPFにはこうした概念が存在せず、sBPFはこれらをファーストクラスのプリミティブとして追加している。

Solanaプログラムをコンパイルすると、ツールチェーン内でtarget tripleとしてsbf-solana-solanaが表示される。これがこのSolana固有の派生版の識別子だ。

RustからバイトコードへーコンパイルパイプラインThe compilation pipeline

cargo build-sbfを実行すると、Rustコードは複数段階のコンパイルパイプラインを通過する。

1. Rustフロントエンド: コンパイラがコードを解析し、マクロを展開し、型チェックを行い、borrow checkerを実行してメモリ安全性を検証する。このステージを抜ける頃には、Rustは他のシステムプログラミング言語を悩ませるバグの類(use-after-free、データ競合、nullポインタ)を丸ごと排除している。これはSolanaの過小評価されがちな利点の一つだ。プログラムがチェーンに触れる前の、コンパイル時に強制されるメモリ安全性である。

2. LLVM最適化: コードはプラットフォームに依存しない表現であるLLVM IRに変換され、そこで本格的な最適化が行われる。定数畳み込み、関数のインライン化、デッドコードの除去、ループアンロールなどだ。こうした最適化が重要なのは、compute unitがコストに直結するからだ。バイトコードが小さく引き締まっているほど、トランザクションは安く済む。

3. sBPFバックエンド: LLVMのBPFバックエンドをSolana独自にフォークしたものが、最適化されたIRをsBPFバイトコードに変換し、ELFファイル(.so)としてパッケージ化する。これがネットワークにデプロイされるものであり、プログラムが呼び出された際にBPF LoaderがVMインスタンスにプロビジョニングするものでもある。

text
Copied
Rust source (.rs) ↓ Rust compiler (type checking, borrow checker) ↓ LLVM IR (optimizations) ↓ sBPF bytecode (.so) ↓ Deployed to Solana

sBPF VMの内部

ここからは最も低レベルの話、つまりバイトコードを実際に実行する仮想マシンそのものだ。

sBPF命令セットは64ビット命令(各8バイト)を使用し、RISC風の設計で約100個のopcodeを持つ。プログラムは11個の64ビットレジスタにアクセスできる。

Column
Purpose

R0

戻り値

R1-R5

関数の引数

R6-R9

callee-saved(呼び出しをまたいで保持)

R10

フレームポインタ(読み取り専用)

メモリレイアウト

VMは、それぞれ特定のアドレスから始まる固定領域にメモリを整理する。

Address
Region
Purpose

0x000000000

.text

プログラムコード

0x100000000

.rodata

定数と読み取り専用データ

0x200000000

Stack

呼び出しフレームごとに4KB

0x300000000

Heap

デフォルトで32KBの割り当て

0x400000000

Input

アカウントとinstructionデータ

このrigidなレイアウトによって、VMは厳格な境界を強制でき、プログラムが誤って(あるいは悪意を持って)アクセスすべきでないメモリにアクセスすることを防いでいる。

JITコンパイルとセキュリティ

sBPF VMは2つの実行モードをサポートする。

  • インタプリタ: 各命令を一つずつ処理する。起動は速いが実行は遅い。ローカルでのテストやデバッグに有用。
  • JITコンパイル: 実行前にsBPFバイトコードをネイティブなx86_64マシンコードに変換する。起動は遅いが、実行時は劇的に速い。本番環境でバリデータが使用するのはこちらだ。

Agaveバリデータは、JITコンパイル済みのプログラムをProgramCacheForTxBatchにキャッシュし、再利用する。Token Programのような頻繁に使われるプログラムは、トランザクションごとに再コンパイルされることはない。すでにキャッシュに存在しているからだ。

セキュリティの強制は2つのレベルで行われる。

  1. 静的検証(実行前):
  • 不正または無効な命令を拒否する
  • すべてのメモリアクセスパターンを検証する
  • ジャンプ先が有効な命令境界に着地することを保証する
  • 到達不能なコードパスを検出する

プログラムが検証に失敗した場合、デプロイされることはない。それだけだ。

  1. ランタイム計測(実行中):
  • すべての命令がcompute unitを消費する
  • VMは各ステップの後に予算をチェックする
  • 予算を超えると、実行は即座に停止する

この2層のアプローチにより、無限ループ、リソース枯渇攻撃、暴走するプログラムを防いでいる。仮に悪意あるバイトコードが何らかの方法で静的検証を通過したとしても、永遠に実行され続けることはできない。compute限界に達し、停止する。

実行エンジンについてさらに学びたい場合は、以下のリソースを参照してほしい。

Part 3: データモデル

これまで、トランザクションがSVMをどう流れるか、そしてバイトコードがsBPF VM内でどう実行されるかをたどってきた。次は、プログラムが何を操作するのかを見ていこう。

ほとんどのプログラミング環境には、変数、データベース、ファイルシステムなど、データを保存・取得するさまざまな手段がある。ブロックチェーンにも同様のもの、つまりトランザクション間で状態を永続化する仕組みが必要だ。EVMはこれを、各コントラクトに独自のストレージ、つまりコントラクト自体に組み込まれたキーバリューストアを与えることで解決した。

Solanaはこれとは根本的に異なるアプローチを取っており、この違いを理解することは不可欠だ。なぜなら、それがこのプラットフォーム上での構築のあらゆる側面を形作っているからだ。

Solana上のすべてはアカウントである

Solanaを巨大なキーバリューデータベースだと考えてみよう。

  • キー:32バイトのアドレス(通常は公開鍵、あるいは導出アドレス)
  • :アカウント(バイト列、残高、メタデータを保持するデータ構造)

この均一性は最初は奇妙に思えるかもしれないが、これこそがSolanaのパフォーマンス特性を可能にしているものだ。すべての状態は、明示的なアドレス、明示的な所有者、そして誰が変更できるかについての明示的なルールを持つ。ランタイムは、何の状態が変化しうるかを調べるためにネストしたコントラクト呼び出しをたどる必要がない。トランザクションのアカウントリストから事前に把握できるからだ。

アカウントの中に実際に何が入っているかを見てみよう。すべてのアカウントは同じ5つのフィールドを持つ。

Field
Description

lamports

Solanaの最小単位での残高(1 SOL = 10億lamports)

data

アカウントの状態を格納する任意のバイト配列

owner

このアカウントのデータを変更できるプログラムID

executable

ブールフラグ―プログラムアカウントの場合はtrue

rent_epoch

rent追跡用のレガシーフィールド(ほぼ非推奨)

ownerフィールドは非常に重要だ。所有プログラムのみが、アカウントのdataフィールドを変更したり、lamportsを引き落としたりできる。誰でもどのアカウントも読み取ることができ(Solana上のすべての状態は公開されている)、誰でも任意のアカウントにlamportsを送金することができる。だが、書き込みは所有権によって制限されている。

この所有権モデルこそが、並列実行を可能にしているものだ。ランタイムは、どのプログラムがどのアカウントを変更できるかを正確に把握しているため、競合しないトランザクションを安全に同時実行できる。

プログラム:設計上ステートレス

Solanaプログラムは、コンパイル済みのsBPFバイトコードを含む実行可能アカウントだ。だがここで重要な洞察がある。プログラムは内部に状態を保存できない。プログラムは、instructionを処理し、渡されたアカウントを変更する純粋関数である。

すべてのSolanaプログラムは同じエントリポイントシグネチャを共有している。

rust
Copied
pub fn process_instruction( program_id: &Pubkey, // This program's address accounts: &[AccountInfo], // All accounts passed to this instruction instruction_data: &[u8], // Arbitrary data (parameters, method selectors, etc.) ) -> ProgramResult

プログラムが実行されると、以下を受け取る。

  1. 自身のアドレス(導出したPDAを検証できるように)
  2. 読み書きが許可されているアカウントの配列
  3. 呼び出し元が渡した任意のパラメータを含むinstructionデータ

プログラムはロジックを実行し、変更が許可されているアカウントを変更し、成功またはエラーを返す。呼び出し間で内部に何も保存しない。

なぜステートレスなのか。それは、明確な所有権の境界を可能にするからだ。もしプログラムが自身の状態を保存していたら、どのプログラムがどのストレージにアクセスできるかについて複雑なルールが必要になるだろう。すべての状態を明示的な所有者を持つアカウントに強制することで、モデルはシンプルなままとなり、並列化も可能なままとなる。

設計上の補足:デフォルトでイミュータブルなEVMコントラクトとは異なり、Solanaプログラムはデフォルトでアップグレード可能だ。アップグレード権限を持つ者は、いつでも新しいバイトコードをプッシュできる。開発者はアップグレード権限を放棄してプログラムをイミュータブルにすることもできるが、これは明示的な選択によるものだ。これは、ほとんどのプログラムがバグ修正や機能アップデートを必要とするという、Solanaの実践的なアプローチを反映している。

Program Derived Addresses(PDA)

プログラムがステートレスで、すべてのデータがアカウント内に存在するなら、プログラムはどうやって自身のアカウントを作成・管理するのだろうか。プログラムは秘密鍵を保持できない。ここでProgram Derived Addresses(PDA)の出番となる。

PDAは、Ed25519の楕円曲線から外れた特殊なアドレスであり、これは有効な秘密鍵が存在しないことを意味する。この暗号学的性質により、プログラムは秘密鍵を保持することなくこれらのアドレスのために「署名」でき、ランタイムが内部でその導出を検証する。

PDAの導出は、seedとプログラムIDと「bump seed」をハッシュ化することで行われる。

rust
Copied
// Find a PDA for storing a user's balance let (user_balance_pda, bump) = Pubkey::find_program_address( &[ b"balance", // Static seed user.key().as_ref() // User's public key as seed ], program_id );

このアルゴリズムは、255からbumpの値を下げながら試行し、曲線から外れたアドレスを生成する値を見つける。最初に有効となったこのbumpが「canonical bump」である。

なぜPDAが重要なのか

  • 決定論的アドレス指定:同じseedを与えれば、常に同じアドレスが得られる。アドレスを別途保存する必要がない。
  • プログラム制御アカウント:プログラムは自身が導出したPDAにアカウントを作成でき、そのPDAのために署名できるのはそのプログラムのみである。
  • キーバリューパターン:seedは関係性(ユーザー + トークンタイプ → 残高アカウント)をエンコードでき、マッピングのようなデータ構造を実現できる。
rust
Copied
// Common PDA patterns // User-specific data let (user*profile, *) = Pubkey::find_program_address( &[b"profile", user.key().as_ref()], program_id ); // Token account for a specific mint let (token*vault, *) = Pubkey::find_program_address( &[b"vault", mint.key().as_ref()], program_id ); // Unique item with incrementing ID let (item, _) = Pubkey::find_program_address( &[b"item", &item_id.to_le_bytes()], program_id );

Rent:状態のための対価

ブロックチェーンの状態はタダではない。すべてのアカウントはバリデータのディスク上のスペースを占有し、そのスペースには実際のコストがかかる。チェーンによってこの扱いは異なる。Ethereumはストレージ操作に対してガスを課金するが、一度書き込まれた状態は永久に残り続け、状態の肥大化を招く一因となっている。

Solanaは異なるアプローチを取っている。「rent」と呼ばれる仕組みがあり、ネットワークはデータをオンチェーンに保持するために、バイトあたり年間約3,480 lamportsを課金する。実際には、mainnet上のすべてのアカウントは「rent免除」でなければならず、事前に2年分のrentを保持する必要がある。

rust
Copied
rent_exempt_minimum = (account_size + 128 bytes overhead) × 3,480 × 2

典型的なトークンアカウント(165バイト)の場合、これはおおよそ0.002 SOLに相当する。

だが、他のチェーンとの重要な違いはここにある。アカウントを閉じると、預けたrentは全額返還される。これは、未使用の状態を整理するための経済的インセンティブを生み出しており、ストレージが永久に残り続けるモデルには存在しないものだ。

rust
Copied
// Closing an account returns lamports to a recipient ctx.accounts.account_to_close.close(ctx.accounts.recipient.to_account_info())?;

Cross-Program Invocations(CPI)

プログラムは孤立して存在しているわけではない。あるDeFiプロトコルはToken Programを呼び出してトークンを転送し、それがAssociated Token Account Programを呼び出すかもしれない。このCross-Program Invocationこそが、Solanaのエコシステムが組み合わさる仕組みだ。

CPIを可能にする関数は2つある。

rust
Copied
// Standard CPI - passes existing signers through invoke( &instruction, &[account1, account2, ...] )?; // CPI with PDA signing - program "signs" for a PDA it controls invoke_signed( &instruction, &[account1, account2, ...], &[&[b"seed", &[bump]]] // Seeds that derive the PDA )?;

PDAのseedを指定してinvoke\_signedを呼び出すと、ランタイムはその導出が期待されるアドレスと一致することを検証し、その呼び出しに対して署名権限を付与する。これが、プログラムが秘密鍵を保持することなく資産を制御できる仕組みだ。

重要な制約:

  • 最大呼び出し深度は5(初回トランザクションから4段までのネストしたCPI)
  • signer権限は呼び出しチェーン全体に連鎖する
  • compute budgetは、トランザクション内のすべてのCPIで共有される

アカウントについてさらに学びたい場合は、以下を参照してほしい。

  • Accounts Overview:Solanaアカウントの完全ガイド
  • PDAs:Program Derived Addressesの解説
  • CPIs:Cross-Program Invocationsガイド

Part 4: 並列実行(Sealevel)

ここまで、すべての基礎となる要素、トランザクションがパイプラインをどう流れるか、sBPF VMがバイトコードをどう実行するか、そしてアカウントモデルが明示的な所有権のもとでどう状態を保存するかを見てきた。

いよいよ、ここまで積み上げてきた問いに答えることができる。Solanaは実際にどうやって並列実行を実現しているのか。

これまでも随所でその片鱗を示してきた。競合しないトランザクションをスケジューリングするBanking Stage、事前に所有者を宣言するアカウント、どのアカウントに触れるかを指定するトランザクション。これらは別々の機能ではない。すべて、Sealevelと呼ばれる一つのシステムの構成要素である。

Sealevelは、Solanaの並列スマートコントラクトランタイムであり、SVMのスループットを可能にするコアイノベーションだ。ここまで扱ってきたすべて、つまりアカウントモデル、所有権のルール、事前のアカウント宣言は、Sealevelを可能にするために存在している。

その根本的な洞察は、驚くほどシンプルだ。

トランザクションが実行開始前に、読み書きするアカウントを宣言していれば、ランタイムは重複しないトランザクションを識別し、それらを同時に実行できる。

それだけだ。それが仕掛けの全てである。だが、これを実際に機能させるには、システムの他のあらゆる部分をこの制約を中心に設計する必要があった。

並列実行のルール

Sealevelのスケジューリングは、シンプルなルールに従う。

Scenario
Execution

異なるアカウントを扱うトランザクション

並列実行

同じアカウントを読み取るだけのトランザクション

並列実行

同じアカウントに書き込むトランザクション

逐次実行

それだけだ。読み取り同士は競合しない。書き込みはすべてと競合する。

text
Copied
TX1: [Account A (write), Account B (read)] TX2: [Account C (write), Account D (read)] ──► PARALLEL TX3: [Account B (read), Account E (write)] TX4: [Account A (write), Account F (read)] ──► SEQUENTIAL with TX1 (conflicts with TX1 on Account A write)

これが、Solanaのトランザクションですべてのアカウントを事前に宣言しなければならない理由だ。単なる形式的な手続きではなく、ランタイムがトランザクションを並列化するために必要な情報なのである。

スケジューリングアルゴリズム

Banking Stageは、アカウント単位のロックによってSealevelを実装している。

  1. 読み書きフラグ付きでアカウントを指定したトランザクションが到着する
  2. 書き込み可能な各アカウントについて:排他ロックを取得する
  3. 読み取り可能な各アカウントについて:共有ロックを取得する(複数の読み取り者はOK)
  4. すべてのロックが取得できたら:実行をスケジューリングする
  5. いずれかのロックがブロックされたら:再キューして後で試す

現在の実装では6つのスレッドを使用しており、4つがvote以外のトランザクション用、2つがvoteトランザクション用となっている。各スレッドは、優先手数料(compute unitあたりの手数料)と到着時刻によって順序付けられたキューを維持する。

より新しいCentral Schedulerアーキテクチャは、単一のスケジューリングスレッドとPrio-Graphアルゴリズム(先読みウィンドウを通じて競合を識別する、遅延生成される依存関係グラフ)を用いる。これにより、高い競合を伴うワークロードのスケジューリング効率が向上する。

実際のパフォーマンス

Metric
Value

理論上の最大TPS

約65,000(単純な転送の場合)

Testnetのベンチマーク

47,370 TPS(200ノード、23リージョン)

Mainnetの一般的な範囲

800~3,600 TPS(実際のユーザートランザクション)

ブロック時間の目標値

400ms

実際のスループットは、トランザクションの構成に大きく依存する。独自のアカウントを扱う単純な転送は完璧に並列化される。同じ流動性プールをすべて叩く複雑なDeFiトランザクションは、競合を生み、シリアライズされる。

これは実は一つの特徴だ。競合は局所化される。あるDEXでの取引量の急増が、別のネオバンクのトークン転送を遅くすることはない。異なるアカウントに触れるからだ。これは、すべてのトランザクションが同じグローバルなスループットを奪い合うチェーンとは根本的に異なる。

なぜ逐次型VMは単純にこれを採用できないのか

なぜEVMが単純に並列実行を追加できないのか、疑問に思うかもしれない。

根本的な問題は、EVMの状態アクセスが実行前ではなく実行時に決定されるという点にある。Solidityコントラクトを呼び出すとき、実際に実行してみるまで、それがどのストレージスロットを読み書きするかを知る方法はない。そのコントラクトは別のコントラクトを呼び出し、それがさらに別のコントラクトを呼び出すかもしれず、それぞれが予測不能な状態にアクセスする。

この「read-write oblivious」なモデルは、投機なしでは安全な並列化を不可能にする。一部の新しいチェーンは「楽観的並列実行」を実装している。競合がないと仮定して投機的に実行し、競合が発生した場合はそれを検出して逐次的に再実行する、というものだ。これは機能するが、複雑さとオーバーヘッドを増やす。

Solanaのアプローチは異なる。競合は解決されるのではなく、そもそも回避される。事前の宣言を要求することで、ランタイムは実行が始まる前に各トランザクションが何を必要とするかを正確に把握している。この制約こそが、最適化を可能にしている。

EIP-2930はEthereumにガス割引のためのオプションの「アクセスリスト」を導入したが、それは必須ではない。Solanaは宣言を必須かつ普遍的なものにしている。それが決定的な違いだ。

SVMエコシステム:Solana Mainnetを超えて

SVMはもはやSolanaだけのものではない。2024年6月、AnzaはSVM APIを導入し、実行エンジンをSolanaのバリデータクライアントから切り離した。このモジュール化により、全く新しいユースケースが可能になった。

Eclipse:Ethereum上のSVM

Eclipseは2024年11月、EthereumにおけるSVMベースの初の本番rollupとしてローンチした。

Layer
Provider

決済

Ethereum

実行

SolanaのSVM

データ可用性

Celestia

不正証明

RISC Zero(ZKアクセラレーション)

Orcaを含む60以上のアプリがローンチ時にデプロイされた。Eclipseはこのブリッジを構築するために6,500万ドルを調達し、SVMがSolanaのコンセンサスレイヤーから独立した価値を持つことを実証した。

SOON Network

SOON(Solana Optimistic Network)は、初の「切り離されたSVM」rollupとして2025年初頭にalpha mainnetを達成した。単純なフォークとは異なり、SOONは実行とコンセンサスを明確に分離し、状態管理にMerkle Patricia Tries(SolanaのAccountsDBとは異なる)を実装している。目標は、Firedancerとの統合により5,000~60万TPSを達成することだ。

Sonic SVM

Sonicは2025年1月、Solana上初のアトミックなSVM Layer 2としてゲーミング向けにローンチした。Mirror World Labsが構築し、ゲームごとにカスタムSVMチェーンを立ち上げるためのHyperGrid Frameworkを含む。Testnetでは、月間200万以上のアクティブウォレットから6億以上のトランザクションが処理された。

MagicBlock:Ephemeral Rollups

MagicBlockは「ephemeral rollups」、つまり一時的でオンデマンドなSVM実行環境を導入した。コントラクトは通常どおりSolana上にデプロイし、50ms未満のレイテンシが必要になったときに特定のアカウントをMagicBlockに委任する。書き込みはephemeralセッション内で発生し、その後Solanaにコミットバックされる。

400msのブロックですら遅すぎるゲーミングやリアルタイムアプリケーションに最適だ。

Firedancer:パフォーマンスのブレークスルー

Jump CryptoのFiredancerクライアントは、SVM開発における最も重要な進展かもしれない。Agaveのコンポーネントを一切共有せず完全にCで書かれており、各機能が専用のCPUコア上で動作し、カーネルバイパスのネットワーキングを用いるタイルベースのアーキテクチャを採用している。

Benchmark
Result

パケット取り込み

100万TPS

SVM実行(単純なプログラム)

5億TPS

これらは合成ベンチマークだが、現在のmainnetパフォーマンスをはるかに超える大きな余地を示唆している。

タイムライン:

  • 2024年9月:Frankendancer(ハイブリッド版)がmainnetに登場
  • Breakpoint 2024:非投票モードでの完全なFiredancer
  • 2025年:完全な本番リリースを予定

SVM上で構築する

ここまでアーキテクチャを見てきたので、次は実践的な話に移ろう。Solana上での開発を始めたい場合、実際に何が必要になるのか。

ツールチェーンの概観

Tool
What It Does

Solana CLI

コアツールキット。keypair管理、デプロイ、クラスターとのやり取り

Anchor

プログラム構築における主要フレームワーク。定型コード、シリアライゼーション、アカウント検証、テストを処理する

Rust + cargo-build-sbf

Rustコードをデプロイ用のsBPFバイトコードにコンパイルする

Bankrun / LiteSVM

完全なバリデータを立ち上げることなく、高速にローカルでテストできる

solana-test-validator

mainnetの状態を用いた統合テスト向けの完全なローカルバリデータ

Alchemy

Solana向けの高性能なRPCノードとAPIを提供し、ブロックチェーンデータへの信頼性の高いアクセス、強化された履歴クエリ、開発・テスト・インタラクションのためのツールを提供する

ほとんどの開発者にとって、Solana CLI + Anchor + Bankrunという組み合わせがユースケースの90%をカバーする。

始め方:5分でのセットアップ

bash
Copied
# Install Solana CLI sh -c "$(curl -sSfL <https://release.anza.xyz/stable/install>)" # Install anchor cargo install --git <https://github.com/coral-xyz/anchor> anchor-cli # Create a new project anchor init my_project && cd my_project # Build and test anchor build anchor test

これだけだ。動作するSolana開発環境、スターター用プログラム、テストスイート、そしてローカルバリデータの統合がすでに整っている。

ここから先は、Anchor Bookが最初の実践的なプログラムの構築を案内してくれるし、Solana Cookbookがよくあるパターンのレシピを提供してくれる。

さらに深く学ぶには:

結論:アーキテクチャ上の賭け

SVMは一貫したアーキテクチャ上のビジョンを体現している。事前の制約(明示的なアカウント宣言、ステートレスなプログラム、複雑なトランザクション構築)を受け入れることで、並列性、スループット、そして局所化された手数料市場を実現するというものだ。これらの制約は付随的なものではなく、パフォーマンスを可能にする仕組みそのものである。

何が可能かを探ってみたいなら、Alchemy's Solana APIsを使えば、Solanaのmainnet、devnet、そしてより広範なSVMエコシステムに、一つのプラットフォームからアクセスできる。実際に構築を始めて、自分の目で確かめてほしい。

よくある質問

Solana Virtual Machine(SVM)とは何ですか?

SVMはSolanaの実行エンジンであり、eBPFバイトコード上に構築されたレジスタベースのランタイムです。すべての状態依存関係を事前に宣言させることでトランザクションを並列実行し、CPUコア全体で数千件の同時トランザクションを可能にします。

SVMはEthereum Virtual Machine(EVM)とどう異なりますか?

スタックベースで逐次実行するEVMのアーキテクチャとは異なり、SVMはレジスタベースの設計で並列実行を行い、アカウントを通じてコードと状態を分離し、同時処理を可能にするためにアカウントの事前宣言を要求します。

Sealevelとは何ですか。なぜ重要なのですか?

Sealevelは、Solanaの並列スマートコントラクトランタイムであり、競合しないトランザクションをCPUコア全体で同時に実行されるようスケジューリングします。異なるアカウントに触れるトランザクションを並列処理することで、数千TPSのスループットを実現しています。

SVM上で構築するにはどのプログラミング言語を使えますか?

プログラムは主にRustで書かれ、LLVMを経由してsBPFバイトコードにコンパイルされますが、C、C++、ZigなどLLVM互換の他の言語もサポートされています。

Program Derived Addresses(PDA)とは何ですか?

PDAは、Ed25519曲線から外れた特殊なアドレスで、有効な秘密鍵が存在しません。これにより、プログラムは秘密鍵を保持することなく、自身が制御するアドレスを決定論的に生成し「署名」することができ、自身のアカウントを管理できるようになります。

SVMのアカウントモデルはどのように機能しますか?

Solana上のすべてはアカウントであり、lamports(残高)、任意のデータバイト、所有者のプログラムID、メタデータを含むデータ構造です。プログラムはステートレスであり、自身が所有するアカウントのみを変更できるため、並列実行のための明確な所有権の境界が実現されています。

sBPFとは何ですか。eBPFとどう関係していますか?

sBPF(Solana Bytecode Format)は、Solanaがブロックチェーン向けにカスタマイズしたeBPFの派生版です。より大きなスタックサイズ(512バイトに対し4KB)、compute unitで制限されたループのサポート、ログ出力・CPI・暗号処理のためのカスタムsyscallを備えています。

Cross-Program Invocations(CPI)とは何ですか?

CPIは、Solanaプログラムが実行中に他のプログラムを呼び出すことを可能にする仕組みで、最大呼び出し深度は5です。共有compute budgetとsigner権限の連鎖を維持しながら、エコシステム全体での組み合わせ可能性を実現しています。

SVMはSolana mainnet以外でも使用できますか?

はい。モジュール化されたSVM APIにより、Solana以外での利用が可能になっています。EclipseはEthereumのrollupとしてSVMを実行し、SOON Networkは切り離されたSVM rollupとして稼働し、SonicやMagicBlockのようなプロジェクトは専用のSVM実行環境を構築しています。

SVM上での構築を始めるにはどうすればよいですか?

Solana CLIとAnchorフレームワークをインストールし、anchor initを使ってスターター用プログラム、テストスイート、ローカルバリデータの統合を備えたプロジェクトを作成してください。Anchor BookとSolana Cookbookが、包括的な開発ガイドを提供しています。

Background gradient

ブロックチェーンで魔法を生み出す

Alchemyは、最も強力なweb3開発者向けプロダクトとツールを、豊富なリソース、コミュニティ、そして卓越したサポートと組み合わせて提供します。