Refactor: Remove KeyedAccount from program runtime (#22226)
				
					
				
			* Makes error handling in BorrowedAccount optional. Adds BorrowedAccount ::get_rent_epoch(). Exposes InstructionContext::get_index_in_transaction(). Turns accounts and account_keys into pinned boxed slices. * Introduces "unsafe" to InvokeContext::push(). * Turns &TransactionContext into &mut TransactionContext in InvokeContext. * Push and pop InstructionContext in InvokeContext. Makes test_process_cross_program and test_native_invoke symmetric. Removes the borrow check from test_invoke_context_verify. * Removes keyed_accounts from prepare_instruction() * Removes usage of invoke_stack. * Removes keyed_accounts from program-test. * Removes caller_write_privileges. * Removes keyed_accounts from BPF parameter (de-)serialization.
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							672fed04cb
						
					
				
				
					commit
					73e6038986
				
			@@ -6,7 +6,6 @@ use {
 | 
			
		||||
    },
 | 
			
		||||
    solana_sdk::{
 | 
			
		||||
        account::{AccountSharedData, ReadableAccount},
 | 
			
		||||
        account_utils::StateMut,
 | 
			
		||||
        bpf_loader_upgradeable::{self, UpgradeableLoaderState},
 | 
			
		||||
        compute_budget::ComputeBudget,
 | 
			
		||||
        feature_set::{
 | 
			
		||||
@@ -15,7 +14,7 @@ use {
 | 
			
		||||
        },
 | 
			
		||||
        hash::Hash,
 | 
			
		||||
        instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
 | 
			
		||||
        keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount},
 | 
			
		||||
        keyed_account::{create_keyed_accounts_unified, KeyedAccount},
 | 
			
		||||
        native_loader,
 | 
			
		||||
        pubkey::Pubkey,
 | 
			
		||||
        rent::Rent,
 | 
			
		||||
@@ -135,7 +134,7 @@ impl<'a> StackFrame<'a> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct InvokeContext<'a> {
 | 
			
		||||
    pub transaction_context: &'a TransactionContext,
 | 
			
		||||
    pub transaction_context: &'a mut TransactionContext,
 | 
			
		||||
    pub return_data: (Pubkey, Vec<u8>),
 | 
			
		||||
    invoke_stack: Vec<StackFrame<'a>>,
 | 
			
		||||
    rent: Rent,
 | 
			
		||||
@@ -158,7 +157,7 @@ pub struct InvokeContext<'a> {
 | 
			
		||||
impl<'a> InvokeContext<'a> {
 | 
			
		||||
    #[allow(clippy::too_many_arguments)]
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        transaction_context: &'a TransactionContext,
 | 
			
		||||
        transaction_context: &'a mut TransactionContext,
 | 
			
		||||
        rent: Rent,
 | 
			
		||||
        builtin_programs: &'a [BuiltinProgram],
 | 
			
		||||
        sysvars: &'a [(Pubkey, Vec<u8>)],
 | 
			
		||||
@@ -194,7 +193,7 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new_mock(
 | 
			
		||||
        transaction_context: &'a TransactionContext,
 | 
			
		||||
        transaction_context: &'a mut TransactionContext,
 | 
			
		||||
        builtin_programs: &'a [BuiltinProgram],
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Self::new(
 | 
			
		||||
@@ -218,8 +217,13 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        &mut self,
 | 
			
		||||
        instruction_accounts: &[InstructionAccount],
 | 
			
		||||
        program_indices: &[usize],
 | 
			
		||||
        instruction_data: &[u8],
 | 
			
		||||
    ) -> Result<(), InstructionError> {
 | 
			
		||||
        if self.invoke_stack.len() > self.compute_budget.max_invoke_depth {
 | 
			
		||||
        if self
 | 
			
		||||
            .transaction_context
 | 
			
		||||
            .get_instruction_context_stack_height()
 | 
			
		||||
            > self.compute_budget.max_invoke_depth
 | 
			
		||||
        {
 | 
			
		||||
            return Err(InstructionError::CallDepth);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -234,7 +238,11 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        {
 | 
			
		||||
            return Err(InstructionError::UnsupportedProgramId);
 | 
			
		||||
        }
 | 
			
		||||
        if self.invoke_stack.is_empty() {
 | 
			
		||||
        if self
 | 
			
		||||
            .transaction_context
 | 
			
		||||
            .get_instruction_context_stack_height()
 | 
			
		||||
            == 0
 | 
			
		||||
        {
 | 
			
		||||
            let mut compute_budget = self.compute_budget;
 | 
			
		||||
            if !self.feature_set.is_active(&tx_wide_compute_cap::id())
 | 
			
		||||
                && self.feature_set.is_active(&neon_evm_compute_budget::id())
 | 
			
		||||
@@ -278,15 +286,26 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
            };
 | 
			
		||||
            visit_each_account_once(instruction_accounts, &mut work)?;
 | 
			
		||||
        } else {
 | 
			
		||||
            let contains = self
 | 
			
		||||
                .invoke_stack
 | 
			
		||||
                .iter()
 | 
			
		||||
                .any(|frame| frame.program_id() == program_id);
 | 
			
		||||
            let is_last = if let Some(last_frame) = self.invoke_stack.last() {
 | 
			
		||||
                last_frame.program_id() == program_id
 | 
			
		||||
            } else {
 | 
			
		||||
                false
 | 
			
		||||
            };
 | 
			
		||||
            let contains = (0..self
 | 
			
		||||
                .transaction_context
 | 
			
		||||
                .get_instruction_context_stack_height())
 | 
			
		||||
                .any(|level| {
 | 
			
		||||
                    self.transaction_context
 | 
			
		||||
                        .get_instruction_context_at(level)
 | 
			
		||||
                        .and_then(|instruction_context| {
 | 
			
		||||
                            instruction_context.try_borrow_program_account(self.transaction_context)
 | 
			
		||||
                        })
 | 
			
		||||
                        .map(|program_account| Some(program_account.get_key()) == program_id)
 | 
			
		||||
                        .unwrap_or_else(|_| program_id.is_none())
 | 
			
		||||
                });
 | 
			
		||||
            let is_last = self
 | 
			
		||||
                .transaction_context
 | 
			
		||||
                .get_current_instruction_context()
 | 
			
		||||
                .and_then(|instruction_context| {
 | 
			
		||||
                    instruction_context.try_borrow_program_account(self.transaction_context)
 | 
			
		||||
                })
 | 
			
		||||
                .map(|program_account| Some(program_account.get_key()) == program_id)
 | 
			
		||||
                .unwrap_or_else(|_| program_id.is_none());
 | 
			
		||||
            if contains && !is_last {
 | 
			
		||||
                // Reentrancy not allowed unless caller is calling itself
 | 
			
		||||
                return Err(InstructionError::ReentrancyNotAllowed);
 | 
			
		||||
@@ -318,21 +337,27 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
            }))
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
        // Unsafe will be removed together with the keyed_accounts
 | 
			
		||||
        self.invoke_stack.push(StackFrame::new(
 | 
			
		||||
            program_indices.len(),
 | 
			
		||||
            create_keyed_accounts_unified(keyed_accounts.as_slice()),
 | 
			
		||||
            create_keyed_accounts_unified(unsafe {
 | 
			
		||||
                std::mem::transmute(keyed_accounts.as_slice())
 | 
			
		||||
            }),
 | 
			
		||||
        ));
 | 
			
		||||
        Ok(())
 | 
			
		||||
        self.transaction_context
 | 
			
		||||
            .push(program_indices, instruction_accounts, instruction_data)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Pop a stack frame from the invocation stack
 | 
			
		||||
    pub fn pop(&mut self) {
 | 
			
		||||
    pub fn pop(&mut self) -> Result<(), InstructionError> {
 | 
			
		||||
        self.invoke_stack.pop();
 | 
			
		||||
        self.transaction_context.pop()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Current depth of the invocation stack
 | 
			
		||||
    pub fn invoke_depth(&self) -> usize {
 | 
			
		||||
        self.invoke_stack.len()
 | 
			
		||||
        self.transaction_context
 | 
			
		||||
            .get_instruction_context_stack_height()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Verify the results of an instruction
 | 
			
		||||
@@ -341,12 +366,11 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        instruction_accounts: &[InstructionAccount],
 | 
			
		||||
        program_indices: &[usize],
 | 
			
		||||
    ) -> Result<(), InstructionError> {
 | 
			
		||||
        let program_id = self
 | 
			
		||||
            .invoke_stack
 | 
			
		||||
            .last()
 | 
			
		||||
            .and_then(|frame| frame.program_id())
 | 
			
		||||
            .ok_or(InstructionError::CallDepth)?;
 | 
			
		||||
        let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
 | 
			
		||||
        let program_id = self
 | 
			
		||||
            .transaction_context
 | 
			
		||||
            .get_program_key()
 | 
			
		||||
            .map_err(|_| InstructionError::CallDepth)?;
 | 
			
		||||
 | 
			
		||||
        // Verify all executable accounts have zero outstanding refs
 | 
			
		||||
        for account_index in program_indices.iter() {
 | 
			
		||||
@@ -414,23 +438,18 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
    fn verify_and_update(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        instruction_accounts: &[InstructionAccount],
 | 
			
		||||
        caller_write_privileges: Option<&[bool]>,
 | 
			
		||||
        before_instruction_context_push: bool,
 | 
			
		||||
    ) -> Result<(), InstructionError> {
 | 
			
		||||
        let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
 | 
			
		||||
        let program_id = self
 | 
			
		||||
            .invoke_stack
 | 
			
		||||
            .last()
 | 
			
		||||
            .and_then(|frame| frame.program_id())
 | 
			
		||||
            .ok_or(InstructionError::CallDepth)?;
 | 
			
		||||
        let rent = &self.rent;
 | 
			
		||||
        let log_collector = &self.log_collector;
 | 
			
		||||
        let transaction_context = self.transaction_context;
 | 
			
		||||
        let pre_accounts = &mut self.pre_accounts;
 | 
			
		||||
        let timings = &mut self.timings;
 | 
			
		||||
        let transaction_context = &self.transaction_context;
 | 
			
		||||
        let instruction_context = transaction_context.get_current_instruction_context()?;
 | 
			
		||||
        let program_id = transaction_context
 | 
			
		||||
            .get_program_key()
 | 
			
		||||
            .map_err(|_| InstructionError::CallDepth)?;
 | 
			
		||||
 | 
			
		||||
        // Verify the per-account instruction results
 | 
			
		||||
        let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
 | 
			
		||||
        let mut work = |index_in_instruction: usize, instruction_account: &InstructionAccount| {
 | 
			
		||||
        let mut work = |_index_in_instruction: usize, instruction_account: &InstructionAccount| {
 | 
			
		||||
            if instruction_account.index_in_transaction
 | 
			
		||||
                < transaction_context.get_number_of_accounts()
 | 
			
		||||
            {
 | 
			
		||||
@@ -438,13 +457,18 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
                    .get_key_of_account_at_index(instruction_account.index_in_transaction);
 | 
			
		||||
                let account = transaction_context
 | 
			
		||||
                    .get_account_at_index(instruction_account.index_in_transaction);
 | 
			
		||||
                let is_writable = if let Some(caller_write_privileges) = caller_write_privileges {
 | 
			
		||||
                    caller_write_privileges[index_in_instruction]
 | 
			
		||||
                let is_writable = if before_instruction_context_push {
 | 
			
		||||
                    instruction_context
 | 
			
		||||
                        .try_borrow_account(
 | 
			
		||||
                            self.transaction_context,
 | 
			
		||||
                            instruction_account.index_in_caller,
 | 
			
		||||
                        )?
 | 
			
		||||
                        .is_writable()
 | 
			
		||||
                } else {
 | 
			
		||||
                    instruction_account.is_writable
 | 
			
		||||
                };
 | 
			
		||||
                // Find the matching PreAccount
 | 
			
		||||
                for pre_account in pre_accounts.iter_mut() {
 | 
			
		||||
                for pre_account in self.pre_accounts.iter_mut() {
 | 
			
		||||
                    if key == pre_account.key() {
 | 
			
		||||
                        {
 | 
			
		||||
                            // Verify account has no outstanding references
 | 
			
		||||
@@ -457,15 +481,15 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
                            .verify(
 | 
			
		||||
                                program_id,
 | 
			
		||||
                                is_writable,
 | 
			
		||||
                                rent,
 | 
			
		||||
                                &self.rent,
 | 
			
		||||
                                &account,
 | 
			
		||||
                                timings,
 | 
			
		||||
                                &mut self.timings,
 | 
			
		||||
                                false,
 | 
			
		||||
                                do_support_realloc,
 | 
			
		||||
                            )
 | 
			
		||||
                            .map_err(|err| {
 | 
			
		||||
                                ic_logger_msg!(
 | 
			
		||||
                                    log_collector,
 | 
			
		||||
                                    self.log_collector,
 | 
			
		||||
                                    "failed to verify account {}: {}",
 | 
			
		||||
                                    key,
 | 
			
		||||
                                    err
 | 
			
		||||
@@ -502,7 +526,7 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        instruction: Instruction,
 | 
			
		||||
        signers: &[Pubkey],
 | 
			
		||||
    ) -> Result<(), InstructionError> {
 | 
			
		||||
        let (instruction_accounts, caller_write_privileges, program_indices) =
 | 
			
		||||
        let (instruction_accounts, program_indices) =
 | 
			
		||||
            self.prepare_instruction(&instruction, signers)?;
 | 
			
		||||
        let mut prev_account_sizes = Vec::with_capacity(instruction_accounts.len());
 | 
			
		||||
        for instruction_account in instruction_accounts.iter() {
 | 
			
		||||
@@ -519,7 +543,6 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        self.process_instruction(
 | 
			
		||||
            &instruction.data,
 | 
			
		||||
            &instruction_accounts,
 | 
			
		||||
            Some(&caller_write_privileges),
 | 
			
		||||
            &program_indices,
 | 
			
		||||
            &mut compute_units_consumed,
 | 
			
		||||
        )?;
 | 
			
		||||
@@ -556,11 +579,11 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        &mut self,
 | 
			
		||||
        instruction: &Instruction,
 | 
			
		||||
        signers: &[Pubkey],
 | 
			
		||||
    ) -> Result<(Vec<InstructionAccount>, Vec<bool>, Vec<usize>), InstructionError> {
 | 
			
		||||
    ) -> Result<(Vec<InstructionAccount>, Vec<usize>), InstructionError> {
 | 
			
		||||
        // Finds the index of each account in the instruction by its pubkey.
 | 
			
		||||
        // Then normalizes / unifies the privileges of duplicate accounts.
 | 
			
		||||
        // Note: This works like visit_each_account_once() and is an O(n^2) algorithm too.
 | 
			
		||||
        let caller_keyed_accounts = self.get_keyed_accounts()?;
 | 
			
		||||
        let instruction_context = self.transaction_context.get_current_instruction_context()?;
 | 
			
		||||
        let mut deduplicated_instruction_accounts: Vec<InstructionAccount> = Vec::new();
 | 
			
		||||
        let mut duplicate_indicies = Vec::with_capacity(instruction.accounts.len());
 | 
			
		||||
        for account_meta in instruction.accounts.iter() {
 | 
			
		||||
@@ -587,9 +610,8 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
                instruction_account.is_signer |= account_meta.is_signer;
 | 
			
		||||
                instruction_account.is_writable |= account_meta.is_writable;
 | 
			
		||||
            } else {
 | 
			
		||||
                let index_in_caller = caller_keyed_accounts
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .position(|keyed_account| *keyed_account.unsigned_key() == account_meta.pubkey)
 | 
			
		||||
                let index_in_caller = instruction_context
 | 
			
		||||
                    .find_index_of_account(self.transaction_context, &account_meta.pubkey)
 | 
			
		||||
                    .ok_or_else(|| {
 | 
			
		||||
                        ic_msg!(
 | 
			
		||||
                            self,
 | 
			
		||||
@@ -598,6 +620,33 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
                        );
 | 
			
		||||
                        InstructionError::MissingAccount
 | 
			
		||||
                    })?;
 | 
			
		||||
                let borrowed_account = instruction_context
 | 
			
		||||
                    .try_borrow_account(self.transaction_context, index_in_caller)?;
 | 
			
		||||
 | 
			
		||||
                // Readonly in caller cannot become writable in callee
 | 
			
		||||
                if account_meta.is_writable && !borrowed_account.is_writable() {
 | 
			
		||||
                    ic_msg!(
 | 
			
		||||
                        self,
 | 
			
		||||
                        "{}'s writable privilege escalated",
 | 
			
		||||
                        borrowed_account.get_key(),
 | 
			
		||||
                    );
 | 
			
		||||
                    return Err(InstructionError::PrivilegeEscalation);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // To be signed in the callee,
 | 
			
		||||
                // it must be either signed in the caller or by the program
 | 
			
		||||
                if account_meta.is_signer
 | 
			
		||||
                    && !(borrowed_account.is_signer()
 | 
			
		||||
                        || signers.contains(borrowed_account.get_key()))
 | 
			
		||||
                {
 | 
			
		||||
                    ic_msg!(
 | 
			
		||||
                        self,
 | 
			
		||||
                        "{}'s signer privilege escalated",
 | 
			
		||||
                        borrowed_account.get_key()
 | 
			
		||||
                    );
 | 
			
		||||
                    return Err(InstructionError::PrivilegeEscalation);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                duplicate_indicies.push(deduplicated_instruction_accounts.len());
 | 
			
		||||
                deduplicated_instruction_accounts.push(InstructionAccount {
 | 
			
		||||
                    index_in_transaction,
 | 
			
		||||
@@ -612,66 +661,25 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
            .map(|duplicate_index| deduplicated_instruction_accounts[duplicate_index].clone())
 | 
			
		||||
            .collect();
 | 
			
		||||
 | 
			
		||||
        // Check for privilege escalation
 | 
			
		||||
        let caller_write_privileges = instruction_accounts
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|instruction_account| {
 | 
			
		||||
                let keyed_account = &caller_keyed_accounts[instruction_account.index_in_caller];
 | 
			
		||||
 | 
			
		||||
                // Readonly in caller cannot become writable in callee
 | 
			
		||||
                if instruction_account.is_writable && !keyed_account.is_writable() {
 | 
			
		||||
                    ic_msg!(
 | 
			
		||||
                        self,
 | 
			
		||||
                        "{}'s writable privilege escalated",
 | 
			
		||||
                        keyed_account.unsigned_key(),
 | 
			
		||||
                    );
 | 
			
		||||
                    return Err(InstructionError::PrivilegeEscalation);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // To be signed in the callee,
 | 
			
		||||
                // it must be either signed in the caller or by the program
 | 
			
		||||
                if instruction_account.is_signer
 | 
			
		||||
                    && !(keyed_account.signer_key().is_some()
 | 
			
		||||
                        || signers.contains(keyed_account.unsigned_key()))
 | 
			
		||||
                {
 | 
			
		||||
                    ic_msg!(
 | 
			
		||||
                        self,
 | 
			
		||||
                        "{}'s signer privilege escalated",
 | 
			
		||||
                        keyed_account.unsigned_key()
 | 
			
		||||
                    );
 | 
			
		||||
                    return Err(InstructionError::PrivilegeEscalation);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Ok(keyed_account.is_writable())
 | 
			
		||||
            })
 | 
			
		||||
            .collect::<Result<Vec<bool>, InstructionError>>()?;
 | 
			
		||||
 | 
			
		||||
        // Find and validate executables / program accounts
 | 
			
		||||
        let callee_program_id = instruction.program_id;
 | 
			
		||||
        let program_account_index = caller_keyed_accounts
 | 
			
		||||
            .iter()
 | 
			
		||||
            .find(|keyed_account| &callee_program_id == keyed_account.unsigned_key())
 | 
			
		||||
            .and_then(|_keyed_account| {
 | 
			
		||||
                self.transaction_context
 | 
			
		||||
                    .find_index_of_program_account(&callee_program_id)
 | 
			
		||||
            })
 | 
			
		||||
        let program_account_index = instruction_context
 | 
			
		||||
            .find_index_of_account(self.transaction_context, &callee_program_id)
 | 
			
		||||
            .ok_or_else(|| {
 | 
			
		||||
                ic_msg!(self, "Unknown program {}", callee_program_id);
 | 
			
		||||
                InstructionError::MissingAccount
 | 
			
		||||
            })?;
 | 
			
		||||
        let program_account = self
 | 
			
		||||
            .transaction_context
 | 
			
		||||
            .get_account_at_index(program_account_index)
 | 
			
		||||
            .borrow();
 | 
			
		||||
        if !program_account.executable() {
 | 
			
		||||
        let borrowed_program_account = instruction_context
 | 
			
		||||
            .try_borrow_account(self.transaction_context, program_account_index)?;
 | 
			
		||||
        if !borrowed_program_account.is_executable() {
 | 
			
		||||
            ic_msg!(self, "Account {} is not executable", callee_program_id);
 | 
			
		||||
            return Err(InstructionError::AccountNotExecutable);
 | 
			
		||||
        }
 | 
			
		||||
        let mut program_indices = vec![];
 | 
			
		||||
        if program_account.owner() == &bpf_loader_upgradeable::id() {
 | 
			
		||||
        if borrowed_program_account.get_owner() == &bpf_loader_upgradeable::id() {
 | 
			
		||||
            if let UpgradeableLoaderState::Program {
 | 
			
		||||
                programdata_address,
 | 
			
		||||
            } = program_account.state()?
 | 
			
		||||
            } = borrowed_program_account.get_state()?
 | 
			
		||||
            {
 | 
			
		||||
                if let Some(programdata_account_index) = self
 | 
			
		||||
                    .transaction_context
 | 
			
		||||
@@ -695,21 +703,16 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
                return Err(InstructionError::MissingAccount);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        program_indices.push(program_account_index);
 | 
			
		||||
        program_indices.push(borrowed_program_account.get_index_in_transaction());
 | 
			
		||||
 | 
			
		||||
        Ok((
 | 
			
		||||
            instruction_accounts,
 | 
			
		||||
            caller_write_privileges,
 | 
			
		||||
            program_indices,
 | 
			
		||||
        ))
 | 
			
		||||
        Ok((instruction_accounts, program_indices))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Processes a cross-program instruction and returns how many compute units were used
 | 
			
		||||
    /// Processes an instruction and returns how many compute units were used
 | 
			
		||||
    pub fn process_instruction(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        instruction_data: &[u8],
 | 
			
		||||
        instruction_accounts: &[InstructionAccount],
 | 
			
		||||
        caller_write_privileges: Option<&[bool]>,
 | 
			
		||||
        program_indices: &[usize],
 | 
			
		||||
        compute_units_consumed: &mut u64,
 | 
			
		||||
    ) -> Result<(), InstructionError> {
 | 
			
		||||
@@ -719,14 +722,17 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
            .map(|index| *self.transaction_context.get_key_of_account_at_index(*index))
 | 
			
		||||
            .unwrap_or_else(native_loader::id);
 | 
			
		||||
 | 
			
		||||
        let is_lowest_invocation_level = self.invoke_stack.is_empty();
 | 
			
		||||
        let is_lowest_invocation_level = self
 | 
			
		||||
            .transaction_context
 | 
			
		||||
            .get_instruction_context_stack_height()
 | 
			
		||||
            == 0;
 | 
			
		||||
        if is_lowest_invocation_level {
 | 
			
		||||
            if let Some(instruction_recorder) = &self.instruction_recorder {
 | 
			
		||||
                instruction_recorder.borrow_mut().begin_next_recording();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // Verify the calling program hasn't misbehaved
 | 
			
		||||
            self.verify_and_update(instruction_accounts, caller_write_privileges)?;
 | 
			
		||||
            self.verify_and_update(instruction_accounts, true)?;
 | 
			
		||||
 | 
			
		||||
            // Record instruction
 | 
			
		||||
            if let Some(instruction_recorder) = &self.instruction_recorder {
 | 
			
		||||
@@ -748,7 +754,7 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let result = self
 | 
			
		||||
            .push(instruction_accounts, program_indices)
 | 
			
		||||
            .push(instruction_accounts, program_indices, instruction_data)
 | 
			
		||||
            .and_then(|_| {
 | 
			
		||||
                self.return_data = (program_id, Vec::new());
 | 
			
		||||
                let pre_remaining_units = self.compute_meter.borrow().get_remaining();
 | 
			
		||||
@@ -761,12 +767,12 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
                if is_lowest_invocation_level {
 | 
			
		||||
                    self.verify(instruction_accounts, program_indices)
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.verify_and_update(instruction_accounts, None)
 | 
			
		||||
                    self.verify_and_update(instruction_accounts, false)
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // Pop the invoke_stack to restore previous state
 | 
			
		||||
        self.pop();
 | 
			
		||||
        let _ = self.pop();
 | 
			
		||||
        result
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -775,14 +781,16 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        &mut self,
 | 
			
		||||
        instruction_data: &[u8],
 | 
			
		||||
    ) -> Result<(), InstructionError> {
 | 
			
		||||
        let keyed_accounts = self.get_keyed_accounts()?;
 | 
			
		||||
        let root_account = keyed_account_at_index(keyed_accounts, 0)
 | 
			
		||||
        let instruction_context = self.transaction_context.get_current_instruction_context()?;
 | 
			
		||||
        let borrowed_root_account = instruction_context
 | 
			
		||||
            .try_borrow_account(self.transaction_context, 0)
 | 
			
		||||
            .map_err(|_| InstructionError::UnsupportedProgramId)?;
 | 
			
		||||
        let root_id = root_account.unsigned_key();
 | 
			
		||||
        let owner_id = &root_account.owner()?;
 | 
			
		||||
        let root_id = borrowed_root_account.get_key();
 | 
			
		||||
        let owner_id = borrowed_root_account.get_owner();
 | 
			
		||||
        if solana_sdk::native_loader::check_id(owner_id) {
 | 
			
		||||
            for entry in self.builtin_programs {
 | 
			
		||||
                if entry.program_id == *root_id {
 | 
			
		||||
                    drop(borrowed_root_account);
 | 
			
		||||
                    // Call the builtin program
 | 
			
		||||
                    return (entry.process_instruction)(
 | 
			
		||||
                        1, // root_id to be skipped
 | 
			
		||||
@@ -792,6 +800,7 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if !self.feature_set.is_active(&remove_native_loader::id()) {
 | 
			
		||||
                drop(borrowed_root_account);
 | 
			
		||||
                let native_loader = NativeLoader::default();
 | 
			
		||||
                // Call the program via the native loader
 | 
			
		||||
                return native_loader.process_instruction(0, instruction_data, self);
 | 
			
		||||
@@ -799,6 +808,7 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        } else {
 | 
			
		||||
            for entry in self.builtin_programs {
 | 
			
		||||
                if entry.program_id == *owner_id {
 | 
			
		||||
                    drop(borrowed_root_account);
 | 
			
		||||
                    // Call the program via a builtin loader
 | 
			
		||||
                    return (entry.process_instruction)(
 | 
			
		||||
                        0, // no root_id was provided
 | 
			
		||||
@@ -811,27 +821,6 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
        Err(InstructionError::UnsupportedProgramId)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the program ID of the currently executing program
 | 
			
		||||
    pub fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
 | 
			
		||||
        self.invoke_stack
 | 
			
		||||
            .last()
 | 
			
		||||
            .and_then(|frame| frame.program_id())
 | 
			
		||||
            .ok_or(InstructionError::CallDepth)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the owner of the currently executing program
 | 
			
		||||
    pub fn get_loader(&self) -> Result<Pubkey, InstructionError> {
 | 
			
		||||
        let frame = self
 | 
			
		||||
            .invoke_stack
 | 
			
		||||
            .last()
 | 
			
		||||
            .ok_or(InstructionError::CallDepth)?;
 | 
			
		||||
        let first_instruction_account = frame
 | 
			
		||||
            .number_of_program_accounts
 | 
			
		||||
            .checked_sub(1)
 | 
			
		||||
            .ok_or(InstructionError::CallDepth)?;
 | 
			
		||||
        frame.keyed_accounts[first_instruction_account].owner()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes the first keyed account
 | 
			
		||||
    #[deprecated(
 | 
			
		||||
        since = "1.9.0",
 | 
			
		||||
@@ -857,17 +846,6 @@ impl<'a> InvokeContext<'a> {
 | 
			
		||||
            .ok_or(InstructionError::CallDepth)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the list of keyed accounts without the chain of program accounts
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note: This only contains the `KeyedAccount`s passed by the caller.
 | 
			
		||||
    pub fn get_instruction_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
 | 
			
		||||
        let frame = self
 | 
			
		||||
            .invoke_stack
 | 
			
		||||
            .last()
 | 
			
		||||
            .ok_or(InstructionError::CallDepth)?;
 | 
			
		||||
        Ok(&frame.keyed_accounts[frame.number_of_program_accounts..])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get this invocation's LogCollector
 | 
			
		||||
    pub fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
 | 
			
		||||
        self.log_collector.clone()
 | 
			
		||||
@@ -975,13 +953,13 @@ pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
 | 
			
		||||
    }];
 | 
			
		||||
    let preparation =
 | 
			
		||||
        prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices);
 | 
			
		||||
    let transaction_context = TransactionContext::new(
 | 
			
		||||
    let mut transaction_context = TransactionContext::new(
 | 
			
		||||
        preparation.transaction_accounts,
 | 
			
		||||
        ComputeBudget::default().max_invoke_depth,
 | 
			
		||||
        ComputeBudget::default().max_invoke_depth.saturating_add(1),
 | 
			
		||||
    );
 | 
			
		||||
    let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
 | 
			
		||||
    let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
 | 
			
		||||
    invoke_context
 | 
			
		||||
        .push(&preparation.instruction_accounts, &program_indices)
 | 
			
		||||
        .push(&preparation.instruction_accounts, &program_indices, &[])
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    callback(&mut invoke_context)
 | 
			
		||||
}
 | 
			
		||||
@@ -1003,15 +981,20 @@ pub fn mock_process_instruction_with_sysvars(
 | 
			
		||||
    preparation
 | 
			
		||||
        .transaction_accounts
 | 
			
		||||
        .push((*loader_id, processor_account));
 | 
			
		||||
    let transaction_context = TransactionContext::new(
 | 
			
		||||
    let mut transaction_context = TransactionContext::new(
 | 
			
		||||
        preparation.transaction_accounts,
 | 
			
		||||
        ComputeBudget::default().max_invoke_depth,
 | 
			
		||||
        ComputeBudget::default().max_invoke_depth.saturating_add(1),
 | 
			
		||||
    );
 | 
			
		||||
    let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
 | 
			
		||||
    let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
 | 
			
		||||
    invoke_context.sysvars = sysvars;
 | 
			
		||||
    let result = invoke_context
 | 
			
		||||
        .push(&preparation.instruction_accounts, &program_indices)
 | 
			
		||||
        .push(
 | 
			
		||||
            &preparation.instruction_accounts,
 | 
			
		||||
            &program_indices,
 | 
			
		||||
            instruction_data,
 | 
			
		||||
        )
 | 
			
		||||
        .and_then(|_| process_instruction(1, instruction_data, &mut invoke_context));
 | 
			
		||||
    invoke_context.pop().unwrap();
 | 
			
		||||
    assert_eq!(result, expected_result);
 | 
			
		||||
    let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
 | 
			
		||||
    transaction_accounts.pop();
 | 
			
		||||
@@ -1062,7 +1045,10 @@ mod tests {
 | 
			
		||||
    use {
 | 
			
		||||
        super::*,
 | 
			
		||||
        serde::{Deserialize, Serialize},
 | 
			
		||||
        solana_sdk::account::{ReadableAccount, WritableAccount},
 | 
			
		||||
        solana_sdk::{
 | 
			
		||||
            account::{ReadableAccount, WritableAccount},
 | 
			
		||||
            keyed_account::keyed_account_at_index,
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    #[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
@@ -1163,7 +1149,8 @@ mod tests {
 | 
			
		||||
        data: &[u8],
 | 
			
		||||
        invoke_context: &mut InvokeContext,
 | 
			
		||||
    ) -> Result<(), InstructionError> {
 | 
			
		||||
        let program_id = invoke_context.get_caller()?;
 | 
			
		||||
        let transaction_context = &invoke_context.transaction_context;
 | 
			
		||||
        let program_id = transaction_context.get_program_key()?;
 | 
			
		||||
        let keyed_accounts = invoke_context.get_keyed_accounts()?;
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            *program_id,
 | 
			
		||||
@@ -1242,14 +1229,14 @@ mod tests {
 | 
			
		||||
                is_writable: false,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        let transaction_context = TransactionContext::new(accounts, MAX_DEPTH);
 | 
			
		||||
        let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
 | 
			
		||||
        let mut transaction_context = TransactionContext::new(accounts, MAX_DEPTH);
 | 
			
		||||
        let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
 | 
			
		||||
 | 
			
		||||
        // Check call depth increases and has a limit
 | 
			
		||||
        let mut depth_reached = 0;
 | 
			
		||||
        for _ in 0..invoke_stack.len() {
 | 
			
		||||
            if Err(InstructionError::CallDepth)
 | 
			
		||||
                == invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached])
 | 
			
		||||
                == invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached], &[])
 | 
			
		||||
            {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -1277,12 +1264,13 @@ mod tests {
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            // modify account owned by the program
 | 
			
		||||
            transaction_context
 | 
			
		||||
            invoke_context
 | 
			
		||||
                .transaction_context
 | 
			
		||||
                .get_account_at_index(owned_index)
 | 
			
		||||
                .borrow_mut()
 | 
			
		||||
                .data_as_mut_slice()[0] = (MAX_DEPTH + owned_index) as u8;
 | 
			
		||||
            invoke_context
 | 
			
		||||
                .verify_and_update(&instruction_accounts, None)
 | 
			
		||||
                .verify_and_update(&instruction_accounts, false)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                invoke_context.pre_accounts[owned_index].data()[0],
 | 
			
		||||
@@ -1290,25 +1278,28 @@ mod tests {
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // modify account not owned by the program
 | 
			
		||||
            let data = transaction_context
 | 
			
		||||
            let data = invoke_context
 | 
			
		||||
                .transaction_context
 | 
			
		||||
                .get_account_at_index(not_owned_index)
 | 
			
		||||
                .borrow_mut()
 | 
			
		||||
                .data()[0];
 | 
			
		||||
            transaction_context
 | 
			
		||||
            invoke_context
 | 
			
		||||
                .transaction_context
 | 
			
		||||
                .get_account_at_index(not_owned_index)
 | 
			
		||||
                .borrow_mut()
 | 
			
		||||
                .data_as_mut_slice()[0] = (MAX_DEPTH + not_owned_index) as u8;
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                invoke_context.verify_and_update(&instruction_accounts, None),
 | 
			
		||||
                invoke_context.verify_and_update(&instruction_accounts, false),
 | 
			
		||||
                Err(InstructionError::ExternalAccountDataModified)
 | 
			
		||||
            );
 | 
			
		||||
            assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data);
 | 
			
		||||
            transaction_context
 | 
			
		||||
            invoke_context
 | 
			
		||||
                .transaction_context
 | 
			
		||||
                .get_account_at_index(not_owned_index)
 | 
			
		||||
                .borrow_mut()
 | 
			
		||||
                .data_as_mut_slice()[0] = data;
 | 
			
		||||
 | 
			
		||||
            invoke_context.pop();
 | 
			
		||||
            invoke_context.pop().unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1317,20 +1308,14 @@ mod tests {
 | 
			
		||||
        let accounts = vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
 | 
			
		||||
        let instruction_accounts = vec![];
 | 
			
		||||
        let program_indices = vec![0];
 | 
			
		||||
        let transaction_context = TransactionContext::new(accounts, 1);
 | 
			
		||||
        let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
 | 
			
		||||
        let mut transaction_context = TransactionContext::new(accounts, 1);
 | 
			
		||||
        let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
 | 
			
		||||
        invoke_context
 | 
			
		||||
            .push(&instruction_accounts, &program_indices)
 | 
			
		||||
            .push(&instruction_accounts, &program_indices, &[])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert!(invoke_context
 | 
			
		||||
            .verify(&instruction_accounts, &program_indices)
 | 
			
		||||
            .is_ok());
 | 
			
		||||
 | 
			
		||||
        let mut _borrowed = transaction_context.get_account_at_index(0).borrow();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            invoke_context.verify(&instruction_accounts, &program_indices),
 | 
			
		||||
            Err(InstructionError::AccountBorrowOutstanding)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -1367,12 +1352,15 @@ mod tests {
 | 
			
		||||
                is_writable: index_in_transaction < 2,
 | 
			
		||||
            })
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
        let transaction_context = TransactionContext::new(accounts, 2);
 | 
			
		||||
        let mut invoke_context = InvokeContext::new_mock(&transaction_context, builtin_programs);
 | 
			
		||||
        let mut transaction_context = TransactionContext::new(accounts, 2);
 | 
			
		||||
        let mut invoke_context =
 | 
			
		||||
            InvokeContext::new_mock(&mut transaction_context, builtin_programs);
 | 
			
		||||
 | 
			
		||||
        // External modification tests
 | 
			
		||||
        {
 | 
			
		||||
            invoke_context.push(&instruction_accounts, &[4]).unwrap();
 | 
			
		||||
            invoke_context
 | 
			
		||||
                .push(&instruction_accounts, &[4], &[])
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let inner_instruction = Instruction::new_with_bincode(
 | 
			
		||||
                callee_program_id,
 | 
			
		||||
                &MockInstruction::NoopSuccess,
 | 
			
		||||
@@ -1411,7 +1399,7 @@ mod tests {
 | 
			
		||||
                .borrow_mut()
 | 
			
		||||
                .data_as_mut_slice()[0] = 0;
 | 
			
		||||
 | 
			
		||||
            invoke_context.pop();
 | 
			
		||||
            invoke_context.pop().unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Internal modification tests
 | 
			
		||||
@@ -1432,18 +1420,22 @@ mod tests {
 | 
			
		||||
            ),
 | 
			
		||||
        ];
 | 
			
		||||
        for case in cases {
 | 
			
		||||
            invoke_context.push(&instruction_accounts, &[4]).unwrap();
 | 
			
		||||
            invoke_context
 | 
			
		||||
                .push(&instruction_accounts, &[4], &[])
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let inner_instruction =
 | 
			
		||||
                Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
 | 
			
		||||
            assert_eq!(invoke_context.native_invoke(inner_instruction, &[]), case.1);
 | 
			
		||||
            invoke_context.pop();
 | 
			
		||||
            invoke_context.pop().unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Compute unit consumption tests
 | 
			
		||||
        let compute_units_to_consume = 10;
 | 
			
		||||
        let expected_results = vec![Ok(()), Err(InstructionError::GenericError)];
 | 
			
		||||
        for expected_result in expected_results {
 | 
			
		||||
            invoke_context.push(&instruction_accounts, &[4]).unwrap();
 | 
			
		||||
            invoke_context
 | 
			
		||||
                .push(&instruction_accounts, &[4], &[])
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let inner_instruction = Instruction::new_with_bincode(
 | 
			
		||||
                callee_program_id,
 | 
			
		||||
                &MockInstruction::ConsumeComputeUnits {
 | 
			
		||||
@@ -1452,16 +1444,14 @@ mod tests {
 | 
			
		||||
                },
 | 
			
		||||
                metas.clone(),
 | 
			
		||||
            );
 | 
			
		||||
            let (inner_instruction_accounts, caller_write_privileges, program_indices) =
 | 
			
		||||
                invoke_context
 | 
			
		||||
                    .prepare_instruction(&inner_instruction, &[])
 | 
			
		||||
                    .unwrap();
 | 
			
		||||
            let (inner_instruction_accounts, program_indices) = invoke_context
 | 
			
		||||
                .prepare_instruction(&inner_instruction, &[])
 | 
			
		||||
                .unwrap();
 | 
			
		||||
 | 
			
		||||
            let mut compute_units_consumed = 0;
 | 
			
		||||
            let result = invoke_context.process_instruction(
 | 
			
		||||
                &inner_instruction.data,
 | 
			
		||||
                &inner_instruction_accounts,
 | 
			
		||||
                Some(&caller_write_privileges),
 | 
			
		||||
                &program_indices,
 | 
			
		||||
                &mut compute_units_consumed,
 | 
			
		||||
            );
 | 
			
		||||
@@ -1473,7 +1463,7 @@ mod tests {
 | 
			
		||||
            assert_eq!(compute_units_consumed, compute_units_to_consume);
 | 
			
		||||
            assert_eq!(result, expected_result);
 | 
			
		||||
 | 
			
		||||
            invoke_context.pop();
 | 
			
		||||
            invoke_context.pop().unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1487,18 +1477,18 @@ mod tests {
 | 
			
		||||
        let mut feature_set = FeatureSet::all_enabled();
 | 
			
		||||
        feature_set.deactivate(&tx_wide_compute_cap::id());
 | 
			
		||||
        feature_set.deactivate(&requestable_heap_size::id());
 | 
			
		||||
        let transaction_context = TransactionContext::new(accounts, 1);
 | 
			
		||||
        let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]);
 | 
			
		||||
        let mut transaction_context = TransactionContext::new(accounts, 1);
 | 
			
		||||
        let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
 | 
			
		||||
        invoke_context.feature_set = Arc::new(feature_set);
 | 
			
		||||
 | 
			
		||||
        invoke_context.push(&[], &[0]).unwrap();
 | 
			
		||||
        invoke_context.push(&[], &[0], &[]).unwrap();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            *invoke_context.get_compute_budget(),
 | 
			
		||||
            ComputeBudget::default()
 | 
			
		||||
        );
 | 
			
		||||
        invoke_context.pop();
 | 
			
		||||
        invoke_context.pop().unwrap();
 | 
			
		||||
 | 
			
		||||
        invoke_context.push(&[], &[1]).unwrap();
 | 
			
		||||
        invoke_context.push(&[], &[1], &[]).unwrap();
 | 
			
		||||
        let expected_compute_budget = ComputeBudget {
 | 
			
		||||
            max_units: 500_000,
 | 
			
		||||
            heap_size: Some(256_usize.saturating_mul(1024)),
 | 
			
		||||
@@ -1508,13 +1498,13 @@ mod tests {
 | 
			
		||||
            *invoke_context.get_compute_budget(),
 | 
			
		||||
            expected_compute_budget
 | 
			
		||||
        );
 | 
			
		||||
        invoke_context.pop();
 | 
			
		||||
        invoke_context.pop().unwrap();
 | 
			
		||||
 | 
			
		||||
        invoke_context.push(&[], &[0]).unwrap();
 | 
			
		||||
        invoke_context.push(&[], &[0], &[]).unwrap();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            *invoke_context.get_compute_budget(),
 | 
			
		||||
            ComputeBudget::default()
 | 
			
		||||
        );
 | 
			
		||||
        invoke_context.pop();
 | 
			
		||||
        invoke_context.pop().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -170,7 +170,7 @@ impl NativeLoader {
 | 
			
		||||
        invoke_context: &mut InvokeContext,
 | 
			
		||||
    ) -> Result<(), InstructionError> {
 | 
			
		||||
        let (program_id, name_vec) = {
 | 
			
		||||
            let program_id = invoke_context.get_caller()?;
 | 
			
		||||
            let program_id = invoke_context.transaction_context.get_program_key()?;
 | 
			
		||||
            let keyed_accounts = invoke_context.get_keyed_accounts()?;
 | 
			
		||||
            let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
 | 
			
		||||
            if native_loader::id() != *program_id {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user