1use {
2 super::Bank,
3 crate::bank::CollectorFeeDetails,
4 log::{debug, warn},
5 solana_account::{ReadableAccount, WritableAccount},
6 solana_fee::FeeFeatures,
7 solana_fee_structure::FeeBudgetLimits,
8 solana_pubkey::Pubkey,
9 solana_reward_info::{RewardInfo, RewardType},
10 solana_runtime_transaction::transaction_with_meta::TransactionWithMeta,
11 solana_svm_rent_collector::svm_rent_collector::SVMRentCollector,
12 solana_system_interface::program as system_program,
13 solana_vote::vote_account::VoteAccountsHashMap,
14 std::{result::Result, sync::atomic::Ordering::Relaxed},
15 thiserror::Error,
16};
17
18#[derive(Error, Debug, PartialEq)]
19enum DepositFeeError {
20 #[error("fee account became rent paying")]
21 InvalidRentPayingAccount,
22 #[error("lamport overflow")]
23 LamportOverflow,
24 #[error("invalid fee account owner")]
25 InvalidAccountOwner,
26}
27
28#[derive(Default)]
29pub struct FeeDistribution {
30 deposit: u64,
31 burn: u64,
32}
33
34impl FeeDistribution {
35 pub fn get_deposit(&self) -> u64 {
36 self.deposit
37 }
38}
39
40impl Bank {
41 pub(super) fn distribute_transaction_fee_details(&self) {
56 let fee_details = self.collector_fee_details.read().unwrap();
57 if fee_details.total() == 0 {
58 return;
60 }
61
62 let FeeDistribution { deposit, burn } =
63 self.calculate_reward_and_burn_fee_details(&fee_details);
64
65 let total_burn = self.deposit_or_burn_fee(deposit).saturating_add(burn);
66 self.capitalization.fetch_sub(total_burn, Relaxed);
67 }
68
69 pub fn calculate_reward_for_transaction(
70 &self,
71 transaction: &impl TransactionWithMeta,
72 fee_budget_limits: &FeeBudgetLimits,
73 ) -> u64 {
74 let (_last_hash, last_lamports_per_signature) =
75 self.last_blockhash_and_lamports_per_signature();
76 let fee_details = solana_fee::calculate_fee_details(
77 transaction,
78 last_lamports_per_signature == 0,
79 self.fee_structure().lamports_per_signature,
80 fee_budget_limits.prioritization_fee,
81 FeeFeatures::from(self.feature_set.as_ref()),
82 );
83 let FeeDistribution {
84 deposit: reward,
85 burn: _,
86 } = self.calculate_reward_and_burn_fee_details(&CollectorFeeDetails::from(fee_details));
87 reward
88 }
89
90 pub fn calculate_reward_and_burn_fee_details(
91 &self,
92 fee_details: &CollectorFeeDetails,
93 ) -> FeeDistribution {
94 if fee_details.transaction_fee == 0 {
95 return FeeDistribution::default();
96 }
97
98 let burn = fee_details.transaction_fee * self.burn_percent() / 100;
99 let deposit = fee_details
100 .priority_fee
101 .saturating_add(fee_details.transaction_fee.saturating_sub(burn));
102 FeeDistribution { deposit, burn }
103 }
104
105 const fn burn_percent(&self) -> u64 {
106 static_assertions::const_assert!(solana_fee_calculator::DEFAULT_BURN_PERCENT <= 100);
110
111 solana_fee_calculator::DEFAULT_BURN_PERCENT as u64
112 }
113
114 fn deposit_or_burn_fee(&self, deposit: u64) -> u64 {
118 if deposit == 0 {
119 return 0;
120 }
121
122 match self.deposit_fees(&self.collector_id, deposit) {
123 Ok(post_balance) => {
124 self.rewards.write().unwrap().push((
125 self.collector_id,
126 RewardInfo {
127 reward_type: RewardType::Fee,
128 lamports: deposit as i64,
129 post_balance,
130 commission: None,
131 },
132 ));
133 0
134 }
135 Err(err) => {
136 debug!(
137 "Burned {} lamport tx fee instead of sending to {} due to {}",
138 deposit, self.collector_id, err
139 );
140 datapoint_warn!(
141 "bank-burned_fee",
142 ("slot", self.slot(), i64),
143 ("num_lamports", deposit, i64),
144 ("error", err.to_string(), String),
145 );
146 deposit
147 }
148 }
149 }
150
151 fn deposit_fees(&self, pubkey: &Pubkey, fees: u64) -> Result<u64, DepositFeeError> {
153 let mut account = self
154 .get_account_with_fixed_root_no_cache(pubkey)
155 .unwrap_or_default();
156
157 if !system_program::check_id(account.owner()) {
158 return Err(DepositFeeError::InvalidAccountOwner);
159 }
160
161 let recipient_pre_rent_state = self.rent_collector().get_account_rent_state(&account);
162 let distribution = account.checked_add_lamports(fees);
163 if distribution.is_err() {
164 return Err(DepositFeeError::LamportOverflow);
165 }
166
167 let recipient_post_rent_state = self.rent_collector().get_account_rent_state(&account);
168 let rent_state_transition_allowed = self
169 .rent_collector()
170 .transition_allowed(&recipient_pre_rent_state, &recipient_post_rent_state);
171 if !rent_state_transition_allowed {
172 return Err(DepositFeeError::InvalidRentPayingAccount);
173 }
174
175 self.store_account(pubkey, &account);
176 Ok(account.lamports())
177 }
178
179 fn distribute_rent_to_validators(
200 &self,
201 vote_accounts: &VoteAccountsHashMap,
202 rent_to_be_distributed: u64,
203 ) {
204 let mut total_staked = 0;
205
206 let mut validator_stakes = vote_accounts
210 .iter()
211 .filter_map(|(_vote_pubkey, (staked, account))| {
212 if *staked == 0 {
213 None
214 } else {
215 total_staked += *staked;
216 Some((*account.node_pubkey(), *staked))
217 }
218 })
219 .collect::<Vec<(Pubkey, u64)>>();
220
221 #[cfg(test)]
222 if validator_stakes.is_empty() {
223 self.capitalization
225 .fetch_sub(rent_to_be_distributed, Relaxed);
226 return;
227 }
228 #[cfg(not(test))]
229 assert!(!validator_stakes.is_empty());
230
231 validator_stakes.sort_unstable_by(|(pubkey1, staked1), (pubkey2, staked2)| {
235 (staked1, pubkey1).cmp(&(staked2, pubkey2)).reverse()
236 });
237
238 let mut rent_distributed_in_initial_round = 0;
239 let validator_rent_shares = validator_stakes
240 .into_iter()
241 .map(|(pubkey, staked)| {
242 let rent_share = (((staked as u128) * (rent_to_be_distributed as u128))
243 / (total_staked as u128))
244 .try_into()
245 .unwrap();
246 rent_distributed_in_initial_round += rent_share;
247 (pubkey, rent_share)
248 })
249 .collect::<Vec<(Pubkey, u64)>>();
250
251 let mut leftover_lamports = rent_to_be_distributed - rent_distributed_in_initial_round;
254
255 let mut rent_to_burn: u64 = 0;
256 let mut rewards = vec![];
257 validator_rent_shares
258 .into_iter()
259 .for_each(|(pubkey, rent_share)| {
260 let rent_to_be_paid = if leftover_lamports > 0 {
261 leftover_lamports -= 1;
262 rent_share + 1
263 } else {
264 rent_share
265 };
266 if rent_to_be_paid > 0 {
267 match self.deposit_fees(&pubkey, rent_to_be_paid) {
268 Ok(post_balance) => {
269 rewards.push((
270 pubkey,
271 RewardInfo {
272 reward_type: RewardType::Rent,
273 lamports: rent_to_be_paid as i64,
274 post_balance,
275 commission: None,
276 },
277 ));
278 }
279 Err(err) => {
280 debug!(
281 "Burned {} lamport rent fee instead of sending to {} due to {}",
282 rent_to_be_paid, pubkey, err
283 );
284
285 rent_to_burn = rent_to_burn.saturating_add(rent_to_be_paid);
288 }
289 }
290 }
291 });
292 self.rewards.write().unwrap().append(&mut rewards);
293
294 if rent_to_burn > 0 {
295 self.capitalization.fetch_sub(rent_to_burn, Relaxed);
296 datapoint_warn!(
297 "bank-burned_rent",
298 ("slot", self.slot(), i64),
299 ("num_lamports", rent_to_burn, i64)
300 );
301 }
302
303 assert_eq!(leftover_lamports, 0);
304 }
305
306 pub(super) fn distribute_rent_fees(&self) {
307 let total_rent_collected = self.collected_rent.load(Relaxed);
308
309 if !self.should_collect_rent() {
310 if total_rent_collected != 0 {
311 warn!("Rent fees collection is disabled, yet total rent collected was non zero! Total rent collected: {total_rent_collected}");
312 }
313 return;
314 }
315
316 let (burned_portion, rent_to_be_distributed) = self
317 .rent_collector
318 .rent
319 .calculate_burn(total_rent_collected);
320
321 debug!(
322 "distributed rent: {} (rounded from: {}, burned: {})",
323 rent_to_be_distributed, total_rent_collected, burned_portion
324 );
325 self.capitalization.fetch_sub(burned_portion, Relaxed);
326
327 if rent_to_be_distributed == 0 {
328 return;
329 }
330
331 self.distribute_rent_to_validators(&self.vote_accounts(), rent_to_be_distributed);
332 }
333}
334
335#[cfg(test)]
336pub mod tests {
337 use {
338 super::*,
339 crate::genesis_utils::{
340 create_genesis_config, create_genesis_config_with_leader,
341 create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs,
342 },
343 solana_account::AccountSharedData,
344 solana_native_token::sol_to_lamports,
345 solana_pubkey as pubkey,
346 solana_rent::Rent,
347 solana_signer::Signer,
348 solana_svm_rent_collector::rent_state::RentState,
349 std::sync::RwLock,
350 };
351
352 #[test]
353 fn test_deposit_or_burn_zero_fee() {
354 let genesis = create_genesis_config(0);
355 let bank = Bank::new_for_tests(&genesis.genesis_config);
356 assert_eq!(bank.deposit_or_burn_fee(0), 0);
357 }
358
359 #[test]
360 fn test_deposit_or_burn_fee() {
361 #[derive(PartialEq)]
362 enum Scenario {
363 Normal,
364 InvalidOwner,
365 RentPaying,
366 }
367
368 struct TestCase {
369 scenario: Scenario,
370 }
371
372 impl TestCase {
373 fn new(scenario: Scenario) -> Self {
374 Self { scenario }
375 }
376 }
377
378 for test_case in [
379 TestCase::new(Scenario::Normal),
380 TestCase::new(Scenario::InvalidOwner),
381 TestCase::new(Scenario::RentPaying),
382 ] {
383 let mut genesis = create_genesis_config(0);
384 let rent = Rent::default();
385 let min_rent_exempt_balance = rent.minimum_balance(0);
386 genesis.genesis_config.rent = rent; let bank = Bank::new_for_tests(&genesis.genesis_config);
388
389 let deposit = 100;
390 let mut burn = 100;
391
392 if test_case.scenario == Scenario::RentPaying {
393 let initial_balance = 100;
395 let account = AccountSharedData::new(initial_balance, 0, &system_program::id());
396 bank.store_account(bank.collector_id(), &account);
397 assert!(initial_balance + deposit < min_rent_exempt_balance);
398 } else if test_case.scenario == Scenario::InvalidOwner {
399 let account =
401 AccountSharedData::new(min_rent_exempt_balance, 0, &Pubkey::new_unique());
402 bank.store_account(bank.collector_id(), &account);
403 } else {
404 let account =
405 AccountSharedData::new(min_rent_exempt_balance, 0, &system_program::id());
406 bank.store_account(bank.collector_id(), &account);
407 }
408
409 let initial_burn = burn;
410 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
411 burn += bank.deposit_or_burn_fee(deposit);
412 let new_collector_id_balance = bank.get_balance(bank.collector_id());
413
414 if test_case.scenario != Scenario::Normal {
415 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
416 assert_eq!(initial_burn + deposit, burn);
417 let locked_rewards = bank.rewards.read().unwrap();
418 assert!(
419 locked_rewards.is_empty(),
420 "There should be no rewards distributed"
421 );
422 } else {
423 assert_eq!(
424 initial_collector_id_balance + deposit,
425 new_collector_id_balance
426 );
427
428 assert_eq!(initial_burn, burn);
429
430 let locked_rewards = bank.rewards.read().unwrap();
431 assert_eq!(
432 locked_rewards.len(),
433 1,
434 "There should be one reward distributed"
435 );
436
437 let reward_info = &locked_rewards[0];
438 assert_eq!(
439 reward_info.1.lamports, deposit as i64,
440 "The reward amount should match the expected deposit"
441 );
442 assert_eq!(
443 reward_info.1.reward_type,
444 RewardType::Fee,
445 "The reward type should be Fee"
446 );
447 }
448 }
449 }
450
451 #[test]
452 fn test_deposit_fees() {
453 let initial_balance = 1_000_000_000;
454 let genesis = create_genesis_config(initial_balance);
455 let bank = Bank::new_for_tests(&genesis.genesis_config);
456 let pubkey = genesis.mint_keypair.pubkey();
457 let deposit_amount = 500;
458
459 assert_eq!(
460 bank.deposit_fees(&pubkey, deposit_amount),
461 Ok(initial_balance + deposit_amount),
462 "New balance should be the sum of the initial balance and deposit amount"
463 );
464 }
465
466 #[test]
467 fn test_deposit_fees_with_overflow() {
468 let initial_balance = u64::MAX;
469 let genesis = create_genesis_config(initial_balance);
470 let bank = Bank::new_for_tests(&genesis.genesis_config);
471 let pubkey = genesis.mint_keypair.pubkey();
472 let deposit_amount = 500;
473
474 assert_eq!(
475 bank.deposit_fees(&pubkey, deposit_amount),
476 Err(DepositFeeError::LamportOverflow),
477 "Expected an error due to lamport overflow"
478 );
479 }
480
481 #[test]
482 fn test_deposit_fees_invalid_account_owner() {
483 let initial_balance = 1000;
484 let genesis = create_genesis_config_with_leader(0, &pubkey::new_rand(), initial_balance);
485 let bank = Bank::new_for_tests(&genesis.genesis_config);
486 let pubkey = genesis.voting_keypair.pubkey();
487 let deposit_amount = 500;
488
489 assert_eq!(
490 bank.deposit_fees(&pubkey, deposit_amount),
491 Err(DepositFeeError::InvalidAccountOwner),
492 "Expected an error due to invalid account owner"
493 );
494 }
495
496 #[test]
497 fn test_deposit_fees_invalid_rent_paying() {
498 let initial_balance = 0;
499 let genesis = create_genesis_config(initial_balance);
500 let pubkey = genesis.mint_keypair.pubkey();
501 let mut genesis_config = genesis.genesis_config;
502 genesis_config.rent = Rent::default(); let bank = Bank::new_for_tests(&genesis_config);
504 let min_rent_exempt_balance = genesis_config.rent.minimum_balance(0);
505
506 let deposit_amount = 500;
507 assert!(initial_balance + deposit_amount < min_rent_exempt_balance);
508
509 assert_eq!(
510 bank.deposit_fees(&pubkey, deposit_amount),
511 Err(DepositFeeError::InvalidRentPayingAccount),
512 "Expected an error due to invalid rent paying account"
513 );
514 }
515
516 #[test]
517 fn test_distribute_rent_to_validators_rent_paying() {
518 solana_logger::setup();
519
520 const RENT_PER_VALIDATOR: u64 = 55;
521 const TOTAL_RENT: u64 = RENT_PER_VALIDATOR * 4;
522
523 let empty_validator = ValidatorVoteKeypairs::new_rand();
524 let rent_paying_validator = ValidatorVoteKeypairs::new_rand();
525 let becomes_rent_exempt_validator = ValidatorVoteKeypairs::new_rand();
526 let rent_exempt_validator = ValidatorVoteKeypairs::new_rand();
527 let keypairs = vec![
528 &empty_validator,
529 &rent_paying_validator,
530 &becomes_rent_exempt_validator,
531 &rent_exempt_validator,
532 ];
533 let genesis_config_info = create_genesis_config_with_vote_accounts(
534 sol_to_lamports(1000.),
535 &keypairs,
536 vec![sol_to_lamports(1000.); 4],
537 );
538 let mut genesis_config = genesis_config_info.genesis_config;
539 genesis_config.rent = Rent::default(); let bank = Bank::new_for_tests(&genesis_config);
542 let rent_exempt_minimum = bank.rent_collector().get_rent().minimum_balance(0);
543
544 let mut empty_validator_account = bank
546 .get_account_with_fixed_root(&empty_validator.node_keypair.pubkey())
547 .unwrap();
548 empty_validator_account.set_lamports(0);
549 bank.store_account(
550 &empty_validator.node_keypair.pubkey(),
551 &empty_validator_account,
552 );
553
554 let mut becomes_rent_exempt_validator_account = bank
556 .get_account_with_fixed_root(&becomes_rent_exempt_validator.node_keypair.pubkey())
557 .unwrap();
558 becomes_rent_exempt_validator_account
559 .set_lamports(rent_exempt_minimum - RENT_PER_VALIDATOR);
560 bank.store_account(
561 &becomes_rent_exempt_validator.node_keypair.pubkey(),
562 &becomes_rent_exempt_validator_account,
563 );
564
565 let mut rent_exempt_validator_account = bank
567 .get_account_with_fixed_root(&rent_exempt_validator.node_keypair.pubkey())
568 .unwrap();
569 rent_exempt_validator_account.set_lamports(rent_exempt_minimum);
570 bank.store_account(
571 &rent_exempt_validator.node_keypair.pubkey(),
572 &rent_exempt_validator_account,
573 );
574
575 let get_rent_state = |bank: &Bank, address: &Pubkey| -> RentState {
576 let account = bank
577 .get_account_with_fixed_root(address)
578 .unwrap_or_default();
579 bank.rent_collector().get_account_rent_state(&account)
580 };
581
582 assert_eq!(
584 get_rent_state(&bank, &empty_validator.node_keypair.pubkey()),
585 RentState::Uninitialized
586 );
587 assert_eq!(
588 get_rent_state(&bank, &rent_paying_validator.node_keypair.pubkey()),
589 RentState::RentPaying {
590 lamports: 42,
591 data_size: 0,
592 }
593 );
594 assert_eq!(
595 get_rent_state(&bank, &becomes_rent_exempt_validator.node_keypair.pubkey()),
596 RentState::RentPaying {
597 lamports: rent_exempt_minimum - RENT_PER_VALIDATOR,
598 data_size: 0,
599 }
600 );
601 assert_eq!(
602 get_rent_state(&bank, &rent_exempt_validator.node_keypair.pubkey()),
603 RentState::RentExempt
604 );
605
606 let old_empty_validator_lamports = bank.get_balance(&empty_validator.node_keypair.pubkey());
607 let old_rent_paying_validator_lamports =
608 bank.get_balance(&rent_paying_validator.node_keypair.pubkey());
609 let old_becomes_rent_exempt_validator_lamports =
610 bank.get_balance(&becomes_rent_exempt_validator.node_keypair.pubkey());
611 let old_rent_exempt_validator_lamports =
612 bank.get_balance(&rent_exempt_validator.node_keypair.pubkey());
613
614 bank.distribute_rent_to_validators(&bank.vote_accounts(), TOTAL_RENT);
615
616 let new_empty_validator_lamports = bank.get_balance(&empty_validator.node_keypair.pubkey());
617 let new_rent_paying_validator_lamports =
618 bank.get_balance(&rent_paying_validator.node_keypair.pubkey());
619 let new_becomes_rent_exempt_validator_lamports =
620 bank.get_balance(&becomes_rent_exempt_validator.node_keypair.pubkey());
621 let new_rent_exempt_validator_lamports =
622 bank.get_balance(&rent_exempt_validator.node_keypair.pubkey());
623
624 assert_eq!(old_empty_validator_lamports, new_empty_validator_lamports);
627
628 assert_eq!(
629 old_rent_paying_validator_lamports,
630 new_rent_paying_validator_lamports
631 );
632
633 assert_eq!(
634 old_becomes_rent_exempt_validator_lamports + RENT_PER_VALIDATOR,
635 new_becomes_rent_exempt_validator_lamports
636 );
637
638 assert_eq!(
639 old_rent_exempt_validator_lamports + RENT_PER_VALIDATOR,
640 new_rent_exempt_validator_lamports
641 );
642
643 assert_eq!(
645 RentState::Uninitialized,
646 get_rent_state(&bank, &empty_validator.node_keypair.pubkey()),
647 );
648 assert_eq!(
649 RentState::RentPaying {
650 lamports: old_rent_paying_validator_lamports,
651 data_size: 0,
652 },
653 get_rent_state(&bank, &rent_paying_validator.node_keypair.pubkey()),
654 );
655 assert_eq!(
656 RentState::RentExempt,
657 get_rent_state(&bank, &becomes_rent_exempt_validator.node_keypair.pubkey()),
658 );
659 assert_eq!(
660 RentState::RentExempt,
661 get_rent_state(&bank, &rent_exempt_validator.node_keypair.pubkey()),
662 );
663 }
664
665 #[test]
666 fn test_distribute_rent_to_validators_invalid_owner() {
667 struct TestCase {
668 use_invalid_owner: bool,
669 }
670
671 impl TestCase {
672 fn new(use_invalid_owner: bool) -> Self {
673 Self { use_invalid_owner }
674 }
675 }
676
677 for test_case in [TestCase::new(false), TestCase::new(true)] {
678 let genesis_config_info =
679 create_genesis_config_with_leader(0, &Pubkey::new_unique(), 100);
680 let mut genesis_config = genesis_config_info.genesis_config;
681 genesis_config.rent = Rent::default(); let bank = Bank::new_for_tests(&genesis_config);
684
685 let initial_balance = 1_000_000;
686 let account_owner = if test_case.use_invalid_owner {
687 Pubkey::new_unique()
688 } else {
689 system_program::id()
690 };
691 let account = AccountSharedData::new(initial_balance, 0, &account_owner);
692 bank.store_account(bank.collector_id(), &account);
693
694 let initial_capitalization = bank.capitalization();
695 let rent_fees = 100;
696 bank.distribute_rent_to_validators(&bank.vote_accounts(), rent_fees);
697 let new_capitalization = bank.capitalization();
698 let new_balance = bank.get_balance(bank.collector_id());
699
700 if test_case.use_invalid_owner {
701 assert_eq!(initial_balance, new_balance);
702 assert_eq!(initial_capitalization - rent_fees, new_capitalization);
703 assert_eq!(bank.rewards.read().unwrap().len(), 0);
704 } else {
705 assert_eq!(initial_balance + rent_fees, new_balance);
706 assert_eq!(initial_capitalization, new_capitalization);
707 assert_eq!(bank.rewards.read().unwrap().len(), 1);
708 }
709 }
710 }
711
712 #[test]
713 fn test_distribute_transaction_fee_details_normal() {
714 let genesis = create_genesis_config(0);
715 let mut bank = Bank::new_for_tests(&genesis.genesis_config);
716 let transaction_fee = 100;
717 let priority_fee = 200;
718 bank.collector_fee_details = RwLock::new(CollectorFeeDetails {
719 transaction_fee,
720 priority_fee,
721 });
722 let expected_burn = transaction_fee * bank.burn_percent() / 100;
723 let expected_rewards = transaction_fee - expected_burn + priority_fee;
724
725 let initial_capitalization = bank.capitalization();
726 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
727 bank.distribute_transaction_fee_details();
728 let new_collector_id_balance = bank.get_balance(bank.collector_id());
729
730 assert_eq!(
731 initial_collector_id_balance + expected_rewards,
732 new_collector_id_balance
733 );
734 assert_eq!(
735 initial_capitalization - expected_burn,
736 bank.capitalization()
737 );
738 let locked_rewards = bank.rewards.read().unwrap();
739 assert_eq!(
740 locked_rewards.len(),
741 1,
742 "There should be one reward distributed"
743 );
744
745 let reward_info = &locked_rewards[0];
746 assert_eq!(
747 reward_info.1.lamports, expected_rewards as i64,
748 "The reward amount should match the expected deposit"
749 );
750 assert_eq!(
751 reward_info.1.reward_type,
752 RewardType::Fee,
753 "The reward type should be Fee"
754 );
755 }
756
757 #[test]
758 fn test_distribute_transaction_fee_details_zero() {
759 let genesis = create_genesis_config(0);
760 let bank = Bank::new_for_tests(&genesis.genesis_config);
761 assert_eq!(
762 *bank.collector_fee_details.read().unwrap(),
763 CollectorFeeDetails::default()
764 );
765
766 let initial_capitalization = bank.capitalization();
767 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
768 bank.distribute_transaction_fee_details();
769 let new_collector_id_balance = bank.get_balance(bank.collector_id());
770
771 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
772 assert_eq!(initial_capitalization, bank.capitalization());
773 let locked_rewards = bank.rewards.read().unwrap();
774 assert!(
775 locked_rewards.is_empty(),
776 "There should be no rewards distributed"
777 );
778 }
779
780 #[test]
781 fn test_distribute_transaction_fee_details_overflow_failure() {
782 let genesis = create_genesis_config(0);
783 let mut bank = Bank::new_for_tests(&genesis.genesis_config);
784 let transaction_fee = 100;
785 let priority_fee = 200;
786 bank.collector_fee_details = RwLock::new(CollectorFeeDetails {
787 transaction_fee,
788 priority_fee,
789 });
790
791 let account = AccountSharedData::new(u64::MAX, 0, &system_program::id());
793 bank.store_account(bank.collector_id(), &account);
794
795 let initial_capitalization = bank.capitalization();
796 let initial_collector_id_balance = bank.get_balance(bank.collector_id());
797 bank.distribute_transaction_fee_details();
798 let new_collector_id_balance = bank.get_balance(bank.collector_id());
799
800 assert_eq!(initial_collector_id_balance, new_collector_id_balance);
801 assert_eq!(
802 initial_capitalization - transaction_fee - priority_fee,
803 bank.capitalization()
804 );
805 let locked_rewards = bank.rewards.read().unwrap();
806 assert!(
807 locked_rewards.is_empty(),
808 "There should be no rewards distributed"
809 );
810 }
811}