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ääritetystä syötejoukosta (esim. merkkijonot, numerot ja muut tiliosoitteet).
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, joilla on PDA lohkoketjuosoitteena, on nimenomaisesti luotava ohjelman kautta, jota käytettiin osoitteen johtamiseen. Voit ajatella PDA:n johtamista kuin osoitteen löytämistä kartalta. Pelkkä osoitteen omistaminen ei tarkoita, että kyseiseen 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 jäävät Ed25519-käyrän ulkopuolelle ja joilla ei ole vastaavaa yksityistä avainta.
- Solana-ohjelmat voivat allekirjoittaa PDA-osoitteiden puolesta, jotka on johdettu sen ohjelmatunnuksesta.
- PDA-osoitteen johtaminen ei automaattisesti luo lohkoketjutiliä.
- Tili, joka käyttää PDA-osoitetta osoitteenaan, on luotava Solana-ohjelman ohjeen kautta.
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.
Taustatietona, 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ä syötteiden joukkoa. 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 ohjelman tilan helppoon tallentamiseen, kartoittamiseen ja hakemiseen.
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 ulkopuolinen) 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 tavuiksi muunnettuina
- 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 "Run"-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 seededeillä
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 seedeihin. 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 kaikki johtavat kelvollisiin PDA:ihin eri
osoitteilla. Tämä tarkoittaa, että samoilla valinnaisilla seededeillä ja
programId
, eri bump seed -arvolla voidaan silti johtaa kelvollinen PDA.
Kun rakennat Solana-ohjelmia, sisällytä aina turvallisuustarkistukset varmistaaksesi, että ohjelmalle välitetty PDA on johdettu kanonisesta bumpista. Jos näitä tarkistuksia ei sisällytetä, se voi tuoda haavoittuvuuksia, jotka mahdollistavat odottamattomien tilien käytön ohjelman ohjeissa. Parhaana käytäntönä on käyttää vain kanonista bumpia PDA:iden johtamisessa.
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 seed-arvot sisältävät kiinteän merkkijonon
data
ja ohjeessa annetun user
-tilin osoitteen. Anchor- kehys löytää
automaattisesti kanonisen bump
-seed-arvon.
#[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 Program -ohjelmaa 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 ohjetilejä varten, 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 seed-arvona, transaktio epäonnistuu. Tämä
tapahtuu, koska johdettuun osoitteeseen on jo olemassa tili.
Is this page helpful?