Ouvrir un coffre-fort
L'instruction open crée un coffre-fort à adresse dérivée du programme (PDA) où les lamports seront déposés en toute sécurité. Ce PDA utilise une clé publique hachée comme graine, garantissant que seul le détenteur de la clé originale pourra retirer les fonds ultérieurement.
Les PDA offrent un moyen sécurisé de stocker des fonds car :
Seul votre programme peut contrôler le compte (aucune clé privée n'existe)
L'adresse du coffre-fort est dérivée de manière déterministe à partir du hash de l'utilisateur
Les retraits ne peuvent s'effectuer que via la logique de votre programme
Comptes requis
L'instruction nécessite ces comptes :
payer: Paie pour la création du coffre-fort (doit être signataire et mutable)vault: Le PDA en cours d'initialisation (doit être mutable)system_program: Requis pour la création du compte (doit être exécutable)
Dans le code, cela ressemble à :
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 })
}
}Données d'instruction
Deux éléments de données sont requis :
hash: Hash SHA-256 de la clé publique de la paire de clés winternitz de l'utilisateur ([u8; 32])bump: Bump de dérivation PDA transmis depuis le client (u8)
Voici à quoi cela ressemble dans le code :
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 })
}
}Logique d'instruction
L'instruction crée un PDA vide attribué à notre programme. Bien que le compte ne contienne aucune donnée, la propriété du programme garantit que les retraits ne peuvent se produire que par notre logique contrôlée.
Voici à quoi cela ressemble dans le code :
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)])
}
}