Solana 文档开发程序

使用 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 项目。

Terminal
cargo init hello_world --lib

导航到项目目录。您应该会看到默认的 src/lib.rsCargo.toml 文件。

Terminal
cd hello_world

接下来,添加 solana-program 依赖项。这是构建 Solana 程序所需的最低依赖项。

Terminal
cargo add solana-program@1.18.26

接下来,将以下代码片段添加到 Cargo.toml。如果您不包含此配置,在构建程序时将不会生成 target/deploy 目录。

Cargo.toml
[lib]
crate-type = ["cdylib", "lib"]

您的 Cargo.toml 文件应如下所示:

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 程序中用于将消息打印到程序日志中。

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(())
}

构建程序

接下来,使用 cargo build-sbf 命令构建程序。

Terminal
cargo build-sbf

此命令会生成一个 target/deploy 目录,其中包含两个重要文件:

  1. 一个 .so 文件(例如,hello_world.so):这是编译后的 Solana 程序,将作为“智能合约”部署到网络。
  2. 一个密钥对文件(例如,hello_world-keypair.json):此密钥对的公钥在部署程序时用作程序 ID。

要查看程序 ID,请在终端中运行以下命令。此命令会打印指定文件路径下密钥对的公钥:

Terminal
solana address -k ./target/deploy/hello_world-keypair.json

示例输出:

4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz

测试程序

接下来,使用 solana-program-test crate 测试程序。将以下依赖项添加到 Cargo.toml

Terminal
cargo add solana-program-test@1.18.26 --dev
cargo add solana-sdk@1.18.26 --dev
cargo add tokio --dev

src/lib.rs 中添加以下测试代码,放在程序代码的下方。这是一个测试模块,用于调用 Hello World 程序。

lib.rs
#[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 instruction
let instruction = Instruction {
program_id,
accounts: vec![],
data: vec![],
};
// Create transaction with instruction
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
// Sign transaction
transaction.sign(&[&payer], recent_blockhash);
let transaction_result = banks_client.process_transaction(transaction).await;
assert!(transaction_result.is_ok());
}
}

使用 cargo test-sbf 命令运行测试。程序日志将显示 "Hello, world!"。

Terminal
cargo test-sbf

示例输出:

Terminal
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 success
test test::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.13s

部署程序

接下来,部署程序。在本地开发时,我们可以使用 solana-test-validator

首先,将 Solana CLI 配置为使用本地 Solana 集群。

Terminal
solana config set -ul

示例输出:

Config File: /.config/solana/cli/config.yml
RPC URL: http://localhost:8899
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: /.config/solana/id.json
Commitment: confirmed

打开一个新终端并运行 solana-test-validators 命令以启动本地 validator。

Terminal
solana-test-validator

在测试 validator 运行时,在另一个终端中运行 solana program deploy 命令,将程序部署到本地 validator。

Terminal
solana program deploy ./target/deploy/hello_world.so

示例输出:

Program Id: 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
Signature:
5osMiNMiDZGM7L1e2tPHxU8wdB8gwG8fDnXLg5G7SbhwFz4dHshYgAijk4wSQL5cXiu8z1MMou5kLadAQuHp7ybH

您可以在 Solana Explorer 上检查程序 ID 和交易签名。请注意,Solana Explorer 上的集群也必须是 localhost。Solana Explorer 上的“Custom RPC URL”选项默认为 http://localhost:8899

调用程序

接下来,我们将演示如何使用 Rust 客户端调用程序。

首先创建一个 examples 目录和一个 client.rs 文件。

Terminal
mkdir -p examples
touch examples/client.rs

将以下内容添加到 Cargo.toml

Cargo.toml
[[example]]
name = "client"
path = "examples/client.rs"

添加 solana-client 依赖项。

Terminal
cargo add solana-client@1.18.26 --dev

将以下代码添加到 examples/client.rs。这是一个 Rust 客户端脚本,用于为新的 keypair 提供资金以支付交易费用,然后调用 Hello World 程序。

example/client.rs
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 devnet
let 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 payer
let payer = Keypair::new();
// Request airdrop
let airdrop_amount = 1_000_000_000; // 1 SOL
let signature = client
.request_airdrop(&payer.pubkey(), airdrop_amount)
.expect("Failed to request airdrop");
// Wait for airdrop confirmation
loop {
let confirmed = client.confirm_transaction(&signature).unwrap();
if confirmed {
break;
}
}
// Create the instruction
let instruction = Instruction::new_with_borsh(
program_id,
&(), // Empty instruction data
vec![], // No accounts needed
);
// Add the instruction to new transaction
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
transaction.sign(&[&payer], client.get_latest_blockhash().unwrap());
// Send and confirm the transaction
match client.send_and_confirm_transaction(&transaction) {
Ok(signature) => println!("Transaction Signature: {}", signature),
Err(err) => eprintln!("Error sending transaction: {}", err),
}
}

在运行脚本之前,将上面代码片段中的程序 ID 替换为您的程序的 ID。

您可以通过运行以下命令获取您的程序 ID。

Terminal
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();
}
}

使用以下命令运行客户端脚本。

Terminal
cargo run --example client

示例输出:

Transaction Signature: 54TWxKi3Jsi3UTeZbhLGUFX6JQH7TspRJjRRFZ8NFnwG5BXM9udxiX77bAACjKAS9fGnVeEazrXL4SfKrW7xZFYV

您可以在 Solana Explorer(本地集群)上检查交易签名,以在程序日志中查看 "Hello, world!"。

更新程序

Solana 程序可以通过重新部署到相同的程序 ID 来更新。在 src/lib.rs 中更新程序,将 "Hello, world!" 修改为 "Hello, Solana!"。

lib.rs
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
- msg!("Hello, world!");
+ msg!("Hello, Solana!");
Ok(())
}

通过运行 cargo test-sbf 命令测试更新后的程序。

Terminal
cargo test-sbf

您应该在程序日志中看到 "Hello, Solana!"。

Terminal
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 success
test test::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s

运行 cargo build-sbf 命令生成一个更新的 .so 文件。

Terminal
cargo build-sbf

使用 solana program deploy 命令重新部署程序。

Terminal
solana program deploy ./target/deploy/hello_world.so

再次运行客户端代码,并在 Solana Explorer 上检查交易签名,以在程序日志中查看 "Hello, Solana!"。

Terminal
cargo run --example client

关闭程序

您可以关闭您的 Solana 程序以回收分配给账户的 SOL。关闭程序是不可逆的操作,因此应谨慎进行。

要关闭程序,请使用 solana program close <PROGRAM_ID> 命令。例如:

Terminal
solana program close 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
--bypass-warning

示例输出:

Closed Program Id 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz, 0.1350588 SOL
reclaimed

请注意,一旦程序被关闭,其程序 ID 将无法再次使用。尝试使用已关闭的程序 ID 部署程序将导致错误。

Error: Program 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz has been closed, use
a new Program Id

如果您需要在关闭程序后使用相同的源代码重新部署程序,您必须生成一个新的程序 ID。要为程序生成新的 keypair,请运行以下命令:

Terminal
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?

Table of Contents

Edit Page