Παραγωγή PDA

Περίληψη

Τα PDA παράγονται με hashing των seeds + program ID + bump μέσω SHA-256 μέχρι το αποτέλεσμα να βρίσκεται εκτός της καμπύλης Ed25519. Το κανονικό bump είναι η πρώτη τιμή που παράγει μια διεύθυνση εκτός καμπύλης. Μέγιστο 16 seeds, μέγιστο 32 bytes ανά seed.

Ιστορικό

Οι τιμές Keypair του Solana είναι σημεία στην καμπύλη Ed25519. Ένα keypair αποτελείται από ένα δημόσιο κλειδί (που χρησιμοποιείται ως διεύθυνση λογαριασμού) και ένα μυστικό κλειδί (που χρησιμοποιείται για την παραγωγή υπογραφών). Οποιοσδήποτε έχει το μυστικό κλειδί μπορεί να υπογράψει συναλλαγές για αυτή τη διεύθυνση.

Δύο λογαριασμοί με διευθύνσεις επί της καμπύληςΔύο λογαριασμοί με διευθύνσεις επί της καμπύλης

Ένα PDA παράγεται σκόπιμα ώστε να βρίσκεται εκτός της καμπύλης Ed25519. Επειδή δεν είναι έγκυρο σημείο της καμπύλης, δεν υπάρχει μυστικό κλειδί και κανένα εξωτερικό μέρος δεν μπορεί να παράγει υπογραφή. Μόνο το πρόγραμμα παραγωγής μπορεί να εξουσιοδοτήσει λειτουργίες στο PDA μέσω invoke_signed.

Διεύθυνση εκτός καμπύληςΔιεύθυνση εκτός καμπύλης

Λογαριασμοί PDA έναντι keypair

ΙδιότηταΛογαριασμός keypairΛογαριασμός PDA
Τύπος διεύθυνσηςΕπί της καμπύλης Ed25519Εκτός της καμπύλης Ed25519
Έχει ιδιωτικό κλειδίΝαιΌχι
Μπορεί να υπογράψει συναλλαγέςΝαι (με ιδιωτικό κλειδί)Όχι
Μπορεί να υπογράψει κατά τη διάρκεια CPIΌχι (εκτός αν η υπογραφή περιλαμβάνεται στη συναλλαγή)Ναι (μέσω invoke_signed)
ΠαραγωγήΔημιουργία keypair Ed25519Ντετερμινιστική από seeds + program ID
Τυπική χρήσηΠορτοφόλια χρηστών, program IDΛογαριασμοί δεδομένων που ανήκουν σε πρόγραμμα

Προαιρετικά seeds

Τα προαιρετικά seeds είναι byte strings που ορίζονται από τον χρήστη και χρησιμεύουν ως εισροές για την παραγωγή PDA. Δημιουργούν μοναδικές, ντετερμινιστικές διευθύνσεις που ανήκουν σε ένα πρόγραμμα. Για παράδειγμα, η χρήση ["user", user_pubkey] ως seeds παράγει διαφορετική PDA για κάθε χρήστη.

Τα seeds πρέπει να ακολουθούν τους εξής περιορισμούς:

  • Μέγιστο 16 seeds ανά παραγωγή (MAX_SEEDS)
  • Μέγιστο 32 bytes ανά seed (MAX_SEED_LEN)

Bump seed

Το bump seed είναι ένα μονό byte (0-255) που προσαρτάται στα προαιρετικά seeds κατά την παραγωγή. Η find_program_address αναζητά από το 255 προς το 0, καλώντας την create_program_address με κάθε τιμή μέχρι το αποτέλεσμα να βγει εκτός της καμπύλης Ed25519. Η πρώτη τιμή που πετυχαίνει είναι το κανονικό bump.

Τα προγράμματα θα πρέπει πάντα να χρησιμοποιούν το κανονικό bump για να διασφαλίζουν μια μοναδική, ντετερμινιστική αντιστοίχιση από τα seeds στη διεύθυνση.

Χρησιμοποιείτε πάντα το κανονικό bump κατά την παραγωγή PDAs. Η χρήση μη κανονικού bump δημιουργεί μια δεύτερη έγκυρη διεύθυνση για τα ίδια seeds, κάτι που μπορεί να οδηγήσει σε ευπάθειες όπου ένας εισβολέας υποκαθιστά διαφορετικό λογαριασμό από τον αναμενόμενο.

PDA DerivationPDA Derivation

Αλγόριθμος παραγωγής

