1use std::{cell::RefCell, collections::HashSet, rc::Rc};
6
7use rialo_hash::Hash;
8use rialo_s_account::{create_account_shared_data_for_test, AccountSharedData, StoredAccount};
9use rialo_s_compute_budget::compute_budget::ComputeBudget;
10use rialo_s_epoch_schedule::EpochSchedule;
11use rialo_s_instruction::{error::InstructionError, AccountMeta};
12use rialo_s_log_collector::{ic_msg, LogCollector};
13use rialo_s_measure::measure::Measure;
14use rialo_s_precompiles::Precompile;
15use rialo_s_pubkey::Pubkey;
16use rialo_s_sdk_ids::{bpf_loader_deprecated, native_loader, sysvar};
17use rialo_s_stable_layout::stable_instruction::StableInstruction;
18use rialo_s_timings::{ExecuteDetailsTimings, ExecuteTimings};
19use rialo_s_transaction_context::{
20 IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext,
21};
22use rialo_s_type_overrides::sync::{atomic::Ordering, Arc};
23pub use rialo_stake_cache_interface::{StakeCacheData, StakesHandle, ValidatorAccount};
24
25use crate::{
26 active_features::ActiveFeatures,
27 loaded_programs::{ProgramCacheEntryType, ProgramCacheForTx},
28 stable_log,
29 sysvar_cache::SysvarCache,
30};
31
32pub type BuiltinFunctionWithContext =
33 fn(&mut InvokeContext<'_, '_>) -> Result<(), InstructionError>;
34
35#[macro_export]
37macro_rules! declare_process_instruction {
38 ($process_instruction:ident, $cu_to_consume:expr, |$invoke_context:ident| $inner:tt) => {
39 pub struct $process_instruction {}
40
41 impl $process_instruction {
42 pub fn vm(
43 invoke_context: &mut $crate::invoke_context::InvokeContext<'_, '_>,
44 ) -> std::result::Result<(), $crate::__private::InstructionError> {
45 fn process_instruction_inner(
46 $invoke_context: &mut $crate::invoke_context::InvokeContext<'_, '_>,
47 ) -> std::result::Result<(), $crate::__private::InstructionError>
48 $inner
49
50 let consumption_result = if $cu_to_consume > 0 {
51 invoke_context.consume_checked($cu_to_consume)
52 } else {
53 Ok(())
54 };
55
56 consumption_result.and_then(|_| process_instruction_inner(invoke_context))
57 }
58 }
59 };
60}
61
62pub enum AccountInsertError {
64 TooManyAccounts,
66
67 AccountAlreadyLoaded,
69}
70
71#[allow(clippy::result_unit_err)]
75pub trait RuntimeAccountLoader {
76 fn load_account(&self, pubkey: &Pubkey) -> Result<Option<StoredAccount>, ()>;
82}
83
84pub struct EnvironmentConfig<'a> {
85 pub blockhash: Hash,
86 pub blockhash_kelvins_per_signature: u64,
87 get_epoch_vote_account_stake_callback: &'a dyn Fn(&'a Pubkey) -> u64,
88 pub active_features: Arc<ActiveFeatures>,
93 sysvar_cache: &'a SysvarCache,
94 pub random_seed: u64,
95 stakes_handle: StakesHandle,
98}
99impl<'a> EnvironmentConfig<'a> {
100 pub fn new(
101 blockhash: Hash,
102 blockhash_kelvins_per_signature: u64,
103 get_epoch_vote_account_stake_callback: &'a dyn Fn(&'a Pubkey) -> u64,
104 active_features: Arc<ActiveFeatures>,
105 sysvar_cache: &'a SysvarCache,
106 random_seed: u64,
107 stakes_handle: StakesHandle,
108 ) -> Self {
109 Self {
110 blockhash,
111 blockhash_kelvins_per_signature,
112 get_epoch_vote_account_stake_callback,
113 active_features,
114 sysvar_cache,
115 random_seed,
116 stakes_handle,
117 }
118 }
119}
120
121pub struct SyscallContext {
122 pub accounts_metadata: Vec<SerializedAccountMetadata>,
123}
124
125#[derive(Debug, Clone)]
126pub struct SerializedAccountMetadata {
127 pub original_data_len: usize,
128 pub vm_data_addr: u64,
129 pub vm_key_addr: u64,
130 pub vm_kelvins_addr: u64,
131 pub vm_owner_addr: u64,
132}
133
134pub struct InvokeContext<'a, 'b> {
136 pub transaction_context: &'a mut TransactionContext,
138 pub program_cache_for_tx_batch: &'a mut ProgramCacheForTx<'b>,
140 pub environment_config: EnvironmentConfig<'a>,
142 compute_budget: ComputeBudget,
144 compute_meter: RefCell<u64>,
147 log_collector: Option<Rc<RefCell<LogCollector>>>,
148 pub execute_time: Option<Measure>,
150 pub timings: ExecuteDetailsTimings,
151 pub syscall_context: Vec<Option<SyscallContext>>,
152 pub account_loader: Option<Box<dyn RuntimeAccountLoader + 'a>>,
153 loaded_accounts: HashSet<Pubkey>,
158 num_account_loads: usize,
160}
161
162impl<'a, 'b> InvokeContext<'a, 'b> {
163 #[allow(clippy::too_many_arguments)]
164 pub fn new(
165 transaction_context: &'a mut TransactionContext,
166 program_cache_for_tx_batch: &'a mut ProgramCacheForTx<'b>,
167 environment_config: EnvironmentConfig<'a>,
168 log_collector: Option<Rc<RefCell<LogCollector>>>,
169 compute_budget: ComputeBudget,
170 ) -> Self {
171 let loaded_accounts = transaction_context.account_keys().copied().collect();
172
173 Self {
174 transaction_context,
175 program_cache_for_tx_batch,
176 environment_config,
177 log_collector,
178 compute_budget,
179 compute_meter: RefCell::new(compute_budget.compute_unit_limit),
180 execute_time: None,
181 timings: ExecuteDetailsTimings::default(),
182 syscall_context: Vec::new(),
183 account_loader: None,
184 loaded_accounts,
185 num_account_loads: 0usize,
186 }
187 }
188
189 pub fn new_with_account_loader(
190 transaction_context: &'a mut TransactionContext,
191 program_cache_for_tx_batch: &'a mut ProgramCacheForTx<'b>,
192 environment_config: EnvironmentConfig<'a>,
193 log_collector: Option<Rc<RefCell<LogCollector>>>,
194 compute_budget: ComputeBudget,
195 account_loader: Box<dyn RuntimeAccountLoader + 'a>,
196 num_account_locks: usize,
197 num_writable_accounts: usize,
198 ) -> Self {
199 let loaded_accounts = transaction_context.account_keys().copied().collect();
200
201 Self {
202 transaction_context,
203 program_cache_for_tx_batch,
204 environment_config,
205 log_collector,
206 compute_budget,
207 compute_meter: RefCell::new(compute_budget.compute_unit_limit),
208 execute_time: None,
209 timings: ExecuteDetailsTimings::default(),
210 syscall_context: Vec::new(),
211 account_loader: Some(account_loader),
212 loaded_accounts,
213 num_account_loads: num_account_locks.saturating_sub(num_writable_accounts),
214 }
215 }
216
217 pub fn add_loaded_account(&mut self, pubkey: Pubkey) -> Result<(), AccountInsertError> {
223 match self.num_account_loads.checked_sub(1) {
224 Some(value) => self.num_account_loads = value,
225 None => return Err(AccountInsertError::TooManyAccounts),
226 }
227
228 if self.loaded_accounts.insert(pubkey) {
229 return Ok(());
230 }
231
232 Err(AccountInsertError::AccountAlreadyLoaded)
233 }
234
235 pub fn push(&mut self) -> Result<(), InstructionError> {
237 let instruction_context = self
238 .transaction_context
239 .get_instruction_context_at_index_in_trace(
240 self.transaction_context.get_instruction_trace_length(),
241 )?;
242 let program_id = instruction_context
243 .get_last_program_key(self.transaction_context)
244 .map_err(|_| InstructionError::UnsupportedProgramId)?;
245 if self
246 .transaction_context
247 .get_instruction_context_stack_height()
248 != 0
249 {
250 let contains = (0..self
251 .transaction_context
252 .get_instruction_context_stack_height())
253 .any(|level| {
254 self.transaction_context
255 .get_instruction_context_at_nesting_level(level)
256 .and_then(|instruction_context| {
257 instruction_context
258 .try_borrow_last_program_account(self.transaction_context)
259 })
260 .map(|program_account| program_account.get_key() == program_id)
261 .unwrap_or(false)
262 });
263 let is_last = self
264 .transaction_context
265 .get_current_instruction_context()
266 .and_then(|instruction_context| {
267 instruction_context.try_borrow_last_program_account(self.transaction_context)
268 })
269 .map(|program_account| program_account.get_key() == program_id)
270 .unwrap_or(false);
271 if contains && !is_last {
272 return Err(InstructionError::ReentrancyNotAllowed);
274 }
275 }
276
277 self.syscall_context.push(None);
278 self.transaction_context.push()
279 }
280
281 fn pop(&mut self) -> Result<(), InstructionError> {
283 self.syscall_context.pop();
284 self.transaction_context.pop()
285 }
286
287 pub fn get_stack_height(&self) -> usize {
290 self.transaction_context
291 .get_instruction_context_stack_height()
292 }
293
294 pub fn native_invoke(
296 &mut self,
297 instruction: StableInstruction,
298 signers: &[Pubkey],
299 ) -> Result<(), InstructionError> {
300 let (instruction_accounts, program_indices) =
301 self.prepare_instruction(&instruction, signers)?;
302 let mut compute_units_consumed = 0;
303 self.process_instruction(
304 &instruction.data,
305 &instruction_accounts,
306 &program_indices,
307 &mut compute_units_consumed,
308 &mut ExecuteTimings::default(),
309 )?;
310 Ok(())
311 }
312
313 #[allow(clippy::type_complexity)]
315 pub fn prepare_instruction(
316 &mut self,
317 instruction: &StableInstruction,
318 signers: &[Pubkey],
319 ) -> Result<(Vec<InstructionAccount>, Vec<IndexOfAccount>), InstructionError> {
320 self.prepare_instruction_inner(instruction.program_id, &instruction.accounts, signers)
321 }
322
323 pub fn prepare_cpi_instruction(
325 &mut self,
326 program_id: Pubkey,
327 account_metas: &[AccountMeta],
328 signers: &[Pubkey],
329 ) -> Result<(Vec<InstructionAccount>, Vec<IndexOfAccount>), InstructionError> {
330 self.prepare_instruction_inner(program_id, account_metas, signers)
331 }
332
333 pub fn prepare_instruction_inner(
334 &mut self,
335 callee_program_id: Pubkey,
336 account_metas: &[AccountMeta],
337 signers: &[Pubkey],
338 ) -> Result<(Vec<InstructionAccount>, Vec<IndexOfAccount>), InstructionError> {
339 let instruction_context = self.transaction_context.get_current_instruction_context()?;
344 let mut deduplicated_instruction_accounts: Vec<InstructionAccount> = Vec::new();
345 let mut duplicate_indicies = Vec::with_capacity(account_metas.len());
346 for (instruction_account_index, account_meta) in account_metas.iter().enumerate() {
347 let index_in_transaction = self
348 .transaction_context
349 .find_index_of_account(&account_meta.pubkey)
350 .ok_or_else(|| {
351 ic_msg!(
352 self,
353 "Instruction references an unknown account {}",
354 account_meta.pubkey,
355 );
356 InstructionError::MissingAccount
357 })?;
358 if let Some(duplicate_index) =
359 deduplicated_instruction_accounts
360 .iter()
361 .position(|instruction_account| {
362 instruction_account.index_in_transaction == index_in_transaction
363 })
364 {
365 duplicate_indicies.push(duplicate_index);
366 let instruction_account = deduplicated_instruction_accounts
367 .get_mut(duplicate_index)
368 .ok_or(InstructionError::NotEnoughAccountKeys)?;
369 instruction_account.is_signer |= account_meta.is_signer;
370 instruction_account.is_writable |= account_meta.is_writable;
371 } else {
372 let index_in_caller = instruction_context
373 .find_index_of_instruction_account(
374 self.transaction_context,
375 &account_meta.pubkey,
376 )
377 .ok_or_else(|| {
378 ic_msg!(
379 self,
380 "Instruction references an unknown account {}",
381 account_meta.pubkey,
382 );
383 InstructionError::MissingAccount
384 })?;
385 duplicate_indicies.push(deduplicated_instruction_accounts.len());
386 deduplicated_instruction_accounts.push(InstructionAccount {
387 index_in_transaction,
388 index_in_caller,
389 index_in_callee: instruction_account_index as IndexOfAccount,
390 is_signer: account_meta.is_signer,
391 is_writable: account_meta.is_writable,
392 });
393 }
394 }
395 for instruction_account in deduplicated_instruction_accounts.iter() {
396 let borrowed_account = instruction_context.try_borrow_instruction_account(
397 self.transaction_context,
398 instruction_account.index_in_caller,
399 )?;
400
401 if instruction_account.is_writable && !borrowed_account.is_writable() {
403 ic_msg!(
404 self,
405 "{}'s writable privilege escalated",
406 borrowed_account.get_key(),
407 );
408 return Err(InstructionError::PrivilegeEscalation);
409 }
410
411 if instruction_account.is_signer
414 && !(borrowed_account.is_signer() || signers.contains(borrowed_account.get_key()))
415 {
416 ic_msg!(
417 self,
418 "{}'s signer privilege escalated",
419 borrowed_account.get_key()
420 );
421 return Err(InstructionError::PrivilegeEscalation);
422 }
423 }
424 let instruction_accounts = duplicate_indicies
425 .into_iter()
426 .map(|duplicate_index| {
427 deduplicated_instruction_accounts
428 .get(duplicate_index)
429 .cloned()
430 .ok_or(InstructionError::NotEnoughAccountKeys)
431 })
432 .collect::<Result<Vec<InstructionAccount>, InstructionError>>()?;
433
434 let program_account_index = match (
439 self.transaction_context
440 .find_index_of_program_account(&callee_program_id),
441 &self.account_loader,
442 ) {
443 (Some(index), _) => index,
444 (None, Some(account_loader)) => match account_loader.load_account(&callee_program_id) {
445 Ok(Some(account)) => {
446 self.transaction_context
447 .add_account((callee_program_id, account.clone()));
448
449 assert!(self.loaded_accounts.insert(callee_program_id));
451
452 self.transaction_context
454 .find_index_of_program_account(&callee_program_id)
455 .expect("program to exist")
456 }
457 Ok(None) => return Err(InstructionError::MissingAccount),
458 Err(()) => return Err(InstructionError::GenericError),
459 },
460 (None, None) => {
461 ic_msg!(self, "Unknown program {}", callee_program_id);
462 return Err(InstructionError::MissingAccount);
463 }
464 };
465
466 Ok((instruction_accounts, vec![program_account_index]))
467 }
468
469 pub fn process_instruction(
471 &mut self,
472 instruction_data: &[u8],
473 instruction_accounts: &[InstructionAccount],
474 program_indices: &[IndexOfAccount],
475 compute_units_consumed: &mut u64,
476 timings: &mut ExecuteTimings,
477 ) -> Result<(), InstructionError> {
478 *compute_units_consumed = 0;
479 self.transaction_context
480 .get_next_instruction_context()?
481 .configure(program_indices, instruction_accounts, instruction_data);
482 self.push()?;
483 self.process_executable_chain(compute_units_consumed, timings)
484 .and(self.pop())
487 }
488
489 pub fn process_precompile<'ix_data>(
491 &mut self,
492 precompile: &Precompile,
493 instruction_data: &[u8],
494 instruction_accounts: &[InstructionAccount],
495 program_indices: &[IndexOfAccount],
496 message_instruction_datas_iter: impl Iterator<Item = &'ix_data [u8]>,
497 ) -> Result<(), InstructionError> {
498 self.transaction_context
499 .get_next_instruction_context()?
500 .configure(program_indices, instruction_accounts, instruction_data);
501 self.push()?;
502
503 let instruction_datas: Vec<_> = message_instruction_datas_iter.collect();
506 precompile
507 .verify(instruction_data, &instruction_datas)
508 .map_err(InstructionError::from)
509 .and(self.pop())
510 }
511
512 fn process_executable_chain(
514 &mut self,
515 compute_units_consumed: &mut u64,
516 timings: &mut ExecuteTimings,
517 ) -> Result<(), InstructionError> {
518 let instruction_context = self.transaction_context.get_current_instruction_context()?;
519 let process_executable_chain_time = Measure::start("process_executable_chain_time");
520
521 let builtin_id = {
522 debug_assert!(instruction_context.get_number_of_program_accounts() <= 1);
523 let borrowed_root_account = instruction_context
524 .try_borrow_program_account(self.transaction_context, 0)
525 .map_err(|_| InstructionError::UnsupportedProgramId)?;
526 let owner_id = borrowed_root_account.get_owner();
527 if native_loader::check_id(owner_id) {
528 *borrowed_root_account.get_key()
529 } else {
530 *owner_id
531 }
532 };
533
534 let entry = self
535 .program_cache_for_tx_batch
536 .find(&builtin_id)
537 .ok_or(InstructionError::UnsupportedProgramId)?;
538 let function = match &entry.program {
539 ProgramCacheEntryType::Builtin(function) => *function,
540 _ => return Err(InstructionError::UnsupportedProgramId),
541 };
542 entry.ix_usage_counter.fetch_add(1, Ordering::Relaxed);
543
544 let program_id = *instruction_context.get_last_program_key(self.transaction_context)?;
545 self.transaction_context
546 .set_return_data(program_id, Vec::new())?;
547 let logger = self.get_log_collector();
548 stable_log::program_invoke(&logger, &program_id, self.get_stack_height());
549 let pre_remaining_units = self.get_remaining();
550 let result = match function(self) {
551 Ok(()) => {
552 stable_log::program_success(&logger, &program_id);
553 Ok(())
554 }
555 Err(err) => {
556 stable_log::program_failure(&logger, &program_id, &err);
557 Err(err)
558 }
559 };
560 let post_remaining_units = self.get_remaining();
561 *compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units);
562
563 if builtin_id == program_id && result.is_ok() && *compute_units_consumed == 0 {
564 return Err(InstructionError::BuiltinProgramsMustConsumeComputeUnits);
565 }
566
567 timings
568 .execute_accessories
569 .process_instructions
570 .process_executable_chain_us += process_executable_chain_time.end_as_us();
571 result
572 }
573
574 pub fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
576 self.log_collector.clone()
577 }
578
579 pub fn get_remaining(&self) -> u64 {
581 *self.compute_meter.borrow()
582 }
583
584 pub fn consume_checked(&self, amount: u64) -> Result<(), InstructionError> {
586 let mut compute_meter = self.compute_meter.borrow_mut();
587 let exceeded = *compute_meter < amount;
588 *compute_meter = compute_meter.saturating_sub(amount);
589 if exceeded {
590 return Err(InstructionError::ComputationalBudgetExceeded);
591 }
592 Ok(())
593 }
594
595 pub fn mock_set_remaining(&self, remaining: u64) {
599 *self.compute_meter.borrow_mut() = remaining;
600 }
601
602 pub fn get_compute_budget(&self) -> &ComputeBudget {
604 &self.compute_budget
605 }
606
607 pub fn get_active_features(&self) -> &Arc<ActiveFeatures> {
611 &self.environment_config.active_features
612 }
613
614 #[cfg(any(test, feature = "dev-context-only-utils"))]
621 pub fn mock_set_active_features(&mut self, active_features: Arc<ActiveFeatures>) {
622 self.environment_config.active_features = active_features;
623 }
624
625 pub fn get_sysvar_cache(&self) -> &SysvarCache {
627 self.environment_config.sysvar_cache
628 }
629
630 pub fn get_epoch_vote_account_stake(&self, pubkey: &'a Pubkey) -> u64 {
632 (self
633 .environment_config
634 .get_epoch_vote_account_stake_callback)(pubkey)
635 }
636
637 pub fn get_current_epoch(&self) -> u64 {
647 self.environment_config
648 .stakes_handle
649 .pending_epoch()
650 .saturating_sub(1)
651 }
652
653 pub fn get_next_epoch(&self) -> u64 {
660 self.environment_config.stakes_handle.pending_epoch()
661 }
662
663 pub fn get_last_freeze_timestamp(&self) -> u64 {
678 self.environment_config
679 .stakes_handle
680 .last_frozen_timestamp()
681 .unwrap_or(0)
682 }
683
684 pub fn mock_set_last_freeze_timestamp(&mut self, timestamp: u64) {
695 self.environment_config.stakes_handle.set_pending_epoch(1);
697
698 let history_entry = StakeCacheData {
700 epoch: 0,
701 timestamp,
702 ..Default::default()
703 };
704 self.environment_config
705 .stakes_handle
706 .push_frozen(history_entry);
707 }
708
709 pub fn mock_insert_stake_account(
716 &mut self,
717 pubkey: rialo_s_pubkey::Pubkey,
718 account: rialo_stake_cache_interface::StakeAccount,
719 ) {
720 self.environment_config
721 .stakes_handle
722 .insert_stake_account(pubkey, account);
723 }
724
725 pub fn mock_insert_validator_account(
736 &mut self,
737 pubkey: rialo_s_pubkey::Pubkey,
738 account: ValidatorAccount,
739 ) {
740 self.environment_config
741 .stakes_handle
742 .insert_validator_account(pubkey, account);
743 }
744
745 pub fn mock_freeze_stakes(&self) {
755 self.environment_config.stakes_handle.freeze_stakes();
756 }
757
758 pub fn signal_freeze_stakes(&self) {
765 self.environment_config
766 .stakes_handle
767 .set_epoch_stakes_frozen();
768 }
769
770 pub fn signal_force_next_auto_freeze(&self) {
773 self.environment_config
774 .stakes_handle
775 .set_force_next_auto_freeze();
776 }
777
778 pub fn take_force_next_auto_freeze(&self) -> bool {
781 self.environment_config
782 .stakes_handle
783 .take_force_next_auto_freeze()
784 }
785
786 pub fn get_all_validator_accounts_from_last_frozen(&self) -> Vec<(Pubkey, ValidatorAccount)> {
795 self.environment_config
796 .stakes_handle
797 .get_all_validator_accounts_from_last_frozen()
798 }
799
800 pub fn frozen_stake_history_len(&self) -> usize {
805 self.environment_config.stakes_handle.frozen_len()
806 }
807
808 pub fn request_epoch_rewards_init(
821 &self,
822 epoch: u64,
823 total_rewards: u64,
824 total_eligible_stake: u64,
825 validator_scores: Vec<u32>,
826 ) {
827 self.environment_config
828 .stakes_handle
829 .request_epoch_rewards_init(
830 epoch,
831 total_rewards,
832 total_eligible_stake,
833 validator_scores,
834 );
835 }
836
837 pub fn get_all_validator_accounts_from_frozen_epoch(
840 &self,
841 epoch: u64,
842 ) -> Vec<(Pubkey, ValidatorAccount)> {
843 self.environment_config
844 .stakes_handle
845 .get_all_validator_accounts_from_frozen_epoch(epoch)
846 }
847
848 pub fn front_frozen_epoch(&self) -> Option<u64> {
855 self.environment_config.stakes_handle.front_frozen_epoch()
856 }
857
858 pub fn is_validator_referenced(
881 &self,
882 validator: &Pubkey,
883 validator_info: &rialo_stake_cache_interface::ValidatorInfo,
884 last_freeze_timestamp: u64,
885 current_timestamp: u64,
886 ) -> bool {
887 self.environment_config
888 .stakes_handle
889 .is_validator_referenced(
890 validator,
891 validator_info,
892 last_freeze_timestamp,
893 current_timestamp,
894 )
895 }
896
897 pub fn has_locked_stakers(
912 &self,
913 validator: &Pubkey,
914 lockup_period: u64,
915 current_timestamp: u64,
916 ) -> bool {
917 self.environment_config.stakes_handle.has_locked_stakers(
918 validator,
919 lockup_period,
920 current_timestamp,
921 )
922 }
923
924 pub fn is_validator_referenced_excluding_self_bond(
930 &self,
931 validator_pubkey: &Pubkey,
932 validator_info: &rialo_stake_cache_interface::ValidatorInfo,
933 last_freeze_timestamp: u64,
934 current_timestamp: u64,
935 ) -> bool {
936 self.environment_config
937 .stakes_handle
938 .is_validator_referenced_excluding_self_bond(
939 validator_pubkey,
940 validator_info,
941 last_freeze_timestamp,
942 current_timestamp,
943 )
944 }
945
946 pub fn get_stake_account_from_pending(
951 &self,
952 pubkey: &Pubkey,
953 ) -> Option<rialo_stake_cache_interface::StakeAccount> {
954 self.environment_config
955 .stakes_handle
956 .get_stake_account_from_pending(pubkey)
957 }
958
959 pub fn is_epoch_rewards_init_pending(&self) -> bool {
964 self.environment_config
965 .stakes_handle
966 .is_epoch_rewards_init_pending()
967 }
968
969 pub fn completed_frozen_epochs(&self) -> Vec<u64> {
975 self.environment_config
976 .stakes_handle
977 .completed_frozen_epochs()
978 }
979
980 pub fn epoch_rewards_exists(&self, epoch: u64) -> bool {
986 self.environment_config
987 .stakes_handle
988 .epoch_rewards_exists(epoch)
989 }
990
991 pub fn is_previous_epoch_adopted(&self) -> bool {
996 self.environment_config
997 .stakes_handle
998 .is_previous_epoch_adopted()
999 }
1000
1001 pub fn get_frozen_epoch_meta(&self, epoch: u64) -> Option<(u64, Option<u64>)> {
1008 self.environment_config
1009 .stakes_handle
1010 .get_frozen_epoch_meta(epoch)
1011 }
1012
1013 pub fn get_all_stake_accounts_from_frozen_epoch(
1015 &self,
1016 epoch: u64,
1017 ) -> Vec<(Pubkey, rialo_stake_cache_interface::StakeAccount)> {
1018 self.environment_config
1019 .stakes_handle
1020 .get_all_stake_accounts_from_frozen_epoch(epoch)
1021 }
1022
1023 #[cfg(feature = "testing")]
1032 pub fn signal_handover(&self, ts: u64) {
1033 self.environment_config.stakes_handle.signal_handover(ts);
1034 }
1035
1036 pub fn get_check_aligned(&self) -> bool {
1038 self.transaction_context
1039 .get_current_instruction_context()
1040 .and_then(|instruction_context| {
1041 let program_account =
1042 instruction_context.try_borrow_last_program_account(self.transaction_context);
1043 debug_assert!(program_account.is_ok());
1044 program_account
1045 })
1046 .map(|program_account| *program_account.get_owner() != bpf_loader_deprecated::id())
1047 .unwrap_or(true)
1048 }
1049
1050 pub fn set_syscall_context(
1052 &mut self,
1053 syscall_context: SyscallContext,
1054 ) -> Result<(), InstructionError> {
1055 *self
1056 .syscall_context
1057 .last_mut()
1058 .ok_or(InstructionError::CallDepth)? = Some(syscall_context);
1059 Ok(())
1060 }
1061
1062 pub fn get_syscall_context(&self) -> Result<&SyscallContext, InstructionError> {
1064 self.syscall_context
1065 .last()
1066 .and_then(std::option::Option::as_ref)
1067 .ok_or(InstructionError::CallDepth)
1068 }
1069
1070 pub fn get_syscall_context_mut(&mut self) -> Result<&mut SyscallContext, InstructionError> {
1072 self.syscall_context
1073 .last_mut()
1074 .and_then(|syscall_context| syscall_context.as_mut())
1075 .ok_or(InstructionError::CallDepth)
1076 }
1077}
1078
1079#[macro_export]
1080macro_rules! with_mock_invoke_context {
1081 (
1082 $invoke_context:ident,
1083 $transaction_context:ident,
1084 $entry:expr,
1085 $transaction_accounts:expr $(,)?
1086 ) => {
1087 use rialo_s_compute_budget::compute_budget::ComputeBudget;
1088 use rialo_s_log_collector::LogCollector;
1089 use rialo_s_type_overrides::sync::Arc;
1090 use $crate::{
1091 __private::{Hash, ReadableAccount, Rent, TransactionContext},
1092 invoke_context::{EnvironmentConfig, InvokeContext},
1093 loaded_programs::{ProgramCacheEntry, ProgramCacheForTx, ProgramCacheForTxBatch},
1094 sysvar_cache::SysvarCache,
1095 };
1096 let compute_budget = ComputeBudget::default();
1097 let mut $transaction_context = TransactionContext::new(
1098 $transaction_accounts,
1099 Rent::default(),
1100 compute_budget.max_instruction_stack_depth,
1101 compute_budget.max_instruction_trace_length,
1102 );
1103 let mut sysvar_cache = SysvarCache::default();
1104 sysvar_cache.fill_missing_entries(|pubkey, callback| {
1105 for index in 0..$transaction_context.get_number_of_accounts() {
1106 if $transaction_context
1107 .get_key_of_account_at_index(index)
1108 .unwrap()
1109 == pubkey
1110 {
1111 callback(
1112 $transaction_context
1113 .get_account_at_index(index)
1114 .unwrap()
1115 .borrow()
1116 .data(),
1117 );
1118 }
1119 }
1120 });
1121 let mock_stakes_handle = $crate::invoke_context::StakesHandle::default();
1125 let environment_config = EnvironmentConfig::new(
1126 Hash::default(),
1127 0,
1128 &|_| 0,
1129 Arc::new($crate::active_features::ActiveFeatures::new()),
1130 &sysvar_cache,
1131 0,
1132 mock_stakes_handle,
1133 );
1134 let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
1135 if let Some((loader_id, builtin)) = $entry {
1136 program_cache_for_tx_batch.replenish(
1137 loader_id,
1138 Arc::new(ProgramCacheEntry::new_builtin(0, 0, builtin)),
1139 );
1140 }
1141 let mut program_cache_for_tx = ProgramCacheForTx::from_cache(&program_cache_for_tx_batch);
1142 let mut $invoke_context = InvokeContext::new(
1143 &mut $transaction_context,
1144 &mut program_cache_for_tx,
1145 environment_config,
1146 Some(LogCollector::new_ref()),
1147 compute_budget,
1148 );
1149 };
1150 (
1151 $invoke_context:ident,
1152 $transaction_context:ident,
1153 $transaction_accounts:expr $(,)?
1154 ) => {
1155 with_mock_invoke_context!(
1156 $invoke_context,
1157 $transaction_context,
1158 None,
1159 $transaction_accounts
1160 )
1161 };
1162}
1163
1164pub fn mock_process_instruction<
1165 T: Into<StoredAccount>,
1166 F: FnMut(&mut InvokeContext<'_, '_>),
1167 G: FnMut(&mut InvokeContext<'_, '_>),
1168>(
1169 loader_id: &Pubkey,
1170 mut program_indices: Vec<IndexOfAccount>,
1171 instruction_data: &[u8],
1172 transaction_accounts: Vec<(Pubkey, T)>,
1173 instruction_account_metas: Vec<AccountMeta>,
1174 expected_result: Result<(), InstructionError>,
1175 builtin_function: BuiltinFunctionWithContext,
1176 mut pre_adjustments: F,
1177 mut post_adjustments: G,
1178) -> Vec<StoredAccount> {
1179 let mut transaction_accounts: Vec<TransactionAccount> = transaction_accounts
1181 .into_iter()
1182 .map(|(key, account)| (key, account.into()))
1183 .collect();
1184 let mut instruction_accounts: Vec<InstructionAccount> =
1185 Vec::with_capacity(instruction_account_metas.len());
1186 for (instruction_account_index, account_meta) in instruction_account_metas.iter().enumerate() {
1187 let index_in_transaction = transaction_accounts
1188 .iter()
1189 .position(|(key, _account)| *key == account_meta.pubkey)
1190 .unwrap_or(transaction_accounts.len())
1191 as IndexOfAccount;
1192 let index_in_callee = instruction_accounts
1193 .get(0..instruction_account_index)
1194 .unwrap()
1195 .iter()
1196 .position(|instruction_account| {
1197 instruction_account.index_in_transaction == index_in_transaction
1198 })
1199 .unwrap_or(instruction_account_index) as IndexOfAccount;
1200 instruction_accounts.push(InstructionAccount {
1201 index_in_transaction,
1202 index_in_caller: index_in_transaction,
1203 index_in_callee,
1204 is_signer: account_meta.is_signer,
1205 is_writable: account_meta.is_writable,
1206 });
1207 }
1208 if program_indices.is_empty() {
1209 program_indices.insert(0, transaction_accounts.len() as IndexOfAccount);
1210 let processor_account =
1211 StoredAccount::Data(AccountSharedData::new(0, 0, &native_loader::id()));
1212 transaction_accounts.push((*loader_id, processor_account));
1213 }
1214 let pop_epoch_schedule_account = if !transaction_accounts
1215 .iter()
1216 .any(|(key, _)| *key == sysvar::epoch_schedule::id())
1217 {
1218 transaction_accounts.push((
1219 sysvar::epoch_schedule::id(),
1220 create_account_shared_data_for_test(&EpochSchedule::default()),
1221 ));
1222 true
1223 } else {
1224 false
1225 };
1226 with_mock_invoke_context!(
1227 invoke_context,
1228 transaction_context,
1229 Some((*loader_id, builtin_function)),
1230 transaction_accounts
1231 );
1232 pre_adjustments(&mut invoke_context);
1233 let result = invoke_context.process_instruction(
1234 instruction_data,
1235 &instruction_accounts,
1236 &program_indices,
1237 &mut 0,
1238 &mut ExecuteTimings::default(),
1239 );
1240 assert_eq!(result, expected_result);
1241 post_adjustments(&mut invoke_context);
1242 drop(invoke_context);
1243 let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
1244 if pop_epoch_schedule_account {
1245 transaction_accounts.pop();
1246 }
1247 transaction_accounts.pop();
1248 transaction_accounts
1249}
1250
1251#[cfg(test)]
1252mod tests {
1253 use rialo_s_compute_budget::compute_budget_limits;
1254 use rialo_s_instruction::Instruction;
1255 use rialo_s_rent::Rent;
1256 use serde::{Deserialize, Serialize};
1257
1258 use super::*;
1259
1260 #[derive(Debug, Serialize, Deserialize)]
1261 enum MockInstruction {
1262 NoopSuccess,
1263 NoopFail,
1264 ModifyOwned,
1265 ModifyNotOwned,
1266 ModifyReadonly,
1267 UnbalancedPush,
1268 UnbalancedPop,
1269 ConsumeComputeUnits {
1270 compute_units_to_consume: u64,
1271 desired_result: Result<(), InstructionError>,
1272 },
1273 Resize {
1274 new_len: u64,
1275 },
1276 }
1277
1278 const MOCK_BUILTIN_COMPUTE_UNIT_COST: u64 = 1;
1279
1280 declare_process_instruction!(
1281 MockBuiltin,
1282 MOCK_BUILTIN_COMPUTE_UNIT_COST,
1283 |invoke_context| {
1284 let transaction_context = &invoke_context.transaction_context;
1285 let instruction_context = transaction_context.get_current_instruction_context()?;
1286 let instruction_data = instruction_context.get_instruction_data();
1287 let program_id = instruction_context.get_last_program_key(transaction_context)?;
1288 let instruction_accounts = (0..4)
1289 .map(|instruction_account_index| InstructionAccount {
1290 index_in_transaction: instruction_account_index,
1291 index_in_caller: instruction_account_index,
1292 index_in_callee: instruction_account_index,
1293 is_signer: false,
1294 is_writable: false,
1295 })
1296 .collect::<Vec<_>>();
1297 assert_eq!(
1298 program_id,
1299 instruction_context
1300 .try_borrow_instruction_account(transaction_context, 0)?
1301 .get_owner()
1302 );
1303 assert_ne!(
1304 instruction_context
1305 .try_borrow_instruction_account(transaction_context, 1)?
1306 .get_owner(),
1307 instruction_context
1308 .try_borrow_instruction_account(transaction_context, 0)?
1309 .get_key()
1310 );
1311
1312 if let Ok(instruction) = bincode::deserialize(instruction_data) {
1313 match instruction {
1314 MockInstruction::NoopSuccess => (),
1315 MockInstruction::NoopFail => return Err(InstructionError::GenericError),
1316 MockInstruction::ModifyOwned => instruction_context
1317 .try_borrow_instruction_account(transaction_context, 0)?
1318 .set_data_from_slice(&[1])?,
1319 MockInstruction::ModifyNotOwned => instruction_context
1320 .try_borrow_instruction_account(transaction_context, 1)?
1321 .set_data_from_slice(&[1])?,
1322 MockInstruction::ModifyReadonly => instruction_context
1323 .try_borrow_instruction_account(transaction_context, 2)?
1324 .set_data_from_slice(&[1])?,
1325 MockInstruction::UnbalancedPush => {
1326 instruction_context
1327 .try_borrow_instruction_account(transaction_context, 0)?
1328 .checked_add_kelvins(1)?;
1329 let program_id = *transaction_context.get_key_of_account_at_index(3)?;
1330 let metas = vec![
1331 AccountMeta::new_readonly(
1332 *transaction_context.get_key_of_account_at_index(0)?,
1333 false,
1334 ),
1335 AccountMeta::new_readonly(
1336 *transaction_context.get_key_of_account_at_index(1)?,
1337 false,
1338 ),
1339 ];
1340 let inner_instruction = Instruction::new_with_bincode(
1341 program_id,
1342 &MockInstruction::NoopSuccess,
1343 metas,
1344 );
1345 invoke_context
1346 .transaction_context
1347 .get_next_instruction_context()
1348 .unwrap()
1349 .configure(&[3], &instruction_accounts, &[]);
1350 let result = invoke_context.push();
1351 assert_eq!(result, Err(InstructionError::UnbalancedInstruction));
1352 result?;
1353 invoke_context
1354 .native_invoke(inner_instruction.into(), &[])
1355 .and(invoke_context.pop())?;
1356 }
1357 MockInstruction::UnbalancedPop => instruction_context
1358 .try_borrow_instruction_account(transaction_context, 0)?
1359 .checked_add_kelvins(1)?,
1360 MockInstruction::ConsumeComputeUnits {
1361 compute_units_to_consume,
1362 desired_result,
1363 } => {
1364 invoke_context
1365 .consume_checked(compute_units_to_consume)
1366 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
1367 return desired_result;
1368 }
1369 MockInstruction::Resize { new_len } => instruction_context
1370 .try_borrow_instruction_account(transaction_context, 0)?
1371 .set_data(vec![0; new_len as usize])?,
1372 }
1373 } else {
1374 return Err(InstructionError::InvalidInstructionData);
1375 }
1376 Ok(())
1377 }
1378 );
1379
1380 #[test]
1381 fn test_instruction_stack_height() {
1382 let one_more_than_max_depth = ComputeBudget::default()
1383 .max_instruction_stack_depth
1384 .saturating_add(1);
1385 let mut invoke_stack = vec![];
1386 let mut transaction_accounts = vec![];
1387 let mut instruction_accounts = vec![];
1388 for index in 0..one_more_than_max_depth {
1389 invoke_stack.push(rialo_s_pubkey::new_rand());
1390 transaction_accounts.push((
1391 rialo_s_pubkey::new_rand(),
1392 AccountSharedData::new(index as u64, 1, invoke_stack.get(index).unwrap()),
1393 ));
1394 instruction_accounts.push(InstructionAccount {
1395 index_in_transaction: index as IndexOfAccount,
1396 index_in_caller: index as IndexOfAccount,
1397 index_in_callee: instruction_accounts.len() as IndexOfAccount,
1398 is_signer: false,
1399 is_writable: true,
1400 });
1401 }
1402 for (index, program_id) in invoke_stack.iter().enumerate() {
1403 transaction_accounts.push((
1404 *program_id,
1405 AccountSharedData::new(1, 1, &rialo_s_pubkey::Pubkey::default()),
1406 ));
1407 instruction_accounts.push(InstructionAccount {
1408 index_in_transaction: index as IndexOfAccount,
1409 index_in_caller: index as IndexOfAccount,
1410 index_in_callee: index as IndexOfAccount,
1411 is_signer: false,
1412 is_writable: false,
1413 });
1414 }
1415 let transaction_accounts: Vec<(Pubkey, StoredAccount)> = transaction_accounts
1416 .into_iter()
1417 .map(|(k, v)| (k, v.into()))
1418 .collect();
1419 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1420
1421 let mut depth_reached = 0;
1423 for _ in 0..invoke_stack.len() {
1424 invoke_context
1425 .transaction_context
1426 .get_next_instruction_context()
1427 .unwrap()
1428 .configure(
1429 &[one_more_than_max_depth.saturating_add(depth_reached) as IndexOfAccount],
1430 &instruction_accounts,
1431 &[],
1432 );
1433 if Err(InstructionError::CallDepth) == invoke_context.push() {
1434 break;
1435 }
1436 depth_reached = depth_reached.saturating_add(1);
1437 }
1438 assert_ne!(depth_reached, 0);
1439 assert!(depth_reached < one_more_than_max_depth);
1440 }
1441
1442 #[test]
1443 fn test_max_instruction_trace_length() {
1444 const MAX_INSTRUCTIONS: usize = 8;
1445 let mut transaction_context = TransactionContext::new::<StoredAccount>(
1446 Vec::new(),
1447 Rent::default(),
1448 1,
1449 MAX_INSTRUCTIONS,
1450 );
1451 for _ in 0..MAX_INSTRUCTIONS {
1452 transaction_context.push().unwrap();
1453 transaction_context.pop().unwrap();
1454 }
1455 assert_eq!(
1456 transaction_context.push(),
1457 Err(InstructionError::MaxInstructionTraceLengthExceeded)
1458 );
1459 }
1460
1461 #[test]
1462 fn test_process_instruction() {
1463 let callee_program_id = rialo_s_pubkey::new_rand();
1464 let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
1465 let not_owned_account = AccountSharedData::new(84, 1, &rialo_s_pubkey::new_rand());
1466 let readonly_account = AccountSharedData::new(168, 1, &rialo_s_pubkey::new_rand());
1467 let loader_account = AccountSharedData::new(0, 1, &native_loader::id());
1468 let program_account = AccountSharedData::new(1, 1, &native_loader::id());
1469 let transaction_accounts = vec![
1470 (rialo_s_pubkey::new_rand(), owned_account),
1471 (rialo_s_pubkey::new_rand(), not_owned_account),
1472 (rialo_s_pubkey::new_rand(), readonly_account),
1473 (callee_program_id, program_account),
1474 (rialo_s_pubkey::new_rand(), loader_account),
1475 ];
1476 let metas = vec![
1477 AccountMeta::new(transaction_accounts.first().unwrap().0, false),
1478 AccountMeta::new(transaction_accounts.get(1).unwrap().0, false),
1479 AccountMeta::new_readonly(transaction_accounts.get(2).unwrap().0, false),
1480 ];
1481 let instruction_accounts = (0..4)
1482 .map(|instruction_account_index| InstructionAccount {
1483 index_in_transaction: instruction_account_index,
1484 index_in_caller: instruction_account_index,
1485 index_in_callee: instruction_account_index,
1486 is_signer: false,
1487 is_writable: instruction_account_index < 2,
1488 })
1489 .collect::<Vec<_>>();
1490 let transaction_accounts: Vec<(Pubkey, StoredAccount)> = transaction_accounts
1491 .into_iter()
1492 .map(|(k, v)| (k, v.into()))
1493 .collect();
1494 with_mock_invoke_context!(
1495 invoke_context,
1496 transaction_context,
1497 Some((callee_program_id, MockBuiltin::vm)),
1498 transaction_accounts
1499 );
1500
1501 let cases = vec![
1503 (MockInstruction::NoopSuccess, Ok(())),
1504 (
1505 MockInstruction::NoopFail,
1506 Err(InstructionError::GenericError),
1507 ),
1508 (MockInstruction::ModifyOwned, Ok(())),
1509 (
1510 MockInstruction::ModifyNotOwned,
1511 Err(InstructionError::ExternalAccountDataModified),
1512 ),
1513 (
1514 MockInstruction::ModifyReadonly,
1515 Err(InstructionError::ReadonlyDataModified),
1516 ),
1517 (
1518 MockInstruction::UnbalancedPush,
1519 Err(InstructionError::UnbalancedInstruction),
1520 ),
1521 (
1522 MockInstruction::UnbalancedPop,
1523 Err(InstructionError::UnbalancedInstruction),
1524 ),
1525 ];
1526 for case in cases {
1527 invoke_context
1528 .transaction_context
1529 .get_next_instruction_context()
1530 .unwrap()
1531 .configure(&[4], &instruction_accounts, &[]);
1532 invoke_context.push().unwrap();
1533 let inner_instruction =
1534 Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
1535 let result = invoke_context
1536 .native_invoke(inner_instruction.into(), &[])
1537 .and(invoke_context.pop());
1538 assert_eq!(result, case.1);
1539 }
1540
1541 let compute_units_to_consume = 10;
1543 let expected_results = vec![Ok(()), Err(InstructionError::GenericError)];
1544 for expected_result in expected_results {
1545 invoke_context
1546 .transaction_context
1547 .get_next_instruction_context()
1548 .unwrap()
1549 .configure(&[4], &instruction_accounts, &[]);
1550 invoke_context.push().unwrap();
1551 let inner_instruction = Instruction::new_with_bincode(
1552 callee_program_id,
1553 &MockInstruction::ConsumeComputeUnits {
1554 compute_units_to_consume,
1555 desired_result: expected_result.clone(),
1556 },
1557 metas.clone(),
1558 );
1559 let inner_instruction = StableInstruction::from(inner_instruction);
1560 let (inner_instruction_accounts, program_indices) = invoke_context
1561 .prepare_instruction(&inner_instruction, &[])
1562 .unwrap();
1563
1564 let mut compute_units_consumed = 0;
1565 let result = invoke_context.process_instruction(
1566 &inner_instruction.data,
1567 &inner_instruction_accounts,
1568 &program_indices,
1569 &mut compute_units_consumed,
1570 &mut ExecuteTimings::default(),
1571 );
1572
1573 assert!(compute_units_consumed > 0);
1577 assert_eq!(
1578 compute_units_consumed,
1579 compute_units_to_consume.saturating_add(MOCK_BUILTIN_COMPUTE_UNIT_COST),
1580 );
1581 assert_eq!(result, expected_result);
1582
1583 invoke_context.pop().unwrap();
1584 }
1585 }
1586
1587 #[test]
1588 fn test_invoke_context_compute_budget() {
1589 let transaction_accounts = vec![(rialo_s_pubkey::new_rand(), AccountSharedData::default())];
1590 let transaction_accounts: Vec<(Pubkey, StoredAccount)> = transaction_accounts
1591 .into_iter()
1592 .map(|(k, v)| (k, v.into()))
1593 .collect();
1594
1595 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1596 invoke_context.compute_budget = ComputeBudget::new(
1597 compute_budget_limits::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
1598 );
1599
1600 invoke_context
1601 .transaction_context
1602 .get_next_instruction_context()
1603 .unwrap()
1604 .configure(&[0], &[], &[]);
1605 invoke_context.push().unwrap();
1606 assert_eq!(
1607 *invoke_context.get_compute_budget(),
1608 ComputeBudget::new(
1609 compute_budget_limits::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64
1610 )
1611 );
1612 invoke_context.pop().unwrap();
1613 }
1614
1615 #[test]
1616 fn test_process_instruction_accounts_resize_delta() {
1617 let program_key = Pubkey::new_unique();
1618 let user_account_data_len = 123u64;
1619 let user_account =
1620 AccountSharedData::new(100, user_account_data_len as usize, &program_key);
1621 let dummy_account = AccountSharedData::new(10, 0, &program_key);
1622 let program_account = AccountSharedData::new(500, 500, &native_loader::id());
1623 let transaction_accounts = vec![
1624 (Pubkey::new_unique(), user_account),
1625 (Pubkey::new_unique(), dummy_account),
1626 (program_key, program_account),
1627 ];
1628 let instruction_accounts = [
1629 InstructionAccount {
1630 index_in_transaction: 0,
1631 index_in_caller: 0,
1632 index_in_callee: 0,
1633 is_signer: false,
1634 is_writable: true,
1635 },
1636 InstructionAccount {
1637 index_in_transaction: 1,
1638 index_in_caller: 1,
1639 index_in_callee: 1,
1640 is_signer: false,
1641 is_writable: false,
1642 },
1643 ];
1644 let transaction_accounts: Vec<(Pubkey, StoredAccount)> = transaction_accounts
1645 .into_iter()
1646 .map(|(k, v)| (k, v.into()))
1647 .collect();
1648 with_mock_invoke_context!(
1649 invoke_context,
1650 transaction_context,
1651 Some((program_key, MockBuiltin::vm)),
1652 transaction_accounts
1653 );
1654
1655 {
1657 let resize_delta: i64 = 0;
1658 let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
1659 let instruction_data =
1660 bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
1661
1662 let result = invoke_context.process_instruction(
1663 &instruction_data,
1664 &instruction_accounts,
1665 &[2],
1666 &mut 0,
1667 &mut ExecuteTimings::default(),
1668 );
1669
1670 assert!(result.is_ok());
1671 assert_eq!(
1672 invoke_context
1673 .transaction_context
1674 .accounts_resize_delta()
1675 .unwrap(),
1676 resize_delta
1677 );
1678 }
1679
1680 {
1682 let resize_delta: i64 = 1;
1683 let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
1684 let instruction_data =
1685 bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
1686
1687 let result = invoke_context.process_instruction(
1688 &instruction_data,
1689 &instruction_accounts,
1690 &[2],
1691 &mut 0,
1692 &mut ExecuteTimings::default(),
1693 );
1694
1695 assert!(result.is_ok());
1696 assert_eq!(
1697 invoke_context
1698 .transaction_context
1699 .accounts_resize_delta()
1700 .unwrap(),
1701 resize_delta
1702 );
1703 }
1704
1705 {
1707 let resize_delta: i64 = -1;
1708 let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
1709 let instruction_data =
1710 bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
1711
1712 let result = invoke_context.process_instruction(
1713 &instruction_data,
1714 &instruction_accounts,
1715 &[2],
1716 &mut 0,
1717 &mut ExecuteTimings::default(),
1718 );
1719
1720 assert!(result.is_ok());
1721 assert_eq!(
1722 invoke_context
1723 .transaction_context
1724 .accounts_resize_delta()
1725 .unwrap(),
1726 resize_delta
1727 );
1728 }
1729 }
1730}