Trong Solana, khi viết program, giả sử chúng ta muốn lưu trữ một vài thông tin của user như tên, tuổi, sdt … chúng ta sẽ nghĩ ngay là sẽ phải tạo account.
Khi mới tiếp xúc solana, mình nghĩ flow tạo account sẽ như thế này:
gửi transaction cho SystemProgram => SystemProgram tạo account
Nhưng như vậy nếu sau đó mình muốn lấy thông tin của account vừa được tạo thì phải làm như thế nào? mình đâu có biết address của nó?
Thực ra lúc đó mình quên mất một step, đó là mình phải tạo address cho account trước (chính là public key của account);
Tạo cặp keypair (pk+pv) => send to System Program => System Program create new account with this keypair
Điểm mấu chốt ở đây là
- Chúng hoàn toàn có thể biết được trước address của một account trước khi tạo ra nó.
- Nếu chỉ generate ra keypair bằng hàm `const kp = Keypair.generate()` thì sẽ chưa có account nào được tạo ra cả.
Ok, chúng ta đã có thể tạo được account (gọi là profile account nha) để lưu trữ thông tin của user. Vậy nếu lần sau user quay lại dapp và muốn xem các thông tin đó thì phải làm như thế nào?
Về lý thuyết, chúng ta phải lưu trữ cái địa chỉ (address or public key) của profile account ở đâu đó (backend ? ) để khi user họ cần truy vấn thông tin trong profile account, chúng ta sẽ dùng cái address đó để truy vấn. Tuy vậy lưu trữ vô backend sẽ không phải là cách hay (chúng ta đang build dapp mà), nên cần phải có 1 giải pháp khác. Đó chính là sử dụng Program derived address hay con gọi là PDA.
PDA là một cái địa chỉ, được “trích xuất” (không biết dùng từ này có đúng không) ra tự một cái seeds (string, number, other account addresses) do lập trình viên tự define + progam_id của program mà mình đang viết. Với cùng một seeds, cùng một program_id thì sẽ cho ra cùng một PDA (deterministic)
pda = findProgramAddressSync(seeds, program_id)
Áp dụng vô bài toán ở trên, thay vì generate ra một keypair ngẫu nhiên để làm địa chỉ cho cái profile account, chúng ta sẽ sử dụng PDA để làm địa chỉ cho account đó. Ví dụ chúng ta define cái seeds là “profile”, khi đó:
pda = findProgramAddressSync([“profile“], my_program_id)
Ở đây cái seeds và cái program_id đã được chúng ta define sẵn, nên cái PDA sẽ được dễ dàng lấy ra nhờ hàm findProgramAddressSync mà không cần phải lưu trữ ở đâu cả.
So far so good.
Tuy vậy cách lựa chọn seeds ở trên vẫn chưa hợp lý !!!!
Chúng ta dùng string `profile` để làm seeds, mà theo tính chất của PDA, với cùng `seeds + program_id` thì nó sẽ cho ra cùng một PDA.
Vì vậy khi user A vô hệ thống, họ tạo profile => chúng ta dùng seeds + program_id => PDA để tạo profile account cho họ.
Tiếp user B vô hệ thống, chúng ta cũng làm như trên, tuy nhiên do seeds và program_id giống nhau, nên nó sẽ sinh ra cùng một PDA (deterministic, again) => mọi user đều sẽ dùng chung một profile account.
Để giải quyết vấn đề này thì chúng ta cần tìm một cái seeds sao cho mỗi user sẽ có một seeds khác nhau. Dễ dàng nhất đó là chúng ta sẽ sử dụng địa chỉ ví của user, vì nó unique và chúng ta cũng không cần lưu trữ nó ở đâu cả (khi user connect ví là lấy ra được địa chỉ ví). Khi đó PDA sẽ ntn:
pda = findProgramAddressSync([“profile“, user_address], my_program_id)
Chuỗi “profile“ có thể không cần thiết, vì user_address đã là unique rồi nên không sợ bị trùng nhau giữa các user. Tuy vậy đó chúng ta vẫn nên để thêm chuỗi “profile“ vào vì giả sử sau này chúng ta không chỉ muốn tạo profile account mà còn balance account, nft account… khi đó các seeds sẽ có dạng như thế này: [“profile“, user_address], [“balance“, user_address], [“nft“, user_address] …
Hi vọng quick note này có thể giúp các bạn hiễu rõ hơn về account address cũng như là PDA.
Hoặc có thể là không ….