Η παραγωγή PDA υλοποιείται στη συνάρτηση create_program_address του SDK. Ο αλγόριθμος λειτουργεί ως εξής:

  1. Επικυρώνει ότι ο αριθμός των seeds δεν υπερβαίνει το MAX_SEEDS (16) και κανένα μεμονωμένο seed δεν υπερβαίνει το MAX_SEED_LEN (32 bytes). Εάν αποτύχει κάποιος από τους ελέγχους, επιστρέφει PubkeyError::MaxSeedLengthExceeded.
  2. Κάνει SHA-256 hash όλα τα seeds, το program ID και το string "ProgramDerivedAddress" μαζί για να παράγει ένα αποτέλεσμα 32 bytes.
  3. Ελέγχει αν το αποτέλεσμα είναι έγκυρο σημείο στην καμπύλη Ed25519.
  4. Εάν το αποτέλεσμα ΕΙΝΑΙ στην καμπύλη, επιστρέφει PubkeyError::InvalidSeeds (η διεύθυνση θα είχε αντίστοιχο ιδιωτικό κλειδί, κάτι που παραβιάζει την ιδιότητα ασφαλείας του PDA).
  5. Εάν το αποτέλεσμα ΔΕΝ ΕΙΝΑΙ στην καμπύλη, το επιστρέφει ως PDA.

Κόστος μονάδων υπολογισμού

Η on-chain syscall για create_program_address χρεώνει 1.500 CUs ανά κλήση.

Η try_find_program_address syscall χρεώνει 1.500 CUs κατά την είσοδο (πριν από τον βρόχο), και στη συνέχεια επιπλέον 1.500 CUs για κάθε αποτυχημένη προσπάθεια bump εντός του βρόχου.

Συνήθη μοτίβα seed

Τα seeds είναι εξειδικευμένα ανά εφαρμογή. Συνήθη μοτίβα περιλαμβάνουν:

ΜοτίβοSeedsΠερίπτωση χρήσης
Global singleton["global"]Μοναδικός λογαριασμός ρυθμίσεων σε επίπεδο προγράμματος
Per-user account["user", user_pubkey]Ένας λογαριασμός ανά χρήστη ανά πρόγραμμα
Per-user-per-entity["vault", user_pubkey, mint_pubkey]Token vaults, ανά χρήστη ανά token
Counter / sequential["order", user_pubkey, &order_id.to_le_bytes()]Διαδοχικές εγγραφές ανά χρήστη

Τα seeds συνενώνονται πριν από το hashing, επομένως τα ["ab", "cd"] και ["abcd"] παράγουν το ίδιο PDA. Χρησιμοποιήστε seeds σταθερού μήκους ή διαχωριστικό για να αποφύγετε συγκρούσεις. Για παράδειγμα, το ["ab", "-", "cd"] είναι μονοσήμαντο.

Παραδείγματα: Παραγωγή ενός PDA

Η παραγωγή ενός PDA υπολογίζει μόνο μια διεύθυνση. Δεν δημιουργεί λογαριασμό on-chain σε αυτή τη διεύθυνση. Ο λογαριασμός πρέπει να δημιουργηθεί ρητά μέσω ξεχωριστής εντολής (συνήθως create_account μέσω CPI).

Τα Solana SDKs παρέχουν συναρτήσεις για την παραγωγή PDA. Κάθε συνάρτηση δέχεται:

  • Program ID: Η διεύθυνση του προγράμματος που χρησιμοποιείται για την παραγωγή του PDA. Αυτό το πρόγραμμα μπορεί να υπογράψει εκ μέρους του PDA.
  • Προαιρετικά seeds: Προκαθορισμένες εισόδους όπως strings, αριθμούς ή άλλες διευθύνσεις λογαριασμών.
SDKΣυνάρτηση
@solana/kit (TypeScript)getProgramDerivedAddress
@solana/web3.js (TypeScript)findProgramAddressSync
solana_sdk (Rust)find_program_address

Τα παρακάτω παραδείγματα παράγουν ένα PDA χρησιμοποιώντας τα Solana SDKs. Κάντε κλικ στο ▷ Run για να εκτελέσετε τον κώδικα.

Παραγωγή ενός PDA με ένα string seed

Το παρακάτω παράδειγμα παράγει ένα PDA χρησιμοποιώντας ένα program ID και ένα προαιρετικό string seed.

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 με ένα address seed

Το παρακάτω παράδειγμα παράγει ένα PDA χρησιμοποιώντας ένα program ID και ένα προαιρετικό address seed.

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 με πολλαπλά seeds

Το παρακάτω παράδειγμα παράγει ένα PDA χρησιμοποιώντας ένα program ID και πολλαπλά προαιρετικά seeds.

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.

Επανάληψη όλων των bumps

Τα παρακάτω παραδείγματα δείχνουν την παραγωγή PDA χρησιμοποιώντας όλα τα πιθανά bump seeds (255 έως 0), επεξηγώντας πώς το find_program_address επιστρέφει το κανονικό bump:

Το παράδειγμα Kit δεν περιλαμβάνεται επειδή η συνάρτηση createProgramDerivedAddress δεν είναι exported.

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

Σε αυτό το παράδειγμα, το bump 255 παράγει μια διεύθυνση επί της καμπύλης και αποτυγχάνει. Το πρώτο έγκυρο bump είναι το 254, καθιστώντας το κανονικό bump.

Is this page helpful?

Διαχειρίζεται από

© 2026 Ίδρυμα Solana.
Με επιφύλαξη παντός δικαιώματος.
Συνδεθείτε