Program-Derived Address

Bir Solana hesap adresi, hesabın blok zincirindeki konumunu gösterir. Birçok hesap adresi bir keypair'in açık anahtarıdır; bu durumda, ilgili özel anahtar hesabı içeren işlemleri imzalamak için kullanılır.

Açık anahtar adresine kullanışlı bir alternatif, program-derived address (PDA)'dir. PDA'lar, program durumunu depolamak, eşleştirmek ve getirmek için kolay bir yöntem sağlar. Bir PDA, bir program kimliği ve isteğe bağlı önceden tanımlanmış girdilerin bir kombinasyonu kullanılarak deterministik olarak oluşturulan bir adrestir. PDA'lar açık anahtar adreslerine benzer, ancak karşılık gelen bir özel anahtarları yoktur.

Solana çalışma zamanı, programların özel bir anahtar gerektirmeden PDA'lar için imza atmasını sağlar. Bir PDA kullanmak, hesabın adresini takip etme ihtiyacını ortadan kaldırır. Bunun yerine, PDA'nın türetilmesi için kullanılan belirli girdileri hatırlayabilirsiniz. (Programların imzalama için PDA'ları nasıl kullandığını öğrenmek için Programlar Arası Çağrılar bölümüne bakın.)

Arka plan

Solana keypair'leri, Ed25519 eğrisi (eliptik eğri kriptografisi) üzerindeki noktalardır. Bunlar bir açık anahtar ve bir özel anahtardan oluşur. Açık anahtar hesap adresi olur ve özel anahtar hesap için geçerli bir imza oluşturmak için kullanılır.

Eğri üzerinde adresleri olan iki hesapEğri üzerinde adresleri olan iki hesap

Bir PDA, kasıtlı olarak Ed25519 eğrisinin dışına düşecek şekilde türetilir. Bu, geçerli bir karşılık gelen özel anahtarı olmadığı ve kriptografik işlemler gerçekleştiremeyeceği anlamına gelir. (İmza sağlamak gibi.) Ancak, Solana, programların özel bir anahtar gerektirmeden PDA'lar için imza atmasını sağlar.

Eğri Dışı AdresEğri Dışı Adres

PDA'ları, önceden tanımlanmış bir girdi kümesi kullanarak zincir üzerinde hashmap benzeri yapılar oluşturmanın bir yolu olarak düşünebilirsiniz. (Örneğin, dizeler, sayılar ve diğer hesap adresleri.)

Program Derived AddressProgram Derived Address

Bir PDA türet

Bir PDA ile hesap oluşturmadan önce, öncelikle adresi türetmeniz gerekir. Bir PDA türetmek, otomatik olarak o adresteki zincir üzerinde bir hesap oluşturmaz - hesap, PDA'yı türetmek için kullanılan program aracılığıyla açıkça oluşturulmalıdır. Bir PDA'yı haritadaki bir adres gibi düşünebilirsiniz: bir adresin var olması, orada bir şeyin inşa edildiği anlamına gelmez.

Solana SDK'ları, aşağıdaki tabloda gösterilen fonksiyonlarla PDA oluşturulmasını destekler. Her fonksiyon şu girdileri alır:

  • Program ID: PDA'yı türetmek için kullanılan programın adresi. Bu program, PDA adına imza atabilir.
  • İsteğe bağlı seed'ler: Dizeler, sayılar veya diğer hesap adresleri gibi önceden tanımlanmış girdiler.
SDKFonksiyon
@solana/kit (Typescript)getProgramDerivedAddress
@solana/web3.js (Typescript)findProgramAddressSync
solana_sdk (Rust)find_program_address

Fonksiyon, program ID'sini ve isteğe bağlı seed'leri kullanır, ardından geçerli bir program adresi oluşturmaya çalışmak için bump değerlerini yineler. Bump değerlerinin yinelenmesi 255'ten başlar ve geçerli bir PDA bulunana kadar 1'er azalır. Geçerli bir PDA bulunduktan sonra, fonksiyon PDA'yı ve bump seed'i döndürür.

Bump seed, geçerli bir eğri dışı adresin oluşturulmasını sağlamak için isteğe bağlı seed'lere eklenen ekstra bir bayttır.

PDA TüretimiPDA Türetimi

Kanonik bump

Bump seed, isteğe bağlı seed'lere eklenen ekstra bir bayttır. Türetme fonksiyonu, 255'ten başlayarak ve 1 azaltarak, geçerli bir eğri dışı adres üretene kadar bump değerlerini yineler. Geçerli bir eğri dışı adres üreten ilk değer "kanonik bump" olarak adlandırılır.

Aşağıdaki örnekler, tüm olası bump seed'leri (255'ten 0'a) kullanarak PDA türetimini göstermektedir:

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);
}
}
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

