Tóm tắt
Tạo tài khoản PDA thông qua invoke_signed với các seed của PDA. Chỉ
chương trình sở hữu mới có thể ký cho PDA. Ràng buộc init của Anchor tự
động hóa việc tạo tài khoản PDA.
Ký PDA thông qua invoke_signed
Khi một chương trình cần ký thay mặt cho PDA trong quá trình CPI, nó sử dụng
invoke_signed với các seed của PDA. Runtime xác minh các seed dẫn xuất PDA
mong đợi bằng cách sử dụng ID chương trình gọi, đảm bảo chỉ chương trình sở
hữu mới có thể ký. Để xem luồng xác minh đầy đủ, hãy xem
PDA Signing.
Tạo tài khoản PDA
Dẫn xuất PDA và tạo tài khoản tại PDA là hai thao tác riêng biệt. Bạn phải tạo tài khoản một cách rõ ràng sau khi dẫn xuất địa chỉ.
Để tạo tài khoản tại PDA, chương trình dẫn xuất gọi lệnh create_account
của System Program thông qua invoke_signed, truyền các seed
của PDA để runtime có thể xác minh quyền của chương trình đối với địa chỉ đó.
Ví dụ dưới đây sử dụng Anchor framework để
tạo tài khoản mới với địa chỉ dẫn xuất từ chương trình. Chương trình bao gồm một
lệnh initialize duy nhất để tạo tài khoản mới, tài khoản
này sẽ lưu trữ địa chỉ người dùng và
bump seed được sử dụng để dẫn xuất PDA.
use anchor_lang::prelude::*;declare_id!("75GJVCJNhaukaa2vCCqhreY31gaphv7XTScBChmr1ueR");#[program]pub mod pda_account {use super::*;pub fn initialize(ctx: Context<Initialize>) -> Result<()> {let account_data = &mut ctx.accounts.pda_account;// store the address of the `user`account_data.user = *ctx.accounts.user.key;// store the canonical bumpaccount_data.bump = ctx.bumps.pda_account;Ok(())}}#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub user: Signer<'info>,#[account(init,// define the seeds to derive the PDAseeds = [b"data", user.key().as_ref()],// use the canonical bumpbump,payer = user,space = 8 + DataAccount::INIT_SPACE // 8 bytes for Anchor account discriminator)]pub pda_account: Account<'info, DataAccount>,pub system_program: Program<'info, System>,}#[account]#[derive(InitSpace)]pub struct DataAccount {pub user: Pubkey,pub bump: u8,}
Ràng buộc init yêu cầu Anchor
gọi System Program để
tạo tài khoản mới sử dụng PDA làm địa chỉ. Các seed được sử
dụng để tạo PDA là:
- Chuỗi cố định: "data"
- Địa chỉ của tài khoản người dùng được cung cấp trong lệnh
- Bump seed chính tắc
Trong ví dụ này, ràng buộc bump không được gán giá trị, do đó Anchor sẽ sử dụng
find_program_address để suy ra PDA và tìm bump.
#[account(init,seeds = [b"data", user.key().as_ref()],bump,payer = user,space = 8 + DataAccount::INIT_SPACE // 8 bytes for Anchor account discriminator)]pub pda_account: Account<'info, DataAccount>,
Tệp kiểm thử bên dưới chứa một giao dịch gọi lệnh
initialize để tạo một tài khoản mới với địa chỉ suy ra
từ chương trình. Tệp này chứa mã để suy ra PDA.
Ví dụ cũng cho thấy cách lấy tài khoản mới sẽ được tạo.
import * as anchor from "@coral-xyz/anchor";import { Program } from "@coral-xyz/anchor";import { PdaAccount } from "../target/types/pda_account";import { PublicKey } from "@solana/web3.js";describe("pda-account", () => {const provider = anchor.AnchorProvider.env();anchor.setProvider(provider);const program = anchor.workspace.PdaAccount as Program<PdaAccount>;const user = provider.wallet as anchor.Wallet;// Derive the PDA address using the seeds specified on the programconst [PDA] = PublicKey.findProgramAddressSync([Buffer.from("data"), user.publicKey.toBuffer()],program.programId);it("Is initialized!", async () => {const transactionSignature = await program.methods.initialize().accounts({user: user.publicKey}).rpc();console.log("Transaction Signature:", transactionSignature);});it("Fetch Account", async () => {const pdaAccount = await program.account.dataAccount.fetch(PDA);console.log(JSON.stringify(pdaAccount, null, 2));});});
Nếu bạn gọi lệnh initialize lần nữa với cùng seed địa chỉ user, giao dịch
sẽ thất bại. Điều này xảy ra vì một tài khoản đã tồn tại tại địa chỉ đã suy
ra.
Is this page helpful?