solana_sdk/
transaction_context.rs

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