Derivasi PDA

Ringkasan

PDA diturunkan dengan melakukan hashing seed + program ID + bump melalui SHA-256 hingga hasilnya berada di luar kurva Ed25519. Bump kanonik adalah nilai pertama yang menghasilkan alamat di luar kurva. Maksimal 16 seed, maksimal 32 byte per seed.

Latar belakang

Nilai Keypair Solana adalah titik-titik pada kurva Ed25519. Sebuah keypair terdiri dari kunci publik (digunakan sebagai alamat akun) dan kunci rahasia (digunakan untuk menghasilkan tanda tangan). Siapa pun yang memiliki kunci rahasia dapat menandatangani transaksi untuk alamat tersebut.

Dua akun dengan alamat pada kurvaDua akun dengan alamat pada kurva

PDA sengaja diturunkan agar berada di luar kurva Ed25519. Karena bukan titik kurva yang valid, tidak ada kunci rahasia, dan tidak ada pihak eksternal yang dapat menghasilkan tanda tangan. Hanya program yang menurunkan yang dapat mengotorisasi operasi pada PDA melalui invoke_signed.

Alamat di luar kurvaAlamat di luar kurva

Akun PDA vs keypair

PropertiAkun keypairAkun PDA
Jenis alamatPada kurva Ed25519Di luar kurva Ed25519
Memiliki kunci privatYaTidak
Dapat menandatangani transaksiYa (dengan kunci privat)Tidak
Dapat menandatangani saat CPITidak (kecuali tanda tangan disertakan dalam transaksi)Ya (melalui invoke_signed)
DerivasiMenghasilkan keypair Ed25519Deterministik dari seed + program ID
Penggunaan umumDompet pengguna, program IDAkun data milik program

Seed opsional

Seed opsional adalah string byte yang ditentukan pengguna yang berfungsi sebagai input untuk derivasi PDA. Mereka membuat alamat unik dan deterministik yang terikat pada sebuah program. Misalnya, menggunakan ["user", user_pubkey] sebagai seed akan menghasilkan PDA yang berbeda untuk setiap pengguna.

Seed harus mengikuti batasan berikut:

  • Maksimal 16 seed per derivasi (MAX_SEEDS)
  • Maksimal 32 byte per seed (MAX_SEED_LEN)

Bump seed

Bump seed adalah satu byte (0-255) yang ditambahkan ke seed opsional selama derivasi. find_program_address mencari dari 255 turun ke 0, memanggil create_program_address dengan setiap nilai hingga hasilnya jatuh dari kurva Ed25519. Nilai pertama yang berhasil adalah bump kanonik.

Program harus selalu menggunakan bump kanonik untuk memastikan pemetaan unik dan deterministik dari seed ke alamat.

Selalu gunakan bump kanonik saat melakukan derivasi PDA. Menggunakan bump non-kanonik akan membuat alamat valid kedua untuk seed yang sama, yang dapat menyebabkan kerentanan di mana penyerang mengganti akun yang berbeda dari yang diharapkan.

Derivasi PDADerivasi PDA

Algoritma derivasi

Derivasi PDA diimplementasikan dalam fungsi create_program_address SDK. Algoritma bekerja sebagai berikut:

  1. Validasi bahwa jumlah seed tidak melebihi MAX_SEEDS (16) dan tidak ada seed individual yang melebihi MAX_SEED_LEN (32 byte). Jika salah satu pemeriksaan gagal, kembalikan PubkeyError::MaxSeedLengthExceeded.
  2. Hash SHA-256 semua seed, ID program, dan string "ProgramDerivedAddress" bersama-sama untuk menghasilkan hasil 32-byte.
  3. Periksa apakah hasilnya adalah titik yang valid pada kurva Ed25519.
  4. Jika hasilnya ADA pada kurva, kembalikan PubkeyError::InvalidSeeds (alamat tersebut akan memiliki kunci privat yang sesuai, yang melanggar properti keamanan PDA).
  5. Jika hasilnya TIDAK pada kurva, kembalikan sebagai PDA.

Biaya compute unit

Syscall on-chain untuk create_program_address mengenakan biaya 1.500 CU per panggilan.

Syscall try_find_program_address mengenakan biaya 1.500 CU saat masuk (sebelum loop), kemudian tambahan 1.500 CU untuk setiap percobaan bump yang gagal dalam loop.

Pola seed umum

Seed bersifat spesifik untuk aplikasi. Pola umum meliputi:

PolaSeedKasus penggunaan
Singleton global["global"]Akun konfigurasi tunggal untuk seluruh program
Akun per pengguna["user", user_pubkey]Satu akun per pengguna per program
Per pengguna per entitas["vault", user_pubkey, mint_pubkey]Vault token, per pengguna per token
Counter / berurutan["order", user_pubkey, &order_id.to_le_bytes()]Catatan berurutan per pengguna

Seed digabungkan sebelum hashing, sehingga ["ab", "cd"] dan ["abcd"] menghasilkan PDA yang sama. Gunakan seed dengan panjang tetap atau separator untuk menghindari tabrakan. Misalnya, ["ab", "-", "cd"] tidak ambigu.

Contoh: Menurunkan PDA

Menurunkan PDA hanya menghitung alamat. Ini tidak membuat akun on-chain pada alamat tersebut. Akun harus dibuat secara eksplisit melalui instruksi terpisah (biasanya create_account melalui CPI).

SDK Solana menyediakan fungsi untuk penurunan PDA. Setiap fungsi menerima:

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

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

Turunkan PDA dengan seed string

Contoh di bawah ini menurunkan PDA menggunakan ID program dan seed string 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.

Turunkan PDA dengan seed alamat

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.

Turunkan PDA dengan beberapa seed

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.

Iterasi semua bump

Contoh berikut menunjukkan penurunan PDA menggunakan semua kemungkinan seed bump (255 hingga 0), mengilustrasikan bagaimana find_program_address mengembalikan canonical bump:

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 255 menghasilkan alamat on-curve dan gagal. Bump valid pertama adalah 254, menjadikannya bump kanonik.

Is this page helpful?

Daftar Isi

Edit Halaman

Dikelola oleh

© 2026 Yayasan Solana.
Semua hak dilindungi.
Terhubung