use crate::address::Address;
use crate::error::ProgramError;
use crate::AccountView;
#[inline]
pub fn create_program_address(
seeds: &[&[u8]],
program_id: &Address,
) -> Result<Address, ProgramError> {
crate::compat::create_program_address(seeds, program_id)
}
#[inline]
pub fn find_program_address(
seeds: &[&[u8]],
program_id: &Address,
) -> (Address, u8) {
#[cfg(target_os = "solana")]
{
crate::compat::find_program_address(seeds, program_id)
}
#[cfg(not(target_os = "solana"))]
{
let _ = (seeds, program_id);
(Address::default(), 0)
}
}
#[inline(always)]
pub fn derive(seeds: &[&[u8]], program_id: &Address) -> (Address, u8) {
find_program_address(seeds, program_id)
}
#[inline]
pub fn verify_pda(
account: &AccountView,
seeds: &[&[u8]],
program_id: &Address,
) -> Result<(), ProgramError> {
#[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
{
hopper_native::pda::verify_pda(
account.as_backend(),
seeds,
crate::compat::as_backend_address(program_id),
)
.map_err(ProgramError::from)
}
#[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
{
let expected = create_program_address(seeds, program_id)?;
if crate::address::address_eq(account.address(), &expected) {
Ok(())
} else {
Err(ProgramError::InvalidSeeds)
}
}
}
#[inline]
pub fn verify_pda_with_bump(
account: &AccountView,
seeds: &[&[u8]],
bump: u8,
program_id: &Address,
) -> Result<(), ProgramError> {
#[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
{
hopper_native::pda::verify_pda_with_bump(
account.as_backend(),
seeds,
bump,
crate::compat::as_backend_address(program_id),
)
.map_err(ProgramError::from)
}
#[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
{
let mut full_seeds: [&[u8]; 17] = [&[]; 17];
let num = seeds.len().min(15);
let mut i = 0;
while i < num {
full_seeds[i] = seeds[i];
i += 1;
}
let bump_bytes = [bump];
full_seeds[num] = &bump_bytes;
let expected = create_program_address(&full_seeds[..num + 1], program_id)?;
if crate::address::address_eq(account.address(), &expected) {
Ok(())
} else {
Err(ProgramError::InvalidSeeds)
}
}
}
#[inline]
pub fn find_and_verify_pda(
account: &AccountView,
seeds: &[&[u8]],
program_id: &Address,
) -> Result<u8, ProgramError> {
#[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
{
let expected_addr = account.as_backend().address();
let backend_expected =
unsafe { &*(expected_addr as *const hopper_native::address::Address) };
verify_pda_sha256_loop(backend_expected, seeds, program_id)
}
#[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
{
let (expected, bump) = find_program_address(seeds, program_id);
if crate::address::address_eq(account.address(), &expected) {
Ok(bump)
} else {
Err(ProgramError::InvalidSeeds)
}
}
}
#[inline]
pub fn verify_pda_strict(
expected: &Address,
seeds: &[&[u8]],
program_id: &Address,
) -> Result<(), ProgramError> {
#[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
{
let backend_expected =
unsafe { &*(expected as *const Address as *const hopper_native::address::Address) };
verify_pda_sha256_loop(backend_expected, seeds, program_id).map(|_| ())
}
#[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
{
let (derived, _) = find_program_address(seeds, program_id);
if crate::address::address_eq(&derived, expected) {
Ok(())
} else {
Err(ProgramError::InvalidSeeds)
}
}
}
#[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
#[inline(always)]
fn verify_pda_sha256_loop(
expected: &hopper_native::address::Address,
seeds: &[&[u8]],
program_id: &Address,
) -> Result<u8, ProgramError> {
let backend_pid = crate::compat::as_backend_address(program_id);
let n = seeds.len().min(16);
let mut slices = core::mem::MaybeUninit::<[&[u8]; 19]>::uninit();
let sptr = slices.as_mut_ptr() as *mut &[u8];
let mut i = 0;
while i < n {
unsafe { sptr.add(i).write(seeds[i]) };
i += 1;
}
let mut bump_byte = [255u8];
unsafe {
sptr.add(n).write(&bump_byte as &[u8]);
sptr.add(n + 1).write(backend_pid.as_ref());
sptr.add(n + 2).write(hopper_native::address::PDA_MARKER.as_slice());
}
let input = unsafe { core::slice::from_raw_parts(sptr as *const &[u8], n + 3) };
let mut bump: u16 = 256;
while bump > 0 {
bump -= 1;
bump_byte[0] = bump as u8;
let mut hash = core::mem::MaybeUninit::<[u8; 32]>::uninit();
unsafe {
hopper_native::syscalls::sol_sha256(
input as *const _ as *const u8,
input.len() as u64,
hash.as_mut_ptr() as *mut u8,
);
}
let derived =
unsafe { &*(hash.as_ptr() as *const hopper_native::address::Address) };
if hopper_native::address::address_eq(derived, expected) {
return Ok(bump as u8);
}
}
Err(ProgramError::InvalidSeeds)
}
#[inline]
pub fn verify_pda_from_stored_bump(
account: &AccountView,
seeds: &[&[u8]],
bump_offset: usize,
program_id: &Address,
) -> Result<(), ProgramError> {
#[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
{
hopper_native::verify_pda_from_stored_bump(
account.as_backend(),
seeds,
bump_offset,
crate::compat::as_backend_address(program_id),
)
.map_err(ProgramError::from)
}
#[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
{
let data = account.try_borrow()?;
if bump_offset >= data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
let bump = data[bump_offset];
let mut full_seeds: [&[u8]; 17] = [&[]; 17];
let num = seeds.len().min(15);
let mut i = 0;
while i < num {
full_seeds[i] = seeds[i];
i += 1;
}
let bump_bytes = [bump];
full_seeds[num] = &bump_bytes;
let expected = create_program_address(&full_seeds[..num + 1], program_id)?;
if crate::address::address_eq(account.address(), &expected) {
Ok(())
} else {
Err(ProgramError::InvalidSeeds)
}
}
}