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, update και delete που θα προσθέσετε στα επόμενα βήματα.

lib.rs
use anchor_lang::prelude::*;
declare_id!("8KPzbM2Cwn4Yjak7QYAEH9wyoQh86NcBicaLuzPaejdw");
#[program]
pub mod pda {
use super::*;
pub fn create(_ctx: Context<Create>) -> Result<()> {
Ok(())
}
pub fn update(_ctx: Context<Update>) -> Result<()> {
Ok(())
}
pub fn delete(_ctx: Context<Delete>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Create {}
#[derive(Accounts)]
pub struct Update {}
#[derive(Accounts)]
pub struct Delete {}
#[account]
pub struct MessageAccount {}

Πριν ξεκινήσετε, εκτελέστε build στο τερματικό του Playground για να ελέγξετε ότι το αρχικό πρόγραμμα μεταγλωττίζεται επιτυχώς.

Terminal
$
build

Ορισμός τύπου λογαριασμού μηνύματος

Πρώτα, ορίστε τη δομή για τον λογαριασμό μηνύματος που δημιουργεί το πρόγραμμα. Αυτή η δομή καθορίζει τα δεδομένα που θα αποθηκευτούν στον λογαριασμό που δημιουργείται από το πρόγραμμα.

Στο lib.rs, ενημερώστε τη δομή MessageAccount με τα εξής:

lib.rs
#[account]
pub struct MessageAccount {
pub user: Pubkey,
pub message: String,
pub bump: u8,
}

Δημιουργήστε ξανά το πρόγραμμα εκτελώντας build στο τερματικό.

Terminal
$
build

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

Προσθήκη οδηγίας δημιουργίας

Τώρα, προσθέστε την οδηγία create που δημιουργεί και αρχικοποιεί το MessageAccount.

Ξεκινήστε καθορίζοντας τους λογαριασμούς που απαιτούνται για την οδηγία, ενημερώνοντας τη δομή Create με τα εξής:

lib.rs
#[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 με τα ακόλουθα:

lib.rs
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(())
}

Επαναδημιουργήστε το πρόγραμμα.

Terminal
$
build

Προσθήκη εντολής ενημέρωσης

Στη συνέχεια, προσθέστε την εντολή update για να αλλάξετε το MessageAccount με ένα νέο μήνυμα.

Όπως και στο προηγούμενο βήμα, πρώτα καθορίστε τους λογαριασμούς που απαιτούνται από την εντολή update.

Ενημερώστε τη δομή Update με τα ακόλουθα:

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

lib.rs
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(())
}

Επαναδημιουργήστε το πρόγραμμα

Terminal
$
build

Προσθήκη εντολής διαγραφής

Στη συνέχεια, προσθέστε την εντολή delete για να κλείσετε το MessageAccount.

Ενημερώστε τη δομή Delete με τα ακόλουθα:

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

lib.rs
pub fn delete(_ctx: Context<Delete>) -> Result<()> {
msg!("Delete Message");
Ok(())
}

Επαναδημιουργήστε το πρόγραμμα.

Terminal
$
build

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

Έχετε πλέον ολοκληρώσει το βασικό πρόγραμμα CRUD. Αναπτύξτε το πρόγραμμα εκτελώντας deploy στο τερματικό του Playground.

Σε αυτό το παράδειγμα, θα αναπτύξετε το πρόγραμμα στο devnet, ένα Solana cluster για δοκιμές ανάπτυξης.

Το πορτοφόλι του Playground συνδέεται στο devnet από προεπιλογή. Βεβαιωθείτε ότι το πορτοφόλι του Playground έχει devnet SOL για να πληρώσει για την ανάπτυξη του προγράμματος. Αποκτήστε devnet SOL από το Solana Faucet.

Terminal
$
deploy

Ρύθμιση αρχείου δοκιμής

Ο αρχικός κώδικας περιλαμβάνει επίσης ένα αρχείο δοκιμών στο anchor.test.ts.

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().

anchor.test.ts
const program = pg.program;
const wallet = pg.wallet;
const [messagePda, messageBump] = PublicKey.findProgramAddressSync(
[Buffer.from("message"), wallet.publicKey.toBuffer()],
program.programId
);

Εκτελέστε το αρχείο δοκιμών τρέχοντας test στο τερματικό του Playground για να ελέγξετε ότι εκτελείται όπως αναμένεται. Τα επόμενα βήματα προσθέτουν τις πραγματικές δοκιμές.

Terminal
$
test

Κλήση της εντολής Create

Ενημερώστε την πρώτη δοκιμή με τα ακόλουθα:

anchor.test.ts
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`
);
});

Επίκληση εντολής ενημέρωσης

Ενημερώστε τη δεύτερη δοκιμή με τα ακόλουθα:

anchor.test.ts
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`
);
});

Επίκληση εντολής διαγραφής

Ενημερώστε την τρίτη δοκιμή με τα ακόλουθα:

anchor.test.ts
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 για να δείτε τις λεπτομέρειες της συναλλαγής.

Terminal
$
test

Εξετάστε τους συνδέσμους SolanaFM για να δείτε τις λεπτομέρειες της συναλλαγής.

Σημειώστε ότι σε αυτό το παράδειγμα, αν εκτελέσετε ξανά τη δοκιμή, η εντολή create αποτυγχάνει επειδή ο λογαριασμός messageAccount υπάρχει ήδη. Μόνο ένας λογαριασμός μπορεί να υπάρχει για ένα συγκεκριμένο PDA.

Is this page helpful?