Dẫn xuất PDA

Tóm tắt

PDA được dẫn xuất bằng cách băm seed + program ID + bump qua SHA-256 cho đến khi kết quả nằm ngoài đường cong Ed25519. Bump chuẩn là giá trị đầu tiên tạo ra địa chỉ ngoài đường cong. Tối đa 16 seed, tối đa 32 byte mỗi seed.

Kiến thức nền tảng

Các giá trị Keypair trên Solana là các điểm trên đường cong Ed25519. Một keypair bao gồm public key (dùng làm địa chỉ tài khoản) và secret key (dùng để tạo chữ ký). Bất kỳ ai có secret key đều có thể ký giao dịch cho địa chỉ đó.

Hai tài khoản với địa chỉ trên đường congHai tài khoản với địa chỉ trên đường cong

PDA được dẫn xuất có chủ đích để rơi ngoài đường cong Ed25519. Vì nó không phải là điểm hợp lệ trên đường cong, không tồn tại secret key nào, và không bên ngoài nào có thể tạo chữ ký. Chỉ program dẫn xuất mới có thể ủy quyền các thao tác trên PDA thông qua invoke_signed.

Địa chỉ ngoài đường congĐịa chỉ ngoài đường cong

Tài khoản PDA so với tài khoản keypair

Thuộc tínhTài khoản keypairTài khoản PDA
Loại địa chỉTrên đường cong Ed25519Ngoài đường cong Ed25519
Có private keyKhông
Có thể ký giao dịchCó (với private key)Không
Có thể ký trong CPIKhông (trừ khi chữ ký được bao gồm trong giao dịch)Có (qua invoke_signed)
Dẫn xuấtTạo keypair Ed25519Xác định từ seed + program ID
Sử dụng điển hìnhVí người dùng, Program IDTài khoản dữ liệu thuộc sở hữu program

Seed tùy chọn

Các seed tùy chọn là các chuỗi byte do người dùng định nghĩa, đóng vai trò là đầu vào cho quá trình tạo PDA. Chúng tạo ra các địa chỉ xác định duy nhất trong phạm vi của một chương trình. Ví dụ, sử dụng ["user", user_pubkey] làm seed sẽ tạo ra một PDA khác nhau cho mỗi người dùng.

Các seed phải tuân theo những ràng buộc sau:

  • Tối đa 16 seed cho mỗi lần tạo (MAX_SEEDS)
  • Tối đa 32 byte cho mỗi seed (MAX_SEED_LEN)

Bump seed

Bump seed là một byte đơn (0-255) được thêm vào các seed tùy chọn trong quá trình tạo. find_program_address tìm kiếm từ 255 xuống 0, gọi create_program_address với mỗi giá trị cho đến khi kết quả nằm ngoài đường cong Ed25519. Giá trị đầu tiên thành công chính là canonical bump.

Các chương trình nên luôn sử dụng canonical bump để đảm bảo ánh xạ duy nhất, xác định từ seed đến địa chỉ.

Luôn sử dụng canonical bump khi tạo PDA. Sử dụng bump không chuẩn sẽ tạo ra địa chỉ hợp lệ thứ hai cho cùng một bộ seed, có thể dẫn đến lỗ hổng bảo mật khi kẻ tấn công thay thế một tài khoản khác với tài khoản mong đợi.

PDA DerivationPDA Derivation

Thuật toán tạo

Quá trình tạo PDA được triển khai trong hàm create_program_address của SDK. Thuật toán hoạt động như sau:

  1. Xác thực rằng số lượng seed không vượt quá MAX_SEEDS (16) và không có seed riêng lẻ nào vượt quá MAX_SEED_LEN (32 byte). Nếu một trong hai kiểm tra thất bại, trả về PubkeyError::MaxSeedLengthExceeded.
  2. Hash SHA-256 tất cả các seed, program ID và chuỗi "ProgramDerivedAddress" với nhau để tạo ra kết quả 32 byte.
  3. Kiểm tra xem kết quả có phải là điểm hợp lệ trên đường cong Ed25519 hay không.
  4. Nếu kết quả NẰM trên đường cong, trả về PubkeyError::InvalidSeeds (địa chỉ sẽ có khóa riêng tương ứng, điều này vi phạm thuộc tính bảo mật của PDA).
  5. Nếu kết quả KHÔNG NẰM trên đường cong, trả về nó làm PDA.

