solana_sdk/
transaction_context.rs

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