use solana_program::{clock::Epoch, instruction::AccountMeta, pubkey::Pubkey};
use super::{deser::DeserializeRef, utils::split_at_checked, MessageDataRef};
impl MessageDataRef<'_> {
pub unsafe fn load(msg_data_ptr: *const u8, msg_data_len: usize) -> Self {
let mut slice = core::slice::from_raw_parts(msg_data_ptr, msg_data_len);
MessageDataRef::deserialize_ref(&mut slice).unwrap()
}
}
#[repr(C)]
pub struct InstructionInfo {
pub compute_units: u32,
pub heap_frame: u32,
pub accounts_len: u32,
pub accounts: [AccountMeta; 57],
}
impl InstructionInfo {
pub fn push_account(&mut self, account_meta: AccountMeta) -> Result<(), AccountMeta> {
if self.accounts_len as usize >= self.accounts.len() {
return Err(account_meta);
}
self.accounts[self.accounts_len as usize] = account_meta;
self.accounts_len += 1;
Ok(())
}
}
#[link(wasm_import_module = "solana_transmitter")]
extern "C" {
fn get_multiple_accounts(keys_ptr: *const Pubkey, keys_len: u32, out_ptr: *mut u8) -> i32;
}
pub struct HostCallContext {
params_ptr: *mut u8,
result_ptr: *mut u8,
}
impl HostCallContext {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn new(msg_data_ptr: *const u8, msg_data_len: usize) -> Self {
const MAX_RESULT_LEN: usize = core::mem::size_of::<InstructionInfo>();
const MAX_PARAMS_LEN: usize = 4 + 32 * 100;
let offset = msg_data_len + MAX_RESULT_LEN;
let offset = offset as isize as usize;
unsafe {
let params_ptr = msg_data_ptr.add(offset).cast_mut();
Self {
params_ptr,
result_ptr: params_ptr.add(MAX_PARAMS_LEN),
}
}
}
pub unsafe fn get_multiple_accounts<'a>(
&mut self,
keys: &[Pubkey],
) -> heapless::Vec<Option<AccountRef<'a>>, 100>
where
Self: 'a,
{
let params_slice = core::slice::from_raw_parts_mut(self.params_ptr.cast(), keys.len());
params_slice.copy_from_slice(keys);
assert!(
get_multiple_accounts(self.params_ptr.cast(), keys.len() as u32, self.result_ptr) == 0
);
let mut result_slice = core::slice::from_raw_parts(self.result_ptr, 1 << (isize::BITS - 1));
let mut res = heapless::Vec::new();
for _ in 0..keys.len() {
let option_tag = result_slice[0];
result_slice = &result_slice[1..];
if option_tag == 0 {
res.push(None).unwrap();
} else {
res.push(Some(AccountRef::deserialize_ref(&mut result_slice).unwrap())).unwrap();
}
}
self.result_ptr = result_slice.as_ptr().cast_mut();
res
}
}
#[derive(Debug)]
pub struct AccountRef<'a> {
pub lamports: u64,
pub data: &'a [u8],
pub owner: Pubkey,
pub executable: bool,
pub rent_epoch: Epoch,
}
impl<'a> DeserializeRef<'a> for AccountRef<'a> {
fn deserialize_ref(slice: &mut &'a [u8]) -> Option<Self> {
let mut read_data = |count| {
let (src, rest) = split_at_checked(slice, count)?;
*slice = rest;
Some(src)
};
let lamports = u64::from_le_bytes(read_data(8)?.try_into().unwrap());
let data_len = u32::from_le_bytes(read_data(4)?.try_into().unwrap()) as usize;
let data = read_data(data_len)?;
let owner = Pubkey::new_from_array(read_data(32)?.try_into().unwrap());
let executable = read_data(1)?[0] != 0;
let rent_epoch = u64::from_le_bytes(read_data(8)?.try_into().unwrap());
Some(Self {
lamports,
data,
owner,
executable,
rent_epoch,
})
}
}