clone_solana_transaction_context/
lib.rs

1//! Data shared between program runtime and built-in programs as well as SBF programs.
2#![deny(clippy::indexing_slicing)]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5#[cfg(all(
6    not(target_os = "solana"),
7    feature = "debug-signature",
8    debug_assertions
9))]
10use clone_solana_signature::Signature;
11#[cfg(not(target_os = "solana"))]
12use {clone_solana_account::WritableAccount, clone_solana_rent::Rent, std::mem::MaybeUninit};
13use {
14    clone_solana_account::{AccountSharedData, ReadableAccount},
15    clone_solana_instruction::error::InstructionError,
16    clone_solana_instructions_sysvar as instructions,
17    clone_solana_pubkey::Pubkey,
18    std::{
19        cell::{Ref, RefCell, RefMut},
20        collections::HashSet,
21        pin::Pin,
22        rc::Rc,
23    },
24};
25
26// Inlined to avoid clone_solana_system_interface dep
27#[cfg(not(target_os = "solana"))]
28const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024;
29#[cfg(test)]
30static_assertions::const_assert_eq!(
31    MAX_PERMITTED_DATA_LENGTH,
32    clone_solana_system_interface::MAX_PERMITTED_DATA_LENGTH
33);
34
35// Inlined to avoid clone_solana_system_interface dep
36#[cfg(not(target_os = "solana"))]
37const MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION: i64 =
38    MAX_PERMITTED_DATA_LENGTH as i64 * 2;
39#[cfg(test)]
40static_assertions::const_assert_eq!(
41    MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION,
42    clone_solana_system_interface::MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
43);
44
45// Inlined to avoid clone_solana_account_info dep
46#[cfg(not(target_os = "solana"))]
47const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10;
48#[cfg(test)]
49static_assertions::const_assert_eq!(
50    MAX_PERMITTED_DATA_INCREASE,
51    clone_solana_account_info::MAX_PERMITTED_DATA_INCREASE
52);
53
54/// Index of an account inside of the TransactionContext or an InstructionContext.
55pub type IndexOfAccount = u16;
56
57/// Contains account meta data which varies between instruction.
58///
59/// It also contains indices to other structures for faster lookup.
60#[derive(Clone, Debug, Eq, PartialEq)]
61pub struct InstructionAccount {
62    /// Points to the account and its key in the `TransactionContext`
63    pub index_in_transaction: IndexOfAccount,
64    /// Points to the first occurrence in the parent `InstructionContext`
65    ///
66    /// This excludes the program accounts.
67    pub index_in_caller: IndexOfAccount,
68    /// Points to the first occurrence in the current `InstructionContext`
69    ///
70    /// This excludes the program accounts.
71    pub index_in_callee: IndexOfAccount,
72    /// Is this account supposed to sign
73    pub is_signer: bool,
74    /// Is this account allowed to become writable
75    pub is_writable: bool,
76}
77
78/// An account key and the matching account
79pub type TransactionAccount = (Pubkey, AccountSharedData);
80
81#[derive(Clone, Debug, PartialEq)]
82pub struct TransactionAccounts {
83    accounts: Vec<RefCell<AccountSharedData>>,
84    touched_flags: RefCell<Box<[bool]>>,
85}
86
87impl TransactionAccounts {
88    #[cfg(not(target_os = "solana"))]
89    fn new(accounts: Vec<RefCell<AccountSharedData>>) -> TransactionAccounts {
90        TransactionAccounts {
91            touched_flags: RefCell::new(vec![false; accounts.len()].into_boxed_slice()),
92            accounts,
93        }
94    }
95
96    fn len(&self) -> usize {
97        self.accounts.len()
98    }
99
100    fn get(&self, index: IndexOfAccount) -> Option<&RefCell<AccountSharedData>> {
101        self.accounts.get(index as usize)
102    }
103
104    #[cfg(not(target_os = "solana"))]
105    pub fn touch(&self, index: IndexOfAccount) -> Result<(), InstructionError> {
106        *self
107            .touched_flags
108            .borrow_mut()
109            .get_mut(index as usize)
110            .ok_or(InstructionError::NotEnoughAccountKeys)? = true;
111        Ok(())
112    }
113
114    #[cfg(not(target_os = "solana"))]
115    pub fn touched_count(&self) -> usize {
116        self.touched_flags
117            .borrow()
118            .iter()
119            .fold(0usize, |accumulator, was_touched| {
120                accumulator.saturating_add(*was_touched as usize)
121            })
122    }
123
124    pub fn try_borrow(
125        &self,
126        index: IndexOfAccount,
127    ) -> Result<Ref<'_, AccountSharedData>, InstructionError> {
128        self.accounts
129            .get(index as usize)
130            .ok_or(InstructionError::MissingAccount)?
131            .try_borrow()
132            .map_err(|_| InstructionError::AccountBorrowFailed)
133    }
134
135    pub fn into_accounts(self) -> Vec<AccountSharedData> {
136        self.accounts
137            .into_iter()
138            .map(|account| account.into_inner())
139            .collect()
140    }
141}
142
143/// Loaded transaction shared between runtime and programs.
144///
145/// This context is valid for the entire duration of a transaction being processed.
146#[derive(Debug, Clone, PartialEq)]
147pub struct TransactionContext {
148    account_keys: Pin<Box<[Pubkey]>>,
149    accounts: Rc<TransactionAccounts>,
150    instruction_stack_capacity: usize,
151    instruction_trace_capacity: usize,
152    instruction_stack: Vec<usize>,
153    instruction_trace: Vec<InstructionContext>,
154    top_level_instruction_index: usize,
155    return_data: TransactionReturnData,
156    accounts_resize_delta: RefCell<i64>,
157    #[cfg(not(target_os = "solana"))]
158    remove_accounts_executable_flag_checks: bool,
159    #[cfg(not(target_os = "solana"))]
160    rent: Rent,
161    /// Useful for debugging to filter by or to look it up on the explorer
162    #[cfg(all(
163        not(target_os = "solana"),
164        feature = "debug-signature",
165        debug_assertions
166    ))]
167    signature: Signature,
168}
169
170impl TransactionContext {
171    /// Constructs a new TransactionContext
172    #[cfg(not(target_os = "solana"))]
173    pub fn new(
174        transaction_accounts: Vec<TransactionAccount>,
175        rent: Rent,
176        instruction_stack_capacity: usize,
177        instruction_trace_capacity: usize,
178    ) -> Self {
179        let (account_keys, accounts): (Vec<_>, Vec<_>) = transaction_accounts
180            .into_iter()
181            .map(|(key, account)| (key, RefCell::new(account)))
182            .unzip();
183        Self {
184            account_keys: Pin::new(account_keys.into_boxed_slice()),
185            accounts: Rc::new(TransactionAccounts::new(accounts)),
186            instruction_stack_capacity,
187            instruction_trace_capacity,
188            instruction_stack: Vec::with_capacity(instruction_stack_capacity),
189            instruction_trace: vec![InstructionContext::default()],
190            top_level_instruction_index: 0,
191            return_data: TransactionReturnData::default(),
192            accounts_resize_delta: RefCell::new(0),
193            remove_accounts_executable_flag_checks: true,
194            rent,
195            #[cfg(all(
196                not(target_os = "solana"),
197                feature = "debug-signature",
198                debug_assertions
199            ))]
200            signature: Signature::default(),
201        }
202    }
203
204    #[cfg(not(target_os = "solana"))]
205    pub fn set_remove_accounts_executable_flag_checks(&mut self, enabled: bool) {
206        self.remove_accounts_executable_flag_checks = enabled;
207    }
208
209    /// Used in mock_process_instruction
210    #[cfg(not(target_os = "solana"))]
211    pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
212        if !self.instruction_stack.is_empty() {
213            return Err(InstructionError::CallDepth);
214        }
215
216        Ok(Rc::try_unwrap(self.accounts)
217            .expect("transaction_context.accounts has unexpected outstanding refs")
218            .into_accounts())
219    }
220
221    #[cfg(not(target_os = "solana"))]
222    pub fn accounts(&self) -> &Rc<TransactionAccounts> {
223        &self.accounts
224    }
225
226    /// Stores the signature of the current transaction
227    #[cfg(all(
228        not(target_os = "solana"),
229        feature = "debug-signature",
230        debug_assertions
231    ))]
232    pub fn set_signature(&mut self, signature: &Signature) {
233        self.signature = *signature;
234    }
235
236    /// Returns the signature of the current transaction
237    #[cfg(all(
238        not(target_os = "solana"),
239        feature = "debug-signature",
240        debug_assertions
241    ))]
242    pub fn get_signature(&self) -> &Signature {
243        &self.signature
244    }
245
246    /// Returns the total number of accounts loaded in this Transaction
247    pub fn get_number_of_accounts(&self) -> IndexOfAccount {
248        self.accounts.len() as IndexOfAccount
249    }
250
251    /// Searches for an account by its key
252    pub fn get_key_of_account_at_index(
253        &self,
254        index_in_transaction: IndexOfAccount,
255    ) -> Result<&Pubkey, InstructionError> {
256        self.account_keys
257            .get(index_in_transaction as usize)
258            .ok_or(InstructionError::NotEnoughAccountKeys)
259    }
260
261    /// Searches for an account by its key
262    #[cfg(all(
263        not(target_os = "solana"),
264        any(test, feature = "dev-context-only-utils")
265    ))]
266    pub fn get_account_at_index(
267        &self,
268        index_in_transaction: IndexOfAccount,
269    ) -> Result<&RefCell<AccountSharedData>, InstructionError> {
270        self.accounts
271            .get(index_in_transaction)
272            .ok_or(InstructionError::NotEnoughAccountKeys)
273    }
274
275    /// Searches for an account by its key
276    pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
277        self.account_keys
278            .iter()
279            .position(|key| key == pubkey)
280            .map(|index| index as IndexOfAccount)
281    }
282
283    /// Searches for a program account by its key
284    pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option<IndexOfAccount> {
285        self.account_keys
286            .iter()
287            .rposition(|key| key == pubkey)
288            .map(|index| index as IndexOfAccount)
289    }
290
291    /// Gets the max length of the InstructionContext trace
292    pub fn get_instruction_trace_capacity(&self) -> usize {
293        self.instruction_trace_capacity
294    }
295
296    /// Returns the instruction trace length.
297    ///
298    /// Not counting the last empty InstructionContext which is always pre-reserved for the next instruction.
299    /// See also `get_next_instruction_context()`.
300    pub fn get_instruction_trace_length(&self) -> usize {
301        self.instruction_trace.len().saturating_sub(1)
302    }
303
304    /// Gets an InstructionContext by its index in the trace
305    pub fn get_instruction_context_at_index_in_trace(
306        &self,
307        index_in_trace: usize,
308    ) -> Result<&InstructionContext, InstructionError> {
309        self.instruction_trace
310            .get(index_in_trace)
311            .ok_or(InstructionError::CallDepth)
312    }
313
314    /// Gets an InstructionContext by its nesting level in the stack
315    pub fn get_instruction_context_at_nesting_level(
316        &self,
317        nesting_level: usize,
318    ) -> Result<&InstructionContext, InstructionError> {
319        let index_in_trace = *self
320            .instruction_stack
321            .get(nesting_level)
322            .ok_or(InstructionError::CallDepth)?;
323        let instruction_context = self.get_instruction_context_at_index_in_trace(index_in_trace)?;
324        debug_assert_eq!(instruction_context.nesting_level, nesting_level);
325        Ok(instruction_context)
326    }
327
328    /// Gets the max height of the InstructionContext stack
329    pub fn get_instruction_stack_capacity(&self) -> usize {
330        self.instruction_stack_capacity
331    }
332
333    /// Gets instruction stack height, top-level instructions are height
334    /// `clone_solana_sdk::instruction::TRANSACTION_LEVEL_STACK_HEIGHT`
335    pub fn get_instruction_context_stack_height(&self) -> usize {
336        self.instruction_stack.len()
337    }
338
339    /// Returns the current InstructionContext
340    pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> {
341        let level = self
342            .get_instruction_context_stack_height()
343            .checked_sub(1)
344            .ok_or(InstructionError::CallDepth)?;
345        self.get_instruction_context_at_nesting_level(level)
346    }
347
348    /// Returns the InstructionContext to configure for the next invocation.
349    ///
350    /// The last InstructionContext is always empty and pre-reserved for the next instruction.
351    pub fn get_next_instruction_context(
352        &mut self,
353    ) -> Result<&mut InstructionContext, InstructionError> {
354        self.instruction_trace
355            .last_mut()
356            .ok_or(InstructionError::CallDepth)
357    }
358
359    /// Pushes the next InstructionContext
360    #[cfg(not(target_os = "solana"))]
361    pub fn push(&mut self) -> Result<(), InstructionError> {
362        let nesting_level = self.get_instruction_context_stack_height();
363        let caller_instruction_context = self
364            .instruction_trace
365            .last()
366            .ok_or(InstructionError::CallDepth)?;
367        let callee_instruction_accounts_lamport_sum =
368            self.instruction_accounts_lamport_sum(caller_instruction_context)?;
369        if !self.instruction_stack.is_empty() {
370            let caller_instruction_context = self.get_current_instruction_context()?;
371            let original_caller_instruction_accounts_lamport_sum =
372                caller_instruction_context.instruction_accounts_lamport_sum;
373            let current_caller_instruction_accounts_lamport_sum =
374                self.instruction_accounts_lamport_sum(caller_instruction_context)?;
375            if original_caller_instruction_accounts_lamport_sum
376                != current_caller_instruction_accounts_lamport_sum
377            {
378                return Err(InstructionError::UnbalancedInstruction);
379            }
380        }
381        {
382            let instruction_context = self.get_next_instruction_context()?;
383            instruction_context.nesting_level = nesting_level;
384            instruction_context.instruction_accounts_lamport_sum =
385                callee_instruction_accounts_lamport_sum;
386        }
387        let index_in_trace = self.get_instruction_trace_length();
388        if index_in_trace >= self.instruction_trace_capacity {
389            return Err(InstructionError::MaxInstructionTraceLengthExceeded);
390        }
391        self.instruction_trace.push(InstructionContext::default());
392        if nesting_level >= self.instruction_stack_capacity {
393            return Err(InstructionError::CallDepth);
394        }
395        self.instruction_stack.push(index_in_trace);
396        if let Some(index_in_transaction) = self.find_index_of_account(&instructions::id()) {
397            let mut mut_account_ref = self
398                .accounts
399                .get(index_in_transaction)
400                .ok_or(InstructionError::NotEnoughAccountKeys)?
401                .try_borrow_mut()
402                .map_err(|_| InstructionError::AccountBorrowFailed)?;
403            instructions::store_current_index(
404                mut_account_ref.data_as_mut_slice(),
405                self.top_level_instruction_index as u16,
406            );
407        }
408        Ok(())
409    }
410
411    /// Pops the current InstructionContext
412    #[cfg(not(target_os = "solana"))]
413    pub fn pop(&mut self) -> Result<(), InstructionError> {
414        if self.instruction_stack.is_empty() {
415            return Err(InstructionError::CallDepth);
416        }
417        // Verify (before we pop) that the total sum of all lamports in this instruction did not change
418        let detected_an_unbalanced_instruction =
419            self.get_current_instruction_context()
420                .and_then(|instruction_context| {
421                    // Verify all executable accounts have no outstanding refs
422                    for index_in_transaction in instruction_context.program_accounts.iter() {
423                        self.accounts
424                            .get(*index_in_transaction)
425                            .ok_or(InstructionError::NotEnoughAccountKeys)?
426                            .try_borrow_mut()
427                            .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
428                    }
429                    self.instruction_accounts_lamport_sum(instruction_context)
430                        .map(|instruction_accounts_lamport_sum| {
431                            instruction_context.instruction_accounts_lamport_sum
432                                != instruction_accounts_lamport_sum
433                        })
434                });
435        // Always pop, even if we `detected_an_unbalanced_instruction`
436        self.instruction_stack.pop();
437        if self.instruction_stack.is_empty() {
438            self.top_level_instruction_index = self.top_level_instruction_index.saturating_add(1);
439        }
440        if detected_an_unbalanced_instruction? {
441            Err(InstructionError::UnbalancedInstruction)
442        } else {
443            Ok(())
444        }
445    }
446
447    /// Gets the return data of the current InstructionContext or any above
448    pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
449        (&self.return_data.program_id, &self.return_data.data)
450    }
451
452    /// Set the return data of the current InstructionContext
453    pub fn set_return_data(
454        &mut self,
455        program_id: Pubkey,
456        data: Vec<u8>,
457    ) -> Result<(), InstructionError> {
458        self.return_data = TransactionReturnData { program_id, data };
459        Ok(())
460    }
461
462    /// Calculates the sum of all lamports within an instruction
463    #[cfg(not(target_os = "solana"))]
464    fn instruction_accounts_lamport_sum(
465        &self,
466        instruction_context: &InstructionContext,
467    ) -> Result<u128, InstructionError> {
468        let mut instruction_accounts_lamport_sum: u128 = 0;
469        for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts()
470        {
471            if instruction_context
472                .is_instruction_account_duplicate(instruction_account_index)?
473                .is_some()
474            {
475                continue; // Skip duplicate account
476            }
477            let index_in_transaction = instruction_context
478                .get_index_of_instruction_account_in_transaction(instruction_account_index)?;
479            instruction_accounts_lamport_sum = (self
480                .accounts
481                .get(index_in_transaction)
482                .ok_or(InstructionError::NotEnoughAccountKeys)?
483                .try_borrow()
484                .map_err(|_| InstructionError::AccountBorrowOutstanding)?
485                .lamports() as u128)
486                .checked_add(instruction_accounts_lamport_sum)
487                .ok_or(InstructionError::ArithmeticOverflow)?;
488        }
489        Ok(instruction_accounts_lamport_sum)
490    }
491
492    /// Returns the accounts resize delta
493    pub fn accounts_resize_delta(&self) -> Result<i64, InstructionError> {
494        self.accounts_resize_delta
495            .try_borrow()
496            .map_err(|_| InstructionError::GenericError)
497            .map(|value_ref| *value_ref)
498    }
499
500    /// Returns a new account data write access handler
501    pub fn account_data_write_access_handler(&self) -> Box<dyn Fn(u64) -> Result<u64, ()>> {
502        let accounts = Rc::clone(&self.accounts);
503        Box::new(move |index_in_transaction| {
504            // The two calls below can't really fail. If they fail because of a bug,
505            // whatever is writing will trigger an EbpfError::AccessViolation like
506            // if the region was readonly, and the transaction will fail gracefully.
507            let mut account = accounts
508                .accounts
509                .get(index_in_transaction as usize)
510                .ok_or(())?
511                .try_borrow_mut()
512                .map_err(|_| ())?;
513            accounts
514                .touch(index_in_transaction as IndexOfAccount)
515                .map_err(|_| ())?;
516
517            if account.is_shared() {
518                // See BorrowedAccount::make_data_mut() as to why we reserve extra
519                // MAX_PERMITTED_DATA_INCREASE bytes here.
520                account.reserve(MAX_PERMITTED_DATA_INCREASE);
521            }
522            Ok(account.data_as_mut_slice().as_mut_ptr() as u64)
523        })
524    }
525}
526
527/// Return data at the end of a transaction
528#[cfg_attr(
529    feature = "serde",
530    derive(serde_derive::Deserialize, serde_derive::Serialize)
531)]
532#[derive(Clone, Debug, Default, PartialEq, Eq)]
533pub struct TransactionReturnData {
534    pub program_id: Pubkey,
535    pub data: Vec<u8>,
536}
537
538/// Loaded instruction shared between runtime and programs.
539///
540/// This context is valid for the entire duration of a (possibly cross program) instruction being processed.
541#[derive(Debug, Clone, Default, Eq, PartialEq)]
542pub struct InstructionContext {
543    nesting_level: usize,
544    instruction_accounts_lamport_sum: u128,
545    program_accounts: Vec<IndexOfAccount>,
546    instruction_accounts: Vec<InstructionAccount>,
547    instruction_data: Vec<u8>,
548}
549
550impl InstructionContext {
551    /// Used together with TransactionContext::get_next_instruction_context()
552    #[cfg(not(target_os = "solana"))]
553    pub fn configure(
554        &mut self,
555        program_accounts: &[IndexOfAccount],
556        instruction_accounts: &[InstructionAccount],
557        instruction_data: &[u8],
558    ) {
559        self.program_accounts = program_accounts.to_vec();
560        self.instruction_accounts = instruction_accounts.to_vec();
561        self.instruction_data = instruction_data.to_vec();
562    }
563
564    /// How many Instructions were on the stack after this one was pushed
565    ///
566    /// That is the number of nested parent Instructions plus one (itself).
567    pub fn get_stack_height(&self) -> usize {
568        self.nesting_level.saturating_add(1)
569    }
570
571    /// Number of program accounts
572    pub fn get_number_of_program_accounts(&self) -> IndexOfAccount {
573        self.program_accounts.len() as IndexOfAccount
574    }
575
576    /// Number of accounts in this Instruction (without program accounts)
577    pub fn get_number_of_instruction_accounts(&self) -> IndexOfAccount {
578        self.instruction_accounts.len() as IndexOfAccount
579    }
580
581    /// Assert that enough accounts were supplied to this Instruction
582    pub fn check_number_of_instruction_accounts(
583        &self,
584        expected_at_least: IndexOfAccount,
585    ) -> Result<(), InstructionError> {
586        if self.get_number_of_instruction_accounts() < expected_at_least {
587            Err(InstructionError::NotEnoughAccountKeys)
588        } else {
589            Ok(())
590        }
591    }
592
593    /// Data parameter for the programs `process_instruction` handler
594    pub fn get_instruction_data(&self) -> &[u8] {
595        &self.instruction_data
596    }
597
598    /// Searches for a program account by its key
599    pub fn find_index_of_program_account(
600        &self,
601        transaction_context: &TransactionContext,
602        pubkey: &Pubkey,
603    ) -> Option<IndexOfAccount> {
604        self.program_accounts
605            .iter()
606            .position(|index_in_transaction| {
607                transaction_context
608                    .account_keys
609                    .get(*index_in_transaction as usize)
610                    == Some(pubkey)
611            })
612            .map(|index| index as IndexOfAccount)
613    }
614
615    /// Searches for an instruction account by its key
616    pub fn find_index_of_instruction_account(
617        &self,
618        transaction_context: &TransactionContext,
619        pubkey: &Pubkey,
620    ) -> Option<IndexOfAccount> {
621        self.instruction_accounts
622            .iter()
623            .position(|instruction_account| {
624                transaction_context
625                    .account_keys
626                    .get(instruction_account.index_in_transaction as usize)
627                    == Some(pubkey)
628            })
629            .map(|index| index as IndexOfAccount)
630    }
631
632    /// Translates the given instruction wide program_account_index into a transaction wide index
633    pub fn get_index_of_program_account_in_transaction(
634        &self,
635        program_account_index: IndexOfAccount,
636    ) -> Result<IndexOfAccount, InstructionError> {
637        Ok(*self
638            .program_accounts
639            .get(program_account_index as usize)
640            .ok_or(InstructionError::NotEnoughAccountKeys)?)
641    }
642
643    /// Translates the given instruction wide instruction_account_index into a transaction wide index
644    pub fn get_index_of_instruction_account_in_transaction(
645        &self,
646        instruction_account_index: IndexOfAccount,
647    ) -> Result<IndexOfAccount, InstructionError> {
648        Ok(self
649            .instruction_accounts
650            .get(instruction_account_index as usize)
651            .ok_or(InstructionError::NotEnoughAccountKeys)?
652            .index_in_transaction as IndexOfAccount)
653    }
654
655    /// Returns `Some(instruction_account_index)` if this is a duplicate
656    /// and `None` if it is the first account with this key
657    pub fn is_instruction_account_duplicate(
658        &self,
659        instruction_account_index: IndexOfAccount,
660    ) -> Result<Option<IndexOfAccount>, InstructionError> {
661        let index_in_callee = self
662            .instruction_accounts
663            .get(instruction_account_index as usize)
664            .ok_or(InstructionError::NotEnoughAccountKeys)?
665            .index_in_callee;
666        Ok(if index_in_callee == instruction_account_index {
667            None
668        } else {
669            Some(index_in_callee)
670        })
671    }
672
673    /// Gets the key of the last program account of this Instruction
674    pub fn get_last_program_key<'a, 'b: 'a>(
675        &'a self,
676        transaction_context: &'b TransactionContext,
677    ) -> Result<&'b Pubkey, InstructionError> {
678        self.get_index_of_program_account_in_transaction(
679            self.get_number_of_program_accounts().saturating_sub(1),
680        )
681        .and_then(|index_in_transaction| {
682            transaction_context.get_key_of_account_at_index(index_in_transaction)
683        })
684    }
685
686    fn try_borrow_account<'a, 'b: 'a>(
687        &'a self,
688        transaction_context: &'b TransactionContext,
689        index_in_transaction: IndexOfAccount,
690        index_in_instruction: IndexOfAccount,
691    ) -> Result<BorrowedAccount<'a>, InstructionError> {
692        let account = transaction_context
693            .accounts
694            .get(index_in_transaction)
695            .ok_or(InstructionError::MissingAccount)?
696            .try_borrow_mut()
697            .map_err(|_| InstructionError::AccountBorrowFailed)?;
698        Ok(BorrowedAccount {
699            transaction_context,
700            instruction_context: self,
701            index_in_transaction,
702            index_in_instruction,
703            account,
704        })
705    }
706
707    /// Gets the last program account of this Instruction
708    pub fn try_borrow_last_program_account<'a, 'b: 'a>(
709        &'a self,
710        transaction_context: &'b TransactionContext,
711    ) -> Result<BorrowedAccount<'a>, InstructionError> {
712        let result = self.try_borrow_program_account(
713            transaction_context,
714            self.get_number_of_program_accounts().saturating_sub(1),
715        );
716        debug_assert!(result.is_ok());
717        result
718    }
719
720    /// Tries to borrow a program account from this Instruction
721    pub fn try_borrow_program_account<'a, 'b: 'a>(
722        &'a self,
723        transaction_context: &'b TransactionContext,
724        program_account_index: IndexOfAccount,
725    ) -> Result<BorrowedAccount<'a>, InstructionError> {
726        let index_in_transaction =
727            self.get_index_of_program_account_in_transaction(program_account_index)?;
728        self.try_borrow_account(
729            transaction_context,
730            index_in_transaction,
731            program_account_index,
732        )
733    }
734
735    /// Gets an instruction account of this Instruction
736    pub fn try_borrow_instruction_account<'a, 'b: 'a>(
737        &'a self,
738        transaction_context: &'b TransactionContext,
739        instruction_account_index: IndexOfAccount,
740    ) -> Result<BorrowedAccount<'a>, InstructionError> {
741        let index_in_transaction =
742            self.get_index_of_instruction_account_in_transaction(instruction_account_index)?;
743        self.try_borrow_account(
744            transaction_context,
745            index_in_transaction,
746            self.get_number_of_program_accounts()
747                .saturating_add(instruction_account_index),
748        )
749    }
750
751    /// Returns whether an instruction account is a signer
752    pub fn is_instruction_account_signer(
753        &self,
754        instruction_account_index: IndexOfAccount,
755    ) -> Result<bool, InstructionError> {
756        Ok(self
757            .instruction_accounts
758            .get(instruction_account_index as usize)
759            .ok_or(InstructionError::MissingAccount)?
760            .is_signer)
761    }
762
763    /// Returns whether an instruction account is writable
764    pub fn is_instruction_account_writable(
765        &self,
766        instruction_account_index: IndexOfAccount,
767    ) -> Result<bool, InstructionError> {
768        Ok(self
769            .instruction_accounts
770            .get(instruction_account_index as usize)
771            .ok_or(InstructionError::MissingAccount)?
772            .is_writable)
773    }
774
775    /// Calculates the set of all keys of signer instruction accounts in this Instruction
776    pub fn get_signers(
777        &self,
778        transaction_context: &TransactionContext,
779    ) -> Result<HashSet<Pubkey>, InstructionError> {
780        let mut result = HashSet::new();
781        for instruction_account in self.instruction_accounts.iter() {
782            if instruction_account.is_signer {
783                result.insert(
784                    *transaction_context
785                        .get_key_of_account_at_index(instruction_account.index_in_transaction)?,
786                );
787            }
788        }
789        Ok(result)
790    }
791}
792
793/// Shared account borrowed from the TransactionContext and an InstructionContext.
794#[derive(Debug)]
795pub struct BorrowedAccount<'a> {
796    transaction_context: &'a TransactionContext,
797    instruction_context: &'a InstructionContext,
798    index_in_transaction: IndexOfAccount,
799    index_in_instruction: IndexOfAccount,
800    account: RefMut<'a, AccountSharedData>,
801}
802
803impl BorrowedAccount<'_> {
804    /// Returns the transaction context
805    pub fn transaction_context(&self) -> &TransactionContext {
806        self.transaction_context
807    }
808
809    /// Returns the index of this account (transaction wide)
810    #[inline]
811    pub fn get_index_in_transaction(&self) -> IndexOfAccount {
812        self.index_in_transaction
813    }
814
815    /// Returns the public key of this account (transaction wide)
816    #[inline]
817    pub fn get_key(&self) -> &Pubkey {
818        self.transaction_context
819            .get_key_of_account_at_index(self.index_in_transaction)
820            .unwrap()
821    }
822
823    /// Returns the owner of this account (transaction wide)
824    #[inline]
825    pub fn get_owner(&self) -> &Pubkey {
826        self.account.owner()
827    }
828
829    /// Assignes the owner of this account (transaction wide)
830    #[cfg(not(target_os = "solana"))]
831    pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
832        // Only the owner can assign a new owner
833        if !self.is_owned_by_current_program() {
834            return Err(InstructionError::ModifiedProgramId);
835        }
836        // and only if the account is writable
837        if !self.is_writable() {
838            return Err(InstructionError::ModifiedProgramId);
839        }
840        // and only if the account is not executable
841        if self.is_executable_internal() {
842            return Err(InstructionError::ModifiedProgramId);
843        }
844        // and only if the data is zero-initialized or empty
845        if !is_zeroed(self.get_data()) {
846            return Err(InstructionError::ModifiedProgramId);
847        }
848        // don't touch the account if the owner does not change
849        if self.get_owner().to_bytes() == pubkey {
850            return Ok(());
851        }
852        self.touch()?;
853        self.account.copy_into_owner_from_slice(pubkey);
854        Ok(())
855    }
856
857    /// Returns the number of lamports of this account (transaction wide)
858    #[inline]
859    pub fn get_lamports(&self) -> u64 {
860        self.account.lamports()
861    }
862
863    /// Overwrites the number of lamports of this account (transaction wide)
864    #[cfg(not(target_os = "solana"))]
865    pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
866        // An account not owned by the program cannot have its balance decrease
867        if !self.is_owned_by_current_program() && lamports < self.get_lamports() {
868            return Err(InstructionError::ExternalAccountLamportSpend);
869        }
870        // The balance of read-only may not change
871        if !self.is_writable() {
872            return Err(InstructionError::ReadonlyLamportChange);
873        }
874        // The balance of executable accounts may not change
875        if self.is_executable_internal() {
876            return Err(InstructionError::ExecutableLamportChange);
877        }
878        // don't touch the account if the lamports do not change
879        if self.get_lamports() == lamports {
880            return Ok(());
881        }
882        self.touch()?;
883        self.account.set_lamports(lamports);
884        Ok(())
885    }
886
887    /// Adds lamports to this account (transaction wide)
888    #[cfg(not(target_os = "solana"))]
889    pub fn checked_add_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
890        self.set_lamports(
891            self.get_lamports()
892                .checked_add(lamports)
893                .ok_or(InstructionError::ArithmeticOverflow)?,
894        )
895    }
896
897    /// Subtracts lamports from this account (transaction wide)
898    #[cfg(not(target_os = "solana"))]
899    pub fn checked_sub_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
900        self.set_lamports(
901            self.get_lamports()
902                .checked_sub(lamports)
903                .ok_or(InstructionError::ArithmeticOverflow)?,
904        )
905    }
906
907    /// Returns a read-only slice of the account data (transaction wide)
908    #[inline]
909    pub fn get_data(&self) -> &[u8] {
910        self.account.data()
911    }
912
913    /// Returns a writable slice of the account data (transaction wide)
914    #[cfg(not(target_os = "solana"))]
915    pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
916        self.can_data_be_changed()?;
917        self.touch()?;
918        self.make_data_mut();
919        Ok(self.account.data_as_mut_slice())
920    }
921
922    /// Returns the spare capacity of the vector backing the account data.
923    ///
924    /// This method should only ever be used during CPI, where after a shrinking
925    /// realloc we want to zero the spare capacity.
926    #[cfg(not(target_os = "solana"))]
927    pub fn spare_data_capacity_mut(&mut self) -> Result<&mut [MaybeUninit<u8>], InstructionError> {
928        debug_assert!(!self.account.is_shared());
929        Ok(self.account.spare_data_capacity_mut())
930    }
931
932    /// Overwrites the account data and size (transaction wide).
933    ///
934    /// You should always prefer set_data_from_slice(). Calling this method is
935    /// currently safe but requires some special casing during CPI when direct
936    /// account mapping is enabled.
937    #[cfg(all(
938        not(target_os = "solana"),
939        any(test, feature = "dev-context-only-utils")
940    ))]
941    pub fn set_data(&mut self, data: Vec<u8>) -> Result<(), InstructionError> {
942        self.can_data_be_resized(data.len())?;
943        self.can_data_be_changed()?;
944        self.touch()?;
945
946        self.update_accounts_resize_delta(data.len())?;
947        self.account.set_data(data);
948        Ok(())
949    }
950
951    /// Overwrites the account data and size (transaction wide).
952    ///
953    /// Call this when you have a slice of data you do not own and want to
954    /// replace the account data with it.
955    #[cfg(not(target_os = "solana"))]
956    pub fn set_data_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
957        self.can_data_be_resized(data.len())?;
958        self.can_data_be_changed()?;
959        self.touch()?;
960        self.update_accounts_resize_delta(data.len())?;
961        // Note that we intentionally don't call self.make_data_mut() here.  make_data_mut() will
962        // allocate + memcpy the current data if self.account is shared. We don't need the memcpy
963        // here tho because account.set_data_from_slice(data) is going to replace the content
964        // anyway.
965        self.account.set_data_from_slice(data);
966
967        Ok(())
968    }
969
970    /// Resizes the account data (transaction wide)
971    ///
972    /// Fills it with zeros at the end if is extended or truncates at the end otherwise.
973    #[cfg(not(target_os = "solana"))]
974    pub fn set_data_length(&mut self, new_length: usize) -> Result<(), InstructionError> {
975        self.can_data_be_resized(new_length)?;
976        self.can_data_be_changed()?;
977        // don't touch the account if the length does not change
978        if self.get_data().len() == new_length {
979            return Ok(());
980        }
981        self.touch()?;
982        self.update_accounts_resize_delta(new_length)?;
983        self.account.resize(new_length, 0);
984        Ok(())
985    }
986
987    /// Appends all elements in a slice to the account
988    #[cfg(not(target_os = "solana"))]
989    pub fn extend_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
990        let new_len = self.get_data().len().saturating_add(data.len());
991        self.can_data_be_resized(new_len)?;
992        self.can_data_be_changed()?;
993
994        if data.is_empty() {
995            return Ok(());
996        }
997
998        self.touch()?;
999        self.update_accounts_resize_delta(new_len)?;
1000        // Even if extend_from_slice never reduces capacity, still realloc using
1001        // make_data_mut() if necessary so that we grow the account of the full
1002        // max realloc length in one go, avoiding smaller reallocations.
1003        self.make_data_mut();
1004        self.account.extend_from_slice(data);
1005        Ok(())
1006    }
1007
1008    /// Reserves capacity for at least additional more elements to be inserted
1009    /// in the given account. Does nothing if capacity is already sufficient.
1010    #[cfg(not(target_os = "solana"))]
1011    pub fn reserve(&mut self, additional: usize) -> Result<(), InstructionError> {
1012        // Note that we don't need to call can_data_be_changed() here nor
1013        // touch() the account. reserve() only changes the capacity of the
1014        // memory that holds the account but it doesn't actually change content
1015        // nor length of the account.
1016        self.make_data_mut();
1017        self.account.reserve(additional);
1018
1019        Ok(())
1020    }
1021
1022    /// Returns the number of bytes the account can hold without reallocating.
1023    #[cfg(not(target_os = "solana"))]
1024    pub fn capacity(&self) -> usize {
1025        self.account.capacity()
1026    }
1027
1028    /// Returns whether the underlying AccountSharedData is shared.
1029    ///
1030    /// The data is shared if the account has been loaded from the accounts database and has never
1031    /// been written to. Writing to an account unshares it.
1032    ///
1033    /// During account serialization, if an account is shared it'll get mapped as CoW, else it'll
1034    /// get mapped directly as writable.
1035    #[cfg(not(target_os = "solana"))]
1036    pub fn is_shared(&self) -> bool {
1037        self.account.is_shared()
1038    }
1039
1040    #[cfg(not(target_os = "solana"))]
1041    fn make_data_mut(&mut self) {
1042        // if the account is still shared, it means this is the first time we're
1043        // about to write into it. Make the account mutable by copying it in a
1044        // buffer with MAX_PERMITTED_DATA_INCREASE capacity so that if the
1045        // transaction reallocs, we don't have to copy the whole account data a
1046        // second time to fullfill the realloc.
1047        //
1048        // NOTE: The account memory region CoW code in bpf_loader::create_vm() implements the same
1049        // logic and must be kept in sync.
1050        if self.account.is_shared() {
1051            self.account.reserve(MAX_PERMITTED_DATA_INCREASE);
1052        }
1053    }
1054
1055    /// Deserializes the account data into a state
1056    #[cfg(all(not(target_os = "solana"), feature = "bincode"))]
1057    pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
1058        self.account
1059            .deserialize_data()
1060            .map_err(|_| InstructionError::InvalidAccountData)
1061    }
1062
1063    /// Serializes a state into the account data
1064    #[cfg(all(not(target_os = "solana"), feature = "bincode"))]
1065    pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
1066        let data = self.get_data_mut()?;
1067        let serialized_size =
1068            bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
1069        if serialized_size > data.len() as u64 {
1070            return Err(InstructionError::AccountDataTooSmall);
1071        }
1072        bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
1073        Ok(())
1074    }
1075
1076    // Returns whether or the lamports currently in the account is sufficient for rent exemption should the
1077    // data be resized to the given size
1078    #[cfg(not(target_os = "solana"))]
1079    pub fn is_rent_exempt_at_data_length(&self, data_length: usize) -> bool {
1080        self.transaction_context
1081            .rent
1082            .is_exempt(self.get_lamports(), data_length)
1083    }
1084
1085    /// Returns whether this account is executable (transaction wide)
1086    #[inline]
1087    #[deprecated(since = "2.1.0", note = "Use `get_owner` instead")]
1088    pub fn is_executable(&self) -> bool {
1089        self.account.executable()
1090    }
1091
1092    /// Feature gating to remove `is_executable` flag related checks
1093    #[cfg(not(target_os = "solana"))]
1094    #[inline]
1095    fn is_executable_internal(&self) -> bool {
1096        !self
1097            .transaction_context
1098            .remove_accounts_executable_flag_checks
1099            && self.account.executable()
1100    }
1101
1102    /// Configures whether this account is executable (transaction wide)
1103    #[cfg(not(target_os = "solana"))]
1104    pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
1105        // To become executable an account must be rent exempt
1106        if !self
1107            .transaction_context
1108            .rent
1109            .is_exempt(self.get_lamports(), self.get_data().len())
1110        {
1111            return Err(InstructionError::ExecutableAccountNotRentExempt);
1112        }
1113        // Only the owner can set the executable flag
1114        if !self.is_owned_by_current_program() {
1115            return Err(InstructionError::ExecutableModified);
1116        }
1117        // and only if the account is writable
1118        if !self.is_writable() {
1119            return Err(InstructionError::ExecutableModified);
1120        }
1121        // one can not clear the executable flag
1122        if self.is_executable_internal() && !is_executable {
1123            return Err(InstructionError::ExecutableModified);
1124        }
1125        // don't touch the account if the executable flag does not change
1126        #[allow(deprecated)]
1127        if self.is_executable() == is_executable {
1128            return Ok(());
1129        }
1130        self.touch()?;
1131        self.account.set_executable(is_executable);
1132        Ok(())
1133    }
1134
1135    /// Returns the rent epoch of this account (transaction wide)
1136    #[cfg(not(target_os = "solana"))]
1137    #[inline]
1138    pub fn get_rent_epoch(&self) -> u64 {
1139        self.account.rent_epoch()
1140    }
1141
1142    /// Returns whether this account is a signer (instruction wide)
1143    pub fn is_signer(&self) -> bool {
1144        if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1145            return false;
1146        }
1147        self.instruction_context
1148            .is_instruction_account_signer(
1149                self.index_in_instruction
1150                    .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1151            )
1152            .unwrap_or_default()
1153    }
1154
1155    /// Returns whether this account is writable (instruction wide)
1156    pub fn is_writable(&self) -> bool {
1157        if self.index_in_instruction < self.instruction_context.get_number_of_program_accounts() {
1158            return false;
1159        }
1160        self.instruction_context
1161            .is_instruction_account_writable(
1162                self.index_in_instruction
1163                    .saturating_sub(self.instruction_context.get_number_of_program_accounts()),
1164            )
1165            .unwrap_or_default()
1166    }
1167
1168    /// Returns true if the owner of this account is the current `InstructionContext`s last program (instruction wide)
1169    pub fn is_owned_by_current_program(&self) -> bool {
1170        self.instruction_context
1171            .get_last_program_key(self.transaction_context)
1172            .map(|key| key == self.get_owner())
1173            .unwrap_or_default()
1174    }
1175
1176    /// Returns an error if the account data can not be mutated by the current program
1177    #[cfg(not(target_os = "solana"))]
1178    pub fn can_data_be_changed(&self) -> Result<(), InstructionError> {
1179        // Only non-executable accounts data can be changed
1180        if self.is_executable_internal() {
1181            return Err(InstructionError::ExecutableDataModified);
1182        }
1183        // and only if the account is writable
1184        if !self.is_writable() {
1185            return Err(InstructionError::ReadonlyDataModified);
1186        }
1187        // and only if we are the owner
1188        if !self.is_owned_by_current_program() {
1189            return Err(InstructionError::ExternalAccountDataModified);
1190        }
1191        Ok(())
1192    }
1193
1194    /// Returns an error if the account data can not be resized to the given length
1195    #[cfg(not(target_os = "solana"))]
1196    pub fn can_data_be_resized(&self, new_length: usize) -> Result<(), InstructionError> {
1197        let old_length = self.get_data().len();
1198        // Only the owner can change the length of the data
1199        if new_length != old_length && !self.is_owned_by_current_program() {
1200            return Err(InstructionError::AccountDataSizeChanged);
1201        }
1202        // The new length can not exceed the maximum permitted length
1203        if new_length > MAX_PERMITTED_DATA_LENGTH as usize {
1204            return Err(InstructionError::InvalidRealloc);
1205        }
1206        // The resize can not exceed the per-transaction maximum
1207        let length_delta = (new_length as i64).saturating_sub(old_length as i64);
1208        if self
1209            .transaction_context
1210            .accounts_resize_delta()?
1211            .saturating_add(length_delta)
1212            > MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1213        {
1214            return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
1215        }
1216        Ok(())
1217    }
1218
1219    #[cfg(not(target_os = "solana"))]
1220    fn touch(&self) -> Result<(), InstructionError> {
1221        self.transaction_context
1222            .accounts()
1223            .touch(self.index_in_transaction)
1224    }
1225
1226    #[cfg(not(target_os = "solana"))]
1227    fn update_accounts_resize_delta(&mut self, new_len: usize) -> Result<(), InstructionError> {
1228        let mut accounts_resize_delta = self
1229            .transaction_context
1230            .accounts_resize_delta
1231            .try_borrow_mut()
1232            .map_err(|_| InstructionError::GenericError)?;
1233        *accounts_resize_delta = accounts_resize_delta
1234            .saturating_add((new_len as i64).saturating_sub(self.get_data().len() as i64));
1235        Ok(())
1236    }
1237}
1238
1239/// Everything that needs to be recorded from a TransactionContext after execution
1240#[cfg(not(target_os = "solana"))]
1241pub struct ExecutionRecord {
1242    pub accounts: Vec<TransactionAccount>,
1243    pub return_data: TransactionReturnData,
1244    pub touched_account_count: u64,
1245    pub accounts_resize_delta: i64,
1246}
1247
1248/// Used by the bank in the runtime to write back the processed accounts and recorded instructions
1249#[cfg(not(target_os = "solana"))]
1250impl From<TransactionContext> for ExecutionRecord {
1251    fn from(context: TransactionContext) -> Self {
1252        let accounts = Rc::try_unwrap(context.accounts)
1253            .expect("transaction_context.accounts has unexpected outstanding refs");
1254        let touched_account_count = accounts.touched_count() as u64;
1255        let accounts = accounts.into_accounts();
1256        Self {
1257            accounts: Vec::from(Pin::into_inner(context.account_keys))
1258                .into_iter()
1259                .zip(accounts)
1260                .collect(),
1261            return_data: context.return_data,
1262            touched_account_count,
1263            accounts_resize_delta: RefCell::into_inner(context.accounts_resize_delta),
1264        }
1265    }
1266}
1267
1268#[cfg(not(target_os = "solana"))]
1269fn is_zeroed(buf: &[u8]) -> bool {
1270    const ZEROS_LEN: usize = 1024;
1271    const ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
1272    let mut chunks = buf.chunks_exact(ZEROS_LEN);
1273
1274    #[allow(clippy::indexing_slicing)]
1275    {
1276        chunks.all(|chunk| chunk == &ZEROS[..])
1277            && chunks.remainder() == &ZEROS[..chunks.remainder().len()]
1278    }
1279}