PDA-Ableitung

Zusammenfassung

PDAs werden abgeleitet, indem Seeds + Programm-ID + Bump über SHA-256 gehasht werden, bis das Ergebnis außerhalb der Ed25519-Kurve liegt. Der kanonische Bump ist der erste Wert, der eine Off-Curve-Adresse erzeugt. Max. 16 Seeds, max. 32 Bytes pro Seed.

Hintergrund

Solana- Keypair-Werte sind Punkte auf der Ed25519-Kurve. Ein Keypair besteht aus einem öffentlichen Schlüssel (verwendet als Kontenadresse) und einem geheimen Schlüssel (verwendet zur Erzeugung von Signaturen). Jeder, der den geheimen Schlüssel besitzt, kann Transaktionen für diese Adresse signieren.

Zwei Konten mit On-Curve-AdressenZwei Konten mit On-Curve-Adressen

Eine PDA wird absichtlich so abgeleitet, dass sie außerhalb der Ed25519-Kurve liegt. Da sie kein gültiger Kurvenpunkt ist, existiert kein geheimer Schlüssel, und keine externe Partei kann eine Signatur erzeugen. Nur das ableitende Programm kann Operationen auf der PDA durch invoke_signed autorisieren.

Off-Curve-AdresseOff-Curve-Adresse

PDA- vs. Keypair-Konten

EigenschaftKeypair-KontoPDA-Konto
AdresstypAuf Ed25519-KurveAußerhalb Ed25519-Kurve
Hat privaten SchlüsselJaNein
Kann Transaktionen signierenJa (mit privatem Schlüssel)Nein
Kann während CPI signierenNein (außer Signatur in Transaktion enthalten)Ja (über invoke_signed)
AbleitungEd25519-Keypair generierenDeterministisch aus Seeds + Programm-ID
Typische VerwendungBenutzer-Wallets, Programm-IDProgrammeigene Datenkonten

Optionale Seeds

Die optionalen Seeds sind benutzerdefinierte Byte-Strings, die als Eingaben für die PDA-Ableitung dienen. Sie erstellen eindeutige, deterministische Adressen, die auf ein Programm beschränkt sind. Beispielsweise leitet die Verwendung von ["user", user_pubkey] als Seeds eine andere PDA für jeden Benutzer ab.

Seeds müssen folgende Einschränkungen erfüllen:

  • Maximal 16 Seeds pro Ableitung (MAX_SEEDS)
  • Maximal 32 Bytes pro Seed (MAX_SEED_LEN)

Bump Seed

Der Bump Seed ist ein einzelnes Byte (0-255), das während der Ableitung an die optionalen Seeds angehängt wird. find_program_address sucht von 255 abwärts bis 0 und ruft create_program_address mit jedem Wert auf, bis das Ergebnis außerhalb der Ed25519-Kurve liegt. Der erste Wert, der erfolgreich ist, ist der kanonische Bump.

Programme sollten immer den kanonischen Bump verwenden, um eine eindeutige, deterministische Zuordnung von Seeds zu Adressen sicherzustellen.

Verwenden Sie beim Ableiten von PDAs immer den kanonischen Bump. Die Verwendung eines nicht-kanonischen Bumps erstellt eine zweite gültige Adresse für dieselben Seeds, was zu Sicherheitslücken führen kann, bei denen ein Angreifer ein anderes Konto als erwartet einsetzt.

PDA-AbleitungPDA-Ableitung

Ableitungsalgorithmus

Die PDA-Ableitung ist in der SDK-Funktion create_program_address implementiert. Der Algorithmus funktioniert wie folgt:

  1. Validieren Sie, dass die Anzahl der Seeds MAX_SEEDS (16) nicht überschreitet und kein einzelner Seed MAX_SEED_LEN (32 Bytes) überschreitet. Wenn eine der Prüfungen fehlschlägt, geben Sie PubkeyError::MaxSeedLengthExceeded zurück.
  2. Hashen Sie alle Seeds, die Programm-ID und den String "ProgramDerivedAddress" mit SHA-256 zusammen, um ein 32-Byte-Ergebnis zu erzeugen.
  3. Prüfen Sie, ob das Ergebnis ein gültiger Punkt auf der Ed25519-Kurve ist.
  4. Wenn das Ergebnis AUF der Kurve liegt, geben Sie PubkeyError::InvalidSeeds zurück (die Adresse hätte einen entsprechenden privaten Schlüssel, was die PDA-Sicherheitseigenschaft verletzt).
  5. Wenn das Ergebnis NICHT auf der Kurve liegt, geben Sie es als PDA zurück.

