gemachain_runtime/
message_processor.rs

1use crate::{
2    accounts::Accounts, ancestors::Ancestors, instruction_recorder::InstructionRecorder,
3    log_collector::LogCollector, rent_collector::RentCollector,
4};
5use log::*;
6use serde::{Deserialize, Serialize};
7use gemachain_measure::measure::Measure;
8use gemachain_program_runtime::{ExecuteDetailsTimings, Executors, InstructionProcessor, PreAccount};
9use gemachain_sdk::{
10    account::{AccountSharedData, ReadableAccount, WritableAccount},
11    compute_budget::ComputeBudget,
12    feature_set::{
13        demote_program_write_locks, neon_evm_compute_budget, tx_wide_compute_cap, FeatureSet,
14    },
15    fee_calculator::FeeCalculator,
16    hash::Hash,
17    ic_logger_msg,
18    instruction::{CompiledInstruction, Instruction, InstructionError},
19    keyed_account::{create_keyed_accounts_unified, KeyedAccount},
20    message::Message,
21    process_instruction::{
22        ComputeMeter, Executor, InvokeContext, InvokeContextStackFrame, Logger,
23        ProcessInstructionWithContext,
24    },
25    pubkey::Pubkey,
26    rent::Rent,
27    sysvar::instructions,
28    transaction::TransactionError,
29};
30use std::{cell::RefCell, rc::Rc, sync::Arc};
31
32pub struct ThisComputeMeter {
33    remaining: u64,
34}
35impl ComputeMeter for ThisComputeMeter {
36    fn consume(&mut self, amount: u64) -> Result<(), InstructionError> {
37        let exceeded = self.remaining < amount;
38        self.remaining = self.remaining.saturating_sub(amount);
39        if exceeded {
40            return Err(InstructionError::ComputationalBudgetExceeded);
41        }
42        Ok(())
43    }
44    fn get_remaining(&self) -> u64 {
45        self.remaining
46    }
47}
48pub struct ThisInvokeContext<'a> {
49    invoke_stack: Vec<InvokeContextStackFrame<'a>>,
50    rent: Rent,
51    pre_accounts: Vec<PreAccount>,
52    accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
53    programs: &'a [(Pubkey, ProcessInstructionWithContext)],
54    logger: Rc<RefCell<dyn Logger>>,
55    compute_budget: ComputeBudget,
56    #[allow(deprecated)]
57    bpf_compute_budget: gemachain_sdk::process_instruction::BpfComputeBudget,
58    compute_meter: Rc<RefCell<dyn ComputeMeter>>,
59    executors: Rc<RefCell<Executors>>,
60    instruction_recorder: Option<InstructionRecorder>,
61    feature_set: Arc<FeatureSet>,
62    pub timings: ExecuteDetailsTimings,
63    account_db: Arc<Accounts>,
64    ancestors: &'a Ancestors,
65    #[allow(clippy::type_complexity)]
66    sysvars: RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>>,
67    blockhash: &'a Hash,
68    fee_calculator: &'a FeeCalculator,
69    // return data and program_id that set it
70    return_data: Option<(Pubkey, Vec<u8>)>,
71}
72impl<'a> ThisInvokeContext<'a> {
73    #[allow(clippy::too_many_arguments)]
74    pub fn new(
75        program_id: &Pubkey,
76        rent: Rent,
77        message: &'a Message,
78        instruction: &'a CompiledInstruction,
79        program_indices: &[usize],
80        accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
81        programs: &'a [(Pubkey, ProcessInstructionWithContext)],
82        log_collector: Option<Rc<LogCollector>>,
83        compute_budget: ComputeBudget,
84        compute_meter: Rc<RefCell<dyn ComputeMeter>>,
85        executors: Rc<RefCell<Executors>>,
86        instruction_recorder: Option<InstructionRecorder>,
87        feature_set: Arc<FeatureSet>,
88        account_db: Arc<Accounts>,
89        ancestors: &'a Ancestors,
90        blockhash: &'a Hash,
91        fee_calculator: &'a FeeCalculator,
92    ) -> Result<Self, InstructionError> {
93        let pre_accounts = MessageProcessor::create_pre_accounts(message, instruction, accounts);
94        let compute_meter = if feature_set.is_active(&tx_wide_compute_cap::id()) {
95            compute_meter
96        } else {
97            Rc::new(RefCell::new(ThisComputeMeter {
98                remaining: compute_budget.max_units,
99            }))
100        };
101        let mut invoke_context = Self {
102            invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth),
103            rent,
104            pre_accounts,
105            accounts,
106            programs,
107            logger: Rc::new(RefCell::new(ThisLogger { log_collector })),
108            compute_budget,
109            bpf_compute_budget: compute_budget.into(),
110            compute_meter,
111            executors,
112            instruction_recorder,
113            feature_set,
114            timings: ExecuteDetailsTimings::default(),
115            account_db,
116            ancestors,
117            sysvars: RefCell::new(vec![]),
118            blockhash,
119            fee_calculator,
120            return_data: None,
121        };
122        let account_indices = (0..accounts.len()).collect::<Vec<usize>>();
123        invoke_context.push(
124            program_id,
125            message,
126            instruction,
127            program_indices,
128            &account_indices,
129        )?;
130        Ok(invoke_context)
131    }
132}
133impl<'a> InvokeContext for ThisInvokeContext<'a> {
134    fn push(
135        &mut self,
136        key: &Pubkey,
137        message: &Message,
138        instruction: &CompiledInstruction,
139        program_indices: &[usize],
140        account_indices: &[usize],
141    ) -> Result<(), InstructionError> {
142        if self.invoke_stack.len() > self.compute_budget.max_invoke_depth {
143            return Err(InstructionError::CallDepth);
144        }
145
146        let contains = self.invoke_stack.iter().any(|frame| frame.key == *key);
147        let is_last = if let Some(last_frame) = self.invoke_stack.last() {
148            last_frame.key == *key
149        } else {
150            false
151        };
152        if contains && !is_last {
153            // Reentrancy not allowed unless caller is calling itself
154            return Err(InstructionError::ReentrancyNotAllowed);
155        }
156
157        // Create the KeyedAccounts that will be passed to the program
158        let demote_program_write_locks = self
159            .feature_set
160            .is_active(&demote_program_write_locks::id());
161        let keyed_accounts = program_indices
162            .iter()
163            .map(|account_index| {
164                (
165                    false,
166                    false,
167                    &self.accounts[*account_index].0,
168                    &self.accounts[*account_index].1 as &RefCell<AccountSharedData>,
169                )
170            })
171            .chain(instruction.accounts.iter().map(|index_in_instruction| {
172                let index_in_instruction = *index_in_instruction as usize;
173                let account_index = account_indices[index_in_instruction];
174                (
175                    message.is_signer(index_in_instruction),
176                    message.is_writable(index_in_instruction, demote_program_write_locks),
177                    &self.accounts[account_index].0,
178                    &self.accounts[account_index].1 as &RefCell<AccountSharedData>,
179                )
180            }))
181            .collect::<Vec<_>>();
182        self.invoke_stack.push(InvokeContextStackFrame::new(
183            *key,
184            create_keyed_accounts_unified(keyed_accounts.as_slice()),
185        ));
186        Ok(())
187    }
188    fn pop(&mut self) {
189        self.invoke_stack.pop();
190    }
191    fn invoke_depth(&self) -> usize {
192        self.invoke_stack.len()
193    }
194    fn verify_and_update(
195        &mut self,
196        instruction: &CompiledInstruction,
197        account_indices: &[usize],
198        write_privileges: &[bool],
199    ) -> Result<(), InstructionError> {
200        let stack_frame = self
201            .invoke_stack
202            .last()
203            .ok_or(InstructionError::CallDepth)?;
204        let program_id = &stack_frame.key;
205        let rent = &self.rent;
206        let logger = self.get_logger();
207        let accounts = &self.accounts;
208        let pre_accounts = &mut self.pre_accounts;
209        let timings = &mut self.timings;
210
211        // Verify the per-account instruction results
212        let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
213        let mut work = |_unique_index: usize, index_in_instruction: usize| {
214            if index_in_instruction < write_privileges.len()
215                && index_in_instruction < account_indices.len()
216            {
217                let account_index = account_indices[index_in_instruction];
218                let (key, account) = &accounts[account_index];
219                let is_writable = write_privileges[index_in_instruction];
220                // Find the matching PreAccount
221                for pre_account in pre_accounts.iter_mut() {
222                    if key == pre_account.key() {
223                        {
224                            // Verify account has no outstanding references
225                            let _ = account
226                                .try_borrow_mut()
227                                .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
228                        }
229                        let account = account.borrow();
230                        pre_account
231                            .verify(program_id, is_writable, rent, &account, timings, false)
232                            .map_err(|err| {
233                                ic_logger_msg!(logger, "failed to verify account {}: {}", key, err);
234                                err
235                            })?;
236                        pre_sum += u128::from(pre_account.carats());
237                        post_sum += u128::from(account.carats());
238                        if is_writable && !pre_account.executable() {
239                            pre_account.update(&account);
240                        }
241                        return Ok(());
242                    }
243                }
244            }
245            Err(InstructionError::MissingAccount)
246        };
247        instruction.visit_each_account(&mut work)?;
248
249        // Verify that the total sum of all the carats did not change
250        if pre_sum != post_sum {
251            return Err(InstructionError::UnbalancedInstruction);
252        }
253        Ok(())
254    }
255    fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
256        self.invoke_stack
257            .last()
258            .map(|frame| &frame.key)
259            .ok_or(InstructionError::CallDepth)
260    }
261    fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> {
262        let stack_frame = &mut self
263            .invoke_stack
264            .last_mut()
265            .ok_or(InstructionError::CallDepth)?;
266        stack_frame.keyed_accounts_range.start =
267            stack_frame.keyed_accounts_range.start.saturating_add(1);
268        Ok(())
269    }
270    fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {
271        self.invoke_stack
272            .last()
273            .map(|frame| &frame.keyed_accounts[frame.keyed_accounts_range.clone()])
274            .ok_or(InstructionError::CallDepth)
275    }
276    fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] {
277        self.programs
278    }
279    fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
280        self.logger.clone()
281    }
282    #[allow(deprecated)]
283    fn get_bpf_compute_budget(&self) -> &gemachain_sdk::process_instruction::BpfComputeBudget {
284        &self.bpf_compute_budget
285    }
286    fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
287        self.compute_meter.clone()
288    }
289    fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>) {
290        self.executors.borrow_mut().insert(*pubkey, executor);
291    }
292    fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
293        self.executors.borrow().get(pubkey)
294    }
295    fn record_instruction(&self, instruction: &Instruction) {
296        if let Some(recorder) = &self.instruction_recorder {
297            recorder.record_instruction(instruction.clone());
298        }
299    }
300    fn is_feature_active(&self, feature_id: &Pubkey) -> bool {
301        self.feature_set.is_active(feature_id)
302    }
303    fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc<RefCell<AccountSharedData>>)> {
304        for (index, (key, account)) in self.accounts.iter().enumerate().rev() {
305            if key == pubkey {
306                return Some((index, account.clone()));
307            }
308        }
309        None
310    }
311    fn update_timing(
312        &mut self,
313        serialize_us: u64,
314        create_vm_us: u64,
315        execute_us: u64,
316        deserialize_us: u64,
317    ) {
318        self.timings.serialize_us += serialize_us;
319        self.timings.create_vm_us += create_vm_us;
320        self.timings.execute_us += execute_us;
321        self.timings.deserialize_us += deserialize_us;
322    }
323    fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
324        if let Ok(mut sysvars) = self.sysvars.try_borrow_mut() {
325            // Try share from cache
326            let mut result = sysvars
327                .iter()
328                .find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None });
329            if result.is_none() {
330                // Load it
331                result = self
332                    .account_db
333                    .load_with_fixed_root(self.ancestors, id)
334                    .map(|(account, _)| Rc::new(account.data().to_vec()));
335                // Cache it
336                sysvars.push((*id, result.clone()));
337            }
338            result
339        } else {
340            None
341        }
342    }
343    fn get_compute_budget(&self) -> &ComputeBudget {
344        &self.compute_budget
345    }
346    fn get_blockhash(&self) -> &Hash {
347        self.blockhash
348    }
349    fn get_fee_calculator(&self) -> &FeeCalculator {
350        self.fee_calculator
351    }
352    fn set_return_data(&mut self, return_data: Option<(Pubkey, Vec<u8>)>) {
353        self.return_data = return_data;
354    }
355    fn get_return_data(&self) -> &Option<(Pubkey, Vec<u8>)> {
356        &self.return_data
357    }
358}
359pub struct ThisLogger {
360    log_collector: Option<Rc<LogCollector>>,
361}
362impl Logger for ThisLogger {
363    fn log_enabled(&self) -> bool {
364        log_enabled!(log::Level::Info) || self.log_collector.is_some()
365    }
366    fn log(&self, message: &str) {
367        debug!("{}", message);
368        if let Some(log_collector) = &self.log_collector {
369            log_collector.log(message);
370        }
371    }
372}
373
374#[derive(Debug, Default, Clone, Deserialize, Serialize)]
375pub struct MessageProcessor {
376    #[serde(skip)]
377    instruction_processor: InstructionProcessor,
378}
379
380#[cfg(RUSTC_WITH_SPECIALIZATION)]
381impl ::gemachain_frozen_abi::abi_example::AbiExample for MessageProcessor {
382    fn example() -> Self {
383        // MessageProcessor's fields are #[serde(skip)]-ed and not Serialize
384        // so, just rely on Default anyway.
385        MessageProcessor::default()
386    }
387}
388
389impl MessageProcessor {
390    /// Add a static entrypoint to intercept instructions before the dynamic loader.
391    pub fn add_program(
392        &mut self,
393        program_id: Pubkey,
394        process_instruction: ProcessInstructionWithContext,
395    ) {
396        self.instruction_processor
397            .add_program(program_id, process_instruction);
398    }
399
400    /// Record the initial state of the accounts so that they can be compared
401    /// after the instruction is processed
402    pub fn create_pre_accounts(
403        message: &Message,
404        instruction: &CompiledInstruction,
405        accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
406    ) -> Vec<PreAccount> {
407        let mut pre_accounts = Vec::with_capacity(instruction.accounts.len());
408        {
409            let mut work = |_unique_index: usize, account_index: usize| {
410                if account_index < message.account_keys.len() && account_index < accounts.len() {
411                    let account = accounts[account_index].1.borrow();
412                    pre_accounts.push(PreAccount::new(&accounts[account_index].0, &account));
413                    return Ok(());
414                }
415                Err(InstructionError::MissingAccount)
416            };
417            let _ = instruction.visit_each_account(&mut work);
418        }
419        pre_accounts
420    }
421
422    /// Verify there are no outstanding borrows
423    pub fn verify_account_references(
424        accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
425        program_indices: &[usize],
426    ) -> Result<(), InstructionError> {
427        for account_index in program_indices.iter() {
428            accounts[*account_index]
429                .1
430                .try_borrow_mut()
431                .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
432        }
433        Ok(())
434    }
435
436    /// Verify the results of an instruction
437    #[allow(clippy::too_many_arguments)]
438    pub fn verify(
439        message: &Message,
440        instruction: &CompiledInstruction,
441        pre_accounts: &[PreAccount],
442        program_indices: &[usize],
443        accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
444        rent: &Rent,
445        timings: &mut ExecuteDetailsTimings,
446        logger: Rc<RefCell<dyn Logger>>,
447        demote_program_write_locks: bool,
448    ) -> Result<(), InstructionError> {
449        // Verify all executable accounts have zero outstanding refs
450        Self::verify_account_references(accounts, program_indices)?;
451
452        // Verify the per-account instruction results
453        let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
454        {
455            let program_id = instruction.program_id(&message.account_keys);
456            let mut work = |unique_index: usize, account_index: usize| {
457                {
458                    // Verify account has no outstanding references
459                    let _ = accounts[account_index]
460                        .1
461                        .try_borrow_mut()
462                        .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
463                }
464                let account = accounts[account_index].1.borrow();
465                pre_accounts[unique_index]
466                    .verify(
467                        program_id,
468                        message.is_writable(account_index, demote_program_write_locks),
469                        rent,
470                        &account,
471                        timings,
472                        true,
473                    )
474                    .map_err(|err| {
475                        ic_logger_msg!(
476                            logger,
477                            "failed to verify account {}: {}",
478                            pre_accounts[unique_index].key(),
479                            err
480                        );
481                        err
482                    })?;
483                pre_sum += u128::from(pre_accounts[unique_index].carats());
484                post_sum += u128::from(account.carats());
485                Ok(())
486            };
487            instruction.visit_each_account(&mut work)?;
488        }
489
490        // Verify that the total sum of all the carats did not change
491        if pre_sum != post_sum {
492            return Err(InstructionError::UnbalancedInstruction);
493        }
494        Ok(())
495    }
496
497    /// Execute an instruction
498    /// This method calls the instruction's program entrypoint method and verifies that the result of
499    /// the call does not violate the bank's accounting rules.
500    /// The accounts are committed back to the bank only if this function returns Ok(_).
501    #[allow(clippy::too_many_arguments)]
502    fn execute_instruction(
503        &self,
504        message: &Message,
505        instruction: &CompiledInstruction,
506        program_indices: &[usize],
507        accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
508        rent_collector: &RentCollector,
509        log_collector: Option<Rc<LogCollector>>,
510        executors: Rc<RefCell<Executors>>,
511        instruction_recorder: Option<InstructionRecorder>,
512        instruction_index: usize,
513        feature_set: Arc<FeatureSet>,
514        compute_budget: ComputeBudget,
515        compute_meter: Rc<RefCell<dyn ComputeMeter>>,
516        timings: &mut ExecuteDetailsTimings,
517        account_db: Arc<Accounts>,
518        ancestors: &Ancestors,
519        blockhash: &Hash,
520        fee_calculator: &FeeCalculator,
521    ) -> Result<(), InstructionError> {
522        // Fixup the special instructions key if present
523        // before the account pre-values are taken care of
524        for (pubkey, accont) in accounts.iter().take(message.account_keys.len()) {
525            if instructions::check_id(pubkey) {
526                let mut mut_account_ref = accont.borrow_mut();
527                instructions::store_current_index(
528                    mut_account_ref.data_as_mut_slice(),
529                    instruction_index as u16,
530                );
531                break;
532            }
533        }
534
535        let program_id = instruction.program_id(&message.account_keys);
536
537        let mut compute_budget = compute_budget;
538        if feature_set.is_active(&neon_evm_compute_budget::id())
539            && *program_id == crate::neon_evm_program::id()
540        {
541            // Bump the compute budget for neon_evm
542            compute_budget.max_units = compute_budget.max_units.max(500_000);
543            compute_budget.heap_size = Some(256 * 1024);
544        }
545
546        let programs = self.instruction_processor.programs();
547        let mut invoke_context = ThisInvokeContext::new(
548            program_id,
549            rent_collector.rent,
550            message,
551            instruction,
552            program_indices,
553            accounts,
554            programs,
555            log_collector,
556            compute_budget,
557            compute_meter,
558            executors,
559            instruction_recorder,
560            feature_set,
561            account_db,
562            ancestors,
563            blockhash,
564            fee_calculator,
565        )?;
566
567        self.instruction_processor.process_instruction(
568            program_id,
569            &instruction.data,
570            &mut invoke_context,
571        )?;
572        Self::verify(
573            message,
574            instruction,
575            &invoke_context.pre_accounts,
576            program_indices,
577            accounts,
578            &rent_collector.rent,
579            timings,
580            invoke_context.get_logger(),
581            invoke_context.is_feature_active(&demote_program_write_locks::id()),
582        )?;
583
584        timings.accumulate(&invoke_context.timings);
585
586        Ok(())
587    }
588
589    /// Process a message.
590    /// This method calls each instruction in the message over the set of loaded Accounts
591    /// The accounts are committed back to the bank only if every instruction succeeds
592    #[allow(clippy::too_many_arguments)]
593    #[allow(clippy::type_complexity)]
594    pub fn process_message(
595        &self,
596        message: &Message,
597        program_indices: &[Vec<usize>],
598        accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
599        rent_collector: &RentCollector,
600        log_collector: Option<Rc<LogCollector>>,
601        executors: Rc<RefCell<Executors>>,
602        instruction_recorders: Option<&[InstructionRecorder]>,
603        feature_set: Arc<FeatureSet>,
604        compute_budget: ComputeBudget,
605        compute_meter: Rc<RefCell<dyn ComputeMeter>>,
606        timings: &mut ExecuteDetailsTimings,
607        account_db: Arc<Accounts>,
608        ancestors: &Ancestors,
609        blockhash: Hash,
610        fee_calculator: FeeCalculator,
611    ) -> Result<(), TransactionError> {
612        for (instruction_index, instruction) in message.instructions.iter().enumerate() {
613            let mut time = Measure::start("execute_instruction");
614            let pre_remaining_units = compute_meter.borrow().get_remaining();
615            let instruction_recorder = instruction_recorders
616                .as_ref()
617                .map(|recorders| recorders[instruction_index].clone());
618            let err = self
619                .execute_instruction(
620                    message,
621                    instruction,
622                    &program_indices[instruction_index],
623                    accounts,
624                    rent_collector,
625                    log_collector.clone(),
626                    executors.clone(),
627                    instruction_recorder,
628                    instruction_index,
629                    feature_set.clone(),
630                    compute_budget,
631                    compute_meter.clone(),
632                    timings,
633                    account_db.clone(),
634                    ancestors,
635                    &blockhash,
636                    &fee_calculator,
637                )
638                .map_err(|err| TransactionError::InstructionError(instruction_index as u8, err));
639            time.stop();
640            let post_remaining_units = compute_meter.borrow().get_remaining();
641
642            timings.accumulate_program(
643                instruction.program_id(&message.account_keys),
644                time.as_us(),
645                pre_remaining_units - post_remaining_units,
646            );
647
648            err?;
649        }
650        Ok(())
651    }
652}
653
654#[cfg(test)]
655mod tests {
656    use super::*;
657    use gemachain_sdk::{
658        instruction::{AccountMeta, Instruction, InstructionError},
659        message::Message,
660        native_loader::{self, create_loadable_account_for_test},
661        process_instruction::MockComputeMeter,
662    };
663
664    #[test]
665    fn test_invoke_context() {
666        const MAX_DEPTH: usize = 10;
667        let mut invoke_stack = vec![];
668        let mut accounts = vec![];
669        let mut metas = vec![];
670        for i in 0..MAX_DEPTH {
671            invoke_stack.push(gemachain_sdk::pubkey::new_rand());
672            accounts.push((
673                gemachain_sdk::pubkey::new_rand(),
674                Rc::new(RefCell::new(AccountSharedData::new(
675                    i as u64,
676                    1,
677                    &invoke_stack[i],
678                ))),
679            ));
680            metas.push(AccountMeta::new(accounts[i].0, false));
681        }
682        for program_id in invoke_stack.iter() {
683            accounts.push((
684                *program_id,
685                Rc::new(RefCell::new(AccountSharedData::new(
686                    1,
687                    1,
688                    &gemachain_sdk::pubkey::Pubkey::default(),
689                ))),
690            ));
691            metas.push(AccountMeta::new(*program_id, false));
692        }
693        let account_indices = (0..accounts.len()).collect::<Vec<usize>>();
694
695        let message = Message::new(
696            &[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)],
697            None,
698        );
699        let ancestors = Ancestors::default();
700        let blockhash = Hash::default();
701        let fee_calculator = FeeCalculator::default();
702        let mut invoke_context = ThisInvokeContext::new(
703            &invoke_stack[0],
704            Rent::default(),
705            &message,
706            &message.instructions[0],
707            &[],
708            &accounts,
709            &[],
710            None,
711            ComputeBudget::default(),
712            Rc::new(RefCell::new(MockComputeMeter::default())),
713            Rc::new(RefCell::new(Executors::default())),
714            None,
715            Arc::new(FeatureSet::all_enabled()),
716            Arc::new(Accounts::default_for_tests()),
717            &ancestors,
718            &blockhash,
719            &fee_calculator,
720        )
721        .unwrap();
722
723        // Check call depth increases and has a limit
724        let mut depth_reached = 1;
725        for program_id in invoke_stack.iter().skip(1) {
726            if Err(InstructionError::CallDepth)
727                == invoke_context.push(
728                    program_id,
729                    &message,
730                    &message.instructions[0],
731                    &[],
732                    &account_indices,
733                )
734            {
735                break;
736            }
737            depth_reached += 1;
738        }
739        assert_ne!(depth_reached, 0);
740        assert!(depth_reached < MAX_DEPTH);
741
742        // Mock each invocation
743        for owned_index in (1..depth_reached).rev() {
744            let not_owned_index = owned_index - 1;
745            let metas = vec![
746                AccountMeta::new(accounts[not_owned_index].0, false),
747                AccountMeta::new(accounts[owned_index].0, false),
748            ];
749            let message = Message::new(
750                &[Instruction::new_with_bytes(
751                    invoke_stack[owned_index],
752                    &[0],
753                    metas,
754                )],
755                None,
756            );
757            let write_privileges: Vec<bool> = (0..message.account_keys.len())
758                .map(|i| message.is_writable(i, /*demote_program_write_locks=*/ true))
759                .collect();
760
761            // modify account owned by the program
762            accounts[owned_index].1.borrow_mut().data_as_mut_slice()[0] =
763                (MAX_DEPTH + owned_index) as u8;
764            invoke_context
765                .verify_and_update(
766                    &message.instructions[0],
767                    &account_indices[not_owned_index..owned_index + 1],
768                    &write_privileges,
769                )
770                .unwrap();
771            assert_eq!(
772                invoke_context.pre_accounts[owned_index].data()[0],
773                (MAX_DEPTH + owned_index) as u8
774            );
775
776            // modify account not owned by the program
777            let data = accounts[not_owned_index].1.borrow_mut().data()[0];
778            accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] =
779                (MAX_DEPTH + not_owned_index) as u8;
780            assert_eq!(
781                invoke_context.verify_and_update(
782                    &message.instructions[0],
783                    &account_indices[not_owned_index..owned_index + 1],
784                    &write_privileges,
785                ),
786                Err(InstructionError::ExternalAccountDataModified)
787            );
788            assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data);
789            accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] = data;
790
791            invoke_context.pop();
792        }
793    }
794
795    #[test]
796    fn test_verify_account_references() {
797        let accounts = vec![(
798            gemachain_sdk::pubkey::new_rand(),
799            Rc::new(RefCell::new(AccountSharedData::default())),
800        )];
801
802        assert!(MessageProcessor::verify_account_references(&accounts, &[0]).is_ok());
803
804        let mut _borrowed = accounts[0].1.borrow();
805        assert_eq!(
806            MessageProcessor::verify_account_references(&accounts, &[0]),
807            Err(InstructionError::AccountBorrowOutstanding)
808        );
809    }
810
811    #[test]
812    fn test_process_message_readonly_handling() {
813        #[derive(Serialize, Deserialize)]
814        enum MockSystemInstruction {
815            Correct,
816            AttemptCredit { carats: u64 },
817            AttemptDataChange { data: u8 },
818        }
819
820        fn mock_system_process_instruction(
821            _program_id: &Pubkey,
822            data: &[u8],
823            invoke_context: &mut dyn InvokeContext,
824        ) -> Result<(), InstructionError> {
825            let keyed_accounts = invoke_context.get_keyed_accounts()?;
826            if let Ok(instruction) = bincode::deserialize(data) {
827                match instruction {
828                    MockSystemInstruction::Correct => Ok(()),
829                    MockSystemInstruction::AttemptCredit { carats } => {
830                        keyed_accounts[0]
831                            .account
832                            .borrow_mut()
833                            .checked_sub_carats(carats)?;
834                        keyed_accounts[1]
835                            .account
836                            .borrow_mut()
837                            .checked_add_carats(carats)?;
838                        Ok(())
839                    }
840                    // Change data in a read-only account
841                    MockSystemInstruction::AttemptDataChange { data } => {
842                        keyed_accounts[1].account.borrow_mut().set_data(vec![data]);
843                        Ok(())
844                    }
845                }
846            } else {
847                Err(InstructionError::InvalidInstructionData)
848            }
849        }
850
851        let mock_system_program_id = Pubkey::new(&[2u8; 32]);
852        let rent_collector = RentCollector::default();
853        let mut message_processor = MessageProcessor::default();
854        message_processor.add_program(mock_system_program_id, mock_system_process_instruction);
855
856        let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
857            "mock_system_program",
858        )));
859        let accounts = vec![
860            (
861                gemachain_sdk::pubkey::new_rand(),
862                AccountSharedData::new_ref(100, 1, &mock_system_program_id),
863            ),
864            (
865                gemachain_sdk::pubkey::new_rand(),
866                AccountSharedData::new_ref(0, 1, &mock_system_program_id),
867            ),
868            (mock_system_program_id, program_account),
869        ];
870        let program_indices = vec![vec![2]];
871
872        let executors = Rc::new(RefCell::new(Executors::default()));
873        let ancestors = Ancestors::default();
874
875        let account_metas = vec![
876            AccountMeta::new(accounts[0].0, true),
877            AccountMeta::new_readonly(accounts[1].0, false),
878        ];
879        let message = Message::new(
880            &[Instruction::new_with_bincode(
881                mock_system_program_id,
882                &MockSystemInstruction::Correct,
883                account_metas.clone(),
884            )],
885            Some(&accounts[0].0),
886        );
887
888        let result = message_processor.process_message(
889            &message,
890            &program_indices,
891            &accounts,
892            &rent_collector,
893            None,
894            executors.clone(),
895            None,
896            Arc::new(FeatureSet::all_enabled()),
897            ComputeBudget::new(),
898            Rc::new(RefCell::new(MockComputeMeter::default())),
899            &mut ExecuteDetailsTimings::default(),
900            Arc::new(Accounts::default_for_tests()),
901            &ancestors,
902            Hash::default(),
903            FeeCalculator::default(),
904        );
905        assert_eq!(result, Ok(()));
906        assert_eq!(accounts[0].1.borrow().carats(), 100);
907        assert_eq!(accounts[1].1.borrow().carats(), 0);
908
909        let message = Message::new(
910            &[Instruction::new_with_bincode(
911                mock_system_program_id,
912                &MockSystemInstruction::AttemptCredit { carats: 50 },
913                account_metas.clone(),
914            )],
915            Some(&accounts[0].0),
916        );
917
918        let result = message_processor.process_message(
919            &message,
920            &program_indices,
921            &accounts,
922            &rent_collector,
923            None,
924            executors.clone(),
925            None,
926            Arc::new(FeatureSet::all_enabled()),
927            ComputeBudget::new(),
928            Rc::new(RefCell::new(MockComputeMeter::default())),
929            &mut ExecuteDetailsTimings::default(),
930            Arc::new(Accounts::default_for_tests()),
931            &ancestors,
932            Hash::default(),
933            FeeCalculator::default(),
934        );
935        assert_eq!(
936            result,
937            Err(TransactionError::InstructionError(
938                0,
939                InstructionError::ReadonlyCaratChange
940            ))
941        );
942
943        let message = Message::new(
944            &[Instruction::new_with_bincode(
945                mock_system_program_id,
946                &MockSystemInstruction::AttemptDataChange { data: 50 },
947                account_metas,
948            )],
949            Some(&accounts[0].0),
950        );
951
952        let result = message_processor.process_message(
953            &message,
954            &program_indices,
955            &accounts,
956            &rent_collector,
957            None,
958            executors,
959            None,
960            Arc::new(FeatureSet::all_enabled()),
961            ComputeBudget::new(),
962            Rc::new(RefCell::new(MockComputeMeter::default())),
963            &mut ExecuteDetailsTimings::default(),
964            Arc::new(Accounts::default_for_tests()),
965            &ancestors,
966            Hash::default(),
967            FeeCalculator::default(),
968        );
969        assert_eq!(
970            result,
971            Err(TransactionError::InstructionError(
972                0,
973                InstructionError::ReadonlyDataModified
974            ))
975        );
976    }
977
978    #[test]
979    fn test_process_message_duplicate_accounts() {
980        #[derive(Serialize, Deserialize)]
981        enum MockSystemInstruction {
982            BorrowFail,
983            MultiBorrowMut,
984            DoWork { carats: u64, data: u8 },
985        }
986
987        fn mock_system_process_instruction(
988            _program_id: &Pubkey,
989            data: &[u8],
990            invoke_context: &mut dyn InvokeContext,
991        ) -> Result<(), InstructionError> {
992            let keyed_accounts = invoke_context.get_keyed_accounts()?;
993            if let Ok(instruction) = bincode::deserialize(data) {
994                match instruction {
995                    MockSystemInstruction::BorrowFail => {
996                        let from_account = keyed_accounts[0].try_account_ref_mut()?;
997                        let dup_account = keyed_accounts[2].try_account_ref_mut()?;
998                        if from_account.carats() != dup_account.carats() {
999                            return Err(InstructionError::InvalidArgument);
1000                        }
1001                        Ok(())
1002                    }
1003                    MockSystemInstruction::MultiBorrowMut => {
1004                        let from_carats = {
1005                            let from_account = keyed_accounts[0].try_account_ref_mut()?;
1006                            from_account.carats()
1007                        };
1008                        let dup_carats = {
1009                            let dup_account = keyed_accounts[2].try_account_ref_mut()?;
1010                            dup_account.carats()
1011                        };
1012                        if from_carats != dup_carats {
1013                            return Err(InstructionError::InvalidArgument);
1014                        }
1015                        Ok(())
1016                    }
1017                    MockSystemInstruction::DoWork { carats, data } => {
1018                        {
1019                            let mut to_account = keyed_accounts[1].try_account_ref_mut()?;
1020                            let mut dup_account = keyed_accounts[2].try_account_ref_mut()?;
1021                            dup_account.checked_sub_carats(carats)?;
1022                            to_account.checked_add_carats(carats)?;
1023                            dup_account.set_data(vec![data]);
1024                        }
1025                        keyed_accounts[0]
1026                            .try_account_ref_mut()?
1027                            .checked_sub_carats(carats)?;
1028                        keyed_accounts[1]
1029                            .try_account_ref_mut()?
1030                            .checked_add_carats(carats)?;
1031                        Ok(())
1032                    }
1033                }
1034            } else {
1035                Err(InstructionError::InvalidInstructionData)
1036            }
1037        }
1038
1039        let mock_program_id = Pubkey::new(&[2u8; 32]);
1040        let rent_collector = RentCollector::default();
1041        let mut message_processor = MessageProcessor::default();
1042        message_processor.add_program(mock_program_id, mock_system_process_instruction);
1043
1044        let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
1045            "mock_system_program",
1046        )));
1047        let accounts = vec![
1048            (
1049                gemachain_sdk::pubkey::new_rand(),
1050                AccountSharedData::new_ref(100, 1, &mock_program_id),
1051            ),
1052            (
1053                gemachain_sdk::pubkey::new_rand(),
1054                AccountSharedData::new_ref(0, 1, &mock_program_id),
1055            ),
1056            (mock_program_id, program_account),
1057        ];
1058        let program_indices = vec![vec![2]];
1059
1060        let executors = Rc::new(RefCell::new(Executors::default()));
1061        let ancestors = Ancestors::default();
1062
1063        let account_metas = vec![
1064            AccountMeta::new(accounts[0].0, true),
1065            AccountMeta::new(accounts[1].0, false),
1066            AccountMeta::new(accounts[0].0, false),
1067        ];
1068
1069        // Try to borrow mut the same account
1070        let message = Message::new(
1071            &[Instruction::new_with_bincode(
1072                mock_program_id,
1073                &MockSystemInstruction::BorrowFail,
1074                account_metas.clone(),
1075            )],
1076            Some(&accounts[0].0),
1077        );
1078        let result = message_processor.process_message(
1079            &message,
1080            &program_indices,
1081            &accounts,
1082            &rent_collector,
1083            None,
1084            executors.clone(),
1085            None,
1086            Arc::new(FeatureSet::all_enabled()),
1087            ComputeBudget::new(),
1088            Rc::new(RefCell::new(MockComputeMeter::default())),
1089            &mut ExecuteDetailsTimings::default(),
1090            Arc::new(Accounts::default_for_tests()),
1091            &ancestors,
1092            Hash::default(),
1093            FeeCalculator::default(),
1094        );
1095        assert_eq!(
1096            result,
1097            Err(TransactionError::InstructionError(
1098                0,
1099                InstructionError::AccountBorrowFailed
1100            ))
1101        );
1102
1103        // Try to borrow mut the same account in a safe way
1104        let message = Message::new(
1105            &[Instruction::new_with_bincode(
1106                mock_program_id,
1107                &MockSystemInstruction::MultiBorrowMut,
1108                account_metas.clone(),
1109            )],
1110            Some(&accounts[0].0),
1111        );
1112        let result = message_processor.process_message(
1113            &message,
1114            &program_indices,
1115            &accounts,
1116            &rent_collector,
1117            None,
1118            executors.clone(),
1119            None,
1120            Arc::new(FeatureSet::all_enabled()),
1121            ComputeBudget::new(),
1122            Rc::new(RefCell::new(MockComputeMeter::default())),
1123            &mut ExecuteDetailsTimings::default(),
1124            Arc::new(Accounts::default_for_tests()),
1125            &ancestors,
1126            Hash::default(),
1127            FeeCalculator::default(),
1128        );
1129        assert_eq!(result, Ok(()));
1130
1131        // Do work on the same account but at different location in keyed_accounts[]
1132        let message = Message::new(
1133            &[Instruction::new_with_bincode(
1134                mock_program_id,
1135                &MockSystemInstruction::DoWork {
1136                    carats: 10,
1137                    data: 42,
1138                },
1139                account_metas,
1140            )],
1141            Some(&accounts[0].0),
1142        );
1143        let ancestors = Ancestors::default();
1144        let result = message_processor.process_message(
1145            &message,
1146            &program_indices,
1147            &accounts,
1148            &rent_collector,
1149            None,
1150            executors,
1151            None,
1152            Arc::new(FeatureSet::all_enabled()),
1153            ComputeBudget::new(),
1154            Rc::new(RefCell::new(MockComputeMeter::default())),
1155            &mut ExecuteDetailsTimings::default(),
1156            Arc::new(Accounts::default_for_tests()),
1157            &ancestors,
1158            Hash::default(),
1159            FeeCalculator::default(),
1160        );
1161        assert_eq!(result, Ok(()));
1162        assert_eq!(accounts[0].1.borrow().carats(), 80);
1163        assert_eq!(accounts[1].1.borrow().carats(), 20);
1164        assert_eq!(accounts[0].1.borrow().data(), &vec![42]);
1165    }
1166
1167    #[test]
1168    fn test_process_cross_program() {
1169        #[derive(Debug, Serialize, Deserialize)]
1170        enum MockInstruction {
1171            NoopSuccess,
1172            NoopFail,
1173            ModifyOwned,
1174            ModifyNotOwned,
1175            ModifyReadonly,
1176        }
1177
1178        fn mock_process_instruction(
1179            program_id: &Pubkey,
1180            data: &[u8],
1181            invoke_context: &mut dyn InvokeContext,
1182        ) -> Result<(), InstructionError> {
1183            let keyed_accounts = invoke_context.get_keyed_accounts()?;
1184            assert_eq!(*program_id, keyed_accounts[0].owner()?);
1185            assert_ne!(
1186                keyed_accounts[1].owner()?,
1187                *keyed_accounts[0].unsigned_key()
1188            );
1189
1190            if let Ok(instruction) = bincode::deserialize(data) {
1191                match instruction {
1192                    MockInstruction::NoopSuccess => (),
1193                    MockInstruction::NoopFail => return Err(InstructionError::GenericError),
1194                    MockInstruction::ModifyOwned => {
1195                        keyed_accounts[0].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
1196                    }
1197                    MockInstruction::ModifyNotOwned => {
1198                        keyed_accounts[1].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
1199                    }
1200                    MockInstruction::ModifyReadonly => {
1201                        keyed_accounts[2].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
1202                    }
1203                }
1204            } else {
1205                return Err(InstructionError::InvalidInstructionData);
1206            }
1207            Ok(())
1208        }
1209
1210        let caller_program_id = gemachain_sdk::pubkey::new_rand();
1211        let callee_program_id = gemachain_sdk::pubkey::new_rand();
1212
1213        let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
1214        let not_owned_account = AccountSharedData::new(84, 1, &gemachain_sdk::pubkey::new_rand());
1215        let readonly_account = AccountSharedData::new(168, 1, &caller_program_id);
1216        let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
1217        program_account.set_executable(true);
1218
1219        #[allow(unused_mut)]
1220        let accounts = vec![
1221            (
1222                gemachain_sdk::pubkey::new_rand(),
1223                Rc::new(RefCell::new(owned_account)),
1224            ),
1225            (
1226                gemachain_sdk::pubkey::new_rand(),
1227                Rc::new(RefCell::new(not_owned_account)),
1228            ),
1229            (
1230                gemachain_sdk::pubkey::new_rand(),
1231                Rc::new(RefCell::new(readonly_account)),
1232            ),
1233            (callee_program_id, Rc::new(RefCell::new(program_account))),
1234        ];
1235        let account_indices = [0, 1, 2];
1236        let program_indices = vec![3];
1237
1238        let programs: Vec<(_, ProcessInstructionWithContext)> =
1239            vec![(callee_program_id, mock_process_instruction)];
1240        let metas = vec![
1241            AccountMeta::new(accounts[0].0, false),
1242            AccountMeta::new(accounts[1].0, false),
1243            AccountMeta::new_readonly(accounts[2].0, false),
1244        ];
1245
1246        let caller_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2, 3]);
1247        let callee_instruction = Instruction::new_with_bincode(
1248            callee_program_id,
1249            &MockInstruction::NoopSuccess,
1250            metas.clone(),
1251        );
1252        let message = Message::new(&[callee_instruction], None);
1253
1254        let feature_set = FeatureSet::all_enabled();
1255        let demote_program_write_locks = feature_set.is_active(&demote_program_write_locks::id());
1256
1257        let ancestors = Ancestors::default();
1258        let blockhash = Hash::default();
1259        let fee_calculator = FeeCalculator::default();
1260        let mut invoke_context = ThisInvokeContext::new(
1261            &caller_program_id,
1262            Rent::default(),
1263            &message,
1264            &caller_instruction,
1265            &program_indices,
1266            &accounts,
1267            programs.as_slice(),
1268            None,
1269            ComputeBudget::default(),
1270            Rc::new(RefCell::new(MockComputeMeter::default())),
1271            Rc::new(RefCell::new(Executors::default())),
1272            None,
1273            Arc::new(feature_set),
1274            Arc::new(Accounts::default_for_tests()),
1275            &ancestors,
1276            &blockhash,
1277            &fee_calculator,
1278        )
1279        .unwrap();
1280
1281        // not owned account modified by the caller (before the invoke)
1282        let caller_write_privileges = message
1283            .account_keys
1284            .iter()
1285            .enumerate()
1286            .map(|(i, _)| message.is_writable(i, demote_program_write_locks))
1287            .collect::<Vec<bool>>();
1288        accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1;
1289        assert_eq!(
1290            InstructionProcessor::process_cross_program_instruction(
1291                &message,
1292                &program_indices,
1293                &account_indices,
1294                &caller_write_privileges,
1295                &mut invoke_context,
1296            ),
1297            Err(InstructionError::ExternalAccountDataModified)
1298        );
1299        accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 0;
1300
1301        // readonly account modified by the invoker
1302        accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1;
1303        assert_eq!(
1304            InstructionProcessor::process_cross_program_instruction(
1305                &message,
1306                &program_indices,
1307                &account_indices,
1308                &caller_write_privileges,
1309                &mut invoke_context,
1310            ),
1311            Err(InstructionError::ReadonlyDataModified)
1312        );
1313        accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0;
1314
1315        let cases = vec![
1316            (MockInstruction::NoopSuccess, Ok(())),
1317            (
1318                MockInstruction::NoopFail,
1319                Err(InstructionError::GenericError),
1320            ),
1321            (MockInstruction::ModifyOwned, Ok(())),
1322            (
1323                MockInstruction::ModifyNotOwned,
1324                Err(InstructionError::ExternalAccountDataModified),
1325            ),
1326        ];
1327
1328        for case in cases {
1329            let callee_instruction =
1330                Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
1331            let message = Message::new(&[callee_instruction], None);
1332
1333            let ancestors = Ancestors::default();
1334            let blockhash = Hash::default();
1335            let fee_calculator = FeeCalculator::default();
1336            let mut invoke_context = ThisInvokeContext::new(
1337                &caller_program_id,
1338                Rent::default(),
1339                &message,
1340                &caller_instruction,
1341                &program_indices,
1342                &accounts,
1343                programs.as_slice(),
1344                None,
1345                ComputeBudget::default(),
1346                Rc::new(RefCell::new(MockComputeMeter::default())),
1347                Rc::new(RefCell::new(Executors::default())),
1348                None,
1349                Arc::new(FeatureSet::all_enabled()),
1350                Arc::new(Accounts::default_for_tests()),
1351                &ancestors,
1352                &blockhash,
1353                &fee_calculator,
1354            )
1355            .unwrap();
1356
1357            let caller_write_privileges = message
1358                .account_keys
1359                .iter()
1360                .enumerate()
1361                .map(|(i, _)| message.is_writable(i, demote_program_write_locks))
1362                .collect::<Vec<bool>>();
1363            assert_eq!(
1364                InstructionProcessor::process_cross_program_instruction(
1365                    &message,
1366                    &program_indices,
1367                    &account_indices,
1368                    &caller_write_privileges,
1369                    &mut invoke_context,
1370                ),
1371                case.1
1372            );
1373        }
1374    }
1375
1376    #[test]
1377    fn test_native_invoke() {
1378        #[derive(Debug, Serialize, Deserialize)]
1379        enum MockInstruction {
1380            NoopSuccess,
1381            NoopFail,
1382            ModifyOwned,
1383            ModifyNotOwned,
1384            ModifyReadonly,
1385        }
1386
1387        fn mock_process_instruction(
1388            program_id: &Pubkey,
1389            data: &[u8],
1390            invoke_context: &mut dyn InvokeContext,
1391        ) -> Result<(), InstructionError> {
1392            let keyed_accounts = invoke_context.get_keyed_accounts()?;
1393            assert_eq!(*program_id, keyed_accounts[0].owner()?);
1394            assert_ne!(
1395                keyed_accounts[1].owner()?,
1396                *keyed_accounts[0].unsigned_key()
1397            );
1398
1399            if let Ok(instruction) = bincode::deserialize(data) {
1400                match instruction {
1401                    MockInstruction::NoopSuccess => (),
1402                    MockInstruction::NoopFail => return Err(InstructionError::GenericError),
1403                    MockInstruction::ModifyOwned => {
1404                        keyed_accounts[0].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
1405                    }
1406                    MockInstruction::ModifyNotOwned => {
1407                        keyed_accounts[1].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
1408                    }
1409                    MockInstruction::ModifyReadonly => {
1410                        keyed_accounts[2].try_account_ref_mut()?.data_as_mut_slice()[0] = 1
1411                    }
1412                }
1413            } else {
1414                return Err(InstructionError::InvalidInstructionData);
1415            }
1416            Ok(())
1417        }
1418
1419        let caller_program_id = gemachain_sdk::pubkey::new_rand();
1420        let callee_program_id = gemachain_sdk::pubkey::new_rand();
1421
1422        let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
1423        let not_owned_account = AccountSharedData::new(84, 1, &gemachain_sdk::pubkey::new_rand());
1424        let readonly_account = AccountSharedData::new(168, 1, &caller_program_id);
1425        let mut program_account = AccountSharedData::new(1, 0, &native_loader::id());
1426        program_account.set_executable(true);
1427
1428        #[allow(unused_mut)]
1429        let accounts = vec![
1430            (
1431                gemachain_sdk::pubkey::new_rand(),
1432                Rc::new(RefCell::new(owned_account)),
1433            ),
1434            (
1435                gemachain_sdk::pubkey::new_rand(),
1436                Rc::new(RefCell::new(not_owned_account)),
1437            ),
1438            (
1439                gemachain_sdk::pubkey::new_rand(),
1440                Rc::new(RefCell::new(readonly_account)),
1441            ),
1442            (callee_program_id, Rc::new(RefCell::new(program_account))),
1443        ];
1444        let program_indices = vec![3];
1445        let programs: Vec<(_, ProcessInstructionWithContext)> =
1446            vec![(callee_program_id, mock_process_instruction)];
1447        let metas = vec![
1448            AccountMeta::new(accounts[0].0, false),
1449            AccountMeta::new(accounts[1].0, false),
1450            AccountMeta::new_readonly(accounts[2].0, false),
1451        ];
1452
1453        let caller_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2, 3]);
1454        let callee_instruction = Instruction::new_with_bincode(
1455            callee_program_id,
1456            &MockInstruction::NoopSuccess,
1457            metas.clone(),
1458        );
1459        let message = Message::new(&[callee_instruction.clone()], None);
1460
1461        let feature_set = FeatureSet::all_enabled();
1462        let ancestors = Ancestors::default();
1463        let blockhash = Hash::default();
1464        let fee_calculator = FeeCalculator::default();
1465        let mut invoke_context = ThisInvokeContext::new(
1466            &caller_program_id,
1467            Rent::default(),
1468            &message,
1469            &caller_instruction,
1470            &program_indices,
1471            &accounts,
1472            programs.as_slice(),
1473            None,
1474            ComputeBudget::default(),
1475            Rc::new(RefCell::new(MockComputeMeter::default())),
1476            Rc::new(RefCell::new(Executors::default())),
1477            None,
1478            Arc::new(feature_set),
1479            Arc::new(Accounts::default_for_tests()),
1480            &ancestors,
1481            &blockhash,
1482            &fee_calculator,
1483        )
1484        .unwrap();
1485
1486        // not owned account modified by the invoker
1487        accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1;
1488        assert_eq!(
1489            InstructionProcessor::native_invoke(
1490                &mut invoke_context,
1491                callee_instruction.clone(),
1492                &[0, 1, 2, 3],
1493                &[]
1494            ),
1495            Err(InstructionError::ExternalAccountDataModified)
1496        );
1497        accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 0;
1498
1499        // readonly account modified by the invoker
1500        accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1;
1501        assert_eq!(
1502            InstructionProcessor::native_invoke(
1503                &mut invoke_context,
1504                callee_instruction,
1505                &[0, 1, 2, 3],
1506                &[]
1507            ),
1508            Err(InstructionError::ReadonlyDataModified)
1509        );
1510        accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0;
1511
1512        // Other test cases
1513        let cases = vec![
1514            (MockInstruction::NoopSuccess, Ok(())),
1515            (
1516                MockInstruction::NoopFail,
1517                Err(InstructionError::GenericError),
1518            ),
1519            (MockInstruction::ModifyOwned, Ok(())),
1520            (
1521                MockInstruction::ModifyNotOwned,
1522                Err(InstructionError::ExternalAccountDataModified),
1523            ),
1524            (
1525                MockInstruction::ModifyReadonly,
1526                Err(InstructionError::ReadonlyDataModified),
1527            ),
1528        ];
1529        for case in cases {
1530            let callee_instruction =
1531                Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
1532            let message = Message::new(&[callee_instruction.clone()], None);
1533
1534            let ancestors = Ancestors::default();
1535            let blockhash = Hash::default();
1536            let fee_calculator = FeeCalculator::default();
1537            let mut invoke_context = ThisInvokeContext::new(
1538                &caller_program_id,
1539                Rent::default(),
1540                &message,
1541                &caller_instruction,
1542                &program_indices,
1543                &accounts,
1544                programs.as_slice(),
1545                None,
1546                ComputeBudget::default(),
1547                Rc::new(RefCell::new(MockComputeMeter::default())),
1548                Rc::new(RefCell::new(Executors::default())),
1549                None,
1550                Arc::new(FeatureSet::all_enabled()),
1551                Arc::new(Accounts::default_for_tests()),
1552                &ancestors,
1553                &blockhash,
1554                &fee_calculator,
1555            )
1556            .unwrap();
1557
1558            assert_eq!(
1559                InstructionProcessor::native_invoke(
1560                    &mut invoke_context,
1561                    callee_instruction,
1562                    &[0, 1, 2, 3],
1563                    &[]
1564                ),
1565                case.1
1566            );
1567        }
1568    }
1569}