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