Ohjelmasta johdettu osoite
Solanan tiliosoite osoittaa tilin sijaintiin lohkoketjussa. Monet tiliosoitteet ovat avainparin julkinen avain, jolloin vastaavaa yksityistä avainta käytetään tiliin liittyvien tapahtumien allekirjoittamiseen.
Hyödyllinen vaihtoehto julkisen avaimen osoitteelle on ohjelmasta johdettu osoite (PDA). PDA:t tarjoavat helpon tavan tallentaa, kartoittaa ja hakea ohjelman tilaa. PDA on osoite, joka luodaan deterministisesti käyttäen ohjelmatunnusta ja yhdistelmää valinnaisia ennalta määritettyjä syötteitä. PDA:t näyttävät samanlaisilta kuin julkisen avaimen osoitteet, mutta niillä ei ole vastaavaa yksityistä avainta.
Solanan ajoympäristö mahdollistaa ohjelmien allekirjoittaa PDA:iden puolesta ilman yksityistä avainta. PDA:n käyttö poistaa tarpeen seurata tilin osoitetta. Sen sijaan voit palauttaa mieleen tietyt syötteet, joita käytettiin PDA:n johtamiseen. (Saadaksesi tietää, miten ohjelmat käyttävät PDA:ita allekirjoittamiseen, katso Ohjelmien väliset kutsut -osio.)
Tausta
Solanan avainparit ovat pisteitä Ed25519-käyrällä (elliptisen käyrän salaus). Ne koostuvat julkisesta avaimesta ja yksityisestä avaimesta. Julkisesta avaimesta tulee tilin osoite, ja yksityistä avainta käytetään tilin kelvollisten allekirjoitusten luomiseen.
Kaksi tiliä käyrällä olevilla osoitteilla
PDA on tarkoituksella johdettu Ed25519-käyrän ulkopuolelle. Tämä tarkoittaa, että sillä ei ole kelvollista vastaavaa yksityistä avainta eikä se voi suorittaa kryptografisia toimintoja. (Kuten allekirjoituksen tarjoaminen.) Solana kuitenkin mahdollistaa ohjelmien allekirjoittaa PDA:iden puolesta ilman yksityistä avainta.
Off Curve Address
Voit ajatella PDA:ita tapana luoda hajautustaulun kaltaisia rakenteita lohkoketjussa käyttäen ennalta määritettyä joukkoa syötteitä. (Esimerkiksi merkkijonoja, numeroita ja muita tilien osoitteita.)
Program Derived Address
PDA:n johtaminen
Ennen kuin luot tilin PDA:lla, sinun on ensin johdettava osoite. PDA:n johtaminen ei automaattisesti luo lohkoketjuun tiliä kyseiseen osoitteeseen – tili on nimenomaisesti luotava PDA:n johtamiseen käytetyn ohjelman kautta. Voit ajatella PDA:ta kuin osoitetta kartalla: vain koska osoite on olemassa, ei tarkoita, että sinne on rakennettu mitään.
Solana SDK:t tukevat PDA:n luomista alla olevassa taulukossa näytetyillä funktioilla. Jokainen funktio vastaanottaa seuraavat syötteet:
- Program ID: Sen ohjelman osoite, jota käytetään PDA:n johtamiseen. Tämä ohjelma voi allekirjoittaa PDA:n puolesta.
- Optional seeds: 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 |
Funktio käyttää program ID:tä ja valinnaisia seedejä, ja käy sitten läpi bump-arvoja yrittäen luoda kelvollisen ohjelma-osoitteen. Bump-arvojen läpikäynti alkaa arvosta 255 ja vähenee yhdellä, kunnes kelvollinen PDA löytyy. Kun kelvollinen PDA on löydetty, funktio palauttaa PDA:n ja bump seedin.
Bump seed on ylimääräinen tavu, joka lisätään valinnaisiin seedeihin varmistaakseen, että luodaan kelvollinen off-curve-osoite.
PDA-johtaminen
Kanoninen bump
Bump seed on ylimääräinen tavu, 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äistä arvoa, joka tuottaa kelvollisen käyrän ulkopuolisen osoitteen, kutsutaan "kanoniseksi bumpiksi".
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
Tässä esimerkissä ensimmäinen bump seed aiheuttaa virheen. Ensimmäinen bump seed, joka johtaa kelvolliseen PDA:han, on 254. Bump seed -arvot 253-251 johtavat myös yksilöllisiin, kelvollisiin PDA:ihin.
Tämä tarkoittaa, että samoilla valinnaisilla seed-arvoilla ja programId, eri
bump seed -arvo voi silti johtaa kelvolliseen PDA:han.
Sisällytä aina turvallisuustarkistukset varmistaaksesi, että ohjelmalle välitetty PDA on johdettu kanonisesta bumpista. Jos näin ei tehdä, voi syntyä haavoittuvuuksia, jotka mahdollistavat odottamattomien tilien käytön ohjelman ohjeissa. Parhaana käytäntönä on käyttää vain kanonista bumpia PDA:ita johdettaessa.
Esimerkkejä
Alla olevat esimerkit johtavat PDA:n käyttäen Solana SDK:ta. Napsauta ▷ Run suorittaaksesi koodin.
PDA:n johtaminen merkkijonon seedillä
Alla oleva esimerkki johtaa PDA:n käyttäen ohjelmatunnusta ja valinnaista merkkijonon seediä.
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 osoitteen seedillä
Alla oleva esimerkki johtaa PDA:n käyttäen ohjelmatunnusta ja valinnaista osoitteen seediä.
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 sedeillä
Alla oleva esimerkki johtaa PDA:n käyttäen ohjelmatunnusta 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}`);
PDA-tilin luominen
Alla oleva esimerkki käyttää Anchor-kehystä
luodakseen uuden tilin ohjelman johtamalla osoitteella. Ohjelma sisältää yhden
initialize -ohjeen uuden tilin luomiseksi, joka
tallentaa käyttäjän osoitteen ja PDA:n johtamiseen
käytetyn bump seedin.
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 bumpdaccount_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,}
init -rajoite kertoo Anchorille, että sen tulee
kutsua System Programia luodakseen
uuden tilin käyttäen PDA:ta osoitteena. PDA:n luomiseen käytetyt
seedit ovat:
- Ohjeessa annetun käyttäjätilin osoite
- Kiinteä merkkijono: "data"
- Kanoninen bump seed
Tässä esimerkissä bump-rajoitteelle ei ole määritetty arvoa, joten Anchor
käyttää find_program_address johtaakseen PDA:n ja löytääkseen bumpin.
#[account(init,seeds = [b"data", user.key().as_ref()],bump,payer = user,space = 8 + DataAccount::INIT_SPACE)]pub pda_account: Account<'info, DataAccount>,
Alla oleva testitiedosto sisältää transaktion, joka kutsuu
initialize -ohjetta luodakseen uuden tilin
ohjelmajohdannaisella osoitteella. Tiedosto sisältää koodin
PDA:n johtamiseen.
Esimerkki näyttää myös, kuinka hakea uusi tili, joka luodaan.
import * as anchor from "@coral-xyz/anchor";import { Program } from "@coral-xyz/anchor";import { PdaAccount } from "../target/types/pda_account";import { PublicKey } from "@solana/web3.js";describe("pda-account", () => {const provider = anchor.AnchorProvider.env();anchor.setProvider(provider);const program = anchor.workspace.PdaAccount as Program<PdaAccount>;const user = provider.wallet as anchor.Wallet;// Derive the PDA address using the seeds specified on the programconst [PDA] = PublicKey.findProgramAddressSync([Buffer.from("data"), user.publicKey.toBuffer()],program.programId);it("Is initialized!", async () => {const transactionSignature = await program.methods.initialize().accounts({user: user.publicKey}).rpc();console.log("Transaction Signature:", transactionSignature);});it("Fetch Account", async () => {const pdaAccount = await program.account.dataAccount.fetch(PDA);console.log(JSON.stringify(pdaAccount, null, 2));});});
Jos kutsut initialize -ohjetta uudelleen samalla user -osoitteen seedillä,
transaktio epäonnistuu. Tämä tapahtuu, koska johdettuun osoitteeseen on jo
olemassa tili.
Is this page helpful?