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