Alamat Turunan Program

Alamat akun Solana menunjuk ke lokasi akun di blockchain. Banyak alamat akun adalah kunci publik dari keypair, dalam hal ini kunci privat yang sesuai digunakan untuk menandatangani transaksi yang melibatkan akun tersebut.

Alternatif yang berguna untuk alamat kunci publik adalah alamat turunan program (PDA). PDA menyediakan metode mudah untuk menyimpan, memetakan, dan mengambil status program. PDA adalah alamat yang dibuat secara deterministik menggunakan ID program dan kombinasi input yang telah ditentukan sebelumnya yang bersifat opsional. PDA terlihat mirip dengan alamat kunci publik, tetapi tidak memiliki kunci privat yang sesuai.

Runtime Solana memungkinkan program untuk menandatangani PDA tanpa memerlukan kunci privat. Menggunakan PDA menghilangkan kebutuhan untuk melacak alamat akun. Sebagai gantinya, Anda dapat mengingat input spesifik yang digunakan untuk derivasi PDA. (Untuk mempelajari bagaimana program menggunakan PDA untuk penandatanganan, lihat bagian Pemanggilan Lintas Program.)

Latar Belakang

Keypair Solana adalah titik pada kurva Ed25519 (kriptografi kurva eliptik). Keypair terdiri dari kunci publik dan kunci privat. Kunci publik menjadi alamat akun, dan kunci privat digunakan untuk menghasilkan tanda tangan yang valid untuk akun.

Dua akun dengan alamat pada kurvaDua akun dengan alamat pada kurva

PDA sengaja diturunkan agar berada di luar kurva Ed25519. Ini berarti PDA tidak memiliki kunci privat yang sesuai yang valid dan tidak dapat melakukan operasi kriptografi. (Seperti menyediakan tanda tangan.) Namun, Solana memungkinkan program untuk menandatangani PDA tanpa memerlukan kunci privat.

Off Curve AddressOff Curve Address

Anda dapat menganggap PDA sebagai cara untuk membuat struktur seperti hashmap di on-chain menggunakan serangkaian input yang telah ditentukan sebelumnya. (Misalnya, string, angka, dan alamat akun lainnya.)

Program Derived AddressProgram Derived Address

Sebelum membuat akun dengan PDA, Anda harus terlebih dahulu menurunkan alamatnya. Menurunkan PDA tidak secara otomatis membuat akun on-chain di alamat tersebut— akun harus secara eksplisit dibuat melalui program yang digunakan untuk menurunkan PDA. Anda dapat menganggap PDA seperti alamat pada peta: hanya karena alamat itu ada tidak berarti ada sesuatu yang dibangun di sana.

SDK Solana mendukung pembuatan PDA dengan fungsi yang ditunjukkan dalam tabel di bawah ini. Setiap fungsi menerima input berikut:

  • Program ID: Alamat program yang digunakan untuk menurunkan PDA. Program ini dapat menandatangani atas nama PDA.
  • Optional seeds: Input yang telah ditentukan sebelumnya, seperti string, angka atau alamat akun lainnya.
SDKFunction
@solana/kit (Typescript)getProgramDerivedAddress
@solana/web3.js (Typescript)findProgramAddressSync
solana_sdk (Rust)find_program_address

Fungsi ini menggunakan program ID dan optional seeds, kemudian melakukan iterasi melalui nilai bump untuk mencoba membuat alamat program yang valid. Iterasi nilai bump dimulai dari 255 dan berkurang sebesar 1 hingga PDA yang valid ditemukan. Setelah PDA yang valid ditemukan, fungsi mengembalikan PDA dan bump seed.

Bump seed adalah byte tambahan yang ditambahkan ke optional seeds untuk memastikan alamat off-curve yang valid dihasilkan.

Derivasi PDADerivasi PDA

Canonical bump

Bump seed adalah byte tambahan yang ditambahkan pada seeds opsional. Fungsi derivasi melakukan iterasi melalui nilai-nilai bump, dimulai dari 255 dan dikurangi 1, hingga menghasilkan alamat off-curve yang valid. Nilai bump pertama yang menghasilkan alamat off-curve yang valid disebut "canonical bump."

Contoh berikut menunjukkan derivasi PDA menggunakan semua kemungkinan bump seed (255 hingga 0):

Contoh Kit tidak disertakan karena fungsi createProgramDerivedAddress tidak diekspor.

import { PublicKey } from "@solana/web3.js";
const programId = new PublicKey("11111111111111111111111111111111");
const optionalSeed = "helloWorld";
// Loop through all bump seeds (255 down to 0)
for (let bump = 255; bump >= 0; bump--) {
try {
const PDA = PublicKey.createProgramAddressSync(
[Buffer.from(optionalSeed), Buffer.from([bump])],
programId
);
console.log("bump " + bump + ": " + PDA);
} catch (error) {
console.log("bump " + bump + ": " + error);
}
}
Console
Click to execute the code.
bump 255: Error: Invalid seeds, address must fall off the curve
bump 254: 46GZzzetjCURsdFPb7rcnspbEMnCBXe9kpjrsZAkKb6X
bump 253: GBNWBGxKmdcd7JrMnBdZke9Fumj9sir4rpbruwEGmR4y
bump 252: THfBMgduMonjaNsCisKa7Qz2cBoG1VCUYHyso7UXYHH
bump 251: EuRrNqJAofo7y3Jy6MGvF7eZAYegqYTwH2dnLCwDDGdP
bump 250: Error: Invalid seeds, address must fall off the curve
...
// remaining bump outputs

