1use {
2 super::Bank,
3 crate::bank::CollectorFeeDetails,
4 log::{debug, warn},
5 solana_feature_set::{remove_rounding_in_fee_calculation, reward_full_priority_fee},
6 solana_sdk::{
7 account::{ReadableAccount, WritableAccount},
8 fee::FeeBudgetLimits,
9 pubkey::Pubkey,
10 reward_info::RewardInfo,
11 reward_type::RewardType,
12 system_program,
13 transaction::SanitizedTransaction,
14 },
15 solana_svm_rent_collector::svm_rent_collector::SVMRentCollector,
16 solana_vote::vote_account::VoteAccountsHashMap,
17 std::{result::Result, sync::atomic::Ordering::Relaxed},
18 thiserror::Error,
19};
20
21#[derive(Error, Debug, PartialEq)]
22enum DepositFeeError {
23 #[error("fee account became rent paying")]
24 InvalidRentPayingAccount,
25 #[error("lamport overflow")]
26 LamportOverflow,
27 #[error("invalid fee account owner")]
28 InvalidAccountOwner,
29}
30
31impl Bank {
32 pub(super) fn distribute_transaction_fees(&self) {
47 let collector_fees = self.collector_fees.load(Relaxed);
48 if collector_fees != 0 {
49 let (deposit, mut burn) = self.calculate_reward_and_burn_fees(collector_fees);
50 if deposit > 0 {
51 self.deposit_or_burn_fee(deposit, &mut burn);
52 }
53 self.capitalization.fetch_sub(burn, Relaxed);
54 }
55 }
56
57 pub(super) fn distribute_transaction_fee_details(&self) {
60 let fee_details = self.collector_fee_details.read().unwrap();
61 if fee_details.total() == 0 {
62 return;
64 }
65
66 let (deposit, mut burn) = self.calculate_reward_and_burn_fee_details(&fee_details);
67
68 if deposit > 0 {
69 self.deposit_or_burn_fee(deposit, &mut burn);
70 }
71 self.capitalization.fetch_sub(burn, Relaxed);
72 }
73
74 pub fn calculate_reward_for_transaction(
75 &self,
76 transaction: &SanitizedTransaction,
77 fee_budget_limits: &FeeBudgetLimits,
78 ) -> u64 {
79 let fee_details = solana_fee::calculate_fee_details(
80 transaction,
81 self.get_lamports_per_signature() == 0,
82 self.fee_structure().lamports_per_signature,
83 fee_budget_limits.prioritization_fee,
84 self.feature_set
85 .is_active(&remove_rounding_in_fee_calculation::id()),
86 );
87 let (reward, _burn) = if self.feature_set.is_active(&reward_full_priority_fee::id()) {
88 self.calculate_reward_and_burn_fee_details(&CollectorFeeDetails::from(fee_details))
89 } else {
90 let fee = fee_details.total_fee();
91 self.calculate_reward_and_burn_fees(fee)
92 };
93 reward
94 }
95
96 fn calculate_reward_and_burn_fees(&self, fee: u64) -> (u64, u64) {
97 self.fee_rate_governor.burn(fee)
98 }
99
100 fn calculate_reward_and_burn_fee_details(
101 &self,
102 fee_details: &CollectorFeeDetails,
103 ) -> (u64, u64) {
104 let (deposit, burn) = if fee_details.transaction_fee != 0 {
105 self.fee_rate_governor.burn(fee_details.transaction_fee)
106 } else {
107 (0, 0)
108 };
109 (deposit.saturating_add(fee_details.priority_fee), burn)
110 }
111
112 fn deposit_or_burn_fee(&self, deposit: u64, burn: &mut u64) {
113 match self.deposit_fees(&self.collector_id, deposit) {
114 Ok(post_balance) => {
115 self.rewards.write().unwrap().push((
116 self.collector_id,
117 RewardInfo {
118 reward_type: RewardType::Fee,
119 lamports: deposit as i64,
120 post_balance,
121 commission: None,
122 },
123 ));
124 }
125 Err(err) => {
126 debug!(
127 "Burned {} lamport tx fee instead of sending to {} due to {}",
128 deposit, self.collector_id, err
129 );
130 datapoint_warn!(
131 "bank-burned_fee",
132 ("slot", self.slot(), i64),
133 ("num_lamports", deposit, i64),
134 ("error", err.to_string(), String),
135 );
136 *burn = burn.saturating_add(deposit);
137 }
138 }
139 }
140
141 fn deposit_fees(&self, pubkey: &Pubkey, fees: u64) -> Result<u64, DepositFeeError> {
143 let mut account = self
144 .get_account_with_fixed_root_no_cache(pubkey)
145 .unwrap_or_default();
146
147 if !system_program::check_id(account.owner()) {
148 return Err(DepositFeeError::InvalidAccountOwner);
149 }
150
151 let recipient_pre_rent_state = self.rent_collector().get_account_rent_state(&account);
152 let distribution = account.checked_add_lamports(fees);
153 if distribution.is_err() {
154 return Err(DepositFeeError::LamportOverflow);
155 }
156
157 let recipient_post_rent_state = self.rent_collector().get_account_rent_state(&account);
158 let rent_state_transition_allowed = self
159 .rent_collector()
160 .transition_allowed(&recipient_pre_rent_state, &recipient_post_rent_state);
161 if !rent_state_transition_allowed {
162 return Err(DepositFeeError::InvalidRentPayingAccount);
163 }
164
165 self.store_account(pubkey, &account);
166 Ok(account.lamports())
167 }
168
169 fn distribute_rent_to_validators(
190 &self,
191 vote_accounts: &VoteAccountsHashMap,
192 rent_to_be_distributed: u64,
193 ) {
194 let mut total_staked = 0;
195
196 let mut validator_stakes = vote_accounts
200 .iter()
201 .filter_map(|(_vote_pubkey, (staked, account))| {
202 if *staked == 0 {
203 None
204 } else {
205 total_staked += *staked;
206 Some((*account.node_pubkey(), *staked))
207 }
208 })
209 .collect::<Vec<(Pubkey, u64)>>();
210
211 #[cfg(test)]
212 if validator_stakes.is_empty() {
213 self.capitalization
215 .fetch_sub(rent_to_be_distributed, Relaxed);
216 return;
217 }
218 #[cfg(not(test))]
219 assert!(!validator_stakes.is_empty());
220
221 validator_stakes.sort_unstable_by(|(pubkey1, staked1), (pubkey2, staked2)| {
225 (staked1, pubkey1).cmp(&(staked2, pubkey2)).reverse()
226 });
227
228 let mut rent_distributed_in_initial_round = 0;
229 let validator_rent_shares = validator_stakes
230 .into_iter()
231 .map(|(pubkey, staked)| {
232 let rent_share = (((staked as u128) * (rent_to_be_distributed as u128))
233 / (total_staked as u128))
234 .try_into()
235 .unwrap();
236 rent_distributed_in_initial_round += rent_share;
237 (pubkey, rent_share)
238 })
239 .collect::<Vec<(Pubkey, u64)>>();
240
241 let mut leftover_lamports = rent_to_be_distributed - rent_distributed_in_initial_round;
244
245 let mut rent_to_burn: u64 = 0;
246 let mut rewards = vec![];
247 validator_rent_shares
248 .into_iter()
249 .for_each(|(pubkey, rent_share)| {
250 let rent_to_be_paid = if leftover_lamports > 0 {
251 leftover_lamports -= 1;
252 rent_share + 1
253 } else {
254 rent_share
255 };
256 if rent_to_be_paid > 0 {
257 match self.deposit_fees(&pubkey, rent_to_be_paid) {
258 Ok(post_balance) => {
259 rewards.push((
260 pubkey,
261 RewardInfo {
262 reward_type: RewardType::Rent,
263 lamports: rent_to_be_paid as i64,
264 post_balance,
265 commission: None,
266 },
267 ));
268 }
269 Err(err) => {
270 debug!(
271 "Burned {} lamport rent fee instead of sending to {} due to {}",
272 rent_to_be_paid, pubkey, err
273 );
274
275 rent_to_burn = rent_to_burn.saturating_add(rent_to_be_paid);
278 }
279 }
280 }
281 });
282 self.rewards.write().unwrap().append(&mut rewards);
283
284 if rent_to_burn > 0 {
285 self.capitalization.fetch_sub(rent_to_burn, Relaxed);
286 datapoint_warn!(
287 "bank-burned_rent",
288 ("slot", self.slot(), i64),
289 ("num_lamports", rent_to_burn, i64)
290 );
291 }
292
293 assert_eq!(leftover_lamports, 0);
294 }
295
296 pub(super) fn distribute_rent_fees(&self) {
297 let total_rent_collected = self.collected_rent.load(Relaxed);
298
299 if !self.should_collect_rent() {
300 if total_rent_collected != 0 {
301 warn!("Rent fees collection is disabled, yet total rent collected was non zero! Total rent collected: {total_rent_collected}");
302 }
303 return;
304 }
305
306 let (burned_portion, rent_to_be_distributed) = self
307 .rent_collector
308 .rent
309 .calculate_burn(total_rent_collected);
310
311 debug!(
312 "distributed rent: {} (rounded from: {}, burned: {})",
313 rent_to_be_distributed, total_rent_collected, burned_portion
314 );
315 self.capitalization.fetch_sub(burned_portion, Relaxed);
316
317 if rent_to_be_distributed == 0 {
318 return;
319 }
320
321 self.distribute_rent_to_validators(&self.vote_accounts(), rent_to_be_distributed);
322 }
323}
324
325#[cfg(test)]
326pub mod tests {
327 use {
328 super::*,
329 crate::genesis_utils::{
330 create_genesis_config, create_genesis_config_with_leader,
331 create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs,
332 },
333 solana_sdk::{
334 account::AccountSharedData, native_token::sol_to_lamports, pubkey, rent::Rent,
335 signature::Signer,
336 },
337 solana_svm_rent_collector::rent_state::RentState,
338 std::sync::RwLock,
339 };
340
341 #[test]
342 fn test_deposit_or_burn_fee() {
343 #[derive(PartialEq)]
344 enum Scenario {
345 Normal,
346 InvalidOwner,
347 RentPaying,
348 }
349
350 struct TestCase {
351 scenario: Scenario,
352 }
353
354 impl TestCase {
355 fn new(scenario: Scenario) -> Self {
356 Self { scenario }
357 }
358 }
359
360 for test_case in [
361 TestCase::new(Scenario::Normal),
362 TestCase::new(Scenario::InvalidOwner),
363 TestCase::new(Scenario::RentPaying),
364 ] {
365 let mut genesis = create_genesis_config(0);
366 let rent = Rent::default();
367 let min_rent_exempt_balance = rent.minimum_balance(0);
368 genesis.genesis_config.rent = rent; let bank = Bank::new_for_tests(&genesis.genesis_config);
370
371 let deposit = 100;
372 let mut burn = 100;
373
374 if test_case.scenario == Scenario::RentPaying {
375 let initial_balance = 100;
377 let account = AccountSharedData::new(initial_balance, 0, &system_program::id());
378 bank.store_account(bank.collector_id(), &account);
379 assert!(initial_balance + deposit < min_rent_exempt_balance);
380 } else if test_case.scenario == Scenario::InvalidOwner {
381 let account =
383 AccountSharedData::new(min_rent_exempt_balance, 0, &Pubkey::new_unique());
384 bank.store_account(bank.collector_id(), &account);
385 } else {
386 let account =
387 AccountSharedData::new(min_rent_exempt_balance, 0, &system_program::id());
388 bank.store_account(bank.collector_id(), &account);
389 }
390
391 let initial_burn = burn;
392 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
393 bank.deposit_or_burn_fee(deposit, &mut burn);
394 let new_collector_id_balance = bank.get_balance(bank.collector_id());
395
396 if test_case.scenario != Scenario::Normal {
397 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
398 assert_eq!(initial_burn + deposit, burn);
399 let locked_rewards = bank.rewards.read().unwrap();
400 assert!(
401 locked_rewards.is_empty(),
402 "There should be no rewards distributed"
403 );
404 } else {
405 assert_eq!(
406 initial_collector_id_balance + deposit,
407 new_collector_id_balance
408 );
409
410 assert_eq!(initial_burn, burn);
411
412 let locked_rewards = bank.rewards.read().unwrap();
413 assert_eq!(
414 locked_rewards.len(),
415 1,
416 "There should be one reward distributed"
417 );
418
419 let reward_info = &locked_rewards[0];
420 assert_eq!(
421 reward_info.1.lamports, deposit as i64,
422 "The reward amount should match the expected deposit"
423 );
424 assert_eq!(
425 reward_info.1.reward_type,
426 RewardType::Fee,
427 "The reward type should be Fee"
428 );
429 }
430 }
431 }
432
433 #[test]
434 fn test_distribute_transaction_fees_normal() {
435 let genesis = create_genesis_config(0);
436 let bank = Bank::new_for_tests(&genesis.genesis_config);
437 let transaction_fees = 100;
438 bank.collector_fees.fetch_add(transaction_fees, Relaxed);
439 assert_eq!(transaction_fees, bank.collector_fees.load(Relaxed));
440 let (expected_collected_fees, burn_amount) = bank.fee_rate_governor.burn(transaction_fees);
441
442 let initial_capitalization = bank.capitalization();
443 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
444 bank.distribute_transaction_fees();
445 let new_collector_id_balance = bank.get_balance(bank.collector_id());
446
447 assert_eq!(
448 initial_collector_id_balance + expected_collected_fees,
449 new_collector_id_balance
450 );
451 assert_eq!(initial_capitalization - burn_amount, bank.capitalization());
452 let locked_rewards = bank.rewards.read().unwrap();
453 assert_eq!(
454 locked_rewards.len(),
455 1,
456 "There should be one reward distributed"
457 );
458
459 let reward_info = &locked_rewards[0];
460 assert_eq!(
461 reward_info.1.lamports, expected_collected_fees as i64,
462 "The reward amount should match the expected deposit"
463 );
464 assert_eq!(
465 reward_info.1.reward_type,
466 RewardType::Fee,
467 "The reward type should be Fee"
468 );
469 }
470
471 #[test]
472 fn test_distribute_transaction_fees_zero() {
473 let genesis = create_genesis_config(0);
474 let bank = Bank::new_for_tests(&genesis.genesis_config);
475 assert_eq!(bank.collector_fees.load(Relaxed), 0);
476
477 let initial_capitalization = bank.capitalization();
478 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
479 bank.distribute_transaction_fees();
480 let new_collector_id_balance = bank.get_balance(bank.collector_id());
481
482 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
483 assert_eq!(initial_capitalization, bank.capitalization());
484 let locked_rewards = bank.rewards.read().unwrap();
485 assert!(
486 locked_rewards.is_empty(),
487 "There should be no rewards distributed"
488 );
489 }
490
491 #[test]
492 fn test_distribute_transaction_fees_burn_all() {
493 let mut genesis = create_genesis_config(0);
494 genesis.genesis_config.fee_rate_governor.burn_percent = 100;
495 let bank = Bank::new_for_tests(&genesis.genesis_config);
496 let transaction_fees = 100;
497 bank.collector_fees.fetch_add(transaction_fees, Relaxed);
498 assert_eq!(transaction_fees, bank.collector_fees.load(Relaxed));
499
500 let initial_capitalization = bank.capitalization();
501 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
502 bank.distribute_transaction_fees();
503 let new_collector_id_balance = bank.get_balance(bank.collector_id());
504
505 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
506 assert_eq!(
507 initial_capitalization - transaction_fees,
508 bank.capitalization()
509 );
510 let locked_rewards = bank.rewards.read().unwrap();
511 assert!(
512 locked_rewards.is_empty(),
513 "There should be no rewards distributed"
514 );
515 }
516
517 #[test]
518 fn test_distribute_transaction_fees_overflow_failure() {
519 let genesis = create_genesis_config(0);
520 let bank = Bank::new_for_tests(&genesis.genesis_config);
521 let transaction_fees = 100;
522 bank.collector_fees.fetch_add(transaction_fees, Relaxed);
523 assert_eq!(transaction_fees, bank.collector_fees.load(Relaxed));
524
525 let account = AccountSharedData::new(u64::MAX, 0, &system_program::id());
527 bank.store_account(bank.collector_id(), &account);
528
529 let initial_capitalization = bank.capitalization();
530 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
531 bank.distribute_transaction_fees();
532 let new_collector_id_balance = bank.get_balance(bank.collector_id());
533
534 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
535 assert_eq!(
536 initial_capitalization - transaction_fees,
537 bank.capitalization()
538 );
539 let locked_rewards = bank.rewards.read().unwrap();
540 assert!(
541 locked_rewards.is_empty(),
542 "There should be no rewards distributed"
543 );
544 }
545
546 #[test]
547 fn test_deposit_fees() {
548 let initial_balance = 1_000_000_000;
549 let genesis = create_genesis_config(initial_balance);
550 let bank = Bank::new_for_tests(&genesis.genesis_config);
551 let pubkey = genesis.mint_keypair.pubkey();
552 let deposit_amount = 500;
553
554 assert_eq!(
555 bank.deposit_fees(&pubkey, deposit_amount),
556 Ok(initial_balance + deposit_amount),
557 "New balance should be the sum of the initial balance and deposit amount"
558 );
559 }
560
561 #[test]
562 fn test_deposit_fees_with_overflow() {
563 let initial_balance = u64::MAX;
564 let genesis = create_genesis_config(initial_balance);
565 let bank = Bank::new_for_tests(&genesis.genesis_config);
566 let pubkey = genesis.mint_keypair.pubkey();
567 let deposit_amount = 500;
568
569 assert_eq!(
570 bank.deposit_fees(&pubkey, deposit_amount),
571 Err(DepositFeeError::LamportOverflow),
572 "Expected an error due to lamport overflow"
573 );
574 }
575
576 #[test]
577 fn test_deposit_fees_invalid_account_owner() {
578 let initial_balance = 1000;
579 let genesis = create_genesis_config_with_leader(0, &pubkey::new_rand(), initial_balance);
580 let bank = Bank::new_for_tests(&genesis.genesis_config);
581 let pubkey = genesis.voting_keypair.pubkey();
582 let deposit_amount = 500;
583
584 assert_eq!(
585 bank.deposit_fees(&pubkey, deposit_amount),
586 Err(DepositFeeError::InvalidAccountOwner),
587 "Expected an error due to invalid account owner"
588 );
589 }
590
591 #[test]
592 fn test_deposit_fees_invalid_rent_paying() {
593 let initial_balance = 0;
594 let genesis = create_genesis_config(initial_balance);
595 let pubkey = genesis.mint_keypair.pubkey();
596 let mut genesis_config = genesis.genesis_config;
597 genesis_config.rent = Rent::default(); let bank = Bank::new_for_tests(&genesis_config);
599 let min_rent_exempt_balance = genesis_config.rent.minimum_balance(0);
600
601 let deposit_amount = 500;
602 assert!(initial_balance + deposit_amount < min_rent_exempt_balance);
603
604 assert_eq!(
605 bank.deposit_fees(&pubkey, deposit_amount),
606 Err(DepositFeeError::InvalidRentPayingAccount),
607 "Expected an error due to invalid rent paying account"
608 );
609 }
610
611 #[test]
612 fn test_distribute_rent_to_validators_rent_paying() {
613 solana_logger::setup();
614
615 const RENT_PER_VALIDATOR: u64 = 55;
616 const TOTAL_RENT: u64 = RENT_PER_VALIDATOR * 4;
617
618 let empty_validator = ValidatorVoteKeypairs::new_rand();
619 let rent_paying_validator = ValidatorVoteKeypairs::new_rand();
620 let becomes_rent_exempt_validator = ValidatorVoteKeypairs::new_rand();
621 let rent_exempt_validator = ValidatorVoteKeypairs::new_rand();
622 let keypairs = vec![
623 &empty_validator,
624 &rent_paying_validator,
625 &becomes_rent_exempt_validator,
626 &rent_exempt_validator,
627 ];
628 let genesis_config_info = create_genesis_config_with_vote_accounts(
629 sol_to_lamports(1000.),
630 &keypairs,
631 vec![sol_to_lamports(1000.); 4],
632 );
633 let mut genesis_config = genesis_config_info.genesis_config;
634 genesis_config.rent = Rent::default(); let bank = Bank::new_for_tests(&genesis_config);
637 let rent_exempt_minimum = bank.rent_collector().get_rent().minimum_balance(0);
638
639 let mut empty_validator_account = bank
641 .get_account_with_fixed_root(&empty_validator.node_keypair.pubkey())
642 .unwrap();
643 empty_validator_account.set_lamports(0);
644 bank.store_account(
645 &empty_validator.node_keypair.pubkey(),
646 &empty_validator_account,
647 );
648
649 let mut becomes_rent_exempt_validator_account = bank
651 .get_account_with_fixed_root(&becomes_rent_exempt_validator.node_keypair.pubkey())
652 .unwrap();
653 becomes_rent_exempt_validator_account
654 .set_lamports(rent_exempt_minimum - RENT_PER_VALIDATOR);
655 bank.store_account(
656 &becomes_rent_exempt_validator.node_keypair.pubkey(),
657 &becomes_rent_exempt_validator_account,
658 );
659
660 let mut rent_exempt_validator_account = bank
662 .get_account_with_fixed_root(&rent_exempt_validator.node_keypair.pubkey())
663 .unwrap();
664 rent_exempt_validator_account.set_lamports(rent_exempt_minimum);
665 bank.store_account(
666 &rent_exempt_validator.node_keypair.pubkey(),
667 &rent_exempt_validator_account,
668 );
669
670 let get_rent_state = |bank: &Bank, address: &Pubkey| -> RentState {
671 let account = bank
672 .get_account_with_fixed_root(address)
673 .unwrap_or_default();
674 bank.rent_collector().get_account_rent_state(&account)
675 };
676
677 assert_eq!(
679 get_rent_state(&bank, &empty_validator.node_keypair.pubkey()),
680 RentState::Uninitialized
681 );
682 assert_eq!(
683 get_rent_state(&bank, &rent_paying_validator.node_keypair.pubkey()),
684 RentState::RentPaying {
685 lamports: 42,
686 data_size: 0,
687 }
688 );
689 assert_eq!(
690 get_rent_state(&bank, &becomes_rent_exempt_validator.node_keypair.pubkey()),
691 RentState::RentPaying {
692 lamports: rent_exempt_minimum - RENT_PER_VALIDATOR,
693 data_size: 0,
694 }
695 );
696 assert_eq!(
697 get_rent_state(&bank, &rent_exempt_validator.node_keypair.pubkey()),
698 RentState::RentExempt
699 );
700
701 let old_empty_validator_lamports = bank.get_balance(&empty_validator.node_keypair.pubkey());
702 let old_rent_paying_validator_lamports =
703 bank.get_balance(&rent_paying_validator.node_keypair.pubkey());
704 let old_becomes_rent_exempt_validator_lamports =
705 bank.get_balance(&becomes_rent_exempt_validator.node_keypair.pubkey());
706 let old_rent_exempt_validator_lamports =
707 bank.get_balance(&rent_exempt_validator.node_keypair.pubkey());
708
709 bank.distribute_rent_to_validators(&bank.vote_accounts(), TOTAL_RENT);
710
711 let new_empty_validator_lamports = bank.get_balance(&empty_validator.node_keypair.pubkey());
712 let new_rent_paying_validator_lamports =
713 bank.get_balance(&rent_paying_validator.node_keypair.pubkey());
714 let new_becomes_rent_exempt_validator_lamports =
715 bank.get_balance(&becomes_rent_exempt_validator.node_keypair.pubkey());
716 let new_rent_exempt_validator_lamports =
717 bank.get_balance(&rent_exempt_validator.node_keypair.pubkey());
718
719 assert_eq!(old_empty_validator_lamports, new_empty_validator_lamports);
722
723 assert_eq!(
724 old_rent_paying_validator_lamports,
725 new_rent_paying_validator_lamports
726 );
727
728 assert_eq!(
729 old_becomes_rent_exempt_validator_lamports + RENT_PER_VALIDATOR,
730 new_becomes_rent_exempt_validator_lamports
731 );
732
733 assert_eq!(
734 old_rent_exempt_validator_lamports + RENT_PER_VALIDATOR,
735 new_rent_exempt_validator_lamports
736 );
737
738 assert_eq!(
740 RentState::Uninitialized,
741 get_rent_state(&bank, &empty_validator.node_keypair.pubkey()),
742 );
743 assert_eq!(
744 RentState::RentPaying {
745 lamports: old_rent_paying_validator_lamports,
746 data_size: 0,
747 },
748 get_rent_state(&bank, &rent_paying_validator.node_keypair.pubkey()),
749 );
750 assert_eq!(
751 RentState::RentExempt,
752 get_rent_state(&bank, &becomes_rent_exempt_validator.node_keypair.pubkey()),
753 );
754 assert_eq!(
755 RentState::RentExempt,
756 get_rent_state(&bank, &rent_exempt_validator.node_keypair.pubkey()),
757 );
758 }
759
760 #[test]
761 fn test_distribute_rent_to_validators_invalid_owner() {
762 struct TestCase {
763 use_invalid_owner: bool,
764 }
765
766 impl TestCase {
767 fn new(use_invalid_owner: bool) -> Self {
768 Self { use_invalid_owner }
769 }
770 }
771
772 for test_case in [TestCase::new(false), TestCase::new(true)] {
773 let genesis_config_info =
774 create_genesis_config_with_leader(0, &Pubkey::new_unique(), 100);
775 let mut genesis_config = genesis_config_info.genesis_config;
776 genesis_config.rent = Rent::default(); let bank = Bank::new_for_tests(&genesis_config);
779
780 let initial_balance = 1_000_000;
781 let account_owner = if test_case.use_invalid_owner {
782 Pubkey::new_unique()
783 } else {
784 system_program::id()
785 };
786 let account = AccountSharedData::new(initial_balance, 0, &account_owner);
787 bank.store_account(bank.collector_id(), &account);
788
789 let initial_capitalization = bank.capitalization();
790 let rent_fees = 100;
791 bank.distribute_rent_to_validators(&bank.vote_accounts(), rent_fees);
792 let new_capitalization = bank.capitalization();
793 let new_balance = bank.get_balance(bank.collector_id());
794
795 if test_case.use_invalid_owner {
796 assert_eq!(initial_balance, new_balance);
797 assert_eq!(initial_capitalization - rent_fees, new_capitalization);
798 assert_eq!(bank.rewards.read().unwrap().len(), 0);
799 } else {
800 assert_eq!(initial_balance + rent_fees, new_balance);
801 assert_eq!(initial_capitalization, new_capitalization);
802 assert_eq!(bank.rewards.read().unwrap().len(), 1);
803 }
804 }
805 }
806
807 #[test]
808 fn test_distribute_transaction_fee_details_normal() {
809 let genesis = create_genesis_config(0);
810 let mut bank = Bank::new_for_tests(&genesis.genesis_config);
811 let transaction_fee = 100;
812 let priority_fee = 200;
813 bank.collector_fee_details = RwLock::new(CollectorFeeDetails {
814 transaction_fee,
815 priority_fee,
816 });
817 let (expected_deposit, expected_burn) = bank.fee_rate_governor.burn(transaction_fee);
818 let expected_rewards = expected_deposit + priority_fee;
819
820 let initial_capitalization = bank.capitalization();
821 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
822 bank.distribute_transaction_fee_details();
823 let new_collector_id_balance = bank.get_balance(bank.collector_id());
824
825 assert_eq!(
826 initial_collector_id_balance + expected_rewards,
827 new_collector_id_balance
828 );
829 assert_eq!(
830 initial_capitalization - expected_burn,
831 bank.capitalization()
832 );
833 let locked_rewards = bank.rewards.read().unwrap();
834 assert_eq!(
835 locked_rewards.len(),
836 1,
837 "There should be one reward distributed"
838 );
839
840 let reward_info = &locked_rewards[0];
841 assert_eq!(
842 reward_info.1.lamports, expected_rewards as i64,
843 "The reward amount should match the expected deposit"
844 );
845 assert_eq!(
846 reward_info.1.reward_type,
847 RewardType::Fee,
848 "The reward type should be Fee"
849 );
850 }
851
852 #[test]
853 fn test_distribute_transaction_fee_details_zero() {
854 let genesis = create_genesis_config(0);
855 let bank = Bank::new_for_tests(&genesis.genesis_config);
856 assert_eq!(
857 *bank.collector_fee_details.read().unwrap(),
858 CollectorFeeDetails::default()
859 );
860
861 let initial_capitalization = bank.capitalization();
862 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
863 bank.distribute_transaction_fee_details();
864 let new_collector_id_balance = bank.get_balance(bank.collector_id());
865
866 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
867 assert_eq!(initial_capitalization, bank.capitalization());
868 let locked_rewards = bank.rewards.read().unwrap();
869 assert!(
870 locked_rewards.is_empty(),
871 "There should be no rewards distributed"
872 );
873 }
874
875 #[test]
876 fn test_distribute_transaction_fee_details_burn_all() {
877 let mut genesis = create_genesis_config(0);
878 genesis.genesis_config.fee_rate_governor.burn_percent = 100;
879 let mut bank = Bank::new_for_tests(&genesis.genesis_config);
880 let transaction_fee = 100;
881 let priority_fee = 200;
882 bank.collector_fee_details = RwLock::new(CollectorFeeDetails {
883 transaction_fee,
884 priority_fee,
885 });
886
887 let initial_capitalization = bank.capitalization();
888 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
889 bank.distribute_transaction_fee_details();
890 let new_collector_id_balance = bank.get_balance(bank.collector_id());
891
892 assert_eq!(
893 initial_collector_id_balance + priority_fee,
894 new_collector_id_balance
895 );
896 assert_eq!(
897 initial_capitalization - transaction_fee,
898 bank.capitalization()
899 );
900 let locked_rewards = bank.rewards.read().unwrap();
901 assert_eq!(
902 locked_rewards.len(),
903 1,
904 "There should be one reward distributed"
905 );
906
907 let reward_info = &locked_rewards[0];
908 assert_eq!(
909 reward_info.1.lamports, priority_fee as i64,
910 "The reward amount should match the expected deposit"
911 );
912 assert_eq!(
913 reward_info.1.reward_type,
914 RewardType::Fee,
915 "The reward type should be Fee"
916 );
917 }
918
919 #[test]
920 fn test_distribute_transaction_fee_details_overflow_failure() {
921 let genesis = create_genesis_config(0);
922 let mut bank = Bank::new_for_tests(&genesis.genesis_config);
923 let transaction_fee = 100;
924 let priority_fee = 200;
925 bank.collector_fee_details = RwLock::new(CollectorFeeDetails {
926 transaction_fee,
927 priority_fee,
928 });
929
930 let account = AccountSharedData::new(u64::MAX, 0, &system_program::id());
932 bank.store_account(bank.collector_id(), &account);
933
934 let initial_capitalization = bank.capitalization();
935 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
936 bank.distribute_transaction_fee_details();
937 let new_collector_id_balance = bank.get_balance(bank.collector_id());
938
939 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
940 assert_eq!(
941 initial_capitalization - transaction_fee - priority_fee,
942 bank.capitalization()
943 );
944 let locked_rewards = bank.rewards.read().unwrap();
945 assert!(
946 locked_rewards.is_empty(),
947 "There should be no rewards distributed"
948 );
949 }
950}