1#[cfg(feature = "dev-context-only-utils")]
2use qualifier_attr::field_qualifiers;
3use {
4 crate::{
5 account_overrides::AccountOverrides,
6 nonce_info::NonceInfo,
7 rollback_accounts::RollbackAccounts,
8 transaction_error_metrics::TransactionErrorMetrics,
9 transaction_execution_result::ExecutedTransaction,
10 transaction_processing_callback::{AccountState, TransactionProcessingCallback},
11 },
12 ahash::{AHashMap, AHashSet},
13 solana_account::{
14 Account, AccountSharedData, ReadableAccount, WritableAccount, PROGRAM_OWNERS,
15 },
16 solana_compute_budget::compute_budget_limits::ComputeBudgetLimits,
17 solana_feature_set::{self as feature_set, FeatureSet},
18 solana_fee_structure::FeeDetails,
19 solana_instruction::{BorrowedAccountMeta, BorrowedInstruction},
20 solana_instructions_sysvar::construct_instructions_data,
21 solana_nonce::state::State as NonceState,
22 solana_nonce_account::{get_system_account_kind, SystemAccountKind},
23 solana_pubkey::Pubkey,
24 solana_rent::RentDue,
25 solana_rent_debits::RentDebits,
26 solana_sdk::rent_collector::{CollectedInfo, RENT_EXEMPT_RENT_EPOCH},
27 solana_sdk_ids::{
28 native_loader,
29 sysvar::{self, slot_history},
30 },
31 solana_svm_rent_collector::svm_rent_collector::SVMRentCollector,
32 solana_svm_transaction::svm_message::SVMMessage,
33 solana_transaction_context::{IndexOfAccount, TransactionAccount},
34 solana_transaction_error::{TransactionError, TransactionResult as Result},
35 std::{
36 num::{NonZeroU32, Saturating},
37 sync::Arc,
38 },
39};
40
41pub(crate) type TransactionRent = u64;
43pub(crate) type TransactionProgramIndices = Vec<Vec<IndexOfAccount>>;
44pub type TransactionCheckResult = Result<CheckedTransactionDetails>;
45type TransactionValidationResult = Result<ValidatedTransactionDetails>;
46
47#[derive(PartialEq, Eq, Debug)]
48pub(crate) enum TransactionLoadResult {
49 Loaded(LoadedTransaction),
51 FeesOnly(FeesOnlyTransaction),
55 NotLoaded(TransactionError),
58}
59
60#[derive(PartialEq, Eq, Debug, Clone)]
61#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
62pub struct CheckedTransactionDetails {
63 pub(crate) nonce: Option<NonceInfo>,
64 pub(crate) lamports_per_signature: u64,
65}
66
67impl CheckedTransactionDetails {
68 pub fn new(nonce: Option<NonceInfo>, lamports_per_signature: u64) -> Self {
69 Self {
70 nonce,
71 lamports_per_signature,
72 }
73 }
74}
75
76#[derive(PartialEq, Eq, Debug, Clone)]
77#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
78pub(crate) struct ValidatedTransactionDetails {
79 pub(crate) rollback_accounts: RollbackAccounts,
80 pub(crate) compute_budget_limits: ComputeBudgetLimits,
81 pub(crate) fee_details: FeeDetails,
82 pub(crate) loaded_fee_payer_account: LoadedTransactionAccount,
83}
84
85#[derive(PartialEq, Eq, Debug, Clone)]
86#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
87pub(crate) struct LoadedTransactionAccount {
88 pub(crate) account: AccountSharedData,
89 pub(crate) loaded_size: usize,
90 pub(crate) rent_collected: u64,
91}
92
93#[derive(PartialEq, Eq, Debug, Clone)]
94#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
95#[cfg_attr(
96 feature = "dev-context-only-utils",
97 field_qualifiers(
98 program_indices(pub),
99 compute_budget_limits(pub),
100 loaded_accounts_data_size(pub)
101 )
102)]
103pub struct LoadedTransaction {
104 pub accounts: Vec<TransactionAccount>,
105 pub(crate) program_indices: TransactionProgramIndices,
106 pub fee_details: FeeDetails,
107 pub rollback_accounts: RollbackAccounts,
108 pub(crate) compute_budget_limits: ComputeBudgetLimits,
109 pub rent: TransactionRent,
110 pub rent_debits: RentDebits,
111 pub(crate) loaded_accounts_data_size: u32,
112}
113
114#[derive(PartialEq, Eq, Debug, Clone)]
115pub struct FeesOnlyTransaction {
116 pub load_error: TransactionError,
117 pub rollback_accounts: RollbackAccounts,
118 pub fee_details: FeeDetails,
119}
120
121#[cfg_attr(feature = "dev-context-only-utils", derive(Clone))]
122pub(crate) struct AccountLoader<'a, CB: TransactionProcessingCallback> {
123 account_cache: AHashMap<Pubkey, AccountSharedData>,
124 callbacks: &'a CB,
125 pub(crate) feature_set: Arc<FeatureSet>,
126}
127impl<'a, CB: TransactionProcessingCallback> AccountLoader<'a, CB> {
128 pub(crate) fn new_with_account_cache_capacity(
129 account_overrides: Option<&'a AccountOverrides>,
130 callbacks: &'a CB,
131 feature_set: Arc<FeatureSet>,
132 capacity: usize,
133 ) -> AccountLoader<'a, CB> {
134 let mut account_cache = AHashMap::with_capacity(capacity);
135
136 if let Some(slot_history) =
139 account_overrides.and_then(|overrides| overrides.get(&slot_history::id()))
140 {
141 account_cache.insert(slot_history::id(), slot_history.clone());
142 }
143
144 Self {
145 account_cache,
146 callbacks,
147 feature_set,
148 }
149 }
150
151 pub(crate) fn load_account(
152 &mut self,
153 account_key: &Pubkey,
154 is_writable: bool,
155 ) -> Option<LoadedTransactionAccount> {
156 let account = if let Some(account) = self.account_cache.get(account_key) {
157 if account.lamports() == 0 {
161 None
162 } else {
163 Some(account.clone())
164 }
165 } else if let Some(account) = self.callbacks.get_account_shared_data(account_key) {
166 self.account_cache.insert(*account_key, account.clone());
167 Some(account)
168 } else {
169 None
170 };
171
172 self.callbacks.inspect_account(
174 account_key,
175 if let Some(ref account) = account {
176 AccountState::Alive(account)
177 } else {
178 AccountState::Dead
179 },
180 is_writable,
181 );
182
183 account.map(|account| LoadedTransactionAccount {
184 loaded_size: account.data().len(),
185 account,
186 rent_collected: 0,
187 })
188 }
189
190 pub(crate) fn update_accounts_for_executed_tx(
191 &mut self,
192 message: &impl SVMMessage,
193 executed_transaction: &ExecutedTransaction,
194 ) {
195 if executed_transaction.was_successful() {
196 self.update_accounts_for_successful_tx(
197 message,
198 &executed_transaction.loaded_transaction.accounts,
199 );
200 } else {
201 self.update_accounts_for_failed_tx(
202 message,
203 &executed_transaction.loaded_transaction.rollback_accounts,
204 );
205 }
206 }
207
208 pub(crate) fn update_accounts_for_failed_tx(
209 &mut self,
210 message: &impl SVMMessage,
211 rollback_accounts: &RollbackAccounts,
212 ) {
213 let fee_payer_address = message.fee_payer();
214 match rollback_accounts {
215 RollbackAccounts::FeePayerOnly { fee_payer_account } => {
216 self.account_cache
217 .insert(*fee_payer_address, fee_payer_account.clone());
218 }
219 RollbackAccounts::SameNonceAndFeePayer { nonce } => {
220 self.account_cache
221 .insert(*nonce.address(), nonce.account().clone());
222 }
223 RollbackAccounts::SeparateNonceAndFeePayer {
224 nonce,
225 fee_payer_account,
226 } => {
227 self.account_cache
228 .insert(*nonce.address(), nonce.account().clone());
229 self.account_cache
230 .insert(*fee_payer_address, fee_payer_account.clone());
231 }
232 }
233 }
234
235 fn update_accounts_for_successful_tx(
236 &mut self,
237 message: &impl SVMMessage,
238 transaction_accounts: &[TransactionAccount],
239 ) {
240 for (i, (address, account)) in (0..message.account_keys().len()).zip(transaction_accounts) {
241 if !message.is_writable(i) {
242 continue;
243 }
244
245 if message.is_invoked(i) && !message.is_instruction_account(i) {
250 continue;
251 }
252
253 self.account_cache.insert(*address, account.clone());
254 }
255 }
256}
257
258pub fn collect_rent_from_account(
262 feature_set: &FeatureSet,
263 rent_collector: &dyn SVMRentCollector,
264 address: &Pubkey,
265 account: &mut AccountSharedData,
266) -> CollectedInfo {
267 if !feature_set.is_active(&feature_set::disable_rent_fees_collection::id()) {
268 rent_collector.collect_rent(address, account)
269 } else {
270 if account.rent_epoch() != RENT_EXEMPT_RENT_EPOCH
275 && rent_collector.get_rent_due(
276 account.lamports(),
277 account.data().len(),
278 account.rent_epoch(),
279 ) == RentDue::Exempt
280 {
281 account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
282 }
283
284 CollectedInfo::default()
285 }
286}
287
288pub fn validate_fee_payer(
294 payer_address: &Pubkey,
295 payer_account: &mut AccountSharedData,
296 payer_index: IndexOfAccount,
297 error_metrics: &mut TransactionErrorMetrics,
298 rent_collector: &dyn SVMRentCollector,
299 fee: u64,
300) -> Result<()> {
301 if payer_account.lamports() == 0 {
302 error_metrics.account_not_found += 1;
303 return Err(TransactionError::AccountNotFound);
304 }
305 let system_account_kind = get_system_account_kind(payer_account).ok_or_else(|| {
306 error_metrics.invalid_account_for_fee += 1;
307 TransactionError::InvalidAccountForFee
308 })?;
309 let min_balance = match system_account_kind {
310 SystemAccountKind::System => 0,
311 SystemAccountKind::Nonce => {
312 rent_collector
315 .get_rent()
316 .minimum_balance(NonceState::size())
317 }
318 };
319
320 payer_account
321 .lamports()
322 .checked_sub(min_balance)
323 .and_then(|v| v.checked_sub(fee))
324 .ok_or_else(|| {
325 error_metrics.insufficient_funds += 1;
326 TransactionError::InsufficientFundsForFee
327 })?;
328
329 let payer_pre_rent_state = rent_collector.get_account_rent_state(payer_account);
330 payer_account
331 .checked_sub_lamports(fee)
332 .map_err(|_| TransactionError::InsufficientFundsForFee)?;
333
334 let payer_post_rent_state = rent_collector.get_account_rent_state(payer_account);
335 rent_collector.check_rent_state_with_account(
336 &payer_pre_rent_state,
337 &payer_post_rent_state,
338 payer_address,
339 payer_account,
340 payer_index,
341 )
342}
343
344pub(crate) fn load_transaction<CB: TransactionProcessingCallback>(
345 account_loader: &mut AccountLoader<CB>,
346 message: &impl SVMMessage,
347 validation_result: TransactionValidationResult,
348 error_metrics: &mut TransactionErrorMetrics,
349 rent_collector: &dyn SVMRentCollector,
350) -> TransactionLoadResult {
351 match validation_result {
352 Err(e) => TransactionLoadResult::NotLoaded(e),
353 Ok(tx_details) => {
354 let load_result = load_transaction_accounts(
355 account_loader,
356 message,
357 tx_details.loaded_fee_payer_account,
358 &tx_details.compute_budget_limits,
359 error_metrics,
360 rent_collector,
361 );
362
363 match load_result {
364 Ok(loaded_tx_accounts) => TransactionLoadResult::Loaded(LoadedTransaction {
365 accounts: loaded_tx_accounts.accounts,
366 program_indices: loaded_tx_accounts.program_indices,
367 fee_details: tx_details.fee_details,
368 rent: loaded_tx_accounts.rent,
369 rent_debits: loaded_tx_accounts.rent_debits,
370 rollback_accounts: tx_details.rollback_accounts,
371 compute_budget_limits: tx_details.compute_budget_limits,
372 loaded_accounts_data_size: loaded_tx_accounts.loaded_accounts_data_size,
373 }),
374 Err(err) => TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
375 load_error: err,
376 fee_details: tx_details.fee_details,
377 rollback_accounts: tx_details.rollback_accounts,
378 }),
379 }
380 }
381 }
382}
383
384#[derive(PartialEq, Eq, Debug, Clone)]
385struct LoadedTransactionAccounts {
386 pub(crate) accounts: Vec<TransactionAccount>,
387 pub(crate) program_indices: TransactionProgramIndices,
388 pub(crate) rent: TransactionRent,
389 pub(crate) rent_debits: RentDebits,
390 pub(crate) loaded_accounts_data_size: u32,
391}
392
393fn load_transaction_accounts<CB: TransactionProcessingCallback>(
394 account_loader: &mut AccountLoader<CB>,
395 message: &impl SVMMessage,
396 loaded_fee_payer_account: LoadedTransactionAccount,
397 compute_budget_limits: &ComputeBudgetLimits,
398 error_metrics: &mut TransactionErrorMetrics,
399 rent_collector: &dyn SVMRentCollector,
400) -> Result<LoadedTransactionAccounts> {
401 let mut tx_rent: TransactionRent = 0;
402 let account_keys = message.account_keys();
403 let mut accounts = Vec::with_capacity(account_keys.len());
404 let mut validated_loaders = AHashSet::with_capacity(PROGRAM_OWNERS.len());
405 let mut rent_debits = RentDebits::default();
406 let mut accumulated_accounts_data_size: Saturating<u32> = Saturating(0);
407
408 let mut collect_loaded_account = |key, loaded_account| -> Result<()> {
409 let LoadedTransactionAccount {
410 account,
411 loaded_size,
412 rent_collected,
413 } = loaded_account;
414
415 accumulate_and_check_loaded_account_data_size(
416 &mut accumulated_accounts_data_size,
417 loaded_size,
418 compute_budget_limits.loaded_accounts_bytes,
419 error_metrics,
420 )?;
421
422 tx_rent += rent_collected;
423 rent_debits.insert(key, rent_collected, account.lamports());
424
425 accounts.push((*key, account));
426 Ok(())
427 };
428
429 collect_loaded_account(message.fee_payer(), loaded_fee_payer_account)?;
432
433 for (account_index, account_key) in account_keys.iter().enumerate().skip(1) {
435 let loaded_account = load_transaction_account(
436 account_loader,
437 message,
438 account_key,
439 account_index,
440 rent_collector,
441 );
442 collect_loaded_account(account_key, loaded_account)?;
443 }
444
445 let program_indices = message
446 .program_instructions_iter()
447 .map(|(program_id, instruction)| {
448 let mut account_indices = Vec::with_capacity(2);
449 if native_loader::check_id(program_id) {
450 return Ok(account_indices);
451 }
452
453 let program_index = instruction.program_id_index as usize;
454
455 let Some(LoadedTransactionAccount {
456 account: program_account,
457 ..
458 }) = account_loader.load_account(program_id, false)
459 else {
460 error_metrics.account_not_found += 1;
461 return Err(TransactionError::ProgramAccountNotFound);
462 };
463
464 if !account_loader
465 .feature_set
466 .is_active(&feature_set::remove_accounts_executable_flag_checks::id())
467 && !program_account.executable()
468 {
469 error_metrics.invalid_program_for_execution += 1;
470 return Err(TransactionError::InvalidProgramForExecution);
471 }
472 account_indices.insert(0, program_index as IndexOfAccount);
473
474 let owner_id = program_account.owner();
475 if native_loader::check_id(owner_id) {
476 return Ok(account_indices);
477 }
478
479 if !validated_loaders.contains(owner_id) {
480 if let Some(LoadedTransactionAccount {
497 account: owner_account,
498 loaded_size: owner_size,
499 ..
500 }) = account_loader.load_account(owner_id, false)
501 {
502 if !native_loader::check_id(owner_account.owner())
503 || (!account_loader
504 .feature_set
505 .is_active(&feature_set::remove_accounts_executable_flag_checks::id())
506 && !owner_account.executable())
507 {
508 error_metrics.invalid_program_for_execution += 1;
509 return Err(TransactionError::InvalidProgramForExecution);
510 }
511 accumulate_and_check_loaded_account_data_size(
512 &mut accumulated_accounts_data_size,
513 owner_size,
514 compute_budget_limits.loaded_accounts_bytes,
515 error_metrics,
516 )?;
517 validated_loaders.insert(*owner_id);
518 } else {
519 error_metrics.account_not_found += 1;
520 return Err(TransactionError::ProgramAccountNotFound);
521 }
522 }
523 Ok(account_indices)
524 })
525 .collect::<Result<Vec<Vec<IndexOfAccount>>>>()?;
526
527 Ok(LoadedTransactionAccounts {
528 accounts,
529 program_indices,
530 rent: tx_rent,
531 rent_debits,
532 loaded_accounts_data_size: accumulated_accounts_data_size.0,
533 })
534}
535
536fn load_transaction_account<CB: TransactionProcessingCallback>(
537 account_loader: &mut AccountLoader<CB>,
538 message: &impl SVMMessage,
539 account_key: &Pubkey,
540 account_index: usize,
541 rent_collector: &dyn SVMRentCollector,
542) -> LoadedTransactionAccount {
543 let is_writable = message.is_writable(account_index);
544 let loaded_account = if solana_sdk_ids::sysvar::instructions::check_id(account_key) {
545 LoadedTransactionAccount {
548 loaded_size: 0,
549 account: construct_instructions_account(message),
550 rent_collected: 0,
551 }
552 } else if let Some(mut loaded_account) = account_loader.load_account(account_key, is_writable) {
553 loaded_account.rent_collected = if is_writable {
554 collect_rent_from_account(
555 &account_loader.feature_set,
556 rent_collector,
557 account_key,
558 &mut loaded_account.account,
559 )
560 .rent_amount
561 } else {
562 0
563 };
564
565 loaded_account
566 } else {
567 let mut default_account = AccountSharedData::default();
568 default_account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
572 LoadedTransactionAccount {
573 loaded_size: default_account.data().len(),
574 account: default_account,
575 rent_collected: 0,
576 }
577 };
578
579 loaded_account
580}
581
582fn accumulate_and_check_loaded_account_data_size(
587 accumulated_loaded_accounts_data_size: &mut Saturating<u32>,
588 account_data_size: usize,
589 requested_loaded_accounts_data_size_limit: NonZeroU32,
590 error_metrics: &mut TransactionErrorMetrics,
591) -> Result<()> {
592 let Ok(account_data_size) = u32::try_from(account_data_size) else {
593 error_metrics.max_loaded_accounts_data_size_exceeded += 1;
594 return Err(TransactionError::MaxLoadedAccountsDataSizeExceeded);
595 };
596 *accumulated_loaded_accounts_data_size += account_data_size;
597 if accumulated_loaded_accounts_data_size.0 > requested_loaded_accounts_data_size_limit.get() {
598 error_metrics.max_loaded_accounts_data_size_exceeded += 1;
599 Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
600 } else {
601 Ok(())
602 }
603}
604
605fn construct_instructions_account(message: &impl SVMMessage) -> AccountSharedData {
606 let account_keys = message.account_keys();
607 let mut decompiled_instructions = Vec::with_capacity(message.num_instructions());
608 for (program_id, instruction) in message.program_instructions_iter() {
609 let accounts = instruction
610 .accounts
611 .iter()
612 .map(|account_index| {
613 let account_index = usize::from(*account_index);
614 BorrowedAccountMeta {
615 is_signer: message.is_signer(account_index),
616 is_writable: message.is_writable(account_index),
617 pubkey: account_keys.get(account_index).unwrap(),
618 }
619 })
620 .collect();
621
622 decompiled_instructions.push(BorrowedInstruction {
623 accounts,
624 data: instruction.data,
625 program_id,
626 });
627 }
628
629 AccountSharedData::from(Account {
630 data: construct_instructions_data(&decompiled_instructions),
631 owner: sysvar::id(),
632 ..Account::default()
633 })
634}
635
636#[cfg(test)]
637mod tests {
638 use {
639 super::*,
640 crate::{
641 transaction_account_state_info::TransactionAccountStateInfo,
642 transaction_processing_callback::TransactionProcessingCallback,
643 },
644 solana_account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
645 solana_compute_budget::{compute_budget::ComputeBudget, compute_budget_limits},
646 solana_epoch_schedule::EpochSchedule,
647 solana_feature_set::FeatureSet,
648 solana_hash::Hash,
649 solana_instruction::{AccountMeta, Instruction},
650 solana_keypair::Keypair,
651 solana_message::{
652 compiled_instruction::CompiledInstruction,
653 v0::{LoadedAddresses, LoadedMessage},
654 LegacyMessage, Message, MessageHeader, SanitizedMessage,
655 },
656 solana_native_token::{sol_to_lamports, LAMPORTS_PER_SOL},
657 solana_nonce::{self as nonce, versions::Versions as NonceVersions},
658 solana_program::bpf_loader_upgradeable::UpgradeableLoaderState,
659 solana_pubkey::Pubkey,
660 solana_rent::Rent,
661 solana_rent_debits::RentDebits,
662 solana_reserved_account_keys::ReservedAccountKeys,
663 solana_sdk::rent_collector::{RentCollector, RENT_EXEMPT_RENT_EPOCH},
664 solana_sdk_ids::{
665 bpf_loader, bpf_loader_upgradeable, native_loader, system_program, sysvar,
666 },
667 solana_signature::Signature,
668 solana_signer::Signer,
669 solana_system_transaction::transfer,
670 solana_transaction::{sanitized::SanitizedTransaction, Transaction},
671 solana_transaction_context::{TransactionAccount, TransactionContext},
672 solana_transaction_error::{TransactionError, TransactionResult as Result},
673 std::{borrow::Cow, cell::RefCell, collections::HashMap, fs::File, io::Read, sync::Arc},
674 };
675
676 #[derive(Clone, Default)]
677 struct TestCallbacks {
678 accounts_map: HashMap<Pubkey, AccountSharedData>,
679 #[allow(clippy::type_complexity)]
680 inspected_accounts:
681 RefCell<HashMap<Pubkey, Vec<(Option<AccountSharedData>, bool)>>>,
682 }
683
684 impl TransactionProcessingCallback for TestCallbacks {
685 fn account_matches_owners(&self, _account: &Pubkey, _owners: &[Pubkey]) -> Option<usize> {
686 None
687 }
688
689 fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
690 self.accounts_map.get(pubkey).cloned()
691 }
692
693 fn inspect_account(
694 &self,
695 address: &Pubkey,
696 account_state: AccountState,
697 is_writable: bool,
698 ) {
699 let account = match account_state {
700 AccountState::Dead => None,
701 AccountState::Alive(account) => Some(account.clone()),
702 };
703 self.inspected_accounts
704 .borrow_mut()
705 .entry(*address)
706 .or_default()
707 .push((account, is_writable));
708 }
709 }
710
711 impl<'a> From<&'a TestCallbacks> for AccountLoader<'a, TestCallbacks> {
712 fn from(callbacks: &'a TestCallbacks) -> AccountLoader<'a, TestCallbacks> {
713 AccountLoader::new_with_account_cache_capacity(
714 None,
715 callbacks,
716 Arc::<FeatureSet>::default(),
717 0,
718 )
719 }
720 }
721
722 fn load_accounts_with_features_and_rent(
723 tx: Transaction,
724 accounts: &[TransactionAccount],
725 rent_collector: &RentCollector,
726 error_metrics: &mut TransactionErrorMetrics,
727 feature_set: &mut FeatureSet,
728 ) -> TransactionLoadResult {
729 feature_set.deactivate(&feature_set::disable_rent_fees_collection::id());
730 let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
731 let fee_payer_account = accounts[0].1.clone();
732 let mut accounts_map = HashMap::new();
733 for (pubkey, account) in accounts {
734 accounts_map.insert(*pubkey, account.clone());
735 }
736 let callbacks = TestCallbacks {
737 accounts_map,
738 ..Default::default()
739 };
740 let mut account_loader: AccountLoader<TestCallbacks> = (&callbacks).into();
741 account_loader.feature_set = Arc::new(feature_set.clone());
742 load_transaction(
743 &mut account_loader,
744 &sanitized_tx,
745 Ok(ValidatedTransactionDetails {
746 loaded_fee_payer_account: LoadedTransactionAccount {
747 account: fee_payer_account,
748 ..LoadedTransactionAccount::default()
749 },
750 ..ValidatedTransactionDetails::default()
751 }),
752 error_metrics,
753 rent_collector,
754 )
755 }
756
757 fn all_features_except(exclude: Option<&[Pubkey]>) -> FeatureSet {
760 let mut features = FeatureSet::all_enabled();
761 if let Some(exclude) = exclude {
762 features.active.retain(|k, _v| !exclude.contains(k));
763 }
764 features
765 }
766
767 fn new_unchecked_sanitized_message(message: Message) -> SanitizedMessage {
768 SanitizedMessage::Legacy(LegacyMessage::new(
769 message,
770 &ReservedAccountKeys::empty_key_set(),
771 ))
772 }
773
774 fn load_accounts_aux_test(
775 tx: Transaction,
776 accounts: &[TransactionAccount],
777 error_metrics: &mut TransactionErrorMetrics,
778 ) -> TransactionLoadResult {
779 load_accounts_with_features_and_rent(
780 tx,
781 accounts,
782 &RentCollector::default(),
783 error_metrics,
784 &mut FeatureSet::all_enabled(),
785 )
786 }
787
788 fn load_accounts_with_excluded_features(
789 tx: Transaction,
790 accounts: &[TransactionAccount],
791 error_metrics: &mut TransactionErrorMetrics,
792 exclude_features: Option<&[Pubkey]>,
793 ) -> TransactionLoadResult {
794 load_accounts_with_features_and_rent(
795 tx,
796 accounts,
797 &RentCollector::default(),
798 error_metrics,
799 &mut all_features_except(exclude_features),
800 )
801 }
802
803 #[test]
804 fn test_load_accounts_unknown_program_id() {
805 let mut accounts: Vec<TransactionAccount> = Vec::new();
806 let mut error_metrics = TransactionErrorMetrics::default();
807
808 let keypair = Keypair::new();
809 let key0 = keypair.pubkey();
810 let key1 = Pubkey::from([5u8; 32]);
811
812 let account = AccountSharedData::new(1, 0, &Pubkey::default());
813 accounts.push((key0, account));
814
815 let account = AccountSharedData::new(2, 1, &Pubkey::default());
816 accounts.push((key1, account));
817
818 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
819 let tx = Transaction::new_with_compiled_instructions(
820 &[&keypair],
821 &[],
822 Hash::default(),
823 vec![Pubkey::default()],
824 instructions,
825 );
826
827 let load_results = load_accounts_aux_test(tx, &accounts, &mut error_metrics);
828
829 assert_eq!(error_metrics.account_not_found.0, 1);
830 assert!(matches!(
831 load_results,
832 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
833 load_error: TransactionError::ProgramAccountNotFound,
834 ..
835 }),
836 ));
837 }
838
839 #[test]
840 fn test_load_accounts_no_loaders() {
841 let mut accounts: Vec<TransactionAccount> = Vec::new();
842 let mut error_metrics = TransactionErrorMetrics::default();
843
844 let keypair = Keypair::new();
845 let key0 = keypair.pubkey();
846 let key1 = Pubkey::from([5u8; 32]);
847
848 let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
849 account.set_rent_epoch(1);
850 accounts.push((key0, account));
851
852 let mut account = AccountSharedData::new(2, 1, &Pubkey::default());
853 account.set_rent_epoch(1);
854 accounts.push((key1, account));
855
856 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
857 let tx = Transaction::new_with_compiled_instructions(
858 &[&keypair],
859 &[key1],
860 Hash::default(),
861 vec![native_loader::id()],
862 instructions,
863 );
864
865 let loaded_accounts =
866 load_accounts_with_excluded_features(tx, &accounts, &mut error_metrics, None);
867
868 assert_eq!(error_metrics.account_not_found.0, 0);
869 match &loaded_accounts {
870 TransactionLoadResult::Loaded(loaded_transaction) => {
871 assert_eq!(loaded_transaction.accounts.len(), 3);
872 assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
873 assert_eq!(loaded_transaction.program_indices.len(), 1);
874 assert_eq!(loaded_transaction.program_indices[0].len(), 0);
875 }
876 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
877 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
878 }
879 }
880
881 #[test]
882 fn test_load_accounts_bad_owner() {
883 let mut accounts: Vec<TransactionAccount> = Vec::new();
884 let mut error_metrics = TransactionErrorMetrics::default();
885
886 let keypair = Keypair::new();
887 let key0 = keypair.pubkey();
888 let key1 = Pubkey::from([5u8; 32]);
889
890 let account = AccountSharedData::new(1, 0, &Pubkey::default());
891 accounts.push((key0, account));
892
893 let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
894 account.set_owner(bpf_loader_upgradeable::id());
895 account.set_executable(true);
896 accounts.push((key1, account));
897
898 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
899 let tx = Transaction::new_with_compiled_instructions(
900 &[&keypair],
901 &[],
902 Hash::default(),
903 vec![key1],
904 instructions,
905 );
906
907 let load_results = load_accounts_aux_test(tx, &accounts, &mut error_metrics);
908
909 assert_eq!(error_metrics.account_not_found.0, 1);
910 assert!(matches!(
911 load_results,
912 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
913 load_error: TransactionError::ProgramAccountNotFound,
914 ..
915 }),
916 ));
917 }
918
919 #[test]
920 fn test_load_accounts_not_executable() {
921 let mut accounts: Vec<TransactionAccount> = Vec::new();
922 let mut error_metrics = TransactionErrorMetrics::default();
923
924 let keypair = Keypair::new();
925 let key0 = keypair.pubkey();
926 let key1 = Pubkey::from([5u8; 32]);
927
928 let account = AccountSharedData::new(1, 0, &Pubkey::default());
929 accounts.push((key0, account));
930
931 let account = AccountSharedData::new(40, 0, &native_loader::id());
932 accounts.push((key1, account));
933
934 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
935 let tx = Transaction::new_with_compiled_instructions(
936 &[&keypair],
937 &[],
938 Hash::default(),
939 vec![key1],
940 instructions,
941 );
942
943 let mut feature_set = FeatureSet::all_enabled();
944 feature_set.deactivate(&feature_set::remove_accounts_executable_flag_checks::id());
945 let load_results = load_accounts_with_features_and_rent(
946 tx,
947 &accounts,
948 &RentCollector::default(),
949 &mut error_metrics,
950 &mut feature_set,
951 );
952
953 assert_eq!(error_metrics.invalid_program_for_execution.0, 1);
954 assert!(matches!(
955 load_results,
956 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
957 load_error: TransactionError::InvalidProgramForExecution,
958 ..
959 }),
960 ));
961 }
962
963 #[test]
964 fn test_load_accounts_multiple_loaders() {
965 let mut accounts: Vec<TransactionAccount> = Vec::new();
966 let mut error_metrics = TransactionErrorMetrics::default();
967
968 let keypair = Keypair::new();
969 let key0 = keypair.pubkey();
970 let key1 = bpf_loader_upgradeable::id();
971 let key2 = Pubkey::from([6u8; 32]);
972
973 let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
974 account.set_rent_epoch(1);
975 accounts.push((key0, account));
976
977 let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
978 account.set_executable(true);
979 account.set_rent_epoch(1);
980 account.set_owner(native_loader::id());
981 accounts.push((key1, account));
982
983 let mut account = AccountSharedData::new(41, 1, &Pubkey::default());
984 account.set_executable(true);
985 account.set_rent_epoch(1);
986 account.set_owner(key1);
987 accounts.push((key2, account));
988
989 let instructions = vec![
990 CompiledInstruction::new(1, &(), vec![0]),
991 CompiledInstruction::new(2, &(), vec![0]),
992 ];
993 let tx = Transaction::new_with_compiled_instructions(
994 &[&keypair],
995 &[],
996 Hash::default(),
997 vec![key1, key2],
998 instructions,
999 );
1000
1001 let loaded_accounts =
1002 load_accounts_with_excluded_features(tx, &accounts, &mut error_metrics, None);
1003
1004 assert_eq!(error_metrics.account_not_found.0, 0);
1005 match &loaded_accounts {
1006 TransactionLoadResult::Loaded(loaded_transaction) => {
1007 assert_eq!(loaded_transaction.accounts.len(), 3);
1008 assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
1009 assert_eq!(loaded_transaction.program_indices.len(), 2);
1010 assert_eq!(loaded_transaction.program_indices[0], &[1]);
1011 assert_eq!(loaded_transaction.program_indices[1], &[2]);
1012 }
1013 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1014 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1015 }
1016 }
1017
1018 fn load_accounts_no_store(
1019 accounts: &[TransactionAccount],
1020 tx: Transaction,
1021 account_overrides: Option<&AccountOverrides>,
1022 ) -> TransactionLoadResult {
1023 let tx = SanitizedTransaction::from_transaction_for_tests(tx);
1024
1025 let mut error_metrics = TransactionErrorMetrics::default();
1026 let mut accounts_map = HashMap::new();
1027 for (pubkey, account) in accounts {
1028 accounts_map.insert(*pubkey, account.clone());
1029 }
1030 let callbacks = TestCallbacks {
1031 accounts_map,
1032 ..Default::default()
1033 };
1034 let mut account_loader = AccountLoader::new_with_account_cache_capacity(
1035 account_overrides,
1036 &callbacks,
1037 Arc::new(FeatureSet::all_enabled()),
1038 0,
1039 );
1040 load_transaction(
1041 &mut account_loader,
1042 &tx,
1043 Ok(ValidatedTransactionDetails::default()),
1044 &mut error_metrics,
1045 &RentCollector::default(),
1046 )
1047 }
1048
1049 #[test]
1050 fn test_instructions() {
1051 solana_logger::setup();
1052 let instructions_key = solana_sdk_ids::sysvar::instructions::id();
1053 let keypair = Keypair::new();
1054 let instructions = vec![CompiledInstruction::new(1, &(), vec![0, 1])];
1055 let tx = Transaction::new_with_compiled_instructions(
1056 &[&keypair],
1057 &[solana_pubkey::new_rand(), instructions_key],
1058 Hash::default(),
1059 vec![native_loader::id()],
1060 instructions,
1061 );
1062
1063 let load_results = load_accounts_no_store(&[], tx, None);
1064 assert!(matches!(
1065 load_results,
1066 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
1067 load_error: TransactionError::ProgramAccountNotFound,
1068 ..
1069 }),
1070 ));
1071 }
1072
1073 #[test]
1074 fn test_overrides() {
1075 solana_logger::setup();
1076 let mut account_overrides = AccountOverrides::default();
1077 let slot_history_id = sysvar::slot_history::id();
1078 let account = AccountSharedData::new(42, 0, &Pubkey::default());
1079 account_overrides.set_slot_history(Some(account));
1080
1081 let keypair = Keypair::new();
1082 let account = AccountSharedData::new(1_000_000, 0, &Pubkey::default());
1083
1084 let instructions = vec![CompiledInstruction::new(2, &(), vec![0])];
1085 let tx = Transaction::new_with_compiled_instructions(
1086 &[&keypair],
1087 &[slot_history_id],
1088 Hash::default(),
1089 vec![native_loader::id()],
1090 instructions,
1091 );
1092
1093 let loaded_accounts =
1094 load_accounts_no_store(&[(keypair.pubkey(), account)], tx, Some(&account_overrides));
1095 match &loaded_accounts {
1096 TransactionLoadResult::Loaded(loaded_transaction) => {
1097 assert_eq!(loaded_transaction.accounts[0].0, keypair.pubkey());
1098 assert_eq!(loaded_transaction.accounts[1].0, slot_history_id);
1099 assert_eq!(loaded_transaction.accounts[1].1.lamports(), 42);
1100 }
1101 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1102 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1103 }
1104 }
1105
1106 #[test]
1107 fn test_accumulate_and_check_loaded_account_data_size() {
1108 let mut error_metrics = TransactionErrorMetrics::default();
1109 let mut accumulated_data_size: Saturating<u32> = Saturating(0);
1110 let data_size: usize = 123;
1111 let requested_data_size_limit = NonZeroU32::new(data_size as u32).unwrap();
1112
1113 assert!(accumulate_and_check_loaded_account_data_size(
1115 &mut accumulated_data_size,
1116 data_size,
1117 requested_data_size_limit,
1118 &mut error_metrics
1119 )
1120 .is_ok());
1121 assert_eq!(data_size as u32, accumulated_data_size.0);
1122
1123 let another_byte: usize = 1;
1125 assert_eq!(
1126 accumulate_and_check_loaded_account_data_size(
1127 &mut accumulated_data_size,
1128 another_byte,
1129 requested_data_size_limit,
1130 &mut error_metrics
1131 ),
1132 Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
1133 );
1134 }
1135
1136 struct ValidateFeePayerTestParameter {
1137 is_nonce: bool,
1138 payer_init_balance: u64,
1139 fee: u64,
1140 expected_result: Result<()>,
1141 payer_post_balance: u64,
1142 }
1143 fn validate_fee_payer_account(
1144 test_parameter: ValidateFeePayerTestParameter,
1145 rent_collector: &RentCollector,
1146 ) {
1147 let payer_account_keys = Keypair::new();
1148 let mut account = if test_parameter.is_nonce {
1149 AccountSharedData::new_data(
1150 test_parameter.payer_init_balance,
1151 &NonceVersions::new(NonceState::Initialized(nonce::state::Data::default())),
1152 &system_program::id(),
1153 )
1154 .unwrap()
1155 } else {
1156 AccountSharedData::new(test_parameter.payer_init_balance, 0, &system_program::id())
1157 };
1158 let result = validate_fee_payer(
1159 &payer_account_keys.pubkey(),
1160 &mut account,
1161 0,
1162 &mut TransactionErrorMetrics::default(),
1163 rent_collector,
1164 test_parameter.fee,
1165 );
1166
1167 assert_eq!(result, test_parameter.expected_result);
1168 assert_eq!(account.lamports(), test_parameter.payer_post_balance);
1169 }
1170
1171 #[test]
1172 fn test_validate_fee_payer() {
1173 let rent_collector = RentCollector::new(
1174 0,
1175 EpochSchedule::default(),
1176 500_000.0,
1177 Rent {
1178 lamports_per_byte_year: 1,
1179 ..Rent::default()
1180 },
1181 );
1182 let min_balance = rent_collector.rent.minimum_balance(NonceState::size());
1183 let fee = 5_000;
1184
1185 {
1188 for (is_nonce, min_balance) in [(true, min_balance), (false, 0)] {
1189 validate_fee_payer_account(
1190 ValidateFeePayerTestParameter {
1191 is_nonce,
1192 payer_init_balance: min_balance + fee,
1193 fee,
1194 expected_result: Ok(()),
1195 payer_post_balance: min_balance,
1196 },
1197 &rent_collector,
1198 );
1199 }
1200 }
1201
1202 {
1205 for is_nonce in [true, false] {
1206 validate_fee_payer_account(
1207 ValidateFeePayerTestParameter {
1208 is_nonce,
1209 payer_init_balance: 0,
1210 fee,
1211 expected_result: Err(TransactionError::AccountNotFound),
1212 payer_post_balance: 0,
1213 },
1214 &rent_collector,
1215 );
1216 }
1217 }
1218
1219 {
1222 for (is_nonce, min_balance) in [(true, min_balance), (false, 0)] {
1223 validate_fee_payer_account(
1224 ValidateFeePayerTestParameter {
1225 is_nonce,
1226 payer_init_balance: min_balance + fee - 1,
1227 fee,
1228 expected_result: Err(TransactionError::InsufficientFundsForFee),
1229 payer_post_balance: min_balance + fee - 1,
1230 },
1231 &rent_collector,
1232 );
1233 }
1234 }
1235
1236 {
1239 validate_fee_payer_account(
1240 ValidateFeePayerTestParameter {
1241 is_nonce: false,
1242 payer_init_balance: u64::MAX,
1243 fee: u64::MAX,
1244 expected_result: Ok(()),
1245 payer_post_balance: 0,
1246 },
1247 &rent_collector,
1248 );
1249 }
1250 }
1251
1252 #[test]
1253 fn test_validate_nonce_fee_payer_with_checked_arithmetic() {
1254 let rent_collector = RentCollector::new(
1255 0,
1256 EpochSchedule::default(),
1257 500_000.0,
1258 Rent {
1259 lamports_per_byte_year: 1,
1260 ..Rent::default()
1261 },
1262 );
1263
1264 validate_fee_payer_account(
1268 ValidateFeePayerTestParameter {
1269 is_nonce: true,
1270 payer_init_balance: u64::MAX,
1271 fee: u64::MAX,
1272 expected_result: Err(TransactionError::InsufficientFundsForFee),
1273 payer_post_balance: u64::MAX,
1274 },
1275 &rent_collector,
1276 );
1277 }
1278
1279 #[test]
1280 fn test_construct_instructions_account() {
1281 let loaded_message = LoadedMessage {
1282 message: Cow::Owned(solana_message::v0::Message::default()),
1283 loaded_addresses: Cow::Owned(LoadedAddresses::default()),
1284 is_writable_account_cache: vec![false],
1285 };
1286 let message = SanitizedMessage::V0(loaded_message);
1287 let shared_data = construct_instructions_account(&message);
1288 let expected = AccountSharedData::from(Account {
1289 data: construct_instructions_data(&message.decompile_instructions()),
1290 owner: sysvar::id(),
1291 ..Account::default()
1292 });
1293 assert_eq!(shared_data, expected);
1294 }
1295
1296 #[test]
1297 fn test_load_transaction_accounts_fee_payer() {
1298 let fee_payer_address = Pubkey::new_unique();
1299 let message = Message {
1300 account_keys: vec![fee_payer_address],
1301 header: MessageHeader::default(),
1302 instructions: vec![],
1303 recent_blockhash: Hash::default(),
1304 };
1305
1306 let sanitized_message = new_unchecked_sanitized_message(message);
1307 let mut mock_bank = TestCallbacks::default();
1308
1309 let fee_payer_balance = 200;
1310 let mut fee_payer_account = AccountSharedData::default();
1311 fee_payer_account.set_lamports(fee_payer_balance);
1312 mock_bank
1313 .accounts_map
1314 .insert(fee_payer_address, fee_payer_account.clone());
1315 let mut account_loader = (&mock_bank).into();
1316 let fee_payer_rent_debit = 42;
1317
1318 let mut error_metrics = TransactionErrorMetrics::default();
1319
1320 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1321 sanitized_message,
1322 vec![Signature::new_unique()],
1323 false,
1324 );
1325 let result = load_transaction_accounts(
1326 &mut account_loader,
1327 sanitized_transaction.message(),
1328 LoadedTransactionAccount {
1329 loaded_size: fee_payer_account.data().len(),
1330 account: fee_payer_account.clone(),
1331 rent_collected: fee_payer_rent_debit,
1332 },
1333 &ComputeBudgetLimits::default(),
1334 &mut error_metrics,
1335 &RentCollector::default(),
1336 );
1337
1338 let expected_rent_debits = {
1339 let mut rent_debits = RentDebits::default();
1340 rent_debits.insert(&fee_payer_address, fee_payer_rent_debit, fee_payer_balance);
1341 rent_debits
1342 };
1343 assert_eq!(
1344 result.unwrap(),
1345 LoadedTransactionAccounts {
1346 accounts: vec![(fee_payer_address, fee_payer_account)],
1347 program_indices: vec![],
1348 rent: fee_payer_rent_debit,
1349 rent_debits: expected_rent_debits,
1350 loaded_accounts_data_size: 0,
1351 }
1352 );
1353 }
1354
1355 #[test]
1356 fn test_load_transaction_accounts_native_loader() {
1357 let key1 = Keypair::new();
1358 let message = Message {
1359 account_keys: vec![key1.pubkey(), native_loader::id()],
1360 header: MessageHeader::default(),
1361 instructions: vec![CompiledInstruction {
1362 program_id_index: 1,
1363 accounts: vec![0],
1364 data: vec![],
1365 }],
1366 recent_blockhash: Hash::default(),
1367 };
1368
1369 let sanitized_message = new_unchecked_sanitized_message(message);
1370 let mut mock_bank = TestCallbacks::default();
1371 mock_bank
1372 .accounts_map
1373 .insert(native_loader::id(), AccountSharedData::default());
1374 let mut fee_payer_account = AccountSharedData::default();
1375 fee_payer_account.set_lamports(200);
1376 mock_bank
1377 .accounts_map
1378 .insert(key1.pubkey(), fee_payer_account.clone());
1379 let mut account_loader = (&mock_bank).into();
1380
1381 let mut error_metrics = TransactionErrorMetrics::default();
1382
1383 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1384 sanitized_message,
1385 vec![Signature::new_unique()],
1386 false,
1387 );
1388 let result = load_transaction_accounts(
1389 &mut account_loader,
1390 sanitized_transaction.message(),
1391 LoadedTransactionAccount {
1392 account: fee_payer_account.clone(),
1393 ..LoadedTransactionAccount::default()
1394 },
1395 &ComputeBudgetLimits::default(),
1396 &mut error_metrics,
1397 &RentCollector::default(),
1398 );
1399
1400 assert_eq!(
1401 result.unwrap(),
1402 LoadedTransactionAccounts {
1403 accounts: vec![
1404 (key1.pubkey(), fee_payer_account),
1405 (
1406 native_loader::id(),
1407 mock_bank.accounts_map[&native_loader::id()].clone()
1408 )
1409 ],
1410 program_indices: vec![vec![]],
1411 rent: 0,
1412 rent_debits: RentDebits::default(),
1413 loaded_accounts_data_size: 0,
1414 }
1415 );
1416 }
1417
1418 #[test]
1419 fn test_load_transaction_accounts_program_account_no_data() {
1420 let key1 = Keypair::new();
1421 let key2 = Keypair::new();
1422
1423 let message = Message {
1424 account_keys: vec![key1.pubkey(), key2.pubkey()],
1425 header: MessageHeader::default(),
1426 instructions: vec![CompiledInstruction {
1427 program_id_index: 1,
1428 accounts: vec![0, 1],
1429 data: vec![],
1430 }],
1431 recent_blockhash: Hash::default(),
1432 };
1433
1434 let sanitized_message = new_unchecked_sanitized_message(message);
1435 let mut mock_bank = TestCallbacks::default();
1436 let mut account_data = AccountSharedData::default();
1437 account_data.set_lamports(200);
1438 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1439 let mut account_loader = (&mock_bank).into();
1440
1441 let mut error_metrics = TransactionErrorMetrics::default();
1442
1443 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1444 sanitized_message,
1445 vec![Signature::new_unique()],
1446 false,
1447 );
1448 let result = load_transaction_accounts(
1449 &mut account_loader,
1450 sanitized_transaction.message(),
1451 LoadedTransactionAccount::default(),
1452 &ComputeBudgetLimits::default(),
1453 &mut error_metrics,
1454 &RentCollector::default(),
1455 );
1456
1457 assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1458 }
1459
1460 #[test]
1461 fn test_load_transaction_accounts_invalid_program_for_execution() {
1462 let key1 = Keypair::new();
1463 let key2 = Keypair::new();
1464
1465 let message = Message {
1466 account_keys: vec![key1.pubkey(), key2.pubkey()],
1467 header: MessageHeader::default(),
1468 instructions: vec![CompiledInstruction {
1469 program_id_index: 0,
1470 accounts: vec![0, 1],
1471 data: vec![],
1472 }],
1473 recent_blockhash: Hash::default(),
1474 };
1475
1476 let sanitized_message = new_unchecked_sanitized_message(message);
1477 let mut mock_bank = TestCallbacks::default();
1478 let mut account_data = AccountSharedData::default();
1479 account_data.set_lamports(200);
1480 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1481 let mut account_loader = (&mock_bank).into();
1482
1483 let mut error_metrics = TransactionErrorMetrics::default();
1484
1485 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1486 sanitized_message,
1487 vec![Signature::new_unique()],
1488 false,
1489 );
1490 let result = load_transaction_accounts(
1491 &mut account_loader,
1492 sanitized_transaction.message(),
1493 LoadedTransactionAccount::default(),
1494 &ComputeBudgetLimits::default(),
1495 &mut error_metrics,
1496 &RentCollector::default(),
1497 );
1498
1499 assert_eq!(
1500 result.err(),
1501 Some(TransactionError::InvalidProgramForExecution)
1502 );
1503 }
1504
1505 #[test]
1506 fn test_load_transaction_accounts_native_loader_owner() {
1507 let key1 = Keypair::new();
1508 let key2 = Keypair::new();
1509
1510 let message = Message {
1511 account_keys: vec![key2.pubkey(), key1.pubkey()],
1512 header: MessageHeader::default(),
1513 instructions: vec![CompiledInstruction {
1514 program_id_index: 1,
1515 accounts: vec![0],
1516 data: vec![],
1517 }],
1518 recent_blockhash: Hash::default(),
1519 };
1520
1521 let sanitized_message = new_unchecked_sanitized_message(message);
1522 let mut mock_bank = TestCallbacks::default();
1523 let mut account_data = AccountSharedData::default();
1524 account_data.set_owner(native_loader::id());
1525 account_data.set_lamports(1);
1526 account_data.set_executable(true);
1527 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1528
1529 let mut fee_payer_account = AccountSharedData::default();
1530 fee_payer_account.set_lamports(200);
1531 mock_bank
1532 .accounts_map
1533 .insert(key2.pubkey(), fee_payer_account.clone());
1534 let mut account_loader = (&mock_bank).into();
1535
1536 let mut error_metrics = TransactionErrorMetrics::default();
1537
1538 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1539 sanitized_message,
1540 vec![Signature::new_unique()],
1541 false,
1542 );
1543 let result = load_transaction_accounts(
1544 &mut account_loader,
1545 sanitized_transaction.message(),
1546 LoadedTransactionAccount {
1547 account: fee_payer_account.clone(),
1548 ..LoadedTransactionAccount::default()
1549 },
1550 &ComputeBudgetLimits::default(),
1551 &mut error_metrics,
1552 &RentCollector::default(),
1553 );
1554
1555 assert_eq!(
1556 result.unwrap(),
1557 LoadedTransactionAccounts {
1558 accounts: vec![
1559 (key2.pubkey(), fee_payer_account),
1560 (
1561 key1.pubkey(),
1562 mock_bank.accounts_map[&key1.pubkey()].clone()
1563 ),
1564 ],
1565 program_indices: vec![vec![1]],
1566 rent: 0,
1567 rent_debits: RentDebits::default(),
1568 loaded_accounts_data_size: 0,
1569 }
1570 );
1571 }
1572
1573 #[test]
1574 fn test_load_transaction_accounts_program_account_not_found_after_all_checks() {
1575 let key1 = Keypair::new();
1576 let key2 = Keypair::new();
1577
1578 let message = Message {
1579 account_keys: vec![key2.pubkey(), key1.pubkey()],
1580 header: MessageHeader::default(),
1581 instructions: vec![CompiledInstruction {
1582 program_id_index: 1,
1583 accounts: vec![0],
1584 data: vec![],
1585 }],
1586 recent_blockhash: Hash::default(),
1587 };
1588
1589 let sanitized_message = new_unchecked_sanitized_message(message);
1590 let mut mock_bank = TestCallbacks::default();
1591 let mut account_data = AccountSharedData::default();
1592 account_data.set_executable(true);
1593 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1594
1595 let mut account_data = AccountSharedData::default();
1596 account_data.set_lamports(200);
1597 mock_bank.accounts_map.insert(key2.pubkey(), account_data);
1598 let mut account_loader = (&mock_bank).into();
1599
1600 let mut error_metrics = TransactionErrorMetrics::default();
1601
1602 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1603 sanitized_message,
1604 vec![Signature::new_unique()],
1605 false,
1606 );
1607 let result = load_transaction_accounts(
1608 &mut account_loader,
1609 sanitized_transaction.message(),
1610 LoadedTransactionAccount::default(),
1611 &ComputeBudgetLimits::default(),
1612 &mut error_metrics,
1613 &RentCollector::default(),
1614 );
1615
1616 assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1617 }
1618
1619 #[test]
1620 fn test_load_transaction_accounts_program_account_invalid_program_for_execution_last_check() {
1621 let key1 = Keypair::new();
1622 let key2 = Keypair::new();
1623 let key3 = Keypair::new();
1624
1625 let message = Message {
1626 account_keys: vec![key2.pubkey(), key1.pubkey()],
1627 header: MessageHeader::default(),
1628 instructions: vec![CompiledInstruction {
1629 program_id_index: 1,
1630 accounts: vec![0],
1631 data: vec![],
1632 }],
1633 recent_blockhash: Hash::default(),
1634 };
1635
1636 let sanitized_message = new_unchecked_sanitized_message(message);
1637 let mut mock_bank = TestCallbacks::default();
1638 let mut account_data = AccountSharedData::default();
1639 account_data.set_lamports(1);
1640 account_data.set_executable(true);
1641 account_data.set_owner(key3.pubkey());
1642 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1643
1644 let mut account_data = AccountSharedData::default();
1645 account_data.set_lamports(200);
1646 mock_bank.accounts_map.insert(key2.pubkey(), account_data);
1647 mock_bank
1648 .accounts_map
1649 .insert(key3.pubkey(), AccountSharedData::default());
1650 let mut account_loader = (&mock_bank).into();
1651
1652 let mut error_metrics = TransactionErrorMetrics::default();
1653
1654 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1655 sanitized_message,
1656 vec![Signature::new_unique()],
1657 false,
1658 );
1659 let result = load_transaction_accounts(
1660 &mut account_loader,
1661 sanitized_transaction.message(),
1662 LoadedTransactionAccount::default(),
1663 &ComputeBudgetLimits::default(),
1664 &mut error_metrics,
1665 &RentCollector::default(),
1666 );
1667
1668 assert_eq!(
1669 result.err(),
1670 Some(TransactionError::InvalidProgramForExecution)
1671 );
1672 }
1673
1674 #[test]
1675 fn test_load_transaction_accounts_program_success_complete() {
1676 let key1 = Keypair::new();
1677 let key2 = Keypair::new();
1678 let key3 = Keypair::new();
1679
1680 let message = Message {
1681 account_keys: vec![key2.pubkey(), key1.pubkey()],
1682 header: MessageHeader::default(),
1683 instructions: vec![CompiledInstruction {
1684 program_id_index: 1,
1685 accounts: vec![0],
1686 data: vec![],
1687 }],
1688 recent_blockhash: Hash::default(),
1689 };
1690
1691 let sanitized_message = new_unchecked_sanitized_message(message);
1692 let mut mock_bank = TestCallbacks::default();
1693 let mut account_data = AccountSharedData::default();
1694 account_data.set_lamports(1);
1695 account_data.set_executable(true);
1696 account_data.set_owner(key3.pubkey());
1697 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1698
1699 let mut fee_payer_account = AccountSharedData::default();
1700 fee_payer_account.set_lamports(200);
1701 mock_bank
1702 .accounts_map
1703 .insert(key2.pubkey(), fee_payer_account.clone());
1704
1705 let mut account_data = AccountSharedData::default();
1706 account_data.set_lamports(1);
1707 account_data.set_executable(true);
1708 account_data.set_owner(native_loader::id());
1709 mock_bank.accounts_map.insert(key3.pubkey(), account_data);
1710 let mut account_loader = (&mock_bank).into();
1711
1712 let mut error_metrics = TransactionErrorMetrics::default();
1713
1714 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1715 sanitized_message,
1716 vec![Signature::new_unique()],
1717 false,
1718 );
1719 let result = load_transaction_accounts(
1720 &mut account_loader,
1721 sanitized_transaction.message(),
1722 LoadedTransactionAccount {
1723 account: fee_payer_account.clone(),
1724 ..LoadedTransactionAccount::default()
1725 },
1726 &ComputeBudgetLimits::default(),
1727 &mut error_metrics,
1728 &RentCollector::default(),
1729 );
1730
1731 assert_eq!(
1732 result.unwrap(),
1733 LoadedTransactionAccounts {
1734 accounts: vec![
1735 (key2.pubkey(), fee_payer_account),
1736 (
1737 key1.pubkey(),
1738 mock_bank.accounts_map[&key1.pubkey()].clone()
1739 ),
1740 ],
1741 program_indices: vec![vec![1]],
1742 rent: 0,
1743 rent_debits: RentDebits::default(),
1744 loaded_accounts_data_size: 0,
1745 }
1746 );
1747 }
1748
1749 #[test]
1750 fn test_load_transaction_accounts_program_builtin_saturating_add() {
1751 let key1 = Keypair::new();
1752 let key2 = Keypair::new();
1753 let key3 = Keypair::new();
1754 let key4 = Keypair::new();
1755
1756 let message = Message {
1757 account_keys: vec![key2.pubkey(), key1.pubkey(), key4.pubkey()],
1758 header: MessageHeader::default(),
1759 instructions: vec![
1760 CompiledInstruction {
1761 program_id_index: 1,
1762 accounts: vec![0],
1763 data: vec![],
1764 },
1765 CompiledInstruction {
1766 program_id_index: 1,
1767 accounts: vec![2],
1768 data: vec![],
1769 },
1770 ],
1771 recent_blockhash: Hash::default(),
1772 };
1773
1774 let sanitized_message = new_unchecked_sanitized_message(message);
1775 let mut mock_bank = TestCallbacks::default();
1776 let mut account_data = AccountSharedData::default();
1777 account_data.set_lamports(1);
1778 account_data.set_executable(true);
1779 account_data.set_owner(key3.pubkey());
1780 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1781
1782 let mut fee_payer_account = AccountSharedData::default();
1783 fee_payer_account.set_lamports(200);
1784 mock_bank
1785 .accounts_map
1786 .insert(key2.pubkey(), fee_payer_account.clone());
1787
1788 let mut account_data = AccountSharedData::default();
1789 account_data.set_lamports(1);
1790 account_data.set_executable(true);
1791 account_data.set_owner(native_loader::id());
1792 mock_bank.accounts_map.insert(key3.pubkey(), account_data);
1793 let mut account_loader = (&mock_bank).into();
1794
1795 let mut error_metrics = TransactionErrorMetrics::default();
1796
1797 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1798 sanitized_message,
1799 vec![Signature::new_unique()],
1800 false,
1801 );
1802 let result = load_transaction_accounts(
1803 &mut account_loader,
1804 sanitized_transaction.message(),
1805 LoadedTransactionAccount {
1806 account: fee_payer_account.clone(),
1807 ..LoadedTransactionAccount::default()
1808 },
1809 &ComputeBudgetLimits::default(),
1810 &mut error_metrics,
1811 &RentCollector::default(),
1812 );
1813
1814 let mut account_data = AccountSharedData::default();
1815 account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
1816 assert_eq!(
1817 result.unwrap(),
1818 LoadedTransactionAccounts {
1819 accounts: vec![
1820 (key2.pubkey(), fee_payer_account),
1821 (
1822 key1.pubkey(),
1823 mock_bank.accounts_map[&key1.pubkey()].clone()
1824 ),
1825 (key4.pubkey(), account_data),
1826 ],
1827 program_indices: vec![vec![1], vec![1]],
1828 rent: 0,
1829 rent_debits: RentDebits::default(),
1830 loaded_accounts_data_size: 0,
1831 }
1832 );
1833 }
1834
1835 #[test]
1836 fn test_rent_state_list_len() {
1837 let mint_keypair = Keypair::new();
1838 let mut bank = TestCallbacks::default();
1839 let recipient = Pubkey::new_unique();
1840 let last_block_hash = Hash::new_unique();
1841
1842 let mut system_data = AccountSharedData::default();
1843 system_data.set_lamports(1);
1844 system_data.set_executable(true);
1845 system_data.set_owner(native_loader::id());
1846 bank.accounts_map
1847 .insert(Pubkey::new_from_array([0u8; 32]), system_data);
1848
1849 let mut mint_data = AccountSharedData::default();
1850 mint_data.set_lamports(2);
1851 bank.accounts_map.insert(mint_keypair.pubkey(), mint_data);
1852 bank.accounts_map
1853 .insert(recipient, AccountSharedData::default());
1854 let mut account_loader = (&bank).into();
1855
1856 let tx = transfer(
1857 &mint_keypair,
1858 &recipient,
1859 sol_to_lamports(1.),
1860 last_block_hash,
1861 );
1862 let num_accounts = tx.message().account_keys.len();
1863 let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
1864 let mut error_metrics = TransactionErrorMetrics::default();
1865 let load_result = load_transaction(
1866 &mut account_loader,
1867 &sanitized_tx.clone(),
1868 Ok(ValidatedTransactionDetails::default()),
1869 &mut error_metrics,
1870 &RentCollector::default(),
1871 );
1872
1873 let TransactionLoadResult::Loaded(loaded_transaction) = load_result else {
1874 panic!("transaction loading failed");
1875 };
1876
1877 let compute_budget = ComputeBudget::new(u64::from(
1878 compute_budget_limits::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
1879 ));
1880 let rent_collector = RentCollector::default();
1881 let transaction_context = TransactionContext::new(
1882 loaded_transaction.accounts,
1883 rent_collector.get_rent().clone(),
1884 compute_budget.max_instruction_stack_depth,
1885 compute_budget.max_instruction_trace_length,
1886 );
1887
1888 assert_eq!(
1889 TransactionAccountStateInfo::new(
1890 &transaction_context,
1891 sanitized_tx.message(),
1892 &rent_collector,
1893 )
1894 .len(),
1895 num_accounts,
1896 );
1897 }
1898
1899 #[test]
1900 fn test_load_accounts_success() {
1901 let key1 = Keypair::new();
1902 let key2 = Keypair::new();
1903 let key3 = Keypair::new();
1904 let key4 = Keypair::new();
1905
1906 let message = Message {
1907 account_keys: vec![key2.pubkey(), key1.pubkey(), key4.pubkey()],
1908 header: MessageHeader::default(),
1909 instructions: vec![
1910 CompiledInstruction {
1911 program_id_index: 1,
1912 accounts: vec![0],
1913 data: vec![],
1914 },
1915 CompiledInstruction {
1916 program_id_index: 1,
1917 accounts: vec![2],
1918 data: vec![],
1919 },
1920 ],
1921 recent_blockhash: Hash::default(),
1922 };
1923
1924 let sanitized_message = new_unchecked_sanitized_message(message);
1925 let mut mock_bank = TestCallbacks::default();
1926 let mut account_data = AccountSharedData::default();
1927 account_data.set_lamports(1);
1928 account_data.set_executable(true);
1929 account_data.set_owner(key3.pubkey());
1930 mock_bank.accounts_map.insert(key1.pubkey(), account_data);
1931
1932 let mut fee_payer_account = AccountSharedData::default();
1933 fee_payer_account.set_lamports(200);
1934 mock_bank
1935 .accounts_map
1936 .insert(key2.pubkey(), fee_payer_account.clone());
1937
1938 let mut account_data = AccountSharedData::default();
1939 account_data.set_lamports(1);
1940 account_data.set_executable(true);
1941 account_data.set_owner(native_loader::id());
1942 mock_bank.accounts_map.insert(key3.pubkey(), account_data);
1943 let mut account_loader = (&mock_bank).into();
1944
1945 let mut error_metrics = TransactionErrorMetrics::default();
1946
1947 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1948 sanitized_message,
1949 vec![Signature::new_unique()],
1950 false,
1951 );
1952 let validation_result = Ok(ValidatedTransactionDetails {
1953 loaded_fee_payer_account: LoadedTransactionAccount {
1954 account: fee_payer_account,
1955 ..LoadedTransactionAccount::default()
1956 },
1957 ..ValidatedTransactionDetails::default()
1958 });
1959
1960 let load_result = load_transaction(
1961 &mut account_loader,
1962 &sanitized_transaction,
1963 validation_result,
1964 &mut error_metrics,
1965 &RentCollector::default(),
1966 );
1967
1968 let mut account_data = AccountSharedData::default();
1969 account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
1970
1971 let TransactionLoadResult::Loaded(loaded_transaction) = load_result else {
1972 panic!("transaction loading failed");
1973 };
1974 assert_eq!(
1975 loaded_transaction,
1976 LoadedTransaction {
1977 accounts: vec![
1978 (
1979 key2.pubkey(),
1980 mock_bank.accounts_map[&key2.pubkey()].clone()
1981 ),
1982 (
1983 key1.pubkey(),
1984 mock_bank.accounts_map[&key1.pubkey()].clone()
1985 ),
1986 (key4.pubkey(), account_data),
1987 ],
1988 program_indices: vec![vec![1], vec![1]],
1989 fee_details: FeeDetails::default(),
1990 rollback_accounts: RollbackAccounts::default(),
1991 compute_budget_limits: ComputeBudgetLimits::default(),
1992 rent: 0,
1993 rent_debits: RentDebits::default(),
1994 loaded_accounts_data_size: 0,
1995 }
1996 );
1997 }
1998
1999 #[test]
2000 fn test_load_accounts_error() {
2001 let mock_bank = TestCallbacks::default();
2002 let mut account_loader = (&mock_bank).into();
2003 let rent_collector = RentCollector::default();
2004
2005 let message = Message {
2006 account_keys: vec![Pubkey::new_from_array([0; 32])],
2007 header: MessageHeader::default(),
2008 instructions: vec![CompiledInstruction {
2009 program_id_index: 0,
2010 accounts: vec![],
2011 data: vec![],
2012 }],
2013 recent_blockhash: Hash::default(),
2014 };
2015
2016 let sanitized_message = new_unchecked_sanitized_message(message);
2017 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2018 sanitized_message,
2019 vec![Signature::new_unique()],
2020 false,
2021 );
2022
2023 let validation_result = Ok(ValidatedTransactionDetails::default());
2024 let load_result = load_transaction(
2025 &mut account_loader,
2026 &sanitized_transaction,
2027 validation_result.clone(),
2028 &mut TransactionErrorMetrics::default(),
2029 &rent_collector,
2030 );
2031
2032 assert!(matches!(
2033 load_result,
2034 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
2035 load_error: TransactionError::ProgramAccountNotFound,
2036 ..
2037 }),
2038 ));
2039
2040 let validation_result = Err(TransactionError::InvalidWritableAccount);
2041
2042 let load_result = load_transaction(
2043 &mut account_loader,
2044 &sanitized_transaction,
2045 validation_result,
2046 &mut TransactionErrorMetrics::default(),
2047 &rent_collector,
2048 );
2049
2050 assert!(matches!(
2051 load_result,
2052 TransactionLoadResult::NotLoaded(TransactionError::InvalidWritableAccount),
2053 ));
2054 }
2055
2056 #[test]
2057 fn test_collect_rent_from_account() {
2058 let feature_set = FeatureSet::all_enabled();
2059 let rent_collector = RentCollector {
2060 epoch: 1,
2061 ..RentCollector::default()
2062 };
2063
2064 let address = Pubkey::new_unique();
2065 let min_exempt_balance = rent_collector.rent.minimum_balance(0);
2066 let mut account = AccountSharedData::from(Account {
2067 lamports: min_exempt_balance,
2068 ..Account::default()
2069 });
2070
2071 assert_eq!(
2072 collect_rent_from_account(&feature_set, &rent_collector, &address, &mut account),
2073 CollectedInfo::default()
2074 );
2075 assert_eq!(account.rent_epoch(), RENT_EXEMPT_RENT_EPOCH);
2076 }
2077
2078 #[test]
2079 fn test_collect_rent_from_account_rent_paying() {
2080 let feature_set = FeatureSet::all_enabled();
2081 let rent_collector = RentCollector {
2082 epoch: 1,
2083 ..RentCollector::default()
2084 };
2085
2086 let address = Pubkey::new_unique();
2087 let mut account = AccountSharedData::from(Account {
2088 lamports: 1,
2089 ..Account::default()
2090 });
2091
2092 assert_eq!(
2093 collect_rent_from_account(&feature_set, &rent_collector, &address, &mut account),
2094 CollectedInfo::default()
2095 );
2096 assert_eq!(account.rent_epoch(), 0);
2097 assert_eq!(account.lamports(), 1);
2098 }
2099
2100 #[test]
2101 fn test_collect_rent_from_account_rent_enabled() {
2102 let feature_set =
2103 all_features_except(Some(&[feature_set::disable_rent_fees_collection::id()]));
2104 let rent_collector = RentCollector {
2105 epoch: 1,
2106 ..RentCollector::default()
2107 };
2108
2109 let address = Pubkey::new_unique();
2110 let mut account = AccountSharedData::from(Account {
2111 lamports: 1,
2112 data: vec![0],
2113 ..Account::default()
2114 });
2115
2116 assert_eq!(
2117 collect_rent_from_account(&feature_set, &rent_collector, &address, &mut account),
2118 CollectedInfo {
2119 rent_amount: 1,
2120 account_data_len_reclaimed: 1
2121 }
2122 );
2123 assert_eq!(account.rent_epoch(), 0);
2124 assert_eq!(account.lamports(), 0);
2125 }
2126
2127 #[test]
2130 fn test_inspect_account_non_fee_payer() {
2131 let mut mock_bank = TestCallbacks::default();
2132
2133 let address0 = Pubkey::new_unique(); let address1 = Pubkey::new_unique(); let address2 = Pubkey::new_unique(); let address3 = Pubkey::new_unique(); let mut account0 = AccountSharedData::default();
2139 account0.set_lamports(1_000_000_000);
2140 mock_bank.accounts_map.insert(address0, account0.clone());
2141
2142 let mut account1 = AccountSharedData::default();
2143 account1.set_lamports(2_000_000_000);
2144 mock_bank.accounts_map.insert(address1, account1.clone());
2145
2146 let mut account3 = AccountSharedData::default();
2149 account3.set_lamports(4_000_000_000);
2150 account3.set_executable(true);
2151 account3.set_owner(bpf_loader::id());
2152 mock_bank.accounts_map.insert(address3, account3.clone());
2153 let mut account_loader = (&mock_bank).into();
2154
2155 let message = Message {
2156 account_keys: vec![address0, address1, address2, address3],
2157 header: MessageHeader::default(),
2158 instructions: vec![
2159 CompiledInstruction {
2160 program_id_index: 3,
2161 accounts: vec![0],
2162 data: vec![],
2163 },
2164 CompiledInstruction {
2165 program_id_index: 3,
2166 accounts: vec![1, 2],
2167 data: vec![],
2168 },
2169 CompiledInstruction {
2170 program_id_index: 3,
2171 accounts: vec![1],
2172 data: vec![],
2173 },
2174 ],
2175 recent_blockhash: Hash::new_unique(),
2176 };
2177 let sanitized_message = new_unchecked_sanitized_message(message);
2178 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2179 sanitized_message,
2180 vec![Signature::new_unique()],
2181 false,
2182 );
2183 let validation_result = Ok(ValidatedTransactionDetails {
2184 loaded_fee_payer_account: LoadedTransactionAccount {
2185 account: account0.clone(),
2186 ..LoadedTransactionAccount::default()
2187 },
2188 ..ValidatedTransactionDetails::default()
2189 });
2190 let _load_results = load_transaction(
2191 &mut account_loader,
2192 &sanitized_transaction,
2193 validation_result,
2194 &mut TransactionErrorMetrics::default(),
2195 &RentCollector::default(),
2196 );
2197
2198 let mut actual_inspected_accounts: Vec<_> = mock_bank
2200 .inspected_accounts
2201 .borrow()
2202 .iter()
2203 .map(|(k, v)| (*k, v.clone()))
2204 .collect();
2205 actual_inspected_accounts.sort_unstable_by(|a, b| a.0.cmp(&b.0));
2206
2207 let mut expected_inspected_accounts = vec![
2208 (address1, vec![(Some(account1), true)]),
2210 (address2, vec![(None, true)]),
2211 (
2212 address3,
2213 vec![(Some(account3.clone()), false), (Some(account3), false)],
2214 ),
2215 (bpf_loader::id(), vec![(None, false)]),
2216 ];
2217 expected_inspected_accounts.sort_unstable_by(|a, b| a.0.cmp(&b.0));
2218
2219 assert_eq!(actual_inspected_accounts, expected_inspected_accounts,);
2220 }
2221
2222 #[test]
2223 fn test_load_transaction_accounts_data_sizes() {
2224 let mut mock_bank = TestCallbacks::default();
2225
2226 let loader_v2 = bpf_loader::id();
2227 let loader_v3 = bpf_loader_upgradeable::id();
2228 let program1_keypair = Keypair::new();
2229 let program1 = program1_keypair.pubkey();
2230 let program2 = Pubkey::new_unique();
2231 let programdata2 = Pubkey::new_unique();
2232 use solana_account::state_traits::StateMut;
2233
2234 let program2_size = std::mem::size_of::<UpgradeableLoaderState>() as u32;
2235 let mut program2_account = AccountSharedData::default();
2236 program2_account.set_owner(loader_v3);
2237 program2_account.set_lamports(LAMPORTS_PER_SOL);
2238 program2_account.set_executable(true);
2239 program2_account.set_data(vec![0; program2_size as usize]);
2240 program2_account
2241 .set_state(&UpgradeableLoaderState::Program {
2242 programdata_address: programdata2,
2243 })
2244 .unwrap();
2245 mock_bank.accounts_map.insert(program2, program2_account);
2246 let mut programdata2_account = AccountSharedData::default();
2247 programdata2_account.set_owner(loader_v3);
2248 programdata2_account.set_lamports(LAMPORTS_PER_SOL);
2249 programdata2_account.set_data(vec![0; program2_size as usize]);
2250 programdata2_account
2251 .set_state(&UpgradeableLoaderState::ProgramData {
2252 slot: 0,
2253 upgrade_authority_address: None,
2254 })
2255 .unwrap();
2256 let mut programdata = programdata2_account.data().to_vec();
2257 let mut file =
2258 File::open("tests/example-programs/hello-solana/hello_solana_program.so").unwrap();
2259 file.read_to_end(&mut programdata).unwrap();
2260 let programdata2_size = programdata.len() as u32;
2261 programdata2_account.set_data(programdata);
2262 mock_bank
2263 .accounts_map
2264 .insert(programdata2, programdata2_account);
2265
2266 let mut next_size = 1;
2267 let mut make_account = |pubkey, owner, executable| {
2268 let size = next_size;
2269 let account = AccountSharedData::create(
2270 LAMPORTS_PER_SOL,
2271 vec![0; size],
2272 owner,
2273 executable,
2274 u64::MAX,
2275 );
2276
2277 mock_bank.accounts_map.insert(pubkey, account.clone());
2278
2279 next_size *= 4;
2282
2283 (size as u32, account)
2284 };
2285
2286 let fee_payer_keypair = Keypair::new();
2287 let fee_payer = fee_payer_keypair.pubkey();
2288 let (fee_payer_size, fee_payer_account) =
2289 make_account(fee_payer, system_program::id(), false);
2290
2291 let account1 = Pubkey::new_unique();
2292 let (account1_size, _) = make_account(account1, program1, false);
2293
2294 let account2 = Pubkey::new_unique();
2295 let (account2_size, _) = make_account(account2, program2, false);
2296
2297 let (native_loader_size, _) = make_account(native_loader::id(), native_loader::id(), true);
2298 let (bpf_loader_size, _) = make_account(loader_v2, native_loader::id(), true);
2299 let (upgradeable_loader_size, _) = make_account(loader_v3, native_loader::id(), true);
2300
2301 let (program1_size, _) = make_account(program1, loader_v2, true);
2302
2303 let mut program_accounts = HashMap::new();
2304 program_accounts.insert(program1, (&loader_v2, 0));
2305 program_accounts.insert(program2, (&loader_v3, 0));
2306
2307 let test_transaction_data_size = |transaction, expected_size| {
2308 let mut account_loader = AccountLoader::new_with_account_cache_capacity(
2309 None,
2310 &mock_bank,
2311 Arc::<FeatureSet>::default(),
2312 0,
2313 );
2314
2315 let loaded_transaction_accounts = load_transaction_accounts(
2316 &mut account_loader,
2317 &transaction,
2318 LoadedTransactionAccount {
2319 account: fee_payer_account.clone(),
2320 loaded_size: fee_payer_size as usize,
2321 rent_collected: 0,
2322 },
2323 &ComputeBudgetLimits::default(),
2324 &mut TransactionErrorMetrics::default(),
2325 &RentCollector::default(),
2326 )
2327 .unwrap();
2328
2329 assert_eq!(
2330 loaded_transaction_accounts.loaded_accounts_data_size,
2331 expected_size
2332 );
2333 };
2334
2335 let test_data_size = |instructions: Vec<_>, expected_size| {
2336 let transaction = SanitizedTransaction::from_transaction_for_tests(
2337 Transaction::new_signed_with_payer(
2338 &instructions,
2339 Some(&fee_payer),
2340 &[&fee_payer_keypair],
2341 Hash::default(),
2342 ),
2343 );
2344
2345 test_transaction_data_size(transaction, expected_size)
2346 };
2347
2348 for account_meta in [AccountMeta::new, AccountMeta::new_readonly] {
2349 let ixns = vec![Instruction::new_with_bytes(program1, &[], vec![])];
2351 test_data_size(ixns, program1_size + bpf_loader_size + fee_payer_size);
2352
2353 let ixns = vec![
2355 Instruction::new_with_bytes(program1, &[], vec![account_meta(account1, false)]),
2356 Instruction::new_with_bytes(program2, &[], vec![account_meta(account2, false)]),
2357 ];
2358 test_data_size(
2359 ixns,
2360 account1_size
2361 + account2_size
2362 + program1_size
2363 + program2_size
2364 + bpf_loader_size
2365 + upgradeable_loader_size
2366 + fee_payer_size,
2367 );
2368
2369 let ixns = vec![
2371 Instruction::new_with_bytes(program2, &[], vec![]),
2372 Instruction::new_with_bytes(program2, &[], vec![]),
2373 ];
2374 test_data_size(
2375 ixns,
2376 program2_size + upgradeable_loader_size + fee_payer_size,
2377 );
2378
2379 let ixns = vec![Instruction::new_with_bytes(bpf_loader::id(), &[], vec![])];
2381 test_data_size(ixns, bpf_loader_size + fee_payer_size);
2382
2383 let ixns = vec![Instruction::new_with_bytes(
2385 bpf_loader::id(),
2386 &[],
2387 vec![account_meta(native_loader::id(), false)],
2388 )];
2389 test_data_size(ixns, bpf_loader_size + native_loader_size + fee_payer_size);
2390
2391 let ixns = vec![Instruction::new_with_bytes(
2393 native_loader::id(),
2394 &[],
2395 vec![],
2396 )];
2397 test_data_size(ixns, native_loader_size + fee_payer_size);
2398
2399 let ixns = vec![Instruction::new_with_bytes(
2401 native_loader::id(),
2402 &[],
2403 vec![account_meta(native_loader::id(), false)],
2404 )];
2405 test_data_size(ixns, native_loader_size + fee_payer_size);
2406
2407 let ixns = vec![Instruction::new_with_bytes(
2409 program2,
2410 &[],
2411 vec![account_meta(bpf_loader_upgradeable::id(), false)],
2412 )];
2413 test_data_size(
2414 ixns,
2415 upgradeable_loader_size * 2 + program2_size + fee_payer_size,
2416 );
2417
2418 let ixns = vec![
2420 Instruction::new_with_bytes(bpf_loader_upgradeable::id(), &[], vec![]),
2421 Instruction::new_with_bytes(program2, &[], vec![]),
2422 ];
2423 test_data_size(
2424 ixns,
2425 upgradeable_loader_size * 2 + program2_size + fee_payer_size,
2426 );
2427
2428 let ixns = vec![
2430 Instruction::new_with_bytes(
2431 program1,
2432 &[],
2433 vec![
2434 account_meta(bpf_loader::id(), false),
2435 account_meta(bpf_loader_upgradeable::id(), false),
2436 ],
2437 ),
2438 Instruction::new_with_bytes(program2, &[], vec![account_meta(account1, false)]),
2439 Instruction::new_with_bytes(
2440 bpf_loader_upgradeable::id(),
2441 &[],
2442 vec![account_meta(account1, false)],
2443 ),
2444 ];
2445 test_data_size(
2446 ixns,
2447 account1_size
2448 + program1_size
2449 + program2_size
2450 + bpf_loader_size * 2
2451 + upgradeable_loader_size * 2
2452 + fee_payer_size,
2453 );
2454
2455 let ixns = vec![Instruction::new_with_bytes(
2457 program1,
2458 &[],
2459 vec![account_meta(fee_payer, false)],
2460 )];
2461 test_data_size(ixns, program1_size + bpf_loader_size + fee_payer_size);
2462
2463 let ixns = vec![Instruction::new_with_bytes(
2465 program2,
2466 &[],
2467 vec![account_meta(programdata2, false)],
2468 )];
2469 test_data_size(
2470 ixns,
2471 program2_size + programdata2_size + upgradeable_loader_size + fee_payer_size,
2472 );
2473
2474 let ixns = vec![Instruction::new_with_bytes(
2476 program2,
2477 &[],
2478 vec![
2479 account_meta(program2, false),
2480 account_meta(programdata2, false),
2481 ],
2482 )];
2483 test_data_size(
2484 ixns,
2485 program2_size + programdata2_size + upgradeable_loader_size + fee_payer_size,
2486 );
2487 }
2488 }
2489}