Documentazione SolanaSviluppo di programmi

Sviluppare programmi in Rust

I programmi Solana sono principalmente sviluppati utilizzando il linguaggio di programmazione Rust. Questa pagina si concentra sulla scrittura di programmi Solana in Rust senza utilizzare il framework Anchor, un approccio spesso definito come scrittura di programmi "Rust nativi".

Lo sviluppo in Rust nativo offre agli sviluppatori un controllo diretto sui loro programmi Solana. Tuttavia, questo approccio richiede più configurazione manuale e codice boilerplate rispetto all'utilizzo del framework Anchor. Questo metodo è consigliato per gli sviluppatori che:

  • Cercano un controllo granulare sulla logica del programma e sulle ottimizzazioni
  • Vogliono imparare i concetti di base prima di passare a framework di livello superiore

Per i principianti, consigliamo di iniziare con il framework Anchor. Consulta la sezione Anchor per maggiori informazioni.

Prerequisiti

Per istruzioni dettagliate sull'installazione, visita la pagina di installazione.

Prima di iniziare, assicurati di avere installato quanto segue:

  • Rust: Il linguaggio di programmazione per costruire programmi Solana.
  • Solana CLI: Strumento da riga di comando per lo sviluppo Solana.

Primi passi

L'esempio seguente copre i passaggi di base per creare il tuo primo programma Solana scritto in Rust. Creeremo un programma minimo che stampa "Hello, world!" nel log del programma.

Crea un nuovo programma

Prima, crea un nuovo progetto Rust utilizzando il comando standard cargo init con l'opzione --lib.

Terminal
cargo init hello_world --lib

Naviga nella directory del progetto. Dovresti vedere i file predefiniti src/lib.rs e Cargo.toml

Terminal
cd hello_world

Successivamente, aggiungi la dipendenza solana-program. Questa è la dipendenza minima richiesta per costruire un programma Solana.

Terminal
cargo add solana-program@1.18.26

Successivamente, aggiungi il seguente snippet a Cargo.toml. Se non includi questa configurazione, la directory target/deploy non verrà generata quando compili il programma.

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

Il tuo file Cargo.toml dovrebbe apparire come segue:

Cargo.toml
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
[dependencies]
solana-program = "1.18.26"

Successivamente, sostituisci il contenuto di src/lib.rs con il seguente codice. Questo è un programma Solana minimale che stampa "Hello, world!" nel log del programma quando viene invocato.

La macro msg! viene utilizzata nei programmi Solana per stampare un messaggio nel log del programma.

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

Compila il programma

Successivamente, compila il programma utilizzando il comando cargo build-sbf.

Terminal
cargo build-sbf

Questo comando genera una directory target/deploy contenente due file importanti:

  1. Un file .so (es., hello_world.so): Questo è il programma Solana compilato che verrà distribuito sulla rete come "smart contract".
  2. Un file keypair (es., hello_world-keypair.json): La chiave pubblica di questo keypair viene utilizzata come ID del programma durante la distribuzione del programma.

Per visualizzare l'ID del programma, esegui il seguente comando nel tuo terminale. Questo comando stampa la chiave pubblica del keypair nel percorso file specificato:

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

Esempio di output:

4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz

Testa il programma

Successivamente, testa il programma utilizzando il crate solana-program-test. Aggiungi le seguenti dipendenze a Cargo.toml.

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

Aggiungi il seguente test a src/lib.rs, sotto il codice del programma. Questo è un modulo di test che invoca il programma 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());
}
}

Esegui il test utilizzando il comando cargo test-sbf. Il log del programma mostrerà "Hello, world!".

Terminal
cargo test-sbf

Output di esempio:

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

Distribuisci il programma

Successivamente, distribuisci il programma. Durante lo sviluppo locale, possiamo utilizzare il solana-test-validator.

Prima, configura la CLI di Solana per utilizzare il cluster Solana locale.

Terminal
solana config set -ul

Output di esempio:

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

