Τεκμηρίωση 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 για το πρόγραμμά μας στο Solana.

Terminal
$
cargo new counter_program --lib
$
cd counter_program

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

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

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

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

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

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

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

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

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

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

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

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

Προσθέστε τον ακόλουθο κώδικα στο 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 χειρίζεται την αποσειριοποίηση των δεδομένων input στις παραμέτρους της συνάρτησης process_instruction.

Ένα entrypoint προγράμματος Solana έχει την ακόλουθη υπογραφή συνάρτησης. Οι προγραμματιστές είναι ελεύθεροι να δημιουργήσουν τη δική τους υλοποίηση της συνάρτησης 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) όπου κάθε παραλλαγή αντιπροσωπεύει μια διαφορετική εντολή.

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

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:

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 αποσειριοποιείται σε μια παραλλαγή μιας απαρίθμησης που αντιπροσωπεύει την εντολή, και στη συνέχεια καλείται η κατάλληλη συνάρτηση χειρισμού. Κάθε συνάρτηση χειρισμού περιλαμβάνει την υλοποίηση για αυτή την εντολή.

Προσθέστε τον ακόλουθο κώδικα στο 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:

  • Σημείο εισόδου: Καθορίζει πού ξεκινά η εκτέλεση του προγράμματος και δρομολογεί όλα τα εισερχόμενα αιτήματα στους κατάλληλους χειριστές οδηγιών
  • Χειρισμός οδηγιών: Καθορίζει τις οδηγίες και τις σχετικές συναρτήσεις χειριστών τους
  • Διαχείριση κατάστασης: Καθορίζει τις δομές δεδομένων λογαριασμού και διαχειρίζεται την κατάστασή τους σε λογαριασμούς που ανήκουν στο πρόγραμμα
  • Cross Program Invocation (CPI): Καλεί το System Program για να δημιουργήσει νέους λογαριασμούς που ανήκουν στο πρόγραμμα

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

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

Πρώτα, ας δημιουργήσουμε ένα νέο έργο Rust για το πρόγραμμά μας στο Solana.

Terminal
$
cargo new counter_program --lib
$
cd counter_program

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

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

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

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

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

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

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

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

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

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

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

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

Προσθέστε τον ακόλουθο κώδικα στο 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 χειρίζεται την αποσειριοποίηση των δεδομένων input στις παραμέτρους της συνάρτησης process_instruction.

Ένα entrypoint προγράμματος Solana έχει την ακόλουθη υπογραφή συνάρτησης. Οι προγραμματιστές είναι ελεύθεροι να δημιουργήσουν τη δική τους υλοποίηση της συνάρτησης 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) όπου κάθε παραλλαγή αντιπροσωπεύει μια διαφορετική εντολή.

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

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:

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 αποσειριοποιείται σε μια παραλλαγή μιας απαρίθμησης που αντιπροσωπεύει την εντολή, και στη συνέχεια καλείται η κατάλληλη συνάρτηση χειρισμού. Κάθε συνάρτηση χειρισμού περιλαμβάνει την υλοποίηση για αυτή την εντολή.

Προσθέστε τον ακόλουθο κώδικα στο 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:

  • Σημείο εισόδου: Καθορίζει πού ξεκινά η εκτέλεση του προγράμματος και δρομολογεί όλα τα εισερχόμενα αιτήματα στους κατάλληλους χειριστές οδηγιών
  • Χειρισμός οδηγιών: Καθορίζει τις οδηγίες και τις σχετικές συναρτήσεις χειριστών τους
  • Διαχείριση κατάστασης: Καθορίζει τις δομές δεδομένων λογαριασμού και διαχειρίζεται την κατάστασή τους σε λογαριασμούς που ανήκουν στο πρόγραμμα
  • Cross Program Invocation (CPI): Καλεί το System Program για να δημιουργήσει νέους λογαριασμούς που ανήκουν στο πρόγραμμα

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

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

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

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

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

Πρώτα, ας προσθέσουμε τις εξαρτήσεις που χρειάζονται για τις δοκιμές. Θα χρησιμοποιήσουμε το 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, επιτρέποντάς μας να δοκιμάσουμε το πρόγραμμά μας χωρίς να το αναπτύξουμε σε ένα πραγματικό σύμπλεγμα.

Προσθέστε τον ακόλουθο κώδικα στο 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);

Εκτελέστε τις δοκιμές με την ακόλουθη εντολή. Η σημαία --nocapture εμφανίζει την έξοδο της δοκιμής.

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, επιτρέποντάς μας να δοκιμάσουμε το πρόγραμμά μας χωρίς να το αναπτύξουμε σε ένα πραγματικό σύμπλεγμα.

Προσθέστε τον ακόλουθο κώδικα στο 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);

Εκτελέστε τις δοκιμές με την ακόλουθη εντολή. Η σημαία --nocapture εμφανίζει την έξοδο της δοκιμής.

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: Κλήση του προγράμματος

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

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

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

Terminal
$
mkdir examples
$
touch examples/client.rs

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

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

Εγκαταστήστε τις εξαρτήσεις του πελάτη:

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

Υλοποίηση κώδικα πελάτη

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

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

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

Προσθέστε τον κώδικα πελάτη στο 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);
}
}
}

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

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

Terminal
$
mkdir examples
$
touch examples/client.rs

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

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

Εγκαταστήστε τις εξαρτήσεις του πελάτη:

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

Υλοποίηση κώδικα πελάτη

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

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

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

Προσθέστε τον κώδικα πελάτη στο 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: Ανάπτυξη του προγράμματος

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

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

Πρώτα, ας δημιουργήσουμε το πρόγραμμά μας.

Terminal
$
cargo build-sbf

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

counter_program.so # The compiled program
counter_program-keypair.json # Keypair for the program 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 με το αναγνωριστικό του προγράμματός σας:

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?