
Solanaプログラミングモデル:Solana開発入門
この記事では何を解説しますか?
Solanaの分散コンピューティングへのアプローチは、すべてをアカウントと呼ばれる独自のメモリ領域に保存するという単純な原則に基づいています。Solanaはグローバルなキー/バリューストアとして動作し、公開鍵が対応するアカウントの一意な識別子として機能します。アカウントは状態を保存するため、Solanaの基盤となります。プログラムからトークン残高まで、あらゆるものが格納されます。トランザクションはアカウントを更新し、状態の変更を反映するために使用されます。
この記事では、Solanaアーキテクチャの複雑さを掘り下げます。まずクラスターと状態の概念を概観し、その後、Solanaの基礎的な構成要素であるアカウントとプログラムの役割について説明します。続いて、トランザクションがアカウントとプログラム間の動的なやり取りをどのように実現するかを確認します。
この記事を読み終えるころには、Solanaのプログラミングモデルを詳しく理解できるようになります。クラスターのアーキテクチャ、データ保存におけるアカウントの重要な役割、トランザクションがアカウントデータを更新するプロセスを把握できます。さらに、rentシステムやバージョン付きトランザクションなど、Solana固有の機能についても学びます。
Solanaクラスターとは何ですか?
Solanaアーキテクチャの中核には、クラスターがあります。これは、連携してトランザクションを処理し、単一の台帳を維持するバリデータの集合です。Solanaには、それぞれ特定の目的を持つ複数の独立したクラスターがあります。
- Localhost:デフォルトのポート8899で動作するローカル開発クラスターです。Solanaコマンドラインインターフェース(CLI)には組み込みのテストバリデータが含まれており、エアドロップやレート制限を必要とせず、各開発者のニーズに合わせてカスタマイズできます
- Devnet:Solana上でのテストや実験に使用できる、実害のないサンドボックス環境です
- Testnet:Solanaのコアコントリビューターが、新しいアップデートや機能をmainnetへの導入前に試すためのテスト環境です。パフォーマンステストを実施したい開発者にも利用されます
- Mainnet Beta:現実のトランザクションが行われる、稼働中のパーミッションレスなクラスターです。ユーザー、開発者、トークン保有者、バリデータが日々やり取りする「本物」のSolanaです
各クラスターは独立して動作し、互いの存在をまったく認識しません。各運用環境の整合性を確保するため、誤ったクラスターに送信されたトランザクションは拒否されます。
クラスターを、データからなるモノリシックなヒープとして想像してください。コンピューターサイエンスにおけるヒープとは、データを動的に保存および変更できるメモリ領域を指します。ただし、クラスターが文字どおりヒープデータ構造を使用しているわけではありません。この比喩は、クラスターが必要に応じて割り当てと解放が可能なさまざまなメモリ領域で構成されていることを理解するための概念的な手段です。クラスターを動的なヒープとして捉えることは、ネットワーク内でデータがどのように管理、アクセス、保護されるかを理解するうえで重要です。
このモノリシックなデータのヒープは、ある種のデジタル倉庫とも考えられます。ここでは、データは棚に置かれた箱のようなもので、それぞれに一意のラベルがあり、移動や内容変更に関する固有のルールが設定されています。これにより、許可された移動や変更のみが認められる、安全で秩序あるシステムが実現します。
Solanaでプログラムと呼ばれるスマートコントラクトには、管理可能な独自の倉庫領域、つまりヒープ領域が割り当てられます。プログラムは倉庫内のどの領域からでも読み取れますが、所有していない領域の内容を変更するには、一定の権限が必要です。例外として、Solanaのネイティブ暗号資産であるlamportを倉庫内の任意の領域へ転送する操作は、常に許可されます。
プログラムを含むすべての状態が、このヒープ内に存在します。各領域にはそれを所有し、適切に管理するプログラムがあります。たとえばプログラムは、オンチェーンプログラムの読み込み、デプロイ、アップグレードを担うプログラムであるBPFLoaderによって所有されます。こうしたメモリ領域、つまりデジタル倉庫の箱をアカウントと呼びます。
アカウントとは何ですか?
Solana上のすべてはアカウントです。アカウントは、コンピューター上のファイルのようにデータを永続的に保持するコンテナだと考えてください。アカウントはSolanaのプログラムモデルを構成する基本要素であり、状態(アカウントの残高、所有権情報、プログラムを保持しているかどうか、rent情報など)の保存に使用されます。
Solanaには、次の3種類のアカウントがあります。
- データを保存するアカウント
- 実行可能なプログラムを保存するアカウント
- ネイティブプログラムを保存するアカウント
これらのアカウントは、その機能に基づいてさらに次のように分類できます。
- 実行可能アカウント - コードを実行できるアカウントです
- 実行不可能アカウント - コードを実行する機能を持たず、データ保存に使用されるアカウントです(コードを保持していないためです)
上の図には、実行可能アカウントと実行不可能アカウントの例がいくつか示されています。実行可能アカウントでは、Bubblegumがプログラムアカウントの一例です。これは、圧縮NFTの作成と管理に使用されるMetaplexのプログラムです。Vote Programは、ネイティブプログラムアカウントの一例です。バリデータの投票状態と報酬を追跡するアカウントの作成と管理に使用されます。プログラムアカウントとネイティブプログラムアカウントの違いについては、プログラムとは何ですか?のセクションで説明します。 現時点では、Solanaには複数の種類の実行可能アカウントがあることを理解しておくことが重要です。
また、すべての実行不可能アカウントはデータアカウントに分類できます。データアカウントの例は次のとおりです。
- Associated Token Account - 特定のトークン、その残高、所有者に関する情報を保持するアカウントです(例:Aliceが10 USDCを保有)
- System Account - System Programによって作成、所有されるアカウントです
- Stake Account - 報酬を獲得できる可能性があるバリデータへトークンを委任するために使用されるアカウントです
アカウントの構造
アカウントはAccountInfo structに従って構成されます。
pub struct AccountInfo<'a> {
pub key: &'a Pubkey,
pub lamports: Rc>,
pub data: Rc>,
pub owner: &'a Pubkey,
pub rent_epoch: Epoch,
pub is_signer: bool,
pub is_writable: bool,
pub executable: bool,
}アカウントは、一意の32バイト公開鍵であるアドレス(key)によって識別されます。
lamportsフィールドには、このアカウントが所有するlamportの数が格納されます。1 lamportは、SolanaのネイティブトークンであるSOLの10億分の1です。
dataは、このアカウントに保存される生のデータバイト配列を指します。デジタル資産のメタデータからトークン残高まで、あらゆるものを保存でき、プログラムによる変更が可能です。
ownerフィールドには、プログラムアカウントのアドレスで表される、このアカウントの所有者が格納されます。アカウントの所有権には、いくつかのルールがあります。
- アカウントのデータを変更し、lamportを引き出せるのは、そのアカウントの所有者だけです
- 誰でもアカウントにlamportを入金できます
- アカウントのデータがゼロにリセットされていれば、アカウントの所有者は所有権を新しい所有者へ移転できます
is_signerフィールドは、対象アカウントの所有者がトランザクションに署名したかどうかを示すbooleanです。つまり、トランザクションに関与するプログラムに対して、そのアカウントが署名者であるかを示します。署名者であるとは、そのアカウントが公開鍵に対応する秘密鍵を保持し、提案されたトランザクションを承認する権限を持つことを意味します。
is_writableフィールドは、アカウントのデータを変更できるかどうかを示すbooleanです。Solanaでは、並列処理を可能にするため、トランザクション内でアカウントを読み取り専用として指定できます。ランタイムは、異なるプログラムによる読み取り専用アカウントへの同時アクセスを許可する一方、書き込み可能なアカウントへの潜在的な書き込み競合を、トランザクションの処理順序によって制御します。これにより、競合しないトランザクションだけが並列処理されます。
executableフィールドは、アカウントが命令を処理できるかどうかを示すbooleanです。つまり、プログラムはアカウント内に保存されています。これについては次のセクションで詳しく説明します。その前に、rentの概念を取り上げる必要があります。
rent_epochフィールドは、このアカウントに次回rentが発生するepochを示します。epochとは、リーダースケジュールが有効なslot数です。従来のオペレーティングシステム上のファイルとは異なり、Solanaのアカウントにはlamport量で表される寿命があります。アカウントが存続できるかどうかはlamport残高に左右されるという考え方が、rentの概念につながります。
Rent
Rentは、Solana上でアカウントを存続させ、バリデータのメモリ内に保持するために発生するストレージコストです。Rentの徴収は、リーダースケジュールが有効なslotによって定義される時間単位であるepochに基づいて評価されます。Rentの仕組みは次のとおりです。
- Rentの徴収 - rentはepochごとに1回徴収されます。トランザクションからアカウントが参照されたときにも徴収される場合があります
- Rentの分配 - 徴収されたrentの一部はバーンされ、流通から永久に取り除かれます。残りは各slotの終了後に投票アカウントへ分配されます
- Rentの支払い - アカウントにrentを支払うための十分なlamportがない場合、ガベージコレクションと呼ばれる処理によってデータが削除され、アカウントの割り当てが解除されます
- Rent免除 - 2年分のrent支払額に相当する最低残高を維持すると、アカウントはrent免除になります。すべての新規アカウントは、アカウントサイズに応じたrent免除の基準額を満たす必要があります
- Rentの回収 - ユーザーはアカウントを閉じることで、残っているlamportを回収できます。これにより、アカウントに保存されているrentを取り戻せます
特定のアカウントサイズに必要なrentは、getMinimumBalanceForRentExemption RPCエンドポイントで見積もれます。Test Driveでは、アカウントのデータ長をusizeで受け取ることで、この処理を簡素化しています。Solana rent CLIサブコマンドを使用して、アカウントがrent免除になるために必要な最低SOL額を見積もることもできます。たとえば、この記事の執筆時点では、コマンドsolana rent 20000を実行すると、Rent-exempt minimum: 0.14009088 SOLが返されます。
Solanaのアドレス
Solanaには、実際には2つの「種類」のアドレスがあります。Solanaでは、アドレスの作成にSHA-512(SHA-2)とCurve22519楕円曲線を使用するEdDSA署名方式であるed25519を採用しています。これによって生成される32バイトの公開鍵が、主要なアドレス形式として機能します。ハッシュ化されていないため、そのまま使用できます。
有効なアドレスであるためには、ed25519曲線上の点でなければなりません。ただし、すべてのアドレスをこの曲線から導出する必要はありません。Program Derived Address(PDA)は曲線外で生成されるため、対応する秘密鍵を持たず、署名には使用できません。PDAはSystem Programを介して作成され、プログラムがアカウントを管理する必要がある場合に使用されます。ここでは補足として、Solanaに異なる種類のアドレスが存在することを読者に知っていただくことだけが目的です。PDAについては今後の記事で説明します。
SolanaのアカウントはEthereumのアカウントとどう違いますか?
Ethereumには、外部所有アカウント(EOA)とコントラクトアカウントという2つの主要なアカウント種別があります。EOAは秘密鍵によって制御されますが、コントラクトアカウントはコントラクトコードによって管理され、単独でトランザクションを開始することはできません。
EOAとコントラクトアカウントは、どちらも同じアカウント構造に従います。
- 残高 - すべてのアカウントにはEther単位で測定される残高があります
- Nonce - EOAでは、そのアカウントから送信されたトランザクション数です。コントラクトでは、そのアカウントが作成したコントラクト数です
- Storage Root - アカウントのストレージ内容をエンコードしたMerkle Patricia Trieのルートノードに対する256ビットのハッシュです
- CodeHash - コントラクトのEthereum Virtual Machine(EVM)コードのハッシュです。これは不変であり、状態は変更できても、一度作成されたコードは変更されません。ただし、プロキシパターンの使用など、Ethereum上のコントラクトをアップグレードする方法には例外がありますが、本記事の範囲外です。EOAにはコードが含まれないため、これは空文字列のハッシュになります
Solanaは、どのアカウントもプログラムになり得る、より統一的なアカウントモデルを採用しています。コードとデータを分離することで、より効率的かつ柔軟な環境を実現します。Solanaプログラムはステートレスであり、重複してデプロイすることなく、さまざまなデータアカウントとやり取りできます。これは、異なるプログラム間で資産を移動せずに複数のプロトコルを利用したいユーザーにとって、分散型金融(DeFi)アプリケーションで特に有利です。これに対し、Ethereumのプログラミングモデルはコードと状態を単一のエンティティにまとめます。そのため操作が複雑になり、状態変更に必要なgasによってコストが高くなる可能性があります。
以前のSolanaアカウントにはrentの支払いが必要で、アクティブな状態を維持するには最低残高を保持する必要がありました。これにより、使用されていないアカウントや資金不足のアカウントが最終的にネットワークによって回収され、状態の肥大化が抑えられます。最近のアップデートにより、mainnetにはrentを支払うアカウントが存在しなくなり、すべてのアカウントがrent免除でなければならなくなりました。一方、Ethereumではリソース割り当ての管理にgasを使用します。このモデルでは、コントラクトのストレージは明示的に消去されない限り無期限に存続します。Solanaのアプローチでは状態保存のコスト構造をより予測しやすい一方、Ethereumのコストは変動し、ネットワーク混雑時には非常に高額になる場合があります。
次のセクションでは、Solanaがプログラムロジックと状態をどのように分離しているかを確認します。Ethereumのプログラミングモデルと比較しながら、このモジュール型のアプローチが、開発者に透明で予測可能なコスト構造を提供しつつ、より効率的なオンチェーン操作を実現する仕組みを説明します。
Solanaプログラムとは何ですか?
プログラムは、BPF Loaderが所有する実行可能アカウントです。トランザクションとプログラムロジックを処理するよう設計されたSolana Runtimeによって実行されます。
Solanaのプログラミングモデルを特徴づける要素の1つが、コードとデータの分離です。プログラムはステートレスであり、内部に状態を保存しません。代わりに、処理に必要なすべてのデータは別のアカウントに保存され、トランザクションを介して参照としてプログラムに渡されます。この設計により、汎用的なプログラムを一度デプロイするだけで、異なるアカウントとやり取りできます。
Solana上のプログラムには、次の機能があります。
- 追加のアカウントを所有する
- 他のアカウントから読み取る、または他のアカウントに入金する
- 所有するアカウントのデータを変更する、または残高を引き落とす
プログラムには2つの種類があります。
- オンチェーンプログラム - ユーザーが作成し、Solana上にデプロイするプログラムです。通常はプログラムをデプロイしたアカウントであるアップグレード権限によって、アップグレードできます
- ネイティブプログラム - Solanaのコアに統合されたプログラムです。バリデータの動作に必要な基本機能を提供します。ネイティブプログラムは、ネットワーク全体のソフトウェアアップデートを通じてのみアップグレードできます。一般的な例として、System Program、BPF Loader Program、Vote Programがあります。
オンチェーンプログラムとネイティブプログラムは、どちらもユーザーや他のプログラムから呼び出せます。主な違いはアップグレードの仕組みにあります。オンチェーンプログラムはアップグレード権限によってアップグレードできますが、ネイティブプログラムはクラスターのアップデートの一部としてのみアップグレードできます。
Solana Labsは、Solana Program Libraryと呼ばれる厳選されたオンチェーンプログラム群を管理しています。このライブラリは、トークンの貸し出しやステークプールの作成を含む、さまざまなオンチェーン操作を実現します。たとえばAssociated Token Account Programは、ユーザーのウォレットを対応するトークンアカウントに関連付けるための標準と仕組みを定めています。また、SPLは動的に発展しています。Token-2022などのプログラムは、Token Programが提供する機能を基盤として拡張しています。
Solanaでのプログラム開発は通常、定型コードを減らし、シリアライズとデシリアライズを効率化してプログラム作成を簡素化する、規約重視のフレームワークAnchorを利用し、Rustで行われます。Rustが推奨されますが、それに限定されるわけではありません。C、C++、およびLLVMのBPFバックエンド(プログラムをBPFバイトコードへコンパイルできるLLVMのコンポーネント)をターゲットにする任意の言語を使用できます。SolangとNeon Labsの最近の進展により、開発者はプログラム開発でSolidityも使用できるようになりました。
プログラムは通常、LocalhostとDevnetで開発およびテストしてから、TestnetまたはMainnet Betaへデプロイします。開発者は、Solana CLIでsolana program deploy <path to program>コマンドを使用してプログラムをデプロイできます。プログラムは、BPFバイトコードを含むELF共有オブジェクトにコンパイルされた後、指定されたSolanaクラスターへアップロードされます。デプロイ済みプログラムはexecutableとマークされたアカウントに格納され、そのアカウントのアドレスがprogram_idとして機能します。
当初、Solana上のプログラムは、プログラムサイズの2倍のアカウントを使用してデプロイされていました。Solanaの1.16アップデートでは、サイズ変更可能なアカウントのサポートが導入されました。これにより、開発者の柔軟性とリソース割り当てが向上しました。現在では、より小さいアカウントでプログラムをデプロイし、後からサイズを拡張できます。
前述のとおり、プログラムがやり取りするデータはすべて、参照として渡される別のアカウントに保存されるため、プログラムはステートレスと見なされます。すべてのプログラムには命令処理を行う単一のエントリーポイントがあり、program_id、アカウントの配列、バイト配列形式の命令データを受け取ります。プログラムは、トランザクションから呼び出されるとSolana Runtimeによって実行されます。
トランザクションとは何ですか?
トランザクションはオンチェーン活動の基盤です。プログラムを呼び出し、状態変更を実行するための仕組みとして機能します。Solanaのトランザクションは、どのアカウントに対してどの操作を実行すべきか、またその操作に必要な権限があるかをバリデータに伝える命令の集合です。
トランザクションは、主に3つの部分で構成されます。
- 読み取りまたは書き込み対象となるアカウントの配列
- 1つ以上の命令
- 1つ以上の署名
SolanaのトランザクションはTransaction structに従います。これにより、ネットワークが操作を処理および検証するために必要な情報が提供されます。定義は次のとおりです。
pub struct Transaction {
pub signatures: Vec,
pub message: Message,
}signaturesフィールドには、シリアライズされたMessageに対応する署名の集合が含まれます。各署名は、手数料支払者を先頭として、Messageのaccount_keysリスト内のアカウントキーに関連付けられます。手数料支払者は、トランザクション処理時に発生するトランザクション手数料を負担するアカウントです。通常は、トランザクションを開始したアカウントです。必要な署名数は、メッセージのMessageHeaderで定義されるnum_required_signaturesと等しくなります。
message自体は、Message型のstructです。次のように定義されます。
pub struct Message {
pub header: MessageHeader,
pub account_keys: Vec,
pub recent_blockhash: Hash,
pub instructions: Vec,
}メッセージのheaderには、3つの符号なし8ビット整数が含まれます。必要な署名数(つまりnum_required_signatures)、読み取り専用の署名者数、読み取り専用の非署名者数です。
account_keysフィールドには、トランザクションに関与するすべてのアカウントアドレスが列挙されます。読み書きアクセスを要求するアカウントが先に配置され、その後に読み取り専用アカウントが続きます。
recent_blockhashは、32バイトのSHA-256ハッシュを含む最近のblockhashです。これはクライアントが最後に台帳を確認した時点を示すために必要であり、最近のトランザクションの有効期限として機能します。バリデータは古いblockhashを持つトランザクションを拒否します。また、最近のblockhashを含めることで、以前のトランザクションと完全に同一のトランザクションが拒否されるため、重複トランザクションの防止にも役立ちます。何らかの理由で、ネットワークへ送信するかなり前にトランザクションへ署名する必要がある場合は、最近のblockhashの代わりに永続的なトランザクションnonceを使用し、一意のトランザクションであることを保証できます。
instructionsフィールドには、1つ以上のCompiledInstruction structが含まれ、それぞれがネットワークのバリデータが実行すべき特定の操作を定めます。
命令
命令は、Solanaプログラムを1回呼び出すための指示です。プログラムにおける実行ロジックの最小単位であり、Solana上の最も基本的な操作単位として機能します。プログラムは命令から渡されたデータを解釈し、指定されたアカウントを操作します。Instruction structは次のように定義されます。
pub struct Instruction {
pub program_id: Pubkey,
pub accounts: Vec,
pub data: Vec,
}program_idフィールドは、実行するプログラムの公開鍵を指定します。これは命令を処理するプログラムのアドレスです。この公開鍵で示されるプログラムアカウントの所有者が、プログラムの初期化と実行を担うローダーを指定します。ローダーは、デプロイ後にオンチェーンのSolana Bytecode Format(SBF)プログラムを実行可能としてマークします。Solanaのランタイムは、実行可能としてマークされていないアカウントを呼び出そうとするトランザクションを拒否します。
accountsフィールドには、命令が読み取りまたは書き込みを行う可能性のあるアカウントが列挙されます。これらのアカウントはAccountMeta値として渡す必要があります。命令によってデータが変更される可能性があるアカウントは、書き込み可能として指定しなければトランザクションが失敗します。これは、プログラムが所有していない、または必要な権限を持たないアカウントへ書き込めないためです。アカウントのlamportを変更する場合も同様です。プログラムが所有していないアカウントからlamportを差し引くとトランザクションは失敗しますが、任意のアカウントへのlamportの追加は許可されます。accountsフィールドには、プログラムが読み書きしないアカウントも指定できます。これはランタイムによるプログラム実行のスケジューリングに影響を与えるために行われますが、それ以外の場合、これらのアカウントは無視されます。
dataは、プログラムへ渡される入力として機能する汎用的な符号なし8ビット整数のベクターです。このフィールドにはプログラムが実行するエンコード済み命令が含まれるため、極めて重要です。
Solanaは命令データの形式に依存しません。ただし、bincodeとborsh(Binary Object Representation Serializer for Hashing)を通じて、シリアライズを標準でサポートしています。シリアライズとは、複雑なデータ構造を、送信または保存可能なフラットなバイト列へ変換する処理です。デコードはすべてオンチェーンで行われるため、データのエンコード方式を選ぶ際には、そのオーバーヘッドを考慮する必要があります。Borshシリアライズは、安定した仕様とJavaScript実装があり、一般的に効率も高いため、bincodeより好まれることがよくあります。
プログラムは、サポートする命令の構築を効率化するためにヘルパー関数を使用します。たとえば、System ProgramはSystemInstruction::Assign命令を構築するヘルパー関数を提供します。
pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction {
let account_metas = vec![AccountMeta::new(*pubkey, true)];
Instruction::new(
system_program::id(),
&SystemInstruction::Assign { owner: *owner },
account_metas,
)
}この関数は、処理されると指定されたアカウントの所有者を、指定された新しい所有者へ変更する命令を構築します。
1つのトランザクションには複数の命令を含めることができ、列挙された順序で逐次的かつアトミックに実行されます。つまり、すべての命令が成功するか、1つも実行されないかのどちらかです。また、命令の順序が極めて重要になる場合もあります。潜在的な悪用を防ぐため、プログラムは考えられるあらゆる命令の並びを安全に処理できるよう堅牢化する必要があります。
たとえば、初期化解除時に、プログラムがアカウントのlamport残高をゼロに設定して、そのアカウントを初期化解除しようとする場合があります。これは、Solanaランタイムがアカウントを削除するという前提に基づきます。この前提はトランザクション間では有効ですが、命令間やCross-Program Invocation間では無効です(Cross-Program Invocationについては今後の記事で説明します)。初期化解除処理に潜むこの欠陥への耐性を高めるため、プログラムはアカウントのデータを明示的にゼロクリアする必要があります。そうしないと、攻撃者が後続の命令を発行し、トランザクションの完了前にアカウントを再利用するなど、削除済みという誤った前提を悪用できる可能性があります。
バージョン付きトランザクションとは何ですか?
Solanaのトランザクションでは、クラスター全体で高速かつ信頼性の高いデータ転送を保証するため、IPv6の最大伝送単位(MTU)標準を使用します。Solanaのネットワークスタックでは、保守的な1280バイトのMTUサイズを採用しています。ヘッダー用の領域を確保すると、パケットデータには1232バイトを使用できます。そのため、Solanaのトランザクションはこのサイズに制限されます。
このサイズ制約はさまざまなネットワーク強化を可能にする一方、単一のトランザクションで実行できる操作の複雑さを制限します。各アカウントアドレスが32バイトのストレージを占有するため、命令がない場合でも、1つのトランザクションに保存できるアカウントは最大35個です。この制約は、単一のトランザクション内で署名不要のアカウントを35個より多く必要とするユースケースに課題をもたらします。
この問題に対処するため、複数バージョンのトランザクション形式をサポートできる新しい形式が導入されました。現在、Solanaランタイムは2つのトランザクションバージョンをサポートしています。
legacy- 元のトランザクション形式です0(Version 0) - Address Lookup Tableのサポートを含む最新のトランザクション形式です
Version 0は、Address Lookup Table(ALT)をサポートするためにリリースされました。基本的には、アカウントアドレスをオンチェーンの表形式データ構造に保存します。これらのテーブルは、アカウントアドレスを保存する独立したアカウントであり、トランザクション内では1バイトのu8インデックスを使用して参照できます。含まれる各アカウントに32バイトではなく1バイトしか必要としないため、トランザクションのサイズが大幅に縮小されます。ALTは、DeFiアプリケーションで一般的な、多数のアカウントが関与する複雑な操作に特に有用です。
この図は、Solana Cookbookのバージョン付きトランザクションに関するセクションを基に作成されています
「バージョン付きトランザクション」という用語は、SolanaがlegacyとVersion 0の両方のトランザクション形式をサポートする方法を指します。このアプローチにより、ランタイムの機能強化を取り入れながら、コンポーザビリティを確保できます。
バージョン付きトランザクションの構造
VersionedTransactionは次のように定義されます。
pub struct VersionedTransaction {
pub signatures: Vec,
pub message: VersionedMessage,
}signaturesフィールドは、トランザクションの署名者による署名のリストです。これらはトランザクションを認証し、その整合性を維持する役割を果たします。messageは、トランザクションの実際の内容です。これは、legacyとVersion 0の両方のメッセージを処理する薄いenumラッパーであるVersionedMessage型によってカプセル化されます。
pub enum VersionedMessage {
Legacy(Message),
V0(Message),
}メッセージのバージョンは、シリアライズ処理の最初のビットによって決まります。最初のビットがセットされている場合、残りの7ビットを使用して、Version 0から始まるどのMessageバージョンがシリアライズされているかを判断します。最初のビットがセットされていない場合、すべてのバイトを使用してlegacyのMessage形式をエンコードします。これは、同じMessageという名前のstructが2つ存在するものの、legacyとv0という異なるモジュールに分けられているためです。
Messageは、トランザクションを圧縮した内部形式を表します。ネットワーク転送とランタイムによる操作に使用されます。トランザクションの命令で使用されるすべてのアカウントの線形リスト、アカウント配列の構造を詳述するMessageHeader、最近のblockhash、メッセージの命令をコンパクトにエンコードしたデータが含まれます。v0のMessage structの構造は次のとおりです。
pub struct Message {
pub header: MessageHeader,
pub account_keys: Vec,
pub recent_blockhash: Hash,
pub instructions: Vec,
pub address_table_lookups: Vec,
}legacyメッセージとv0メッセージの違いは、address_table_lookupsフィールドが含まれることです。
プログラミングモデルとSolanaのトランザクションフローの統合
Solanaのプログラミングモデルは、アカウントおよびトランザクションシステムと密接に統合されています。各概念は次のように結びついています。
- 状態としてのアカウント - Solanaのアカウントは、プログラムの状態コンテナとして機能します。プログラミングモデルは、命令に応じてこれらのコンテナに保存されたデータを変更することを中心に構成されています
- 命令 - プログラムは、トランザクション内に含まれる命令を処理するためのロジックを定義します。これらの命令は、アカウントデータとやり取りする実行可能な構成要素です
- シリアライズと処理 - トランザクションがシリアライズされる際、プログラムの命令によってアカウント状態の変更内容が決まります。シリアライズ処理は、legacyまたはVersion 0のどちらのトランザクション形式を使用する場合でも、プログラムの設計に従います
- アトミック性 - Solanaのプログラミングモデルは、命令処理のアトミック性を保証します。プログラムは、同時実行されるトランザクションを安全かつ効率的に処理できるよう設計する必要があります
- スケーラビリティ - Solanaのプログラミングモデルは、Address Lookup Table(ALT)などの機能によってスケーラビリティを実現します。これらのテーブルはトランザクションのサイズを縮小し、トランザクションが参照できるアカウント数を増やします
Solanaのプログラミングモデルは、単にコードを書くことだけではありません。そのコードがより広範なエコシステム内でどのように相互作用するかを理解することが重要です。アカウントはこのモデルに不可欠であり、ネットワーク上でデータを保存および変更する主要な手段として機能します。トランザクションは、作成、更新、削除すべきデータをバリデータに伝えることで、オンチェーン活動を実現します。Solanaエコシステム内でパフォーマンスと連携性を最適化したアプリケーションを構築するには、開発者がこれらの要素を深く理解することが不可欠です。
まとめ
お疲れさまでした。この記事では、Solanaのシステムアーキテクチャの複雑さをたどり、クラスターをモノリシックなデータのヒープとして捉える概念を掘り下げました。このヒープがアカウントと呼ばれる個別のメモリ領域に編成され、Solanaのプログラミングモデルの基盤を形成する仕組みを確認しました。アカウントには、ユーザーのトークンからネットワークの動作を定義するプログラムそのものまで、あらゆるものが保存され、すべてトランザクションを介して変更されます。
開発者にとって、Solanaの分散コンピューティングへのアプローチを理解することは極めて重要です。Solanaの機能を最大限に活用するアプリケーションを構築するには、アカウント、プログラム、トランザクションの仕組みを把握する必要があります。重要なのは、コードと状態が分離されたシステムを理解することです。その結果、これまでにない規模のコンポーザビリティとアップグレード可能性を備え、アカウントを介してデータとやり取りするステートレスなプログラムが実現します。
投資家と一般ユーザーのどちらにとっても、Solanaの設計が堅牢で柔軟かつ効率的なエコシステムをどのように生み出すかを理解することは、プラットフォームの実現可能性と、Solanaでしか実現できない革新的なアプリケーションを育む能力を評価するうえで重要です。
匿名の皆さん、ここまで読んでいただきありがとうございます。さらに深く学ぶ準備はできましたか?今すぐDiscordに参加して、Solanaプログラミングを始めましょう。
その他のリソース/参考資料
関連記事
Heliusを購読
Solana開発の最新情報や新しい記事の公開通知を受け取れます


