Τεκμηρίωση SolanaΑνάπτυξη προγραμμάτωνΠρογράμματα Rust

Δομή προγράμματος Rust

Τα προγράμματα Solana που είναι γραμμένα σε Rust έχουν ελάχιστες δομικές απαιτήσεις, επιτρέποντας ευελιξία στον τρόπο οργάνωσης του κώδικα. Η μόνη απαίτηση είναι ότι ένα πρόγραμμα πρέπει να έχει ένα entrypoint, το οποίο ορίζει πού ξεκινά η εκτέλεση ενός προγράμματος.

Δομή προγράμματος

Ενώ δεν υπάρχουν αυστηροί κανόνες για τη δομή αρχείων, τα προγράμματα Solana συνήθως ακολουθούν ένα κοινό μοτίβο:

  • entrypoint.rs: Ορίζει το σημείο εισόδου που δρομολογεί τις εισερχόμενες εντολές.
  • state.rs: Ορίζει την κατάσταση του προγράμματος (δεδομένα λογαριασμού).
  • instructions.rs: Ορίζει τις εντολές που μπορεί να εκτελέσει το πρόγραμμα.
  • processor.rs: Ορίζει τους χειριστές εντολών (συναρτήσεις) που υλοποιούν τη λογική επιχειρηματικότητας για κάθε εντολή.
  • error.rs: Ορίζει προσαρμοσμένα σφάλματα που μπορεί να επιστρέψει το πρόγραμμα.

Για παράδειγμα, δείτε το Token Program.

Παράδειγμα προγράμματος

Για να δείξουμε πώς να δημιουργήσετε ένα εγγενές πρόγραμμα Rust με πολλαπλές εντολές, θα εξετάσουμε ένα απλό πρόγραμμα μετρητή που υλοποιεί δύο εντολές:

  1. InitializeCounter: Δημιουργεί και αρχικοποιεί έναν νέο λογαριασμό με μια αρχική τιμή.
  2. IncrementCounter: Αυξάνει την τιμή που είναι αποθηκευμένη σε έναν υπάρχοντα λογαριασμό.

Για απλότητα, το πρόγραμμα θα υλοποιηθεί σε ένα μόνο αρχείο lib.rs, αν και στην πράξη μπορεί να θέλετε να χωρίσετε μεγαλύτερα προγράμματα σε πολλαπλά αρχεία.

Μέρος 1: Συγγραφή του προγράμματος

Ας ξεκινήσουμε δημιουργώντας το πρόγραμμα μετρητή. Θα δημιουργήσουμε ένα πρόγραμμα που μπορεί να αρχικοποιήσει έναν μετρητή με μια αρχική τιμή και να τον αυξήσει.

Δημιουργία νέου προγράμματος

Αρχικά, ας δημιουργήσουμε ένα νέο Rust project για το Solana πρόγραμμά μας.

Terminal
$
cargo new counter_program --lib
$
cd counter_program

Θα πρέπει να δείτε τα προεπιλεγμένα αρχεία src/lib.rs και Cargo.toml.

Ενημερώστε το πεδίο edition στο Cargo.toml σε 2021. Διαφορετικά, ενδέχεται να αντιμετωπίσετε σφάλμα κατά τη δημιουργία του προγράμματος.

Προσθήκη εξαρτήσεων

Τώρα ας προσθέσουμε τις απαραίτητες εξαρτήσεις για τη δημιουργία ενός Solana προγράμματος. Χρειαζόμαστε το solana-program για το βασικό SDK και το borsh για serialization.

Terminal
$
cargo add solana-program@2.2.0
$
cargo add borsh

Δεν υπάρχει απαίτηση να χρησιμοποιήσετε το Borsh. Ωστόσο, είναι μια ευρέως χρησιμοποιούμενη βιβλιοθήκη serialization για προγράμματα Solana.

Διαμόρφωση crate-type

