Özet
PDA'lar, seed'ler + program ID + bump değerlerinin SHA-256 ile hash'lenmesiyle, sonuç Ed25519 eğrisinin dışına çıkana kadar türetilir. Canonical bump, eğri dışı bir adres üreten ilk değerdir. Maksimum 16 seed, seed başına maksimum 32 bayt.
Arka plan
Solana
Keypair
değerleri Ed25519 eğrisi üzerindeki noktalardır.
Bir keypair, bir public key (hesap adresi olarak kullanılır) ve bir secret
key'den (imza üretmek için kullanılır) oluşur. Secret key'e sahip olan herkes o
adres için işlem imzalayabilir.
Eğri üzerinde adreslere sahip iki hesap
Bir PDA, kasıtlı olarak Ed25519 eğrisinin dışına düşecek şekilde türetilir.
Geçerli bir eğri noktası olmadığı için secret key mevcut değildir ve harici bir
taraf imza üretemez. Yalnızca türeten program, invoke_signed aracılığıyla
PDA üzerindeki işlemleri yetkilendirebilir.
Eğri dışı adres
PDA ve keypair hesapları
| Özellik | Keypair hesabı | PDA hesabı |
|---|---|---|
| Adres türü | Ed25519 eğrisi üzerinde | Ed25519 eğrisi dışında |
| Private key var mı | Evet | Hayır |
| İşlem imzalayabilir mi | Evet (private key ile) | Hayır |
| CPI sırasında imzalayabilir mi | Hayır (imza işleme dahil edilmedikçe) | Evet (invoke_signed ile) |
| Türetme | Ed25519 keypair oluştur | Seed'ler + program ID'den deterministik |
| Tipik kullanım | Kullanıcı cüzdanları, program ID | Programa ait veri hesapları |
İsteğe bağlı seed'ler
İsteğe bağlı seed'ler, PDA türetme işlemi için girdi olarak kullanılan kullanıcı
tanımlı bayt dizileridir. Bunlar bir programa özgü benzersiz, deterministik
adresler oluştururlar. Örneğin, ["user", user_pubkey] seed olarak
kullanıldığında her kullanıcı için farklı bir PDA türetilir.
Seed'ler şu kısıtlamalara uymalıdır:
- Türetme başına maksimum 16 seed (
MAX_SEEDS) - Seed başına maksimum 32 bayt (
MAX_SEED_LEN)
Bump seed
Bump seed, türetme sırasında isteğe bağlı seed'lere eklenen tek bir bayttır
(0-255).
find_program_address
255'ten 0'a doğru arama yapar ve her değerle create_program_address
çağrısı yaparak sonucun Ed25519 eğrisinin dışına çıkmasını sağlar. Başarılı olan
ilk değer kanonik bump'tır.
Programlar, seed'lerden adrese benzersiz ve deterministik bir eşleme sağlamak için her zaman kanonik bump kullanmalıdır.
PDA'ları türetirken her zaman kanonik bump kullanın. Kanonik olmayan bir bump kullanmak aynı seed'ler için ikinci bir geçerli adres oluşturur ve bu durum bir saldırganın beklenenden farklı bir hesabı yerine koyabileceği güvenlik açıklarına yol açabilir.
PDA türetme
Türetme algoritması
PDA türetme işlemi SDK'nın
create_program_address
fonksiyonunda uygulanmıştır. Algoritma şu şekilde çalışır:
- Seed sayısının
MAX_SEEDS(16) değerini aşmadığını ve hiçbir seed'inMAX_SEED_LEN(32 bayt) değerini aşmadığını doğrulayın. Her iki kontrolden biri başarısız olursaPubkeyError::MaxSeedLengthExceededdöndürün. - Tüm seed'leri, program ID'sini ve
"ProgramDerivedAddress"dizesini birlikte SHA-256 ile hash'leyerek 32 baytlık bir sonuç üretin. - Sonucun Ed25519 eğrisi üzerinde geçerli bir nokta olup olmadığını kontrol edin.
- Sonuç eğri ÜZERİNDE ise
PubkeyError::InvalidSeedsdöndürün (adresin karşılık gelen bir özel anahtarı olur ki bu PDA güvenlik özelliğini ihlal eder). - Sonuç eğri üzerinde DEĞİLSE, onu PDA olarak döndürün.
Hesaplama birimi maliyetleri
create_program_address için
on-chain syscall
çağrı başına
1.500 CU
ücretlendirir.
try_find_program_address syscall
girişte 1.500 CU (döngüden önce), ardından döngü içindeki her başarısız bump
denemesi için ek 1.500 CU ücretlendirir.
Yaygın seed kalıpları
Seed'ler uygulamaya özgüdür. Yaygın kalıplar şunlardır:
| Kalıp | Seed'ler | Kullanım durumu |
|---|---|---|
| Global singleton | ["global"] | Program genelinde tek config hesabı |
| Kullanıcı başına hesap | ["user", user_pubkey] | Program başına kullanıcı başına bir hesap |
| Kullanıcı başına varlık başına | ["vault", user_pubkey, mint_pubkey] | Token vault'ları, kullanıcı başına token başına |
| Sayaç / sıralı | ["order", user_pubkey, &order_id.to_le_bytes()] | Kullanıcı başına sıralı kayıtlar |
Seed'ler hash'lenmeden önce birleştirilir, bu nedenle ["ab", "cd"] ve
["abcd"] aynı PDA'yı üretir. Çakışmaları önlemek için sabit uzunlukta
seed'ler veya ayırıcı kullanın. Örneğin, ["ab", "-", "cd"] belirsizlik
içermez.
Örnekler: Bir PDA türetme
Bir PDA türetmek yalnızca bir adres hesaplar. Bu adreste on-chain bir hesap
oluşturmaz. Hesap, ayrı bir talimat aracılığıyla açıkça oluşturulmalıdır
(genellikle CPI aracılığıyla create_account).
Solana SDK'ları PDA türetme için fonksiyonlar sağlar. Her fonksiyon şunları alır:
- Program ID: PDA'yı türetmek için kullanılan programın adresi. Bu program, PDA adına imzalayabilir.
- Opsiyonel seed'ler: String'ler, sayılar veya diğer hesap adresleri gibi önceden tanımlanmış girdiler.
| SDK | Fonksiyon |
|---|---|
@solana/kit (TypeScript) | getProgramDerivedAddress |
@solana/web3.js (TypeScript) | findProgramAddressSync |
solana_sdk (Rust) | find_program_address |
Aşağıdaki örnekler Solana SDK'larını kullanarak bir PDA türetir. Kodu çalıştırmak için ▷ Çalıştır'a tıklayın.
String seed ile PDA türetme
Aşağıdaki örnek, bir program ID'si ve isteğe bağlı bir string seed kullanarak PDA türetir.
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}`);
Adres seed'i ile PDA türetme
Aşağıdaki örnek, bir program ID'si ve isteğe bağlı bir adres seed'i kullanarak PDA türetir.
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}`);
Birden fazla seed ile PDA türetme
Aşağıdaki örnek, bir program ID'si ve birden fazla isteğe bağlı seed kullanarak PDA türetir.
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}`);
Tüm bump'ları yineleme
Aşağıdaki örnekler, tüm olası bump seed'lerini (255'ten 0'a) kullanarak PDA
türetmeyi gösterir ve find_program_address fonksiyonunun
kanonik bump değerini nasıl döndürdüğünü gösterir:
Kit örneği dahil edilmemiştir çünkü
createProgramDerivedAddress
fonksiyonu dışa aktarılmamıştır.
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);}}
bump 255: Error: Invalid seeds, address must fall off the curvebump 254: 46GZzzetjCURsdFPb7rcnspbEMnCBXe9kpjrsZAkKb6Xbump 253: GBNWBGxKmdcd7JrMnBdZke9Fumj9sir4rpbruwEGmR4ybump 252: THfBMgduMonjaNsCisKa7Qz2cBoG1VCUYHyso7UXYHHbump 251: EuRrNqJAofo7y3Jy6MGvF7eZAYegqYTwH2dnLCwDDGdPbump 250: Error: Invalid seeds, address must fall off the curve...// remaining bump outputs
Bu örnekte, bump 255 eğri üzerinde bir adres üretir ve başarısız olur. İlk geçerli bump 254'tür ve bu onu kanonik bump yapar.
Is this page helpful?