Program Derived Address
Σε αυτή την ενότητα, θα μάθετε πώς να δημιουργήσετε ένα βασικό πρόγραμμα Δημιουργίας, Ανάγνωσης, Ενημέρωσης, Διαγραφής (CRUD).
Αυτός ο οδηγός παρουσιάζει ένα απλό πρόγραμμα όπου οι χρήστες μπορούν να δημιουργούν, να ενημερώνουν και να διαγράφουν ένα μήνυμα. Κάθε μήνυμα υπάρχει σε έναν λογαριασμό με ντετερμινιστική διεύθυνση που προέρχεται από το ίδιο το πρόγραμμα (Program Derived Address ή PDA).
Αυτός ο οδηγός σας καθοδηγεί στη δημιουργία και τον έλεγχο ενός προγράμματος Solana χρησιμοποιώντας το πλαίσιο Anchor, ενώ παράλληλα επιδεικνύει τα Program Derived Addresses (PDAs). Για περισσότερες λεπτομέρειες, ανατρέξτε στη σελίδα Program Derived Addresses.
Για αναφορά, μπορείτε να δείτε τον τελικό κώδικα αφού ολοκληρώσετε τόσο τις ενότητες PDA όσο και Cross-Program Invocation (CPI).
Αρχικός Κώδικας
Ξεκινήστε ανοίγοντας αυτόν τον σύνδεσμο Solana Playground με τον αρχικό κώδικα. Στη συνέχεια, κάντε κλικ στο κουμπί "Import" για να προσθέσετε το πρόγραμμα στα έργα σας στο Solana Playground.
Εισαγωγή
Στο αρχείο lib.rs
, θα βρείτε ένα πρόγραμμα με τις εντολές
create_message
, update_message
, και
delete_message
που θα προσθέσετε στα επόμενα βήματα.
use anchor_lang::prelude::*;declare_id!("11111111111111111111111111111111");#[program]pub mod message_program {use super::*;pub fn create_message(ctx: Context<CreateMessage>, content: String) -> Result<()> {// TODO: Implement create messageOk(())}pub fn update_message(ctx: Context<UpdateMessage>, content: String) -> Result<()> {// TODO: Implement update messageOk(())}pub fn delete_message(ctx: Context<DeleteMessage>) -> Result<()> {// TODO: Implement delete messageOk(())}}
Πριν ξεκινήσετε, εκτελέστε anchor build
στο τερματικό του Playground για να
ελέγξετε ότι το αρχικό πρόγραμμα μεταγλωττίζεται με επιτυχία.
$ anchor buildBuildOptions {skip_lint: false,mode: Release,features: "",}Compiling proc-macro2 v1.0.66Compiling unicode-ident v1.0.11Compiling quote v1.0.33Compiling syn v1.0.109Compiling thiserror v1.0.48Compiling typenum v1.16.0Compiling version_check v0.9.4Compiling subtle v2.5.0Compiling serde v1.0.188Compiling keccak v0.1.4Compiling serde_derive v1.0.188Compiling sha2 v0.10.7Compiling bs58 v0.4.0Compiling toml_datetime v0.6.3Compiling winnow v0.5.15Compiling generic-array v0.14.7Compiling block-buffer v0.10.4Compiling serde_json v1.0.107Compiling digest v0.10.7Compiling crypto-common v0.1.6Compiling cfg-if v1.0.0Compiling cpufeatures v0.2.9Compiling itoa v1.0.9Compiling ryu v1.0.15Compiling toml_edit v0.19.14Compiling ahash v0.7.6Compiling once_cell v1.18.0Compiling memchr v2.6.3Compiling regex-syntax v0.7.5Compiling regex-automata v0.3.8Compiling regex v1.9.5Compiling hashbrown v0.12.3Compiling solana-frozen-abi-macro v1.16.13Compiling solana-program v1.16.13Compiling thiserror-impl v1.0.48Compiling num-traits v0.2.16Compiling num-integer v0.1.45Compiling num-bigint v0.4.4Compiling solana-frozen-abi v1.16.13Compiling anchor-attribute-constant v0.28.0Compiling anchor-attribute-error v0.28.0Compiling anchor-attribute-interface v0.28.0Compiling anchor-attribute-event v0.28.0Compiling anchor-attribute-account v0.28.0Compiling anchor-derive-accounts v0.28.0Compiling anchor-attribute-program v0.28.0Compiling anchor-attribute-access-control v0.28.0Compiling anchor-attribute-state v0.28.0Compiling anchor-syn v0.28.0Compiling anchor-lang v0.28.0Compiling message_program v0.1.0 (/workspace/message_program/programs/message_program)Finished release [optimized] target(s) in 1m 01s
Ορισμός Τύπου Λογαριασμού Μηνύματος
Πρώτα, ορίστε τη δομή για τον λογαριασμό μηνύματος που δημιουργεί το πρόγραμμα. Αυτή η δομή καθορίζει τα δεδομένα που θα αποθηκευτούν στον λογαριασμό που δημιουργείται από το πρόγραμμα.
Στο lib.rs
, ενημερώστε τη δομή MessageAccount
με τα εξής:
#[account]pub struct MessageAccount {pub user: Pubkey,pub message: String,pub bump: u8,}
Δημιουργήστε ξανά το πρόγραμμα εκτελώντας build
στο τερματικό.
$build
Αυτός ο κώδικας καθορίζει ποια δεδομένα θα αποθηκευτούν στον λογαριασμό μηνύματος. Στη συνέχεια, θα προσθέσετε τις οδηγίες του προγράμματος.
Προσθήκη οδηγίας δημιουργίας
Τώρα, προσθέστε την οδηγία create
που δημιουργεί και αρχικοποιεί το
MessageAccount
.
Ξεκινήστε ορίζοντας τους λογαριασμούς που απαιτούνται για την οδηγία,
ενημερώνοντας τη δομή Create
με τα ακόλουθα:
#[derive(Accounts)]#[instruction(message: String)]pub struct Create<'info> {#[account(mut)]pub user: Signer<'info>,#[account(init,seeds = [b"message", user.key().as_ref()],bump,payer = user,space = 8 + 32 + 4 + message.len() + 1)]pub message_account: Account<'info, MessageAccount>,pub system_program: Program<'info, System>,}
Στη συνέχεια, προσθέστε την επιχειρηματική λογική για την εντολή create
ενημερώνοντας τη συνάρτηση create
με τα ακόλουθα:
pub fn create(ctx: Context<Create>, message: String) -> Result<()> {msg!("Create Message: {}", message);let account_data = &mut ctx.accounts.message_account;account_data.user = ctx.accounts.user.key();account_data.message = message;account_data.bump = ctx.bumps.message_account;Ok(())}
Επαναδημιουργήστε το πρόγραμμα.
$build
Προσθήκη εντολής ενημέρωσης
Στη συνέχεια, προσθέστε την εντολή update
για να αλλάξετε το MessageAccount
με ένα νέο μήνυμα.
Όπως και στο προηγούμενο βήμα, πρώτα καθορίστε τους λογαριασμούς που απαιτούνται
από την εντολή update
.
Ενημερώστε τη δομή Update
με τα ακόλουθα:
#[derive(Accounts)]#[instruction(message: String)]pub struct Update<'info> {#[account(mut)]pub user: Signer<'info>,#[account(mut,seeds = [b"message", user.key().as_ref()],bump = message_account.bump,realloc = 8 + 32 + 4 + message.len() + 1,realloc::payer = user,realloc::zero = true,)]pub message_account: Account<'info, MessageAccount>,pub system_program: Program<'info, System>,}
Στη συνέχεια, προσθέστε τη λογική για την εντολή update
.
pub fn update(ctx: Context<Update>, message: String) -> Result<()> {msg!("Update Message: {}", message);let account_data = &mut ctx.accounts.message_account;account_data.message = message;Ok(())}
Επαναδημιουργήστε το πρόγραμμα
$build
Προσθήκη εντολής διαγραφής
Στη συνέχεια, προσθέστε την εντολή delete
για να κλείσετε το
MessageAccount
.
Ενημερώστε τη δομή Delete
με τα ακόλουθα:
#[derive(Accounts)]pub struct Delete<'info> {#[account(mut)]pub user: Signer<'info>,#[account(mut,seeds = [b"message", user.key().as_ref()],bump = message_account.bump,close = user,)]pub message_account: Account<'info, MessageAccount>,}
Στη συνέχεια, προσθέστε τη λογική για την εντολή delete
.
pub fn delete(_ctx: Context<Delete>) -> Result<()> {msg!("Delete Message");Ok(())}
Επαναδημιουργήστε το πρόγραμμα.
$build
Ανάπτυξη προγράμματος
Έχετε πλέον ολοκληρώσει το βασικό πρόγραμμα CRUD. Αναπτύξτε το πρόγραμμα
εκτελώντας deploy
στο τερματικό του Playground.
Σε αυτό το παράδειγμα, θα αναπτύξετε το πρόγραμμα στο devnet, ένα Solana cluster για δοκιμές ανάπτυξης.
Το πορτοφόλι του Playground συνδέεται στο devnet από προεπιλογή. Βεβαιωθείτε ότι το πορτοφόλι του Playground έχει devnet SOL για να πληρώσει για την ανάπτυξη του προγράμματος. Αποκτήστε devnet SOL από το Solana Faucet.
$deploy
Ρύθμιση αρχείου δοκιμής
Ο αρχικός κώδικας περιλαμβάνει επίσης ένα αρχείο δοκιμών στο anchor.test.ts
.
import { PublicKey } from "@solana/web3.js";describe("pda", () => {it("Create Message Account", async () => {});it("Update Message Account", async () => {});it("Delete Message Account", async () => {});});
Προσθέστε τον παρακάτω κώδικα μέσα στο describe()
, αλλά πριν από τις
ενότητες it()
.
const program = pg.program;const wallet = pg.wallet;const [messagePda, messageBump] = PublicKey.findProgramAddressSync([Buffer.from("message"), wallet.publicKey.toBuffer()],program.programId);
Εκτελέστε το αρχείο δοκιμών τρέχοντας test
στο τερματικό του Playground
για να ελέγξετε ότι εκτελείται όπως αναμένεται. Τα επόμενα βήματα προσθέτουν τις
πραγματικές δοκιμές.
$test
Κλήση της εντολής Create
Ενημερώστε την πρώτη δοκιμή με τα εξής:
it("Create Message Account", async () => {const message = "Hello, World!";const transactionSignature = await program.methods.create(message).accounts({messageAccount: messagePda}).rpc({ commitment: "confirmed" });const messageAccount = await program.account.messageAccount.fetch(messagePda,"confirmed");console.log(JSON.stringify(messageAccount, null, 2));console.log("Transaction Signature:",`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`);});
Επίκληση της εντολής ενημέρωσης
Ενημερώστε τη δεύτερη δοκιμή με τα ακόλουθα:
it("Update Message Account", async () => {const message = "Hello, Solana!";const transactionSignature = await program.methods.update(message).accounts({messageAccount: messagePda}).rpc({ commitment: "confirmed" });const messageAccount = await program.account.messageAccount.fetch(messagePda,"confirmed");console.log(JSON.stringify(messageAccount, null, 2));console.log("Transaction Signature:",`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`);});
Κλήση της εντολής διαγραφής
Ενημερώστε την τρίτη δοκιμή με τα ακόλουθα:
it("Delete Message Account", async () => {const transactionSignature = await program.methods.delete().accounts({messageAccount: messagePda}).rpc({ commitment: "confirmed" });const messageAccount = await program.account.messageAccount.fetchNullable(messagePda,"confirmed");console.log("Expect Null:", JSON.stringify(messageAccount, null, 2));console.log("Transaction Signature:",`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`);});
Εκτέλεση δοκιμής
Αφού προετοιμάσετε τις δοκιμές σας, εκτελέστε το αρχείο δοκιμής με test
στο τερματικό του Playground. Αυτή η εντολή εκτελεί τις δοκιμές στο πρόγραμμα
που έχει αναπτυχθεί στο devnet και καταγράφει συνδέσμους προς το SolanaFM για να
δείτε τις λεπτομέρειες της συναλλαγής.
$test
Εξετάστε τους συνδέσμους SolanaFM για να δείτε τις λεπτομέρειες της συναλλαγής.
Σημειώστε ότι σε αυτό το παράδειγμα, αν εκτελέσετε ξανά τη δοκιμή, η εντολή
create
θα αποτύχει επειδή ο λογαριασμός messageAccount
υπάρχει ήδη. Μόνο
ένας λογαριασμός μπορεί να υπάρχει για ένα συγκεκριμένο PDA.
Is this page helpful?