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