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 AddressProgram 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 osoiteKä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 osoiteKä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 johtaminenPDA:n johtaminen

Käytä seuraavia funktioita vastaavista SDK:ista PDA:n johtamiseen.

SDKFunktio
@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}`);
Click to execute the code.

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}`);
Click to execute the code.

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}`);
Click to execute the code.

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);
}
}
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

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 bump
account_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 PDA
seeds = [b"data", user.key().as_ref()],
// use the canonical bump
bump,
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.

pda_account
#[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.

pda_account
#[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.

Derive PDA
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ää.

Invoke Initialize Instruction
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.

Fetch Account
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?

Sisällysluettelo

Muokkaa sivua