Rustでのプログラム開発
Solanaプログラムは主にRustプログラミング言語を使用して開発されています。このページでは、Anchorフレームワークを使用せずにRustでSolanaプログラムを書くことに焦点を当てています。このアプローチは「ネイティブRust」プログラムを書くとも呼ばれています。
ネイティブRust開発は、開発者にSolanaプログラムに対する直接的な制御を提供します。ただし、このアプローチはAnchorフレームワークを使用する場合と比較して、より多くの手動セットアップとボイラープレートコードが必要です。このメソッドは以下のような開発者に推奨されます:
- プログラムロジックと最適化に対する細かい制御を求める方
- 上位レベルのフレームワークに移る前に基礎となる概念を学びたい方
初心者の方には、Anchorフレームワークから始めることをお勧めします。詳細については Anchorセクションをご覧ください。
前提条件
詳細なインストール手順については、 インストールページをご覧ください。
始める前に、以下がインストールされていることを確認してください:
- Rust: Solanaプログラムを構築するためのプログラミング言語。
- Solana CLI: Solana開発のためのコマンドラインツール。
はじめに
以下の例では、Rustで書かれた最初のSolanaプログラムを作成するための基本的なステップを説明します。「Hello, world!」をプログラムログに出力する最小限のプログラムを作成します。
新しいプログラムを作成する
まず、標準の cargo new
コマンドを --lib
フラグと共に使用して、新しいRustプロジェクトを作成します。
$cargo new hello_world --lib
プロジェクトディレクトリに移動します。デフォルトの src/lib.rs
と Cargo.toml
ファイルが表示されるはずです
$cd hello_world
Cargo.toml
の edition
フィールドを 2021
に更新してください。そうしないと、
プログラムをビルドする際にエラーが発生する可能性があります。
solana-programの依存関係を追加する
次に、solana-program
依存関係を追加します。これはSolanaプログラムを構築するために必要な最小限の依存関係です。
$cargo add solana-program@2.2.0
crate-typeを追加する
次に、以下のスニペットをCargo.toml
に追加します。
[lib]crate-type = ["cdylib", "lib"]
このコンフィグを含めないと、プログラムをビルドする際にtarget/deploy
ディレクトリが生成されません。
あなたのCargo.toml
ファイルは以下のようになるはずです:
[package]name = "hello_world"version = "0.1.0"edition = "2021"[lib]crate-type = ["cdylib", "lib"][dependencies]solana-program = "2.2.0"
プログラムコードを追加する
次に、src/lib.rs
の内容を以下のコードに置き換えます。これはプログラムが呼び出されたときに「Hello,
world!」をプログラムログに出力する最小限のSolanaプログラムです。
msg!
マクロはSolanaプログラムでメッセージをプログラムログに出力するために使用されます。
use solana_program::{account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,};entrypoint!(process_instruction);pub fn process_instruction(_program_id: &Pubkey,_accounts: &[AccountInfo],_instruction_data: &[u8],) -> ProgramResult {msg!("Hello, world!");Ok(())}
プログラムをビルドする
次に、cargo build-sbf
コマンドを使用してプログラムをビルドします。
$cargo build-sbf
このコマンドはtarget/deploy
ディレクトリを生成し、その中に2つの重要なファイルが含まれます:
.so
ファイル(例:hello_world.so
):これはネットワークに「スマートコントラクト」としてデプロイされるコンパイル済みのSolanaプログラムです。- keypairファイル(例:
hello_world-keypair.json
):このkeypairの公開鍵はプログラムをデプロイする際のプログラムIDとして使用されます。
プログラムIDを確認するには、ターミナルで以下のコマンドを実行します。このコマンドは指定されたファイルパスにあるkeypairの公開鍵を表示します:
$solana address -k ./target/deploy/hello_world-keypair.json
出力例:
4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
テスト依存関係の追加
次に、litesvm
クレートを使用してプログラムをテストします。以下の依存関係を
Cargo.toml
に追加してください。
$cargo add litesvm --dev$cargo add solana-sdk@2.2.0 --dev
プログラムのテスト
以下のテストをプログラムコードの下の src/lib.rs
に追加します。これはハローワールドプログラムを呼び出すテストモジュールです。
use solana_program::{account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,};entrypoint!(process_instruction);pub fn process_instruction(_program_id: &Pubkey,_accounts: &[AccountInfo],_instruction_data: &[u8],) -> ProgramResult {msg!("Hello, world!");Ok(())}#[cfg(test)]mod test {use litesvm::LiteSVM;use solana_sdk::{instruction::Instruction,message::Message,signature::{Keypair, Signer},transaction::Transaction,};#[test]fn test_hello_world() {// Create a new LiteSVM instancelet mut svm = LiteSVM::new();// Create a keypair for the transaction payerlet payer = Keypair::new();// Airdrop some lamports to the payersvm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap();// Load our programlet program_keypair = Keypair::new();let program_id = program_keypair.pubkey();svm.add_program_from_file(program_id, "target/deploy/hello_world.so").unwrap();// Create instruction with no accounts and no datalet instruction = Instruction {program_id,accounts: vec![],data: vec![],};// Create transactionlet message = Message::new(&[instruction], Some(&payer.pubkey()));let transaction = Transaction::new(&[&payer], message, svm.latest_blockhash());// Send transaction and verify it succeedslet result = svm.send_transaction(transaction);assert!(result.is_ok(), "Transaction should succeed");let logs = result.unwrap().logs;println!("Logs: {logs:#?}");}}
cargo test
コマンドを使用してテストを実行します。プログラムログには「Hello,
world!」と表示されます。
$cargo test -- --no-capture
出力例:
running 1 testLogs: ["Program 9528phXNvdWp5kkR4rgpoeZvR8ZWT5THVywK95YRprkk invoke [1]","Program log: Hello, world!","Program 9528phXNvdWp5kkR4rgpoeZvR8ZWT5THVywK95YRprkk consumed 211 of 200000 compute units","Program 9528phXNvdWp5kkR4rgpoeZvR8ZWT5THVywK95YRprkk success",]test test::test_hello_world ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s
プログラムのデプロイ
次に、プログラムをデプロイします。ローカルで開発する場合は、solana-test-validator
を使用できます。
まず、Solana CLIをローカルのSolanaクラスターを使用するように設定します。
$solana config set -ul
出力例:
Config File: /.config/solana/cli/config.ymlRPC URL: http://localhost:8899WebSocket URL: ws://localhost:8900/ (computed)Keypair Path: /.config/solana/id.jsonCommitment: confirmed
新しいターミナルを開き、solana-test-validators
コマンドを実行してローカルvalidatorを起動します。
$solana-test-validator
テストvalidatorが実行されている間に、別のターミナルで solana program deploy
コマンドを実行して、プログラムをローカルvalidatorにデプロイします。
$solana program deploy ./target/deploy/hello_world.so
出力例:
Program Id: 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMzSignature:5osMiNMiDZGM7L1e2tPHxU8wdB8gwG8fDnXLg5G7SbhwFz4dHshYgAijk4wSQL5cXiu8z1MMou5kLadAQuHp7ybH
プログラムIDとトランザクション署名はSolana Explorerで確認できます。
Solana Explorerのクラスターもlocalhostに設定する必要があります。Solana
Explorerの「Custom RPC URL」オプションのデフォルトは http://localhost:8899
です。
クライアント例の作成
次に、Rustクライアントを使用してプログラムを呼び出す方法を示します。
まず、examples
ディレクトリと client.rs
ファイルを作成します。
$mkdir -p examples && touch examples/client.rs
以下を Cargo.toml
に追加します。
[[example]]name = "client"path = "examples/client.rs"
solana-client
と tokio
の依存関係を追加します。
$cargo add solana-client@2.2.0 --dev$cargo add tokio --dev
クライアントの追加
以下のコードを examples/client.rs
に追加します。これはトランザクション手数料を支払うための新しいkeypairに資金を提供し、helloワールドプログラムを呼び出すRustクライアントスクリプトです。
use solana_client::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,instruction::Instruction,pubkey::Pubkey,signature::{Keypair, Signer},transaction::Transaction,};use std::str::FromStr;#[tokio::main]async fn main() {// Program ID (replace with your actual program ID)let program_id = Pubkey::from_str("4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz").unwrap();// Connect to the Solana devnetlet rpc_url = String::from("http://localhost:8899");let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());// Generate a new keypair for the payerlet payer = Keypair::new();// Request airdroplet airdrop_amount = 1_000_000_000; // 1 SOLlet signature = client.request_airdrop(&payer.pubkey(), airdrop_amount).expect("Failed to request airdrop");// Wait for airdrop confirmationloop {let confirmed = client.confirm_transaction(&signature).unwrap();if confirmed {break;}}// Create the instructionlet instruction = Instruction::new_with_borsh(program_id,&(), // Empty instruction datavec![], // No accounts needed);// Add the instruction to new transactionlet mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));transaction.sign(&[&payer], client.get_latest_blockhash().unwrap());// Send and confirm the transactionmatch client.send_and_confirm_transaction(&transaction) {Ok(signature) => println!("Transaction Signature: {}", signature),Err(err) => eprintln!("Error sending transaction: {}", err),}}
プログラムIDの置き換え
クライアントコードを実行する前に、コードスニペット内のプログラムIDをあなたのプログラムのものに置き換えてください。
以下のコマンドを実行することで、プログラムIDを取得できます。
$solana address -k ./target/deploy/hello_world-keypair.json
プログラムの呼び出し
以下のコマンドでクライアントスクリプトを実行します。
$cargo run --example client
出力例:
Transaction Signature: 54TWxKi3Jsi3UTeZbhLGUFX6JQH7TspRJjRRFZ8NFnwG5BXM9udxiX77bAACjKAS9fGnVeEazrXL4SfKrW7xZFYV
Solana Explorer(ローカルクラスター)でトランザクション署名を確認すると、プログラムログに「Hello, world!」が表示されます。
プログラムの更新
Solanaプログラムは、同じプログラムIDに再デプロイすることで更新できます。src/lib.rs
のプログラムを更新して、「Hello,
world!」の代わりに「Hello, Solana!」と表示するようにしましょう。
pub fn process_instruction(_program_id: &Pubkey,_accounts: &[AccountInfo],_instruction_data: &[u8],) -> ProgramResult {-msg!("Hello, world!");+msg!("Hello, Solana!");Ok(())}
cargo build-sbf
コマンドを実行して、更新された.so
ファイルを生成します。
$cargo build-sbf
cargo test
コマンドを実行して、更新されたプログラムをテストします。
$cargo test -- --no-capture
プログラムログに「Hello, Solana!」が表示されるはずです。
running 1 testLogs: ["Program 5y8bHrnwfq2dLDgLn3WoTHb9dDuyorj9gyapW6aeyrpV invoke [1]","Program log: Hello, Solana!","Program 5y8bHrnwfq2dLDgLn3WoTHb9dDuyorj9gyapW6aeyrpV consumed 211 of 200000 compute units","Program 5y8bHrnwfq2dLDgLn3WoTHb9dDuyorj9gyapW6aeyrpV success",]test test::test_hello_world ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s
プログラムの再デプロイ
同じsolana program deploy
コマンドを使用してプログラムを再デプロイします。
$solana program deploy ./target/deploy/hello_world.so
クライアントコードを再度実行し、Solana Explorerでトランザクション署名を確認して、プログラムログに「Hello, Solana!」が表示されていることを確認します。
$cargo run --example client
プログラムの終了
Solanaプログラムを終了して、アカウントに割り当てられたSOLを回収することができます。プログラムの終了は元に戻せないため、注意して行う必要があります。
プログラムを終了するには、solana program close <PROGRAM_ID>
コマンドを使用します。例えば:
$solana program close 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz --bypass-warning
出力例:
Closed Program Id 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz, 0.1350588 SOLreclaimed
プログラムが閉鎖されると、そのプログラムIDは再利用できなくなることに注意してください。以前に閉鎖されたプログラムIDでプログラムをデプロイしようとすると、エラーが発生します。
Error: Program 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz has been closed, usea new Program Id
閉鎖されたプログラムの再デプロイ
プログラムを閉鎖した後、同じソースコードでプログラムを再デプロイする必要がある場合は、新しいプログラムIDを生成する必要があります。プログラム用の新しいkeypairを生成するには、次のコマンドを実行します:
$solana-keygen new -o ./target/deploy/hello_world-keypair.json --force
あるいは、既存のkeypairファイル(例:./target/deploy/hello_world-keypair.json
)を削除して、cargo build-sbf
を再度実行することもできます。
これにより新しいkeypairファイルが生成されます。
Is this page helpful?