Τα προγράμματα Solana πρέπει να μεταγλωττίζονται ως δυναμικές βιβλιοθήκες. Προσθέστε την ενότητα [lib] για να διαμορφώσετε τον τρόπο με τον οποίο το Cargo δημιουργεί το πρόγραμμα.

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

Εάν δεν συμπεριλάβετε αυτή τη διαμόρφωση, ο κατάλογος target/deploy δεν θα δημιουργηθεί όταν δημιουργείτε το πρόγραμμα.

Ρύθμιση σημείου εισόδου προγράμματος

Κάθε πρόγραμμα Solana έχει ένα σημείο εισόδου, που είναι η συνάρτηση που καλείται όταν επικαλείται το πρόγραμμα. Ας ξεκινήσουμε προσθέτοντας τα imports που θα χρειαστούμε για το πρόγραμμα και ρυθμίζοντας το σημείο εισόδου.

Προσθέστε τον ακόλουθο κώδικα στο lib.rs:

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:

lib.rs
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
pub count: u64,
}

Ορισμός enum εντολών

Ας ορίσουμε τις εντολές που μπορεί να εκτελέσει το πρόγραμμά μας. Θα χρησιμοποιήσουμε ένα enum όπου κάθε παραλλαγή αντιπροσωπεύει μια διαφορετική εντολή.

Προσθέστε τον ακόλουθο κώδικα στο lib.rs:

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:

lib.rs
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:

lib.rs
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:

lib.rs
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:

lib.rs
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 για να δημιουργήσει νέους λογαριασμούς που ανήκουν στο πρόγραμμα

Το επόμενο βήμα είναι να δοκιμάσουμε το πρόγραμμα για να διασφαλίσουμε ότι όλα λειτουργούν σωστά.

Δημιουργία νέου προγράμματος

Αρχικά, ας δημιουργήσουμε ένα νέο Rust project για το Solana πρόγραμμά μας.

Terminal
$
cargo new counter_program --lib
$
cd counter_program

Θα πρέπει να δείτε τα προεπιλεγμένα αρχεία src/lib.rs και Cargo.toml.

Ενημερώστε το πεδίο edition στο Cargo.toml σε 2021. Διαφορετικά, ενδέχεται να αντιμετωπίσετε σφάλμα κατά τη δημιουργία του προγράμματος.

Προσθήκη εξαρτήσεων

Τώρα ας προσθέσουμε τις απαραίτητες εξαρτήσεις για τη δημιουργία ενός Solana προγράμματος. Χρειαζόμαστε το solana-program για το βασικό SDK και το borsh για serialization.

Terminal
$
cargo add solana-program@2.2.0
$
cargo add borsh

Δεν υπάρχει απαίτηση να χρησιμοποιήσετε το Borsh. Ωστόσο, είναι μια ευρέως χρησιμοποιούμενη βιβλιοθήκη serialization για προγράμματα Solana.

Διαμόρφωση crate-type

Τα προγράμματα Solana πρέπει να μεταγλωττίζονται ως δυναμικές βιβλιοθήκες. Προσθέστε την ενότητα [lib] για να διαμορφώσετε τον τρόπο με τον οποίο το Cargo δημιουργεί το πρόγραμμα.

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

Εάν δεν συμπεριλάβετε αυτή τη διαμόρφωση, ο κατάλογος target/deploy δεν θα δημιουργηθεί όταν δημιουργείτε το πρόγραμμα.

Ρύθμιση σημείου εισόδου προγράμματος

Κάθε πρόγραμμα Solana έχει ένα σημείο εισόδου, που είναι η συνάρτηση που καλείται όταν επικαλείται το πρόγραμμα. Ας ξεκινήσουμε προσθέτοντας τα imports που θα χρειαστούμε για το πρόγραμμα και ρυθμίζοντας το σημείο εισόδου.

Προσθέστε τον ακόλουθο κώδικα στο lib.rs:

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:

lib.rs
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
pub count: u64,
}

