Rust-Programmstruktur
In Rust geschriebene Solana-Programme haben minimale strukturelle Anforderungen
und bieten Flexibilität bei der Organisation des Codes. Die einzige Anforderung
ist, dass ein Programm einen entrypoint
haben muss, der definiert, wo die
Ausführung eines Programms beginnt.
Programmstruktur
Obwohl es keine strengen Regeln für die Dateistruktur gibt, folgen Solana-Programme typischerweise einem gemeinsamen Muster:
entrypoint.rs
: Definiert den Einstiegspunkt, der eingehende Anweisungen weiterleitet.state.rs
: Definiert den Programmzustand (Kontendaten).instructions.rs
: Definiert die Anweisungen, die das Programm ausführen kann.processor.rs
: Definiert die Anweisungshandler (Funktionen), die die Geschäftslogik für jede Anweisung implementieren.error.rs
: Definiert benutzerdefinierte Fehler, die das Programm zurückgeben kann.
Siehe zum Beispiel das Token-Programm.
Beispielprogramm
Um zu demonstrieren, wie man ein natives Rust-Programm mit mehreren Anweisungen erstellt, werden wir ein einfaches Zählerprogramm durchgehen, das zwei Anweisungen implementiert:
InitializeCounter
: Erstellt und initialisiert ein neues Konto mit einem Anfangswert.IncrementCounter
: Erhöht den in einem bestehenden Konto gespeicherten Wert.
Der Einfachheit halber wird das Programm in einer einzigen lib.rs
-Datei
implementiert, obwohl Sie in der Praxis größere Programme möglicherweise auf
mehrere Dateien aufteilen möchten.
Teil 1: Das Programm schreiben
Beginnen wir mit dem Aufbau des Zählerprogramms. Wir erstellen ein Programm, das einen Zähler mit einem Startwert initialisieren und erhöhen kann.
Erstellen eines neuen Programms
Zuerst erstellen wir ein neues Rust-Projekt für unser Solana-Programm.
$cargo new counter_program --lib$cd counter_program
Du solltest die Standard-Dateien src/lib.rs
und Cargo.toml
sehen.
Aktualisiere das Feld edition
in Cargo.toml
auf 2021. Andernfalls könntest
du beim Erstellen des Programms auf einen Fehler stoßen.
Abhängigkeiten hinzufügen
Jetzt fügen wir die notwendigen Abhängigkeiten für die Entwicklung eines
Solana-Programms hinzu. Wir benötigen solana-program
für das Core SDK und
borsh
für die Serialisierung.
$cargo add solana-program@2.2.0$cargo add borsh
Es gibt keine Verpflichtung, Borsh zu verwenden. Es ist jedoch eine häufig verwendete Serialisierungsbibliothek für Solana-Programme.
Crate-Type konfigurieren
Solana-Programme müssen als dynamische Bibliotheken kompiliert werden. Füge den
Abschnitt [lib]
hinzu, um zu konfigurieren, wie Cargo das Programm erstellt.
[lib]crate-type = ["cdylib", "lib"]
Wenn du diese Konfiguration nicht einschließt, wird das Verzeichnis target/deploy nicht generiert, wenn du das Programm erstellst.
Programm-Einstiegspunkt einrichten
Jedes Solana-Programm hat einen Einstiegspunkt, also die Funktion, die aufgerufen wird, wenn das Programm ausgeführt wird. Beginnen wir damit, die Imports hinzuzufügen, die wir für das Programm benötigen, und den Einstiegspunkt einzurichten.
Füge den folgenden Code zu lib.rs
hinzu:
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(())}
Das
entrypoint
Makro übernimmt die Deserialisierung der input
Daten in die Parameter der
process_instruction
Funktion.
Ein Solana-Programm entrypoint
hat die folgende Funktionssignatur. Entwickler
können ihre eigene Implementierung der entrypoint
Funktion erstellen.
#[no_mangle]pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64;
Programmzustand definieren
Jetzt definieren wir die Datenstruktur, die in unseren Counter-Konten
gespeichert wird. Dies sind die Daten, die im Feld data
des Kontos gespeichert
werden.
Fügen Sie den folgenden Code zu lib.rs
hinzu:
#[derive(BorshSerialize, BorshDeserialize, Debug)]pub struct CounterAccount {pub count: u64,}
Anweisungs-Enum definieren
Definieren wir die Anweisungen, die unser Programm ausführen kann. Wir verwenden ein Enum, bei dem jede Variante eine andere Anweisung darstellt.
Fügen Sie den folgenden Code zu lib.rs
hinzu:
#[derive(BorshSerialize, BorshDeserialize, Debug)]pub enum CounterInstruction {InitializeCounter { initial_value: u64 },IncrementCounter,}
Implementierung der Anweisungs-Deserialisierung
Jetzt müssen wir die instruction_data
(Rohbytes) in eine unserer
CounterInstruction
Enum-Varianten deserialisieren. Die Borsh-Methode
try_from_slice
übernimmt diese Umwandlung automatisch.
Aktualisieren Sie die Funktion process_instruction
, um die
Borsh-Deserialisierung zu verwenden:
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(())}
Anweisungen an Handler weiterleiten
Jetzt aktualisieren wir die Hauptfunktion process_instruction
, um Anweisungen
an ihre entsprechenden Handler-Funktionen weiterzuleiten.
Dieses Routing-Muster ist in Solana-Programmen üblich. Die instruction_data
wird in eine Variante eines Enums deserialisiert, das die Anweisung
repräsentiert, dann wird die entsprechende Handler-Funktion aufgerufen. Jede
Handler-Funktion enthält die Implementierung für diese Anweisung.
Fügen Sie den folgenden Code zu lib.rs
hinzu, um die Funktion
process_instruction
zu aktualisieren und die Handler für die Anweisungen
InitializeCounter
und IncrementCounter
hinzuzufügen:
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(())}
Initialisierungs-Handler implementieren
Implementieren wir den Handler, um ein neues Counter-Konto zu erstellen und zu initialisieren. Da nur das System Program Konten auf Solana erstellen kann, verwenden wir eine Cross Program Invocation (CPI), im Wesentlichen rufen wir ein anderes Programm von unserem Programm aus auf.
Unser Programm führt einen CPI aus, um die Anweisung create_account
des System
Program aufzurufen. Das neue Konto wird mit unserem Programm als Eigentümer
erstellt, was unserem Programm die Möglichkeit gibt, in das Konto zu schreiben
und die Daten zu initialisieren.
Füge den folgenden Code zu lib.rs
hinzu, um die process_initialize_counter
Funktion zu aktualisieren:
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(())}
Diese Anweisung dient nur zu Demonstrationszwecken. Sie enthält keine Sicherheits- und Validierungsprüfungen, die für Programme im Produktivbetrieb erforderlich sind.
Implementiere den Increment-Handler
Jetzt implementieren wir den Handler, der einen vorhandenen Zähler erhöht. Diese Anweisung:
- Liest das
data
Feld des Konten für dascounter_account
- Deserialisiert es in eine
CounterAccount
Struktur - Erhöht das
count
Feld um 1 - Serialisiert die
CounterAccount
Struktur zurück in dasdata
Feld des Konten
Füge den folgenden Code zu lib.rs
hinzu, um die process_increment_counter
Funktion zu aktualisieren:
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(())}
Diese Anweisung dient nur zu Demonstrationszwecken. Sie enthält keine Sicherheits- und Validierungsprüfungen, die für Programme im Produktivbetrieb erforderlich sind.
Fertiges Programm
Glückwunsch! Du hast ein vollständiges Solana-Programm erstellt, das die grundlegende Struktur zeigt, die allen Solana-Programmen gemeinsam ist:
- Entrypoint: Definiert, wo die Programmausführung beginnt und leitet alle eingehenden Anfragen an die entsprechenden Anweisungshandler weiter
- Anweisungsverarbeitung: Definiert Anweisungen und ihre zugehörigen Handler- Funktionen
- Zustandsverwaltung: Definiert Datenstrukturen für Konten und verwaltet deren Zustand in programmeigenen Konten
- Cross Program Invocation (CPI): Ruft das System Program auf, um neue programmeigene Konten zu erstellen
Der nächste Schritt ist, das Programm zu testen, um sicherzustellen, dass alles korrekt funktioniert.
Teil 2: Testen des Programms
Jetzt testen wir unser Counter-Programm. Wir verwenden LiteSVM, ein Test-Framework, das uns ermöglicht, Programme zu testen, ohne sie auf einem Cluster zu deployen.
Füge Test-Abhängigkeiten hinzu
Zuerst fügen wir die für das Testen benötigten Abhängigkeiten hinzu. Wir
verwenden litesvm
für das Testen und solana-sdk
.
$cargo add litesvm@0.6.1 --dev$cargo add solana-sdk@2.2.0 --dev
Testmodul erstellen
Jetzt fügen wir unserem Programm ein Testmodul hinzu. Wir beginnen mit dem grundlegenden Gerüst und den Importen.
Füge den folgenden Code zu lib.rs
hinzu, direkt unter dem Programmcode:
#[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}}
Das Attribut #[cfg(test)]
stellt sicher, dass dieser Code nur beim Ausführen
von Tests kompiliert wird.
Testumgebung initialisieren
Richten wir die Testumgebung mit LiteSVM ein und finanzieren ein Zahlerkonto.
LiteSVM simuliert die Solana-Laufzeitumgebung und ermöglicht es uns, unser Programm zu testen, ohne es auf einem echten Cluster bereitzustellen.
Füge den folgenden Code zu lib.rs
hinzu und aktualisiere die Funktion
test_counter_program
:
let mut svm = LiteSVM::new();let payer = Keypair::new();svm.airdrop(&payer.pubkey(), 1_000_000_000).expect("Failed to airdrop");
Programm laden
Jetzt müssen wir unser Programm erstellen und in die Testumgebung laden. Führe
den Befehl cargo build-sbf
aus, um das Programm zu erstellen. Dies generiert
die Datei counter_program.so
im Verzeichnis target/deploy
.
$cargo build-sbf
Stelle sicher, dass edition
in Cargo.toml
auf 2021
gesetzt ist.
Nach dem Erstellen können wir das Programm laden.
Aktualisiere die Funktion test_counter_program
, um das Programm in die
Testumgebung zu laden.
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");
Du musst cargo build-sbf
ausführen, bevor du Tests startest, um die Datei
.so
zu generieren. Der Test lädt das kompilierte Programm.
Initialisierungs-Anweisung testen
Testen wir die Initialisierungs-Anweisung, indem wir ein neues Zählerkonto mit einem Startwert erstellen.
Fügen Sie den folgenden Code zu lib.rs
hinzu, um die Funktion
test_counter_program
zu aktualisieren:
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);
Initialisierung überprüfen
Nach der Initialisierung sollten wir überprüfen, ob das Konten-Konto korrekt mit dem erwarteten Wert erstellt wurde.
Fügen Sie den folgenden Code zu lib.rs
hinzu, um die Funktion
test_counter_program
zu aktualisieren:
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);
Inkrement-Anweisung testen
Jetzt testen wir die Inkrement-Anweisung, um sicherzustellen, dass sie den Zählerwert korrekt aktualisiert.
Fügen Sie den folgenden Code zu lib.rs
hinzu, um die Funktion
test_counter_program
zu aktualisieren:
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);
Endergebnisse überprüfen
Zum Schluss überprüfen wir, ob die Inkrementierung korrekt funktioniert hat, indem wir den aktualisierten Zählerwert kontrollieren.
Fügen Sie den folgenden Code zu lib.rs
hinzu, um die Funktion
test_counter_program
zu aktualisieren:
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);
Führen Sie die Tests mit dem folgenden Befehl aus. Das Flag --nocapture
gibt
die Ausgabe des Tests aus.
$cargo test -- --nocapture
Erwartete Ausgabe:
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
Teil 3: Das Programm aufrufen
Jetzt fügen wir ein Client-Skript hinzu, um das Programm aufzurufen.
Client-Beispiel erstellen
Lassen Sie uns einen Rust-Client erstellen, um mit unserem bereitgestellten Programm zu interagieren.
$mkdir examples$touch examples/client.rs
Fügen Sie die folgende Konfiguration zu Cargo.toml
hinzu:
[[example]]name = "client"path = "examples/client.rs"
Installieren Sie die Client-Abhängigkeiten:
$cargo add solana-client@2.2.0 --dev$cargo add tokio --dev
Client-Code implementieren
Jetzt implementieren wir den Client, der unser bereitgestelltes Programm aufrufen wird.
Führen Sie den folgenden Befehl aus, um Ihre Programm-ID aus der keypair-Datei zu erhalten:
$solana address -k ./target/deploy/counter_program-keypair.json
Fügen Sie den Client-Code zu examples/client.rs
hinzu und ersetzen Sie die
program_id
mit der Ausgabe des vorherigen Befehls:
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);}}}
Teil 4: Bereitstellung des Programms
Nachdem wir unser Programm und den Client vorbereitet haben, lassen Sie uns das Programm bauen, bereitstellen und aufrufen.
Programm bauen
Zuerst bauen wir unser Programm.
$cargo build-sbf
Dieser Befehl kompiliert dein Programm und generiert zwei wichtige Dateien in
target/deploy/
:
counter_program.so # The compiled programcounter_program-keypair.json # Keypair for the program ID
Du kannst die ID deines Programms anzeigen, indem du folgenden Befehl ausführst:
$solana address -k ./target/deploy/counter_program-keypair.json
Beispielausgabe:
HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
Lokalen Validator starten
Für die Entwicklung verwenden wir einen lokalen Test-Validator.
Konfiguriere zunächst die Solana CLI für die Verwendung von localhost:
$solana config set -ul
Beispielausgabe:
Config File: ~/.config/solana/cli/config.ymlRPC URL: http://localhost:8899WebSocket URL: ws://localhost:8900/ (computed)Keypair Path: ~/.config/solana/id.jsonCommitment: confirmed
Starte nun den Test-Validator in einem separaten Terminal:
$solana-test-validator
Programm deployen
Wenn der Validator läuft, deploye dein Programm auf den lokalen Cluster:
$solana program deploy ./target/deploy/counter_program.so
Beispielausgabe:
Program Id: HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJFSignature: 5xKdnh3dDFnZXB5UevYYkFBpCVcuqo5SaUPLnryFWY7eQD2CJxaeVDKjQ4ezQVJfkGNqZGYqMZBNqymPKwCQQx5h
Du kannst das Deployment mit dem Befehl solana program show
und deiner
Programm-ID überprüfen:
$solana program show HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
Beispielausgabe:
Program Id: HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJFOwner: BPFLoaderUpgradeab1e11111111111111111111111ProgramData Address: 47MVf5tRZ4zWXQMX7ydrkgcFQr8XTk1QBjohwsUzaiuMAuthority: 4kh6HxYZiAebF8HWLsUWod2EaQQ6iWHpHYCz8UcmFbM1Last Deployed In Slot: 16Data Length: 82696 (0x14308) bytesBalance: 0.57676824 SOL
Client ausführen
Führen Sie den Client aus, während der lokale Validator noch läuft:
$cargo run --example client
Erwartete Ausgabe:
Requesting airdrop...Airdrop confirmedInitializing counter...Counter initialized!Transaction: 2uenChtqNeLC1fitqoVE2LBeygSBTDchMZ4gGqs7AiDvZZVJguLDE5PfxsfkgY7xs6zFWnYsbEtb82dWv9tDT14kCounter address: EppPAmwqD42u4SCPWpPT7wmWKdFad5VnM9J4R9ZfofcyIncrementing counter...Counter incremented!Transaction: 4qv1Rx6FHu1M3woVgDQ6KtYUaJgBzGcHnhej76ZpaKGCgsTorbcHnPKxoH916UENw7X5ppnQ8PkPnhXxEwrYuUxS
Mit dem laufenden lokalen Validator können Sie die Transaktionen auf dem
Solana Explorer anhand der
ausgegebenen Transaktionssignaturen einsehen. Beachten Sie, dass der Cluster im
Solana Explorer auf "Custom RPC URL" eingestellt sein muss, was standardmäßig
auf http://localhost:8899
verweist, auf dem der solana-test-validator
läuft.
Is this page helpful?