Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 25 additions & 24 deletions spl-v2/src/token_2022_extensions/cpi_guard.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use {
crate::token_2022::spl_token_2022::error::TokenError,
anchor_lang::{CpiContext, CpiHandle, CpiHandleMut, ToCpiAccounts},
solana_program_error::ProgramError,
};
Expand All @@ -14,14 +15,17 @@ pub struct CpiGuard<'a> {
note = "Token-2022 rejects CPI-initiated toggling of CPI Guard with CpiGuardSettingsLocked."
)]
pub fn cpi_guard_enable<'a>(_ctx: CpiContext<'a, CpiGuard<'a>>) -> Result<(), ProgramError> {
panic!("Token-2022 rejects CPI-initiated toggling of CPI Guard with CpiGuardSettingsLocked")
// Always invoked via CPI from a caller program, so Token-2022 would reject
// the toggle. Return the matching error instead of panicking through a
// `Result` API.
Err(TokenError::CpiGuardSettingsLocked.into())
}

#[deprecated(
note = "Token-2022 rejects CPI-initiated toggling of CPI Guard with CpiGuardSettingsLocked."
)]
pub fn cpi_guard_disable<'a>(_ctx: CpiContext<'a, CpiGuard<'a>>) -> Result<(), ProgramError> {
panic!("Token-2022 rejects CPI-initiated toggling of CPI Guard with CpiGuardSettingsLocked")
Err(TokenError::CpiGuardSettingsLocked.into())
}

#[cfg(test)]
Expand All @@ -44,45 +48,42 @@ mod tests {
buffer
}

fn sample_ctx<'a>(
program: &'a Address,
account_view: &'a mut pinocchio::account::AccountView,
owner_view: &'a pinocchio::account::AccountView,
) -> CpiContext<'a, CpiGuard<'a>> {
let accounts = CpiGuard {
account: CpiHandleMut::writable(account_view),
owner: CpiHandle::readonly(owner_view),
};
CpiContext::new(program, accounts)
}

#[test]
#[allow(deprecated)]
#[should_panic(
expected = "Token-2022 rejects CPI-initiated toggling of CPI Guard with CpiGuardSettingsLocked"
)]
fn cpi_guard_enable_panics_at_runtime() {
fn cpi_guard_enable_returns_settings_locked() {
let program = Address::new_from_array([7; 32]);
let account_buffer = account([1; 32], false, true);
let owner_buffer = account([2; 32], true, false);
let mut account_view = unsafe { account_buffer.view() };
let owner_view = unsafe { owner_buffer.view() };

let accounts = CpiGuard {
account: CpiHandleMut::writable(&mut account_view),
owner: CpiHandle::readonly(&owner_view),
};
let ctx = CpiContext::new(&program, accounts);

let _ = cpi_guard_enable(ctx);
let err = cpi_guard_enable(sample_ctx(&program, &mut account_view, &owner_view)).unwrap_err();
assert_eq!(err, TokenError::CpiGuardSettingsLocked.into());
}

#[test]
#[allow(deprecated)]
#[should_panic(
expected = "Token-2022 rejects CPI-initiated toggling of CPI Guard with CpiGuardSettingsLocked"
)]
fn cpi_guard_disable_panics_at_runtime() {
fn cpi_guard_disable_returns_settings_locked() {
let program = Address::new_from_array([7; 32]);
let account_buffer = account([1; 32], false, true);
let owner_buffer = account([2; 32], true, false);
let mut account_view = unsafe { account_buffer.view() };
let owner_view = unsafe { owner_buffer.view() };

let accounts = CpiGuard {
account: CpiHandleMut::writable(&mut account_view),
owner: CpiHandle::readonly(&owner_view),
};
let ctx = CpiContext::new(&program, accounts);

let _ = cpi_guard_disable(ctx);
let err =
cpi_guard_disable(sample_ctx(&program, &mut account_view, &owner_view)).unwrap_err();
assert_eq!(err, TokenError::CpiGuardSettingsLocked.into());
}
}
15 changes: 14 additions & 1 deletion tests-v2/tests/token_2022_extensions/cpi_guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,27 @@ fn cpi_guard_toggles_fail_under_cpi_without_mutating_state() {
Meta::new_readonly(owner.pubkey(), true),
Meta::new_readonly(token_2022_program_id(), false),
];
expect_program_failure(
expect_cpi_guard_settings_locked(
send(&mut svm, id, vec![discrim], metas, &payer, &[&owner]),
&format!("cpi-guard {label} should be rejected when invoked through CPI"),
);
assert_cpi_guard(&svm, account, false);
}
}

fn expect_cpi_guard_settings_locked<T, E: std::fmt::Display>(result: Result<T, E>, context: &str) {
let Err(error) = result else {
panic!("{context}");
};
let error = error.to_string();
// TokenError::CpiGuardSettingsLocked == Custom(41) / 0x29. The deprecated
// helpers return that error instead of panicking through a Result API.
assert!(
error.contains("Custom(41)") || error.contains("0x29") || error.contains("CpiGuardSettingsLocked"),
"{context}: expected CpiGuardSettingsLocked (Custom(41)), got:\n{error}"
);
}

fn assert_cpi_guard(svm: &litesvm::LiteSVM, account: Pubkey, expected_enabled: bool) {
let mut data = svm.get_account(&account).expect("token exists").data;
let state =
Expand Down
Loading