Ορισμός enum εντολών

Ας ορίσουμε τις εντολές που μπορεί να εκτελέσει το πρόγραμμά μας. Θα χρησιμοποιήσουμε ένα enum όπου κάθε παραλλαγή αντιπροσωπεύει μια διαφορετική εντολή.

Προσθέστε τον ακόλουθο κώδικα στο lib.rs:

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:

lib.rs
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:

lib.rs
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:

lib.rs
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:

lib.rs
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 για να δημιουργήσει νέους λογαριασμούς που ανήκουν στο πρόγραμμα

Το επόμενο βήμα είναι να δοκιμάσουμε το πρόγραμμα για να διασφαλίσουμε ότι όλα λειτουργούν σωστά.

Cargo.toml
lib.rs
[package]
name = "counter_program"
version = "0.1.0"
edition = "2021"
[dependencies]

Μέρος 2: Δοκιμή του προγράμματος

Τώρα ας δοκιμάσουμε το πρόγραμμα μέτρησης. Θα χρησιμοποιήσουμε το LiteSVM, ένα πλαίσιο δοκιμών που μας επιτρέπει να δοκιμάζουμε προγράμματα χωρίς να τα αναπτύσσουμε σε cluster.

Προσθήκη εξαρτήσεων δοκιμών

Πρώτα, ας προσθέσουμε τις εξαρτήσεις που χρειάζονται για τις δοκιμές. Θα χρησιμοποιήσουμε το litesvm για δοκιμές και το solana-sdk.

Terminal
$
cargo add litesvm@0.6.1 --dev
$
cargo add solana-sdk@2.2.0 --dev

Δημιουργία ενότητας δοκιμών

Τώρα ας προσθέσουμε μια ενότητα δοκιμών στο πρόγραμμά μας. Θα ξεκινήσουμε με τη βασική δομή και τις εισαγωγές.

Προσθέστε τον ακόλουθο κώδικα στο lib.rs, ακριβώς κάτω από τον κώδικα του προγράμματος:

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:

lib.rs
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.

Terminal
$
cargo build-sbf

Βεβαιωθείτε ότι το edition στο Cargo.toml έχει οριστεί σε 2021.

Μετά τη δημιουργία, μπορούμε να φορτώσουμε το πρόγραμμα.

Ενημερώστε τη συνάρτηση test_counter_program για να φορτώσετε το πρόγραμμα στο περιβάλλον δοκιμών.

lib.rs
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:

lib.rs
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:

lib.rs
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:

lib.rs
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:

lib.rs
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.

Terminal
$
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: 42
Testing 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

Προσθήκη εξαρτήσεων δοκιμών

Πρώτα, ας προσθέσουμε τις εξαρτήσεις που χρειάζονται για τις δοκιμές. Θα χρησιμοποιήσουμε το litesvm για δοκιμές και το solana-sdk.

Terminal
$
cargo add litesvm@0.6.1 --dev
$
cargo add solana-sdk@2.2.0 --dev

Δημιουργία ενότητας δοκιμών

Τώρα ας προσθέσουμε μια ενότητα δοκιμών στο πρόγραμμά μας. Θα ξεκινήσουμε με τη βασική δομή και τις εισαγωγές.

Προσθέστε τον ακόλουθο κώδικα στο lib.rs, ακριβώς κάτω από τον κώδικα του προγράμματος:

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:

lib.rs
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.

Terminal
$
cargo build-sbf

Βεβαιωθείτε ότι το edition στο Cargo.toml έχει οριστεί σε 2021.

Μετά τη δημιουργία, μπορούμε να φορτώσουμε το πρόγραμμα.

Ενημερώστε τη συνάρτηση test_counter_program για να φορτώσετε το πρόγραμμα στο περιβάλλον δοκιμών.

lib.rs
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:

lib.rs
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:

lib.rs
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:

lib.rs
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:

lib.rs
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.