Bu örnekte, ilk bump seed bir hata fırlatır. Geçerli bir PDA türeten ilk bump seed 254'tür. 253-251 bump seed'leri de benzersiz, geçerli PDA'lar türetir.

Bu, aynı isteğe bağlı seed'ler ve programId verildiğinde, farklı bir değere sahip bir bump seed'in hala geçerli bir PDA türetebileceği anlamına gelir.

Programa iletilen bir PDA'nın kanonik bump'tan türetildiğinden emin olmak için her zaman güvenlik kontrolleri ekleyin. Bunu yapmamak, programın talimatlarında beklenmeyen hesapların kullanılmasına izin veren güvenlik açıklarına yol açabilir. PDA'ları türetirken yalnızca kanonik bump'ı kullanmak en iyi uygulamadır.

Örnekler

Aşağıdaki örnekler, Solana SDK'larını kullanarak bir PDA türetmektedir. Kodu çalıştırmak için ▷ Çalıştır'a tıklayın.

Bir string seed ile PDA türetme

Aşağıdaki örnek, bir program kimliği ve isteğe bağlı bir string seed kullanarak bir 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}`);
Console
Click to execute the code.

Adres seed ile bir PDA türetin

Aşağıdaki örnek, bir program kimliği ve isteğe bağlı bir adres seed kullanarak bir 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}`);
Console
Click to execute the code.

Birden fazla seed ile bir PDA türetin

Aşağıdaki örnek, bir program kimliği ve birden fazla isteğe bağlı seed kullanarak bir 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}`);
Console
Click to execute the code.

Bir PDA hesabı oluşturun

Aşağıdaki örnek, program türetilmiş bir adrese sahip yeni bir hesap oluşturmak için Anchor framework kullanır. Program, PDA'yı türetmek için kullanılan kullanıcı adresini ve bump seed depolayacak yeni hesabı oluşturmak için tek bir initialize talimatı içerir.

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,
}

init kısıtlaması, Anchor'a PDA'yı adres olarak kullanarak yeni bir hesap oluşturmak için System Program'ı çağırmasını söyler. PDA'yı oluşturmak için kullanılan seeds şunlardır:

  • Talimatta sağlanan kullanıcı hesabının adresi
  • Sabit dize: "data"
  • Kanonik bump seed

Bu örnekte, bump kısıtlamasına bir değer atanmamıştır, bu nedenle Anchor, PDA'yı türetmek ve bump'ı bulmak için find_program_address kullanacaktır.

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>,

Aşağıdaki test dosyası, program türetilmiş bir adresle yeni bir hesap oluşturmak için initialize talimatını çağıran bir işlem içerir. Dosya, PDA'yı türetmek için kod içerir.

Örnek ayrıca oluşturulacak yeni hesabı nasıl alacağınızı gösterir.

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));
});
});

Eğer aynı user adres seed'i ile initialize talimatını tekrar çağırırsanız, işlem başarısız olacaktır. Bu, türetilen adreste zaten bir hesap var olduğu için gerçekleşir.

Is this page helpful?

İçindekiler

Sayfayı Düzenle

Yönetici

© 2025 Solana Vakfı.
Tüm hakları saklıdır.
Bağlanın