Compute Unit-Kosten

Der On-Chain-Syscall für create_program_address berechnet 1.500 CUs pro Aufruf.

Der try_find_program_address-Syscall berechnet 1.500 CUs beim Eintritt (vor der Schleife) und dann zusätzliche 1.500 CUs für jeden fehlgeschlagenen Bump-Versuch innerhalb der Schleife.

Gängige Seed-Muster

Seeds sind anwendungsspezifisch. Gängige Muster umfassen:

MusterSeedsAnwendungsfall
Globales Singleton["global"]Einzelnes programmweites Konfigurations-Konto
Pro-Benutzer-Konto["user", user_pubkey]Ein Konto pro Benutzer pro Programm
Pro-Benutzer-pro-Entität["vault", user_pubkey, mint_pubkey]Token-Vaults, pro Benutzer pro Token
Zähler / sequenziell["order", user_pubkey, &order_id.to_le_bytes()]Sequenzielle Datensätze pro Benutzer

Seeds werden vor dem Hashing verkettet, daher erzeugen ["ab", "cd"] und ["abcd"] dieselbe PDA. Verwenden Sie Seeds mit fester Länge oder ein Trennzeichen, um Kollisionen zu vermeiden. Zum Beispiel ist ["ab", "-", "cd"] eindeutig.

Beispiele: Eine PDA ableiten

Das Ableiten einer PDA berechnet nur eine Adresse. Es erstellt kein On-Chain-Konto an dieser Adresse. Das Konto muss explizit durch eine separate Anweisung erstellt werden (typischerweise create_account via CPI).

Die Solana-SDKs bieten Funktionen für die PDA-Ableitung. Jede Funktion benötigt:

  • Programm-ID: Die Adresse des Programms, das zur Ableitung der PDA verwendet wird. Dieses Programm kann im Namen der PDA signieren.
  • Optionale Seeds: Vordefinierte Eingaben wie Strings, Zahlen oder andere Konten-Adressen.
SDKFunktion
@solana/kit (TypeScript)getProgramDerivedAddress
@solana/web3.js (TypeScript)findProgramAddressSync
solana_sdk (Rust)find_program_address

Die folgenden Beispiele leiten eine PDA mithilfe der Solana-SDKs ab. Klicken Sie auf ▷ Ausführen, um den Code auszuführen.

Eine PDA mit einem String-Seed ableiten

Das folgende Beispiel leitet eine PDA unter Verwendung einer Programm-ID und eines optionalen String-Seeds ab.

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.

Eine PDA mit einem Adressen-Seed ableiten

Das folgende Beispiel leitet eine PDA unter Verwendung einer Programm-ID und eines optionalen Adressen-Seeds ab.

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.

Eine PDA mit mehreren Seeds ableiten

Das folgende Beispiel leitet eine PDA unter Verwendung einer Programm-ID und mehrerer optionaler Seeds ab.

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.

Iteration über alle Bumps

Die folgenden Beispiele zeigen die PDA-Ableitung unter Verwendung aller möglichen Bump-Seeds (255 bis 0) und veranschaulichen, wie find_program_address den kanonischen Bump zurückgibt:

Das Kit-Beispiel ist nicht enthalten, da die Funktion createProgramDerivedAddress nicht exportiert wird.

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

In diesem Beispiel erzeugt Bump 255 eine On-Curve-Adresse und schlägt fehl. Der erste gültige Bump ist 254, was ihn zum kanonischen Bump macht.

Is this page helpful?

Verwaltet von

© 2026 Solana Foundation.
Alle Rechte vorbehalten.
Verbinden Sie sich