Δομή Προγράμματος Rust
Τα προγράμματα Solana που είναι γραμμένα σε Rust έχουν ελάχιστες δομικές
απαιτήσεις, επιτρέποντας ευελιξία στον τρόπο οργάνωσης του κώδικα. Η μόνη
απαίτηση είναι ότι ένα πρόγραμμα πρέπει να έχει ένα entrypoint
, το οποίο
καθορίζει από πού ξεκινά η εκτέλεση ενός προγράμματος.
Δομή Προγράμματος
Αν και δεν υπάρχουν αυστηροί κανόνες για τη δομή των αρχείων, τα προγράμματα Solana συνήθως ακολουθούν ένα κοινό μοτίβο:
entrypoint.rs
: Καθορίζει το σημείο εισόδου που δρομολογεί τις εισερχόμενες εντολές.state.rs
: Καθορίζει την κατάσταση του προγράμματος (δεδομένα λογαριασμού).instructions.rs
: Καθορίζει τις εντολές που μπορεί να εκτελέσει το πρόγραμμα.processor.rs
: Καθορίζει τους χειριστές εντολών (συναρτήσεις) που υλοποιούν την επιχειρησιακή λογική για κάθε εντολή.error.rs
: Καθορίζει προσαρμοσμένα σφάλματα που μπορεί να επιστρέψει το πρόγραμμα.
Για παράδειγμα, δείτε το Token Program.
Παράδειγμα Προγράμματος
Για να δείξουμε πώς να δημιουργήσετε ένα εγγενές πρόγραμμα Rust με πολλαπλές εντολές, θα εξετάσουμε ένα απλό πρόγραμμα μετρητή που υλοποιεί δύο εντολές:
InitializeCounter
: Δημιουργεί και αρχικοποιεί έναν νέο λογαριασμό με μια αρχική τιμή.IncrementCounter
: Αυξάνει την τιμή που είναι αποθηκευμένη σε έναν υπάρχοντα λογαριασμό.
Για απλότητα, το πρόγραμμα θα υλοποιηθεί σε ένα μόνο αρχείο lib.rs
, αν και
στην πράξη μπορεί να θέλετε να χωρίσετε μεγαλύτερα προγράμματα σε πολλαπλά
αρχεία.
Μέρος 1: Γράφοντας το Πρόγραμμα
Ας ξεκινήσουμε με τη δημιουργία του προγράμματος μετρητή. Θα δημιουργήσουμε ένα πρόγραμμα που μπορεί να αρχικοποιήσει έναν μετρητή με μια αρχική τιμή και να τον αυξήσει.
Δημιουργία νέου προγράμματος
Πρώτα, ας δημιουργήσουμε ένα νέο έργο Rust για το πρόγραμμά μας στο Solana.
$cargo new counter_program --lib$cd counter_program
Θα πρέπει να δείτε τα προεπιλεγμένα αρχεία src/lib.rs
και Cargo.toml
.
Ενημερώστε το πεδίο edition
στο Cargo.toml
σε 2021. Διαφορετικά, μπορεί να
αντιμετωπίσετε σφάλμα κατά τη δημιουργία του προγράμματος.
Προσθήκη εξαρτήσεων
Τώρα ας προσθέσουμε τις απαραίτητες εξαρτήσεις για τη δημιουργία ενός
προγράμματος Solana. Χρειαζόμαστε το solana-program
για το βασικό SDK και το
borsh
για σειριοποίηση.
$cargo add solana-program@2.2.0$cargo add borsh
Δεν υπάρχει απαίτηση να χρησιμοποιήσετε το Borsh. Ωστόσο, είναι μια συχνά χρησιμοποιούμενη βιβλιοθήκη σειριοποίησης για προγράμματα Solana.
Διαμόρφωση crate-type
Τα προγράμματα Solana πρέπει να μεταγλωττιστούν ως δυναμικές βιβλιοθήκες.
Προσθέστε την ενότητα [lib]
για να διαμορφώσετε τον τρόπο με τον οποίο το
Cargo δημιουργεί το πρόγραμμα.
[lib]crate-type = ["cdylib", "lib"]
Αν δεν συμπεριλάβετε αυτή τη διαμόρφωση, ο κατάλογος target/deploy δεν θα δημιουργηθεί όταν δημιουργείτε το πρόγραμμα.
Ρύθμιση σημείου εισόδου προγράμματος
Κάθε πρόγραμμα Solana έχει ένα σημείο εισόδου, το οποίο είναι η συνάρτηση που καλείται όταν γίνεται επίκληση του προγράμματος. Ας ξεκινήσουμε προσθέτοντας τις εισαγωγές που θα χρειαστούμε για το πρόγραμμα και ρυθμίζοντας το σημείο εισόδου.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
:
use borsh::{BorshDeserialize, BorshSerialize};use solana_program::{account_info::{next_account_info, AccountInfo},entrypoint,entrypoint::ProgramResult,msg,program::invoke,program_error::ProgramError,pubkey::Pubkey,system_instruction,sysvar::{rent::Rent, Sysvar},};entrypoint!(process_instruction);pub fn process_instruction(program_id: &Pubkey,accounts: &[AccountInfo],instruction_data: &[u8],) -> ProgramResult {Ok(())}
Η μακροεντολή
entrypoint
χειρίζεται την αποσειριοποίηση των δεδομένων input
στις παραμέτρους της
συνάρτησης process_instruction
.
Ένα entrypoint
προγράμματος Solana έχει την ακόλουθη υπογραφή συνάρτησης. Οι
προγραμματιστές είναι ελεύθεροι να δημιουργήσουν τη δική τους υλοποίηση της
συνάρτησης entrypoint
.
#[no_mangle]pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64;
Ορισμός κατάστασης προγράμματος
Τώρα ας ορίσουμε τη δομή δεδομένων που θα αποθηκεύεται στους λογαριασμούς
μετρητή μας. Αυτά είναι τα δεδομένα που θα αποθηκευτούν στο πεδίο data
του
λογαριασμού.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
:
#[derive(BorshSerialize, BorshDeserialize, Debug)]pub struct CounterAccount {pub count: u64,}
Ορισμός απαρίθμησης εντολών
Ας ορίσουμε τις εντολές που μπορεί να εκτελέσει το πρόγραμμά μας. Θα χρησιμοποιήσουμε μια απαρίθμηση (enum) όπου κάθε παραλλαγή αντιπροσωπεύει μια διαφορετική εντολή.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
:
#[derive(BorshSerialize, BorshDeserialize, Debug)]pub enum CounterInstruction {InitializeCounter { initial_value: u64 },IncrementCounter,}
Υλοποίηση αποσειριοποίησης εντολών
Τώρα πρέπει να αποσειριοποιήσουμε το instruction_data
(ακατέργαστα bytes) σε
μία από τις παραλλαγές της απαρίθμησης CounterInstruction
. Η μέθοδος
try_from_slice
του Borsh χειρίζεται αυτή τη μετατροπή αυτόματα.
Ενημερώστε τη συνάρτηση process_instruction
για να χρησιμοποιήσει την
αποσειριοποίηση Borsh:
pub fn process_instruction(program_id: &Pubkey,accounts: &[AccountInfo],instruction_data: &[u8],) -> ProgramResult {let instruction = CounterInstruction::try_from_slice(instruction_data).map_err(|_| ProgramError::InvalidInstructionData)?;Ok(())}
Δρομολόγηση εντολών στους χειριστές
Τώρα ας ενημερώσουμε την κύρια συνάρτηση process_instruction
για να δρομολογεί
τις εντολές στις κατάλληλες συναρτήσεις χειρισμού τους.
Αυτό το μοτίβο δρομολόγησης είναι συνηθισμένο στα προγράμματα Solana. Το
instruction_data
αποσειριοποιείται σε μια παραλλαγή μιας απαρίθμησης που
αντιπροσωπεύει την εντολή, και στη συνέχεια καλείται η κατάλληλη συνάρτηση
χειρισμού. Κάθε συνάρτηση χειρισμού περιλαμβάνει την υλοποίηση για αυτή την
εντολή.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
ενημερώνοντας τη συνάρτηση
process_instruction
και προσθέτοντας τους χειριστές για τις εντολές
InitializeCounter
και IncrementCounter
:
pub fn process_instruction(program_id: &Pubkey,accounts: &[AccountInfo],instruction_data: &[u8],) -> ProgramResult {let instruction = CounterInstruction::try_from_slice(instruction_data).map_err(|_| ProgramError::InvalidInstructionData)?;match instruction {CounterInstruction::InitializeCounter { initial_value } => {process_initialize_counter(program_id, accounts, initial_value)?}CounterInstruction::IncrementCounter => {process_increment_counter(program_id, accounts)?}};Ok(())}fn process_initialize_counter(program_id: &Pubkey,accounts: &[AccountInfo],initial_value: u64,) -> ProgramResult {Ok(())}fn process_increment_counter(program_id: &Pubkey,accounts: &[AccountInfo],) -> ProgramResult {Ok(())}
Υλοποίηση χειριστή αρχικοποίησης
Ας υλοποιήσουμε τον χειριστή για τη δημιουργία και αρχικοποίηση ενός νέου λογαριασμού μετρητή. Εφόσον μόνο το System Program μπορεί να δημιουργήσει λογαριασμούς στη Solana, θα χρησιμοποιήσουμε μια Cross Program Invocation (CPI), ουσιαστικά καλώντας ένα άλλο πρόγραμμα από το πρόγραμμά μας.
Το πρόγραμμά μας κάνει ένα CPI για να καλέσει την εντολή create_account
του
System Program. Ο νέος λογαριασμός δημιουργείται με το πρόγραμμά μας ως
ιδιοκτήτη, δίνοντας στο πρόγραμμά μας τη δυνατότητα να γράφει στον λογαριασμό
και να αρχικοποιεί τα δεδομένα.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
ενημερώνοντας τη συνάρτηση
process_initialize_counter
:
fn process_initialize_counter(program_id: &Pubkey,accounts: &[AccountInfo],initial_value: u64,) -> ProgramResult {let accounts_iter = &mut accounts.iter();let counter_account = next_account_info(accounts_iter)?;let payer_account = next_account_info(accounts_iter)?;let system_program = next_account_info(accounts_iter)?;let account_space = 8;let rent = Rent::get()?;let required_lamports = rent.minimum_balance(account_space);invoke(&system_instruction::create_account(payer_account.key,counter_account.key,required_lamports,account_space as u64,program_id,),&[payer_account.clone(),counter_account.clone(),system_program.clone(),],)?;let counter_data = CounterAccount {count: initial_value,};let mut account_data = &mut counter_account.data.borrow_mut()[..];counter_data.serialize(&mut account_data)?;msg!("Counter initialized with value: {}", initial_value);Ok(())}
Αυτή η οδηγία είναι μόνο για σκοπούς επίδειξης. Δεν περιλαμβάνει ελέγχους ασφαλείας και επικύρωσης που απαιτούνται για προγράμματα παραγωγής.
Υλοποίηση χειριστή αύξησης
Τώρα ας υλοποιήσουμε τον χειριστή που αυξάνει έναν υπάρχοντα μετρητή. Αυτή η οδηγία:
- Διαβάζει το πεδίο
data
του λογαριασμού για τοcounter_account
- Το αποσειριοποιεί σε μια δομή
CounterAccount
- Αυξάνει το πεδίο
count
κατά 1 - Σειριοποιεί τη δομή
CounterAccount
πίσω στο πεδίοdata
του λογαριασμού
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
ενημερώνοντας τη συνάρτηση
process_increment_counter
:
fn process_increment_counter(program_id: &Pubkey,accounts: &[AccountInfo],) -> ProgramResult {let accounts_iter = &mut accounts.iter();let counter_account = next_account_info(accounts_iter)?;if counter_account.owner != program_id {return Err(ProgramError::IncorrectProgramId);}let mut data = counter_account.data.borrow_mut();let mut counter_data: CounterAccount = CounterAccount::try_from_slice(&data)?;counter_data.count = counter_data.count.checked_add(1).ok_or(ProgramError::InvalidAccountData)?;counter_data.serialize(&mut &mut data[..])?;msg!("Counter incremented to: {}", counter_data.count);Ok(())}
Αυτή η οδηγία είναι μόνο για σκοπούς επίδειξης. Δεν περιλαμβάνει ελέγχους ασφαλείας και επικύρωσης που απαιτούνται για προγράμματα παραγωγής.
Ολοκληρωμένο πρόγραμμα
Συγχαρητήρια! Έχετε δημιουργήσει ένα πλήρες πρόγραμμα Solana που επιδεικνύει τη βασική δομή που μοιράζονται όλα τα προγράμματα Solana:
- Σημείο εισόδου: Καθορίζει πού ξεκινά η εκτέλεση του προγράμματος και δρομολογεί όλα τα εισερχόμενα αιτήματα στους κατάλληλους χειριστές οδηγιών
- Χειρισμός οδηγιών: Καθορίζει τις οδηγίες και τις σχετικές συναρτήσεις χειριστών τους
- Διαχείριση κατάστασης: Καθορίζει τις δομές δεδομένων λογαριασμού και διαχειρίζεται την κατάστασή τους σε λογαριασμούς που ανήκουν στο πρόγραμμα
- Cross Program Invocation (CPI): Καλεί το System Program για να δημιουργήσει νέους λογαριασμούς που ανήκουν στο πρόγραμμα
Το επόμενο βήμα είναι να δοκιμάσουμε το πρόγραμμα για να διασφαλίσουμε ότι όλα λειτουργούν σωστά.
Μέρος 2: Δοκιμή του προγράμματος
Τώρα ας δοκιμάσουμε το πρόγραμμα μετρητή μας. Θα χρησιμοποιήσουμε το LiteSVM, ένα πλαίσιο δοκιμών που μας επιτρέπει να δοκιμάζουμε προγράμματα χωρίς να τα αναπτύσσουμε σε ένα σύμπλεγμα.
Προσθήκη εξαρτήσεων δοκιμών
Πρώτα, ας προσθέσουμε τις εξαρτήσεις που χρειάζονται για τις δοκιμές. Θα
χρησιμοποιήσουμε το litesvm
για τις δοκιμές και το solana-sdk
.
$cargo add litesvm@0.6.1 --dev$cargo add solana-sdk@2.2.0 --dev
Δημιουργία δοκιμαστικής μονάδας
Τώρα ας προσθέσουμε μια δοκιμαστική μονάδα στο πρόγραμμά μας. Θα ξεκινήσουμε με το βασικό σκελετό και τις εισαγωγές.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
, ακριβώς κάτω από τον κώδικα του
προγράμματος:
#[cfg(test)]mod test {use super::*;use litesvm::LiteSVM;use solana_sdk::{account::ReadableAccount,instruction::{AccountMeta, Instruction},message::Message,signature::{Keypair, Signer},system_program,transaction::Transaction,};#[test]fn test_counter_program() {// Test implementation will go here}}
Το χαρακτηριστικό #[cfg(test)]
διασφαλίζει ότι αυτός ο κώδικας μεταγλωττίζεται
μόνο κατά την εκτέλεση δοκιμών.
Αρχικοποίηση περιβάλλοντος δοκιμών
Ας ρυθμίσουμε το περιβάλλον δοκιμών με το LiteSVM και ας χρηματοδοτήσουμε έναν λογαριασμό πληρωτή.
Το LiteSVM προσομοιώνει το περιβάλλον εκτέλεσης του Solana, επιτρέποντάς μας να δοκιμάσουμε το πρόγραμμά μας χωρίς να το αναπτύξουμε σε ένα πραγματικό σύμπλεγμα.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
ενημερώνοντας τη συνάρτηση
test_counter_program
:
let mut svm = LiteSVM::new();let payer = Keypair::new();svm.airdrop(&payer.pubkey(), 1_000_000_000).expect("Failed to airdrop");
Φόρτωση του προγράμματος
Τώρα πρέπει να δημιουργήσουμε και να φορτώσουμε το πρόγραμμά μας στο περιβάλλον
δοκιμών. Εκτελέστε την εντολή cargo build-sbf
για να δημιουργήσετε το
πρόγραμμα. Αυτό θα δημιουργήσει το αρχείο counter_program.so
στον κατάλογο
target/deploy
.
$cargo build-sbf
Βεβαιωθείτε ότι το edition
στο Cargo.toml
έχει οριστεί σε 2021
.
Μετά τη δημιουργία, μπορούμε να φορτώσουμε το πρόγραμμα.
Ενημερώστε τη συνάρτηση test_counter_program
για να φορτώσετε το πρόγραμμα στο
περιβάλλον δοκιμών.
let program_keypair = Keypair::new();let program_id = program_keypair.pubkey();svm.add_program_from_file(program_id,"target/deploy/counter_program.so").expect("Failed to load program");
Πρέπει να εκτελέσετε το cargo build-sbf
πριν εκτελέσετε τις δοκιμές για να
δημιουργήσετε το αρχείο .so
. Η δοκιμή φορτώνει το μεταγλωττισμένο πρόγραμμα.
Δοκιμή εντολής αρχικοποίησης
Ας δοκιμάσουμε την εντολή αρχικοποίησης δημιουργώντας έναν νέο λογαριασμό μετρητή με μια αρχική τιμή.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
ενημερώνοντας τη συνάρτηση
test_counter_program
:
let counter_keypair = Keypair::new();let initial_value: u64 = 42;println!("Testing counter initialization...");let init_instruction_data =borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value }).expect("Failed to serialize instruction");let initialize_instruction = Instruction::new_with_bytes(program_id,&init_instruction_data,vec![AccountMeta::new(counter_keypair.pubkey(), true),AccountMeta::new(payer.pubkey(), true),AccountMeta::new_readonly(system_program::id(), false),],);let message = Message::new(&[initialize_instruction], Some(&payer.pubkey()));let transaction = Transaction::new(&[&payer, &counter_keypair],message,svm.latest_blockhash());let result = svm.send_transaction(transaction);assert!(result.is_ok(), "Initialize transaction should succeed");let logs = result.unwrap().logs;println!("Transaction logs:\n{:#?}", logs);
Επαλήθευση αρχικοποίησης
Μετά την αρχικοποίηση, ας επαληθεύσουμε ότι ο λογαριασμός μετρητή δημιουργήθηκε σωστά με την αναμενόμενη τιμή.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
ενημερώνοντας τη συνάρτηση
test_counter_program
:
let account = svm.get_account(&counter_keypair.pubkey()).expect("Failed to get counter account");let counter: CounterAccount = CounterAccount::try_from_slice(account.data()).expect("Failed to deserialize counter data");assert_eq!(counter.count, 42);println!("Counter initialized successfully with value: {}", counter.count);
Δοκιμή εντολής αύξησης
Τώρα ας δοκιμάσουμε την εντολή αύξησης για να διασφαλίσουμε ότι ενημερώνει σωστά την τιμή του μετρητή.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
ενημερώνοντας τη συνάρτηση
test_counter_program
:
println!("Testing counter increment...");let increment_instruction_data =borsh::to_vec(&CounterInstruction::IncrementCounter).expect("Failed to serialize instruction");let increment_instruction = Instruction::new_with_bytes(program_id,&increment_instruction_data,vec![AccountMeta::new(counter_keypair.pubkey(), true)],);let message = Message::new(&[increment_instruction], Some(&payer.pubkey()));let transaction = Transaction::new(&[&payer, &counter_keypair],message,svm.latest_blockhash());let result = svm.send_transaction(transaction);assert!(result.is_ok(), "Increment transaction should succeed");let logs = result.unwrap().logs;println!("Transaction logs:\n{:#?}", logs);
Επαλήθευση τελικών αποτελεσμάτων
Τέλος, ας επαληθεύσουμε ότι η αύξηση λειτούργησε σωστά ελέγχοντας την ενημερωμένη τιμή του μετρητή.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs
ενημερώνοντας τη συνάρτηση
test_counter_program
:
let account = svm.get_account(&counter_keypair.pubkey()).expect("Failed to get counter account");let counter: CounterAccount = CounterAccount::try_from_slice(account.data()).expect("Failed to deserialize counter data");assert_eq!(counter.count, 43);println!("Counter incremented successfully to: {}", counter.count);
Εκτελέστε τις δοκιμές με την ακόλουθη εντολή. Η σημαία --nocapture
εμφανίζει
την έξοδο της δοκιμής.
$cargo test -- --nocapture
Αναμενόμενη έξοδος:
Testing counter initialization...Transaction logs:["Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq invoke [1]","Program 11111111111111111111111111111111 invoke [2]","Program 11111111111111111111111111111111 success","Program log: Counter initialized with value: 42","Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq consumed 3803 of 200000 compute units","Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq success",]Counter initialized successfully with value: 42Testing counter increment...Transaction logs:["Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq invoke [1]","Program log: Counter incremented to: 43","Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq consumed 762 of 200000 compute units","Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq success",]Counter incremented successfully to: 43
Μέρος 3: Κλήση του προγράμματος
Τώρα ας προσθέσουμε ένα script πελάτη για να καλέσουμε το πρόγραμμα.
Δημιουργία παραδείγματος πελάτη
Ας δημιουργήσουμε έναν πελάτη Rust για να αλληλεπιδράσουμε με το αναπτυγμένο πρόγραμμά μας.
$mkdir examples$touch examples/client.rs
Προσθέστε την ακόλουθη διαμόρφωση στο Cargo.toml
:
[[example]]name = "client"path = "examples/client.rs"
Εγκαταστήστε τις εξαρτήσεις του πελάτη:
$cargo add solana-client@2.2.0 --dev$cargo add tokio --dev
Υλοποίηση κώδικα πελάτη
Τώρα ας υλοποιήσουμε τον πελάτη που θα καλέσει το αναπτυγμένο πρόγραμμά μας.
Εκτελέστε την ακόλουθη εντολή για να πάρετε το αναγνωριστικό του προγράμματός σας από το αρχείο keypair:
$solana address -k ./target/deploy/counter_program-keypair.json
Προσθέστε τον κώδικα πελάτη στο examples/client.rs
και αντικαταστήστε το
program_id
με την έξοδο της προηγούμενης εντολής:
let program_id = Pubkey::from_str("BDLLezrtFEXVGYqG3aS7eAC7GVeojJ4JHhKJM6pAFCDH").expect("Invalid program ID");
use solana_client::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,instruction::{AccountMeta, Instruction},pubkey::Pubkey,signature::{Keypair, Signer},system_program,transaction::Transaction,};use std::str::FromStr;use counter_program::CounterInstruction;#[tokio::main]async fn main() {// Replace with your actual program ID from deploymentlet program_id = Pubkey::from_str("BDLLezrtFEXVGYqG3aS7eAC7GVeojJ4JHhKJM6pAFCDH").expect("Invalid program ID");// Connect to local clusterlet rpc_url = String::from("http://localhost:8899");let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());// Generate a new keypair for paying feeslet payer = Keypair::new();// Request airdrop of 1 SOL for transaction feesprintln!("Requesting airdrop...");let airdrop_signature = client.request_airdrop(&payer.pubkey(), 1_000_000_000).expect("Failed to request airdrop");// Wait for airdrop confirmationloop {if client.confirm_transaction(&airdrop_signature).unwrap_or(false){break;}std::thread::sleep(std::time::Duration::from_millis(500));}println!("Airdrop confirmed");println!("\nInitializing counter...");let counter_keypair = Keypair::new();let initial_value = 100u64;// Serialize the initialize instruction datalet instruction_data = borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value }).expect("Failed to serialize instruction");let initialize_instruction = Instruction::new_with_bytes(program_id,&instruction_data,vec![AccountMeta::new(counter_keypair.pubkey(), true),AccountMeta::new(payer.pubkey(), true),AccountMeta::new_readonly(system_program::id(), false),],);let mut transaction =Transaction::new_with_payer(&[initialize_instruction], Some(&payer.pubkey()));let blockhash = client.get_latest_blockhash().expect("Failed to get blockhash");transaction.sign(&[&payer, &counter_keypair], blockhash);match client.send_and_confirm_transaction(&transaction) {Ok(signature) => {println!("Counter initialized!");println!("Transaction: {}", signature);println!("Counter address: {}", counter_keypair.pubkey());}Err(err) => {eprintln!("Failed to initialize counter: {}", err);return;}}println!("\nIncrementing counter...");// Serialize the increment instruction datalet increment_data = borsh::to_vec(&CounterInstruction::IncrementCounter).expect("Failed to serialize instruction");let increment_instruction = Instruction::new_with_bytes(program_id,&increment_data,vec![AccountMeta::new(counter_keypair.pubkey(), true)],);let mut transaction =Transaction::new_with_payer(&[increment_instruction], Some(&payer.pubkey()));transaction.sign(&[&payer, &counter_keypair], blockhash);match client.send_and_confirm_transaction(&transaction) {Ok(signature) => {println!("Counter incremented!");println!("Transaction: {}", signature);}Err(err) => {eprintln!("Failed to increment counter: {}", err);}}}
Μέρος 4: Ανάπτυξη του προγράμματος
Τώρα που έχουμε έτοιμο το πρόγραμμα και τον πελάτη μας, ας δημιουργήσουμε, αναπτύξουμε και καλέσουμε το πρόγραμμα.
Δημιουργία του προγράμματος
Πρώτα, ας δημιουργήσουμε το πρόγραμμά μας.
$cargo build-sbf
Αυτή η εντολή μεταγλωττίζει το πρόγραμμά σας και δημιουργεί δύο σημαντικά αρχεία
στο target/deploy/
:
counter_program.so # The compiled programcounter_program-keypair.json # Keypair for the program ID
Μπορείτε να δείτε το αναγνωριστικό του προγράμματός σας εκτελώντας την ακόλουθη εντολή:
$solana address -k ./target/deploy/counter_program-keypair.json
Παράδειγμα εξόδου:
HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
Εκκίνηση τοπικού validator
Για ανάπτυξη, θα χρησιμοποιήσουμε έναν τοπικό validator δοκιμών.
Πρώτα, ρυθμίστε το Solana CLI ώστε να χρησιμοποιεί το localhost:
$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
Τώρα ξεκινήστε τον validator δοκιμών σε ξεχωριστό τερματικό:
$solana-test-validator
Ανάπτυξη του προγράμματος
Με τον validator σε λειτουργία, αναπτύξτε το πρόγραμμά σας στο τοπικό cluster:
$solana program deploy ./target/deploy/counter_program.so
Παράδειγμα εξόδου:
Program Id: HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJFSignature: 5xKdnh3dDFnZXB5UevYYkFBpCVcuqo5SaUPLnryFWY7eQD2CJxaeVDKjQ4ezQVJfkGNqZGYqMZBNqymPKwCQQx5h
Μπορείτε να επαληθεύσετε την ανάπτυξη χρησιμοποιώντας την εντολή
solana program show
με το αναγνωριστικό του προγράμματός σας:
$solana program show HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
Παράδειγμα εξόδου:
Program Id: HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJFOwner: BPFLoaderUpgradeab1e11111111111111111111111ProgramData Address: 47MVf5tRZ4zWXQMX7ydrkgcFQr8XTk1QBjohwsUzaiuMAuthority: 4kh6HxYZiAebF8HWLsUWod2EaQQ6iWHpHYCz8UcmFbM1Last Deployed In Slot: 16Data Length: 82696 (0x14308) bytesBalance: 0.57676824 SOL
Εκτέλεση του client
Με τον τοπικό validator να εξακολουθεί να τρέχει, εκτελέστε τον client:
$cargo run --example client
Αναμενόμενη έξοδος:
Requesting airdrop...Airdrop confirmedInitializing counter...Counter initialized!Transaction: 2uenChtqNeLC1fitqoVE2LBeygSBTDchMZ4gGqs7AiDvZZVJguLDE5PfxsfkgY7xs6zFWnYsbEtb82dWv9tDT14kCounter address: EppPAmwqD42u4SCPWpPT7wmWKdFad5VnM9J4R9ZfofcyIncrementing counter...Counter incremented!Transaction: 4qv1Rx6FHu1M3woVgDQ6KtYUaJgBzGcHnhej76ZpaKGCgsTorbcHnPKxoH916UENw7X5ppnQ8PkPnhXxEwrYuUxS
Με τον τοπικό validator σε λειτουργία, μπορείτε να δείτε τις συναλλαγές στον
Solana Explorer χρησιμοποιώντας
τις υπογραφές συναλλαγών που εμφανίζονται στην έξοδο. Σημειώστε ότι το cluster
στον Solana Explorer πρέπει να οριστεί σε "Custom RPC URL", το οποίο έχει
προεπιλεγμένη τιμή http://localhost:8899
στην οποία τρέχει ο
solana-test-validator
.
Is this page helpful?