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