Yhteenveto
PDA:t johdetaan hajautusarvolla seedit + ohjelmatunniste + bump SHA-256:n kautta, kunnes tulos on Ed25519-käyrän ulkopuolella. Kanoninen bump on ensimmäinen arvo, joka tuottaa käyrän ulkopuolisen osoitteen. Maksimi 16 seediä, maksimi 32 tavua per seedi.
Tausta
Solanan
Keypair
-arvot ovat pisteitä Ed25519-käyrällä. Avainpari
koostuu julkisesta avaimesta (käytetään tilin osoitteena) ja salaisesta
avaimesta (käytetään allekirjoitusten tuottamiseen). Kuka tahansa, jolla on
salainen avain, voi allekirjoittaa transaktioita kyseiselle osoitteelle.
PDA johdetaan tarkoituksella putoamaan Ed25519-käyrän ulkopuolelle. Koska se
ei ole kelvollinen käyräpiste, salaista avainta ei ole olemassa, eikä mikään
ulkopuolinen taho voi tuottaa allekirjoitusta. Vain johdattava ohjelma voi
valtuuttaa operaatioita PDA:lla invoke_signed:n kautta.
PDA vs avainparitilit
| Ominaisuus | Avainparitili | PDA-tili |
|---|---|---|
| Osoitetyyppi | Ed25519-käyrällä | Ed25519-käyrän ulkopuolella |
| On yksityinen avain | Kyllä | Ei |
| Voi allekirjoittaa transaktioita | Kyllä (yksityisellä avaimella) | Ei |
| Voi allekirjoittaa CPI:n aikana | Ei (ellei allekirjoitusta sisällytetä transaktioon) | Kyllä (invoke_signed:n kautta) |
| Johdannainen | Generoi Ed25519-avainpari | Deterministinen seediistä + ohjelmatunnisteesta |
| Tyypillinen käyttö | Käyttäjälompakot, ohjelmatunniste | Ohjelman omistamat datatilit |
Valinnaiset siemenet
Valinnaiset siemenet ovat käyttäjän määrittämiä tavujonoja, jotka toimivat
syötteinä PDA-johdannaiselle. Ne luovat ainutlaatuisia, deterministisiä
osoitteita, jotka on rajattu ohjelmaan. Esimerkiksi ["user", user_pubkey]:n
käyttäminen siemeninä johtaa eri PDA:han jokaiselle käyttäjälle.
Siementen on noudatettava seuraavia rajoituksia:
- Enintään 16 siementä johdannaista kohti (
MAX_SEEDS) - Enintään 32 tavua siementä kohti (
MAX_SEED_LEN)
Bump seed
Bump seed on yksi tavu (u8), joka liitetään valinnaisten seedien perään
johdannaisessa.
find_program_address
etsii arvosta 255 alaspäin arvoon 1, kutsuen create_program_address
-funktiota jokaisella arvolla, kunnes tulos putoaa Ed25519-käyrän ulkopuolelle.
Ensimmäinen onnistunut arvo on kanoninen bump.
Ohjelmien tulisi aina käyttää kanonista bumpia varmistaakseen ainutlaatuisen, deterministisen yhdistämisen siemenistä osoitteeseen.
Käytä aina kanonista bumpia johdettaessa PDA:ita. Ei-kanonisen bumpin käyttäminen luo toisen kelvollisen osoitteen samoille siemenille, mikä voi johtaa haavoittuvuuksiin, joissa hyökkääjä korvaa odotetun tilin toisella.
Johdannaisalgoritmi
PDA-johdannainen on toteutettu SDK:n
create_program_address-funktiossa.
Algoritmi toimii seuraavasti:
- Validoi, että siementen määrä ei ylitä
MAX_SEEDS:a (16) eikä yksittäinen siemen ylitäMAX_SEED_LEN:a (32 tavua). Jos jompikumpi tarkistus epäonnistuu, palautaPubkeyError::MaxSeedLengthExceeded. - SHA-256-hajautusarvo kaikista siemenistä, ohjelmatunnuksesta ja merkkijonosta
"ProgramDerivedAddress"yhdessä tuottaaksesi 32-tavuisen tuloksen. - Tarkista, onko tulos kelvollinen piste Ed25519-käyrällä.
- Jos tulos ON käyrällä, palauta
PubkeyError::InvalidSeeds(osoitteella olisi vastaava yksityinen avain, mikä rikkoo PDA:n turvallisuusominaisuutta). - Jos tulos EI ole käyrällä, palauta se PDA:na.
Laskentayksikkökustannukset
Lohkoketjussa käytettävä
onchain syscall
create_program_address-toiminnolle veloittaa
1 500 CU:ta
per kutsu.
try_find_program_address syscall
veloittaa 1 500 CU:ta alussa (ennen silmukkaa), ja sen jälkeen ylimääräiset 1
500 CU:ta jokaisesta epäonnistuneesta bump-yrityksestä silmukan sisällä.
Yleiset seed-mallit
Seedit ovat sovelluskohtaisia. Yleisiä malleja ovat:
| Malli | Seedit | Käyttötapaus |
|---|---|---|
| Globaali singleton | ["global"] | Yksittäinen ohjelman laajuinen konfiguraatiotili |
| Käyttäjäkohtainen tili | ["user", user_pubkey] | Yksi tili per käyttäjä per ohjelma |
| Käyttäjä-entiteetti-kohtainen | ["vault", user_pubkey, mint_pubkey] | Token-holvit, per käyttäjä per token |
| Laskuri / peräkkäinen | ["order", user_pubkey, &order_id.to_le_bytes()] | Peräkkäiset tietueet per käyttäjä |
Seedit yhdistetään ennen hajautusta, joten ["ab", "cd"] ja ["abcd"]
tuottavat saman PDA:n. Käytä kiinteän pituisia seedejä tai erotinta
välttääksesi törmäykset. Esimerkiksi ["ab", "-", "cd"] on yksiselitteinen.
Esimerkit: PDA:n johdattaminen
PDA:n johdannainen laskee vain osoitteen. Se ei luo lohkoketjussa olevaa tiliä
tuohon osoitteeseen. Tili on luotava erikseen erillisellä instruktiolla
(tyypillisesti create_account CPI:n kautta).
Solana SDK:t tarjoavat funktioita PDA:n johdattamiseen. Jokainen funktio ottaa:
- Program ID: Ohjelman osoite, jota käytetään PDA:n johdattamiseen. Tämä ohjelma voi allekirjoittaa PDA:n puolesta.
- Valinnaiset seedit: Ennalta määritellyt syötteet, kuten merkkijonot, numerot tai muut tilien osoitteet.
| SDK | Funktio |
|---|---|
@solana/kit (TypeScript) | getProgramDerivedAddress |
@solana/web3.js (TypeScript) | findProgramAddressSync |
solana_sdk (Rust) | find_program_address |
Alla olevat esimerkit johdattavat PDA:n käyttäen Solana SDK:ita. Napsauta ▷ Suorita suorittaaksesi koodin.
Johda PDA merkkijonoseedillä
Alla oleva esimerkki johtaa PDA:n käyttäen ohjelma-ID:tä ja valinnaista merkkijonoseedä.
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}`);
Johda PDA osoiteseedillä
Alla oleva esimerkki johtaa PDA:n käyttäen ohjelma-ID:tä ja valinnaista osoiteseedä.
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}`);
Johda PDA useilla seedeillä
Alla oleva esimerkki johtaa PDA:n käyttäen ohjelma-ID:tä ja useita valinnaisia seedejä.
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}`);
Kaikkien mahdollisten bump-arvojen iterointi
Seuraavat esimerkit näyttävät PDA:n johtamisen käyttäen kaikkia mahdollisia
bump-seedejä (255:stä 0:aan), havainnollistaen kuinka find_program_address
palauttaa kanonisen bumpin:
Tämä eroaa nykyisestä kanonisesta hakukäyttäytymisestä, joka on dokumentoitu
sol_try_find_program_address -toiminnolle ja joka iteroi arvosta 255
alaspäin arvoon 1.
Kit-esimerkkiä ei ole mukana, koska
createProgramDerivedAddress
-funktiota ei ole viety.
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
Tässä esimerkissä bump 255 tuottaa käyrällä olevan osoitteen ja epäonnistuu. Ensimmäinen kelpaava bump on 254, mikä tekee siitä kanonisen bumpin.
Is this page helpful?