Chi phí compute unit

Syscall trên chuỗi cho create_program_address tính phí 1.500 CU mỗi lần gọi.

Syscall try_find_program_address tính phí 1.500 CU khi bắt đầu (trước vòng lặp), sau đó thêm 1.500 CU cho mỗi lần thử bump thất bại trong vòng lặp.

Các mẫu seed phổ biến

Seed phụ thuộc vào ứng dụng cụ thể. Các mẫu phổ biến bao gồm:

MẫuSeedTrường hợp sử dụng
Singleton toàn cục["global"]Tài khoản cấu hình duy nhất cho toàn bộ chương trình
Tài khoản theo người dùng["user", user_pubkey]Một tài khoản cho mỗi người dùng trên mỗi chương trình
Theo người dùng và thực thể["vault", user_pubkey, mint_pubkey]Kho token, theo người dùng và token
Bộ đếm / tuần tự["order", user_pubkey, &order_id.to_le_bytes()]Bản ghi tuần tự theo người dùng

Các seed được nối lại trước khi băm, do đó ["ab", "cd"]["abcd"] tạo ra cùng một PDA. Sử dụng seed có độ dài cố định hoặc ký tự phân cách để tránh xung đột. Ví dụ, ["ab", "-", "cd"] là rõ ràng.

Ví dụ: Tạo PDA

Việc tạo PDA chỉ tính toán địa chỉ. Nó không tạo tài khoản trên chuỗi tại địa chỉ đó. Tài khoản phải được tạo rõ ràng thông qua một instruction riêng biệt (thường là create_account thông qua CPI).

Các SDK của Solana cung cấp các hàm để tạo PDA. Mỗi hàm nhận:

  • Program ID: Địa chỉ của chương trình được sử dụng để tạo PDA. Chương trình này có thể ký thay mặt cho PDA.
  • Seed tùy chọn: Các đầu vào được xác định trước như chuỗi, số hoặc địa chỉ tài khoản khác.
SDKHàm
@solana/kit (TypeScript)getProgramDerivedAddress
@solana/web3.js (TypeScript)findProgramAddressSync
solana_sdk (Rust)find_program_address

Các ví dụ dưới đây tạo PDA bằng cách sử dụng các SDK của Solana. Nhấp ▷ Run để thực thi mã.

Tạo PDA với seed chuỗi

Ví dụ dưới đây tạo một PDA sử dụng program ID và một seed chuỗi tùy chọn.

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.

Tạo PDA với seed địa chỉ

Ví dụ dưới đây tạo một PDA sử dụng program ID và một seed địa chỉ tùy chọn.

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.

Tạo PDA với nhiều seed

Ví dụ dưới đây tạo một PDA sử dụng program ID và nhiều seed tùy chọn.

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.

Lặp qua tất cả các bump

Các ví dụ sau đây cho thấy việc tạo PDA sử dụng tất cả các bump seed có thể (từ 255 đến 0), minh họa cách find_program_address trả về canonical bump:

Ví dụ Kit không được bao gồm vì hàm createProgramDerivedAddress không được export.

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

Trong ví dụ này, bump 255 tạo ra một địa chỉ trên đường cong và thất bại. Bump hợp lệ đầu tiên là 254, khiến nó trở thành bump chuẩn.

Is this page helpful?

Quản lý bởi

© 2026 Solana Foundation.
Đã đăng ký bản quyền.
Kết nối