Rust
Pinocchio Quantum Vault

Pinocchio Quantum Vault

9 Graduates

分拆保險庫

split 指令允許從抗量子保險庫中部分提取資金,通過將 lamports 分配到多個帳戶中實現。這對於 Winternitz 簽名方案至關重要,因為它只能安全地使用一次。

與傳統密碼學不同,Winternitz 簽名在使用一次後會變得脆弱。分拆指令允許您:

  • 在一次交易中將付款分配給多個接收者

  • 將剩餘資金轉移到具有新密鑰對的全新量子保險庫(通過將量子保險庫作為 refund 帳戶傳遞)

所需帳戶

該指令需要三個帳戶:

  • vault:包含存儲 lamports 的來源保險庫(必須是可變的)

  • split:指定金額的接收帳戶(必須是可變的)

  • refund:剩餘保險庫餘額的接收帳戶(必須是可變的)

refund 帳戶通常是一個具有新 Winternitz 密鑰對的全新量子保險庫,確保剩餘資金的持續安全。

以下是代碼示例:

rust
pub struct SplitVaultAccounts<'a> {
    pub vault: &'a AccountInfo,
    pub split: &'a AccountInfo,
    pub refund: &'a AccountInfo,
}

impl<'a> TryFrom<&'a [AccountInfo]> for SplitVaultAccounts<'a> {
    type Error = ProgramError;

    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
        let [vault, split, refund] = accounts else {
            return Err(ProgramError::NotEnoughAccountKeys);
        };

        Ok(Self { vault, split, refund })
    }
}

帳戶驗證由運行時處理。如果帳戶不符合要求(可變性),指令將自動失敗。

指令數據

需要三個數據:

  • signature:證明保險庫密鑰對所有權的 Winternitz 簽名

  • amount:轉移到分拆帳戶的 lamports 數量(8 字節,小端序)

  • bump:用於優化的 PDA 派生偏移量(1 字節)

以下是代碼示例:

rust
pub struct SplitVaultInstructionData {
    pub signature: WinternitzSignature,
    pub amount: [u8; 8],
    pub bump: [u8; 1],
}

impl<'a> TryFrom<&'a [u8]> for SplitVaultInstructionData {
    type Error = ProgramError;

    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
        if data.len() != core::mem::size_of::<SplitVaultInstructionData>() {
            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)?,
            amount: data[897..905].try_into().map_err(|_| ProgramError::InvalidInstructionData)?,
        })
    }
}

指令邏輯

驗證過程遵循以下步驟:

  1. 消息組裝:構建一個 72 字節的消息,其中包含:分拆金額、split 帳戶公鑰和 refund 帳戶公鑰

  2. 簽名驗證:使用 Winternitz 簽名來恢復原始的公鑰哈希,然後將其與保管庫的 PDA 衍生種子進行比較。

  3. PDA 驗證:快速等效性檢查確保恢復的哈希與保管庫的 PDA 匹配,證明簽署者擁有該保管庫。

  4. 資金分配 如果驗證成功:指定的金額將轉移到 split 帳戶,剩餘餘額將轉移到 refund 帳戶,並關閉 vault 帳戶。

由於程式擁有 vault 帳戶,您可以直接轉移 lamports,而無需調用跨程式調用 (CPI)。

以下是程式碼中的實現方式:

rust
pub struct SplitVault<'a> {
    pub accounts: SplitVaultAccounts<'a>,
    pub instruction_data: SplitVaultInstructionData,
}

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

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

        Ok(Self { accounts, instruction_data })
    }
}

impl<'a> SplitVault<'a> {
    pub const DISCRIMINATOR: &'a u8 = &1;

    pub fn process(&self) -> ProgramResult {
        // Assemble our Split message
        let mut message = [0u8; 72];
        message[0..8].clone_from_slice(&self.instruction_data.amount);
        message[8..40].clone_from_slice(self.accounts.split.key());
        message[40..].clone_from_slice(self.accounts.refund.key());

        // Recover our pubkey hash from the signature
        let hash = self.instruction_data.signature.recover_pubkey(&message).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, send split balance to Split account, refund remainder to Refund account
        *self.accounts.split.try_borrow_mut_lamports()? += u64::from_le_bytes(self.instruction_data.amount);
        *self.accounts.refund.try_borrow_mut_lamports()? += self.accounts.vault.lamports().saturating_sub(u64::from_le_bytes(self.instruction_data.amount));

        self.accounts.vault.close()
    }
}

重建訊息可防止簽名重播攻擊,這種攻擊中惡意行為者會在從內存池捕獲有效簽名時,替換不同的接收帳戶以進行 MEV 操作。

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