use crate::account_view::AccountView;
use crate::address::Address;
use crate::error::ProgramError;
use crate::raw_account::RuntimeAccount;
use crate::{ProgramResult, NOT_BORROWED};
use core::marker::PhantomData;
#[repr(C)]
#[derive(Debug, Clone)]
pub struct InstructionAccount<'a> {
pub address: &'a Address,
pub is_writable: bool,
pub is_signer: bool,
}
impl<'a> InstructionAccount<'a> {
#[inline(always)]
pub const fn new(address: &'a Address, is_writable: bool, is_signer: bool) -> Self {
Self {
address,
is_writable,
is_signer,
}
}
#[inline(always)]
pub const fn readonly(address: &'a Address) -> Self {
Self {
address,
is_writable: false,
is_signer: false,
}
}
#[inline(always)]
pub const fn writable(address: &'a Address) -> Self {
Self {
address,
is_writable: true,
is_signer: false,
}
}
#[inline(always)]
pub const fn readonly_signer(address: &'a Address) -> Self {
Self {
address,
is_writable: false,
is_signer: true,
}
}
#[inline(always)]
pub const fn writable_signer(address: &'a Address) -> Self {
Self {
address,
is_writable: true,
is_signer: true,
}
}
}
impl<'a> From<&'a AccountView<'a>> for InstructionAccount<'a> {
#[inline(always)]
fn from(view: &'a AccountView<'a>) -> Self {
Self {
address: view.address(),
is_writable: view.is_writable(),
is_signer: view.is_signer(),
}
}
}
#[derive(Debug, Clone)]
pub struct InstructionView<'a, 'b, 'c, 'd>
where
'a: 'b,
{
pub program_id: &'c Address,
pub data: &'d [u8],
pub accounts: &'b [InstructionAccount<'a>],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct CpiAccount<'a> {
address: *const Address,
lamports: *const u64,
data_len: u64,
data: *const u8,
owner: *const Address,
rent_epoch: u64,
is_signer: bool,
is_writable: bool,
executable: bool,
_account_view: PhantomData<&'a AccountView<'a>>,
}
impl<'a> From<&'a AccountView<'a>> for CpiAccount<'a> {
#[inline(always)]
fn from(view: &'a AccountView<'a>) -> Self {
let raw = view.account_ptr();
let header = unsafe { core::ptr::read_unaligned(raw as *const u32) };
Self {
address: unsafe { &(*raw).address as *const Address },
lamports: unsafe { &(*raw).lamports as *const u64 },
data_len: view.data_len() as u64,
data: view.data_ptr_unchecked(),
owner: unsafe { &(*raw).owner as *const Address },
rent_epoch: 0,
is_signer: header & 0x0000_FF00 != 0,
is_writable: header & 0x00FF_0000 != 0,
executable: header & 0xFF00_0000 != 0,
_account_view: PhantomData,
}
}
}
impl<'a> CpiAccount<'a> {
#[inline(always)]
pub(crate) fn instruction_account(
&self,
is_writable: bool,
is_signer: bool,
) -> InstructionAccount<'a> {
let address = unsafe { &*self.address };
InstructionAccount::new(address, is_writable, is_signer)
}
}
#[inline(always)]
pub(crate) fn preflight_cpi_accounts(
accounts: &[CpiAccount<'_>],
writable_mask: usize,
signer_mask: usize,
has_pda_signers: bool,
) -> ProgramResult {
let mut index = 0usize;
while index < accounts.len() {
let account = &accounts[index];
if signer_mask & (1usize << index) != 0 && !account.is_signer && !has_pda_signers {
return Err(ProgramError::MissingRequiredSignature);
}
let is_writable_meta = writable_mask & (1usize << index) != 0;
if is_writable_meta && !account.is_writable {
return Err(ProgramError::Immutable);
}
let raw = unsafe { account.data.sub(RuntimeAccount::SIZE) as *const RuntimeAccount };
let borrow_state = unsafe { (*raw).borrow_state };
let compatible = if is_writable_meta {
borrow_state == NOT_BORROWED
} else {
borrow_state != 0
};
if !compatible {
return Err(ProgramError::AccountBorrowFailed);
}
index += 1;
}
Ok(())
}
const _: () = {
assert!(core::mem::size_of::<InstructionAccount<'static>>() == 16);
assert!(core::mem::align_of::<InstructionAccount<'static>>() == 8);
assert!(core::mem::offset_of!(InstructionAccount<'static>, address) == 0);
assert!(core::mem::offset_of!(InstructionAccount<'static>, is_writable) == 8);
assert!(core::mem::offset_of!(InstructionAccount<'static>, is_signer) == 9);
assert!(core::mem::size_of::<CpiAccount<'static>>() == 56);
assert!(core::mem::align_of::<CpiAccount<'static>>() == 8);
assert!(core::mem::offset_of!(CpiAccount<'static>, address) == 0);
assert!(core::mem::offset_of!(CpiAccount<'static>, lamports) == 8);
assert!(core::mem::offset_of!(CpiAccount<'static>, data_len) == 16);
assert!(core::mem::offset_of!(CpiAccount<'static>, data) == 24);
assert!(core::mem::offset_of!(CpiAccount<'static>, owner) == 32);
assert!(core::mem::offset_of!(CpiAccount<'static>, rent_epoch) == 40);
assert!(core::mem::offset_of!(CpiAccount<'static>, is_signer) == 48);
assert!(core::mem::offset_of!(CpiAccount<'static>, is_writable) == 49);
assert!(core::mem::offset_of!(CpiAccount<'static>, executable) == 50);
};
#[repr(C)]
#[derive(Debug, Clone)]
pub struct Seed<'a> {
pub(crate) seed: *const u8,
pub(crate) len: u64,
_bytes: PhantomData<&'a [u8]>,
}
impl<'a> From<&'a [u8]> for Seed<'a> {
#[inline(always)]
fn from(bytes: &'a [u8]) -> Self {
Self {
seed: bytes.as_ptr(),
len: bytes.len() as u64,
_bytes: PhantomData,
}
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for Seed<'a> {
#[inline(always)]
fn from(bytes: &'a [u8; N]) -> Self {
Self {
seed: bytes.as_ptr(),
len: N as u64,
_bytes: PhantomData,
}
}
}
impl core::ops::Deref for Seed<'_> {
type Target = [u8];
#[inline(always)]
fn deref(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.seed, self.len as usize) }
}
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct Signer<'a, 'b> {
pub(crate) seeds: *const Seed<'a>,
pub(crate) len: u64,
_seeds: PhantomData<&'b [Seed<'a>]>,
}
impl<'a, 'b> From<&'b [Seed<'a>]> for Signer<'a, 'b> {
#[inline(always)]
fn from(seeds: &'b [Seed<'a>]) -> Self {
Self {
seeds: seeds.as_ptr(),
len: seeds.len() as u64,
_seeds: PhantomData,
}
}
}
impl<'a, 'b, const N: usize> From<&'b [Seed<'a>; N]> for Signer<'a, 'b> {
#[inline(always)]
fn from(seeds: &'b [Seed<'a>; N]) -> Self {
Self {
seeds: seeds.as_ptr(),
len: N as u64,
_seeds: PhantomData,
}
}
}
#[macro_export]
macro_rules! seeds {
( $($seed:expr),* $(,)? ) => {
[$(
$crate::instruction::Seed::from($seed),
)*]
};
}