Program Derived Address (PDA)
Program Derived Address (PDA) -osoitteet tarjoavat Solanan kehittäjille kaksi pääkäyttötapausta:
- Deterministiset tiliosoitteet: PDA-osoitteet tarjoavat mekanismin luoda deterministisesti osoitteen käyttämällä yhdistelmää valinnaisia "seed"-arvoja (ennalta määritettyjä syötteitä) ja tiettyä ohjelmatunnusta.
- Mahdollistaa ohjelman allekirjoituksen: Solanan ajoympäristö mahdollistaa ohjelmien "allekirjoittaa" PDA-osoitteita, jotka on johdettu ohjelman osoitteesta.
Voit ajatella PDA-osoitteita tapana luoda hajautustaulun kaltaisia rakenteita lohkoketjuun ennalta määritetyistä syötteistä (esim. merkkijonoista, numeroista ja muista tiliosoitteista).
Tämän lähestymistavan etu on, että se poistaa tarpeen seurata tarkkaa osoitetta. Sen sijaan sinun tarvitsee vain muistaa tietyt syötteet, joita käytettiin sen johtamiseen.
Program Derived Address
On tärkeää ymmärtää, että pelkkä Program Derived Address (PDA) -osoitteen johtaminen ei automaattisesti luo lohkoketjuun tiliä kyseiseen osoitteeseen. Tilit, joiden osoitteena on PDA, täytyy nimenomaisesti luoda ohjelman kautta, jota käytettiin osoitteen johtamiseen. Voit ajatella PDA:n johtamista kuin osoitteen löytämistä kartalta. Pelkkä osoitteen omistaminen ei tarkoita, että siihen paikkaan olisi rakennettu mitään.
Tämä osio käsittelee PDA-osoitteiden johtamisen yksityiskohtia. Osio Cross Program Invocation (CPI) selittää, miten ohjelmat käyttävät PDA-osoitteita allekirjoittamiseen.
Keskeiset kohdat
- PDA-osoitteet ovat osoitteita, jotka johdetaan deterministisesti käyttäen yhdistelmää ennalta määritettyjä seed-arvoja, bump seed -arvoa ja ohjelman tunnusta.
- PDA-osoitteet ovat osoitteita, jotka eivät sijaitse Ed25519-käyrällä ja niillä ei ole vastaavaa yksityistä avainta.
- Solana-ohjelmat voivat allekirjoittaa PDA-osoitteiden puolesta, jotka on johdettu niiden ohjelmatunnuksesta.
- PDA-osoitteen johtaminen ei automaattisesti luo lohkoketjuun tiliä.
- Tili, joka käyttää PDA-osoitetta osoitteenaan, täytyy luoda ohjeen kautta Solana-ohjelmassa.
Mikä on PDA
PDA:t ovat deterministisesti johdettuja osoitteita, jotka näyttävät julkisilta avaimiltä, mutta niillä ei ole yksityisiä avaimia. Tämä tarkoittaa, että osoitteelle ei ole mahdollista luoda kelvollista allekirjoitusta. Solanan ajoympäristö kuitenkin mahdollistaa ohjelmien "allekirjoittaa" PDA:iden puolesta ilman yksityistä avainta.
Kontekstina, Solanan Avainparit ovat pisteitä Ed25519-käyrällä (elliptisen käyrän kryptografia), joilla on julkinen avain ja vastaava yksityinen avain. Julkisia avaimia käytetään osoitteina (yksilöllisinä tunnisteina) lohkoketjun tileille.
Käyrällä oleva osoite
PDA on piste, joka on tarkoituksella johdettu Ed25519-käyrän ulkopuolelle käyttäen ennalta määritettyä joukkoa syötteitä. Pisteellä, joka ei ole Ed25519-käyrällä, ei ole kelvollista vastaavaa yksityistä avainta eikä se voi suorittaa kryptografisia toimintoja (allekirjoittamista).
PDA voi toimia osoitteena (yksilöllisenä tunnisteena) lohkoketjun tilille, tarjoten menetelmän helposti tallentaa, kartoittaa ja hakea ohjelman tilaa.
Käyrän ulkopuolinen osoite
Miten PDA johdetaan
PDA:n johtaminen vaatii kolme syötettä:
- Valinnaiset seed-arvot: Ennalta määritettyjä syötteitä (esim. merkkijonoja, numeroita, muita tilien osoitteita) PDA:n johtamiseen.
- Bump seed: Ylimääräinen tavu, joka lisätään valinnaisiin seed-arvoihin varmistaakseen, että kelvollinen PDA (käyrän ulkopuolella) luodaan. Bump seed alkaa arvosta 255 ja vähenee 1:llä kunnes kelvollinen PDA löytyy.
- Ohjelmatunnus: Sen ohjelman osoite, josta PDA johdetaan. Tämä ohjelma voi allekirjoittaa PDA:n puolesta.
PDA:n johtaminen
Käytä seuraavia funktioita vastaavista SDK:ista PDA:n johtamiseen.
SDK | Funktio |
---|---|
@solana/kit (Typescript) | getProgramDerivedAddress |
@solana/web3.js (Typescript) | findProgramAddressSync |
solana_sdk (Rust) | find_program_address |
PDA:n johtamiseksi anna seuraavat syötteet SDK-funktioon:
- Ennalta määritellyt valinnaiset seedit muunnettuna tavuiksi
- Johtamiseen käytetty ohjelmatunnus (osoite)
Kun kelvollinen PDA löytyy, funktio palauttaa sekä osoitteen (PDA) että johtamiseen käytetyn bump seedin.
Esimerkkejä
Seuraavat esimerkit näyttävät, miten PDA johdetaan käyttäen eri SDK:ita.
Napsauta "Suorita"-painiketta suorittaaksesi koodin.
PDA:n johtaminen valinnaisella merkkijonon seedillä
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}`);
PDA:n johtaminen valinnaisella osoitteen seedillä
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}`);
PDA:n johtaminen useilla valinnaisilla sedeillä
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}`);
Kanoninen bump
PDA-johtaminen vaatii "bump seed" -arvon, ylimääräisen tavun, joka lisätään valinnaisiin seed-arvoihin. Johtamisfunktio käy läpi bump-arvoja alkaen arvosta 255 ja vähentäen arvoa yhdellä, kunnes arvo tuottaa kelvollisen käyrän ulkopuolisen osoitteen. Ensimmäinen bump-arvo, joka tuottaa kelvollisen käyrän ulkopuolisen osoitteen, on "kanoninen bump".
Seuraavat esimerkit näyttävät PDA-johtamisen käyttäen kaikkia mahdollisia bump seed -arvoja (255- 0):
Kit-esimerkkiä ei ole sisällytetty, 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
Bump seed -arvo 255 aiheuttaa virheen ja ensimmäinen bump seed, joka johtaa kelvolliseen PDA:han on 254.
Huomaa, että bump seed -arvot 253-251 johtavat kaikki kelvollisiin PDA:ihin eri
osoitteilla. Tämä tarkoittaa, että samoilla valinnaisilla seed-arvoilla ja
programId
, eri bump seed -arvo voi silti johtaa kelvolliseen PDA:han.
Kun rakennat Solana-ohjelmia, sisällytä aina turvallisuustarkistukset varmistaaksesi, että ohjelmalle välitetty PDA on johdettu kanonisesta bump-arvosta. Jos näitä tarkistuksia ei sisällytetä, se voi aiheuttaa haavoittuvuuksia, jotka mahdollistavat odottamattomien tilien käytön ohjelman ohjeissa. Parhaana käytäntönä on käyttää vain kanonista bump-arvoa PDA:ita johdettaessa.
PDA-tilien luominen
Alla oleva esimerkkiohjelma näyttää, miten luodaan tili käyttäen PDA:ta uuden tilin osoitteena. Esimerkkiohjelma käyttää Anchor-kehystä.
Ohjelma sisältää yhden initialize
-ohjeen uuden tilin luomiseksi käyttäen
PDA:ta tilin osoitteena. Uusi tili tallentaa user
-osoitteen ja bump
seed-arvon, jota käytettiin PDA:n johtamiseen.
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 bumpaccount_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 PDAseeds = [b"data", user.key().as_ref()],// use the canonical bumpbump,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,}
Tässä esimerkissä PDA-johtamisen seedit sisältävät kiinteän merkkijonon data
ja ohjeessa annetun user
tilin osoitteen. Anchor- kehys löytää automaattisesti
kanonisen bump
seedin.
#[account(init,seeds = [b"data", user.key().as_ref()],bump,payer = user,space = 8 + DataAccount::INIT_SPACE)]pub pda_account: Account<'info, DataAccount>,
init
rajoite ohjeistaa Anchoria kutsumaan System Programia uuden tilin
luomiseksi käyttäen PDA:ta osoitteena. Anchor tekee tämän
CPI-kutsun kautta.
#[account(init,seeds = [b"data", user.key().as_ref()],bump,payer = user,space = 8 + DataAccount::INIT_SPACE)]pub pda_account: Account<'info, DataAccount>,
Testitiedosto sisältää Typescript-koodin PDA:n johtamiseksi.
const [PDA] = PublicKey.findProgramAddressSync([Buffer.from("data"), user.publicKey.toBuffer()],program.programId);
Testitiedoston transaktio kutsuu initialize
ohjetta luodakseen uuden
lohkoketjutilin käyttäen PDA:ta osoitteena. Tässä esimerkissä Anchor voi
päätellä PDA-osoitteen ohjeen tileissä, joten sitä ei tarvitse nimenomaisesti
määrittää.
it("Is initialized!", async () => {const transactionSignature = await program.methods.initialize().accounts({user: user.publicKey}).rpc();console.log("Transaction Signature:", transactionSignature);});
Testitiedosto näyttää myös, miten hakea kyseiseen osoitteeseen luotu lohkoketjutili transaktion lähettämisen jälkeen.
it("Fetch Account", async () => {const pdaAccount = await program.account.dataAccount.fetch(PDA);console.log(JSON.stringify(pdaAccount, null, 2));});
Huomaa, että tässä esimerkissä, jos kutsut initialize
ohjetta useammin kuin
kerran käyttäen samaa user
osoitetta seedinä, transaktio epäonnistuu. Tämä
tapahtuu, koska johdettuun osoitteeseen on jo olemassa tili.
Is this page helpful?