2022-08-15 11:41:46 -07:00
|
|
|
//! Data shared between program runtime and built-in programs as well as SBF programs
|
2022-09-05 07:29:02 -07:00
|
|
|
#![deny(clippy::indexing_slicing)]
|
2021-12-27 09:49:32 -08:00
|
|
|
|
2022-09-02 03:42:06 -07:00
|
|
|
#[cfg(target_os = "solana")]
|
|
|
|
use crate::instruction::AccountPropertyUpdate;
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
|
|
|
use crate::{
|
|
|
|
account::WritableAccount,
|
|
|
|
rent::Rent,
|
|
|
|
system_instruction::{
|
|
|
|
MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION, MAX_PERMITTED_DATA_LENGTH,
|
|
|
|
},
|
|
|
|
};
|
2022-02-18 21:32:29 -08:00
|
|
|
use {
|
|
|
|
crate::{
|
2022-08-31 08:47:47 -07:00
|
|
|
account::{AccountSharedData, ReadableAccount},
|
2022-02-18 21:32:29 -08:00
|
|
|
instruction::InstructionError,
|
|
|
|
pubkey::Pubkey,
|
|
|
|
},
|
|
|
|
std::{
|
|
|
|
cell::{RefCell, RefMut},
|
|
|
|
collections::HashSet,
|
|
|
|
pin::Pin,
|
|
|
|
},
|
2022-01-03 14:30:56 -08:00
|
|
|
};
|
2021-12-27 09:49:32 -08:00
|
|
|
|
2022-09-02 03:42:06 -07:00
|
|
|
/// For addressing (nested) properties of the TransactionContext
|
|
|
|
#[repr(u16)]
|
|
|
|
pub enum TransactionContextAttribute {
|
|
|
|
/// TransactionContext -> &[u8]
|
|
|
|
ReturnData,
|
|
|
|
/// TransactionContext -> u128
|
|
|
|
AccountsResizeDelta,
|
|
|
|
/// TransactionContext -> u16
|
|
|
|
TransactionAccountCount,
|
|
|
|
/// TransactionContext -> &[TransactionAccount] -> Pubkey
|
|
|
|
TransactionAccountKey,
|
|
|
|
/// TransactionContext -> &[TransactionAccount] -> Pubkey
|
|
|
|
TransactionAccountOwner,
|
|
|
|
/// TransactionContext -> &[TransactionAccount] -> u64
|
|
|
|
TransactionAccountLamports,
|
|
|
|
/// TransactionContext -> &[TransactionAccount] -> &[u8]
|
|
|
|
TransactionAccountData,
|
|
|
|
/// TransactionContext -> &[TransactionAccount] -> bool
|
|
|
|
TransactionAccountIsExecutable,
|
|
|
|
/// TransactionContext -> &[TransactionAccount] -> u64
|
|
|
|
TransactionAccountRentEpoch,
|
|
|
|
/// TransactionContext -> &[TransactionAccount] -> bool
|
|
|
|
TransactionAccountTouchedFlag,
|
|
|
|
/// TransactionContext -> u8
|
|
|
|
InstructionStackHeight,
|
|
|
|
/// TransactionContext -> u8
|
|
|
|
InstructionStackCapacity,
|
|
|
|
/// TransactionContext -> &[u8]
|
|
|
|
InstructionStackEntry,
|
|
|
|
/// TransactionContext -> u16
|
|
|
|
InstructionTraceLength,
|
|
|
|
/// TransactionContext -> u16
|
|
|
|
InstructionTraceCapacity,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> u8
|
|
|
|
InstructionTraceNestingLevel,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> u128
|
|
|
|
InstructionTraceLamportSum,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> &[u8]
|
|
|
|
InstructionTraceInstructionData,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> &[u16]
|
|
|
|
InstructionTraceProgramAccount,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> &[InstructionAccount] -> u16
|
|
|
|
InstructionAccountIndexInTransaction,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> &[InstructionAccount] -> u16
|
|
|
|
InstructionAccountIndexInCaller,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> &[InstructionAccount] -> u16
|
|
|
|
InstructionAccountIndexInCallee,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> &[InstructionAccount] -> bool
|
|
|
|
InstructionAccountIsSigner,
|
|
|
|
/// TransactionContext -> &[InstructionContext] -> &[InstructionAccount] -> bool
|
|
|
|
InstructionAccountIsWritable,
|
|
|
|
}
|
|
|
|
|
2022-09-06 02:31:40 -07:00
|
|
|
/// Index of an account inside of the TransactionContext or an InstructionContext.
|
|
|
|
pub type IndexOfAccount = u16;
|
|
|
|
|
2022-05-24 15:04:46 -07:00
|
|
|
/// Contains account meta data which varies between instruction.
|
|
|
|
///
|
|
|
|
/// It also contains indices to other structures for faster lookup.
|
2021-12-27 09:49:32 -08:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct InstructionAccount {
|
2022-05-24 15:04:46 -07:00
|
|
|
/// Points to the account and its key in the `TransactionContext`
|
2022-09-06 02:31:40 -07:00
|
|
|
pub index_in_transaction: IndexOfAccount,
|
2022-05-24 15:04:46 -07:00
|
|
|
/// Points to the first occurrence in the parent `InstructionContext`
|
|
|
|
///
|
2022-05-25 04:43:20 -07:00
|
|
|
/// This excludes the program accounts.
|
2022-09-06 02:31:40 -07:00
|
|
|
pub index_in_caller: IndexOfAccount,
|
2022-05-24 15:04:46 -07:00
|
|
|
/// Points to the first occurrence in the current `InstructionContext`
|
|
|
|
///
|
|
|
|
/// This excludes the program accounts.
|
2022-09-06 02:31:40 -07:00
|
|
|
pub index_in_callee: IndexOfAccount,
|
2022-05-24 15:04:46 -07:00
|
|
|
/// Is this account supposed to sign
|
2021-12-27 09:49:32 -08:00
|
|
|
pub is_signer: bool,
|
2022-05-24 15:04:46 -07:00
|
|
|
/// Is this account allowed to become writable
|
2021-12-27 09:49:32 -08:00
|
|
|
pub is_writable: bool,
|
|
|
|
}
|
|
|
|
|
2022-09-05 07:29:02 -07:00
|
|
|
/// An account key and the matching account
|
|
|
|
pub type TransactionAccount = (Pubkey, AccountSharedData);
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Loaded transaction shared between runtime and programs.
|
|
|
|
///
|
|
|
|
/// This context is valid for the entire duration of a transaction being processed.
|
2021-12-30 06:46:36 -08:00
|
|
|
#[derive(Debug)]
|
2021-12-27 09:49:32 -08:00
|
|
|
pub struct TransactionContext {
|
2022-01-03 14:30:56 -08:00
|
|
|
account_keys: Pin<Box<[Pubkey]>>,
|
|
|
|
accounts: Pin<Box<[RefCell<AccountSharedData>]>>,
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-07-15 00:31:34 -07:00
|
|
|
account_touched_flags: RefCell<Pin<Box<[bool]>>>,
|
2021-12-27 09:49:32 -08:00
|
|
|
instruction_context_capacity: usize,
|
2022-02-09 11:04:49 -08:00
|
|
|
instruction_stack: Vec<usize>,
|
2022-08-20 02:20:47 -07:00
|
|
|
instruction_trace: Vec<InstructionContext>,
|
2022-03-22 15:17:05 -07:00
|
|
|
return_data: TransactionReturnData,
|
2022-07-15 00:31:34 -07:00
|
|
|
accounts_resize_delta: RefCell<i64>,
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-07-15 00:31:34 -07:00
|
|
|
rent: Option<Rent>,
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-08-29 11:30:48 -07:00
|
|
|
is_cap_accounts_data_allocations_per_transaction_enabled: bool,
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TransactionContext {
|
|
|
|
/// Constructs a new TransactionContext
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-12-27 09:49:32 -08:00
|
|
|
pub fn new(
|
|
|
|
transaction_accounts: Vec<TransactionAccount>,
|
2022-07-15 00:31:34 -07:00
|
|
|
rent: Option<Rent>,
|
2021-12-27 09:49:32 -08:00
|
|
|
instruction_context_capacity: usize,
|
2022-08-20 02:20:47 -07:00
|
|
|
_number_of_instructions_at_transaction_level: usize,
|
2021-12-27 09:49:32 -08:00
|
|
|
) -> Self {
|
2022-01-03 14:30:56 -08:00
|
|
|
let (account_keys, accounts): (Vec<Pubkey>, Vec<RefCell<AccountSharedData>>) =
|
|
|
|
transaction_accounts
|
|
|
|
.into_iter()
|
|
|
|
.map(|(key, account)| (key, RefCell::new(account)))
|
|
|
|
.unzip();
|
2022-07-15 00:31:34 -07:00
|
|
|
let account_touched_flags = vec![false; accounts.len()];
|
2021-12-27 09:49:32 -08:00
|
|
|
Self {
|
2022-01-03 14:30:56 -08:00
|
|
|
account_keys: Pin::new(account_keys.into_boxed_slice()),
|
|
|
|
accounts: Pin::new(accounts.into_boxed_slice()),
|
2022-07-15 00:31:34 -07:00
|
|
|
account_touched_flags: RefCell::new(Pin::new(account_touched_flags.into_boxed_slice())),
|
2021-12-27 09:49:32 -08:00
|
|
|
instruction_context_capacity,
|
2022-02-09 11:04:49 -08:00
|
|
|
instruction_stack: Vec::with_capacity(instruction_context_capacity),
|
2022-09-03 01:34:57 -07:00
|
|
|
instruction_trace: vec![InstructionContext::default()],
|
2022-03-22 15:17:05 -07:00
|
|
|
return_data: TransactionReturnData::default(),
|
2022-07-15 00:31:34 -07:00
|
|
|
accounts_resize_delta: RefCell::new(0),
|
|
|
|
rent,
|
2022-08-29 11:30:48 -07:00
|
|
|
is_cap_accounts_data_allocations_per_transaction_enabled: false,
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Used in mock_process_instruction
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-12-27 09:49:32 -08:00
|
|
|
pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
|
2022-02-09 11:04:49 -08:00
|
|
|
if !self.instruction_stack.is_empty() {
|
2021-12-27 09:49:32 -08:00
|
|
|
return Err(InstructionError::CallDepth);
|
|
|
|
}
|
2022-01-03 14:30:56 -08:00
|
|
|
Ok(Vec::from(Pin::into_inner(self.accounts))
|
2021-12-27 09:49:32 -08:00
|
|
|
.into_iter()
|
|
|
|
.map(|account| account.into_inner())
|
|
|
|
.collect())
|
|
|
|
}
|
|
|
|
|
2022-07-15 00:31:34 -07:00
|
|
|
/// Returns true if `enable_early_verification_of_account_modifications` is active
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-07-15 00:31:34 -07:00
|
|
|
pub fn is_early_verification_of_account_modifications_enabled(&self) -> bool {
|
|
|
|
self.rent.is_some()
|
|
|
|
}
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Returns the total number of accounts loaded in this Transaction
|
2022-09-06 02:31:40 -07:00
|
|
|
pub fn get_number_of_accounts(&self) -> IndexOfAccount {
|
|
|
|
self.accounts.len() as IndexOfAccount
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Searches for an account by its key
|
2022-02-03 02:34:51 -08:00
|
|
|
pub fn get_key_of_account_at_index(
|
|
|
|
&self,
|
2022-09-06 02:31:40 -07:00
|
|
|
index_in_transaction: IndexOfAccount,
|
2022-02-03 02:34:51 -08:00
|
|
|
) -> Result<&Pubkey, InstructionError> {
|
|
|
|
self.account_keys
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(index_in_transaction as usize)
|
2022-02-03 02:34:51 -08:00
|
|
|
.ok_or(InstructionError::NotEnoughAccountKeys)
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Searches for an account by its key
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-02-03 02:34:51 -08:00
|
|
|
pub fn get_account_at_index(
|
|
|
|
&self,
|
2022-09-06 02:31:40 -07:00
|
|
|
index_in_transaction: IndexOfAccount,
|
2022-02-03 02:34:51 -08:00
|
|
|
) -> Result<&RefCell<AccountSharedData>, InstructionError> {
|
|
|
|
self.accounts
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(index_in_transaction as usize)
|
2022-02-03 02:34:51 -08:00
|
|
|
.ok_or(InstructionError::NotEnoughAccountKeys)
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Searches for an account by its key
|
2022-09-06 02:31:40 -07:00
|
|
|
pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
|
|
|
|
self.account_keys
|
|
|
|
.iter()
|
|
|
|
.position(|key| key == pubkey)
|
|
|
|
.map(|index| index as IndexOfAccount)
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Searches for a program account by its key
|
2022-09-06 02:31:40 -07:00
|
|
|
pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
|
|
|
|
self.account_keys
|
|
|
|
.iter()
|
|
|
|
.rposition(|key| key == pubkey)
|
|
|
|
.map(|index| index as IndexOfAccount)
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
2022-09-03 01:34:57 -07:00
|
|
|
/// Returns the instruction trace length.
|
|
|
|
///
|
|
|
|
/// Not counting the last empty InstructionContext which is always pre-reserved for the next instruction.
|
|
|
|
/// See also `get_next_instruction_context()`.
|
2022-08-20 02:20:47 -07:00
|
|
|
pub fn get_instruction_trace_length(&self) -> usize {
|
2022-09-03 01:34:57 -07:00
|
|
|
self.instruction_trace.len().saturating_sub(1)
|
2022-08-20 02:20:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets an InstructionContext by its index in the trace
|
|
|
|
pub fn get_instruction_context_at_index_in_trace(
|
|
|
|
&self,
|
|
|
|
index_in_trace: usize,
|
|
|
|
) -> Result<&InstructionContext, InstructionError> {
|
|
|
|
self.instruction_trace
|
|
|
|
.get(index_in_trace)
|
|
|
|
.ok_or(InstructionError::CallDepth)
|
|
|
|
}
|
|
|
|
|
2022-04-12 17:52:47 -07:00
|
|
|
/// Gets an InstructionContext by its nesting level in the stack
|
2022-08-20 02:20:47 -07:00
|
|
|
pub fn get_instruction_context_at_nesting_level(
|
2021-12-27 09:49:32 -08:00
|
|
|
&self,
|
2022-08-20 02:20:47 -07:00
|
|
|
nesting_level: usize,
|
2022-04-12 17:52:47 -07:00
|
|
|
) -> Result<&InstructionContext, InstructionError> {
|
2022-08-20 02:20:47 -07:00
|
|
|
let index_in_trace = *self
|
2022-02-09 11:04:49 -08:00
|
|
|
.instruction_stack
|
2022-08-20 02:20:47 -07:00
|
|
|
.get(nesting_level)
|
2022-02-09 11:04:49 -08:00
|
|
|
.ok_or(InstructionError::CallDepth)?;
|
2022-08-20 02:20:47 -07:00
|
|
|
let instruction_context = self.get_instruction_context_at_index_in_trace(index_in_trace)?;
|
|
|
|
debug_assert_eq!(instruction_context.nesting_level, nesting_level);
|
2022-02-09 11:04:49 -08:00
|
|
|
Ok(instruction_context)
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the max height of the InstructionContext stack
|
|
|
|
pub fn get_instruction_context_capacity(&self) -> usize {
|
|
|
|
self.instruction_context_capacity
|
|
|
|
}
|
|
|
|
|
2022-02-02 16:45:57 -08:00
|
|
|
/// Gets instruction stack height, top-level instructions are height
|
|
|
|
/// `solana_sdk::instruction::TRANSACTION_LEVEL_STACK_HEIGHT`
|
2021-12-30 06:46:36 -08:00
|
|
|
pub fn get_instruction_context_stack_height(&self) -> usize {
|
2022-02-09 11:04:49 -08:00
|
|
|
self.instruction_stack.len()
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the current InstructionContext
|
|
|
|
pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
|
2021-12-30 06:46:36 -08:00
|
|
|
let level = self
|
2022-02-09 11:04:49 -08:00
|
|
|
.get_instruction_context_stack_height()
|
2021-12-30 06:46:36 -08:00
|
|
|
.checked_sub(1)
|
|
|
|
.ok_or(InstructionError::CallDepth)?;
|
2022-08-20 02:20:47 -07:00
|
|
|
self.get_instruction_context_at_nesting_level(level)
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
2022-09-03 01:34:57 -07:00
|
|
|
/// Returns the InstructionContext to configure for the next invocation.
|
|
|
|
///
|
|
|
|
/// The last InstructionContext is always empty and pre-reserved for the next instruction.
|
|
|
|
pub fn get_next_instruction_context(
|
2021-12-27 09:49:32 -08:00
|
|
|
&mut self,
|
2022-09-03 01:34:57 -07:00
|
|
|
) -> Result<&mut InstructionContext, InstructionError> {
|
|
|
|
self.instruction_trace
|
|
|
|
.last_mut()
|
|
|
|
.ok_or(InstructionError::CallDepth)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Pushes the next InstructionContext
|
|
|
|
#[cfg(not(target_os = "solana"))]
|
|
|
|
pub fn push(&mut self) -> Result<(), InstructionError> {
|
|
|
|
let nesting_level = self.get_instruction_context_stack_height();
|
|
|
|
let caller_instruction_context = self
|
|
|
|
.instruction_trace
|
|
|
|
.last()
|
|
|
|
.ok_or(InstructionError::CallDepth)?;
|
2022-09-05 07:29:02 -07:00
|
|
|
let callee_instruction_accounts_lamport_sum =
|
|
|
|
self.instruction_accounts_lamport_sum(caller_instruction_context)?;
|
2022-08-20 02:20:47 -07:00
|
|
|
if !self.instruction_stack.is_empty()
|
|
|
|
&& self.is_early_verification_of_account_modifications_enabled()
|
|
|
|
{
|
|
|
|
let caller_instruction_context = self.get_current_instruction_context()?;
|
|
|
|
let original_caller_instruction_accounts_lamport_sum =
|
|
|
|
caller_instruction_context.instruction_accounts_lamport_sum;
|
2022-09-05 07:29:02 -07:00
|
|
|
let current_caller_instruction_accounts_lamport_sum =
|
|
|
|
self.instruction_accounts_lamport_sum(caller_instruction_context)?;
|
2022-08-20 02:20:47 -07:00
|
|
|
if original_caller_instruction_accounts_lamport_sum
|
|
|
|
!= current_caller_instruction_accounts_lamport_sum
|
|
|
|
{
|
|
|
|
return Err(InstructionError::UnbalancedInstruction);
|
2022-07-15 00:31:34 -07:00
|
|
|
}
|
2022-08-20 02:20:47 -07:00
|
|
|
}
|
2022-09-03 01:34:57 -07:00
|
|
|
{
|
|
|
|
let mut instruction_context = self.get_next_instruction_context()?;
|
|
|
|
instruction_context.nesting_level = nesting_level;
|
|
|
|
instruction_context.instruction_accounts_lamport_sum =
|
|
|
|
callee_instruction_accounts_lamport_sum;
|
|
|
|
}
|
2022-09-05 07:29:02 -07:00
|
|
|
let index_in_trace = self.get_instruction_trace_length();
|
2022-09-03 01:34:57 -07:00
|
|
|
self.instruction_trace.push(InstructionContext::default());
|
|
|
|
if nesting_level >= self.instruction_context_capacity {
|
2022-02-09 11:04:49 -08:00
|
|
|
return Err(InstructionError::CallDepth);
|
2022-01-19 13:40:09 -08:00
|
|
|
}
|
2022-02-09 11:04:49 -08:00
|
|
|
self.instruction_stack.push(index_in_trace);
|
2021-12-27 09:49:32 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Pops the current InstructionContext
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-12-27 09:49:32 -08:00
|
|
|
pub fn pop(&mut self) -> Result<(), InstructionError> {
|
2022-02-09 11:04:49 -08:00
|
|
|
if self.instruction_stack.is_empty() {
|
2021-12-27 09:49:32 -08:00
|
|
|
return Err(InstructionError::CallDepth);
|
|
|
|
}
|
2022-07-15 00:31:34 -07:00
|
|
|
// Verify (before we pop) that the total sum of all lamports in this instruction did not change
|
2022-08-20 02:20:47 -07:00
|
|
|
let detected_an_unbalanced_instruction =
|
|
|
|
if self.is_early_verification_of_account_modifications_enabled() {
|
|
|
|
self.get_current_instruction_context()
|
|
|
|
.and_then(|instruction_context| {
|
|
|
|
// Verify all executable accounts have no outstanding refs
|
|
|
|
for account_index in instruction_context.program_accounts.iter() {
|
|
|
|
self.get_account_at_index(*account_index)?
|
|
|
|
.try_borrow_mut()
|
|
|
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?;
|
|
|
|
}
|
2022-09-05 07:29:02 -07:00
|
|
|
self.instruction_accounts_lamport_sum(instruction_context)
|
|
|
|
.map(|instruction_accounts_lamport_sum| {
|
|
|
|
instruction_context.instruction_accounts_lamport_sum
|
|
|
|
!= instruction_accounts_lamport_sum
|
|
|
|
})
|
2022-08-20 02:20:47 -07:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Ok(false)
|
|
|
|
};
|
2022-07-15 00:31:34 -07:00
|
|
|
// Always pop, even if we `detected_an_unbalanced_instruction`
|
2022-02-09 11:04:49 -08:00
|
|
|
self.instruction_stack.pop();
|
2022-07-15 00:31:34 -07:00
|
|
|
if detected_an_unbalanced_instruction? {
|
|
|
|
Err(InstructionError::UnbalancedInstruction)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the return data of the current InstructionContext or any above
|
|
|
|
pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
|
2022-03-22 15:17:05 -07:00
|
|
|
(&self.return_data.program_id, &self.return_data.data)
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the return data of the current InstructionContext
|
2022-01-10 09:26:51 -08:00
|
|
|
pub fn set_return_data(
|
|
|
|
&mut self,
|
|
|
|
program_id: Pubkey,
|
|
|
|
data: Vec<u8>,
|
|
|
|
) -> Result<(), InstructionError> {
|
2022-03-22 15:17:05 -07:00
|
|
|
self.return_data = TransactionReturnData { program_id, data };
|
2021-12-27 09:49:32 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
2022-01-19 13:40:09 -08:00
|
|
|
|
2022-07-15 00:31:34 -07:00
|
|
|
/// Calculates the sum of all lamports within an instruction
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-09-05 07:29:02 -07:00
|
|
|
fn instruction_accounts_lamport_sum(
|
|
|
|
&self,
|
|
|
|
instruction_context: &InstructionContext,
|
|
|
|
) -> Result<u128, InstructionError> {
|
2022-07-15 00:31:34 -07:00
|
|
|
if !self.is_early_verification_of_account_modifications_enabled() {
|
|
|
|
return Ok(0);
|
|
|
|
}
|
|
|
|
let mut instruction_accounts_lamport_sum: u128 = 0;
|
2022-09-05 07:29:02 -07:00
|
|
|
for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts()
|
|
|
|
{
|
|
|
|
if instruction_context
|
|
|
|
.is_instruction_account_duplicate(instruction_account_index)?
|
|
|
|
.is_some()
|
|
|
|
{
|
2022-07-15 00:31:34 -07:00
|
|
|
continue; // Skip duplicate account
|
|
|
|
}
|
2022-09-05 07:29:02 -07:00
|
|
|
let index_in_transaction = instruction_context
|
|
|
|
.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
|
2022-07-15 00:31:34 -07:00
|
|
|
instruction_accounts_lamport_sum = (self
|
2022-09-05 07:29:02 -07:00
|
|
|
.get_account_at_index(index_in_transaction)?
|
2022-07-15 00:31:34 -07:00
|
|
|
.try_borrow()
|
|
|
|
.map_err(|_| InstructionError::AccountBorrowOutstanding)?
|
|
|
|
.lamports() as u128)
|
|
|
|
.checked_add(instruction_accounts_lamport_sum)
|
|
|
|
.ok_or(InstructionError::ArithmeticOverflow)?;
|
|
|
|
}
|
|
|
|
Ok(instruction_accounts_lamport_sum)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the accounts resize delta
|
|
|
|
pub fn accounts_resize_delta(&self) -> Result<i64, InstructionError> {
|
|
|
|
self.accounts_resize_delta
|
|
|
|
.try_borrow()
|
|
|
|
.map_err(|_| InstructionError::GenericError)
|
|
|
|
.map(|value_ref| *value_ref)
|
|
|
|
}
|
2022-08-29 11:30:48 -07:00
|
|
|
|
|
|
|
/// Enables enforcing a maximum accounts data allocation size per transaction
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-08-29 11:30:48 -07:00
|
|
|
pub fn enable_cap_accounts_data_allocations_per_transaction(&mut self) {
|
|
|
|
self.is_cap_accounts_data_allocations_per_transaction_enabled = true;
|
|
|
|
}
|
2022-02-02 16:45:57 -08:00
|
|
|
}
|
|
|
|
|
2022-03-22 15:17:05 -07:00
|
|
|
/// Return data at the end of a transaction
|
2022-05-22 18:00:42 -07:00
|
|
|
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
2022-03-22 15:17:05 -07:00
|
|
|
pub struct TransactionReturnData {
|
|
|
|
pub program_id: Pubkey,
|
|
|
|
pub data: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Loaded instruction shared between runtime and programs.
|
|
|
|
///
|
|
|
|
/// This context is valid for the entire duration of a (possibly cross program) instruction being processed.
|
2022-09-03 01:34:57 -07:00
|
|
|
#[derive(Debug, Clone, Default)]
|
2021-12-27 09:49:32 -08:00
|
|
|
pub struct InstructionContext {
|
2022-02-09 11:04:49 -08:00
|
|
|
nesting_level: usize,
|
2022-07-15 00:31:34 -07:00
|
|
|
instruction_accounts_lamport_sum: u128,
|
2022-09-06 02:31:40 -07:00
|
|
|
program_accounts: Vec<IndexOfAccount>,
|
2021-12-30 06:46:36 -08:00
|
|
|
instruction_accounts: Vec<InstructionAccount>,
|
2021-12-27 09:49:32 -08:00
|
|
|
instruction_data: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl InstructionContext {
|
2022-09-03 01:34:57 -07:00
|
|
|
/// Used together with TransactionContext::get_next_instruction_context()
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-09-03 01:34:57 -07:00
|
|
|
pub fn configure(
|
|
|
|
&mut self,
|
2022-09-06 02:31:40 -07:00
|
|
|
program_accounts: &[IndexOfAccount],
|
2022-09-03 01:34:57 -07:00
|
|
|
instruction_accounts: &[InstructionAccount],
|
|
|
|
instruction_data: &[u8],
|
|
|
|
) {
|
|
|
|
self.program_accounts = program_accounts.to_vec();
|
|
|
|
self.instruction_accounts = instruction_accounts.to_vec();
|
|
|
|
self.instruction_data = instruction_data.to_vec();
|
2022-02-02 16:45:57 -08:00
|
|
|
}
|
|
|
|
|
2022-02-09 11:04:49 -08:00
|
|
|
/// How many Instructions were on the stack after this one was pushed
|
|
|
|
///
|
|
|
|
/// That is the number of nested parent Instructions plus one (itself).
|
|
|
|
pub fn get_stack_height(&self) -> usize {
|
|
|
|
self.nesting_level.saturating_add(1)
|
|
|
|
}
|
|
|
|
|
2022-08-31 08:47:47 -07:00
|
|
|
/// Returns the sum of lamports of the instruction accounts in this Instruction
|
|
|
|
pub fn get_instruction_accounts_lamport_sum(&self) -> u128 {
|
|
|
|
self.instruction_accounts_lamport_sum
|
|
|
|
}
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Number of program accounts
|
2022-09-06 02:31:40 -07:00
|
|
|
pub fn get_number_of_program_accounts(&self) -> IndexOfAccount {
|
|
|
|
self.program_accounts.len() as IndexOfAccount
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Number of accounts in this Instruction (without program accounts)
|
2022-09-06 02:31:40 -07:00
|
|
|
pub fn get_number_of_instruction_accounts(&self) -> IndexOfAccount {
|
|
|
|
self.instruction_accounts.len() as IndexOfAccount
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
2022-02-18 17:06:33 -08:00
|
|
|
/// Assert that enough account were supplied to this Instruction
|
|
|
|
pub fn check_number_of_instruction_accounts(
|
|
|
|
&self,
|
2022-09-06 02:31:40 -07:00
|
|
|
expected_at_least: IndexOfAccount,
|
2022-02-18 17:06:33 -08:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
if self.get_number_of_instruction_accounts() < expected_at_least {
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Data parameter for the programs `process_instruction` handler
|
|
|
|
pub fn get_instruction_data(&self) -> &[u8] {
|
|
|
|
&self.instruction_data
|
|
|
|
}
|
|
|
|
|
2021-12-30 06:46:36 -08:00
|
|
|
/// Searches for a program account by its key
|
|
|
|
pub fn find_index_of_program_account(
|
|
|
|
&self,
|
|
|
|
transaction_context: &TransactionContext,
|
|
|
|
pubkey: &Pubkey,
|
2022-09-06 02:31:40 -07:00
|
|
|
) -> Option<IndexOfAccount> {
|
2021-12-30 06:46:36 -08:00
|
|
|
self.program_accounts
|
|
|
|
.iter()
|
|
|
|
.position(|index_in_transaction| {
|
2022-09-06 02:31:40 -07:00
|
|
|
transaction_context
|
|
|
|
.account_keys
|
|
|
|
.get(*index_in_transaction as usize)
|
|
|
|
== Some(pubkey)
|
2021-12-30 06:46:36 -08:00
|
|
|
})
|
2022-09-06 02:31:40 -07:00
|
|
|
.map(|index| index as IndexOfAccount)
|
2021-12-30 06:46:36 -08:00
|
|
|
}
|
|
|
|
|
2022-06-16 09:46:17 -07:00
|
|
|
/// Searches for an instruction account by its key
|
|
|
|
pub fn find_index_of_instruction_account(
|
2021-12-30 06:46:36 -08:00
|
|
|
&self,
|
|
|
|
transaction_context: &TransactionContext,
|
|
|
|
pubkey: &Pubkey,
|
2022-09-06 02:31:40 -07:00
|
|
|
) -> Option<IndexOfAccount> {
|
2021-12-30 06:46:36 -08:00
|
|
|
self.instruction_accounts
|
|
|
|
.iter()
|
|
|
|
.position(|instruction_account| {
|
2022-09-05 07:29:02 -07:00
|
|
|
transaction_context
|
|
|
|
.account_keys
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(instruction_account.index_in_transaction as usize)
|
2022-09-05 07:29:02 -07:00
|
|
|
== Some(pubkey)
|
2021-12-30 06:46:36 -08:00
|
|
|
})
|
2022-09-06 02:31:40 -07:00
|
|
|
.map(|index| index as IndexOfAccount)
|
2021-12-30 06:46:36 -08:00
|
|
|
}
|
|
|
|
|
2022-06-16 09:46:17 -07:00
|
|
|
/// Translates the given instruction wide program_account_index into a transaction wide index
|
|
|
|
pub fn get_index_of_program_account_in_transaction(
|
2022-01-03 14:30:56 -08:00
|
|
|
&self,
|
2022-09-06 02:31:40 -07:00
|
|
|
program_account_index: IndexOfAccount,
|
|
|
|
) -> Result<IndexOfAccount, InstructionError> {
|
2022-06-16 09:46:17 -07:00
|
|
|
Ok(*self
|
|
|
|
.program_accounts
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(program_account_index as usize)
|
2022-06-16 09:46:17 -07:00
|
|
|
.ok_or(InstructionError::NotEnoughAccountKeys)?)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Translates the given instruction wide instruction_account_index into a transaction wide index
|
|
|
|
pub fn get_index_of_instruction_account_in_transaction(
|
|
|
|
&self,
|
2022-09-06 02:31:40 -07:00
|
|
|
instruction_account_index: IndexOfAccount,
|
|
|
|
) -> Result<IndexOfAccount, InstructionError> {
|
2022-06-16 09:46:17 -07:00
|
|
|
Ok(self
|
|
|
|
.instruction_accounts
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(instruction_account_index as usize)
|
2022-06-16 09:46:17 -07:00
|
|
|
.ok_or(InstructionError::NotEnoughAccountKeys)?
|
2022-09-06 02:31:40 -07:00
|
|
|
.index_in_transaction as IndexOfAccount)
|
2022-01-03 14:30:56 -08:00
|
|
|
}
|
|
|
|
|
2022-06-16 09:46:17 -07:00
|
|
|
/// Returns `Some(instruction_account_index)` if this is a duplicate
|
2022-05-24 15:04:46 -07:00
|
|
|
/// and `None` if it is the first account with this key
|
2022-06-16 09:46:17 -07:00
|
|
|
pub fn is_instruction_account_duplicate(
|
2022-05-24 15:04:46 -07:00
|
|
|
&self,
|
2022-09-06 02:31:40 -07:00
|
|
|
instruction_account_index: IndexOfAccount,
|
|
|
|
) -> Result<Option<IndexOfAccount>, InstructionError> {
|
2022-06-16 09:46:17 -07:00
|
|
|
let index_in_callee = self
|
|
|
|
.instruction_accounts
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(instruction_account_index as usize)
|
2022-06-16 09:46:17 -07:00
|
|
|
.ok_or(InstructionError::NotEnoughAccountKeys)?
|
|
|
|
.index_in_callee;
|
|
|
|
Ok(if index_in_callee == instruction_account_index {
|
|
|
|
None
|
2022-05-24 15:04:46 -07:00
|
|
|
} else {
|
2022-06-16 09:46:17 -07:00
|
|
|
Some(index_in_callee)
|
|
|
|
})
|
2022-05-24 15:04:46 -07:00
|
|
|
}
|
|
|
|
|
2022-02-18 17:06:33 -08:00
|
|
|
/// Gets the key of the last program account of this Instruction
|
2022-06-16 09:46:17 -07:00
|
|
|
pub fn get_last_program_key<'a, 'b: 'a>(
|
2022-02-18 17:06:33 -08:00
|
|
|
&'a self,
|
|
|
|
transaction_context: &'b TransactionContext,
|
|
|
|
) -> Result<&'b Pubkey, InstructionError> {
|
2022-09-03 01:34:57 -07:00
|
|
|
self.get_index_of_program_account_in_transaction(
|
2022-09-05 07:29:02 -07:00
|
|
|
self.get_number_of_program_accounts().saturating_sub(1),
|
2022-09-03 01:34:57 -07:00
|
|
|
)
|
|
|
|
.and_then(|index_in_transaction| {
|
|
|
|
transaction_context.get_key_of_account_at_index(index_in_transaction)
|
|
|
|
})
|
2022-02-18 17:06:33 -08:00
|
|
|
}
|
|
|
|
|
2022-06-16 09:46:17 -07:00
|
|
|
fn try_borrow_account<'a, 'b: 'a>(
|
2021-12-27 09:49:32 -08:00
|
|
|
&'a self,
|
|
|
|
transaction_context: &'b TransactionContext,
|
2022-09-06 02:31:40 -07:00
|
|
|
index_in_transaction: IndexOfAccount,
|
|
|
|
index_in_instruction: IndexOfAccount,
|
2021-12-27 09:49:32 -08:00
|
|
|
) -> Result<BorrowedAccount<'a>, InstructionError> {
|
2022-06-16 09:46:17 -07:00
|
|
|
let account = transaction_context
|
|
|
|
.accounts
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(index_in_transaction as usize)
|
2022-06-16 09:46:17 -07:00
|
|
|
.ok_or(InstructionError::MissingAccount)?
|
2021-12-27 09:49:32 -08:00
|
|
|
.try_borrow_mut()
|
|
|
|
.map_err(|_| InstructionError::AccountBorrowFailed)?;
|
|
|
|
Ok(BorrowedAccount {
|
|
|
|
transaction_context,
|
|
|
|
instruction_context: self,
|
|
|
|
index_in_transaction,
|
|
|
|
index_in_instruction,
|
|
|
|
account,
|
|
|
|
})
|
|
|
|
}
|
2021-12-30 06:46:36 -08:00
|
|
|
|
2022-01-05 00:39:37 -08:00
|
|
|
/// Gets the last program account of this Instruction
|
2022-06-16 09:46:17 -07:00
|
|
|
pub fn try_borrow_last_program_account<'a, 'b: 'a>(
|
|
|
|
&'a self,
|
|
|
|
transaction_context: &'b TransactionContext,
|
|
|
|
) -> Result<BorrowedAccount<'a>, InstructionError> {
|
|
|
|
let result = self.try_borrow_program_account(
|
|
|
|
transaction_context,
|
2022-09-05 07:29:02 -07:00
|
|
|
self.get_number_of_program_accounts().saturating_sub(1),
|
2022-06-16 09:46:17 -07:00
|
|
|
);
|
|
|
|
debug_assert!(result.is_ok());
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tries to borrow a program account from this Instruction
|
2021-12-30 06:46:36 -08:00
|
|
|
pub fn try_borrow_program_account<'a, 'b: 'a>(
|
|
|
|
&'a self,
|
|
|
|
transaction_context: &'b TransactionContext,
|
2022-09-06 02:31:40 -07:00
|
|
|
program_account_index: IndexOfAccount,
|
2021-12-30 06:46:36 -08:00
|
|
|
) -> Result<BorrowedAccount<'a>, InstructionError> {
|
2022-06-16 09:46:17 -07:00
|
|
|
let index_in_transaction =
|
|
|
|
self.get_index_of_program_account_in_transaction(program_account_index)?;
|
2021-12-30 06:46:36 -08:00
|
|
|
self.try_borrow_account(
|
|
|
|
transaction_context,
|
2022-06-16 09:46:17 -07:00
|
|
|
index_in_transaction,
|
|
|
|
program_account_index,
|
2021-12-30 06:46:36 -08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-05 00:39:37 -08:00
|
|
|
/// Gets an instruction account of this Instruction
|
2021-12-30 06:46:36 -08:00
|
|
|
pub fn try_borrow_instruction_account<'a, 'b: 'a>(
|
|
|
|
&'a self,
|
|
|
|
transaction_context: &'b TransactionContext,
|
2022-09-06 02:31:40 -07:00
|
|
|
instruction_account_index: IndexOfAccount,
|
2021-12-30 06:46:36 -08:00
|
|
|
) -> Result<BorrowedAccount<'a>, InstructionError> {
|
2022-06-16 09:46:17 -07:00
|
|
|
let index_in_transaction =
|
|
|
|
self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
|
2021-12-30 06:46:36 -08:00
|
|
|
self.try_borrow_account(
|
|
|
|
transaction_context,
|
2022-06-16 09:46:17 -07:00
|
|
|
index_in_transaction,
|
2022-09-05 07:29:02 -07:00
|
|
|
self.get_number_of_program_accounts()
|
2022-02-18 17:06:33 -08:00
|
|
|
.saturating_add(instruction_account_index),
|
2021-12-30 06:46:36 -08:00
|
|
|
)
|
|
|
|
}
|
2022-01-05 00:39:37 -08:00
|
|
|
|
2022-06-16 09:46:17 -07:00
|
|
|
/// Returns whether an instruction account is a signer
|
|
|
|
pub fn is_instruction_account_signer(
|
|
|
|
&self,
|
2022-09-06 02:31:40 -07:00
|
|
|
instruction_account_index: IndexOfAccount,
|
2022-06-16 09:46:17 -07:00
|
|
|
) -> Result<bool, InstructionError> {
|
|
|
|
Ok(self
|
|
|
|
.instruction_accounts
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(instruction_account_index as usize)
|
2022-06-16 09:46:17 -07:00
|
|
|
.ok_or(InstructionError::MissingAccount)?
|
|
|
|
.is_signer)
|
2022-02-02 16:45:57 -08:00
|
|
|
}
|
|
|
|
|
2022-06-16 09:46:17 -07:00
|
|
|
/// Returns whether an instruction account is writable
|
|
|
|
pub fn is_instruction_account_writable(
|
|
|
|
&self,
|
2022-09-06 02:31:40 -07:00
|
|
|
instruction_account_index: IndexOfAccount,
|
2022-06-16 09:46:17 -07:00
|
|
|
) -> Result<bool, InstructionError> {
|
|
|
|
Ok(self
|
|
|
|
.instruction_accounts
|
2022-09-06 02:31:40 -07:00
|
|
|
.get(instruction_account_index as usize)
|
2022-06-16 09:46:17 -07:00
|
|
|
.ok_or(InstructionError::MissingAccount)?
|
|
|
|
.is_writable)
|
2022-02-03 08:19:42 -08:00
|
|
|
}
|
|
|
|
|
2022-06-16 09:46:17 -07:00
|
|
|
/// Calculates the set of all keys of signer instruction accounts in this Instruction
|
2022-09-05 07:29:02 -07:00
|
|
|
pub fn get_signers(
|
|
|
|
&self,
|
|
|
|
transaction_context: &TransactionContext,
|
|
|
|
) -> Result<HashSet<Pubkey>, InstructionError> {
|
2022-02-03 08:19:42 -08:00
|
|
|
let mut result = HashSet::new();
|
|
|
|
for instruction_account in self.instruction_accounts.iter() {
|
|
|
|
if instruction_account.is_signer {
|
|
|
|
result.insert(
|
2022-09-05 07:29:02 -07:00
|
|
|
*transaction_context
|
|
|
|
.get_key_of_account_at_index(instruction_account.index_in_transaction)?,
|
2022-02-03 08:19:42 -08:00
|
|
|
);
|
|
|
|
}
|
2022-02-02 16:45:57 -08:00
|
|
|
}
|
2022-09-05 07:29:02 -07:00
|
|
|
Ok(result)
|
2022-02-02 16:45:57 -08:00
|
|
|
}
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Shared account borrowed from the TransactionContext and an InstructionContext.
|
2021-12-30 06:46:36 -08:00
|
|
|
#[derive(Debug)]
|
2021-12-27 09:49:32 -08:00
|
|
|
pub struct BorrowedAccount<'a> {
|
|
|
|
transaction_context: &'a TransactionContext,
|
|
|
|
instruction_context: &'a InstructionContext,
|
2022-09-06 02:31:40 -07:00
|
|
|
index_in_transaction: IndexOfAccount,
|
|
|
|
index_in_instruction: IndexOfAccount,
|
2021-12-27 09:49:32 -08:00
|
|
|
account: RefMut<'a, AccountSharedData>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> BorrowedAccount<'a> {
|
2021-12-30 06:46:36 -08:00
|
|
|
/// Returns the index of this account (transaction wide)
|
2022-09-06 02:31:40 -07:00
|
|
|
pub fn get_index_in_transaction(&self) -> IndexOfAccount {
|
2021-12-30 06:46:36 -08:00
|
|
|
self.index_in_transaction
|
|
|
|
}
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Returns the public key of this account (transaction wide)
|
|
|
|
pub fn get_key(&self) -> &Pubkey {
|
2022-09-05 07:29:02 -07:00
|
|
|
self.transaction_context
|
|
|
|
.get_key_of_account_at_index(self.index_in_transaction)
|
|
|
|
.unwrap()
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the owner of this account (transaction wide)
|
|
|
|
pub fn get_owner(&self) -> &Pubkey {
|
|
|
|
self.account.owner()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Assignes the owner of this account (transaction wide)
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-05-21 08:47:09 -07:00
|
|
|
pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
|
2022-07-15 00:31:34 -07:00
|
|
|
if self
|
|
|
|
.transaction_context
|
|
|
|
.is_early_verification_of_account_modifications_enabled()
|
|
|
|
{
|
|
|
|
// Only the owner can assign a new owner
|
|
|
|
if !self.is_owned_by_current_program() {
|
|
|
|
return Err(InstructionError::ModifiedProgramId);
|
|
|
|
}
|
|
|
|
// and only if the account is writable
|
|
|
|
if !self.is_writable() {
|
|
|
|
return Err(InstructionError::ModifiedProgramId);
|
|
|
|
}
|
|
|
|
// and only if the account is not executable
|
|
|
|
if self.is_executable() {
|
|
|
|
return Err(InstructionError::ModifiedProgramId);
|
|
|
|
}
|
|
|
|
// and only if the data is zero-initialized or empty
|
|
|
|
if !is_zeroed(self.get_data()) {
|
|
|
|
return Err(InstructionError::ModifiedProgramId);
|
|
|
|
}
|
|
|
|
// don't touch the account if the owner does not change
|
|
|
|
if self.get_owner().to_bytes() == pubkey {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
self.touch()?;
|
|
|
|
}
|
2022-01-03 14:30:56 -08:00
|
|
|
self.account.copy_into_owner_from_slice(pubkey);
|
2022-05-21 08:47:09 -07:00
|
|
|
Ok(())
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
2022-09-02 03:42:06 -07:00
|
|
|
#[cfg(target_os = "solana")]
|
|
|
|
pub fn set_owner<'b>(&mut self, pubkey: &'b [u8]) -> AccountPropertyUpdate<'b> {
|
|
|
|
AccountPropertyUpdate {
|
|
|
|
instruction_account_index: self.index_in_instruction as u16,
|
|
|
|
attribute: TransactionContextAttribute::TransactionAccountOwner as u16,
|
|
|
|
value: pubkey.as_ptr() as u64,
|
|
|
|
_marker: std::marker::PhantomData::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Returns the number of lamports of this account (transaction wide)
|
|
|
|
pub fn get_lamports(&self) -> u64 {
|
|
|
|
self.account.lamports()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Overwrites the number of lamports of this account (transaction wide)
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-05-21 08:47:09 -07:00
|
|
|
pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
|
2022-07-15 00:31:34 -07:00
|
|
|
if self
|
|
|
|
.transaction_context
|
|
|
|
.is_early_verification_of_account_modifications_enabled()
|
|
|
|
{
|
|
|
|
// An account not owned by the program cannot have its balance decrease
|
|
|
|
if !self.is_owned_by_current_program() && lamports < self.get_lamports() {
|
|
|
|
return Err(InstructionError::ExternalAccountLamportSpend);
|
|
|
|
}
|
|
|
|
// The balance of read-only may not change
|
|
|
|
if !self.is_writable() {
|
|
|
|
return Err(InstructionError::ReadonlyLamportChange);
|
|
|
|
}
|
|
|
|
// The balance of executable accounts may not change
|
|
|
|
if self.is_executable() {
|
|
|
|
return Err(InstructionError::ExecutableLamportChange);
|
|
|
|
}
|
|
|
|
// don't touch the account if the lamports do not change
|
|
|
|
if self.get_lamports() == lamports {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
self.touch()?;
|
|
|
|
}
|
2021-12-27 09:49:32 -08:00
|
|
|
self.account.set_lamports(lamports);
|
2022-05-21 08:47:09 -07:00
|
|
|
Ok(())
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
2022-09-02 03:42:06 -07:00
|
|
|
#[cfg(target_os = "solana")]
|
|
|
|
pub fn set_lamports(&mut self, lamports: u64) -> AccountPropertyUpdate<'static> {
|
|
|
|
AccountPropertyUpdate {
|
|
|
|
instruction_account_index: self.index_in_instruction as u16,
|
|
|
|
attribute: TransactionContextAttribute::TransactionAccountLamports as u16,
|
|
|
|
value: lamports,
|
|
|
|
_marker: std::marker::PhantomData::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-30 06:46:36 -08:00
|
|
|
/// Adds lamports to this account (transaction wide)
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-12-30 06:46:36 -08:00
|
|
|
pub fn checked_add_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
|
|
|
|
self.set_lamports(
|
|
|
|
self.get_lamports()
|
|
|
|
.checked_add(lamports)
|
2022-06-28 01:04:54 -07:00
|
|
|
.ok_or(InstructionError::ArithmeticOverflow)?,
|
2022-05-21 08:47:09 -07:00
|
|
|
)
|
2021-12-30 06:46:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Subtracts lamports from this account (transaction wide)
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-12-30 06:46:36 -08:00
|
|
|
pub fn checked_sub_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
|
|
|
|
self.set_lamports(
|
|
|
|
self.get_lamports()
|
|
|
|
.checked_sub(lamports)
|
2022-06-28 01:04:54 -07:00
|
|
|
.ok_or(InstructionError::ArithmeticOverflow)?,
|
2022-05-21 08:47:09 -07:00
|
|
|
)
|
2022-01-05 00:39:37 -08:00
|
|
|
}
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Returns a read-only slice of the account data (transaction wide)
|
|
|
|
pub fn get_data(&self) -> &[u8] {
|
|
|
|
self.account.data()
|
|
|
|
}
|
|
|
|
|
2022-02-03 08:19:42 -08:00
|
|
|
/// Returns a writable slice of the account data (transaction wide)
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-05-21 08:47:09 -07:00
|
|
|
pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
|
2022-07-15 00:31:34 -07:00
|
|
|
self.can_data_be_changed()?;
|
|
|
|
self.touch()?;
|
2022-05-21 08:47:09 -07:00
|
|
|
Ok(self.account.data_as_mut_slice())
|
2022-02-03 08:19:42 -08:00
|
|
|
}
|
|
|
|
|
2022-09-23 17:37:02 -07:00
|
|
|
/// Overwrites the account data and size (transaction wide).
|
|
|
|
///
|
|
|
|
/// Call this when you have an owned buffer and want to replace the account
|
|
|
|
/// data with it.
|
|
|
|
///
|
|
|
|
/// If you have a slice, use set_data_from_slice().
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-09-23 17:37:02 -07:00
|
|
|
pub fn set_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
|
2022-07-15 00:31:34 -07:00
|
|
|
self.can_data_be_resized(data.len())?;
|
|
|
|
self.can_data_be_changed()?;
|
|
|
|
self.touch()?;
|
2022-09-23 17:37:02 -07:00
|
|
|
self.update_accounts_resize_delta(data.len())?;
|
|
|
|
self.account.set_data(data);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Overwrites the account data and size (transaction wide).
|
|
|
|
///
|
|
|
|
/// Call this when you have a slice of data you do not own and want to
|
|
|
|
/// replace the account data with it.
|
|
|
|
///
|
|
|
|
/// If you have an owned buffer (eg Vec<u8>), use set_data().
|
|
|
|
#[cfg(not(target_os = "solana"))]
|
|
|
|
pub fn set_data_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
|
|
|
|
self.can_data_be_resized(data.len())?;
|
|
|
|
self.can_data_be_changed()?;
|
|
|
|
self.touch()?;
|
|
|
|
self.update_accounts_resize_delta(data.len())?;
|
|
|
|
self.account.set_data_from_slice(data);
|
|
|
|
|
2022-05-21 08:47:09 -07:00
|
|
|
Ok(())
|
2021-12-30 06:46:36 -08:00
|
|
|
}
|
|
|
|
|
2022-02-03 08:19:42 -08:00
|
|
|
/// Resizes the account data (transaction wide)
|
|
|
|
///
|
|
|
|
/// Fills it with zeros at the end if is extended or truncates at the end otherwise.
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-07-06 10:27:42 -07:00
|
|
|
pub fn set_data_length(&mut self, new_length: usize) -> Result<(), InstructionError> {
|
2022-07-15 00:31:34 -07:00
|
|
|
self.can_data_be_resized(new_length)?;
|
|
|
|
self.can_data_be_changed()?;
|
|
|
|
// don't touch the account if the length does not change
|
|
|
|
if self.get_data().len() == new_length {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
self.touch()?;
|
2022-09-23 17:37:02 -07:00
|
|
|
self.update_accounts_resize_delta(new_length)?;
|
2022-07-06 10:27:42 -07:00
|
|
|
self.account.data_mut().resize(new_length, 0);
|
2022-05-21 08:47:09 -07:00
|
|
|
Ok(())
|
2022-02-03 08:19:42 -08:00
|
|
|
}
|
2022-01-03 14:30:56 -08:00
|
|
|
|
2022-09-02 03:42:06 -07:00
|
|
|
#[cfg(target_os = "solana")]
|
|
|
|
pub fn set_data_length(&mut self, new_length: usize) -> AccountPropertyUpdate<'static> {
|
|
|
|
AccountPropertyUpdate {
|
|
|
|
instruction_account_index: self.index_in_instruction as u16,
|
|
|
|
attribute: TransactionContextAttribute::TransactionAccountData as u16,
|
|
|
|
value: new_length as u64,
|
|
|
|
_marker: std::marker::PhantomData::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-23 17:37:02 -07:00
|
|
|
/// Appends all elements in a slice to the account
|
|
|
|
#[cfg(not(target_os = "solana"))]
|
|
|
|
pub fn extend_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
|
|
|
|
let new_len = self.get_data().len().saturating_add(data.len());
|
|
|
|
self.can_data_be_resized(new_len)?;
|
|
|
|
self.can_data_be_changed()?;
|
|
|
|
|
|
|
|
if data.is_empty() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.touch()?;
|
|
|
|
self.update_accounts_resize_delta(new_len)?;
|
|
|
|
self.account.data_mut().extend_from_slice(data);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-12-30 06:46:36 -08:00
|
|
|
/// Deserializes the account data into a state
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-12-30 06:46:36 -08:00
|
|
|
pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
|
|
|
|
self.account
|
|
|
|
.deserialize_data()
|
|
|
|
.map_err(|_| InstructionError::InvalidAccountData)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Serializes a state into the account data
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-12-30 06:46:36 -08:00
|
|
|
pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
|
2022-07-15 00:31:34 -07:00
|
|
|
let data = self.get_data_mut()?;
|
2021-12-30 06:46:36 -08:00
|
|
|
let serialized_size =
|
|
|
|
bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
|
|
|
|
if serialized_size > data.len() as u64 {
|
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
2022-01-03 14:30:56 -08:00
|
|
|
bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
|
2022-02-03 08:19:42 -08:00
|
|
|
Ok(())
|
2021-12-30 06:46:36 -08:00
|
|
|
}
|
|
|
|
|
2021-12-27 09:49:32 -08:00
|
|
|
/// Returns whether this account is executable (transaction wide)
|
|
|
|
pub fn is_executable(&self) -> bool {
|
|
|
|
self.account.executable()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Configures whether this account is executable (transaction wide)
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-05-21 08:47:09 -07:00
|
|
|
pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
|
2022-07-15 00:31:34 -07:00
|
|
|
if let Some(rent) = self.transaction_context.rent {
|
|
|
|
// To become executable an account must be rent exempt
|
|
|
|
if !rent.is_exempt(self.get_lamports(), self.get_data().len()) {
|
|
|
|
return Err(InstructionError::ExecutableAccountNotRentExempt);
|
|
|
|
}
|
|
|
|
// Only the owner can set the executable flag
|
|
|
|
if !self.is_owned_by_current_program() {
|
|
|
|
return Err(InstructionError::ExecutableModified);
|
|
|
|
}
|
|
|
|
// and only if the account is writable
|
|
|
|
if !self.is_writable() {
|
|
|
|
return Err(InstructionError::ExecutableModified);
|
|
|
|
}
|
|
|
|
// one can not clear the executable flag
|
|
|
|
if self.is_executable() && !is_executable {
|
|
|
|
return Err(InstructionError::ExecutableModified);
|
|
|
|
}
|
|
|
|
// don't touch the account if the executable flag does not change
|
|
|
|
if self.is_executable() == is_executable {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
self.touch()?;
|
|
|
|
}
|
2021-12-27 09:49:32 -08:00
|
|
|
self.account.set_executable(is_executable);
|
2022-05-21 08:47:09 -07:00
|
|
|
Ok(())
|
2022-01-03 14:30:56 -08:00
|
|
|
}
|
|
|
|
|
2022-09-02 03:42:06 -07:00
|
|
|
#[cfg(target_os = "solana")]
|
|
|
|
pub fn set_executable(&mut self, is_executable: bool) -> AccountPropertyUpdate<'static> {
|
|
|
|
AccountPropertyUpdate {
|
|
|
|
instruction_account_index: self.index_in_instruction as u16,
|
|
|
|
attribute: TransactionContextAttribute::TransactionAccountIsExecutable as u16,
|
|
|
|
value: is_executable as u64,
|
|
|
|
_marker: std::marker::PhantomData::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-03 14:30:56 -08:00
|
|
|
/// Returns the rent epoch of this account (transaction wide)
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-01-03 14:30:56 -08:00
|
|
|
pub fn get_rent_epoch(&self) -> u64 {
|
|
|
|
self.account.rent_epoch()
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns whether this account is a signer (instruction wide)
|
|
|
|
pub fn is_signer(&self) -> bool {
|
2022-09-05 07:29:02 -07:00
|
|
|
if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
|
2022-06-16 09:46:17 -07:00
|
|
|
return false;
|
|
|
|
}
|
2022-02-03 08:19:42 -08:00
|
|
|
self.instruction_context
|
2022-06-16 09:46:17 -07:00
|
|
|
.is_instruction_account_signer(
|
|
|
|
self.index_in_instruction
|
2022-09-05 07:29:02 -07:00
|
|
|
.saturating_sub(self.instruction_context.get_number_of_program_accounts()),
|
2022-06-16 09:46:17 -07:00
|
|
|
)
|
2022-02-03 08:19:42 -08:00
|
|
|
.unwrap_or_default()
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns whether this account is writable (instruction wide)
|
|
|
|
pub fn is_writable(&self) -> bool {
|
2022-09-05 07:29:02 -07:00
|
|
|
if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
|
2022-06-16 09:46:17 -07:00
|
|
|
return false;
|
|
|
|
}
|
2022-02-03 08:19:42 -08:00
|
|
|
self.instruction_context
|
2022-06-16 09:46:17 -07:00
|
|
|
.is_instruction_account_writable(
|
|
|
|
self.index_in_instruction
|
2022-09-05 07:29:02 -07:00
|
|
|
.saturating_sub(self.instruction_context.get_number_of_program_accounts()),
|
2022-06-16 09:46:17 -07:00
|
|
|
)
|
2022-02-03 08:19:42 -08:00
|
|
|
.unwrap_or_default()
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
2022-07-15 00:31:34 -07:00
|
|
|
|
|
|
|
/// Returns true if the owner of this account is the current `InstructionContext`s last program (instruction wide)
|
|
|
|
pub fn is_owned_by_current_program(&self) -> bool {
|
|
|
|
self.instruction_context
|
|
|
|
.get_last_program_key(self.transaction_context)
|
|
|
|
.map(|key| key == self.get_owner())
|
|
|
|
.unwrap_or_default()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns an error if the account data can not be mutated by the current program
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-07-15 00:31:34 -07:00
|
|
|
pub fn can_data_be_changed(&self) -> Result<(), InstructionError> {
|
|
|
|
if !self
|
|
|
|
.transaction_context
|
|
|
|
.is_early_verification_of_account_modifications_enabled()
|
|
|
|
{
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
// Only non-executable accounts data can be changed
|
|
|
|
if self.is_executable() {
|
|
|
|
return Err(InstructionError::ExecutableDataModified);
|
|
|
|
}
|
|
|
|
// and only if the account is writable
|
|
|
|
if !self.is_writable() {
|
|
|
|
return Err(InstructionError::ReadonlyDataModified);
|
|
|
|
}
|
|
|
|
// and only if we are the owner
|
|
|
|
if !self.is_owned_by_current_program() {
|
|
|
|
return Err(InstructionError::ExternalAccountDataModified);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns an error if the account data can not be resized to the given length
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-07-15 00:31:34 -07:00
|
|
|
pub fn can_data_be_resized(&self, new_length: usize) -> Result<(), InstructionError> {
|
|
|
|
if !self
|
|
|
|
.transaction_context
|
|
|
|
.is_early_verification_of_account_modifications_enabled()
|
|
|
|
{
|
|
|
|
return Ok(());
|
|
|
|
}
|
2022-08-29 11:30:48 -07:00
|
|
|
let old_length = self.get_data().len();
|
2022-07-15 00:31:34 -07:00
|
|
|
// Only the owner can change the length of the data
|
2022-08-29 11:30:48 -07:00
|
|
|
if new_length != old_length && !self.is_owned_by_current_program() {
|
2022-07-15 00:31:34 -07:00
|
|
|
return Err(InstructionError::AccountDataSizeChanged);
|
|
|
|
}
|
|
|
|
// The new length can not exceed the maximum permitted length
|
|
|
|
if new_length > MAX_PERMITTED_DATA_LENGTH as usize {
|
|
|
|
return Err(InstructionError::InvalidRealloc);
|
|
|
|
}
|
2022-08-29 11:30:48 -07:00
|
|
|
if self
|
|
|
|
.transaction_context
|
|
|
|
.is_cap_accounts_data_allocations_per_transaction_enabled
|
|
|
|
{
|
|
|
|
// The resize can not exceed the per-transaction maximum
|
|
|
|
let length_delta = (new_length as i64).saturating_sub(old_length as i64);
|
|
|
|
if self
|
|
|
|
.transaction_context
|
|
|
|
.accounts_resize_delta()?
|
|
|
|
.saturating_add(length_delta)
|
|
|
|
> MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
|
|
|
|
{
|
|
|
|
return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
|
|
|
|
}
|
|
|
|
}
|
2022-07-15 00:31:34 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-07-15 00:31:34 -07:00
|
|
|
fn touch(&self) -> Result<(), InstructionError> {
|
|
|
|
if self
|
|
|
|
.transaction_context
|
|
|
|
.is_early_verification_of_account_modifications_enabled()
|
|
|
|
{
|
|
|
|
*self
|
|
|
|
.transaction_context
|
|
|
|
.account_touched_flags
|
|
|
|
.try_borrow_mut()
|
|
|
|
.map_err(|_| InstructionError::GenericError)?
|
2022-09-06 02:31:40 -07:00
|
|
|
.get_mut(self.index_in_transaction as usize)
|
2022-07-15 00:31:34 -07:00
|
|
|
.ok_or(InstructionError::NotEnoughAccountKeys)? = true;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-09-23 17:37:02 -07:00
|
|
|
|
|
|
|
#[cfg(not(target_os = "solana"))]
|
|
|
|
fn update_accounts_resize_delta(&mut self, new_len: usize) -> Result<(), InstructionError> {
|
|
|
|
let mut accounts_resize_delta = self
|
|
|
|
.transaction_context
|
|
|
|
.accounts_resize_delta
|
|
|
|
.try_borrow_mut()
|
|
|
|
.map_err(|_| InstructionError::GenericError)?;
|
|
|
|
*accounts_resize_delta = accounts_resize_delta
|
|
|
|
.saturating_add((new_len as i64).saturating_sub(self.get_data().len() as i64));
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-12-27 09:49:32 -08:00
|
|
|
}
|
2022-03-22 15:17:05 -07:00
|
|
|
|
|
|
|
/// Everything that needs to be recorded from a TransactionContext after execution
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-03-22 15:17:05 -07:00
|
|
|
pub struct ExecutionRecord {
|
|
|
|
pub accounts: Vec<TransactionAccount>,
|
|
|
|
pub return_data: TransactionReturnData,
|
2022-08-23 05:58:32 -07:00
|
|
|
pub touched_account_count: u64,
|
2022-07-15 00:31:34 -07:00
|
|
|
pub accounts_resize_delta: i64,
|
2022-03-22 15:17:05 -07:00
|
|
|
}
|
2022-07-06 10:27:42 -07:00
|
|
|
|
2022-03-22 15:17:05 -07:00
|
|
|
/// Used by the bank in the runtime to write back the processed accounts and recorded instructions
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-03-22 15:17:05 -07:00
|
|
|
impl From<TransactionContext> for ExecutionRecord {
|
|
|
|
fn from(context: TransactionContext) -> Self {
|
2022-07-15 00:31:34 -07:00
|
|
|
let account_touched_flags = context
|
|
|
|
.account_touched_flags
|
|
|
|
.try_borrow()
|
|
|
|
.expect("borrowing transaction_context.account_touched_flags failed");
|
2022-08-23 05:58:32 -07:00
|
|
|
let touched_account_count = account_touched_flags
|
|
|
|
.iter()
|
|
|
|
.fold(0u64, |accumulator, was_touched| {
|
|
|
|
accumulator.saturating_add(*was_touched as u64)
|
|
|
|
});
|
2022-03-22 15:17:05 -07:00
|
|
|
Self {
|
|
|
|
accounts: Vec::from(Pin::into_inner(context.account_keys))
|
|
|
|
.into_iter()
|
|
|
|
.zip(
|
|
|
|
Vec::from(Pin::into_inner(context.accounts))
|
|
|
|
.into_iter()
|
|
|
|
.map(|account| account.into_inner()),
|
|
|
|
)
|
|
|
|
.collect(),
|
|
|
|
return_data: context.return_data,
|
2022-08-23 05:58:32 -07:00
|
|
|
touched_account_count,
|
2022-07-15 00:31:34 -07:00
|
|
|
accounts_resize_delta: RefCell::into_inner(context.accounts_resize_delta),
|
2022-03-22 15:17:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-15 00:31:34 -07:00
|
|
|
|
2022-08-31 08:47:47 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-07-15 00:31:34 -07:00
|
|
|
fn is_zeroed(buf: &[u8]) -> bool {
|
|
|
|
const ZEROS_LEN: usize = 1024;
|
|
|
|
const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
|
|
|
|
let mut chunks = buf.chunks_exact(ZEROS_LEN);
|
|
|
|
|
|
|
|
#[allow(clippy::indexing_slicing)]
|
|
|
|
{
|
|
|
|
chunks.all(|chunk| chunk == &ZEROS[..])
|
|
|
|
&& chunks.remainder() == &ZEROS[..chunks.remainder().len()]
|
|
|
|
}
|
|
|
|
}
|