使用 Rust 开发程序
Solana 程序主要使用 Rust 编程语言开发。本页面重点介绍如何在不使用 Anchor 框架的情况下用 Rust 编写 Solana 程序,这种方法通常被称为编写“原生 Rust”程序。
原生 Rust 开发为开发者提供了对 Solana 程序的直接控制。然而,与使用 Anchor 框架相比,这种方法需要更多的手动设置和样板代码。此方法推荐给以下开发者:
- 需要对程序逻辑和优化进行精细控制
- 希望在使用高级框架之前学习底层概念
对于初学者,我们建议从 Anchor 框架开始。有关更多信息,请参阅 Anchor 部分。
前置要求
有关详细的安装说明,请访问 安装 页面。
在开始之前,请确保已安装以下内容:
- Rust:用于构建 Solana 程序的编程语言。
- Solana CLI:用于 Solana 开发的命令行工具。
入门
以下示例涵盖了用 Rust 编写第一个 Solana 程序的基本步骤。我们将创建一个简单的程序,在程序日志中打印 "Hello, world!"。
创建新程序
首先,使用标准的 cargo init
命令和 --lib
标志创建一个新的 Rust 项目。
cargo init hello_world --lib
导航到项目目录。您应该会看到默认的 src/lib.rs
和 Cargo.toml
文件。
cd hello_world
接下来,添加 solana-program
依赖项。这是构建 Solana 程序所需的最低依赖项。
cargo add solana-program@1.18.26
接下来,将以下代码片段添加到
Cargo.toml
。如果您不包含此配置,在构建程序时将不会生成 target/deploy
目录。
[lib]crate-type = ["cdylib", "lib"]
您的 Cargo.toml
文件应如下所示:
[package]name = "hello_world"version = "0.1.0"edition = "2021"[lib]crate-type = ["cdylib", "lib"][dependencies]solana-program = "1.18.26"
接下来,将 src/lib.rs
的内容替换为以下代码。这是一个最小的 Solana 程序,当程序被调用时,它会将 "Hello,
world!" 打印到程序日志中。
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
目录,其中包含两个重要文件:
- 一个
.so
文件(例如,hello_world.so
):这是编译后的 Solana 程序,将作为“智能合约”部署到网络。 - 一个密钥对文件(例如,
hello_world-keypair.json
):此密钥对的公钥在部署程序时用作程序 ID。
要查看程序 ID,请在终端中运行以下命令。此命令会打印指定文件路径下密钥对的公钥:
solana address -k ./target/deploy/hello_world-keypair.json
示例输出:
4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
测试程序
接下来,使用 solana-program-test
crate 测试程序。将以下依赖项添加到
Cargo.toml
。
cargo add solana-program-test@1.18.26 --devcargo add solana-sdk@1.18.26 --devcargo add tokio --dev
在 src/lib.rs
中添加以下测试代码,放在程序代码的下方。这是一个测试模块,用于调用 Hello
World 程序。
#[cfg(test)]mod test {use solana_program_test::*;use solana_sdk::{instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::Transaction,};#[tokio::test]async fn test_hello_world() {let program_id = Pubkey::new_unique();let mut program_test = ProgramTest::default();program_test.add_program("hello_world", program_id, None);let (mut banks_client, payer, recent_blockhash) = program_test.start().await;// Create instructionlet instruction = Instruction {program_id,accounts: vec![],data: vec![],};// Create transaction with instructionlet mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));// Sign transactiontransaction.sign(&[&payer], recent_blockhash);let transaction_result = banks_client.process_transaction(transaction).await;assert!(transaction_result.is_ok());}}
使用 cargo test-sbf
命令运行测试。程序日志将显示 "Hello, world!"。
cargo test-sbf
示例输出:
running 1 test[2024-10-18T21:24:54.889570000Z INFO solana_program_test] "hello_world" SBF program from /hello_world/target/deploy/hello_world.so, modified 35 seconds, 828 ms, 268 µs and 398 ns ago[2024-10-18T21:24:54.974294000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM invoke [1][2024-10-18T21:24:54.974814000Z DEBUG solana_runtime::message_processor::stable_log] Program log: Hello, world![2024-10-18T21:24:54.976848000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM consumed 140 of 200000 compute units[2024-10-18T21:24:54.976868000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM successtest test::test_hello_world ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.13s
部署程序
接下来,部署程序。在本地开发时,我们可以使用 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
您可以在
Solana Explorer
上检查程序 ID 和交易签名。请注意,Solana
Explorer 上的集群也必须是 localhost。Solana Explorer 上的“Custom RPC
URL”选项默认为 http://localhost:8899
。
调用程序
接下来,我们将演示如何使用 Rust 客户端调用程序。
首先创建一个 examples
目录和一个 client.rs
文件。
mkdir -p examplestouch examples/client.rs
将以下内容添加到 Cargo.toml
。
[[example]]name = "client"path = "examples/client.rs"
添加 solana-client
依赖项。
cargo add solana-client@1.18.26 --dev
将以下代码添加到
examples/client.rs
。这是一个 Rust 客户端脚本,用于为新的 keypair 提供资金以支付交易费用,然后调用 Hello
World 程序。
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://127.0.0.1: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
#[tokio::main]async fn main() {- let program_id = Pubkey::from_str("4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz").unwrap();+ let program_id = Pubkey::from_str("YOUR_PROGRAM_ID).unwrap();}}
使用以下命令运行客户端脚本。
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 test-sbf
命令测试更新后的程序。
cargo test-sbf
您应该在程序日志中看到 "Hello, Solana!"。
running 1 test[2024-10-23T19:28:28.842639000Z INFO solana_program_test] "hello_world" SBF program from /code/misc/delete/hello_world/target/deploy/hello_world.so, modified 4 minutes, 31 seconds, 435 ms, 566 µs and 766 ns ago[2024-10-23T19:28:28.934854000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM invoke [1][2024-10-23T19:28:28.936735000Z DEBUG solana_runtime::message_processor::stable_log] Program log: Hello, Solana![2024-10-23T19:28:28.938774000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM consumed 140 of 200000 compute units[2024-10-23T19:28:28.938793000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM successtest test::test_hello_world ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s
运行 cargo build-sbf
命令生成一个更新的 .so
文件。
cargo build-sbf
使用 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?