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是如何能幫我們解讀接口描述。

初探Solana程式接口描述 - Anchor IDL