PDA-afleiding

Samenvatting

PDA's worden afgeleid door seeds + programma-ID + bump te hashen via SHA-256 totdat het resultaat buiten de Ed25519-curve valt. De canonieke bump is de eerste waarde die een off-curve-adres produceert. Max. 16 seeds, max. 32 bytes per seed.

Achtergrond

Solana Keypair waarden zijn punten op de Ed25519-curve. Een keypair bevat een publieke sleutel (gebruikt als accountadres) en een geheime sleutel (gebruikt om handtekeningen te produceren). Iedereen met de geheime sleutel kan transacties ondertekenen voor dat adres.

Twee accounts met on-curve-adressenTwee accounts met on-curve-adressen

Een PDA wordt opzettelijk afgeleid om buiten de Ed25519-curve te vallen. Omdat het geen geldig curvepunt is, bestaat er geen geheime sleutel en kan geen externe partij een handtekening produceren. Alleen het afleidende programma kan operaties op de PDA autoriseren via invoke_signed.

Off-curve-adresOff-curve-adres

PDA vs keypair-accounts

EigenschapKeypair-accountPDA-account
AdrestypeOp Ed25519-curveBuiten Ed25519-curve
Heeft privésleutelJaNee
Kan transacties ondertekenenJa (met privésleutel)Nee
Kan ondertekenen tijdens CPINee (tenzij handtekening in transactie is opgenomen)Ja (via invoke_signed)
AfleidingGenereer Ed25519-keypairDeterministisch vanuit seeds + programma-ID
Typisch gebruikGebruikerswallets, programma-IDAccounts met programma-eigendom

Optionele seeds

De optionele seeds zijn door de gebruiker gedefinieerde byte-strings die dienen als invoer voor de PDA-afleiding. Ze creëren unieke, deterministische adressen binnen het bereik van een programma. Bijvoorbeeld, het gebruik van ["user", user_pubkey] als seeds leidt tot een andere PDA voor elke gebruiker.

Seeds moeten aan deze beperkingen voldoen:

  • Maximaal 16 seeds per afleiding (MAX_SEEDS)
  • Maximaal 32 bytes per seed (MAX_SEED_LEN)

Bump seed

De bump seed is een enkele byte (0-255) die wordt toegevoegd aan de optionele seeds tijdens de afleiding. find_program_address zoekt van 255 naar beneden tot 0, waarbij create_program_address wordt aangeroepen met elke waarde totdat het resultaat buiten de Ed25519-curve valt. De eerste waarde die slaagt is de canonieke bump.

Programma's moeten altijd de canonieke bump gebruiken om een unieke, deterministische mapping van seeds naar adres te garanderen.

Gebruik altijd de canonieke bump bij het afleiden van PDA's. Het gebruik van een niet-canonieke bump creëert een tweede geldig adres voor dezelfde seeds, wat kan leiden tot kwetsbaarheden waarbij een aanvaller een ander account kan substitueren dan verwacht.

PDA-afleidingPDA-afleiding

Afleidingsalgoritme

De PDA-afleiding is geïmplementeerd in de SDK's create_program_address functie. Het algoritme werkt als volgt:

  1. Valideer dat het aantal seeds niet meer dan MAX_SEEDS (16) bedraagt en geen individuele seed groter is dan MAX_SEED_LEN (32 bytes). Als een van beide controles mislukt, retourneer PubkeyError::MaxSeedLengthExceeded.
  2. SHA-256 hash alle seeds, het programma-ID en de string "ProgramDerivedAddress" samen om een 32-byte resultaat te produceren.
  3. Controleer of het resultaat een geldig punt op de Ed25519-curve is.
  4. Als het resultaat WEL op de curve ligt, retourneer PubkeyError::InvalidSeeds (het adres zou een bijbehorende private key hebben, wat de PDA-beveiligingseigenschap schendt).
  5. Als het resultaat NIET op de curve ligt, retourneer het als de PDA.

Compute unit-kosten

De on-chain syscall voor create_program_address rekent 1.500 CU's per aanroep.

De try_find_program_address syscall rekent 1.500 CU's bij binnenkomst (vóór de lus), en vervolgens nog eens 1.500 CU's voor elke mislukte bump-poging binnen de lus.

Veelvoorkomende seed-patronen

Seeds zijn applicatiespecifiek. Veelvoorkomende patronen zijn:

PatroonSeedsGebruikssituatie
Globale singleton["global"]Enkel programmabreed configuratie-account
Per-gebruiker-account["user", user_pubkey]Eén account per gebruiker per programma
Per-gebruiker-per-entiteit["vault", user_pubkey, mint_pubkey]Token-kluizen, per-gebruiker-per-token
Teller / sequentieel["order", user_pubkey, &order_id.to_le_bytes()]Sequentiële records per gebruiker

Seeds worden samengevoegd vóór hashing, dus ["ab", "cd"] en ["abcd"] produceren dezelfde PDA. Gebruik seeds met vaste lengte of een scheidingsteken om botsingen te voorkomen. Bijvoorbeeld, ["ab", "-", "cd"] is ondubbelzinnig.

Voorbeelden: een PDA afleiden

Het afleiden van een PDA berekent alleen een adres. Het creëert geen on-chain account op dat adres. Het account moet expliciet worden aangemaakt via een aparte instructie (meestal create_account via CPI).

De Solana SDK's bieden functies voor PDA-afleiding. Elke functie neemt:

  • Programma-ID: het adres van het programma dat wordt gebruikt om de PDA af te leiden. Dit programma kan namens de PDA ondertekenen.
  • Optionele seeds: voorgedefinieerde inputs zoals strings, nummers of andere accountadressen.
SDKFunctie
@solana/kit (TypeScript)getProgramDerivedAddress
@solana/web3.js (TypeScript)findProgramAddressSync
solana_sdk (Rust)find_program_address

De onderstaande voorbeelden leiden een PDA af met behulp van de Solana SDK's. Klik op ▷ Uitvoeren om de code uit te voeren.

Een PDA afleiden met een string seed

Het onderstaande voorbeeld leidt een PDA af met behulp van een programma-ID en een optionele string seed.

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.

Een PDA afleiden met een adres seed

Het onderstaande voorbeeld leidt een PDA af met behulp van een programma-ID en een optionele adres seed.

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.

Een PDA afleiden met meerdere seeds

Het onderstaande voorbeeld leidt een PDA af met behulp van een programma-ID en meerdere optionele seeds.

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.

Alle bumps doorlopen

De volgende voorbeelden tonen PDA-afleiding met alle mogelijke bump seeds (255 tot 0), waarbij wordt geïllustreerd hoe find_program_address de canonieke bump retourneert:

Kit-voorbeeld is niet opgenomen omdat de createProgramDerivedAddress functie niet wordt geëxporteerd.

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

In dit voorbeeld produceert bump 255 een on-curve adres en mislukt. De eerste geldige bump is 254, waardoor dit de canonieke bump is.

Is this page helpful?

Beheerd door

© 2026 Solana Foundation.
Alle rechten voorbehouden.
Blijf Verbonden