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_feature_set::{
12 lift_cpi_caller_restriction, move_precompile_verification_to_svm,
13 remove_accounts_executable_flag_checks, FeatureSet,
14};
15use rialo_s_instruction::{error::InstructionError, AccountMeta};
16use rialo_s_log_collector::{ic_msg, LogCollector};
17use rialo_s_measure::measure::Measure;
18use rialo_s_precompiles::Precompile;
19use rialo_s_pubkey::Pubkey;
20use rialo_s_sdk_ids::{bpf_loader_deprecated, native_loader, sysvar};
21use rialo_s_stable_layout::stable_instruction::StableInstruction;
22use rialo_s_timings::{ExecuteDetailsTimings, ExecuteTimings};
23use rialo_s_transaction_context::{
24 IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext,
25};
26use rialo_s_type_overrides::sync::{atomic::Ordering, Arc};
27pub use rialo_stake_cache_interface::{StakeCacheData, StakesHandle, ValidatorAccount};
28
29use crate::{
30 active_features::ActiveFeatures,
31 loaded_programs::{ProgramCacheEntryType, ProgramCacheForTx},
32 stable_log,
33 sysvar_cache::SysvarCache,
34};
35
36pub type BuiltinFunctionWithContext =
37 fn(&mut InvokeContext<'_, '_>) -> Result<(), InstructionError>;
38
39#[macro_export]
41macro_rules! declare_process_instruction {
42 ($process_instruction:ident, $cu_to_consume:expr, |$invoke_context:ident| $inner:tt) => {
43 pub struct $process_instruction {}
44
45 impl $process_instruction {
46 pub fn vm(
47 invoke_context: &mut $crate::invoke_context::InvokeContext<'_, '_>,
48 ) -> std::result::Result<(), $crate::__private::InstructionError> {
49 fn process_instruction_inner(
50 $invoke_context: &mut $crate::invoke_context::InvokeContext<'_, '_>,
51 ) -> std::result::Result<(), $crate::__private::InstructionError>
52 $inner
53
54 let consumption_result = if $cu_to_consume > 0 {
55 invoke_context.consume_checked($cu_to_consume)
56 } else {
57 Ok(())
58 };
59
60 consumption_result.and_then(|_| process_instruction_inner(invoke_context))
61 }
62 }
63 };
64}
65
66pub enum AccountInsertError {
68 TooManyAccounts,
70
71 AccountAlreadyLoaded,
73}
74
75#[allow(clippy::result_unit_err)]
79pub trait RuntimeAccountLoader {
80 fn load_account(&self, pubkey: &Pubkey) -> Result<Option<StoredAccount>, ()>;
86}
87
88pub struct EnvironmentConfig<'a> {
89 pub blockhash: Hash,
90 pub blockhash_kelvins_per_signature: u64,
91 get_epoch_vote_account_stake_callback: &'a dyn Fn(&'a Pubkey) -> u64,
92 pub feature_set: Arc<FeatureSet>,
93 pub active_features: Arc<ActiveFeatures>,
98 sysvar_cache: &'a SysvarCache,
99 pub random_seed: u64,
100 stakes_handle: StakesHandle,
103}
104impl<'a> EnvironmentConfig<'a> {
105 #[allow(clippy::too_many_arguments)]
106 pub fn new(
107 blockhash: Hash,
108 blockhash_kelvins_per_signature: u64,
109 get_epoch_vote_account_stake_callback: &'a dyn Fn(&'a Pubkey) -> u64,
110 feature_set: Arc<FeatureSet>,
111 active_features: Arc<ActiveFeatures>,
112 sysvar_cache: &'a SysvarCache,
113 random_seed: u64,
114 stakes_handle: StakesHandle,
115 ) -> Self {
116 Self {
117 blockhash,
118 blockhash_kelvins_per_signature,
119 get_epoch_vote_account_stake_callback,
120 feature_set,
121 active_features,
122 sysvar_cache,
123 random_seed,
124 stakes_handle,
125 }
126 }
127}
128
129pub struct SyscallContext {
130 pub accounts_metadata: Vec<SerializedAccountMetadata>,
131}
132
133#[derive(Debug, Clone)]
134pub struct SerializedAccountMetadata {
135 pub original_data_len: usize,
136 pub vm_data_addr: u64,
137 pub vm_key_addr: u64,
138 pub vm_kelvins_addr: u64,
139 pub vm_owner_addr: u64,
140}
141
142pub struct InvokeContext<'a, 'b> {
144 pub transaction_context: &'a mut TransactionContext,
146 pub program_cache_for_tx_batch: &'a mut ProgramCacheForTx<'b>,
148 pub environment_config: EnvironmentConfig<'a>,
150 compute_budget: ComputeBudget,
152 compute_meter: RefCell<u64>,
155 log_collector: Option<Rc<RefCell<LogCollector>>>,
156 pub execute_time: Option<Measure>,
158 pub timings: ExecuteDetailsTimings,
159 pub syscall_context: Vec<Option<SyscallContext>>,
160 pub account_loader: Option<Box<dyn RuntimeAccountLoader + 'a>>,
161 loaded_accounts: HashSet<Pubkey>,
166 num_account_loads: usize,
168}
169
170impl<'a, 'b> InvokeContext<'a, 'b> {
171 #[allow(clippy::too_many_arguments)]
172 pub fn new(
173 transaction_context: &'a mut TransactionContext,
174 program_cache_for_tx_batch: &'a mut ProgramCacheForTx<'b>,
175 environment_config: EnvironmentConfig<'a>,
176 log_collector: Option<Rc<RefCell<LogCollector>>>,
177 compute_budget: ComputeBudget,
178 ) -> Self {
179 let loaded_accounts = transaction_context.account_keys().copied().collect();
180
181 Self {
182 transaction_context,
183 program_cache_for_tx_batch,
184 environment_config,
185 log_collector,
186 compute_budget,
187 compute_meter: RefCell::new(compute_budget.compute_unit_limit),
188 execute_time: None,
189 timings: ExecuteDetailsTimings::default(),
190 syscall_context: Vec::new(),
191 account_loader: None,
192 loaded_accounts,
193 num_account_loads: 0usize,
194 }
195 }
196
197 pub fn new_with_account_loader(
198 transaction_context: &'a mut TransactionContext,
199 program_cache_for_tx_batch: &'a mut ProgramCacheForTx<'b>,
200 environment_config: EnvironmentConfig<'a>,
201 log_collector: Option<Rc<RefCell<LogCollector>>>,
202 compute_budget: ComputeBudget,
203 account_loader: Box<dyn RuntimeAccountLoader + 'a>,
204 num_account_locks: usize,
205 num_writable_accounts: usize,
206 ) -> Self {
207 let loaded_accounts = transaction_context.account_keys().copied().collect();
208
209 Self {
210 transaction_context,
211 program_cache_for_tx_batch,
212 environment_config,
213 log_collector,
214 compute_budget,
215 compute_meter: RefCell::new(compute_budget.compute_unit_limit),
216 execute_time: None,
217 timings: ExecuteDetailsTimings::default(),
218 syscall_context: Vec::new(),
219 account_loader: Some(account_loader),
220 loaded_accounts,
221 num_account_loads: num_account_locks.saturating_sub(num_writable_accounts),
222 }
223 }
224
225 pub fn add_loaded_account(&mut self, pubkey: Pubkey) -> Result<(), AccountInsertError> {
231 match self.num_account_loads.checked_sub(1) {
232 Some(value) => self.num_account_loads = value,
233 None => return Err(AccountInsertError::TooManyAccounts),
234 }
235
236 if self.loaded_accounts.insert(pubkey) {
237 return Ok(());
238 }
239
240 Err(AccountInsertError::AccountAlreadyLoaded)
241 }
242
243 pub fn push(&mut self) -> Result<(), InstructionError> {
245 let instruction_context = self
246 .transaction_context
247 .get_instruction_context_at_index_in_trace(
248 self.transaction_context.get_instruction_trace_length(),
249 )?;
250 let program_id = instruction_context
251 .get_last_program_key(self.transaction_context)
252 .map_err(|_| InstructionError::UnsupportedProgramId)?;
253 if self
254 .transaction_context
255 .get_instruction_context_stack_height()
256 != 0
257 {
258 let contains = (0..self
259 .transaction_context
260 .get_instruction_context_stack_height())
261 .any(|level| {
262 self.transaction_context
263 .get_instruction_context_at_nesting_level(level)
264 .and_then(|instruction_context| {
265 instruction_context
266 .try_borrow_last_program_account(self.transaction_context)
267 })
268 .map(|program_account| program_account.get_key() == program_id)
269 .unwrap_or(false)
270 });
271 let is_last = self
272 .transaction_context
273 .get_current_instruction_context()
274 .and_then(|instruction_context| {
275 instruction_context.try_borrow_last_program_account(self.transaction_context)
276 })
277 .map(|program_account| program_account.get_key() == program_id)
278 .unwrap_or(false);
279 if contains && !is_last {
280 return Err(InstructionError::ReentrancyNotAllowed);
282 }
283 }
284
285 self.syscall_context.push(None);
286 self.transaction_context.push()
287 }
288
289 fn pop(&mut self) -> Result<(), InstructionError> {
291 self.syscall_context.pop();
292 self.transaction_context.pop()
293 }
294
295 pub fn get_stack_height(&self) -> usize {
298 self.transaction_context
299 .get_instruction_context_stack_height()
300 }
301
302 pub fn native_invoke(
304 &mut self,
305 instruction: StableInstruction,
306 signers: &[Pubkey],
307 ) -> Result<(), InstructionError> {
308 let (instruction_accounts, program_indices) =
309 self.prepare_instruction(&instruction, signers)?;
310 let mut compute_units_consumed = 0;
311 self.process_instruction(
312 &instruction.data,
313 &instruction_accounts,
314 &program_indices,
315 &mut compute_units_consumed,
316 &mut ExecuteTimings::default(),
317 )?;
318 Ok(())
319 }
320
321 #[allow(clippy::type_complexity)]
323 pub fn prepare_instruction(
324 &mut self,
325 instruction: &StableInstruction,
326 signers: &[Pubkey],
327 ) -> Result<(Vec<InstructionAccount>, Vec<IndexOfAccount>), InstructionError> {
328 self.prepare_instruction_inner(instruction.program_id, &instruction.accounts, signers)
329 }
330
331 pub fn prepare_cpi_instruction(
333 &mut self,
334 program_id: Pubkey,
335 account_metas: &[AccountMeta],
336 signers: &[Pubkey],
337 ) -> Result<(Vec<InstructionAccount>, Vec<IndexOfAccount>), InstructionError> {
338 self.prepare_instruction_inner(program_id, account_metas, signers)
339 }
340
341 pub fn prepare_instruction_inner(
342 &mut self,
343 callee_program_id: Pubkey,
344 account_metas: &[AccountMeta],
345 signers: &[Pubkey],
346 ) -> Result<(Vec<InstructionAccount>, Vec<IndexOfAccount>), InstructionError> {
347 let instruction_context = self.transaction_context.get_current_instruction_context()?;
352 let mut deduplicated_instruction_accounts: Vec<InstructionAccount> = Vec::new();
353 let mut duplicate_indicies = Vec::with_capacity(account_metas.len());
354 for (instruction_account_index, account_meta) in account_metas.iter().enumerate() {
355 let index_in_transaction = self
356 .transaction_context
357 .find_index_of_account(&account_meta.pubkey)
358 .ok_or_else(|| {
359 ic_msg!(
360 self,
361 "Instruction references an unknown account {}",
362 account_meta.pubkey,
363 );
364 InstructionError::MissingAccount
365 })?;
366 if let Some(duplicate_index) =
367 deduplicated_instruction_accounts
368 .iter()
369 .position(|instruction_account| {
370 instruction_account.index_in_transaction == index_in_transaction
371 })
372 {
373 duplicate_indicies.push(duplicate_index);
374 let instruction_account = deduplicated_instruction_accounts
375 .get_mut(duplicate_index)
376 .ok_or(InstructionError::NotEnoughAccountKeys)?;
377 instruction_account.is_signer |= account_meta.is_signer;
378 instruction_account.is_writable |= account_meta.is_writable;
379 } else {
380 let index_in_caller = instruction_context
381 .find_index_of_instruction_account(
382 self.transaction_context,
383 &account_meta.pubkey,
384 )
385 .ok_or_else(|| {
386 ic_msg!(
387 self,
388 "Instruction references an unknown account {}",
389 account_meta.pubkey,
390 );
391 InstructionError::MissingAccount
392 })?;
393 duplicate_indicies.push(deduplicated_instruction_accounts.len());
394 deduplicated_instruction_accounts.push(InstructionAccount {
395 index_in_transaction,
396 index_in_caller,
397 index_in_callee: instruction_account_index as IndexOfAccount,
398 is_signer: account_meta.is_signer,
399 is_writable: account_meta.is_writable,
400 });
401 }
402 }
403 for instruction_account in deduplicated_instruction_accounts.iter() {
404 let borrowed_account = instruction_context.try_borrow_instruction_account(
405 self.transaction_context,
406 instruction_account.index_in_caller,
407 )?;
408
409 if instruction_account.is_writable && !borrowed_account.is_writable() {
411 ic_msg!(
412 self,
413 "{}'s writable privilege escalated",
414 borrowed_account.get_key(),
415 );
416 return Err(InstructionError::PrivilegeEscalation);
417 }
418
419 if instruction_account.is_signer
422 && !(borrowed_account.is_signer() || signers.contains(borrowed_account.get_key()))
423 {
424 ic_msg!(
425 self,
426 "{}'s signer privilege escalated",
427 borrowed_account.get_key()
428 );
429 return Err(InstructionError::PrivilegeEscalation);
430 }
431 }
432 let instruction_accounts = duplicate_indicies
433 .into_iter()
434 .map(|duplicate_index| {
435 deduplicated_instruction_accounts
436 .get(duplicate_index)
437 .cloned()
438 .ok_or(InstructionError::NotEnoughAccountKeys)
439 })
440 .collect::<Result<Vec<InstructionAccount>, InstructionError>>()?;
441
442 let program_account_index = if self
444 .get_feature_set()
445 .is_active(&lift_cpi_caller_restriction::id())
446 {
447 match (
448 self.transaction_context
449 .find_index_of_program_account(&callee_program_id),
450 &self.account_loader,
451 ) {
452 (Some(index), _) => index,
453 (None, Some(account_loader)) => {
454 match account_loader.load_account(&callee_program_id) {
455 Ok(Some(account)) => {
456 self.transaction_context
457 .add_account((callee_program_id, account.clone()));
458
459 assert!(self.loaded_accounts.insert(callee_program_id));
461
462 self.transaction_context
464 .find_index_of_program_account(&callee_program_id)
465 .expect("program to exist")
466 }
467 Ok(None) => return Err(InstructionError::MissingAccount),
468 Err(()) => return Err(InstructionError::GenericError),
469 }
470 }
471 (None, None) => {
472 ic_msg!(self, "Unknown program {}", callee_program_id);
473 return Err(InstructionError::MissingAccount);
474 }
475 }
476 } else {
477 let program_account_index = instruction_context
478 .find_index_of_instruction_account(self.transaction_context, &callee_program_id)
479 .ok_or_else(|| {
480 ic_msg!(self, "Unknown program {}", callee_program_id);
481 InstructionError::MissingAccount
482 })?;
483 let borrowed_program_account = instruction_context
484 .try_borrow_instruction_account(self.transaction_context, program_account_index)?;
485 #[allow(deprecated)]
486 if !self
487 .get_feature_set()
488 .is_active(&remove_accounts_executable_flag_checks::id())
489 && !borrowed_program_account.is_executable()
490 {
491 ic_msg!(self, "Account {} is not executable", callee_program_id);
492 return Err(InstructionError::AccountNotExecutable);
493 }
494 borrowed_program_account.get_index_in_transaction()
495 };
496
497 Ok((instruction_accounts, vec![program_account_index]))
498 }
499
500 pub fn process_instruction(
502 &mut self,
503 instruction_data: &[u8],
504 instruction_accounts: &[InstructionAccount],
505 program_indices: &[IndexOfAccount],
506 compute_units_consumed: &mut u64,
507 timings: &mut ExecuteTimings,
508 ) -> Result<(), InstructionError> {
509 *compute_units_consumed = 0;
510 self.transaction_context
511 .get_next_instruction_context()?
512 .configure(program_indices, instruction_accounts, instruction_data);
513 self.push()?;
514 self.process_executable_chain(compute_units_consumed, timings)
515 .and(self.pop())
518 }
519
520 pub fn process_precompile<'ix_data>(
522 &mut self,
523 precompile: &Precompile,
524 instruction_data: &[u8],
525 instruction_accounts: &[InstructionAccount],
526 program_indices: &[IndexOfAccount],
527 message_instruction_datas_iter: impl Iterator<Item = &'ix_data [u8]>,
528 ) -> Result<(), InstructionError> {
529 self.transaction_context
530 .get_next_instruction_context()?
531 .configure(program_indices, instruction_accounts, instruction_data);
532 self.push()?;
533
534 let feature_set = self.get_feature_set();
535 let move_precompile_verification_to_svm =
536 feature_set.is_active(&move_precompile_verification_to_svm::id());
537 if move_precompile_verification_to_svm {
538 let instruction_datas: Vec<_> = message_instruction_datas_iter.collect();
539 precompile
540 .verify(instruction_data, &instruction_datas, feature_set)
541 .map_err(InstructionError::from)
542 .and(self.pop())
543 } else {
544 self.pop()
545 }
546 }
547
548 fn process_executable_chain(
550 &mut self,
551 compute_units_consumed: &mut u64,
552 timings: &mut ExecuteTimings,
553 ) -> Result<(), InstructionError> {
554 let instruction_context = self.transaction_context.get_current_instruction_context()?;
555 let process_executable_chain_time = Measure::start("process_executable_chain_time");
556
557 let builtin_id = {
558 debug_assert!(instruction_context.get_number_of_program_accounts() <= 1);
559 let borrowed_root_account = instruction_context
560 .try_borrow_program_account(self.transaction_context, 0)
561 .map_err(|_| InstructionError::UnsupportedProgramId)?;
562 let owner_id = borrowed_root_account.get_owner();
563 if native_loader::check_id(owner_id) {
564 *borrowed_root_account.get_key()
565 } else {
566 *owner_id
567 }
568 };
569
570 let entry = self
571 .program_cache_for_tx_batch
572 .find(&builtin_id)
573 .ok_or(InstructionError::UnsupportedProgramId)?;
574 let function = match &entry.program {
575 ProgramCacheEntryType::Builtin(function) => *function,
576 _ => return Err(InstructionError::UnsupportedProgramId),
577 };
578 entry.ix_usage_counter.fetch_add(1, Ordering::Relaxed);
579
580 let program_id = *instruction_context.get_last_program_key(self.transaction_context)?;
581 self.transaction_context
582 .set_return_data(program_id, Vec::new())?;
583 let logger = self.get_log_collector();
584 stable_log::program_invoke(&logger, &program_id, self.get_stack_height());
585 let pre_remaining_units = self.get_remaining();
586 let result = match function(self) {
587 Ok(()) => {
588 stable_log::program_success(&logger, &program_id);
589 Ok(())
590 }
591 Err(err) => {
592 stable_log::program_failure(&logger, &program_id, &err);
593 Err(err)
594 }
595 };
596 let post_remaining_units = self.get_remaining();
597 *compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units);
598
599 if builtin_id == program_id && result.is_ok() && *compute_units_consumed == 0 {
600 return Err(InstructionError::BuiltinProgramsMustConsumeComputeUnits);
601 }
602
603 timings
604 .execute_accessories
605 .process_instructions
606 .process_executable_chain_us += process_executable_chain_time.end_as_us();
607 result
608 }
609
610 pub fn get_log_collector(&self) -> Option<Rc<RefCell<LogCollector>>> {
612 self.log_collector.clone()
613 }
614
615 pub fn get_remaining(&self) -> u64 {
617 *self.compute_meter.borrow()
618 }
619
620 pub fn consume_checked(&self, amount: u64) -> Result<(), InstructionError> {
622 let mut compute_meter = self.compute_meter.borrow_mut();
623 let exceeded = *compute_meter < amount;
624 *compute_meter = compute_meter.saturating_sub(amount);
625 if exceeded {
626 return Err(InstructionError::ComputationalBudgetExceeded);
627 }
628 Ok(())
629 }
630
631 pub fn mock_set_remaining(&self, remaining: u64) {
635 *self.compute_meter.borrow_mut() = remaining;
636 }
637
638 pub fn get_compute_budget(&self) -> &ComputeBudget {
640 &self.compute_budget
641 }
642
643 pub fn get_feature_set(&self) -> &FeatureSet {
645 &self.environment_config.feature_set
646 }
647
648 pub fn mock_set_feature_set(&mut self, feature_set: Arc<FeatureSet>) {
652 self.environment_config.feature_set = feature_set;
653 }
654
655 pub fn get_active_features(&self) -> &Arc<ActiveFeatures> {
659 &self.environment_config.active_features
660 }
661
662 #[cfg(any(test, feature = "dev-context-only-utils"))]
669 pub fn mock_set_active_features(&mut self, active_features: Arc<ActiveFeatures>) {
670 self.environment_config.active_features = active_features;
671 }
672
673 pub fn get_sysvar_cache(&self) -> &SysvarCache {
675 self.environment_config.sysvar_cache
676 }
677
678 pub fn get_epoch_vote_account_stake(&self, pubkey: &'a Pubkey) -> u64 {
680 (self
681 .environment_config
682 .get_epoch_vote_account_stake_callback)(pubkey)
683 }
684
685 pub fn get_current_epoch(&self) -> u64 {
695 self.environment_config
696 .stakes_handle
697 .pending_epoch()
698 .saturating_sub(1)
699 }
700
701 pub fn get_next_epoch(&self) -> u64 {
708 self.environment_config.stakes_handle.pending_epoch()
709 }
710
711 pub fn get_last_freeze_timestamp(&self) -> u64 {
726 self.environment_config
727 .stakes_handle
728 .last_frozen_timestamp()
729 .unwrap_or(0)
730 }
731
732 pub fn mock_set_last_freeze_timestamp(&mut self, timestamp: u64) {
743 self.environment_config.stakes_handle.set_pending_epoch(1);
745
746 let history_entry = StakeCacheData {
748 epoch: 0,
749 timestamp,
750 ..Default::default()
751 };
752 self.environment_config
753 .stakes_handle
754 .push_frozen(history_entry);
755 }
756
757 pub fn mock_insert_stake_account(
764 &mut self,
765 pubkey: rialo_s_pubkey::Pubkey,
766 account: rialo_stake_cache_interface::StakeAccount,
767 ) {
768 self.environment_config
769 .stakes_handle
770 .insert_stake_account(pubkey, account);
771 }
772
773 pub fn mock_insert_validator_account(
784 &mut self,
785 pubkey: rialo_s_pubkey::Pubkey,
786 account: ValidatorAccount,
787 ) {
788 self.environment_config
789 .stakes_handle
790 .insert_validator_account(pubkey, account);
791 }
792
793 pub fn mock_freeze_stakes(&self) {
803 self.environment_config.stakes_handle.freeze_stakes();
804 }
805
806 pub fn signal_freeze_stakes(&self) {
813 self.environment_config
814 .stakes_handle
815 .set_epoch_stakes_frozen();
816 }
817
818 pub fn signal_force_next_auto_freeze(&self) {
821 self.environment_config
822 .stakes_handle
823 .set_force_next_auto_freeze();
824 }
825
826 pub fn take_force_next_auto_freeze(&self) -> bool {
829 self.environment_config
830 .stakes_handle
831 .take_force_next_auto_freeze()
832 }
833
834 pub fn get_all_validator_accounts_from_last_frozen(&self) -> Vec<(Pubkey, ValidatorAccount)> {
843 self.environment_config
844 .stakes_handle
845 .get_all_validator_accounts_from_last_frozen()
846 }
847
848 pub fn frozen_stake_history_len(&self) -> usize {
853 self.environment_config.stakes_handle.frozen_len()
854 }
855
856 pub fn request_epoch_rewards_init(
869 &self,
870 epoch: u64,
871 total_rewards: u64,
872 total_eligible_stake: u64,
873 validator_scores: Vec<u32>,
874 ) {
875 self.environment_config
876 .stakes_handle
877 .request_epoch_rewards_init(
878 epoch,
879 total_rewards,
880 total_eligible_stake,
881 validator_scores,
882 );
883 }
884
885 pub fn get_all_validator_accounts_from_frozen_epoch(
888 &self,
889 epoch: u64,
890 ) -> Vec<(Pubkey, ValidatorAccount)> {
891 self.environment_config
892 .stakes_handle
893 .get_all_validator_accounts_from_frozen_epoch(epoch)
894 }
895
896 pub fn front_frozen_epoch(&self) -> Option<u64> {
903 self.environment_config.stakes_handle.front_frozen_epoch()
904 }
905
906 pub fn is_validator_referenced(
929 &self,
930 validator: &Pubkey,
931 validator_info: &rialo_stake_cache_interface::ValidatorInfo,
932 last_freeze_timestamp: u64,
933 current_timestamp: u64,
934 ) -> bool {
935 self.environment_config
936 .stakes_handle
937 .is_validator_referenced(
938 validator,
939 validator_info,
940 last_freeze_timestamp,
941 current_timestamp,
942 )
943 }
944
945 pub fn has_locked_stakers(
960 &self,
961 validator: &Pubkey,
962 lockup_period: u64,
963 current_timestamp: u64,
964 ) -> bool {
965 self.environment_config.stakes_handle.has_locked_stakers(
966 validator,
967 lockup_period,
968 current_timestamp,
969 )
970 }
971
972 pub fn is_validator_referenced_excluding_self_bond(
978 &self,
979 validator_pubkey: &Pubkey,
980 validator_info: &rialo_stake_cache_interface::ValidatorInfo,
981 last_freeze_timestamp: u64,
982 current_timestamp: u64,
983 ) -> bool {
984 self.environment_config
985 .stakes_handle
986 .is_validator_referenced_excluding_self_bond(
987 validator_pubkey,
988 validator_info,
989 last_freeze_timestamp,
990 current_timestamp,
991 )
992 }
993
994 pub fn get_stake_account_from_pending(
999 &self,
1000 pubkey: &Pubkey,
1001 ) -> Option<rialo_stake_cache_interface::StakeAccount> {
1002 self.environment_config
1003 .stakes_handle
1004 .get_stake_account_from_pending(pubkey)
1005 }
1006
1007 pub fn is_epoch_rewards_init_pending(&self) -> bool {
1012 self.environment_config
1013 .stakes_handle
1014 .is_epoch_rewards_init_pending()
1015 }
1016
1017 pub fn completed_frozen_epochs(&self) -> Vec<u64> {
1023 self.environment_config
1024 .stakes_handle
1025 .completed_frozen_epochs()
1026 }
1027
1028 pub fn epoch_rewards_exists(&self, epoch: u64) -> bool {
1034 self.environment_config
1035 .stakes_handle
1036 .epoch_rewards_exists(epoch)
1037 }
1038
1039 pub fn is_previous_epoch_adopted(&self) -> bool {
1044 self.environment_config
1045 .stakes_handle
1046 .is_previous_epoch_adopted()
1047 }
1048
1049 pub fn get_frozen_epoch_meta(&self, epoch: u64) -> Option<(u64, Option<u64>)> {
1056 self.environment_config
1057 .stakes_handle
1058 .get_frozen_epoch_meta(epoch)
1059 }
1060
1061 pub fn get_all_stake_accounts_from_frozen_epoch(
1063 &self,
1064 epoch: u64,
1065 ) -> Vec<(Pubkey, rialo_stake_cache_interface::StakeAccount)> {
1066 self.environment_config
1067 .stakes_handle
1068 .get_all_stake_accounts_from_frozen_epoch(epoch)
1069 }
1070
1071 #[cfg(feature = "testing")]
1080 pub fn signal_handover(&self, ts: u64) {
1081 self.environment_config.stakes_handle.signal_handover(ts);
1082 }
1083
1084 pub fn get_check_aligned(&self) -> bool {
1086 self.transaction_context
1087 .get_current_instruction_context()
1088 .and_then(|instruction_context| {
1089 let program_account =
1090 instruction_context.try_borrow_last_program_account(self.transaction_context);
1091 debug_assert!(program_account.is_ok());
1092 program_account
1093 })
1094 .map(|program_account| *program_account.get_owner() != bpf_loader_deprecated::id())
1095 .unwrap_or(true)
1096 }
1097
1098 pub fn set_syscall_context(
1100 &mut self,
1101 syscall_context: SyscallContext,
1102 ) -> Result<(), InstructionError> {
1103 *self
1104 .syscall_context
1105 .last_mut()
1106 .ok_or(InstructionError::CallDepth)? = Some(syscall_context);
1107 Ok(())
1108 }
1109
1110 pub fn get_syscall_context(&self) -> Result<&SyscallContext, InstructionError> {
1112 self.syscall_context
1113 .last()
1114 .and_then(std::option::Option::as_ref)
1115 .ok_or(InstructionError::CallDepth)
1116 }
1117
1118 pub fn get_syscall_context_mut(&mut self) -> Result<&mut SyscallContext, InstructionError> {
1120 self.syscall_context
1121 .last_mut()
1122 .and_then(|syscall_context| syscall_context.as_mut())
1123 .ok_or(InstructionError::CallDepth)
1124 }
1125}
1126
1127#[macro_export]
1128macro_rules! with_mock_invoke_context {
1129 (
1130 $invoke_context:ident,
1131 $transaction_context:ident,
1132 $entry:expr,
1133 $transaction_accounts:expr $(,)?
1134 ) => {
1135 use rialo_s_compute_budget::compute_budget::ComputeBudget;
1136 use rialo_s_feature_set::FeatureSet;
1137 use rialo_s_log_collector::LogCollector;
1138 use rialo_s_type_overrides::sync::Arc;
1139 use $crate::{
1140 __private::{Hash, ReadableAccount, Rent, TransactionContext},
1141 invoke_context::{EnvironmentConfig, InvokeContext},
1142 loaded_programs::{ProgramCacheEntry, ProgramCacheForTx, ProgramCacheForTxBatch},
1143 sysvar_cache::SysvarCache,
1144 };
1145 let compute_budget = ComputeBudget::default();
1146 let mut $transaction_context = TransactionContext::new(
1147 $transaction_accounts,
1148 Rent::default(),
1149 compute_budget.max_instruction_stack_depth,
1150 compute_budget.max_instruction_trace_length,
1151 );
1152 let mut sysvar_cache = SysvarCache::default();
1153 sysvar_cache.fill_missing_entries(|pubkey, callback| {
1154 for index in 0..$transaction_context.get_number_of_accounts() {
1155 if $transaction_context
1156 .get_key_of_account_at_index(index)
1157 .unwrap()
1158 == pubkey
1159 {
1160 callback(
1161 $transaction_context
1162 .get_account_at_index(index)
1163 .unwrap()
1164 .borrow()
1165 .data(),
1166 );
1167 }
1168 }
1169 });
1170 let mock_stakes_handle = $crate::invoke_context::StakesHandle::default();
1174 let environment_config = EnvironmentConfig::new(
1175 Hash::default(),
1176 0,
1177 &|_| 0,
1178 Arc::new(FeatureSet::all_enabled()),
1179 Arc::new($crate::active_features::ActiveFeatures::new()),
1180 &sysvar_cache,
1181 0,
1182 mock_stakes_handle,
1183 );
1184 let mut program_cache_for_tx_batch = ProgramCacheForTxBatch::default();
1185 if let Some((loader_id, builtin)) = $entry {
1186 program_cache_for_tx_batch.replenish(
1187 loader_id,
1188 Arc::new(ProgramCacheEntry::new_builtin(0, 0, builtin)),
1189 );
1190 }
1191 let mut program_cache_for_tx = ProgramCacheForTx::from_cache(&program_cache_for_tx_batch);
1192 let mut $invoke_context = InvokeContext::new(
1193 &mut $transaction_context,
1194 &mut program_cache_for_tx,
1195 environment_config,
1196 Some(LogCollector::new_ref()),
1197 compute_budget,
1198 );
1199 };
1200 (
1201 $invoke_context:ident,
1202 $transaction_context:ident,
1203 $transaction_accounts:expr $(,)?
1204 ) => {
1205 with_mock_invoke_context!(
1206 $invoke_context,
1207 $transaction_context,
1208 None,
1209 $transaction_accounts
1210 )
1211 };
1212}
1213
1214pub fn mock_process_instruction<
1215 T: Into<StoredAccount>,
1216 F: FnMut(&mut InvokeContext<'_, '_>),
1217 G: FnMut(&mut InvokeContext<'_, '_>),
1218>(
1219 loader_id: &Pubkey,
1220 mut program_indices: Vec<IndexOfAccount>,
1221 instruction_data: &[u8],
1222 transaction_accounts: Vec<(Pubkey, T)>,
1223 instruction_account_metas: Vec<AccountMeta>,
1224 expected_result: Result<(), InstructionError>,
1225 builtin_function: BuiltinFunctionWithContext,
1226 mut pre_adjustments: F,
1227 mut post_adjustments: G,
1228) -> Vec<StoredAccount> {
1229 let mut transaction_accounts: Vec<TransactionAccount> = transaction_accounts
1231 .into_iter()
1232 .map(|(key, account)| (key, account.into()))
1233 .collect();
1234 let mut instruction_accounts: Vec<InstructionAccount> =
1235 Vec::with_capacity(instruction_account_metas.len());
1236 for (instruction_account_index, account_meta) in instruction_account_metas.iter().enumerate() {
1237 let index_in_transaction = transaction_accounts
1238 .iter()
1239 .position(|(key, _account)| *key == account_meta.pubkey)
1240 .unwrap_or(transaction_accounts.len())
1241 as IndexOfAccount;
1242 let index_in_callee = instruction_accounts
1243 .get(0..instruction_account_index)
1244 .unwrap()
1245 .iter()
1246 .position(|instruction_account| {
1247 instruction_account.index_in_transaction == index_in_transaction
1248 })
1249 .unwrap_or(instruction_account_index) as IndexOfAccount;
1250 instruction_accounts.push(InstructionAccount {
1251 index_in_transaction,
1252 index_in_caller: index_in_transaction,
1253 index_in_callee,
1254 is_signer: account_meta.is_signer,
1255 is_writable: account_meta.is_writable,
1256 });
1257 }
1258 if program_indices.is_empty() {
1259 program_indices.insert(0, transaction_accounts.len() as IndexOfAccount);
1260 let processor_account =
1261 StoredAccount::Data(AccountSharedData::new(0, 0, &native_loader::id()));
1262 transaction_accounts.push((*loader_id, processor_account));
1263 }
1264 let pop_epoch_schedule_account = if !transaction_accounts
1265 .iter()
1266 .any(|(key, _)| *key == sysvar::epoch_schedule::id())
1267 {
1268 transaction_accounts.push((
1269 sysvar::epoch_schedule::id(),
1270 create_account_shared_data_for_test(&EpochSchedule::default()),
1271 ));
1272 true
1273 } else {
1274 false
1275 };
1276 with_mock_invoke_context!(
1277 invoke_context,
1278 transaction_context,
1279 Some((*loader_id, builtin_function)),
1280 transaction_accounts
1281 );
1282 pre_adjustments(&mut invoke_context);
1283 let result = invoke_context.process_instruction(
1284 instruction_data,
1285 &instruction_accounts,
1286 &program_indices,
1287 &mut 0,
1288 &mut ExecuteTimings::default(),
1289 );
1290 assert_eq!(result, expected_result);
1291 post_adjustments(&mut invoke_context);
1292 drop(invoke_context);
1293 let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
1294 if pop_epoch_schedule_account {
1295 transaction_accounts.pop();
1296 }
1297 transaction_accounts.pop();
1298 transaction_accounts
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303 use rialo_s_compute_budget::compute_budget_limits;
1304 use rialo_s_instruction::Instruction;
1305 use rialo_s_rent::Rent;
1306 use serde::{Deserialize, Serialize};
1307
1308 use super::*;
1309
1310 #[derive(Debug, Serialize, Deserialize)]
1311 enum MockInstruction {
1312 NoopSuccess,
1313 NoopFail,
1314 ModifyOwned,
1315 ModifyNotOwned,
1316 ModifyReadonly,
1317 UnbalancedPush,
1318 UnbalancedPop,
1319 ConsumeComputeUnits {
1320 compute_units_to_consume: u64,
1321 desired_result: Result<(), InstructionError>,
1322 },
1323 Resize {
1324 new_len: u64,
1325 },
1326 }
1327
1328 const MOCK_BUILTIN_COMPUTE_UNIT_COST: u64 = 1;
1329
1330 declare_process_instruction!(
1331 MockBuiltin,
1332 MOCK_BUILTIN_COMPUTE_UNIT_COST,
1333 |invoke_context| {
1334 let transaction_context = &invoke_context.transaction_context;
1335 let instruction_context = transaction_context.get_current_instruction_context()?;
1336 let instruction_data = instruction_context.get_instruction_data();
1337 let program_id = instruction_context.get_last_program_key(transaction_context)?;
1338 let instruction_accounts = (0..4)
1339 .map(|instruction_account_index| InstructionAccount {
1340 index_in_transaction: instruction_account_index,
1341 index_in_caller: instruction_account_index,
1342 index_in_callee: instruction_account_index,
1343 is_signer: false,
1344 is_writable: false,
1345 })
1346 .collect::<Vec<_>>();
1347 assert_eq!(
1348 program_id,
1349 instruction_context
1350 .try_borrow_instruction_account(transaction_context, 0)?
1351 .get_owner()
1352 );
1353 assert_ne!(
1354 instruction_context
1355 .try_borrow_instruction_account(transaction_context, 1)?
1356 .get_owner(),
1357 instruction_context
1358 .try_borrow_instruction_account(transaction_context, 0)?
1359 .get_key()
1360 );
1361
1362 if let Ok(instruction) = bincode::deserialize(instruction_data) {
1363 match instruction {
1364 MockInstruction::NoopSuccess => (),
1365 MockInstruction::NoopFail => return Err(InstructionError::GenericError),
1366 MockInstruction::ModifyOwned => instruction_context
1367 .try_borrow_instruction_account(transaction_context, 0)?
1368 .set_data_from_slice(&[1])?,
1369 MockInstruction::ModifyNotOwned => instruction_context
1370 .try_borrow_instruction_account(transaction_context, 1)?
1371 .set_data_from_slice(&[1])?,
1372 MockInstruction::ModifyReadonly => instruction_context
1373 .try_borrow_instruction_account(transaction_context, 2)?
1374 .set_data_from_slice(&[1])?,
1375 MockInstruction::UnbalancedPush => {
1376 instruction_context
1377 .try_borrow_instruction_account(transaction_context, 0)?
1378 .checked_add_kelvins(1)?;
1379 let program_id = *transaction_context.get_key_of_account_at_index(3)?;
1380 let metas = vec![
1381 AccountMeta::new_readonly(
1382 *transaction_context.get_key_of_account_at_index(0)?,
1383 false,
1384 ),
1385 AccountMeta::new_readonly(
1386 *transaction_context.get_key_of_account_at_index(1)?,
1387 false,
1388 ),
1389 ];
1390 let inner_instruction = Instruction::new_with_bincode(
1391 program_id,
1392 &MockInstruction::NoopSuccess,
1393 metas,
1394 );
1395 invoke_context
1396 .transaction_context
1397 .get_next_instruction_context()
1398 .unwrap()
1399 .configure(&[3], &instruction_accounts, &[]);
1400 let result = invoke_context.push();
1401 assert_eq!(result, Err(InstructionError::UnbalancedInstruction));
1402 result?;
1403 invoke_context
1404 .native_invoke(inner_instruction.into(), &[])
1405 .and(invoke_context.pop())?;
1406 }
1407 MockInstruction::UnbalancedPop => instruction_context
1408 .try_borrow_instruction_account(transaction_context, 0)?
1409 .checked_add_kelvins(1)?,
1410 MockInstruction::ConsumeComputeUnits {
1411 compute_units_to_consume,
1412 desired_result,
1413 } => {
1414 invoke_context
1415 .consume_checked(compute_units_to_consume)
1416 .map_err(|_| InstructionError::ComputationalBudgetExceeded)?;
1417 return desired_result;
1418 }
1419 MockInstruction::Resize { new_len } => instruction_context
1420 .try_borrow_instruction_account(transaction_context, 0)?
1421 .set_data(vec![0; new_len as usize])?,
1422 }
1423 } else {
1424 return Err(InstructionError::InvalidInstructionData);
1425 }
1426 Ok(())
1427 }
1428 );
1429
1430 #[test]
1431 fn test_instruction_stack_height() {
1432 let one_more_than_max_depth = ComputeBudget::default()
1433 .max_instruction_stack_depth
1434 .saturating_add(1);
1435 let mut invoke_stack = vec![];
1436 let mut transaction_accounts = vec![];
1437 let mut instruction_accounts = vec![];
1438 for index in 0..one_more_than_max_depth {
1439 invoke_stack.push(rialo_s_pubkey::new_rand());
1440 transaction_accounts.push((
1441 rialo_s_pubkey::new_rand(),
1442 AccountSharedData::new(index as u64, 1, invoke_stack.get(index).unwrap()),
1443 ));
1444 instruction_accounts.push(InstructionAccount {
1445 index_in_transaction: index as IndexOfAccount,
1446 index_in_caller: index as IndexOfAccount,
1447 index_in_callee: instruction_accounts.len() as IndexOfAccount,
1448 is_signer: false,
1449 is_writable: true,
1450 });
1451 }
1452 for (index, program_id) in invoke_stack.iter().enumerate() {
1453 transaction_accounts.push((
1454 *program_id,
1455 AccountSharedData::new(1, 1, &rialo_s_pubkey::Pubkey::default()),
1456 ));
1457 instruction_accounts.push(InstructionAccount {
1458 index_in_transaction: index as IndexOfAccount,
1459 index_in_caller: index as IndexOfAccount,
1460 index_in_callee: index as IndexOfAccount,
1461 is_signer: false,
1462 is_writable: false,
1463 });
1464 }
1465 let transaction_accounts: Vec<(Pubkey, StoredAccount)> = transaction_accounts
1466 .into_iter()
1467 .map(|(k, v)| (k, v.into()))
1468 .collect();
1469 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1470
1471 let mut depth_reached = 0;
1473 for _ in 0..invoke_stack.len() {
1474 invoke_context
1475 .transaction_context
1476 .get_next_instruction_context()
1477 .unwrap()
1478 .configure(
1479 &[one_more_than_max_depth.saturating_add(depth_reached) as IndexOfAccount],
1480 &instruction_accounts,
1481 &[],
1482 );
1483 if Err(InstructionError::CallDepth) == invoke_context.push() {
1484 break;
1485 }
1486 depth_reached = depth_reached.saturating_add(1);
1487 }
1488 assert_ne!(depth_reached, 0);
1489 assert!(depth_reached < one_more_than_max_depth);
1490 }
1491
1492 #[test]
1493 fn test_max_instruction_trace_length() {
1494 const MAX_INSTRUCTIONS: usize = 8;
1495 let mut transaction_context = TransactionContext::new::<StoredAccount>(
1496 Vec::new(),
1497 Rent::default(),
1498 1,
1499 MAX_INSTRUCTIONS,
1500 );
1501 for _ in 0..MAX_INSTRUCTIONS {
1502 transaction_context.push().unwrap();
1503 transaction_context.pop().unwrap();
1504 }
1505 assert_eq!(
1506 transaction_context.push(),
1507 Err(InstructionError::MaxInstructionTraceLengthExceeded)
1508 );
1509 }
1510
1511 #[test]
1512 fn test_process_instruction() {
1513 let callee_program_id = rialo_s_pubkey::new_rand();
1514 let owned_account = AccountSharedData::new(42, 1, &callee_program_id);
1515 let not_owned_account = AccountSharedData::new(84, 1, &rialo_s_pubkey::new_rand());
1516 let readonly_account = AccountSharedData::new(168, 1, &rialo_s_pubkey::new_rand());
1517 let loader_account = AccountSharedData::new(0, 1, &native_loader::id());
1518 let program_account = AccountSharedData::new(1, 1, &native_loader::id());
1519 let transaction_accounts = vec![
1520 (rialo_s_pubkey::new_rand(), owned_account),
1521 (rialo_s_pubkey::new_rand(), not_owned_account),
1522 (rialo_s_pubkey::new_rand(), readonly_account),
1523 (callee_program_id, program_account),
1524 (rialo_s_pubkey::new_rand(), loader_account),
1525 ];
1526 let metas = vec![
1527 AccountMeta::new(transaction_accounts.first().unwrap().0, false),
1528 AccountMeta::new(transaction_accounts.get(1).unwrap().0, false),
1529 AccountMeta::new_readonly(transaction_accounts.get(2).unwrap().0, false),
1530 ];
1531 let instruction_accounts = (0..4)
1532 .map(|instruction_account_index| InstructionAccount {
1533 index_in_transaction: instruction_account_index,
1534 index_in_caller: instruction_account_index,
1535 index_in_callee: instruction_account_index,
1536 is_signer: false,
1537 is_writable: instruction_account_index < 2,
1538 })
1539 .collect::<Vec<_>>();
1540 let transaction_accounts: Vec<(Pubkey, StoredAccount)> = transaction_accounts
1541 .into_iter()
1542 .map(|(k, v)| (k, v.into()))
1543 .collect();
1544 with_mock_invoke_context!(
1545 invoke_context,
1546 transaction_context,
1547 Some((callee_program_id, MockBuiltin::vm)),
1548 transaction_accounts
1549 );
1550
1551 let cases = vec![
1553 (MockInstruction::NoopSuccess, Ok(())),
1554 (
1555 MockInstruction::NoopFail,
1556 Err(InstructionError::GenericError),
1557 ),
1558 (MockInstruction::ModifyOwned, Ok(())),
1559 (
1560 MockInstruction::ModifyNotOwned,
1561 Err(InstructionError::ExternalAccountDataModified),
1562 ),
1563 (
1564 MockInstruction::ModifyReadonly,
1565 Err(InstructionError::ReadonlyDataModified),
1566 ),
1567 (
1568 MockInstruction::UnbalancedPush,
1569 Err(InstructionError::UnbalancedInstruction),
1570 ),
1571 (
1572 MockInstruction::UnbalancedPop,
1573 Err(InstructionError::UnbalancedInstruction),
1574 ),
1575 ];
1576 for case in cases {
1577 invoke_context
1578 .transaction_context
1579 .get_next_instruction_context()
1580 .unwrap()
1581 .configure(&[4], &instruction_accounts, &[]);
1582 invoke_context.push().unwrap();
1583 let inner_instruction =
1584 Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
1585 let result = invoke_context
1586 .native_invoke(inner_instruction.into(), &[])
1587 .and(invoke_context.pop());
1588 assert_eq!(result, case.1);
1589 }
1590
1591 let compute_units_to_consume = 10;
1593 let expected_results = vec![Ok(()), Err(InstructionError::GenericError)];
1594 for expected_result in expected_results {
1595 invoke_context
1596 .transaction_context
1597 .get_next_instruction_context()
1598 .unwrap()
1599 .configure(&[4], &instruction_accounts, &[]);
1600 invoke_context.push().unwrap();
1601 let inner_instruction = Instruction::new_with_bincode(
1602 callee_program_id,
1603 &MockInstruction::ConsumeComputeUnits {
1604 compute_units_to_consume,
1605 desired_result: expected_result.clone(),
1606 },
1607 metas.clone(),
1608 );
1609 let inner_instruction = StableInstruction::from(inner_instruction);
1610 let (inner_instruction_accounts, program_indices) = invoke_context
1611 .prepare_instruction(&inner_instruction, &[])
1612 .unwrap();
1613
1614 let mut compute_units_consumed = 0;
1615 let result = invoke_context.process_instruction(
1616 &inner_instruction.data,
1617 &inner_instruction_accounts,
1618 &program_indices,
1619 &mut compute_units_consumed,
1620 &mut ExecuteTimings::default(),
1621 );
1622
1623 assert!(compute_units_consumed > 0);
1627 assert_eq!(
1628 compute_units_consumed,
1629 compute_units_to_consume.saturating_add(MOCK_BUILTIN_COMPUTE_UNIT_COST),
1630 );
1631 assert_eq!(result, expected_result);
1632
1633 invoke_context.pop().unwrap();
1634 }
1635 }
1636
1637 #[test]
1638 fn test_invoke_context_compute_budget() {
1639 let transaction_accounts = vec![(rialo_s_pubkey::new_rand(), AccountSharedData::default())];
1640 let transaction_accounts: Vec<(Pubkey, StoredAccount)> = transaction_accounts
1641 .into_iter()
1642 .map(|(k, v)| (k, v.into()))
1643 .collect();
1644
1645 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1646 invoke_context.compute_budget = ComputeBudget::new(
1647 compute_budget_limits::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
1648 );
1649
1650 invoke_context
1651 .transaction_context
1652 .get_next_instruction_context()
1653 .unwrap()
1654 .configure(&[0], &[], &[]);
1655 invoke_context.push().unwrap();
1656 assert_eq!(
1657 *invoke_context.get_compute_budget(),
1658 ComputeBudget::new(
1659 compute_budget_limits::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64
1660 )
1661 );
1662 invoke_context.pop().unwrap();
1663 }
1664
1665 #[test]
1666 fn test_process_instruction_accounts_resize_delta() {
1667 let program_key = Pubkey::new_unique();
1668 let user_account_data_len = 123u64;
1669 let user_account =
1670 AccountSharedData::new(100, user_account_data_len as usize, &program_key);
1671 let dummy_account = AccountSharedData::new(10, 0, &program_key);
1672 let program_account = AccountSharedData::new(500, 500, &native_loader::id());
1673 let transaction_accounts = vec![
1674 (Pubkey::new_unique(), user_account),
1675 (Pubkey::new_unique(), dummy_account),
1676 (program_key, program_account),
1677 ];
1678 let instruction_accounts = [
1679 InstructionAccount {
1680 index_in_transaction: 0,
1681 index_in_caller: 0,
1682 index_in_callee: 0,
1683 is_signer: false,
1684 is_writable: true,
1685 },
1686 InstructionAccount {
1687 index_in_transaction: 1,
1688 index_in_caller: 1,
1689 index_in_callee: 1,
1690 is_signer: false,
1691 is_writable: false,
1692 },
1693 ];
1694 let transaction_accounts: Vec<(Pubkey, StoredAccount)> = transaction_accounts
1695 .into_iter()
1696 .map(|(k, v)| (k, v.into()))
1697 .collect();
1698 with_mock_invoke_context!(
1699 invoke_context,
1700 transaction_context,
1701 Some((program_key, MockBuiltin::vm)),
1702 transaction_accounts
1703 );
1704
1705 {
1707 let resize_delta: i64 = 0;
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 {
1732 let resize_delta: i64 = 1;
1733 let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
1734 let instruction_data =
1735 bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
1736
1737 let result = invoke_context.process_instruction(
1738 &instruction_data,
1739 &instruction_accounts,
1740 &[2],
1741 &mut 0,
1742 &mut ExecuteTimings::default(),
1743 );
1744
1745 assert!(result.is_ok());
1746 assert_eq!(
1747 invoke_context
1748 .transaction_context
1749 .accounts_resize_delta()
1750 .unwrap(),
1751 resize_delta
1752 );
1753 }
1754
1755 {
1757 let resize_delta: i64 = -1;
1758 let new_len = (user_account_data_len as i64).saturating_add(resize_delta) as u64;
1759 let instruction_data =
1760 bincode::serialize(&MockInstruction::Resize { new_len }).unwrap();
1761
1762 let result = invoke_context.process_instruction(
1763 &instruction_data,
1764 &instruction_accounts,
1765 &[2],
1766 &mut 0,
1767 &mut ExecuteTimings::default(),
1768 );
1769
1770 assert!(result.is_ok());
1771 assert_eq!(
1772 invoke_context
1773 .transaction_context
1774 .accounts_resize_delta()
1775 .unwrap(),
1776 resize_delta
1777 );
1778 }
1779 }
1780}