Закрити сховище
Інструкція close виконує повне виведення коштів із квантово-стійких сховищ, переказуючи всі лампорти на один обліковий запис отримувача.
На відміну від інструкції split, це забезпечує простіший механізм повного виведення коштів, коли розподіл коштів не потрібен.
Required Accounts
Інструкція потребує трьох облікових записів:
vault: Вихідне сховище, що містить збережені лампорти (має бути змінюваним)refund: Обліковий запис отримувача для балансу лампортів сховища (має бути змінюваним)
Ось як це виглядає в коді:
pub struct CloseVaultAccounts<'a> {
pub vault: &'a AccountInfo,
pub refund: &'a AccountInfo,
}
impl<'a> TryFrom<&'a [AccountInfo]> for CloseVaultAccounts<'a> {
type Error = ProgramError;
fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
let [vault, refund] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
Ok(Self { vault, refund })
}
}
Instruction Data
Потрібні два елементи даних:
signature: Підпис Вінтерніца, що підтверджує володіння ключовою парою сховищаbump: Параметр відхилення для виведення PDA для оптимізації (1 байт)
Ось як це виглядає в коді:
pub struct CloseVaultInstructionData {
pub signature: WinternitzSignature,
pub bump: [u8; 1],
}
impl<'a> TryFrom<&'a [u8]> for CloseVaultInstructionData {
type Error = ProgramError;
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
if data.len() != core::mem::size_of::<CloseVaultInstructionData>() {
return Err(ProgramError::InvalidInstructionData);
}
let mut signature_array = MaybeUninit::<[u8; 896]>::uninit();
unsafe {
core::ptr::copy_nonoverlapping(data[0..896].as_ptr(), signature_array.as_mut_ptr() as *mut u8, 896);
}
Ok(Self {
signature: WinternitzSignature::from(unsafe { signature_array.assume_init() }),
bump: data[896..897].try_into().map_err(|_| ProgramError::InvalidInstructionData)?,
})
}
}Instruction Logic
Процес перевірки включає такі кроки:
Складання повідомлення: Створюється 32-байтове повідомлення, що містить публічний ключ облікового запису
refundПеревірка підпису: Підпис Вінтерніца використовується для відновлення хешу оригінального публічного ключа, який потім порівнюється з насінням виведення PDA сховища.
Валідація PDA: Швидка перевірка еквівалентності гарантує, що відновлений хеш відповідає PDA сховища, доводячи, що підписувач володіє сховищем.
Розподіл коштів: Якщо валідація успішна, весь баланс переказується на обліковий запис
refund, а обліковий записvaultзакривається.
Ось як це виглядає в коді:
pub struct CloseVault<'a> {
pub accounts: CloseVaultAccounts<'a>,
pub instruction_data: CloseVaultInstructionData,
}
impl<'a> TryFrom<(&'a [u8], &'a [AccountInfo])> for CloseVault<'a> {
type Error = ProgramError;
fn try_from((data, accounts): (&'a [u8], &'a [AccountInfo])) -> Result<Self, Self::Error> {
let instruction_data = CloseVaultInstructionData::try_from(data)?;
let accounts = CloseVaultAccounts::try_from(accounts)?;
Ok(Self { accounts, instruction_data })
}
}
impl<'a> CloseVault<'a> {
pub const DISCRIMINATOR: &'a u8 = &2;
pub fn process(&self) -> ProgramResult {
// Recover our pubkey hash from the signature
let hash = self.instruction_data.signature.recover_pubkey(self.accounts.refund.key()).merklize();
// Fast PDA equivalence check
if solana_nostd_sha256::hashv(&[
hash.as_ref(),
self.instruction_data.bump.as_ref(),
crate::ID.as_ref(),
b"ProgramDerivedAddress",
])
.ne(self.accounts.vault.key())
{
return Err(ProgramError::MissingRequiredSignature);
}
// Close Vault and refund balance to Refund account
*self.accounts.refund.try_borrow_mut_lamports()? += self.accounts.vault.lamports();
self.accounts.vault.close()
}
}