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-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-adres
PDA vs keypair-accounts
| Eigenschap | Keypair-account | PDA-account |
|---|---|---|
| Adrestype | Op Ed25519-curve | Buiten Ed25519-curve |
| Heeft privésleutel | Ja | Nee |
| Kan transacties ondertekenen | Ja (met privésleutel) | Nee |
| Kan ondertekenen tijdens CPI | Nee (tenzij handtekening in transactie is opgenomen) | Ja (via invoke_signed) |
| Afleiding | Genereer Ed25519-keypair | Deterministisch vanuit seeds + programma-ID |
| Typisch gebruik | Gebruikerswallets, programma-ID | Accounts 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-afleiding
Afleidingsalgoritme
De PDA-afleiding is geïmplementeerd in de SDK's
create_program_address
functie. Het algoritme werkt als volgt:
- Valideer dat het aantal seeds niet meer dan
MAX_SEEDS(16) bedraagt en geen individuele seed groter is danMAX_SEED_LEN(32 bytes). Als een van beide controles mislukt, retourneerPubkeyError::MaxSeedLengthExceeded. - SHA-256 hash alle seeds, het programma-ID en de string
"ProgramDerivedAddress"samen om een 32-byte resultaat te produceren. - Controleer of het resultaat een geldig punt op de Ed25519-curve is.
- Als het resultaat WEL op de curve ligt, retourneer
PubkeyError::InvalidSeeds(het adres zou een bijbehorende private key hebben, wat de PDA-beveiligingseigenschap schendt). - 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:
| Patroon | Seeds | Gebruikssituatie |
|---|---|---|
| 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.
| SDK | Functie |
|---|---|
@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}`);
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}`);
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}`);
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);}}
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
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?