use {
crate::{
IndexOfAccount,
instruction_accounts::{BorrowedInstructionAccount, InstructionAccount},
transaction::TransactionContext,
vm_addresses::{
GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS, GUEST_INSTRUCTION_DATA_BASE_ADDRESS,
GUEST_REGION_SIZE,
},
vm_slice::VmSlice,
},
solana_account::ReadableAccount,
solana_instruction::error::InstructionError,
solana_pubkey::Pubkey,
std::collections::HashSet,
};
#[repr(C)]
#[derive(Debug)]
pub struct InstructionFrame {
pub reserved: u16,
pub program_account_index_in_tx: u16,
pub nesting_level: u16,
pub index_of_caller_instruction: u16,
pub instruction_accounts: VmSlice<InstructionAccount>,
pub instruction_data: VmSlice<u8>,
}
impl Default for InstructionFrame {
fn default() -> Self {
InstructionFrame {
nesting_level: 0,
program_account_index_in_tx: 0,
index_of_caller_instruction: u16::MAX,
instruction_accounts: VmSlice::new(0, 0),
instruction_data: VmSlice::new(0, 0),
reserved: 0,
}
}
}
#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
impl InstructionFrame {
pub fn configure_vm_slices(
&mut self,
instruction_index: u64,
instruction_accounts_len: usize,
instruction_data_len: u64,
) {
let common_offset = GUEST_REGION_SIZE.saturating_mul(instruction_index);
self.instruction_data = VmSlice::new(
GUEST_INSTRUCTION_DATA_BASE_ADDRESS.saturating_add(common_offset),
instruction_data_len,
);
self.instruction_accounts = VmSlice::new(
GUEST_INSTRUCTION_ACCOUNT_BASE_ADDRESS.saturating_add(common_offset),
instruction_accounts_len as u64,
);
}
}
#[derive(Debug)]
pub struct InstructionContext<'a, 'ix_data> {
pub(crate) transaction_context: &'a TransactionContext<'ix_data>,
pub(crate) index_in_trace: usize,
pub(crate) nesting_level: usize,
pub(crate) index_of_caller_instruction: usize,
pub(crate) program_account_index_in_tx: IndexOfAccount,
pub(crate) instruction_accounts: &'a [InstructionAccount],
pub(crate) dedup_map: &'a [u16],
pub(crate) instruction_data: &'ix_data [u8],
}
impl<'a> InstructionContext<'a, '_> {
pub fn get_index_in_trace(&self) -> usize {
self.index_in_trace
}
pub fn get_index_of_caller(&self) -> usize {
self.index_of_caller_instruction
}
pub fn get_stack_height(&self) -> usize {
self.nesting_level.saturating_add(1)
}
pub fn get_number_of_instruction_accounts(&self) -> IndexOfAccount {
self.instruction_accounts.len() as IndexOfAccount
}
pub fn check_number_of_instruction_accounts(
&self,
expected_at_least: IndexOfAccount,
) -> Result<(), InstructionError> {
if self.get_number_of_instruction_accounts() < expected_at_least {
Err(InstructionError::MissingAccount)
} else {
Ok(())
}
}
pub fn get_instruction_data(&self) -> &[u8] {
self.instruction_data
}
pub fn get_index_of_program_account_in_transaction(
&self,
) -> Result<IndexOfAccount, InstructionError> {
if self.program_account_index_in_tx == u16::MAX {
Err(InstructionError::MissingAccount)
} else {
Ok(self.program_account_index_in_tx)
}
}
pub fn get_index_of_instruction_account_in_transaction(
&self,
instruction_account_index: IndexOfAccount,
) -> Result<IndexOfAccount, InstructionError> {
Ok(self
.instruction_accounts
.get(instruction_account_index as usize)
.ok_or(InstructionError::MissingAccount)?
.index_in_transaction as IndexOfAccount)
}
pub fn get_index_of_account_in_instruction(
&self,
index_in_transaction: IndexOfAccount,
) -> Result<IndexOfAccount, InstructionError> {
self.dedup_map
.get(index_in_transaction as usize)
.and_then(|idx| {
if *idx as usize >= self.instruction_accounts.len() {
None
} else {
Some(*idx as IndexOfAccount)
}
})
.ok_or(InstructionError::MissingAccount)
}
pub fn is_instruction_account_duplicate(
&self,
instruction_account_index: IndexOfAccount,
) -> Result<Option<IndexOfAccount>, InstructionError> {
let index_in_transaction =
self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
let first_instruction_account_index =
self.get_index_of_account_in_instruction(index_in_transaction)?;
Ok(
if first_instruction_account_index == instruction_account_index {
None
} else {
Some(first_instruction_account_index)
},
)
}
pub fn get_program_key(&self) -> Result<&'a Pubkey, InstructionError> {
self.get_index_of_program_account_in_transaction()
.and_then(|index_in_transaction| {
self.transaction_context
.get_key_of_account_at_index(index_in_transaction)
})
}
pub fn get_program_owner(&self) -> Result<Pubkey, InstructionError> {
self.get_index_of_program_account_in_transaction()
.and_then(|index_in_transaction| {
self.transaction_context
.accounts
.try_borrow(index_in_transaction)
})
.map(|acc| *acc.owner())
}
pub fn try_borrow_instruction_account(
&self,
index_in_instruction: IndexOfAccount,
) -> Result<BorrowedInstructionAccount<'_, '_>, InstructionError> {
let instruction_account = *self
.instruction_accounts
.get(index_in_instruction as usize)
.ok_or(InstructionError::MissingAccount)?;
let account = self
.transaction_context
.accounts
.try_borrow_mut(instruction_account.index_in_transaction)?;
Ok(BorrowedInstructionAccount {
transaction_context: self.transaction_context,
instruction_account,
account,
index_in_transaction_of_instruction_program: self.program_account_index_in_tx,
})
}
pub fn is_instruction_account_signer(
&self,
instruction_account_index: IndexOfAccount,
) -> Result<bool, InstructionError> {
Ok(self
.instruction_accounts
.get(instruction_account_index as usize)
.ok_or(InstructionError::MissingAccount)?
.is_signer())
}
pub fn is_instruction_account_writable(
&self,
instruction_account_index: IndexOfAccount,
) -> Result<bool, InstructionError> {
Ok(self
.instruction_accounts
.get(instruction_account_index as usize)
.ok_or(InstructionError::MissingAccount)?
.is_writable())
}
pub fn get_signers(&self) -> Result<HashSet<Pubkey>, InstructionError> {
let mut result = HashSet::new();
for instruction_account in self.instruction_accounts.iter() {
if instruction_account.is_signer() {
result.insert(
*self
.transaction_context
.get_key_of_account_at_index(instruction_account.index_in_transaction)?,
);
}
}
Ok(result)
}
pub fn instruction_accounts(&self) -> &[InstructionAccount] {
self.instruction_accounts
}
pub fn get_key_of_instruction_account(
&self,
index_in_instruction: IndexOfAccount,
) -> Result<&'a Pubkey, InstructionError> {
self.get_index_of_instruction_account_in_transaction(index_in_instruction)
.and_then(|idx| self.transaction_context.get_key_of_account_at_index(idx))
}
}