Terminal
$
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: 42
Testing 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
Cargo.toml
[package]
name = "counter_program"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
[dependencies]
borsh = "1.5.7"
solana-program = "2.2.0"
[dev-dependencies]
litesvm = "0.6.1"
solana-sdk = "2.2.0"

Μέρος 3: Κλήση του προγράμματος

Τώρα ας προσθέσουμε ένα client script για να καλέσουμε το πρόγραμμα.

Δημιουργία παραδείγματος client

Ας δημιουργήσουμε έναν Rust client για να αλληλεπιδράσουμε με το αναπτυγμένο πρόγραμμά μας.

Terminal
$
mkdir examples
$
touch examples/client.rs

Προσθέστε την ακόλουθη διαμόρφωση στο Cargo.toml:

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

Εγκαταστήστε τις εξαρτήσεις του client:

Terminal
$
cargo add solana-client@2.2.0 --dev
$
cargo add tokio --dev

Υλοποίηση κώδικα client

Τώρα ας υλοποιήσουμε τον client που θα καλέσει το αναπτυγμένο πρόγραμμά μας.

Εκτελέστε την ακόλουθη εντολή για να λάβετε το program ID από το αρχείο keypair:

Terminal
$
solana address -k ./target/deploy/counter_program-keypair.json

Προσθέστε τον κώδικα client στο examples/client.rs και αντικαταστήστε το program_id με την έξοδο της προηγούμενης εντολής:

examples/client.rs
let program_id = Pubkey::from_str("BDLLezrtFEXVGYqG3aS7eAC7GVeojJ4JHhKJM6pAFCDH")
.expect("Invalid program ID");
examples/client.rs
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 deployment
let program_id = Pubkey::from_str("BDLLezrtFEXVGYqG3aS7eAC7GVeojJ4JHhKJM6pAFCDH")
.expect("Invalid program ID");
// Connect to local cluster
let rpc_url = String::from("http://localhost:8899");
let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());
// Generate a new keypair for paying fees
let payer = Keypair::new();
// Request airdrop of 1 SOL for transaction fees
println!("Requesting airdrop...");
let airdrop_signature = client
.request_airdrop(&payer.pubkey(), 1_000_000_000)
.expect("Failed to request airdrop");
// Wait for airdrop confirmation
loop {
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 data
let 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 data
let 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);
}
}
}

Δημιουργία παραδείγματος client

Ας δημιουργήσουμε έναν Rust client για να αλληλεπιδράσουμε με το αναπτυγμένο πρόγραμμά μας.

Terminal
$
mkdir examples
$
touch examples/client.rs

Προσθέστε την ακόλουθη διαμόρφωση στο Cargo.toml:

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

Εγκαταστήστε τις εξαρτήσεις του client:

Terminal
$
cargo add solana-client@2.2.0 --dev
$
cargo add tokio --dev

Υλοποίηση κώδικα client

Τώρα ας υλοποιήσουμε τον client που θα καλέσει το αναπτυγμένο πρόγραμμά μας.

Εκτελέστε την ακόλουθη εντολή για να λάβετε το program ID από το αρχείο keypair:

Terminal
$
solana address -k ./target/deploy/counter_program-keypair.json

Προσθέστε τον κώδικα client στο examples/client.rs και αντικαταστήστε το program_id με την έξοδο της προηγούμενης εντολής:

examples/client.rs
let program_id = Pubkey::from_str("BDLLezrtFEXVGYqG3aS7eAC7GVeojJ4JHhKJM6pAFCDH")
.expect("Invalid program ID");
Cargo.toml
[package]
name = "counter_program"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
[dependencies]
borsh = "1.5.7"
solana-program = "2.2.0"
[dev-dependencies]
litesvm = "0.6.1"
solana-sdk = "2.2.0"
solana-client = "2.2.0"
tokio = "1.47.1"
[[example]]
name = "client"
path = "examples/client.rs"

Μέρος 4: Ανάπτυξη του προγράμματος