Dalam contoh ini, bump seed pertama menghasilkan error. Bump seed pertama yang menghasilkan PDA valid adalah 254. Bump seed 253-251 juga menghasilkan PDA valid yang unik.

Ini berarti bahwa dengan seeds opsional yang sama dan programId, bump seed dengan nilai yang berbeda masih dapat menghasilkan PDA yang valid.

Selalu sertakan pemeriksaan keamanan untuk memastikan PDA yang diteruskan ke program diturunkan dari canonical bump. Kegagalan melakukan hal ini dapat memunculkan kerentanan yang memungkinkan akun yang tidak diharapkan digunakan dalam instruksi program. Praktik terbaik adalah hanya menggunakan canonical bump saat menurunkan PDA.

Contoh

Contoh di bawah ini menurunkan PDA menggunakan Solana SDK. Klik ▷ Run untuk menjalankan kode.

Contoh di bawah ini menurunkan PDA menggunakan program ID dan string seed opsional.

import { Address, getProgramDerivedAddress } from "@solana/kit";
const programAddress = "11111111111111111111111111111111" as Address;
const seeds = ["helloWorld"];
const [pda, bump] = await getProgramDerivedAddress({
programAddress,
seeds
});
console.log(`PDA: ${pda}`);
console.log(`Bump: ${bump}`);
Console
Click to execute the code.

Contoh di bawah ini menurunkan PDA menggunakan ID program dan seed alamat opsional.

import {
Address,
getAddressEncoder,
getProgramDerivedAddress
} from "@solana/kit";
const programAddress = "11111111111111111111111111111111" as Address;
const addressEncoder = getAddressEncoder();
const optionalSeedAddress = addressEncoder.encode(
"B9Lf9z5BfNPT4d5KMeaBFx8x1G4CULZYR1jA2kmxRDka" as Address
);
const seeds = [optionalSeedAddress];
const [pda, bump] = await getProgramDerivedAddress({
programAddress,
seeds
});
console.log(`PDA: ${pda}`);
console.log(`Bump: ${bump}`);
Console
Click to execute the code.

Contoh di bawah ini menurunkan PDA menggunakan ID program dan beberapa seed opsional.

import {
Address,
getAddressEncoder,
getProgramDerivedAddress
} from "@solana/kit";
const programAddress = "11111111111111111111111111111111" as Address;
const optionalSeedString = "helloWorld";
const addressEncoder = getAddressEncoder();
const optionalSeedAddress = addressEncoder.encode(
"B9Lf9z5BfNPT4d5KMeaBFx8x1G4CULZYR1jA2kmxRDka" as Address
);
const seeds = [optionalSeedString, optionalSeedAddress];
const [pda, bump] = await getProgramDerivedAddress({
programAddress,
seeds
});
console.log(`PDA: ${pda}`);
console.log(`Bump: ${bump}`);
Console
Click to execute the code.

Membuat akun PDA

Contoh di bawah ini menggunakan framework Anchor untuk membuat akun baru dengan alamat yang diturunkan dari program. Program ini mencakup satu instruksi initialize untuk membuat akun baru, yang akan menyimpan alamat pengguna dan bump seed yang digunakan untuk menurunkan PDA.

Program
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 bumpd
account_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 PDA
seeds = [b"data", user.key().as_ref()],
// use the canonical bump
bump,
payer = user,
space = 8 + DataAccount::INIT_SPACE
)]
pub pda_account: Account<'info, DataAccount>,
pub system_program: Program<'info, System>,
}
#[account]
#[derive(InitSpace)]
pub struct DataAccount {
pub user: Pubkey,
pub bump: u8,
}

Batasan init memberi tahu Anchor untuk memanggil System Program untuk membuat akun baru menggunakan PDA sebagai alamat. Seeds yang digunakan untuk membuat PDA adalah:

  • Alamat akun pengguna yang disediakan dalam instruksi
  • String tetap: "data"
  • Bump seed kanonik

Dalam contoh ini, batasan bump tidak diberi nilai, sehingga Anchor akan menggunakan find_program_address untuk menurunkan PDA dan menemukan bump.

pda_account
#[account(
init,
seeds = [b"data", user.key().as_ref()],
bump,
payer = user,
space = 8 + DataAccount::INIT_SPACE
)]
pub pda_account: Account<'info, DataAccount>,

File pengujian di bawah ini berisi transaksi yang memanggil instruksi initialize untuk membuat akun baru dengan program-derived address. File tersebut berisi kode untuk menurunkan PDA.

Contoh ini juga menunjukkan cara mengambil akun baru yang akan dibuat.

Test
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 program
const [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));
});
});

Jika Anda memanggil instruksi initialize lagi dengan seed alamat user yang sama, transaksi akan gagal. Ini terjadi karena akun sudah ada di alamat yang diturunkan.

Is this page helpful?

Daftar Isi

Edit Halaman

Dikelola oleh

© 2025 Yayasan Solana.
Semua hak dilindungi.