Apri un nuovo terminale ed esegui il comando solana-test-validators per avviare il validator locale.

Terminal
solana-test-validator

Mentre il validator di test è in esecuzione, esegui il comando solana program deploy in un terminale separato per distribuire il programma al validator locale.

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

Output di esempio:

Program Id: 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
Signature:
5osMiNMiDZGM7L1e2tPHxU8wdB8gwG8fDnXLg5G7SbhwFz4dHshYgAijk4wSQL5cXiu8z1MMou5kLadAQuHp7ybH

Puoi ispezionare l'ID del programma e la firma della transazione su Solana Explorer. Nota che il cluster su Solana Explorer deve essere anch'esso localhost. L'opzione "Custom RPC URL" su Solana Explorer è impostata di default su http://localhost:8899.

Invoca il programma

Successivamente, dimostreremo come invocare il programma utilizzando un client Rust.

Prima crea una directory examples e un file client.rs.

Terminal
mkdir -p examples
touch examples/client.rs

Aggiungi quanto segue a Cargo.toml.

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

Aggiungi la dipendenza solana-client.

Terminal
cargo add solana-client@1.18.26 --dev

Aggiungi il seguente codice a examples/client.rs. Questo è uno script client in Rust che finanzia un nuovo keypair per pagare le commissioni di transazione e poi invoca il programma 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),
}
}

Prima di eseguire lo script, sostituisci l'ID del programma nel frammento di codice sopra con quello del tuo programma.

Puoi ottenere l'ID del tuo programma eseguendo il seguente comando.

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

Esegui lo script client con il seguente comando.

Terminal
cargo run --example client

Esempio di output:

Transaction Signature: 54TWxKi3Jsi3UTeZbhLGUFX6JQH7TspRJjRRFZ8NFnwG5BXM9udxiX77bAACjKAS9fGnVeEazrXL4SfKrW7xZFYV

Puoi ispezionare la firma della transazione su Solana Explorer (cluster locale) per vedere "Hello, world!" nel log del programma.

Aggiornare il programma

I programmi Solana possono essere aggiornati ridistribuendoli allo stesso ID programma. Aggiorna il programma in src/lib.rs per stampare "Hello, Solana!" invece di "Hello, world!".

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

Testa il programma aggiornato eseguendo il comando cargo test-sbf.

Terminal
cargo test-sbf

Dovresti vedere "Hello, Solana!" nel log del programma.

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

Esegui il comando cargo build-sbf per generare un file .so aggiornato.

Terminal
cargo build-sbf

Ridistribuisci il programma usando il comando solana program deploy.

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

Esegui nuovamente il codice client e ispeziona la firma della transazione su Solana Explorer per vedere "Hello, Solana!" nel log del programma.

Terminal
cargo run --example client

Chiudere il programma

Puoi chiudere il tuo programma Solana per recuperare il SOL allocato all'account. La chiusura di un programma è irreversibile, quindi dovrebbe essere fatta con cautela.

Per chiudere un programma, usa il comando solana program close <PROGRAM_ID>. Per esempio:

Terminal
solana program close 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
--bypass-warning

Output di esempio:

Closed Program Id 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz, 0.1350588 SOL
reclaimed

Nota che una volta chiuso un programma, il suo program ID non può essere riutilizzato. Il tentativo di distribuire un programma con un program ID precedentemente chiuso risulterà in un errore.

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

Se hai bisogno di ridistribuire un programma con lo stesso codice sorgente dopo aver chiuso un programma, devi generare un nuovo program ID. Per generare una nuova keypair per il programma, esegui il seguente comando:

Terminal
solana-keygen new -o ./target/deploy/hello_world-keypair.json --force

In alternativa, puoi eliminare il file keypair esistente (ad es. ./target/deploy/hello_world-keypair.json) ed eseguire nuovamente cargo build-sbf, che genererà un nuovo file keypair.

Is this page helpful?

Indice

Modifica Pagina