solana_program_runtime/
invoke_context.rs

1#[allow(deprecated)]
2use solana_sdk::keyed_account::{create_keyed_accounts_unified, KeyedAccount};
3use {
4    crate::{
5        accounts_data_meter::AccountsDataMeter,
6        compute_budget::ComputeBudget,
7        executor_cache::{Executor, Executors, TransactionExecutor},
8        ic_logger_msg, ic_msg,
9        log_collector::LogCollector,
10        pre_account::PreAccount,
11        stable_log,
12        sysvar_cache::SysvarCache,
13        timings::{ExecuteDetailsTimings, ExecuteTimings},
14    },
15    safecoin_measure::measure::Measure,
16    solana_sdk::{
17        account::{AccountSharedData, ReadableAccount},
18        bpf_loader_upgradeable::{self, UpgradeableLoaderState},
19        feature_set::{
20            cap_accounts_data_len, enable_early_verification_of_account_modifications, FeatureSet,
21        },
22        hash::Hash,
23        instruction::{AccountMeta, Instruction, InstructionError},
24        native_loader,
25        pubkey::Pubkey,
26        rent::Rent,
27        saturating_add_assign,
28        transaction_context::{InstructionAccount, TransactionAccount, TransactionContext},
29    },
30    std::{
31        alloc::Layout,
32        borrow::Cow,
33        cell::RefCell,
34        fmt::{self, Debug},
35        rc::Rc,
36        sync::Arc,
37    },
38};
39
40pub type ProcessInstructionWithContext =
41    fn(usize, &mut InvokeContext) -> Result<(), InstructionError>;
42
43#[derive(Clone)]
44pub struct BuiltinProgram {
45    pub program_id: Pubkey,
46    pub process_instruction: ProcessInstructionWithContext,
47}
48
49impl std::fmt::Debug for BuiltinProgram {
50    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
51        // These are just type aliases for work around of Debug-ing above pointers
52        type ErasedProcessInstructionWithContext =
53            fn(usize, &'static mut InvokeContext<'static>) -> Result<(), InstructionError>;
54
55        // rustc doesn't compile due to bug without this work around
56        // https://github.com/rust-lang/rust/issues/50280
57        // https://users.rust-lang.org/t/display-function-pointer/17073/2
58        let erased_instruction: ErasedProcessInstructionWithContext = self.process_instruction;
59        write!(f, "{}: {:p}", self.program_id, erased_instruction)
60    }
61}
62
63/// Compute meter
64pub struct ComputeMeter {
65    remaining: u64,
66}
67impl ComputeMeter {
68    /// Consume compute units
69    pub fn consume(&mut self, amount: u64) -> Result<(), InstructionError> {
70        let exceeded = self.remaining < amount;
71        self.remaining = self.remaining.saturating_sub(amount);
72        if exceeded {
73            return Err(InstructionError::ComputationalBudgetExceeded);
74        }
75        Ok(())
76    }
77    /// Get the number of remaining compute units
78    pub fn get_remaining(&self) -> u64 {
79        self.remaining
80    }
81    /// Set compute units
82    ///
83    /// Only use for tests and benchmarks
84    pub fn mock_set_remaining(&mut self, remaining: u64) {
85        self.remaining = remaining;
86    }
87    /// Construct a new one with the given remaining units
88    pub fn new_ref(remaining: u64) -> Rc<RefCell<Self>> {
89        Rc::new(RefCell::new(Self { remaining }))
90    }
91}
92
93/// Based loosely on the unstable std::alloc::Alloc trait
94pub trait Alloc {
95    fn alloc(&mut self, layout: Layout) -> Result<u64, AllocErr>;
96    fn dealloc(&mut self, addr: u64, layout: Layout);
97}
98
99#[derive(Clone, PartialEq, Eq, Debug)]
100pub struct AllocErr;
101
102impl fmt::Display for AllocErr {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        f.write_str("Error: Memory allocation failed")
105    }
106}
107
108#[deprecated(
109    since = "1.11.0",
110    note = "Please use InstructionContext instead of StackFrame"
111)]
112#[allow(deprecated)]
113pub struct StackFrame<'a> {
114    pub number_of_program_accounts: usize,
115    pub keyed_accounts: Vec<KeyedAccount<'a>>,
116    pub keyed_accounts_range: std::ops::Range<usize>,
117}
118
119#[allow(deprecated)]
120impl<'a> StackFrame<'a> {
121    pub fn new(number_of_program_accounts: usize, keyed_accounts: Vec<KeyedAccount<'a>>) -> Self {
122        let keyed_accounts_range = std::ops::Range {
123            start: 0,
124            end: keyed_accounts.len(),
125        };
126        Self {
127            number_of_program_accounts,
128            keyed_accounts,
129            keyed_accounts_range,
130        }
131    }
132
133    pub fn program_id(&self) -> Option<&Pubkey> {
134        self.keyed_accounts
135            .get(self.number_of_program_accounts.saturating_sub(1))
136            .map(|keyed_account| keyed_account.unsigned_key())
137    }
138}
139
140struct SyscallContext {
141    check_aligned: bool,
142    check_size: bool,
143    orig_account_lengths: Vec<usize>,
144    allocator: Rc<RefCell<dyn Alloc>>,
145}
146
147pub struct InvokeContext<'a> {
148    pub transaction_context: &'a mut TransactionContext,
149    #[allow(deprecated)]
150    invoke_stack: Vec<StackFrame<'a>>,
151    rent: Rent,
152    pre_accounts: Vec<PreAccount>,
153    builtin_programs: &'a [BuiltinProgram],
154    pub sysvar_cache: Cow<'a, SysvarCache>,
155    log_collector: Option<Rc<RefCell<LogCollector>>>,
156    compute_budget: ComputeBudget,
157    current_compute_budget: ComputeBudget,
158    compute_meter: Rc<RefCell<ComputeMeter>>,
159    accounts_data_meter: AccountsDataMeter,
160    executors: Rc<RefCell<Executors>>,
161    pub feature_set: Arc<FeatureSet>,
162    pub timings: ExecuteDetailsTimings,
163    pub blockhash: Hash,
164    pub lamports_per_signature: u64,
165    syscall_context: Vec<Option<SyscallContext>>,
166}
167
168impl<'a> InvokeContext<'a> {
169    #[allow(clippy::too_many_arguments)]
170    pub fn new(
171        transaction_context: &'a mut TransactionContext,
172        rent: Rent,
173        builtin_programs: &'a [BuiltinProgram],
174        sysvar_cache: Cow<'a, SysvarCache>,
175        log_collector: Option<Rc<RefCell<LogCollector>>>,
176        compute_budget: ComputeBudget,
177        executors: Rc<RefCell<Executors>>,
178        feature_set: Arc<FeatureSet>,
179        blockhash: Hash,
180        lamports_per_signature: u64,
181        prev_accounts_data_len: u64,
182    ) -> Self {
183        Self {
184            transaction_context,
185            invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth),
186            rent,
187            pre_accounts: Vec::new(),
188            builtin_programs,
189            sysvar_cache,
190            log_collector,
191            current_compute_budget: compute_budget,
192            compute_budget,
193            compute_meter: ComputeMeter::new_ref(compute_budget.compute_unit_limit),
194            accounts_data_meter: AccountsDataMeter::new(prev_accounts_data_len),
195            executors,
196            feature_set,
197            timings: ExecuteDetailsTimings::default(),
198            blockhash,
199            lamports_per_signature,
200            syscall_context: Vec::new(),
201        }
202    }
203
204    pub fn new_mock(
205        transaction_context: &'a mut TransactionContext,
206        builtin_programs: &'a [BuiltinProgram],
207    ) -> Self {
208        let mut sysvar_cache = SysvarCache::default();
209        sysvar_cache.fill_missing_entries(|pubkey, callback| {
210            for index in 0..transaction_context.get_number_of_accounts() {
211                if transaction_context
212                    .get_key_of_account_at_index(index)
213                    .unwrap()
214                    == pubkey
215                {
216                    callback(
217                        transaction_context
218                            .get_account_at_index(index)
219                            .unwrap()
220                            .borrow()
221                            .data(),
222                    );
223                }
224            }
225        });
226        Self::new(
227            transaction_context,
228            Rent::default(),
229            builtin_programs,
230            Cow::Owned(sysvar_cache),
231            Some(LogCollector::new_ref()),
232            ComputeBudget::default(),
233            Rc::new(RefCell::new(Executors::default())),
234            Arc::new(FeatureSet::all_enabled()),
235            Hash::default(),
236            0,
237            0,
238        )
239    }
240
241    /// Push a stack frame onto the invocation stack
242    pub fn push(
243        &mut self,
244        instruction_accounts: &[InstructionAccount],
245        program_indices: &[usize],
246        instruction_data: &[u8],
247    ) -> Result<(), InstructionError> {
248        let program_id = self.transaction_context.get_key_of_account_at_index(
249            *program_indices
250                .last()
251                .ok_or(InstructionError::UnsupportedProgramId)?,
252        )?;
253        if self
254            .transaction_context
255            .get_instruction_context_stack_height()
256            == 0
257        {
258            self.current_compute_budget = self.compute_budget;
259
260            if !self
261                .feature_set
262                .is_active(&enable_early_verification_of_account_modifications::id())
263            {
264                self.pre_accounts = Vec::with_capacity(instruction_accounts.len());
265                for (instruction_account_index, instruction_account) in
266                    instruction_accounts.iter().enumerate()
267                {
268                    if instruction_account_index != instruction_account.index_in_callee {
269                        continue; // Skip duplicate account
270                    }
271                    if instruction_account.index_in_transaction
272                        >= self.transaction_context.get_number_of_accounts()
273                    {
274                        return Err(InstructionError::MissingAccount);
275                    }
276                    let account = self
277                        .transaction_context
278                        .get_account_at_index(instruction_account.index_in_transaction)?
279                        .borrow()
280                        .clone();
281                    self.pre_accounts.push(PreAccount::new(
282                        self.transaction_context.get_key_of_account_at_index(
283                            instruction_account.index_in_transaction,
284                        )?,
285                        account,
286                    ));
287                }
288            }
289        } else {
290            let contains = (0..self
291                .transaction_context
292                .get_instruction_context_stack_height())
293                .any(|level| {
294                    self.transaction_context
295                        .get_instruction_context_at(level)
296                        .and_then(|instruction_context| {
297                            instruction_context
298                                .try_borrow_last_program_account(self.transaction_context)
299                        })
300                        .map(|program_account| program_account.get_key() == program_id)
301                        .unwrap_or(false)
302                });
303            let is_last = self
304                .transaction_context
305                .get_current_instruction_context()
306                .and_then(|instruction_context| {
307                    instruction_context.try_borrow_last_program_account(self.transaction_context)
308                })
309                .map(|program_account| program_account.get_key() == program_id)
310                .unwrap_or(false);
311            if contains && !is_last {
312                // Reentrancy not allowed unless caller is calling itself
313                return Err(InstructionError::ReentrancyNotAllowed);
314            }
315        }
316
317        // Create the KeyedAccounts that will be passed to the program
318        #[allow(deprecated)]
319        let keyed_accounts = program_indices
320            .iter()
321            .map(|account_index| {
322                Ok((
323                    false,
324                    false,
325                    self.transaction_context
326                        .get_key_of_account_at_index(*account_index)?,
327                    self.transaction_context
328                        .get_account_at_index(*account_index)?,
329                ))
330            })
331            .chain(instruction_accounts.iter().map(|instruction_account| {
332                Ok((
333                    instruction_account.is_signer,
334                    instruction_account.is_writable,
335                    self.transaction_context
336                        .get_key_of_account_at_index(instruction_account.index_in_transaction)?,
337                    self.transaction_context
338                        .get_account_at_index(instruction_account.index_in_transaction)?,
339                ))
340            }))
341            .collect::<Result<Vec<_>, InstructionError>>()?;
342
343        // Unsafe will be removed together with the keyed_accounts
344        #[allow(deprecated)]
345        self.invoke_stack.push(StackFrame::new(
346            program_indices.len(),
347            create_keyed_accounts_unified(unsafe {
348                std::mem::transmute(keyed_accounts.as_slice())
349            }),
350        ));
351        self.syscall_context.push(None);
352        self.transaction_context
353            .push(program_indices, instruction_accounts, instruction_data)
354    }
355
356    /// Pop a stack frame from the invocation stack
357    pub fn pop(&mut self) -> Result<(), InstructionError> {
358        self.syscall_context.pop();
359        self.invoke_stack.pop();
360        self.transaction_context.pop()
361    }
362
363    /// Current height of the invocation stack, top level instructions are height
364    /// `solana_sdk::instruction::TRANSACTION_LEVEL_STACK_HEIGHT`
365    pub fn get_stack_height(&self) -> usize {
366        self.transaction_context
367            .get_instruction_context_stack_height()
368    }
369
370    /// Verify the results of an instruction
371    ///
372    /// Note: `instruction_accounts` must be the same as passed to `InvokeContext::push()`,
373    /// so that they match the order of `pre_accounts`.
374    fn verify(
375        &mut self,
376        instruction_accounts: &[InstructionAccount],
377        program_indices: &[usize],
378    ) -> Result<(), InstructionError> {
379        let cap_accounts_data_len = self.feature_set.is_active(&cap_accounts_data_len::id());
380        let instruction_context = self
381            .transaction_context
382            .get_current_instruction_context()
383            .map_err(|_| InstructionError::CallDepth)?;
384        let program_id = instruction_context
385            .get_last_program_key(self.transaction_context)
386            .map_err(|_| InstructionError::CallDepth)?;
387
388        // Verify all executable accounts have zero outstanding refs
389        for account_index in program_indices.iter() {
390            self.transaction_context
391                .get_account_at_index(*account_index)?
392                .try_borrow_mut()
393                .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
394        }
395
396        // Verify the per-account instruction results
397        let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
398        let mut pre_account_index = 0;
399        for (instruction_account_index, instruction_account) in
400            instruction_accounts.iter().enumerate()
401        {
402            if instruction_account_index != instruction_account.index_in_callee {
403                continue; // Skip duplicate account
404            }
405            {
406                // Verify account has no outstanding references
407                let _ = self
408                    .transaction_context
409                    .get_account_at_index(instruction_account.index_in_transaction)?
410                    .try_borrow_mut()
411                    .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
412            }
413            let pre_account = &self
414                .pre_accounts
415                .get(pre_account_index)
416                .ok_or(InstructionError::NotEnoughAccountKeys)?;
417            pre_account_index = pre_account_index.saturating_add(1);
418            let account = self
419                .transaction_context
420                .get_account_at_index(instruction_account.index_in_transaction)?
421                .borrow();
422            pre_account
423                .verify(
424                    program_id,
425                    instruction_account.is_writable,
426                    &self.rent,
427                    &account,
428                    &mut self.timings,
429                    true,
430                )
431                .map_err(|err| {
432                    ic_logger_msg!(
433                        self.log_collector,
434                        "failed to verify account {}: {}",
435                        pre_account.key(),
436                        err
437                    );
438                    err
439                })?;
440            pre_sum = pre_sum
441                .checked_add(u128::from(pre_account.lamports()))
442                .ok_or(InstructionError::UnbalancedInstruction)?;
443            post_sum = post_sum
444                .checked_add(u128::from(account.lamports()))
445                .ok_or(InstructionError::UnbalancedInstruction)?;
446
447            let pre_data_len = pre_account.data().len() as i64;
448            let post_data_len = account.data().len() as i64;
449            let data_len_delta = post_data_len.saturating_sub(pre_data_len);
450            if cap_accounts_data_len {
451                self.accounts_data_meter.adjust_delta(data_len_delta)?;
452            } else {
453                self.accounts_data_meter
454                    .adjust_delta_unchecked(data_len_delta);
455            }
456        }
457
458        // Verify that the total sum of all the lamports did not change
459        if pre_sum != post_sum {
460            return Err(InstructionError::UnbalancedInstruction);
461        }
462        Ok(())
463    }
464
465    /// Verify and update PreAccount state based on program execution
466    ///
467    /// Note: `instruction_accounts` must be the same as passed to `InvokeContext::push()`,
468    /// so that they match the order of `pre_accounts`.
469    fn verify_and_update(
470        &mut self,
471        instruction_accounts: &[InstructionAccount],
472        before_instruction_context_push: bool,
473    ) -> Result<(), InstructionError> {
474        let cap_accounts_data_len = self.feature_set.is_active(&cap_accounts_data_len::id());
475        let transaction_context = &self.transaction_context;
476        let instruction_context = transaction_context.get_current_instruction_context()?;
477        let program_id = instruction_context
478            .get_last_program_key(transaction_context)
479            .map_err(|_| InstructionError::CallDepth)?;
480
481        // Verify the per-account instruction results
482        let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
483        for (instruction_account_index, instruction_account) in
484            instruction_accounts.iter().enumerate()
485        {
486            if instruction_account_index != instruction_account.index_in_callee {
487                continue; // Skip duplicate account
488            }
489            if instruction_account.index_in_transaction
490                < transaction_context.get_number_of_accounts()
491            {
492                let key = transaction_context
493                    .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
494                let account = transaction_context
495                    .get_account_at_index(instruction_account.index_in_transaction)?;
496                let is_writable = if before_instruction_context_push {
497                    instruction_context
498                        .is_instruction_account_writable(instruction_account.index_in_caller)?
499                } else {
500                    instruction_account.is_writable
501                };
502                // Find the matching PreAccount
503                for pre_account in self.pre_accounts.iter_mut() {
504                    if key == pre_account.key() {
505                        {
506                            // Verify account has no outstanding references
507                            let _ = account
508                                .try_borrow_mut()
509                                .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
510                        }
511                        let account = account.borrow();
512                        pre_account
513                            .verify(
514                                program_id,
515                                is_writable,
516                                &self.rent,
517                                &account,
518                                &mut self.timings,
519                                false,
520                            )
521                            .map_err(|err| {
522                                ic_logger_msg!(
523                                    self.log_collector,
524                                    "failed to verify account {}: {}",
525                                    key,
526                                    err
527                                );
528                                err
529                            })?;
530                        pre_sum = pre_sum
531                            .checked_add(u128::from(pre_account.lamports()))
532                            .ok_or(InstructionError::UnbalancedInstruction)?;
533                        post_sum = post_sum
534                            .checked_add(u128::from(account.lamports()))
535                            .ok_or(InstructionError::UnbalancedInstruction)?;
536                        if is_writable && !pre_account.executable() {
537                            pre_account.update(account.clone());
538                        }
539
540                        let pre_data_len = pre_account.data().len() as i64;
541                        let post_data_len = account.data().len() as i64;
542                        let data_len_delta = post_data_len.saturating_sub(pre_data_len);
543                        if cap_accounts_data_len {
544                            self.accounts_data_meter.adjust_delta(data_len_delta)?;
545                        } else {
546                            self.accounts_data_meter
547                                .adjust_delta_unchecked(data_len_delta);
548                        }
549
550                        break;
551                    }
552                }
553            }
554        }
555
556        // Verify that the total sum of all the lamports did not change
557        if pre_sum != post_sum {
558            return Err(InstructionError::UnbalancedInstruction);
559        }
560        Ok(())
561    }
562
563    /// Entrypoint for a cross-program invocation from a builtin program
564    pub fn native_invoke(
565        &mut self,
566        instruction: Instruction,
567        signers: &[Pubkey],
568    ) -> Result<(), InstructionError> {
569        let (instruction_accounts, program_indices) =
570            self.prepare_instruction(&instruction, signers)?;
571        let mut compute_units_consumed = 0;
572        self.process_instruction(
573            &instruction.data,
574            &instruction_accounts,
575            &program_indices,
576            &mut compute_units_consumed,
577            &mut ExecuteTimings::default(),
578        )?;
579        Ok(())
580    }
581
582    /// Helper to prepare for process_instruction()
583    #[allow(clippy::type_complexity)]
584    pub fn prepare_instruction(
585        &mut self,
586        instruction: &Instruction,
587        signers: &[Pubkey],
588    ) -> Result<(Vec<InstructionAccount>, Vec<usize>), InstructionError> {
589        // Finds the index of each account in the instruction by its pubkey.
590        // Then normalizes / unifies the privileges of duplicate accounts.
591        // Note: This is an O(n^2) algorithm,
592        // but performed on a very small slice and requires no heap allocations.
593        let instruction_context = self.transaction_context.get_current_instruction_context()?;
594        let mut deduplicated_instruction_accounts: Vec<InstructionAccount> = Vec::new();
595        let mut duplicate_indicies = Vec::with_capacity(instruction.accounts.len());
596        for (instruction_account_index, account_meta) in instruction.accounts.iter().enumerate() {
597            let index_in_transaction = self
598                .transaction_context
599                .find_index_of_account(&account_meta.pubkey)
600                .ok_or_else(|| {
601                    ic_msg!(
602                        self,
603                        "Instruction references an unknown account {}",
604                        account_meta.pubkey,
605                    );
606                    InstructionError::MissingAccount
607                })?;
608            if let Some(duplicate_index) =
609                deduplicated_instruction_accounts
610                    .iter()
611                    .position(|instruction_account| {
612                        instruction_account.index_in_transaction == index_in_transaction
613                    })
614            {
615                duplicate_indicies.push(duplicate_index);
616                let instruction_account = deduplicated_instruction_accounts
617                    .get_mut(duplicate_index)
618                    .ok_or(InstructionError::NotEnoughAccountKeys)?;
619                instruction_account.is_signer |= account_meta.is_signer;
620                instruction_account.is_writable |= account_meta.is_writable;
621            } else {
622                let index_in_caller = instruction_context
623                    .find_index_of_instruction_account(
624                        self.transaction_context,
625                        &account_meta.pubkey,
626                    )
627                    .ok_or_else(|| {
628                        ic_msg!(
629                            self,
630                            "Instruction references an unknown account {}",
631                            account_meta.pubkey,
632                        );
633                        InstructionError::MissingAccount
634                    })?;
635                duplicate_indicies.push(deduplicated_instruction_accounts.len());
636                deduplicated_instruction_accounts.push(InstructionAccount {
637                    index_in_transaction,
638                    index_in_caller,
639                    index_in_callee: instruction_account_index,
640                    is_signer: account_meta.is_signer,
641                    is_writable: account_meta.is_writable,
642                });
643            }
644        }
645        for instruction_account in deduplicated_instruction_accounts.iter() {
646            let borrowed_account = instruction_context.try_borrow_instruction_account(
647                self.transaction_context,
648                instruction_account.index_in_caller,
649            )?;
650
651            // Readonly in caller cannot become writable in callee
652            if instruction_account.is_writable && !borrowed_account.is_writable() {
653                ic_msg!(
654                    self,
655                    "{}'s writable privilege escalated",
656                    borrowed_account.get_key(),
657                );
658                return Err(InstructionError::PrivilegeEscalation);
659            }
660
661            // To be signed in the callee,
662            // it must be either signed in the caller or by the program
663            if instruction_account.is_signer
664                && !(borrowed_account.is_signer() || signers.contains(borrowed_account.get_key()))
665            {
666                ic_msg!(
667                    self,
668                    "{}'s signer privilege escalated",
669                    borrowed_account.get_key()
670                );
671                return Err(InstructionError::PrivilegeEscalation);
672            }
673        }
674        let instruction_accounts = duplicate_indicies
675            .into_iter()
676            .map(|duplicate_index| {
677                Ok(deduplicated_instruction_accounts
678                    .get(duplicate_index)
679                    .ok_or(InstructionError::NotEnoughAccountKeys)?
680                    .clone())
681            })
682            .collect::<Result<Vec<InstructionAccount>, InstructionError>>()?;
683
684        // Find and validate executables / program accounts
685        let callee_program_id = instruction.program_id;
686        let program_account_index = instruction_context
687            .find_index_of_instruction_account(self.transaction_context, &callee_program_id)
688            .ok_or_else(|| {
689                ic_msg!(self, "Unknown program {}", callee_program_id);
690                InstructionError::MissingAccount
691            })?;
692        let borrowed_program_account = instruction_context
693            .try_borrow_instruction_account(self.transaction_context, program_account_index)?;
694        if !borrowed_program_account.is_executable() {
695            ic_msg!(self, "Account {} is not executable", callee_program_id);
696            return Err(InstructionError::AccountNotExecutable);
697        }
698        let mut program_indices = vec![];
699        if borrowed_program_account.get_owner() == &bpf_loader_upgradeable::id() {
700            if let UpgradeableLoaderState::Program {
701                programdata_address,
702            } = borrowed_program_account.get_state()?
703            {
704                if let Some(programdata_account_index) = self
705                    .transaction_context
706                    .find_index_of_program_account(&programdata_address)
707                {
708                    program_indices.push(programdata_account_index);
709                } else {
710                    ic_msg!(
711                        self,
712                        "Unknown upgradeable programdata account {}",
713                        programdata_address,
714                    );
715                    return Err(InstructionError::MissingAccount);
716                }
717            } else {
718                ic_msg!(
719                    self,
720                    "Invalid upgradeable program account {}",
721                    callee_program_id,
722                );
723                return Err(InstructionError::MissingAccount);
724            }
725        }
726        program_indices.push(borrowed_program_account.get_index_in_transaction());
727
728        Ok((instruction_accounts, program_indices))
729    }
730
731    /// Processes an instruction and returns how many compute units were used
732    pub fn process_instruction(
733        &mut self,
734        instruction_data: &[u8],
735        instruction_accounts: &[InstructionAccount],
736        program_indices: &[usize],
737        compute_units_consumed: &mut u64,
738        timings: &mut ExecuteTimings,
739    ) -> Result<(), InstructionError> {
740        *compute_units_consumed = 0;
741
742        let nesting_level = self
743            .transaction_context
744            .get_instruction_context_stack_height();
745        let is_top_level_instruction = nesting_level == 0;
746        if !is_top_level_instruction
747            && !self
748                .feature_set
749                .is_active(&enable_early_verification_of_account_modifications::id())
750        {
751            // Verify the calling program hasn't misbehaved
752            let mut verify_caller_time = Measure::start("verify_caller_time");
753            let verify_caller_result = self.verify_and_update(instruction_accounts, true);
754            verify_caller_time.stop();
755            saturating_add_assign!(
756                timings
757                    .execute_accessories
758                    .process_instructions
759                    .verify_caller_us,
760                verify_caller_time.as_us()
761            );
762            verify_caller_result?;
763        }
764
765        self.push(instruction_accounts, program_indices, instruction_data)?;
766        self.process_executable_chain(compute_units_consumed, timings)
767            .and_then(|_| {
768                if self
769                    .feature_set
770                    .is_active(&enable_early_verification_of_account_modifications::id())
771                {
772                    Ok(())
773                } else {
774                    // Verify the called program has not misbehaved
775                    let mut verify_callee_time = Measure::start("verify_callee_time");
776                    let result = if is_top_level_instruction {
777                        self.verify(instruction_accounts, program_indices)
778                    } else {
779                        self.verify_and_update(instruction_accounts, false)
780                    };
781                    verify_callee_time.stop();
782                    saturating_add_assign!(
783                        timings
784                            .execute_accessories
785                            .process_instructions
786                            .verify_callee_us,
787                        verify_callee_time.as_us()
788                    );
789                    result
790                }
791            })
792            // MUST pop if and only if `push` succeeded, independent of `result`.
793            // Thus, the `.and()` instead of an `.and_then()`.
794            .and(self.pop())
795    }
796
797    /// Calls the instruction's program entrypoint method
798    fn process_executable_chain(
799        &mut self,
800        compute_units_consumed: &mut u64,
801        timings: &mut ExecuteTimings,
802    ) -> Result<(), InstructionError> {
803        let instruction_context = self.transaction_context.get_current_instruction_context()?;
804        let mut process_executable_chain_time = Measure::start("process_executable_chain_time");
805
806        let (first_instruction_account, builtin_id) = {
807            let borrowed_root_account = instruction_context
808                .try_borrow_program_account(self.transaction_context, 0)
809                .map_err(|_| InstructionError::UnsupportedProgramId)?;
810            let owner_id = borrowed_root_account.get_owner();
811            if native_loader::check_id(owner_id) {
812                (1, *borrowed_root_account.get_key())
813            } else {
814                (0, *owner_id)
815            }
816        };
817
818        for entry in self.builtin_programs {
819            if entry.program_id == builtin_id {
820                let program_id =
821                    *instruction_context.get_last_program_key(self.transaction_context)?;
822                self.transaction_context
823                    .set_return_data(program_id, Vec::new())?;
824
825                let pre_remaining_units = self.compute_meter.borrow().get_remaining();
826                let result = if builtin_id == program_id {
827                    let logger = self.get_log_collector();
828                    stable_log::program_invoke(&logger, &program_id, self.get_stack_height());
829                    (entry.process_instruction)(first_instruction_account, self)
830                        .map(|()| {
831                            stable_log::program_success(&logger, &program_id);
832                        })
833                        .map_err(|err| {
834                            stable_log::program_failure(&logger, &program_id, &err);
835                            err
836                        })
837                } else {
838                    (entry.process_instruction)(first_instruction_account, self)
839                };
840                let post_remaining_units = self.compute_meter.borrow().get_remaining();
841                *compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units);
842
843                process_executable_chain_time.stop();
844                saturating_add_assign!(
845                    timings
846                        .execute_accessories
847                        .process_instructions
848                        .process_executable_chain_us,
849                    process_executable_chain_time.as_us()
850                );
851                return result;
852            }
853        }
854
855        Err(InstructionError::UnsupportedProgramId)
856    }
857
858    #[deprecated(
859        since = "1.11.0",
860        note = "Please use BorrowedAccount instead of KeyedAccount"
861    )]
862    #[allow(deprecated)]
863    /// Get the list of keyed accounts including the chain of program accounts
864    pub fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
865        self.invoke_stack
866            .last()
867            .and_then(|frame| frame.keyed_accounts.get(frame.keyed_accounts_range.clone()))
868            .ok_or(InstructionError::CallDepth)
869    }
870
871    /// Get this invocation's LogCollector
872    pub fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
873        self.log_collector.clone()
874    }
875
876    /// Get this invocation's ComputeMeter
877    pub fn get_compute_meter(&self) -> Rc<RefCell<ComputeMeter>> {
878        self.compute_meter.clone()
879    }
880
881    /// Get this invocation's AccountsDataMeter
882    pub fn get_accounts_data_meter(&self) -> &AccountsDataMeter {
883        &self.accounts_data_meter
884    }
885
886    /// Cache an executor that wasn't found in the cache
887    pub fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>) {
888        self.executors
889            .borrow_mut()
890            .insert(*pubkey, TransactionExecutor::new_miss(executor));
891    }
892
893    /// Cache an executor that has changed
894    pub fn update_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>) {
895        self.executors
896            .borrow_mut()
897            .insert(*pubkey, TransactionExecutor::new_updated(executor));
898    }
899
900    /// Get the completed loader work that can be re-used across execution
901    pub fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
902        self.executors
903            .borrow()
904            .get(pubkey)
905            .map(|tx_executor| tx_executor.executor.clone())
906    }
907
908    /// Get this invocation's compute budget
909    pub fn get_compute_budget(&self) -> &ComputeBudget {
910        &self.current_compute_budget
911    }
912
913    /// Get cached sysvars
914    pub fn get_sysvar_cache(&self) -> &SysvarCache {
915        &self.sysvar_cache
916    }
917
918    // Get pubkey of account at index
919    pub fn get_key_of_account_at_index(
920        &self,
921        index_in_transaction: usize,
922    ) -> Result<&Pubkey, InstructionError> {
923        self.transaction_context
924            .get_key_of_account_at_index(index_in_transaction)
925    }
926
927    // Set this instruction syscall context
928    pub fn set_syscall_context(
929        &mut self,
930        check_aligned: bool,
931        check_size: bool,
932        orig_account_lengths: Vec<usize>,
933        allocator: Rc<RefCell<dyn Alloc>>,
934    ) -> Result<(), InstructionError> {
935        *self
936            .syscall_context
937            .last_mut()
938            .ok_or(InstructionError::CallDepth)? = Some(SyscallContext {
939            check_aligned,
940            check_size,
941            orig_account_lengths,
942            allocator,
943        });
944        Ok(())
945    }
946
947    // Should alignment be enforced during user pointer translation
948    pub fn get_check_aligned(&self) -> bool {
949        self.syscall_context
950            .last()
951            .and_then(|context| context.as_ref())
952            .map(|context| context.check_aligned)
953            .unwrap_or(true)
954    }
955
956    // Set should type size be checked during user pointer translation
957    pub fn get_check_size(&self) -> bool {
958        self.syscall_context
959            .last()
960            .and_then(|context| context.as_ref())
961            .map(|context| context.check_size)
962            .unwrap_or(true)
963    }
964
965    /// Get the original account lengths
966    pub fn get_orig_account_lengths(&self) -> Result<&[usize], InstructionError> {
967        self.syscall_context
968            .last()
969            .and_then(|context| context.as_ref())
970            .map(|context| context.orig_account_lengths.as_slice())
971            .ok_or(InstructionError::CallDepth)
972    }
973
974    // Get this instruction's memory allocator
975    pub fn get_allocator(&self) -> Result<Rc<RefCell<dyn Alloc>>, InstructionError> {
976        self.syscall_context
977            .last()
978            .and_then(|context| context.as_ref())
979            .map(|context| context.allocator.clone())
980            .ok_or(InstructionError::CallDepth)
981    }
982}
983
984pub struct MockInvokeContextPreparation {
985    pub transaction_accounts: Vec<TransactionAccount>,
986    pub instruction_accounts: Vec<InstructionAccount>,
987}
988
989pub fn prepare_mock_invoke_context(
990    transaction_accounts: Vec<TransactionAccount>,
991    instruction_account_metas: Vec<AccountMeta>,
992    _program_indices: &[usize],
993) -> MockInvokeContextPreparation {
994    let mut instruction_accounts: Vec<InstructionAccount> =
995        Vec::with_capacity(instruction_account_metas.len());
996    for (instruction_account_index, account_meta) in instruction_account_metas.iter().enumerate() {
997        let index_in_transaction = transaction_accounts
998            .iter()
999            .position(|(key, _account)| *key == account_meta.pubkey)
1000            .unwrap_or(transaction_accounts.len());
1001        let index_in_callee = instruction_accounts
1002            .get(0..instruction_account_index)
1003            .unwrap()
1004            .iter()
1005            .position(|instruction_account| {
1006                instruction_account.index_in_transaction == index_in_transaction
1007            })
1008            .unwrap_or(instruction_account_index);
1009        instruction_accounts.push(InstructionAccount {
1010            index_in_transaction,
1011            index_in_caller: index_in_transaction,
1012            index_in_callee,
1013            is_signer: account_meta.is_signer,
1014            is_writable: account_meta.is_writable,
1015        });
1016    }
1017    MockInvokeContextPreparation {
1018        transaction_accounts,
1019        instruction_accounts,
1020    }
1021}
1022
1023pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
1024    loader_id: Pubkey,
1025    account_size: usize,
1026    mut callback: F,
1027) -> R {
1028    let program_indices = vec![0, 1];
1029    let transaction_accounts = vec![
1030        (
1031            loader_id,
1032            AccountSharedData::new(0, 0, &native_loader::id()),
1033        ),
1034        (
1035            Pubkey::new_unique(),
1036            AccountSharedData::new(1, 0, &loader_id),
1037        ),
1038        (
1039            Pubkey::new_unique(),
1040            AccountSharedData::new(2, account_size, &Pubkey::new_unique()),
1041        ),
1042    ];
1043    let instruction_accounts = vec![AccountMeta {
1044        pubkey: transaction_accounts.get(2).unwrap().0,
1045        is_signer: false,
1046        is_writable: false,
1047    }];
1048    let preparation =
1049        prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
1050    let mut transaction_context = TransactionContext::new(
1051        preparation.transaction_accounts,
1052        Some(Rent::default()),
1053        ComputeBudget::default().max_invoke_depth.saturating_add(1),
1054        1,
1055    );
1056    let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
1057    invoke_context
1058        .push(&preparation.instruction_accounts, &program_indices, &[])
1059        .unwrap();
1060    callback(&mut invoke_context)
1061}
1062
1063pub fn mock_process_instruction(
1064    loader_id: &Pubkey,
1065    mut program_indices: Vec<usize>,
1066    instruction_data: &[u8],
1067    transaction_accounts: Vec<TransactionAccount>,
1068    instruction_accounts: Vec<AccountMeta>,
1069    sysvar_cache_override: Option<&SysvarCache>,
1070    feature_set_override: Option<Arc<FeatureSet>>,
1071    expected_result: Result<(), InstructionError>,
1072    process_instruction: ProcessInstructionWithContext,
1073) -> Vec<AccountSharedData> {
1074    program_indices.insert(0, transaction_accounts.len());
1075    let mut preparation =
1076        prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
1077    let processor_account = AccountSharedData::new(0, 0, &native_loader::id());
1078    preparation
1079        .transaction_accounts
1080        .push((*loader_id, processor_account));
1081    let mut transaction_context = TransactionContext::new(
1082        preparation.transaction_accounts,
1083        Some(Rent::default()),
1084        ComputeBudget::default().max_invoke_depth.saturating_add(1),
1085        1,
1086    );
1087    let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
1088    if let Some(sysvar_cache) = sysvar_cache_override {
1089        invoke_context.sysvar_cache = Cow::Borrowed(sysvar_cache);
1090    }
1091    if let Some(feature_set) = feature_set_override {
1092        invoke_context.feature_set = feature_set;
1093    }
1094    let result = invoke_context
1095        .push(
1096            &preparation.instruction_accounts,
1097            &program_indices,
1098            instruction_data,
1099        )
1100        .and_then(|_| process_instruction(1, &mut invoke_context));
1101    let pop_result = invoke_context.pop();
1102    assert_eq!(result.and(pop_result), expected_result);
1103    let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
1104    transaction_accounts.pop();
1105    transaction_accounts
1106}
1107
1108#[cfg(test)]
1109mod tests {
1110    use {
1111        super::*,
1112        crate::compute_budget,
1113        serde::{Deserialize, Serialize},
1114        solana_sdk::account::WritableAccount,
1115    };
1116
1117    #[derive(Debug, Serialize, Deserialize)]
1118    enum MockInstruction {
1119        NoopSuccess,
1120        NoopFail,
1121        ModifyOwned,
1122        ModifyNotOwned,
1123        ModifyReadonly,
1124        UnbalancedPush,
1125        UnbalancedPop,
1126        ConsumeComputeUnits {
1127            compute_units_to_consume: u64,
1128            desired_result: Result<(), InstructionError>,
1129        },
1130        Resize {
1131            new_len: u64,
1132        },
1133    }
1134
1135    #[test]
1136    fn test_program_entry_debug() {
1137        #[allow(clippy::unnecessary_wraps)]
1138        fn mock_process_instruction(
1139            _first_instruction_account: usize,
1140            _invoke_context: &mut InvokeContext,
1141        ) -> Result<(), InstructionError> {
1142            Ok(())
1143        }
1144        #[allow(clippy::unnecessary_wraps)]
1145        fn mock_ix_processor(
1146            _first_instruction_account: usize,
1147            _invoke_context: &mut InvokeContext,
1148        ) -> Result<(), InstructionError> {
1149            Ok(())
1150        }
1151        let builtin_programs = &[
1152            BuiltinProgram {
1153                program_id: solana_sdk::pubkey::new_rand(),
1154                process_instruction: mock_process_instruction,
1155            },
1156            BuiltinProgram {
1157                program_id: solana_sdk::pubkey::new_rand(),
1158                process_instruction: mock_ix_processor,
1159            },
1160        ];
1161        assert!(!format!("{:?}", builtin_programs).is_empty());
1162    }
1163
1164    #[allow(clippy::integer_arithmetic)]
1165    fn mock_process_instruction(
1166        _first_instruction_account: usize,
1167        invoke_context: &mut InvokeContext,
1168    ) -> Result<(), InstructionError> {
1169        let transaction_context = &invoke_context.transaction_context;
1170        let instruction_context = transaction_context.get_current_instruction_context()?;
1171        let instruction_data = instruction_context.get_instruction_data();
1172        let program_id = instruction_context.get_last_program_key(transaction_context)?;
1173        let instruction_accounts = (0..4)
1174            .map(|instruction_account_index| InstructionAccount {
1175                index_in_transaction: instruction_account_index,
1176                index_in_caller: instruction_account_index,
1177                index_in_callee: instruction_account_index,
1178                is_signer: false,
1179                is_writable: false,
1180            })
1181            .collect::<Vec<_>>();
1182        assert_eq!(
1183            program_id,
1184            instruction_context
1185                .try_borrow_instruction_account(transaction_context, 0)?
1186                .get_owner()
1187        );
1188        assert_ne!(
1189            instruction_context
1190                .try_borrow_instruction_account(transaction_context, 1)?
1191                .get_owner(),
1192            instruction_context
1193                .try_borrow_instruction_account(transaction_context, 0)?
1194                .get_key()
1195        );
1196
1197        if let Ok(instruction) = bincode::deserialize(instruction_data) {
1198            match instruction {
1199                MockInstruction::NoopSuccess => (),
1200                MockInstruction::NoopFail => return Err(InstructionError::GenericError),
1201                MockInstruction::ModifyOwned => instruction_context
1202                    .try_borrow_instruction_account(transaction_context, 0)?
1203                    .set_data(&[1])?,
1204                MockInstruction::ModifyNotOwned => instruction_context
1205                    .try_borrow_instruction_account(transaction_context, 1)?
1206                    .set_data(&[1])?,
1207                MockInstruction::ModifyReadonly => instruction_context
1208                    .try_borrow_instruction_account(transaction_context, 2)?
1209                    .set_data(&[1])?,
1210                MockInstruction::UnbalancedPush => {
1211                    instruction_context
1212                        .try_borrow_instruction_account(transaction_context, 0)?
1213                        .checked_add_lamports(1)?;
1214                    let program_id = *transaction_context.get_key_of_account_at_index(3)?;
1215                    let metas = vec![
1216                        AccountMeta::new_readonly(
1217                            *transaction_context.get_key_of_account_at_index(0)?,
1218                            false,
1219                        ),
1220                        AccountMeta::new_readonly(
1221                            *transaction_context.get_key_of_account_at_index(1)?,
1222                            false,
1223                        ),
1224                    ];
1225                    let inner_instruction = Instruction::new_with_bincode(
1226                        program_id,
1227                        &MockInstruction::NoopSuccess,
1228                        metas,
1229                    );
1230                    let result = invoke_context.push(&instruction_accounts, &[3], &[]);
1231                    assert_eq!(result, Err(InstructionError::UnbalancedInstruction));
1232                    result?;
1233                    invoke_context
1234                        .native_invoke(inner_instruction, &[])
1235                        .and(invoke_context.pop())?;
1236                }
1237                MockInstruction::UnbalancedPop => instruction_context
1238                    .try_borrow_instruction_account(transaction_context, 0)?
1239                    .checked_add_lamports(1)?,
1240                MockInstruction::ConsumeComputeUnits {
1241                    compute_units_to_consume,
1242                    desired_result,
1243                } => {
1244                    invoke_context
1245                        .get_compute_meter()
1246                        .borrow_mut()
1247                        .consume(compute_units_to_consume)?;
1248                    return desired_result;
1249                }
1250                MockInstruction::Resize { new_len } => instruction_context
1251                    .try_borrow_instruction_account(transaction_context, 0)?
1252                    .set_data(&vec![0; new_len as usize])?,
1253            }
1254        } else {
1255            return Err(InstructionError::InvalidInstructionData);
1256        }
1257        Ok(())
1258    }
1259
1260    #[test]
1261    fn test_instruction_stack_height() {
1262        const MAX_DEPTH: usize = 10;
1263        let mut invoke_stack = vec![];
1264        let mut accounts = vec![];
1265        let mut instruction_accounts = vec![];
1266        for index in 0..MAX_DEPTH {
1267            invoke_stack.push(solana_sdk::pubkey::new_rand());
1268            accounts.push((
1269                solana_sdk::pubkey::new_rand(),
1270                AccountSharedData::new(index as u64, 1, invoke_stack.get(index).unwrap()),
1271            ));
1272            instruction_accounts.push(InstructionAccount {
1273                index_in_transaction: index,
1274                index_in_caller: index,
1275                index_in_callee: instruction_accounts.len(),
1276                is_signer: false,
1277                is_writable: true,
1278            });
1279        }
1280        for (index, program_id) in invoke_stack.iter().enumerate() {
1281            accounts.push((
1282                *program_id,
1283                AccountSharedData::new(1, 1, &solana_sdk::pubkey::Pubkey::default()),
1284            ));
1285            instruction_accounts.push(InstructionAccount {
1286                index_in_transaction: index,
1287                index_in_caller: index,
1288                index_in_callee: index,
1289                is_signer: false,
1290                is_writable: false,
1291            });
1292        }
1293        let mut transaction_context = TransactionContext::new(
1294            accounts,
1295            Some(Rent::default()),
1296            ComputeBudget::default().max_invoke_depth,
1297            1,
1298        );
1299        let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
1300
1301        // Check call depth increases and has a limit
1302        let mut depth_reached = 0;
1303        for _ in 0..invoke_stack.len() {
1304            if Err(InstructionError::CallDepth)
1305                == invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached], &[])
1306            {
1307                break;
1308            }
1309            depth_reached += 1;
1310        }
1311        assert_ne!(depth_reached, 0);
1312        assert!(depth_reached < MAX_DEPTH);
1313    }
1314
1315    #[test]
1316    fn test_process_instruction() {
1317        let callee_program_id = solana_sdk::pubkey::new_rand();
1318        let builtin_programs = &[BuiltinProgram {
1319            program_id: callee_program_id,
1320            process_instruction: mock_process_instruction,
1321        }];
1322
1323        let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
1324        let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand());
1325        let readonly_account = AccountSharedData::new(168, 1, &solana_sdk::pubkey::new_rand());
1326        let loader_account = AccountSharedData::new(0, 0, &native_loader::id());
1327        let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
1328        program_account.set_executable(true);
1329        let accounts = vec![
1330            (solana_sdk::pubkey::new_rand(), owned_account),
1331            (solana_sdk::pubkey::new_rand(), not_owned_account),
1332            (solana_sdk::pubkey::new_rand(), readonly_account),
1333            (callee_program_id, program_account),
1334            (solana_sdk::pubkey::new_rand(), loader_account),
1335        ];
1336        let metas = vec![
1337            AccountMeta::new(accounts.get(0).unwrap().0, false),
1338            AccountMeta::new(accounts.get(1).unwrap().0, false),
1339            AccountMeta::new_readonly(accounts.get(2).unwrap().0, false),
1340        ];
1341        let instruction_accounts = (0..4)
1342            .map(|instruction_account_index| InstructionAccount {
1343                index_in_transaction: instruction_account_index,
1344                index_in_caller: instruction_account_index,
1345                index_in_callee: instruction_account_index,
1346                is_signer: false,
1347                is_writable: instruction_account_index < 2,
1348            })
1349            .collect::<Vec<_>>();
1350        let mut transaction_context =
1351            TransactionContext::new(accounts, Some(Rent::default()), 2, 9);
1352        let mut invoke_context =
1353            InvokeContext::new_mock(&mut transaction_context, builtin_programs);
1354
1355        // Account modification tests
1356        let cases = vec![
1357            (MockInstruction::NoopSuccess, Ok(())),
1358            (
1359                MockInstruction::NoopFail,
1360                Err(InstructionError::GenericError),
1361            ),
1362            (MockInstruction::ModifyOwned, Ok(())),
1363            (
1364                MockInstruction::ModifyNotOwned,
1365                Err(InstructionError::ExternalAccountDataModified),
1366            ),
1367            (
1368                MockInstruction::ModifyReadonly,
1369                Err(InstructionError::ReadonlyDataModified),
1370            ),
1371            (
1372                MockInstruction::UnbalancedPush,
1373                Err(InstructionError::UnbalancedInstruction),
1374            ),
1375            (
1376                MockInstruction::UnbalancedPop,
1377                Err(InstructionError::UnbalancedInstruction),
1378            ),
1379        ];
1380        for case in cases {
1381            invoke_context
1382                .push(&instruction_accounts, &[4], &[])
1383                .unwrap();
1384            let inner_instruction =
1385                Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
1386            let result = invoke_context
1387                .native_invoke(inner_instruction, &[])
1388                .and(invoke_context.pop());
1389            assert_eq!(result, case.1);
1390        }
1391
1392        // Compute unit consumption tests
1393        let compute_units_to_consume = 10;
1394        let expected_results = vec![Ok(()), Err(InstructionError::GenericError)];
1395        for expected_result in expected_results {
1396            invoke_context
1397                .push(&instruction_accounts, &[4], &[])
1398                .unwrap();
1399            let inner_instruction = Instruction::new_with_bincode(
1400                callee_program_id,
1401                &MockInstruction::ConsumeComputeUnits {
1402                    compute_units_to_consume,
1403                    desired_result: expected_result.clone(),
1404                },
1405                metas.clone(),
1406            );
1407            let (inner_instruction_accounts, program_indices) = invoke_context
1408                .prepare_instruction(&inner_instruction, &[])
1409                .unwrap();
1410
1411            let mut compute_units_consumed = 0;
1412            let result = invoke_context.process_instruction(
1413                &inner_instruction.data,
1414                &inner_instruction_accounts,
1415                &program_indices,
1416                &mut compute_units_consumed,
1417                &mut ExecuteTimings::default(),
1418            );
1419
1420            // Because the instruction had compute cost > 0, then regardless of the execution result,
1421            // the number of compute units consumed should be a non-default which is something greater
1422            // than zero.
1423            assert!(compute_units_consumed > 0);
1424            assert_eq!(compute_units_consumed, compute_units_to_consume);
1425            assert_eq!(result, expected_result);
1426
1427            invoke_context.pop().unwrap();
1428        }
1429    }
1430
1431    #[test]
1432    fn test_invoke_context_compute_budget() {
1433        let accounts = vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
1434
1435        let mut transaction_context =
1436            TransactionContext::new(accounts, Some(Rent::default()), 1, 3);
1437        let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
1438        invoke_context.compute_budget =
1439            ComputeBudget::new(compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64);
1440
1441        invoke_context.push(&[], &[0], &[]).unwrap();
1442        assert_eq!(
1443            *invoke_context.get_compute_budget(),
1444            ComputeBudget::new(compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64)
1445        );
1446        invoke_context.pop().unwrap();
1447    }
1448
1449    #[test]
1450    fn test_process_instruction_accounts_data_meter() {
1451        solana_logger::setup();
1452
1453        let program_key = Pubkey::new_unique();
1454        let user_account_data_len = 123u64;
1455        let user_account =
1456            AccountSharedData::new(100, user_account_data_len as usize, &program_key);
1457        let dummy_account = AccountSharedData::new(10, 0, &program_key);
1458        let mut program_account = AccountSharedData::new(500, 500, &native_loader::id());
1459        program_account.set_executable(true);
1460        let accounts = vec![
1461            (Pubkey::new_unique(), user_account),
1462            (Pubkey::new_unique(), dummy_account),
1463            (program_key, program_account),
1464        ];
1465
1466        let builtin_programs = [BuiltinProgram {
1467            program_id: program_key,
1468            process_instruction: mock_process_instruction,
1469        }];
1470
1471        let mut transaction_context =
1472            TransactionContext::new(accounts, Some(Rent::default()), 1, 3);
1473        let mut invoke_context =
1474            InvokeContext::new_mock(&mut transaction_context, &builtin_programs);
1475
1476        invoke_context
1477            .accounts_data_meter
1478            .set_initial(user_account_data_len as u64);
1479        invoke_context
1480            .accounts_data_meter
1481            .set_maximum(user_account_data_len as u64 * 3);
1482        let remaining_account_data_len = invoke_context.accounts_data_meter.remaining();
1483
1484        let instruction_accounts = [
1485            InstructionAccount {
1486                index_in_transaction: 0,
1487                index_in_caller: 0,
1488                index_in_callee: 0,
1489                is_signer: false,
1490                is_writable: true,
1491            },
1492            InstructionAccount {
1493                index_in_transaction: 1,
1494                index_in_caller: 1,
1495                index_in_callee: 1,
1496                is_signer: false,
1497                is_writable: false,
1498            },
1499        ];
1500
1501        // Test 1: Resize the account to use up all the space; this must succeed
1502        {
1503            let new_len = user_account_data_len + remaining_account_data_len;
1504            let instruction_data =
1505                bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
1506
1507            let result = invoke_context.process_instruction(
1508                &instruction_data,
1509                &instruction_accounts,
1510                &[2],
1511                &mut 0,
1512                &mut ExecuteTimings::default(),
1513            );
1514
1515            assert!(result.is_ok());
1516            assert_eq!(
1517                invoke_context
1518                    .transaction_context
1519                    .accounts_resize_delta()
1520                    .unwrap(),
1521                user_account_data_len as i64 * 2
1522            );
1523        }
1524
1525        // Test 2: Resize the account to *the same size*, so not consuming any additional size; this must succeed
1526        {
1527            let new_len = user_account_data_len + remaining_account_data_len;
1528            let instruction_data =
1529                bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
1530
1531            let result = invoke_context.process_instruction(
1532                &instruction_data,
1533                &instruction_accounts,
1534                &[2],
1535                &mut 0,
1536                &mut ExecuteTimings::default(),
1537            );
1538
1539            assert!(result.is_ok());
1540            assert_eq!(
1541                invoke_context
1542                    .transaction_context
1543                    .accounts_resize_delta()
1544                    .unwrap(),
1545                user_account_data_len as i64 * 2
1546            );
1547        }
1548
1549        // Test 3: Resize the account to exceed the budget; this must succeed
1550        {
1551            let new_len = user_account_data_len + remaining_account_data_len + 1;
1552            let instruction_data =
1553                bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
1554
1555            let result = invoke_context.process_instruction(
1556                &instruction_data,
1557                &instruction_accounts,
1558                &[2],
1559                &mut 0,
1560                &mut ExecuteTimings::default(),
1561            );
1562
1563            assert!(result.is_ok());
1564            assert_eq!(
1565                invoke_context
1566                    .transaction_context
1567                    .accounts_resize_delta()
1568                    .unwrap(),
1569                user_account_data_len as i64 * 2 + 1
1570            );
1571        }
1572    }
1573}