導入
どんな Rust プログラムにも main 関数という入り口があるように、Solana のプログラムにも決まった入り口があります。ただし呼ばれ方が特殊で、solana_program クレートが用意するマクロを使って登録します。ここを理解すれば、これから書くほぼすべてのプログラムの骨格が読めるようになります。
説明
最小構成のネイティブプログラムは、次のような形をしています。
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
msg,
};
// この1行で「process_instruction関数がこのプログラムの入り口だ」と登録する
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
msg!("Hello from my Solana program!");
Ok(())
}
entrypoint! は Rust のマクロで、Solana のランタイムがバイナリを呼び出すときに使う低レベルな入り口(メモリ上のデータの受け渡しなど)を自動生成し、私たちが書いた process_instruction 関数につないでくれます。私たちが意識するのは、この3つの引数だけです。
flowchart LR
subgraph runtime["Solanaランタイム(バリデータ側)"]
TX["トランザクション内の1つの命令"]
end
TX --> PI["program_id: &Pubkey<br/>(このプログラム自身のアドレス)"]
TX --> AC["accounts: &[AccountInfo]<br/>(この命令が触れるアカウント一覧)"]
TX --> ID["instruction_data: &[u8]<br/>(何をしてほしいかのバイト列)"]
PI --> F["process_instruction"]
AC --> F
ID --> F
program_id… 実行中のプログラム自身の アドレス。「自分は誰か」を知るために使います(後の章でセキュリティチェックにも使います)。accounts… このトランザクションの命令が指定した、読み書き対象のアカウント一覧。順序が重要で、次章で1つずつ取り出す方法を学びます。instruction_data… クライアント(アプリ側)が「何をしてほしいか」を詰め込んだ生のバイト列。「カウンタを+1して」のような命令の中身は、すべてこの一列のバイト列にエンコードされて渡ってきます。
このように、Solana のプログラムには 1つの入り口しかありません。「増やす」「減らす」「初期化する」のような複数の操作も、すべてこの process_instruction の中で instruction_data を見て分岐します。この分岐のさせ方は第5章で扱います。
演習
process_instruction の3つの引数(program_id / accounts / instruction_data)を、それぞれ「誰が」「何に対して」「何をするか」という日本語のフレーズに言い換えてみましょう。
ヒント1を見る
accounts は命令の対象(操作したいデータそのもの)です。
ヒント2を見る
instruction_data は「操作の種類とパラメータ」を表す、ただのバイト列だと考えるとイメージしやすいです。