1#[cfg(feature = "dev-context-only-utils")]
2use qualifier_attr::{field_qualifiers, qualifiers};
3use {
4 crate::{
5 account_overrides::AccountOverrides,
6 rent_calculator::{RENT_EXEMPT_RENT_EPOCH, check_static_account_rent_state_transition},
7 rollback_accounts::RollbackAccounts,
8 transaction_error_metrics::TransactionErrorMetrics,
9 },
10 ahash::{AHashMap, AHashSet},
11 solana_account::{
12 Account, AccountSharedData, ReadableAccount, WritableAccount, state_traits::StateMut,
13 },
14 solana_clock::Slot,
15 solana_fee_structure::FeeDetails,
16 solana_instruction::{BorrowedAccountMeta, BorrowedInstruction},
17 solana_instructions_sysvar::construct_instructions_data,
18 solana_loader_v3_interface::state::UpgradeableLoaderState,
19 solana_nonce::state::State as NonceState,
20 solana_nonce_account::{SystemAccountKind, get_system_account_kind},
21 solana_program_runtime::execution_budget::{
22 SVMTransactionExecutionAndFeeBudgetLimits, SVMTransactionExecutionBudget,
23 },
24 solana_pubkey::Pubkey,
25 solana_rent::Rent,
26 solana_sdk_ids::{
27 bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, native_loader,
28 sysvar::{self, slot_history},
29 },
30 solana_svm_callback::{AccountState, TransactionProcessingCallback},
31 solana_svm_feature_set::SVMFeatureSet,
32 solana_svm_transaction::svm_message::SVMMessage,
33 solana_transaction_context::{IndexOfAccount, transaction_accounts::KeyedAccountSharedData},
34 solana_transaction_error::{TransactionError, TransactionResult as Result},
35};
36
37#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
40pub(crate) const TRANSACTION_ACCOUNT_BASE_SIZE: usize = 64;
41
42pub const PROGRAM_OWNERS: &[Pubkey] = &[
44 bpf_loader_upgradeable::id(),
45 bpf_loader::id(),
46 bpf_loader_deprecated::id(),
47 loader_v4::id(),
48];
49
50const ADDRESS_LOOKUP_TABLE_BASE_SIZE: usize = 8248;
53
54pub type TransactionCheckResult = Result<CheckedTransactionDetails>;
56type TransactionValidationResult = Result<ValidatedTransactionDetails>;
57
58#[derive(PartialEq, Eq, Debug)]
59pub(crate) enum TransactionLoadResult {
60 Loaded(LoadedTransaction),
62 FeesOnly(FeesOnlyTransaction),
66 NotLoaded(TransactionError),
69}
70
71#[derive(PartialEq, Eq, Debug, Clone)]
72#[cfg_attr(
73 feature = "svm-internal",
74 qualifier_attr::field_qualifiers(nonce_address(pub))
75)]
76pub struct CheckedTransactionDetails {
77 pub(crate) nonce_address: Option<Pubkey>,
78 pub(crate) compute_budget_and_limits: SVMTransactionExecutionAndFeeBudgetLimits,
79}
80
81#[cfg(feature = "dev-context-only-utils")]
82impl Default for CheckedTransactionDetails {
83 fn default() -> Self {
84 Self {
85 nonce_address: None,
86 compute_budget_and_limits: SVMTransactionExecutionAndFeeBudgetLimits {
87 budget: SVMTransactionExecutionBudget::default(),
88 loaded_accounts_data_size_limit: 32,
89 fee_details: FeeDetails::default(),
90 },
91 }
92 }
93}
94
95impl CheckedTransactionDetails {
96 pub fn new(
97 nonce_address: Option<Pubkey>,
98 compute_budget_and_limits: SVMTransactionExecutionAndFeeBudgetLimits,
99 ) -> Self {
100 Self {
101 nonce_address,
102 compute_budget_and_limits,
103 }
104 }
105}
106
107#[derive(PartialEq, Eq, Debug, Clone)]
108pub(crate) struct ValidatedTransactionDetails {
109 pub(crate) rollback_accounts: RollbackAccounts,
110 pub(crate) compute_budget: SVMTransactionExecutionBudget,
111 pub(crate) loaded_accounts_bytes_limit: u32,
112 pub(crate) fee_details: FeeDetails,
113 pub(crate) loaded_fee_payer_account: LoadedTransactionAccount,
114}
115
116#[cfg(feature = "dev-context-only-utils")]
117impl Default for ValidatedTransactionDetails {
118 fn default() -> Self {
119 Self {
120 rollback_accounts: RollbackAccounts::default(),
121 compute_budget: SVMTransactionExecutionBudget::default(),
122 loaded_accounts_bytes_limit:
123 solana_program_runtime::execution_budget::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get(),
124 fee_details: FeeDetails::default(),
125 loaded_fee_payer_account: LoadedTransactionAccount::default(),
126 }
127 }
128}
129
130#[derive(PartialEq, Eq, Debug, Clone)]
131#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
132pub(crate) struct LoadedTransactionAccount {
133 pub(crate) account: AccountSharedData,
134 pub(crate) loaded_size: usize,
135}
136
137#[derive(PartialEq, Eq, Debug, Clone)]
138#[cfg_attr(feature = "dev-context-only-utils", derive(Default))]
139#[cfg_attr(
140 feature = "dev-context-only-utils",
141 field_qualifiers(compute_budget(pub))
142)]
143pub struct LoadedTransaction {
144 pub accounts: Vec<KeyedAccountSharedData>,
145 pub touched_flags: Box<[bool]>,
148 pub fee_details: FeeDetails,
149 pub rollback_accounts: RollbackAccounts,
150 pub(crate) compute_budget: SVMTransactionExecutionBudget,
151 pub loaded_accounts_data_size: u32,
152}
153
154#[derive(PartialEq, Eq, Debug, Clone)]
155pub struct FeesOnlyTransaction {
156 pub load_error: TransactionError,
157 pub rollback_accounts: RollbackAccounts,
158 pub fee_details: FeeDetails,
159 pub loaded_accounts_data_size: u32,
160}
161
162#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
169pub(crate) struct AccountLoader<'a, CB: TransactionProcessingCallback> {
170 loaded_accounts: AHashMap<Pubkey, (AccountSharedData, Slot)>,
171 callbacks: &'a CB,
172 pub(crate) feature_set: &'a SVMFeatureSet,
173}
174
175impl<'a, CB: TransactionProcessingCallback> AccountLoader<'a, CB> {
176 #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
178 pub(crate) fn new_with_loaded_accounts_capacity(
179 account_overrides: Option<&'a AccountOverrides>,
180 callbacks: &'a CB,
181 feature_set: &'a SVMFeatureSet,
182 capacity: usize,
183 ) -> AccountLoader<'a, CB> {
184 let mut loaded_accounts = AHashMap::with_capacity(capacity);
185
186 if let Some(slot_history) =
189 account_overrides.and_then(|overrides| overrides.get(&slot_history::id()))
190 {
191 loaded_accounts.insert(slot_history::id(), (slot_history.clone(), 0));
192 }
193
194 Self {
195 loaded_accounts,
196 callbacks,
197 feature_set,
198 }
199 }
200
201 pub(crate) fn load_transaction_account(
207 &mut self,
208 account_key: &Pubkey,
209 is_writable: bool,
210 ) -> Option<LoadedTransactionAccount> {
211 let account = self.load_account(account_key);
212
213 self.callbacks.inspect_account(
221 account_key,
222 if let Some(ref account) = account {
223 AccountState::Alive(account)
224 } else {
225 AccountState::Dead
226 },
227 is_writable,
228 );
229
230 account.map(|account| LoadedTransactionAccount {
231 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE.saturating_add(account.data().len()),
232 account,
233 })
234 }
235
236 pub(crate) fn load_account(&mut self, account_key: &Pubkey) -> Option<AccountSharedData> {
239 match self.do_load(account_key) {
240 (Some((account, _last_modification_slot)), false) => Some(account),
242 (None, false) => None,
244 (Some((account, last_modification_slot)), true) => {
246 self.loaded_accounts
247 .insert(*account_key, (account.clone(), last_modification_slot));
248 Some(account)
249 }
250 (None, true) => {
252 self.loaded_accounts
253 .insert(*account_key, (AccountSharedData::default(), 0));
254 None
255 }
256 }
257 }
258
259 fn do_load(&self, account_key: &Pubkey) -> (Option<(AccountSharedData, Slot)>, bool) {
263 if let Some((account, slot)) = self.loaded_accounts.get(account_key) {
264 let option_account = if account.lamports() == 0 {
268 None
269 } else {
270 Some((account.clone(), *slot))
271 };
272
273 (option_account, false)
274 } else if let Some((account, slot)) = self.callbacks.get_account_shared_data(account_key) {
275 (Some((account, slot)), true)
276 } else {
277 (None, true)
278 }
279 }
280
281 pub(crate) fn update_accounts_for_failed_tx(
282 &mut self,
283 rollback_accounts: &RollbackAccounts,
284 current_slot: Slot,
285 ) {
286 for (account_address, account) in rollback_accounts {
287 self.loaded_accounts
288 .insert(*account_address, (account.clone(), current_slot));
289 }
290 }
291
292 pub(crate) fn update_accounts_for_successful_tx(
293 &mut self,
294 message: &impl SVMMessage,
295 transaction_accounts: &[KeyedAccountSharedData],
296 touched_flags: &[bool],
297 current_slot: Slot,
298 ) {
299 for (i, (address, account)) in (0..message.account_keys().len()).zip(transaction_accounts) {
300 if !message.is_writable(i) {
301 continue;
302 }
303
304 if !touched_flags[i] {
306 continue;
307 }
308
309 if message.is_invoked(i) && !message.is_instruction_account(i) {
314 continue;
315 }
316
317 self.loaded_accounts
318 .insert(*address, (account.clone(), current_slot));
319 }
320 }
321}
322
323impl<CB: TransactionProcessingCallback> TransactionProcessingCallback for AccountLoader<'_, CB> {
328 fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
329 self.do_load(pubkey).0
330 }
331}
332
333pub fn update_rent_exempt_status_for_account(rent: &Rent, account: &mut AccountSharedData) {
339 if account.rent_epoch() != RENT_EXEMPT_RENT_EPOCH
345 && rent.is_exempt(account.lamports(), account.data().len())
346 {
347 account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
348 }
349}
350
351pub fn validate_fee_payer(
357 payer_account: &mut AccountSharedData,
358 payer_index: IndexOfAccount,
359 error_metrics: &mut TransactionErrorMetrics,
360 rent: &Rent,
361 fee: u64,
362 relax_post_exec_min_balance_check: bool,
363) -> Result<()> {
364 if payer_account.lamports() == 0 {
365 error_metrics.account_not_found += 1;
366 return Err(TransactionError::AccountNotFound);
367 }
368 let system_account_kind = get_system_account_kind(payer_account).ok_or_else(|| {
369 error_metrics.invalid_account_for_fee += 1;
370 TransactionError::InvalidAccountForFee
371 })?;
372 let min_balance = match system_account_kind {
373 SystemAccountKind::System => 0,
374 SystemAccountKind::Nonce => {
375 rent.minimum_balance(NonceState::size())
378 }
379 };
380
381 payer_account
382 .lamports()
383 .checked_sub(min_balance)
384 .and_then(|v| v.checked_sub(fee))
385 .ok_or_else(|| {
386 error_metrics.insufficient_funds += 1;
387 TransactionError::InsufficientFundsForFee
388 })?;
389
390 let pre_balance = payer_account.lamports();
391 payer_account
392 .checked_sub_lamports(fee)
393 .map_err(|_| TransactionError::InsufficientFundsForFee)?;
394 let post_balance = payer_account.lamports();
395
396 check_static_account_rent_state_transition(
397 pre_balance,
398 post_balance,
399 payer_account.data().len(),
400 rent,
401 payer_index,
402 relax_post_exec_min_balance_check,
403 )
404}
405
406pub(crate) fn load_transaction<CB: TransactionProcessingCallback>(
407 account_loader: &mut AccountLoader<CB>,
408 message: &impl SVMMessage,
409 validation_result: TransactionValidationResult,
410 error_metrics: &mut TransactionErrorMetrics,
411 rent: &Rent,
412) -> TransactionLoadResult {
413 match validation_result {
414 Err(e) => TransactionLoadResult::NotLoaded(e),
415 Ok(tx_details) => {
416 let mut loaded_transaction_data_size =
417 LoadedTransactionDataSize::with_max_size(tx_details.loaded_accounts_bytes_limit);
418
419 let load_result = load_transaction_accounts(
420 account_loader,
421 message,
422 tx_details.loaded_fee_payer_account,
423 &mut loaded_transaction_data_size,
424 error_metrics,
425 rent,
426 );
427
428 match load_result {
429 Ok(accounts) => TransactionLoadResult::Loaded(LoadedTransaction {
430 accounts,
431 touched_flags: Box::default(),
433 fee_details: tx_details.fee_details,
434 rollback_accounts: tx_details.rollback_accounts,
435 compute_budget: tx_details.compute_budget,
436 loaded_accounts_data_size: loaded_transaction_data_size.into(),
437 }),
438 Err(err) => TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
439 load_error: err,
440 fee_details: tx_details.fee_details,
441 loaded_accounts_data_size: if account_loader
442 .feature_set
443 .define_ltds_fee_only_semantics
444 {
445 loaded_transaction_data_size.into()
446 } else {
447 tx_details.rollback_accounts.data_size() as u32
448 },
449 rollback_accounts: tx_details.rollback_accounts,
450 }),
451 }
452 }
453 }
454}
455
456#[derive(PartialEq, Eq, Debug, Clone)]
457struct LoadedTransactionDataSize {
458 loaded_accounts_data_size: u32,
459 requested_loaded_accounts_data_size_limit: u32,
460}
461
462impl LoadedTransactionDataSize {
463 fn with_max_size(requested_loaded_accounts_data_size_limit: u32) -> Self {
464 Self {
465 loaded_accounts_data_size: 0,
466 requested_loaded_accounts_data_size_limit,
467 }
468 }
469
470 fn increase_calculated_data_size(
471 &mut self,
472 data_size_delta: usize,
473 error_metrics: &mut TransactionErrorMetrics,
474 ) -> Result<()> {
475 let Ok(data_size_delta) = u32::try_from(data_size_delta) else {
478 self.loaded_accounts_data_size = u32::MAX;
479 error_metrics.max_loaded_accounts_data_size_exceeded += 1;
480 return Err(TransactionError::MaxLoadedAccountsDataSizeExceeded);
481 };
482
483 self.loaded_accounts_data_size = self
484 .loaded_accounts_data_size
485 .saturating_add(data_size_delta);
486
487 if self.loaded_accounts_data_size > self.requested_loaded_accounts_data_size_limit {
488 error_metrics.max_loaded_accounts_data_size_exceeded += 1;
489 Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
490 } else {
491 Ok(())
492 }
493 }
494}
495
496impl From<LoadedTransactionDataSize> for u32 {
497 fn from(value: LoadedTransactionDataSize) -> Self {
498 value
499 .loaded_accounts_data_size
500 .min(value.requested_loaded_accounts_data_size_limit)
501 }
502}
503
504fn load_transaction_accounts<CB: TransactionProcessingCallback>(
505 account_loader: &mut AccountLoader<CB>,
506 message: &impl SVMMessage,
507 loaded_fee_payer_account: LoadedTransactionAccount,
508 loaded_tx_data_size: &mut LoadedTransactionDataSize,
509 error_metrics: &mut TransactionErrorMetrics,
510 rent: &Rent,
511) -> Result<Vec<KeyedAccountSharedData>> {
512 let account_keys = message.account_keys();
513 let mut loaded_transaction_accounts = Vec::with_capacity(account_keys.len());
514 let mut additional_loaded_accounts: AHashSet<Pubkey> = AHashSet::new();
515
516 loaded_tx_data_size.increase_calculated_data_size(
518 message
519 .num_lookup_tables()
520 .saturating_mul(ADDRESS_LOOKUP_TABLE_BASE_SIZE),
521 error_metrics,
522 )?;
523
524 let mut collect_loaded_account =
525 |account_loader: &mut AccountLoader<CB>, key: &Pubkey, loaded_account| -> Result<()> {
526 let LoadedTransactionAccount {
527 account,
528 loaded_size,
529 } = loaded_account;
530
531 loaded_tx_data_size.increase_calculated_data_size(loaded_size, error_metrics)?;
532
533 if bpf_loader_upgradeable::check_id(account.owner())
545 && let Ok(UpgradeableLoaderState::Program {
546 programdata_address,
547 }) = account.state()
548 {
549 if !account_keys.iter().any(|key| programdata_address == *key)
551 && !additional_loaded_accounts.contains(&programdata_address)
552 {
553 if let Some(programdata_account) =
555 account_loader.load_account(&programdata_address)
556 {
557 loaded_tx_data_size.increase_calculated_data_size(
559 TRANSACTION_ACCOUNT_BASE_SIZE
560 .saturating_add(programdata_account.data().len()),
561 error_metrics,
562 )?;
563 additional_loaded_accounts.insert(programdata_address);
564 }
565 }
566 }
567
568 loaded_transaction_accounts.push((*key, account));
569
570 Ok(())
571 };
572
573 collect_loaded_account(
576 account_loader,
577 message.fee_payer(),
578 loaded_fee_payer_account,
579 )?;
580
581 for (account_index, account_key) in account_keys.iter().enumerate().skip(1) {
583 let loaded_account =
584 load_transaction_account(account_loader, message, account_key, account_index, rent)?;
585 collect_loaded_account(account_loader, account_key, loaded_account)?;
586 }
587
588 for (program_id, _) in message.program_instructions_iter() {
589 let Some(program_account) = account_loader.load_account(program_id) else {
590 error_metrics.account_not_found += 1;
591 return Err(TransactionError::ProgramAccountNotFound);
592 };
593
594 let owner_id = program_account.owner();
595 if !native_loader::check_id(owner_id) && !PROGRAM_OWNERS.contains(owner_id) {
596 error_metrics.invalid_program_for_execution += 1;
597 return Err(TransactionError::InvalidProgramForExecution);
598 }
599 }
600
601 Ok(loaded_transaction_accounts)
602}
603
604fn load_transaction_account<CB: TransactionProcessingCallback>(
605 account_loader: &mut AccountLoader<CB>,
606 message: &impl SVMMessage,
607 account_key: &Pubkey,
608 account_index: usize,
609 rent: &Rent,
610) -> Result<LoadedTransactionAccount> {
611 let is_writable = message.is_writable(account_index);
612 if solana_sdk_ids::sysvar::instructions::check_id(account_key) {
613 Ok(LoadedTransactionAccount {
616 loaded_size: 0,
617 account: construct_instructions_account(message)?,
618 })
619 } else if let Some(mut loaded_account) =
620 account_loader.load_transaction_account(account_key, is_writable)
621 {
622 if is_writable {
623 update_rent_exempt_status_for_account(rent, &mut loaded_account.account);
624 }
625 Ok(loaded_account)
626 } else {
627 let mut default_account = AccountSharedData::default();
628 default_account.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
629 Ok(LoadedTransactionAccount {
630 loaded_size: default_account.data().len(),
631 account: default_account,
632 })
633 }
634}
635
636fn construct_instructions_account(message: &impl SVMMessage) -> Result<AccountSharedData> {
637 let account_keys = message.account_keys();
638 let mut decompiled_instructions = Vec::with_capacity(message.num_instructions());
639 for (program_id, instruction) in message.program_instructions_iter() {
640 let accounts = instruction
641 .accounts
642 .iter()
643 .map(|account_index| {
644 let account_index = usize::from(*account_index);
645 BorrowedAccountMeta {
646 is_signer: message.is_signer(account_index),
647 is_writable: message.is_writable(account_index),
648 pubkey: account_keys.get(account_index).unwrap(),
649 }
650 })
651 .collect();
652
653 decompiled_instructions.push(BorrowedInstruction {
654 accounts,
655 data: instruction.data,
656 program_id,
657 });
658 }
659
660 Ok(AccountSharedData::from(Account {
661 data: construct_instructions_data(&decompiled_instructions)
662 .map_err(|_err| TransactionError::MaxLoadedAccountsDataSizeExceeded)?,
663 owner: sysvar::id(),
664 ..Account::default()
665 }))
666}
667
668#[cfg(test)]
669mod tests {
670 use {
671 super::*,
672 crate::transaction_account_state_info::TransactionAccountStateInfo,
673 rand::prelude::*,
674 solana_account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
675 solana_hash::Hash,
676 solana_instruction::{AccountMeta, Instruction},
677 solana_keypair::Keypair,
678 solana_loader_v3_interface::state::UpgradeableLoaderState,
679 solana_message::{
680 LegacyMessage, Message, MessageHeader, SanitizedMessage,
681 compiled_instruction::CompiledInstruction,
682 v0::{LoadedAddresses, LoadedMessage},
683 v1,
684 },
685 solana_native_token::LAMPORTS_PER_SOL,
686 solana_nonce::{self as nonce, versions::Versions as NonceVersions},
687 solana_program_runtime::execution_budget::{
688 DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
689 },
690 solana_pubkey::Pubkey,
691 solana_rent::Rent,
692 solana_sdk_ids::{
693 bpf_loader, bpf_loader_upgradeable, native_loader, system_program, sysvar,
694 },
695 solana_signature::Signature,
696 solana_signer::Signer,
697 solana_svm_callback::TransactionProcessingCallback,
698 solana_system_transaction::transfer,
699 solana_transaction::{Transaction, sanitized::SanitizedTransaction},
700 solana_transaction_context::{
701 transaction::TransactionContext, transaction_accounts::KeyedAccountSharedData,
702 },
703 solana_transaction_error::{TransactionError, TransactionResult as Result},
704 std::{
705 borrow::Cow,
706 cell::RefCell,
707 collections::{HashMap, HashSet},
708 sync::Arc,
709 },
710 };
711
712 fn setup_test_logger() {
713 let _ = env_logger::Builder::from_env(env_logger::Env::new().default_filter_or("error"))
714 .format_timestamp_nanos()
715 .is_test(true)
716 .try_init();
717 }
718
719 #[derive(Clone)]
720 struct TestCallbacks {
721 accounts_map: HashMap<Pubkey, (AccountSharedData, Slot)>,
722 #[allow(clippy::type_complexity)]
723 inspected_accounts:
724 RefCell<HashMap<Pubkey, Vec<(Option<AccountSharedData>, bool)>>>,
725 feature_set: SVMFeatureSet,
726 }
727
728 impl Default for TestCallbacks {
729 fn default() -> Self {
730 Self {
731 accounts_map: HashMap::default(),
732 inspected_accounts: RefCell::default(),
733 feature_set: SVMFeatureSet::all_enabled(),
734 }
735 }
736 }
737
738 impl TransactionProcessingCallback for TestCallbacks {
739 fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
740 self.accounts_map
741 .get(pubkey)
742 .map(|(account, slot)| (account.clone(), *slot))
743 }
744
745 fn inspect_account(
746 &self,
747 address: &Pubkey,
748 account_state: AccountState,
749 is_writable: bool,
750 ) {
751 let account = match account_state {
752 AccountState::Dead => None,
753 AccountState::Alive(account) => Some(account.clone()),
754 };
755 self.inspected_accounts
756 .borrow_mut()
757 .entry(*address)
758 .or_default()
759 .push((account, is_writable));
760 }
761 }
762
763 impl<'a> From<&'a TestCallbacks> for AccountLoader<'a, TestCallbacks> {
764 fn from(callbacks: &'a TestCallbacks) -> AccountLoader<'a, TestCallbacks> {
765 AccountLoader::new_with_loaded_accounts_capacity(
766 None,
767 callbacks,
768 &callbacks.feature_set,
769 0,
770 )
771 }
772 }
773
774 fn load_accounts_with_features_and_rent(
775 tx: Transaction,
776 accounts: &[KeyedAccountSharedData],
777 rent: &Rent,
778 error_metrics: &mut TransactionErrorMetrics,
779 feature_set: SVMFeatureSet,
780 ) -> TransactionLoadResult {
781 let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
782 let fee_payer_account = accounts[0].1.clone();
783 let mut accounts_map = HashMap::new();
784 for (pubkey, account) in accounts {
785 accounts_map.insert(*pubkey, (account.clone(), 1));
786 }
787 let callbacks = TestCallbacks {
788 accounts_map,
789 ..Default::default()
790 };
791 let mut account_loader: AccountLoader<TestCallbacks> = (&callbacks).into();
792 account_loader.feature_set = &feature_set;
793 load_transaction(
794 &mut account_loader,
795 &sanitized_tx,
796 Ok(ValidatedTransactionDetails {
797 loaded_fee_payer_account: LoadedTransactionAccount {
798 account: fee_payer_account,
799 ..LoadedTransactionAccount::default()
800 },
801 ..ValidatedTransactionDetails::default()
802 }),
803 error_metrics,
804 rent,
805 )
806 }
807
808 fn new_unchecked_sanitized_message(message: Message) -> SanitizedMessage {
809 SanitizedMessage::Legacy(LegacyMessage::new(message, &HashSet::new()))
810 }
811
812 #[test]
813 fn test_update_accounts_for_successful_tx_skips_untouched() {
814 let fee_payer = Keypair::new();
815 let (touched_key, untouched_key) = (Pubkey::new_unique(), Pubkey::new_unique());
816 let program = Pubkey::new_unique();
817
818 let instructions = vec![CompiledInstruction::new(3, &(), vec![1, 2])];
820 let message = new_unchecked_sanitized_message(Message::new_with_compiled_instructions(
821 1, 0, 1, vec![fee_payer.pubkey(), touched_key, untouched_key, program],
825 Hash::default(),
826 instructions,
827 ));
828 let transaction_accounts = [
829 (
830 fee_payer.pubkey(),
831 AccountSharedData::new(100, 0, &Pubkey::default()),
832 ),
833 (
834 touched_key,
835 AccountSharedData::new(1, 0, &Pubkey::default()),
836 ),
837 (
838 untouched_key,
839 AccountSharedData::new(2, 0, &Pubkey::default()),
840 ),
841 (program, AccountSharedData::new(3, 0, &Pubkey::default())),
842 ];
843
844 let touched_flags: Box<[bool]> = [true, true, false, false].into();
847
848 let callbacks = TestCallbacks::default();
849 let mut account_loader: AccountLoader<TestCallbacks> = (&callbacks).into();
850 account_loader.update_accounts_for_successful_tx(
851 &message,
852 &transaction_accounts,
853 &touched_flags,
854 5,
855 );
856
857 assert!(
859 account_loader
860 .loaded_accounts
861 .contains_key(&fee_payer.pubkey())
862 );
863 assert!(account_loader.loaded_accounts.contains_key(&touched_key));
864 assert!(!account_loader.loaded_accounts.contains_key(&untouched_key));
865 assert!(!account_loader.loaded_accounts.contains_key(&program));
866 }
867
868 #[test]
869 fn test_load_accounts_unknown_program_id() {
870 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
871 let mut error_metrics = TransactionErrorMetrics::default();
872
873 let keypair = Keypair::new();
874 let key0 = keypair.pubkey();
875 let key1 = Pubkey::from([5u8; 32]);
876
877 let account = AccountSharedData::new(1, 0, &Pubkey::default());
878 accounts.push((key0, account));
879
880 let account = AccountSharedData::new(2, 1, &Pubkey::default());
881 accounts.push((key1, account));
882
883 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
884 let tx = Transaction::new_with_compiled_instructions(
885 &[&keypair],
886 &[],
887 Hash::default(),
888 vec![Pubkey::default()],
889 instructions,
890 );
891
892 let feature_set = SVMFeatureSet::all_enabled();
893 let load_results = load_accounts_with_features_and_rent(
894 tx,
895 &accounts,
896 &Rent::default(),
897 &mut error_metrics,
898 feature_set,
899 );
900
901 assert_eq!(error_metrics.account_not_found.0, 1);
902 assert!(matches!(
903 load_results,
904 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
905 load_error: TransactionError::ProgramAccountNotFound,
906 ..
907 }),
908 ));
909 }
910
911 #[test]
912 fn test_load_accounts_no_loaders() {
913 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
914 let mut error_metrics = TransactionErrorMetrics::default();
915
916 let keypair = Keypair::new();
917 let key0 = keypair.pubkey();
918 let key1 = Pubkey::from([5u8; 32]);
919
920 let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
921 account.set_rent_epoch(1);
922 accounts.push((key0, account));
923
924 let mut account = AccountSharedData::new(2, 1, &Pubkey::default());
925 account.set_rent_epoch(1);
926 accounts.push((key1, account));
927
928 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
929 let tx = Transaction::new_with_compiled_instructions(
930 &[&keypair],
931 &[key1],
932 Hash::default(),
933 vec![native_loader::id()],
934 instructions,
935 );
936
937 let feature_set = SVMFeatureSet::all_enabled();
938 let loaded_accounts = load_accounts_with_features_and_rent(
939 tx,
940 &accounts,
941 &Rent::default(),
942 &mut error_metrics,
943 feature_set,
944 );
945
946 match &loaded_accounts {
947 TransactionLoadResult::FeesOnly(fees_only_tx) => {
948 assert_eq!(error_metrics.account_not_found.0, 1);
949 assert_eq!(
950 fees_only_tx.load_error,
951 TransactionError::ProgramAccountNotFound,
952 );
953 }
954 result => panic!("unexpected result: {result:?}"),
955 }
956 }
957
958 #[test]
959 fn test_load_accounts_bad_owner() {
960 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
961 let mut error_metrics = TransactionErrorMetrics::default();
962
963 let keypair = Keypair::new();
964 let key0 = keypair.pubkey();
965 let key1 = Pubkey::from([5u8; 32]);
966
967 let account = AccountSharedData::new(1, 0, &Pubkey::default());
968 accounts.push((key0, account));
969
970 let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
971 account.set_executable(true);
972 accounts.push((key1, account));
973
974 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
975 let tx = Transaction::new_with_compiled_instructions(
976 &[&keypair],
977 &[],
978 Hash::default(),
979 vec![key1],
980 instructions,
981 );
982
983 let feature_set = SVMFeatureSet::all_enabled();
984 let load_results = load_accounts_with_features_and_rent(
985 tx,
986 &accounts,
987 &Rent::default(),
988 &mut error_metrics,
989 feature_set,
990 );
991
992 assert_eq!(error_metrics.invalid_program_for_execution.0, 1);
993 assert!(matches!(
994 load_results,
995 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
996 load_error: TransactionError::InvalidProgramForExecution,
997 ..
998 }),
999 ));
1000 }
1001
1002 #[test]
1003 fn test_load_accounts_not_executable() {
1004 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
1005 let mut error_metrics = TransactionErrorMetrics::default();
1006
1007 let keypair = Keypair::new();
1008 let key0 = keypair.pubkey();
1009 let key1 = Pubkey::from([5u8; 32]);
1010
1011 let account = AccountSharedData::new(1, 0, &Pubkey::default());
1012 accounts.push((key0, account));
1013
1014 let account = AccountSharedData::new(40, 0, &native_loader::id());
1015 accounts.push((key1, account));
1016
1017 let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
1018 let tx = Transaction::new_with_compiled_instructions(
1019 &[&keypair],
1020 &[],
1021 Hash::default(),
1022 vec![key1],
1023 instructions,
1024 );
1025
1026 let feature_set = SVMFeatureSet::all_enabled();
1027 let load_results = load_accounts_with_features_and_rent(
1028 tx,
1029 &accounts,
1030 &Rent::default(),
1031 &mut error_metrics,
1032 feature_set,
1033 );
1034
1035 assert_eq!(error_metrics.invalid_program_for_execution.0, 0);
1036 match &load_results {
1037 TransactionLoadResult::Loaded(loaded_transaction) => {
1038 assert_eq!(loaded_transaction.accounts.len(), 2);
1039 assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
1040 assert_eq!(loaded_transaction.accounts[1].1, accounts[1].1);
1041 }
1042 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1043 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1044 }
1045 }
1046
1047 #[test]
1048 fn test_load_accounts_multiple_loaders() {
1049 let mut accounts: Vec<KeyedAccountSharedData> = Vec::new();
1050 let mut error_metrics = TransactionErrorMetrics::default();
1051
1052 let keypair = Keypair::new();
1053 let key0 = keypair.pubkey();
1054 let key1 = bpf_loader_upgradeable::id();
1055 let key2 = Pubkey::from([6u8; 32]);
1056
1057 let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
1058 account.set_rent_epoch(1);
1059 accounts.push((key0, account));
1060
1061 let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
1062 account.set_executable(true);
1063 account.set_rent_epoch(1);
1064 account.set_owner(native_loader::id());
1065 accounts.push((key1, account));
1066
1067 let mut account = AccountSharedData::new(41, 1, &Pubkey::default());
1068 account.set_executable(true);
1069 account.set_rent_epoch(1);
1070 account.set_owner(key1);
1071 accounts.push((key2, account));
1072
1073 let instructions = vec![
1074 CompiledInstruction::new(1, &(), vec![0]),
1075 CompiledInstruction::new(2, &(), vec![0]),
1076 ];
1077 let tx = Transaction::new_with_compiled_instructions(
1078 &[&keypair],
1079 &[],
1080 Hash::default(),
1081 vec![key1, key2],
1082 instructions,
1083 );
1084
1085 let feature_set = SVMFeatureSet::all_enabled();
1086 let loaded_accounts = load_accounts_with_features_and_rent(
1087 tx,
1088 &accounts,
1089 &Rent::default(),
1090 &mut error_metrics,
1091 feature_set,
1092 );
1093
1094 assert_eq!(error_metrics.account_not_found.0, 0);
1095 match &loaded_accounts {
1096 TransactionLoadResult::Loaded(loaded_transaction) => {
1097 assert_eq!(loaded_transaction.accounts.len(), 3);
1098 assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
1099 }
1100 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1101 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1102 }
1103 }
1104
1105 fn load_accounts_no_store(
1106 accounts: &[KeyedAccountSharedData],
1107 tx: Transaction,
1108 account_overrides: Option<&AccountOverrides>,
1109 ) -> TransactionLoadResult {
1110 let tx = SanitizedTransaction::from_transaction_for_tests(tx);
1111
1112 let mut error_metrics = TransactionErrorMetrics::default();
1113 let mut accounts_map = HashMap::new();
1114 for (pubkey, account) in accounts {
1115 accounts_map.insert(*pubkey, (account.clone(), 1));
1116 }
1117 let callbacks = TestCallbacks {
1118 accounts_map,
1119 ..Default::default()
1120 };
1121 let feature_set = SVMFeatureSet::all_enabled();
1122 let mut account_loader = AccountLoader::new_with_loaded_accounts_capacity(
1123 account_overrides,
1124 &callbacks,
1125 &feature_set,
1126 0,
1127 );
1128 load_transaction(
1129 &mut account_loader,
1130 &tx,
1131 Ok(ValidatedTransactionDetails::default()),
1132 &mut error_metrics,
1133 &Rent::default(),
1134 )
1135 }
1136
1137 #[test]
1138 fn test_instructions() {
1139 setup_test_logger();
1140 let instructions_key = solana_sdk_ids::sysvar::instructions::id();
1141 let keypair = Keypair::new();
1142 let instructions = vec![CompiledInstruction::new(1, &(), vec![0, 1])];
1143 let tx = Transaction::new_with_compiled_instructions(
1144 &[&keypair],
1145 &[solana_pubkey::new_rand(), instructions_key],
1146 Hash::default(),
1147 vec![native_loader::id()],
1148 instructions,
1149 );
1150
1151 let load_results = load_accounts_no_store(&[], tx, None);
1152 assert!(matches!(
1153 load_results,
1154 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
1155 load_error: TransactionError::ProgramAccountNotFound,
1156 ..
1157 }),
1158 ));
1159 }
1160
1161 #[test]
1162 fn test_overrides() {
1163 setup_test_logger();
1164 let mut account_overrides = AccountOverrides::default();
1165 let slot_history_id = sysvar::slot_history::id();
1166 let account = AccountSharedData::new(42, 0, &Pubkey::default());
1167 account_overrides.set_slot_history(Some(account));
1168
1169 let keypair = Keypair::new();
1170 let account = AccountSharedData::new(1_000_000, 0, &Pubkey::default());
1171
1172 let mut program_account = AccountSharedData::default();
1173 program_account.set_lamports(1);
1174 program_account.set_executable(true);
1175 program_account.set_owner(native_loader::id());
1176
1177 let instructions = vec![CompiledInstruction::new(2, &(), vec![0])];
1178 let tx = Transaction::new_with_compiled_instructions(
1179 &[&keypair],
1180 &[slot_history_id],
1181 Hash::default(),
1182 vec![bpf_loader::id()],
1183 instructions,
1184 );
1185
1186 let loaded_accounts = load_accounts_no_store(
1187 &[
1188 (keypair.pubkey(), account),
1189 (bpf_loader::id(), program_account),
1190 ],
1191 tx,
1192 Some(&account_overrides),
1193 );
1194 match &loaded_accounts {
1195 TransactionLoadResult::Loaded(loaded_transaction) => {
1196 assert_eq!(loaded_transaction.accounts[0].0, keypair.pubkey());
1197 assert_eq!(loaded_transaction.accounts[1].0, slot_history_id);
1198 assert_eq!(loaded_transaction.accounts[1].1.lamports(), 42);
1199 }
1200 TransactionLoadResult::FeesOnly(fees_only_tx) => panic!("{}", fees_only_tx.load_error),
1201 TransactionLoadResult::NotLoaded(e) => panic!("{e}"),
1202 }
1203 }
1204
1205 #[test]
1206 fn test_increase_calculated_data_size() {
1207 let mut error_metrics = TransactionErrorMetrics::default();
1208 let data_size: usize = 123;
1209 let requested_data_size_limit = data_size as u32 + 1;
1210 let mut acc = LoadedTransactionDataSize::with_max_size(requested_data_size_limit);
1211
1212 assert!(
1214 acc.increase_calculated_data_size(data_size, &mut error_metrics)
1215 .is_ok()
1216 );
1217 assert_eq!(data_size as u32, u32::from(acc.clone()));
1218
1219 assert!(
1221 acc.increase_calculated_data_size(1, &mut error_metrics)
1222 .is_ok()
1223 );
1224 assert_eq!(requested_data_size_limit, u32::from(acc.clone()));
1225
1226 assert_eq!(
1229 acc.increase_calculated_data_size(1, &mut error_metrics),
1230 Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
1231 );
1232 assert_eq!(requested_data_size_limit, u32::from(acc));
1233
1234 let mut acc = LoadedTransactionDataSize::with_max_size(requested_data_size_limit);
1235
1236 assert_eq!(
1239 acc.increase_calculated_data_size(u32::MAX as usize + 1, &mut error_metrics),
1240 Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
1241 );
1242 assert_eq!(requested_data_size_limit, u32::from(acc));
1243 }
1244
1245 struct ValidateFeePayerTestParameter {
1246 is_nonce: bool,
1247 payer_init_balance: u64,
1248 fee: u64,
1249 relax_post_exec_min_balance_check: bool,
1250 expected_result: Result<()>,
1251 payer_post_balance: u64,
1252 }
1253 fn validate_fee_payer_account(test_parameter: ValidateFeePayerTestParameter, rent: &Rent) {
1254 let mut account = if test_parameter.is_nonce {
1255 AccountSharedData::new_data(
1256 test_parameter.payer_init_balance,
1257 &NonceVersions::new(NonceState::Initialized(nonce::state::Data::default())),
1258 &system_program::id(),
1259 )
1260 .unwrap()
1261 } else {
1262 AccountSharedData::new(test_parameter.payer_init_balance, 0, &system_program::id())
1263 };
1264 let result = validate_fee_payer(
1265 &mut account,
1266 0,
1267 &mut TransactionErrorMetrics::default(),
1268 rent,
1269 test_parameter.fee,
1270 test_parameter.relax_post_exec_min_balance_check,
1271 );
1272
1273 assert_eq!(result, test_parameter.expected_result);
1274 assert_eq!(account.lamports(), test_parameter.payer_post_balance);
1275 }
1276
1277 #[test]
1278 fn test_validate_fee_payer() {
1279 let rent = Rent::default();
1280 let nonce_min_balance = rent.minimum_balance(NonceState::size());
1281 let system_min_balance = rent.minimum_balance(0);
1282 let fee = 5_000;
1283
1284 {
1287 for relax_post_exec_min_balance_check in [false, true] {
1288 for (is_nonce, min_balance) in [
1289 (true, nonce_min_balance),
1290 (false, system_min_balance), (false, 0), ] {
1293 validate_fee_payer_account(
1294 ValidateFeePayerTestParameter {
1295 is_nonce,
1296 payer_init_balance: min_balance + fee,
1297 fee,
1298 relax_post_exec_min_balance_check,
1299 expected_result: Ok(()),
1300 payer_post_balance: min_balance,
1301 },
1302 &rent,
1303 );
1304 }
1305 }
1306 }
1307
1308 {
1311 for relax_post_exec_min_balance_check in [false, true] {
1312 for is_nonce in [true, false] {
1313 validate_fee_payer_account(
1314 ValidateFeePayerTestParameter {
1315 is_nonce,
1316 payer_init_balance: 0,
1317 fee,
1318 relax_post_exec_min_balance_check,
1319 expected_result: Err(TransactionError::AccountNotFound),
1320 payer_post_balance: 0,
1321 },
1322 &rent,
1323 );
1324 }
1325 }
1326 }
1327
1328 {
1332 for relax_post_exec_min_balance_check in [false, true] {
1333 for (is_nonce, payer_init_balance, expected_result) in [
1334 (
1336 true,
1337 nonce_min_balance + fee - 1,
1338 Err(TransactionError::InsufficientFundsForFee),
1339 ),
1340 (
1342 true,
1343 nonce_min_balance - 1,
1344 Err(TransactionError::InsufficientFundsForFee),
1345 ),
1346 (
1348 false,
1349 fee - 1,
1350 Err(TransactionError::InsufficientFundsForFee),
1351 ),
1352 (
1357 false,
1358 system_min_balance - 1,
1359 if relax_post_exec_min_balance_check {
1360 Err(TransactionError::InsufficientFundsForRent { account_index: 0 })
1361 } else {
1362 Ok(())
1363 },
1364 ),
1365 (
1367 false,
1368 system_min_balance + fee - 1,
1369 Err(TransactionError::InsufficientFundsForRent { account_index: 0 }),
1370 ),
1371 ] {
1372 let expected_post_balance = if matches!(
1376 expected_result,
1377 Err(TransactionError::InsufficientFundsForFee)
1378 ) {
1379 payer_init_balance
1380 } else {
1381 payer_init_balance - fee
1382 };
1383 validate_fee_payer_account(
1384 ValidateFeePayerTestParameter {
1385 is_nonce,
1386 payer_init_balance,
1387 fee,
1388 relax_post_exec_min_balance_check,
1389 expected_result,
1390 payer_post_balance: expected_post_balance,
1391 },
1392 &rent,
1393 );
1394 }
1395 }
1396 }
1397
1398 {
1401 let fee = nonce_min_balance;
1403 for relax_post_exec_min_balance_check in [false, true] {
1404 validate_fee_payer_account(
1405 ValidateFeePayerTestParameter {
1406 is_nonce: true,
1407 payer_init_balance: nonce_min_balance,
1408 fee,
1409 relax_post_exec_min_balance_check,
1410 expected_result: Err(TransactionError::InsufficientFundsForFee),
1411 payer_post_balance: nonce_min_balance,
1412 },
1413 &rent,
1414 );
1415 }
1416 }
1417
1418 {
1421 for relax_post_exec_min_balance_check in [false, true] {
1422 validate_fee_payer_account(
1423 ValidateFeePayerTestParameter {
1424 is_nonce: false,
1425 payer_init_balance: u64::MAX,
1426 fee: u64::MAX,
1427 relax_post_exec_min_balance_check,
1428 expected_result: Ok(()),
1429 payer_post_balance: 0,
1430 },
1431 &rent,
1432 );
1433 }
1434 }
1435 }
1436
1437 #[test]
1438 fn test_validate_nonce_fee_payer_with_checked_arithmetic() {
1439 let rent = Rent {
1440 lamports_per_byte: 1,
1441 ..Rent::default()
1442 };
1443
1444 for relax_post_exec_min_balance_check in [false, true] {
1447 validate_fee_payer_account(
1448 ValidateFeePayerTestParameter {
1449 is_nonce: true,
1450 payer_init_balance: u64::MAX,
1451 fee: u64::MAX,
1452 relax_post_exec_min_balance_check,
1453 expected_result: Err(TransactionError::InsufficientFundsForFee),
1454 payer_post_balance: u64::MAX,
1455 },
1456 &rent,
1457 );
1458 }
1459 }
1460
1461 #[test]
1462 fn test_construct_instructions_account() {
1463 let loaded_message = LoadedMessage {
1464 message: Cow::Owned(solana_message::v0::Message::default()),
1465 loaded_addresses: Cow::Owned(LoadedAddresses::default()),
1466 is_writable_account_cache: vec![false],
1467 };
1468 let message = SanitizedMessage::V0(loaded_message);
1469 let shared_data = construct_instructions_account(&message).unwrap();
1470 let expected = AccountSharedData::from(Account {
1471 data: construct_instructions_data(&message.decompile_instructions()).unwrap(),
1472 owner: sysvar::id(),
1473 ..Account::default()
1474 });
1475 assert_eq!(shared_data, expected);
1476 }
1477
1478 #[test]
1479 fn test_load_transaction_accounts_fee_payer() {
1480 let fee_payer_address = Pubkey::new_unique();
1481 let message = Message {
1482 account_keys: vec![fee_payer_address],
1483 header: MessageHeader::default(),
1484 instructions: vec![],
1485 recent_blockhash: Hash::default(),
1486 };
1487
1488 let sanitized_message = new_unchecked_sanitized_message(message);
1489 let mut mock_bank = TestCallbacks::default();
1490
1491 let fee_payer_balance = 200;
1492 let mut fee_payer_account = AccountSharedData::default();
1493 fee_payer_account.set_lamports(fee_payer_balance);
1494 mock_bank
1495 .accounts_map
1496 .insert(fee_payer_address, (fee_payer_account.clone(), 1));
1497 let mut account_loader = (&mock_bank).into();
1498
1499 let mut error_metrics = TransactionErrorMetrics::default();
1500
1501 let mut loaded_transaction_data_size =
1502 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1503
1504 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1505 sanitized_message,
1506 vec![Signature::new_unique()],
1507 false,
1508 );
1509 let result = load_transaction_accounts(
1510 &mut account_loader,
1511 sanitized_transaction.message(),
1512 LoadedTransactionAccount {
1513 loaded_size: fee_payer_account.data().len(),
1514 account: fee_payer_account.clone(),
1515 },
1516 &mut loaded_transaction_data_size,
1517 &mut error_metrics,
1518 &Rent::default(),
1519 );
1520 assert_eq!(
1521 vec![(fee_payer_address, fee_payer_account)],
1522 result.unwrap(),
1523 );
1524 assert_eq!(0, loaded_transaction_data_size.loaded_accounts_data_size);
1525 }
1526
1527 #[test]
1528 fn test_load_transaction_accounts_native_loader() {
1529 let key1 = Keypair::new();
1530 let message = Message {
1531 account_keys: vec![key1.pubkey(), native_loader::id()],
1532 header: MessageHeader::default(),
1533 instructions: vec![CompiledInstruction {
1534 program_id_index: 1,
1535 accounts: vec![0],
1536 data: vec![],
1537 }],
1538 recent_blockhash: Hash::default(),
1539 };
1540
1541 let sanitized_message = new_unchecked_sanitized_message(message);
1542 let mut mock_bank = TestCallbacks::default();
1543 mock_bank
1544 .accounts_map
1545 .insert(native_loader::id(), (AccountSharedData::default(), 0));
1546 let mut fee_payer_account = AccountSharedData::default();
1547 fee_payer_account.set_lamports(200);
1548 mock_bank
1549 .accounts_map
1550 .insert(key1.pubkey(), (fee_payer_account.clone(), 1));
1551 let mut account_loader = (&mock_bank).into();
1552
1553 let mut error_metrics = TransactionErrorMetrics::default();
1554
1555 let mut loaded_transaction_data_size =
1556 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1557
1558 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1559 sanitized_message,
1560 vec![Signature::new_unique()],
1561 false,
1562 );
1563
1564 let result = load_transaction_accounts(
1565 &mut account_loader,
1566 sanitized_transaction.message(),
1567 LoadedTransactionAccount {
1568 account: fee_payer_account.clone(),
1569 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
1570 },
1571 &mut loaded_transaction_data_size,
1572 &mut error_metrics,
1573 &Rent::default(),
1574 );
1575
1576 assert_eq!(
1577 result.unwrap_err(),
1578 TransactionError::ProgramAccountNotFound
1579 );
1580 }
1581
1582 #[test]
1583 fn test_load_transaction_accounts_program_account_no_data() {
1584 let key1 = Keypair::new();
1585 let key2 = Keypair::new();
1586
1587 let message = Message {
1588 account_keys: vec![key1.pubkey(), key2.pubkey()],
1589 header: MessageHeader::default(),
1590 instructions: vec![CompiledInstruction {
1591 program_id_index: 1,
1592 accounts: vec![0, 1],
1593 data: vec![],
1594 }],
1595 recent_blockhash: Hash::default(),
1596 };
1597
1598 let sanitized_message = new_unchecked_sanitized_message(message);
1599 let mut mock_bank = TestCallbacks::default();
1600 let mut account_data = AccountSharedData::default();
1601 account_data.set_lamports(200);
1602 mock_bank
1603 .accounts_map
1604 .insert(key1.pubkey(), (account_data, 1));
1605 let mut account_loader = (&mock_bank).into();
1606
1607 let mut error_metrics = TransactionErrorMetrics::default();
1608
1609 let mut loaded_transaction_data_size =
1610 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1611
1612 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1613 sanitized_message,
1614 vec![Signature::new_unique()],
1615 false,
1616 );
1617 let result = load_transaction_accounts(
1618 &mut account_loader,
1619 sanitized_transaction.message(),
1620 LoadedTransactionAccount::default(),
1621 &mut loaded_transaction_data_size,
1622 &mut error_metrics,
1623 &Rent::default(),
1624 );
1625
1626 assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1627 }
1628
1629 #[test]
1630 fn test_load_transaction_accounts_invalid_program_for_execution() {
1631 let key1 = Keypair::new();
1632 let key2 = Keypair::new();
1633
1634 let message = Message {
1635 account_keys: vec![key1.pubkey(), key2.pubkey()],
1636 header: MessageHeader::default(),
1637 instructions: vec![CompiledInstruction {
1638 program_id_index: 0,
1639 accounts: vec![0, 1],
1640 data: vec![],
1641 }],
1642 recent_blockhash: Hash::default(),
1643 };
1644
1645 let sanitized_message = new_unchecked_sanitized_message(message);
1646 let mut mock_bank = TestCallbacks::default();
1647 let mut account_data = AccountSharedData::default();
1648 account_data.set_lamports(200);
1649 mock_bank
1650 .accounts_map
1651 .insert(key1.pubkey(), (account_data, 1));
1652 let mut account_loader = (&mock_bank).into();
1653
1654 let mut error_metrics = TransactionErrorMetrics::default();
1655
1656 let mut loaded_transaction_data_size =
1657 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1658
1659 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1660 sanitized_message,
1661 vec![Signature::new_unique()],
1662 false,
1663 );
1664 let result = load_transaction_accounts(
1665 &mut account_loader,
1666 sanitized_transaction.message(),
1667 LoadedTransactionAccount::default(),
1668 &mut loaded_transaction_data_size,
1669 &mut error_metrics,
1670 &Rent::default(),
1671 );
1672
1673 assert_eq!(
1674 result.err(),
1675 Some(TransactionError::InvalidProgramForExecution)
1676 );
1677 }
1678
1679 #[test]
1680 fn test_load_transaction_accounts_native_loader_owner() {
1681 let key1 = Keypair::new();
1682 let key2 = Keypair::new();
1683
1684 let message = Message {
1685 account_keys: vec![key2.pubkey(), key1.pubkey()],
1686 header: MessageHeader::default(),
1687 instructions: vec![CompiledInstruction {
1688 program_id_index: 1,
1689 accounts: vec![0],
1690 data: vec![],
1691 }],
1692 recent_blockhash: Hash::default(),
1693 };
1694
1695 let sanitized_message = new_unchecked_sanitized_message(message);
1696 let mut mock_bank = TestCallbacks::default();
1697 let mut account_data = AccountSharedData::default();
1698 account_data.set_owner(native_loader::id());
1699 account_data.set_lamports(1);
1700 account_data.set_executable(true);
1701 mock_bank
1702 .accounts_map
1703 .insert(key1.pubkey(), (account_data, 1));
1704
1705 let mut fee_payer_account = AccountSharedData::default();
1706 fee_payer_account.set_lamports(200);
1707 mock_bank
1708 .accounts_map
1709 .insert(key2.pubkey(), (fee_payer_account.clone(), 1));
1710 let mut account_loader = (&mock_bank).into();
1711
1712 let mut error_metrics = TransactionErrorMetrics::default();
1713
1714 let mut loaded_transaction_data_size =
1715 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1716
1717 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1718 sanitized_message,
1719 vec![Signature::new_unique()],
1720 false,
1721 );
1722
1723 let result = load_transaction_accounts(
1724 &mut account_loader,
1725 sanitized_transaction.message(),
1726 LoadedTransactionAccount {
1727 account: fee_payer_account.clone(),
1728 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
1729 },
1730 &mut loaded_transaction_data_size,
1731 &mut error_metrics,
1732 &Rent::default(),
1733 );
1734
1735 let expected_loaded_accounts_data_size = TRANSACTION_ACCOUNT_BASE_SIZE as u32 * 2;
1736
1737 assert_eq!(
1738 vec![
1739 (key2.pubkey(), fee_payer_account),
1740 (
1741 key1.pubkey(),
1742 mock_bank.accounts_map[&key1.pubkey()].0.clone()
1743 ),
1744 ],
1745 result.unwrap(),
1746 );
1747 assert_eq!(
1748 expected_loaded_accounts_data_size,
1749 loaded_transaction_data_size.loaded_accounts_data_size
1750 );
1751 }
1752
1753 #[test]
1754 fn test_load_transaction_accounts_program_account_not_found_after_all_checks() {
1755 let key1 = Keypair::new();
1756 let key2 = Keypair::new();
1757
1758 let message = Message {
1759 account_keys: vec![key2.pubkey(), key1.pubkey()],
1760 header: MessageHeader::default(),
1761 instructions: vec![CompiledInstruction {
1762 program_id_index: 1,
1763 accounts: vec![0],
1764 data: vec![],
1765 }],
1766 recent_blockhash: Hash::default(),
1767 };
1768
1769 let sanitized_message = new_unchecked_sanitized_message(message);
1770 let mut mock_bank = TestCallbacks::default();
1771 let mut account_data = AccountSharedData::default();
1772 account_data.set_executable(true);
1773 mock_bank
1774 .accounts_map
1775 .insert(key1.pubkey(), (account_data, 1));
1776
1777 let mut account_data = AccountSharedData::default();
1778 account_data.set_lamports(200);
1779 mock_bank
1780 .accounts_map
1781 .insert(key2.pubkey(), (account_data, 1));
1782 let mut account_loader = (&mock_bank).into();
1783
1784 let mut error_metrics = TransactionErrorMetrics::default();
1785
1786 let mut loaded_transaction_data_size =
1787 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1788
1789 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1790 sanitized_message,
1791 vec![Signature::new_unique()],
1792 false,
1793 );
1794 let result = load_transaction_accounts(
1795 &mut account_loader,
1796 sanitized_transaction.message(),
1797 LoadedTransactionAccount::default(),
1798 &mut loaded_transaction_data_size,
1799 &mut error_metrics,
1800 &Rent::default(),
1801 );
1802
1803 assert_eq!(result.err(), Some(TransactionError::ProgramAccountNotFound));
1804 }
1805
1806 #[test]
1807 fn test_load_transaction_accounts_program_account_invalid_program_for_execution_last_check() {
1808 let key1 = Keypair::new();
1809 let key2 = Keypair::new();
1810 let key3 = Keypair::new();
1811
1812 let message = Message {
1813 account_keys: vec![key2.pubkey(), key1.pubkey()],
1814 header: MessageHeader::default(),
1815 instructions: vec![CompiledInstruction {
1816 program_id_index: 1,
1817 accounts: vec![0],
1818 data: vec![],
1819 }],
1820 recent_blockhash: Hash::default(),
1821 };
1822
1823 let sanitized_message = new_unchecked_sanitized_message(message);
1824 let mut mock_bank = TestCallbacks::default();
1825 let mut account_data = AccountSharedData::default();
1826 account_data.set_lamports(1);
1827 account_data.set_executable(true);
1828 account_data.set_owner(key3.pubkey());
1829 mock_bank
1830 .accounts_map
1831 .insert(key1.pubkey(), (account_data, 1));
1832
1833 let mut account_data = AccountSharedData::default();
1834 account_data.set_lamports(200);
1835 mock_bank
1836 .accounts_map
1837 .insert(key2.pubkey(), (account_data, 1));
1838 mock_bank
1839 .accounts_map
1840 .insert(key3.pubkey(), (AccountSharedData::default(), 0));
1841 let mut account_loader = (&mock_bank).into();
1842
1843 let mut error_metrics = TransactionErrorMetrics::default();
1844
1845 let mut loaded_transaction_data_size =
1846 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1847
1848 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1849 sanitized_message,
1850 vec![Signature::new_unique()],
1851 false,
1852 );
1853
1854 let result = load_transaction_accounts(
1855 &mut account_loader,
1856 sanitized_transaction.message(),
1857 LoadedTransactionAccount::default(),
1858 &mut loaded_transaction_data_size,
1859 &mut error_metrics,
1860 &Rent::default(),
1861 );
1862
1863 assert_eq!(
1864 result.err(),
1865 Some(TransactionError::InvalidProgramForExecution)
1866 );
1867 }
1868
1869 #[test]
1870 fn test_load_transaction_accounts_program_success_complete() {
1871 let key1 = Keypair::new();
1872 let key2 = Keypair::new();
1873
1874 let message = Message {
1875 account_keys: vec![key2.pubkey(), key1.pubkey()],
1876 header: MessageHeader::default(),
1877 instructions: vec![CompiledInstruction {
1878 program_id_index: 1,
1879 accounts: vec![0],
1880 data: vec![],
1881 }],
1882 recent_blockhash: Hash::default(),
1883 };
1884
1885 let sanitized_message = new_unchecked_sanitized_message(message);
1886 let mut mock_bank = TestCallbacks::default();
1887 let mut account_data = AccountSharedData::default();
1888 account_data.set_lamports(1);
1889 account_data.set_executable(true);
1890 account_data.set_owner(bpf_loader::id());
1891 mock_bank
1892 .accounts_map
1893 .insert(key1.pubkey(), (account_data, 1));
1894
1895 let mut fee_payer_account = AccountSharedData::default();
1896 fee_payer_account.set_lamports(200);
1897 mock_bank
1898 .accounts_map
1899 .insert(key2.pubkey(), (fee_payer_account.clone(), 1));
1900
1901 let mut account_data = AccountSharedData::default();
1902 account_data.set_lamports(1);
1903 account_data.set_executable(true);
1904 account_data.set_owner(native_loader::id());
1905 mock_bank
1906 .accounts_map
1907 .insert(bpf_loader::id(), (account_data, 0));
1908 let mut account_loader = (&mock_bank).into();
1909
1910 let mut error_metrics = TransactionErrorMetrics::default();
1911
1912 let sanitized_transaction = SanitizedTransaction::new_for_tests(
1913 sanitized_message,
1914 vec![Signature::new_unique()],
1915 false,
1916 );
1917
1918 let mut loaded_transaction_data_size =
1919 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
1920
1921 let result = load_transaction_accounts(
1922 &mut account_loader,
1923 sanitized_transaction.message(),
1924 LoadedTransactionAccount {
1925 account: fee_payer_account.clone(),
1926 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
1927 },
1928 &mut loaded_transaction_data_size,
1929 &mut error_metrics,
1930 &Rent::default(),
1931 );
1932
1933 let expected_loaded_accounts_data_size = TRANSACTION_ACCOUNT_BASE_SIZE as u32 * 2;
1934
1935 assert_eq!(
1936 vec![
1937 (key2.pubkey(), fee_payer_account),
1938 (
1939 key1.pubkey(),
1940 mock_bank.accounts_map[&key1.pubkey()].0.clone()
1941 ),
1942 ],
1943 result.unwrap(),
1944 );
1945 assert_eq!(
1946 expected_loaded_accounts_data_size,
1947 loaded_transaction_data_size.loaded_accounts_data_size
1948 );
1949 }
1950
1951 #[test]
1952 fn test_load_transaction_accounts_program_builtin_saturating_add() {
1953 let key1 = Keypair::new();
1954 let key2 = Keypair::new();
1955 let key3 = Keypair::new();
1956
1957 let message = Message {
1958 account_keys: vec![key2.pubkey(), key1.pubkey(), key3.pubkey()],
1959 header: MessageHeader::default(),
1960 instructions: vec![
1961 CompiledInstruction {
1962 program_id_index: 1,
1963 accounts: vec![0],
1964 data: vec![],
1965 },
1966 CompiledInstruction {
1967 program_id_index: 1,
1968 accounts: vec![2],
1969 data: vec![],
1970 },
1971 ],
1972 recent_blockhash: Hash::default(),
1973 };
1974
1975 let sanitized_message = new_unchecked_sanitized_message(message);
1976 let mut mock_bank = TestCallbacks::default();
1977 let mut account_data = AccountSharedData::default();
1978 account_data.set_lamports(1);
1979 account_data.set_executable(true);
1980 account_data.set_owner(bpf_loader::id());
1981 mock_bank
1982 .accounts_map
1983 .insert(key1.pubkey(), (account_data, 0));
1984
1985 let mut fee_payer_account = AccountSharedData::default();
1986 fee_payer_account.set_lamports(200);
1987 mock_bank
1988 .accounts_map
1989 .insert(key2.pubkey(), (fee_payer_account.clone(), 1));
1990
1991 let mut account_data = AccountSharedData::default();
1992 account_data.set_lamports(1);
1993 account_data.set_executable(true);
1994 account_data.set_owner(native_loader::id());
1995 mock_bank
1996 .accounts_map
1997 .insert(bpf_loader::id(), (account_data, 0));
1998 let mut account_loader = (&mock_bank).into();
1999
2000 let mut error_metrics = TransactionErrorMetrics::default();
2001
2002 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2003 sanitized_message,
2004 vec![Signature::new_unique()],
2005 false,
2006 );
2007
2008 let mut loaded_transaction_data_size =
2009 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
2010
2011 let result = load_transaction_accounts(
2012 &mut account_loader,
2013 sanitized_transaction.message(),
2014 LoadedTransactionAccount {
2015 account: fee_payer_account.clone(),
2016 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
2017 },
2018 &mut loaded_transaction_data_size,
2019 &mut error_metrics,
2020 &Rent::default(),
2021 );
2022
2023 let expected_loaded_accounts_data_size = TRANSACTION_ACCOUNT_BASE_SIZE as u32 * 2;
2024
2025 let mut account_data = AccountSharedData::default();
2026 account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2027 assert_eq!(
2028 vec![
2029 (key2.pubkey(), fee_payer_account),
2030 (
2031 key1.pubkey(),
2032 mock_bank.accounts_map[&key1.pubkey()].0.clone()
2033 ),
2034 (key3.pubkey(), account_data),
2035 ],
2036 result.unwrap(),
2037 );
2038 assert_eq!(
2039 expected_loaded_accounts_data_size,
2040 loaded_transaction_data_size.loaded_accounts_data_size
2041 );
2042 }
2043
2044 #[test]
2045 fn test_rent_state_list_len() {
2046 let mint_keypair = Keypair::new();
2047 let mut bank = TestCallbacks::default();
2048 let recipient = Pubkey::new_unique();
2049 let last_block_hash = Hash::new_unique();
2050
2051 let mut system_data = AccountSharedData::default();
2052 system_data.set_lamports(1);
2053 system_data.set_executable(true);
2054 system_data.set_owner(native_loader::id());
2055 bank.accounts_map
2056 .insert(Pubkey::new_from_array([0u8; 32]), (system_data, 0));
2057
2058 let mut mint_data = AccountSharedData::default();
2059 mint_data.set_lamports(2);
2060 bank.accounts_map
2061 .insert(mint_keypair.pubkey(), (mint_data, 0));
2062 bank.accounts_map
2063 .insert(recipient, (AccountSharedData::default(), 1));
2064 let mut account_loader = (&bank).into();
2065
2066 let tx = transfer(&mint_keypair, &recipient, LAMPORTS_PER_SOL, last_block_hash);
2067 let num_accounts = tx.message().account_keys.len();
2068 let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
2069 let mut error_metrics = TransactionErrorMetrics::default();
2070 let load_result = load_transaction(
2071 &mut account_loader,
2072 &sanitized_tx,
2073 Ok(ValidatedTransactionDetails::default()),
2074 &mut error_metrics,
2075 &Rent::default(),
2076 );
2077
2078 let TransactionLoadResult::Loaded(loaded_transaction) = load_result else {
2079 panic!("transaction loading failed");
2080 };
2081
2082 let compute_budget = SVMTransactionExecutionBudget {
2083 compute_unit_limit: u64::from(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT),
2084 ..SVMTransactionExecutionBudget::default()
2085 };
2086 let rent = Rent::default();
2087 let transaction_context = TransactionContext::new(
2088 loaded_transaction.accounts,
2089 rent.clone(),
2090 compute_budget.max_instruction_stack_depth,
2091 compute_budget.max_instruction_trace_length,
2092 1,
2093 );
2094
2095 let pre_account_state_info = TransactionAccountStateInfo::new_pre_exec(
2096 &transaction_context,
2097 sanitized_tx.message(),
2098 &rent,
2099 true,
2100 );
2101 assert_eq!(pre_account_state_info.len(), num_accounts);
2102
2103 assert_eq!(
2104 TransactionAccountStateInfo::new_post_exec(
2105 &transaction_context,
2106 sanitized_tx.message(),
2107 &pre_account_state_info,
2108 &rent,
2109 true,
2110 )
2111 .len(),
2112 num_accounts,
2113 );
2114 }
2115
2116 #[test]
2117 fn test_load_accounts_success() {
2118 let key1 = Keypair::new();
2119 let key2 = Keypair::new();
2120 let key3 = Keypair::new();
2121
2122 let message = Message {
2123 account_keys: vec![key2.pubkey(), key1.pubkey(), key3.pubkey()],
2124 header: MessageHeader::default(),
2125 instructions: vec![
2126 CompiledInstruction {
2127 program_id_index: 1,
2128 accounts: vec![0],
2129 data: vec![],
2130 },
2131 CompiledInstruction {
2132 program_id_index: 1,
2133 accounts: vec![2],
2134 data: vec![],
2135 },
2136 ],
2137 recent_blockhash: Hash::default(),
2138 };
2139
2140 let sanitized_message = new_unchecked_sanitized_message(message);
2141 let mut mock_bank = TestCallbacks::default();
2142 let mut account_data = AccountSharedData::default();
2143 account_data.set_lamports(1);
2144 account_data.set_executable(true);
2145 account_data.set_owner(bpf_loader::id());
2146 mock_bank
2147 .accounts_map
2148 .insert(key1.pubkey(), (account_data, 0));
2149
2150 let mut fee_payer_account = AccountSharedData::default();
2151 fee_payer_account.set_lamports(200);
2152 mock_bank
2153 .accounts_map
2154 .insert(key2.pubkey(), (fee_payer_account.clone(), 1));
2155
2156 let mut account_data = AccountSharedData::default();
2157 account_data.set_lamports(1);
2158 account_data.set_executable(true);
2159 account_data.set_owner(native_loader::id());
2160 mock_bank
2161 .accounts_map
2162 .insert(bpf_loader::id(), (account_data, 0));
2163 let mut account_loader = (&mock_bank).into();
2164
2165 let mut error_metrics = TransactionErrorMetrics::default();
2166
2167 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2168 sanitized_message,
2169 vec![Signature::new_unique()],
2170 false,
2171 );
2172
2173 let validation_result = Ok(ValidatedTransactionDetails {
2174 loaded_fee_payer_account: LoadedTransactionAccount {
2175 account: fee_payer_account,
2176 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
2177 },
2178 ..ValidatedTransactionDetails::default()
2179 });
2180
2181 let load_result = load_transaction(
2182 &mut account_loader,
2183 &sanitized_transaction,
2184 validation_result,
2185 &mut error_metrics,
2186 &Rent::default(),
2187 );
2188
2189 let loaded_accounts_data_size = TRANSACTION_ACCOUNT_BASE_SIZE as u32 * 2;
2190
2191 let mut account_data = AccountSharedData::default();
2192 account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
2193
2194 let TransactionLoadResult::Loaded(loaded_transaction) = load_result else {
2195 panic!("transaction loading failed");
2196 };
2197 assert_eq!(
2198 loaded_transaction,
2199 LoadedTransaction {
2200 accounts: vec![
2201 (
2202 key2.pubkey(),
2203 mock_bank.accounts_map[&key2.pubkey()].0.clone()
2204 ),
2205 (
2206 key1.pubkey(),
2207 mock_bank.accounts_map[&key1.pubkey()].0.clone()
2208 ),
2209 (key3.pubkey(), account_data),
2210 ],
2211 touched_flags: Box::default(),
2212 fee_details: FeeDetails::default(),
2213 rollback_accounts: RollbackAccounts::default(),
2214 compute_budget: SVMTransactionExecutionBudget::default(),
2215 loaded_accounts_data_size,
2216 }
2217 );
2218 }
2219
2220 #[test]
2221 fn test_load_accounts_error() {
2222 let mock_bank = TestCallbacks::default();
2223 let mut account_loader = (&mock_bank).into();
2224 let rent = Rent::default();
2225
2226 let message = Message {
2227 account_keys: vec![Pubkey::new_from_array([0; 32])],
2228 header: MessageHeader::default(),
2229 instructions: vec![CompiledInstruction {
2230 program_id_index: 0,
2231 accounts: vec![],
2232 data: vec![],
2233 }],
2234 recent_blockhash: Hash::default(),
2235 };
2236
2237 let sanitized_message = new_unchecked_sanitized_message(message);
2238 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2239 sanitized_message,
2240 vec![Signature::new_unique()],
2241 false,
2242 );
2243
2244 let validation_result = Ok(ValidatedTransactionDetails::default());
2245 let load_result = load_transaction(
2246 &mut account_loader,
2247 &sanitized_transaction,
2248 validation_result,
2249 &mut TransactionErrorMetrics::default(),
2250 &rent,
2251 );
2252
2253 assert!(matches!(
2254 load_result,
2255 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
2256 load_error: TransactionError::ProgramAccountNotFound,
2257 ..
2258 }),
2259 ));
2260
2261 let validation_result = Err(TransactionError::InvalidWritableAccount);
2262
2263 let load_result = load_transaction(
2264 &mut account_loader,
2265 &sanitized_transaction,
2266 validation_result,
2267 &mut TransactionErrorMetrics::default(),
2268 &rent,
2269 );
2270
2271 assert!(matches!(
2272 load_result,
2273 TransactionLoadResult::NotLoaded(TransactionError::InvalidWritableAccount),
2274 ));
2275 }
2276
2277 #[test]
2278 fn test_load_accounts_v1_instructions_sysvar_overflow() {
2279 const NUM_INSTRUCTIONS: usize = 64;
2280 const ACCOUNTS_PER_INSTRUCTION: usize = 31;
2281
2282 let fee_payer = Pubkey::new_unique();
2283 let instructions_sysvar = sysvar::instructions::id();
2284 let program = native_loader::id();
2285 let message = v1::Message::new(
2286 MessageHeader {
2287 num_required_signatures: 1,
2288 num_readonly_signed_accounts: 0,
2289 num_readonly_unsigned_accounts: 2,
2290 },
2291 v1::TransactionConfig::empty(),
2292 Hash::default(),
2293 vec![fee_payer, instructions_sysvar, program],
2294 vec![
2295 CompiledInstruction {
2296 program_id_index: 2,
2297 accounts: vec![1; ACCOUNTS_PER_INSTRUCTION],
2298 data: vec![],
2299 };
2300 NUM_INSTRUCTIONS
2301 ],
2302 );
2303 message.validate().unwrap();
2304 assert!(
2305 1 + message.size() + Signature::default().as_ref().len() <= v1::MAX_TRANSACTION_SIZE
2306 );
2307
2308 let sanitized_message =
2309 SanitizedMessage::V1(v1::CachedMessage::new(message, &HashSet::new()));
2310 let mock_bank = TestCallbacks::default();
2311 let mut account_loader = (&mock_bank).into();
2312
2313 let fee_payer_account = AccountSharedData::new(200, 0, &Pubkey::default());
2314 let load_result = load_transaction(
2315 &mut account_loader,
2316 &sanitized_message,
2317 Ok(ValidatedTransactionDetails {
2318 loaded_fee_payer_account: LoadedTransactionAccount {
2319 account: fee_payer_account,
2320 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE,
2321 },
2322 ..ValidatedTransactionDetails::default()
2323 }),
2324 &mut TransactionErrorMetrics::default(),
2325 &Rent::default(),
2326 );
2327
2328 assert!(matches!(
2329 load_result,
2330 TransactionLoadResult::FeesOnly(FeesOnlyTransaction {
2331 load_error: TransactionError::MaxLoadedAccountsDataSizeExceeded,
2332 ..
2333 }),
2334 ));
2335 }
2336
2337 #[test]
2338 fn test_update_rent_exempt_status_for_account() {
2339 let rent = Rent::default();
2340
2341 let min_exempt_balance = rent.minimum_balance(0);
2342 let mut account = AccountSharedData::from(Account {
2343 lamports: min_exempt_balance,
2344 ..Account::default()
2345 });
2346
2347 update_rent_exempt_status_for_account(&rent, &mut account);
2348 assert_eq!(account.rent_epoch(), RENT_EXEMPT_RENT_EPOCH);
2349 }
2350
2351 #[test]
2352 fn test_update_rent_exempt_status_for_rent_paying_account() {
2353 let rent = Rent::default();
2354
2355 let mut account = AccountSharedData::from(Account {
2356 lamports: 1,
2357 ..Account::default()
2358 });
2359
2360 update_rent_exempt_status_for_account(&rent, &mut account);
2361 assert_eq!(account.rent_epoch(), 0);
2362 assert_eq!(account.lamports(), 1);
2363 }
2364
2365 #[test]
2368 fn test_inspect_account_non_fee_payer() {
2369 let mut mock_bank = TestCallbacks::default();
2370
2371 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();
2377 account0.set_lamports(1_000_000_000);
2378 mock_bank
2379 .accounts_map
2380 .insert(address0, (account0.clone(), 1));
2381
2382 let mut account1 = AccountSharedData::default();
2383 account1.set_lamports(2_000_000_000);
2384 mock_bank
2385 .accounts_map
2386 .insert(address1, (account1.clone(), 1));
2387
2388 let mut account3 = AccountSharedData::default();
2391 account3.set_lamports(4_000_000_000);
2392 account3.set_executable(true);
2393 account3.set_owner(bpf_loader::id());
2394 mock_bank
2395 .accounts_map
2396 .insert(address3, (account3.clone(), 0));
2397 let mut account_loader = (&mock_bank).into();
2398
2399 let message = Message {
2400 account_keys: vec![address0, address1, address2, address3],
2401 header: MessageHeader::default(),
2402 instructions: vec![
2403 CompiledInstruction {
2404 program_id_index: 3,
2405 accounts: vec![0],
2406 data: vec![],
2407 },
2408 CompiledInstruction {
2409 program_id_index: 3,
2410 accounts: vec![1, 2],
2411 data: vec![],
2412 },
2413 CompiledInstruction {
2414 program_id_index: 3,
2415 accounts: vec![1],
2416 data: vec![],
2417 },
2418 ],
2419 recent_blockhash: Hash::new_unique(),
2420 };
2421 let sanitized_message = new_unchecked_sanitized_message(message);
2422 let sanitized_transaction = SanitizedTransaction::new_for_tests(
2423 sanitized_message,
2424 vec![Signature::new_unique()],
2425 false,
2426 );
2427 let validation_result = Ok(ValidatedTransactionDetails {
2428 loaded_fee_payer_account: LoadedTransactionAccount {
2429 account: account0.clone(),
2430 ..LoadedTransactionAccount::default()
2431 },
2432 ..ValidatedTransactionDetails::default()
2433 });
2434 let _load_results = load_transaction(
2435 &mut account_loader,
2436 &sanitized_transaction,
2437 validation_result,
2438 &mut TransactionErrorMetrics::default(),
2439 &Rent::default(),
2440 );
2441
2442 let mut actual_inspected_accounts: Vec<_> = mock_bank
2444 .inspected_accounts
2445 .borrow()
2446 .iter()
2447 .map(|(k, v)| (*k, v.clone()))
2448 .collect();
2449 actual_inspected_accounts.sort_unstable_by_key(|a| a.0);
2450
2451 let mut expected_inspected_accounts = vec![
2452 (address1, vec![(Some(account1), true)]),
2454 (address2, vec![(None, true)]),
2455 (address3, vec![(Some(account3), false)]),
2456 ];
2457 expected_inspected_accounts.sort_unstable_by_key(|a| a.0);
2458
2459 assert_eq!(actual_inspected_accounts, expected_inspected_accounts,);
2460 }
2461
2462 #[test]
2463 fn test_account_loader_wrappers() {
2464 let fee_payer = Pubkey::new_unique();
2465 let mut fee_payer_account = AccountSharedData::default();
2466 fee_payer_account.set_rent_epoch(u64::MAX);
2467 fee_payer_account.set_lamports(5000);
2468
2469 let mut mock_bank = TestCallbacks::default();
2470 mock_bank
2471 .accounts_map
2472 .insert(fee_payer, (fee_payer_account.clone(), 1));
2473
2474 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2476 assert_eq!(
2477 account_loader
2478 .load_transaction_account(&fee_payer, false)
2479 .unwrap()
2480 .account,
2481 fee_payer_account
2482 );
2483
2484 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2485 assert_eq!(
2486 account_loader
2487 .load_transaction_account(&fee_payer, true)
2488 .unwrap()
2489 .account,
2490 fee_payer_account
2491 );
2492
2493 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2494 assert_eq!(
2495 account_loader.load_account(&fee_payer).unwrap(),
2496 fee_payer_account
2497 );
2498
2499 let account_loader: AccountLoader<_> = (&mock_bank).into();
2500 assert_eq!(
2501 account_loader
2502 .get_account_shared_data(&fee_payer)
2503 .unwrap()
2504 .0,
2505 fee_payer_account
2506 );
2507
2508 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2510 account_loader.load_account(&fee_payer).unwrap();
2511
2512 assert_eq!(
2513 account_loader
2514 .load_transaction_account(&fee_payer, false)
2515 .unwrap()
2516 .account,
2517 fee_payer_account
2518 );
2519 assert_eq!(
2520 account_loader
2521 .load_transaction_account(&fee_payer, true)
2522 .unwrap()
2523 .account,
2524 fee_payer_account
2525 );
2526 assert_eq!(
2527 account_loader.load_account(&fee_payer).unwrap(),
2528 fee_payer_account
2529 );
2530 assert_eq!(
2531 account_loader
2532 .get_account_shared_data(&fee_payer)
2533 .unwrap()
2534 .0,
2535 fee_payer_account
2536 );
2537
2538 fee_payer_account.set_lamports(0);
2540 account_loader.update_accounts_for_failed_tx(
2541 &RollbackAccounts::FeePayerOnly {
2542 fee_payer: (fee_payer, fee_payer_account),
2543 },
2544 0,
2545 );
2546
2547 assert_eq!(
2548 account_loader.load_transaction_account(&fee_payer, false),
2549 None
2550 );
2551 assert_eq!(
2552 account_loader.load_transaction_account(&fee_payer, true),
2553 None
2554 );
2555 assert_eq!(account_loader.load_account(&fee_payer), None);
2556 assert_eq!(account_loader.get_account_shared_data(&fee_payer), None);
2557 }
2558
2559 #[test]
2562 fn test_load_transaction_accounts_data_sizes() {
2563 let mut rng = rand::rng();
2564 let mut mock_bank = TestCallbacks::default();
2565
2566 for _ in 0..128 {
2568 let account = AccountSharedData::create_from_existing_shared_data(
2569 1,
2570 Arc::new(vec![0; rng.random_range(0..128)]),
2571 Pubkey::new_unique(),
2572 rng.random(),
2573 u64::MAX,
2574 );
2575 mock_bank
2576 .accounts_map
2577 .insert(Pubkey::new_unique(), (account, 1));
2578 }
2579
2580 let mut fee_payers = vec![];
2582 for _ in 0..8 {
2583 let fee_payer = Pubkey::new_unique();
2584 let account = AccountSharedData::create_from_existing_shared_data(
2585 LAMPORTS_PER_SOL,
2586 Arc::new(vec![0; rng.random_range(0..32)]),
2587 system_program::id(),
2588 rng.random(),
2589 u64::MAX,
2590 );
2591 mock_bank.accounts_map.insert(fee_payer, (account, 1));
2592 fee_payers.push(fee_payer);
2593 }
2594
2595 let mut loader_owned_accounts = vec![];
2597 let mut programdata_tracker = AHashMap::new();
2598 for loader in PROGRAM_OWNERS {
2599 for _ in 0..16 {
2600 let program_id = Pubkey::new_unique();
2601 let mut account = AccountSharedData::create_from_existing_shared_data(
2602 1,
2603 Arc::new(vec![0; rng.random_range(0..512)]),
2604 *loader,
2605 rng.random(),
2606 u64::MAX,
2607 );
2608
2609 if *loader == bpf_loader_upgradeable::id() && account.data().len() >= 64 {
2615 let programdata_address = Pubkey::new_unique();
2616 let has_programdata = rng.random();
2617
2618 if has_programdata {
2619 let programdata_account =
2620 AccountSharedData::create_from_existing_shared_data(
2621 1,
2622 Arc::new(vec![0; rng.random_range(0..512)]),
2623 *loader,
2624 rng.random(),
2625 u64::MAX,
2626 );
2627 programdata_tracker.insert(
2628 program_id,
2629 (programdata_address, programdata_account.data().len()),
2630 );
2631 mock_bank
2632 .accounts_map
2633 .insert(programdata_address, (programdata_account, 1));
2634 loader_owned_accounts.push(programdata_address);
2635 }
2636
2637 if has_programdata || rng.random() {
2638 account
2639 .set_state(&UpgradeableLoaderState::Program {
2640 programdata_address,
2641 })
2642 .unwrap();
2643 }
2644 }
2645
2646 mock_bank.accounts_map.insert(program_id, (account, 1));
2647 loader_owned_accounts.push(program_id);
2648 }
2649 }
2650
2651 let mut all_accounts = mock_bank.accounts_map.keys().copied().collect::<Vec<_>>();
2652
2653 for _ in 0..32 {
2656 all_accounts.push(Pubkey::new_unique());
2657 }
2658
2659 let mut account_loader = (&mock_bank).into();
2660
2661 for _ in 0..1024 {
2672 let mut instructions = vec![];
2673 for _ in 0..rng.random_range(1..8) {
2674 let mut accounts = vec![];
2675 for _ in 0..rng.random_range(1..16) {
2676 all_accounts.shuffle(&mut rng);
2677 let pubkey = all_accounts[0];
2678
2679 accounts.push(AccountMeta {
2680 pubkey,
2681 is_writable: rng.random(),
2682 is_signer: rng.random() && rng.random(),
2683 });
2684 }
2685
2686 loader_owned_accounts.shuffle(&mut rng);
2687 let program_id = loader_owned_accounts[0];
2688 instructions.push(Instruction {
2689 accounts,
2690 program_id,
2691 data: vec![],
2692 });
2693 }
2694
2695 fee_payers.shuffle(&mut rng);
2696 let fee_payer = fee_payers[0];
2697 let fee_payer_account = mock_bank.accounts_map.get(&fee_payer).cloned().unwrap().0;
2698
2699 let transaction = SanitizedTransaction::from_transaction_for_tests(
2700 Transaction::new_with_payer(&instructions, Some(&fee_payer)),
2701 );
2702
2703 let mut expected_size = 0;
2704 let mut counted_programdatas = transaction
2705 .account_keys()
2706 .iter()
2707 .copied()
2708 .collect::<AHashSet<_>>();
2709
2710 for pubkey in transaction.account_keys().iter() {
2711 if let Some((account, _last_modification_slot)) = mock_bank.accounts_map.get(pubkey)
2712 {
2713 expected_size += TRANSACTION_ACCOUNT_BASE_SIZE + account.data().len();
2714 };
2715
2716 if let Some((programdata_address, programdata_size)) =
2717 programdata_tracker.get(pubkey)
2718 && counted_programdatas.get(programdata_address).is_none()
2719 {
2720 expected_size += TRANSACTION_ACCOUNT_BASE_SIZE + programdata_size;
2721 counted_programdatas.insert(*programdata_address);
2722 }
2723 }
2724
2725 assert!(expected_size <= MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get() as usize);
2726
2727 let mut loaded_transaction_data_size =
2728 LoadedTransactionDataSize::with_max_size(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get());
2729
2730 load_transaction_accounts(
2731 &mut account_loader,
2732 &transaction,
2733 LoadedTransactionAccount {
2734 loaded_size: TRANSACTION_ACCOUNT_BASE_SIZE + fee_payer_account.data().len(),
2735 account: fee_payer_account,
2736 },
2737 &mut loaded_transaction_data_size,
2738 &mut TransactionErrorMetrics::default(),
2739 &Rent::default(),
2740 )
2741 .unwrap();
2742
2743 assert_eq!(
2744 loaded_transaction_data_size.loaded_accounts_data_size,
2745 expected_size as u32,
2746 );
2747 }
2748 }
2749
2750 #[test]
2751 fn test_loader_aliasing() {
2752 let mut mock_bank = TestCallbacks::default();
2753
2754 let hit_address = Pubkey::new_unique();
2755 let miss_address = Pubkey::new_unique();
2756
2757 let expected_hit_account = AccountSharedData::default();
2758 mock_bank
2759 .accounts_map
2760 .insert(hit_address, (expected_hit_account.clone(), 1));
2761
2762 let mut account_loader: AccountLoader<_> = (&mock_bank).into();
2763
2764 account_loader.load_account(&hit_address);
2766 let actual_hit_account = account_loader.loaded_accounts.get(&hit_address);
2767
2768 assert_eq!(actual_hit_account.as_ref().unwrap().0, expected_hit_account);
2769 assert_eq!(actual_hit_account.as_ref().unwrap().1, 1);
2770 assert!(Arc::ptr_eq(
2771 &actual_hit_account.unwrap().0.data_clone(),
2772 &expected_hit_account.data_clone()
2773 ));
2774
2775 account_loader.load_account(&hit_address);
2777 let actual_hit_account = account_loader.loaded_accounts.get(&hit_address);
2778
2779 assert_eq!(actual_hit_account.as_ref().unwrap().0, expected_hit_account);
2780 assert_eq!(actual_hit_account.as_ref().unwrap().1, 1);
2781 assert!(Arc::ptr_eq(
2782 &actual_hit_account.unwrap().0.data_clone(),
2783 &expected_hit_account.data_clone()
2784 ));
2785
2786 account_loader.load_account(&miss_address);
2788 let expected_miss_account = account_loader
2789 .loaded_accounts
2790 .get(&miss_address)
2791 .unwrap()
2792 .clone();
2793
2794 assert!(!Arc::ptr_eq(
2795 &expected_miss_account.0.data_clone(),
2796 &expected_hit_account.data_clone()
2797 ));
2798
2799 account_loader.load_account(&miss_address);
2801 let actual_miss_account = account_loader.loaded_accounts.get(&miss_address);
2802
2803 assert_eq!(actual_miss_account, Some(&expected_miss_account));
2804 assert!(Arc::ptr_eq(
2805 &actual_miss_account.unwrap().0.data_clone(),
2806 &expected_miss_account.0.data_clone()
2807 ));
2808 }
2809}