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 osoitteillaKaksi 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 AddressOff 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 AddressProgram 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.
SDKFunktio
@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-johtaminenPDA-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);
}
}
Console
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

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

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

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

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.

Program
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 bumpd
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,
}

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.

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>,

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.

Test
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 program
const [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?

Sisällysluettelo

Muokkaa sivua

Hallinnoi

© 2025 Solana Foundation.
Kaikki oikeudet pidätetään.