Tresor schließen
Die close Anweisung führt eine vollständige Abhebung aus quantenresistenten Tresoren durch und überträgt alle Lamports an ein einzelnes Empfängerkonto.
Im Gegensatz zur split Anweisung bietet dies einen einfacheren Mechanismus für vollständige Abhebungen, wenn keine Fondsverteilung erforderlich ist.
Erforderliche Konten
Die Anweisung erfordert drei Konten:
vault: Quell-Tresor mit gespeicherten Lamports (muss veränderbar sein)refund: Empfängerkonto für das Lamports-Guthaben des Tresors (muss veränderbar sein)
So sieht es im Code aus:
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 })
}
}
Anweisungsdaten
Zwei Datenstücke sind erforderlich:
signature: Winternitz-Signatur, die den Besitz des Tresor-Schlüsselpaars nachweistbump: PDA-Ableitungs-Bump zur Optimierung (1 Byte)
So sieht es im Code aus:
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)?,
})
}
}Anweisungslogik
Der Verifizierungsprozess folgt diesen Schritten:
Nachrichtenzusammenstellung: Eine 32-Byte-Nachricht wird erstellt, die den öffentlichen Schlüssel des
refundKontos enthältSignaturverifizierung: Die Winternitz-Signatur wird verwendet, um den ursprünglichen Hash des öffentlichen Schlüssels wiederherzustellen, der dann mit den PDA-Ableitungs-Seeds des Tresors verglichen wird.
PDA-Validierung: Eine schnelle Äquivalenzprüfung stellt sicher, dass der wiederhergestellte Hash mit dem PDA des Tresors übereinstimmt und beweist, dass der Unterzeichner der Besitzer des Tresors ist.
Fondsverteilung bei erfolgreicher Validierung: Das gesamte Guthaben wird auf das
refundKonto übertragen und dasvaultKonto wird geschlossen.
So sieht es im Code aus:
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()
}
}