Rust
Pinocchio Quantum Vault

Pinocchio Quantum Vault

9 Graduates

開啟保管庫

open 指令會建立一個程式衍生地址(PDA)保管庫,用於安全存放 lamports。此 PDA 使用經過雜湊的公鑰作為種子,確保只有原始密鑰的持有者才能稍後提取資金。

PDA 提供了一種安全存放資金的方法,因為:

  • 只有您的程式可以控制該帳戶(不存在私鑰)

  • 保管庫地址是從用戶的雜湊確定性衍生的

  • 提款只能通過您的程式邏輯進行

所需帳戶

該指令需要以下帳戶:

  • payer:支付保管庫建立費用(必須是簽署者且可變)

  • vault:正在初始化的 PDA(必須可變)

  • system_program:帳戶建立所需(必須可執行)

在程式碼中,這看起來像這樣:

rust
pub struct OpenVaultAccounts<'a> {
    pub payer: &'a AccountInfo,
    pub vault: &'a AccountInfo,
}

impl<'a> TryFrom<&'a [AccountInfo]> for OpenVaultAccounts<'a> {
    type Error = ProgramError;
 
    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
        let [payer, vault, _system_program] = accounts else {
            return Err(ProgramError::NotEnoughAccountKeys);
        };
 
        Ok(Self { payer, vault })
    }
}

帳戶驗證由 CreateAccount CPI 處理。如果帳戶不符合要求(簽署者、可變性、可執行性),指令將自動失敗。

指令數據

需要兩個數據:

  • hash:用戶 winternitz 密鑰對公鑰的 SHA-256 雜湊值([u8; 32])

  • bump:從客戶端傳遞的 PDA 衍生 bump(u8)

我們從客戶端傳遞 bump,而不是在鏈上衍生,以節省計算成本。

以下是程式碼中的示例:

rust
pub struct OpenVaultInstructionData {
    pub hash: [u8; 32],
    pub bump: [u8; 1],
}

impl<'a> TryFrom<&'a [u8]> for OpenVaultInstructionData {
    type Error = ProgramError;
 
    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
        if data.len() != core::mem::size_of::<OpenVaultInstructionData>() {
            return Err(ProgramError::InvalidInstructionData);
        }

        let hash = data[0..32].try_into().map_err(|_| ProgramError::InvalidInstructionData)?;
        let bump = data[32..33].try_into().map_err(|_| ProgramError::InvalidInstructionData)?;
 
        Ok(Self { hash, bump })
    }
}

再次強調,無效的參數會自我糾正:傳遞錯誤的雜湊會使保管庫無法使用,傳遞錯誤的 bump 會導致 PDA 衍生失敗。

指令邏輯

該指令會建立一個分配給我們程式的空 PDA。雖然該帳戶不包含任何數據,但程式擁有權確保提款只能通過我們控制的邏輯進行。

即使是空的帳戶,也必須符合免租金要求才能在 Solana 上持續存在。

以下是程式碼中的示例:

rust
pub struct OpenVault<'a> {
    pub accounts: OpenVaultAccounts<'a>,
    pub instruction_data: OpenVaultInstructionData,
}

impl<'a> TryFrom<(&'a [u8], &'a [AccountInfo])> for OpenVault<'a> {
    type Error = ProgramError;

    fn try_from((data, accounts): (&'a [u8], &'a [AccountInfo])) -> Result<Self, Self::Error> {
        let instruction_data = OpenVaultInstructionData::try_from(data)?;
        let accounts = OpenVaultAccounts::try_from(accounts)?;

        Ok(Self { accounts, instruction_data })
    }
}

impl<'a> OpenVault<'a> {
    pub const DISCRIMINATOR: &'a u8 = &0;
 
    pub fn process(&self) -> ProgramResult {
        let lamports = Rent::get()?.minimum_balance(0);
        let seeds = [Seed::from(&self.instruction_data.hash), Seed::from(&self.instruction_data.bump)];

        // Create our vault
        CreateAccount {
            from: self.accounts.payer,
            to: self.accounts.vault,
            lamports,
            space: 0,
            owner: &crate::ID,
        }
        .invoke_signed(&[Signer::from(&seeds)])
    }
}

此指令僅用於建立保管庫結構。存款則需透過簡單的 lamport 轉帳到保管庫帳戶來單獨處理。

Next Page分割保險庫
或跳過到挑戰
準備好參加挑戰了嗎?
Blueshift © 2025Commit: e573eab