Τώρα που έχουμε έτοιμο το πρόγραμμά μας και τον πελάτη, ας κάνουμε build, deploy και κλήση του προγράμματος.

Κάντε build το πρόγραμμα

Πρώτα, ας κάνουμε build το πρόγραμμά μας.

Terminal
$
cargo build-sbf

Αυτή η εντολή μεταγλωττίζει το πρόγραμμά σας και δημιουργεί δύο σημαντικά αρχεία στο target/deploy/:

counter_program.so # The compiled program
counter_program-keypair.json # Keypair for the program ID

Μπορείτε να δείτε το ID του προγράμματός σας εκτελώντας την ακόλουθη εντολή:

Terminal
$
solana address -k ./target/deploy/counter_program-keypair.json

Παράδειγμα εξόδου:

HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF

Εκκινήστε τον τοπικό validator

Για ανάπτυξη, θα χρησιμοποιήσουμε έναν τοπικό validator δοκιμών.

Πρώτα, διαμορφώστε το Solana CLI να χρησιμοποιεί το localhost:

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

Τώρα εκκινήστε τον validator δοκιμών σε ξεχωριστό τερματικό:

Terminal
$
solana-test-validator

Ανάπτυξη του προγράμματος

Με τον validator σε λειτουργία, αναπτύξτε το πρόγραμμά σας στο τοπικό cluster:

Terminal
$
solana program deploy ./target/deploy/counter_program.so

Παράδειγμα εξόδου:

Program Id: HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
Signature: 5xKdnh3dDFnZXB5UevYYkFBpCVcuqo5SaUPLnryFWY7eQD2CJxaeVDKjQ4ezQVJfkGNqZGYqMZBNqymPKwCQQx5h

Μπορείτε να επαληθεύσετε την ανάπτυξη χρησιμοποιώντας την εντολή solana program show με το program ID σας:

Terminal
$
solana program show HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF

Παράδειγμα εξόδου:

Program Id: HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
Owner: BPFLoaderUpgradeab1e11111111111111111111111
ProgramData Address: 47MVf5tRZ4zWXQMX7ydrkgcFQr8XTk1QBjohwsUzaiuM
Authority: 4kh6HxYZiAebF8HWLsUWod2EaQQ6iWHpHYCz8UcmFbM1
Last Deployed In Slot: 16
Data Length: 82696 (0x14308) bytes
Balance: 0.57676824 SOL

Εκτέλεση του client

Με τον τοπικό validator ακόμα σε λειτουργία, εκτελέστε τον client:

Terminal
$
cargo run --example client

Αναμενόμενη έξοδος:

Requesting airdrop...
Airdrop confirmed
Initializing counter...
Counter initialized!
Transaction: 2uenChtqNeLC1fitqoVE2LBeygSBTDchMZ4gGqs7AiDvZZVJguLDE5PfxsfkgY7xs6zFWnYsbEtb82dWv9tDT14k
Counter address: EppPAmwqD42u4SCPWpPT7wmWKdFad5VnM9J4R9Zfofcy
Incrementing counter...
Counter incremented!
Transaction: 4qv1Rx6FHu1M3woVgDQ6KtYUaJgBzGcHnhej76ZpaKGCgsTorbcHnPKxoH916UENw7X5ppnQ8PkPnhXxEwrYuUxS

Με τον τοπικό validator σε λειτουργία, μπορείτε να δείτε τις συναλλαγές στον Solana Explorer χρησιμοποιώντας τις υπογραφές συναλλαγών εξόδου. Σημειώστε ότι το cluster στον Solana Explorer πρέπει να οριστεί σε "Custom RPC URL", το οποίο έχει προεπιλογή στο http://localhost:8899 όπου τρέχει ο solana-test-validator.

Is this page helpful?

Διαχειρίζεται από

© 2026 Ίδρυμα Solana.
Με επιφύλαξη παντός δικαιώματος.
Συνδεθείτε