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