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: 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 return Err(InstructionError::ReentrancyNotAllowed);
155 }
156
157 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 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 for pre_account in pre_accounts.iter_mut() {
222 if key == pre_account.key() {
223 {
224 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 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 let mut result = sysvars
327 .iter()
328 .find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None });
329 if result.is_none() {
330 result = self
332 .account_db
333 .load_with_fixed_root(self.ancestors, id)
334 .map(|(account, _)| Rc::new(account.data().to_vec()));
335 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::default()
386 }
387}
388
389impl MessageProcessor {
390 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 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 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 #[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 Self::verify_account_references(accounts, program_indices)?;
451
452 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 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 if pre_sum != post_sum {
492 return Err(InstructionError::UnbalancedInstruction);
493 }
494 Ok(())
495 }
496
497 #[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 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 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 #[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 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 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, true))
759 .collect();
760
761 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 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 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 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 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 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 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 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 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 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 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}