打开保险库
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 })
}
}指令数据
需要两部分数据:
hash:用户 Winternitz 密钥对公钥的 SHA-256 哈希值 ([u8; 32])bump:客户端传递的 PDA 派生 bump 值 (u8)
代码中如下所示:
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 })
}
}指令逻辑
该指令创建了一个分配给我们程序的空 PDA。虽然账户不包含数据,但程序所有权确保提款只能通过我们控制的逻辑进行。
以下是代码中的实现方式:
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)])
}
}