Solana小技巧 - 解構PDA數據結構
前言
這篇教學文章的目的是, 以實例去解構用anchor寫Solana program的PDA data account儲存數據結構。
案例
我們有一個用anchor去寫的Solana program, 其中有個instruction RegisterKeyAccount
, 當中有個需要init的account user_key_record
。
#[derive(Accounts)]
#[instruction(data: RegisterKeyData)]
pub struct RegisterKeyAccounts<'info> {
......
#[account(init, payer = signer,
space = 8+32+8+1,
seeds = [b"userKeyRecord:".as_ref(), signer.key().as_ref(), b"nftOwnerProof:".as_ref(), &user_key_counter.next_id.to_le_bytes()], bump)]
pub user_key_record: Account<'info, UserKeyRecord>,
......
}
而user_key_record
的結構如下:
#[account]
pub struct UserKeyRecord {
key_address: Pubkey,
exp: u64,
state: u8,
}
在某次transaction中 ( https://solscan.io/tx/mUUEckVA7P2jALEMc4NAWep5P5X6QV4XJu2u3mUypGBQoXV5r7UDdCyuJG8ZHLeV4N9iRvGQiVa2S8eWQUhSB1N?cluster=devnet ) ,
我們invoke了這個instruction並創建了新的PDA去儲存UserKeyRecord
, address是HHZ6CJbtRCKjNZZWB8xWPZ8zG8iJjXfuANpmtQht8h1t
。
數據解構 Let's go!
現在我們去看看在chain上是寫了什麼數據。我們可以去https://borsh.m2.xyz 這個網上工具去查看PDA數據內容。
這次查看的是這個(記得要選devnet):
https://borsh.m2.xyz/address/HHZ6CJbtRCKjNZZWB8xWPZ8zG8iJjXfuANpmtQht8h1t
這裡其實本身只知道raw bytes 254871512aa1aa025697ee10730af95ad7d469bd37eec2d2e61e9b898d8e4075884daad4722823cc000000000000000001
, 但並不懂如何解構。我們是因為有smart contract source code知道如何解構, 才能填上下面的Field Name及type去解構的。
這裡看到的raw bytes是49 bytes。其實這也印證program中allocate的space = 8+32+8+1 = 49。兩者是一致的。
Anchor自帶8 bytes起首account discriminator
需要先說明一點:
但凡是用anchor所創建的PDA account, 其都會把account名做個Hash當成類似signature的東西寫在頭8 bytes。
Hash的方法可以參看anchor client library的code:
其中input中的name
, 在這例子中就是user_key_record
。記得要camelcase換算啊!
這個跟之前說PDA accound
"account:UserKeyRecord" -> 把它SHA256 Hash -> 取頭8bytes ...
就會得到254871512aa1aa02
。
還記得解構出來的raw bytes數據嗎?
254871512aa1aa025697ee10730af95ad7d469bd37eec2d2e61e9b898d8e4075884daad4722823cc000000000000000001
這裡要寫這8bytes account signature的作用, 我相信anchor是為了做一些安全檢查, 防止誤寫數據到錯誤的account裡。
解構餘下Struct結構
餘下的結構, 在解構的網頁UI上一一順序填上struct type就會得出正確值,分別是:
key_address (public key): 是6q2RvE6S1tac2dV7qL6TXFJNWsxYUpyTnT9nGhKLdugP
exp (u64) : 0
state (u8) : 1
這些值不是亂作的, 從transaction log上也能看到這真的是我們所pass的值。
伸延
如果你對instruction data的解構有興趣, 能看看我寫的另一篇文章:
[Example study] Solana program Instruction data encoding
又或是如果你覺得PDA這樣把數據封裝編碼成bytes的方式太過「盲盒」, 也可以看看我寫的這篇文章, 初探Anchor IDL是如何能幫我們解讀接口描述。