Solanaを理解する上で重要な用語5つを噛み砕いてみる
✨ Solana の概要
Solana はオープンソースのパブリックチェーンです。
スケーリング問題を解決するブロックチェーンとして 2017 年に開発され、スマートコントラクト・NFT・dApps などに対応しています。
Solana が開発者に愛されるチェーンとなった理由としては、
- 高速なトランザクションスループット
- 65,000 TPS(Transaction Per Second) の理論値を持つ
- 現状は 2,700 TPS(2022/4/23 時点)なので、まだまだスケール可能
- トランザクションコストがほぼ 0
ネットワーク 平均トランザクションコスト(2022/4/23 時点) Bitcoin $131.69 Ethereum $2.6 Solana $0.00025 - この手数料の安さから『VISA に取って代わる決済システムになる』との期待
- Rust の人気上昇
- Solana の
Program
(スマートコントラクト)は Rust で実装される - Rust は難易度が高く将来が期待できる言語であるため、情報感度の高い熟練したエンジニアを中心とした開発コミュニティを形成できた
- Solana の
このように Solana は将来を期待できるチェーンとして多くの開発者を集めています。
💬 Solana における重要用語
Solana における用語は、Ethereum などで使用される意味とは異なることが多いです。
そんな少しトリッキーながら Solana でよく使用される重要用語を私なりに噛み砕いて説明したいと思います。
(記事内容は GitHub で管理していますので、修正リクエストなどは受け付けています!)
Account
A record in the Solana ledger that either holds data or is an executable program.
Like an account at a traditional bank, a Solana account may hold funds called lamports. Like a file in Linux, it is addressable by a key, often referred to as a public key or pubkey.(Solana 公式ドキュメント
terminology
より引用)
Solana における Account(アカウント)は 2 つに大別できます。
- Executable Account
- Program(コントラクトロジック) を実行できるアカウント
- 変更することができない
- Non-executable Account
- いわゆる State を管理するアカウント
- Owner だけがステートを変更できる
- Wallet 残高・lamports など、変更可能なデータなどを保存
Executable Account は実行ロジックをバイトコードとして持っているものになります。
(Ethereum の EVM でいう Contract Account に近いイメージですね)
そしてこの Executable Account でのコントラクトを実行できるのは、そのowner
のみとなっています。
このアカウントはデプロイ後に中身を変更することができないので、完全に実行ロジックだけに責任を持つのです。
Non-executable Accountはステートを管理することができるアカウントになります。
つまり、スマートコントラクトを実行してウォレット残高を増減させるような場合には Non-executable Account
内のステートを変更する必要があります。
そこで Executable Account が Non-executable Account のオーナーになることによって、そのステートを管理するわけですね。
Solana-Twitter での例
https://lorisleiva.com/ に素晴らしい例があったので引用
こちらは Solana 版 Twitter の図解です。
-
Solana-Twitter Program
という Executable なアカウントがデプロイされる - ↑ はデフォルトで
System Program
という Solana 公式が持つアカウントが Owner となる - 各 Tweet や User は Non-executable なアカウントとしてステートを持つ
- Owner である Executable Account によって
Tweet
やUser
のステートが変更されていくアカウント名 種類 Solana-Twitter Program Executable Account Tweet Non-executable Account User Non-executable Account
つまり Solana チェーンでは、すべての要素がアカウントとして存在するのです。
これは他のブロックチェーンと異なった思想でしょう。
ソースコード
ソースコードを見てみましょう。
Account 構造体のフィールドをざっくり説明すると、
フィールド | 説明 |
---|---|
lamports | SOL の最小単位(ETH でいう wei)がいくらストアされているか |
data | アカウントに含まれるデータ |
owner | アカウントのオーナーの公開鍵 |
executable | 実行可能かどうか |
rent_epoch | アカウントが Rent(家賃)を払う期限 |
補足としては、
- Solana の通貨で使用される最小単位は
lamport
といい、1lamport = 0.000000001 SOL - 各アカウントは
Rent
という家賃を払う必要があるため、lamports や rent_epoch というフィールドがある
Rentに関しては後に説明するため、ひとまず Account についてはこのくらい分かれば十分でしょう。
Program
The code that interprets instructions.
(Solana 公式ドキュメント
terminology
より引用)
Program はコントラクトのロジックを表します。
(いわゆるスマートコントラクトですね)
先に『Solana では全てがアカウントとして存在する』と言いましたが、Program もアカウントの一種です。
Account のうち、executable
フィールドが true になっているものが Program として認識されます。
- Account の作成
- SOL を転送する
- ステーキングする
- Vote をする
といった動作が Program によって実現されているというわけですね!
Anchor での例
Anchor は Solana における Program とそのクライアントを開発するフレームワークの例です。
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
mod basic_0 {
use super::*;
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
チュートリアルで示されている例を見てみると、#[program]
のアトリビュートがついているものが Program だと分かります。
basic_0
という Program の中身に具体的な実装(RPC request handler)であるinitialize
メソッドを確認できます。
クライアント側からの呼び出し
// Read the generated IDL.
const idl = JSON.parse(
require("fs").readFileSync("./target/idl/basic_0.json", "utf8")
);
// Address of the deployed program.
const programId = new anchor.web3.PublicKey("<YOUR-PROGRAM-ID>");
// Generate the program client from IDL.
const program = new anchor.Program(idl, programId);
// Execute the RPC.
await program.rpc.initialize();
無事に Program がデプロイされるとクライアントからinitialize
メソッドを呼び出すことができるわけですね。
Instruction
The smallest contiguous unit of execution logic in a program.
An instruction specifies which program it is calling, which accounts it wants to read or modify, and additional data that serves as auxiliary input to the program.
A client can include one or multiple instructions in a transaction. An instruction may contain one or more cross-program invocations.(Solana 公式ドキュメント
terminology
より引用)
Instruction(命令) は Program に含まれる実行ロジックの最小単位です。
Anchor での例
#[program]
mod basic_1 {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
Ok(())
}
pub fn update(ctx: Context<Update>, data: u64) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub my_account: Account<'info, MyAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'info> {
#[account(mut)]
pub my_account: Account<'info, MyAccount>,
}
#[account]
pub struct MyAccount {
pub data: u64,
}
こちらの例でいうinitialize
やupdate
は Instruction になります。
Program の中に実装されているため、Instrution はどの Program が呼び出されているのかを内部から知ることができます。
そしてこれら Instruction が実行されると Account が読み込まれてステートが変更されます。
その証拠にInititalize
やUpdate
といった Account が定義され、引数のコンテキストに渡されていますよね。
クライアントからの呼び出し
// The program to execute.
const program = anchor.workspace.Basic1;
// The Account to create.
const myAccount = anchor.web3.Keypair.generate();
// Create the new account and initialize it with the program.
await program.rpc.initialize(new anchor.BN(1234), {
accounts: {
myAccount: myAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [myAccount],
});
そしてクライアントからも呼び出すことができます。
RPC request handler としてinitialize
メソッドが呼ばれていますよね。
ソースコード
ソースコードの Instrution 構造体を見てみましょう。
各フィールドを見てみると、
フィールド | 説明 |
---|---|
program_id | Program の公開鍵。どの Program なら正しくdata を解釈できるかを示す |
accounts | 参照される Account を並べたメタデータ |
data | Program に渡されるデータ。実行すべき操作などを含む(特定の Program だけが中身を解釈できる) |
つまりこれらをまとめると、
- Instrution は Program 内の実行ロジックの単位
-
program_id
が渡されるので、どの Program にデータを渡すべきか判別できる - コントラクトの実行により、ステートを管理する Account を操作することができる
- クライアントから RPC で実行可能
というわけです。かなり全体像が理解できてきましたよね!
Transaction
One or more instructions signed by a client using one or more keypairs and executed atomically with only two possible outcomes: success or failure.
(Solana 公式ドキュメント
terminology
より引用)
Transaction はコントラクトの全体像を示す用語です。
今までの用語を理解していれば、Transaction の流れがスッキリ分かりますよね。
Transaction flow throughout the network
Solana WhitePaperより引用
- クライアントが Transaction のリクエストをし、Solana のランタイムへ送られる
- Solana のランタイムによって Instruction が呼び出される
- Instruction はどの Program や Account が関与すべきかを判別し、データを適切な Program へ渡す
- Program は Instruction から受け取ったデータを読み込んでコントラクトを実行する
- Program はコントラクトの結果が
success
かerror
かを返す - コントラクトに関わった Account の
Owner
(所有者)は変更を承認するために署名を行う - 検証ノードが Transaction を承認する
ざっくりこのような流れで Transaction が行われます。
ソースコード
Transaction 構造体を見てみましょう。
各フィールドを見てみると、
フィールド | 説明 |
---|---|
signatures | Account が Transaction の結果を承認したことを伝える署名 |
message | Transaction の全体情報をコンパクトにまとめたもの |
このようにシンプルな構成になっています。
このmessage
フィールドを詳しく見るとMessageHeader
構造体が確認できると思いますが、そのなかのnum_required_signatures
というフィールドに示された数字とsignatures
の署名数は一致しなければなりません。
Rent
Since validators on the network need to maintain a working copy of this state in memory, the network charges a time-and-space based fee for this resource consumption, also known as Rent.
(Solana 公式ドキュメント
Rent
より引用)
Solana チェーンに Account を保持するためにはRentと呼ばれるコストがかかります。
Rent は名前の通り"家賃"であり、Solana 上で開発するには避けられない経費です。
なぜ Rent が存在するのか?
Rent が無いとどんな問題が起こりうるでしょうか?
- 将来起こりうる Transaction に備えて、ずっと Account をメモリスペースに保持しておかないといけない
- メモリスペースを確保し続けるのもコストがかかるよね...。
- ほとんど動いていない休眠 Account を残しておくのは無駄じゃない?
- 悪い開発者が Account を大量に作成して、メモリスペースを独占する可能性
- 運用に多大なコストをかけられてしまうかも...。
こうした問題に対応するために Rent というシステムが導入されました。
主となる目的は Solana ネットワークを構成するハードウェアの費用を賄うことです。
Account を存在させ続ける限りメモリスペースのコストがかかりますから、維持費としての Rent を払ってもらいます。
また、使わなくなった Account を削除するインセンティブにもなりますからリソースの無駄遣いも防げますよね。
Rent があることで悪質な開発者が Account を大量に作ることが防がれます。
Account を作るごとにコストがかかってしまいますから、ハッカーが少数派である限りネットワークが健全に保たれやすいです。
Rent が徴収されるタイミング
- トランザクションで Account が参照されたとき
- Epoch(Solana ネットワークで決まっている特定の周期)ごとに 1 回
通常はこの 2 つのタイミングに Rent が徴収されてしまいます。
しかし、2 年分の Rent を先に預けることで徴収が免除される(rent-exempt)ので、Account を作る際には rent-exempt 状態にするのが通常です。
(現状では新しく Account を作る際には rent-exempt にする必要があるようです)
まとめ
Solana で使用される用語は Bitcoin や Ethereum などで使用されるものと異なる部分が多いです。
(僕も最初に Solana で dApps を作った際にはアーキテクチャを理解するのに苦労しました)
理解する難易度が高い上に日本語のドキュメントや記事も少ないですから、僕の説明によって少しでも Solana 開発のイメージが深まれば嬉しいです。
Discussion