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