Τα προγράμματα Solana που είναι γραμμένα σε Rust έχουν ελάχιστες δομικές
απαιτήσεις, επιτρέποντας ευελιξία στον τρόπο οργάνωσης του κώδικα. Η μόνη
απαίτηση είναι ότι ένα πρόγραμμα πρέπει να έχει ένα entrypoint, το οποίο
ορίζει πού ξεκινά η εκτέλεση ενός προγράμματος.
Δομή προγράμματος
Ενώ δεν υπάρχουν αυστηροί κανόνες για τη δομή αρχείων, τα προγράμματα Solana συνήθως ακολουθούν ένα κοινό μοτίβο:
entrypoint.rs: Ορίζει το σημείο εισόδου που δρομολογεί τις εισερχόμενες εντολές.state.rs: Ορίζει την κατάσταση του προγράμματος (δεδομένα λογαριασμού).instructions.rs: Ορίζει τις εντολές που μπορεί να εκτελέσει το πρόγραμμα.processor.rs: Ορίζει τους χειριστές εντολών (συναρτήσεις) που υλοποιούν τη λογική επιχειρηματικότητας για κάθε εντολή.error.rs: Ορίζει προσαρμοσμένα σφάλματα που μπορεί να επιστρέψει το πρόγραμμα.
Για παράδειγμα, δείτε το Token Program.
Παράδειγμα προγράμματος
Για να δείξουμε πώς να δημιουργήσετε ένα εγγενές πρόγραμμα Rust με πολλαπλές εντολές, θα εξετάσουμε ένα απλό πρόγραμμα μετρητή που υλοποιεί δύο εντολές:
InitializeCounter: Δημιουργεί και αρχικοποιεί έναν νέο λογαριασμό με μια αρχική τιμή.IncrementCounter: Αυξάνει την τιμή που είναι αποθηκευμένη σε έναν υπάρχοντα λογαριασμό.
Για απλότητα, το πρόγραμμα θα υλοποιηθεί σε ένα μόνο αρχείο lib.rs, αν και
στην πράξη μπορεί να θέλετε να χωρίσετε μεγαλύτερα προγράμματα σε πολλαπλά
αρχεία.
Μέρος 1: Συγγραφή του προγράμματος
Ας ξεκινήσουμε δημιουργώντας το πρόγραμμα μετρητή. Θα δημιουργήσουμε ένα πρόγραμμα που μπορεί να αρχικοποιήσει έναν μετρητή με μια αρχική τιμή και να τον αυξήσει.
Δημιουργία νέου προγράμματος
Αρχικά, ας δημιουργήσουμε ένα νέο Rust project για το Solana πρόγραμμά μας.
$cargo new counter_program --lib$cd counter_program
Θα πρέπει να δείτε τα προεπιλεγμένα αρχεία src/lib.rs και Cargo.toml.
Ενημερώστε το πεδίο edition στο Cargo.toml σε 2021. Διαφορετικά, ενδέχεται
να αντιμετωπίσετε σφάλμα κατά τη δημιουργία του προγράμματος.
Προσθήκη εξαρτήσεων
Τώρα ας προσθέσουμε τις απαραίτητες εξαρτήσεις για τη δημιουργία ενός Solana
προγράμματος. Χρειαζόμαστε το solana-program για το βασικό SDK και το borsh
για serialization.
$cargo add solana-program@2.2.0$cargo add borsh
Δεν υπάρχει απαίτηση να χρησιμοποιήσετε το Borsh. Ωστόσο, είναι μια ευρέως χρησιμοποιούμενη βιβλιοθήκη serialization για προγράμματα Solana.
Διαμόρφωση crate-type
Τα προγράμματα Solana πρέπει να μεταγλωττίζονται ως δυναμικές βιβλιοθήκες.
Προσθέστε την ενότητα [lib] για να διαμορφώσετε τον τρόπο με τον οποίο το
Cargo δημιουργεί το πρόγραμμα.
[lib]crate-type = ["cdylib", "lib"]
Εάν δεν συμπεριλάβετε αυτή τη διαμόρφωση, ο κατάλογος target/deploy δεν θα δημιουργηθεί όταν δημιουργείτε το πρόγραμμα.
Ρύθμιση σημείου εισόδου προγράμματος
Κάθε πρόγραμμα Solana έχει ένα σημείο εισόδου, που είναι η συνάρτηση που καλείται όταν επικαλείται το πρόγραμμα. Ας ξεκινήσουμε προσθέτοντας τα imports που θα χρειαστούμε για το πρόγραμμα και ρυθμίζοντας το σημείο εισόδου.
Προσθέστε τον ακόλουθο κώδικα στο 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
macro χειρίζεται την αποσειριοποίηση των δεδομένων input στις παραμέτρους της
συνάρτησης process_instruction.
Ένα entrypoint προγράμματος Solana έχει την ακόλουθη υπογραφή συνάρτησης. Οι
developers είναι ελεύθεροι να δημιουργήσουν τη δική τους υλοποίηση της
συνάρτησης 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 εντολών
Ας ορίσουμε τις εντολές που μπορεί να εκτελέσει το πρόγραμμά μας. Θα χρησιμοποιήσουμε ένα enum όπου κάθε παραλλαγή αντιπροσωπεύει μια διαφορετική εντολή.
Προσθέστε τον ακόλουθο κώδικα στο lib.rs:
#[derive(BorshSerialize, BorshDeserialize, Debug)]pub enum CounterInstruction {InitializeCounter { initial_value: u64 },IncrementCounter,}
Υλοποίηση αποσειριοποίησης εντολών
Τώρα πρέπει να αποσειριοποιήσουμε το instruction_data (ακατέργαστα bytes) σε
μία από τις παραλλαγές του enum 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 αποσειριοποιείται σε μια παραλλαγή ενός enum που
αντιπροσωπεύει την εντολή, και στη συνέχεια καλείται η κατάλληλη συνάρτηση
χειριστή. Κάθε συνάρτηση χειριστή περιλαμβάνει την υλοποίηση για αυτή την
εντολή.
Προσθέστε τον ακόλουθο κώδικα στο 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:
- Entrypoint: Ορίζει πού ξεκινά η εκτέλεση του προγράμματος και δρομολογεί όλα τα εισερχόμενα αιτήματα στους κατάλληλους χειριστές εντολών
- Χειρισμός εντολών: Ορίζει τις εντολές και τις συναρτήσεις χειριστών που σχετίζονται με αυτές
- Διαχείριση κατάστασης: Ορίζει τις δομές δεδομένων λογαριασμών και διαχειρίζεται την κατάστασή τους σε λογαριασμούς που ανήκουν στο πρόγραμμα
- Cross Program Invocation (CPI): Καλεί το System Program για να δημιουργήσει νέους λογαριασμούς που ανήκουν στο πρόγραμμα
Το επόμενο βήμα είναι να δοκιμάσουμε το πρόγραμμα για να διασφαλίσουμε ότι όλα λειτουργούν σωστά.
Μέρος 2: Δοκιμή του προγράμματος
Τώρα ας δοκιμάσουμε το πρόγραμμα μέτρησης. Θα χρησιμοποιήσουμε το LiteSVM, ένα πλαίσιο δοκιμών που μας επιτρέπει να δοκιμάζουμε προγράμματα χωρίς να τα αναπτύσσουμε σε cluster.
Προσθήκη εξαρτήσεων δοκιμών
Πρώτα, ας προσθέσουμε τις εξαρτήσεις που χρειάζονται για τις δοκιμές. Θα
χρησιμοποιήσουμε το 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, επιτρέποντάς μας να δοκιμάσουμε το πρόγραμμά μας χωρίς να το αναπτύξουμε σε πραγματικό cluster.
Προσθέστε τον ακόλουθο κώδικα στο 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);
Εκτελέστε τα tests με την ακόλουθη εντολή. Η σημαία --nocapture εμφανίζει την
έξοδο του test.
$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: Κλήση του προγράμματος
Τώρα ας προσθέσουμε ένα client script για να καλέσουμε το πρόγραμμα.
Δημιουργία παραδείγματος client
Ας δημιουργήσουμε έναν Rust client για να αλληλεπιδράσουμε με το αναπτυγμένο πρόγραμμά μας.
$mkdir examples$touch examples/client.rs
Προσθέστε την ακόλουθη διαμόρφωση στο Cargo.toml:
[[example]]name = "client"path = "examples/client.rs"
Εγκαταστήστε τις εξαρτήσεις του client:
$cargo add solana-client@2.2.0 --dev$cargo add tokio --dev
Υλοποίηση κώδικα client
Τώρα ας υλοποιήσουμε τον client που θα καλέσει το αναπτυγμένο πρόγραμμά μας.
Εκτελέστε την ακόλουθη εντολή για να λάβετε το program ID από το αρχείο keypair:
$solana address -k ./target/deploy/counter_program-keypair.json
Προσθέστε τον κώδικα client στο 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: Ανάπτυξη του προγράμματος
Τώρα που έχουμε έτοιμο το πρόγραμμά μας και τον πελάτη, ας κάνουμε build, deploy και κλήση του προγράμματος.
Κάντε build το πρόγραμμα
Πρώτα, ας κάνουμε build το πρόγραμμά μας.
$cargo build-sbf
Αυτή η εντολή μεταγλωττίζει το πρόγραμμά σας και δημιουργεί δύο σημαντικά αρχεία
στο target/deploy/:
counter_program.so # The compiled programcounter_program-keypair.json # Keypair for the program ID
Μπορείτε να δείτε το 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 με το program ID σας:
$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?