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