導入
Solana では、「プログラム」も「そのプログラムが扱うデータ」も、どちらも同じ入れ物 ―― アカウント に格納されています。この「すべてはアカウント」という発想が、Solana のアーキテクチャ全体の出発点です。
説明
Solana 上のあらゆる情報は アカウント という単位で保存されます。アカウントには一意のアドレス(公開鍵)があり、そこに次のような情報が紐づいています。
- 残高(SOL、正確には lamport)
- 中身のデータ(バイト列)
- そのデータを実行可能なコードとして扱うかどうかのフラグ(
executable) - そのアカウントを管理する owner(次のレッスンで扱います)
プログラム(スマートコントラクト)は、executable: true が立った特別なアカウントとして保存されます。ここには実行可能なバイトコードが入っていますが、そのプログラムが使うデータそのものは、プログラムのアカウントには入りません。データは、プログラムとは別の「データアカウント」に置かれ、プログラムはそれを読み書きしにいく、という関係になっています。
classDiagram
class SolanaProgramAccount {
+pubkey Address
+executable true
+data バイトコード(コンパイル済みプログラム)
}
class SolanaDataAccount {
+pubkey Address
+owner ProgramAddress
+data 状態データ(バイト列)
+lamports 残高
}
class EthereumContract {
+address Address
+code バイトコード(EVM)
+storage 状態データ(内蔵)
+balance ETH残高
}
SolanaProgramAccount ..> SolanaDataAccount : 命令(instruction)経由で読み書き
Ethereum のコントラクトと比べると、この違いがはっきりします。Ethereum では「コントラクトのアドレス」1つに、コード(code)とストレージ(storage)が両方とも内蔵されています。一方 Solana では、プログラムのコードを持つアカウントと、状態を持つデータアカウントが完全に分離しています。
この分離には利点があります。同じ1つのプログラム(例えばトークンを扱う共通のプログラム)を、無数の異なるデータアカウント(それぞれのユーザーのトークン残高など)に対して使い回せるのです。プログラムのコードを何度も複製する必要がなく、「ロジックは1つ、データはユーザーの数だけ」という構成が自然に作れます。
// Solanaプログラム側のイメージ(Rust)
// プログラム自体はロジックのみを持ち、状態は引数で渡される「データアカウント」に読み書きする
pub struct CounterAccount {
pub count: u64, // データアカウントの中身の例
}
pub fn increment(account_data: &mut CounterAccount) {
account_data.count += 1;
}
演習
同じ「カウンターを増やすプログラム」を、100人のユーザーがそれぞれ自分のカウンターとして使いたいとします。Solana のアカウントモデルでは、プログラムのアカウントとデータアカウントはそれぞれいくつ必要になるでしょうか。
ヒント1を見る
プログラム(ロジック)は全ユーザー共通で使い回せます。
ヒント2を見る
データ(カウンターの現在値)はユーザーごとに別々に持つ必要があります。