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