From 9daaa71e2363cb6cb40de19ab1b6f36ae4e5fe61 Mon Sep 17 00:00:00 2001 From: Michal R Date: Mon, 27 Oct 2025 17:24:07 +0100 Subject: [PATCH] runtime: Test epoch rewards cache for multiple forks Add a unit test which ensures that only one bank fork performs rewards computation during epoch boundary. Co-authored-by: Danele Murua --- .../partitioned_epoch_rewards/calculation.rs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/runtime/src/bank/partitioned_epoch_rewards/calculation.rs b/runtime/src/bank/partitioned_epoch_rewards/calculation.rs index 90686c98fba28d..8a19720959dd47 100644 --- a/runtime/src/bank/partitioned_epoch_rewards/calculation.rs +++ b/runtime/src/bank/partitioned_epoch_rewards/calculation.rs @@ -682,6 +682,7 @@ mod tests { RewardInfo, VoteReward, }, stake_account::StakeAccount, + stake_utils, stakes::Stakes, }, agave_feature_set::FeatureSet, @@ -1414,4 +1415,78 @@ mod tests { assert_eq!(vote_reward_c.commission, 10); assert_eq!(vote_reward_c.vote_rewards, 50); } + + #[test] + fn test_epoch_rewards_cache_multiple_forks() { + let (mut genesis_config, _mint_keypair) = + create_genesis_config(1_000_000 * LAMPORTS_PER_SOL); + + const NUM_STAKES: usize = 1000; + + for _i in 0..NUM_STAKES { + let vote_pubkey = Pubkey::new_unique(); + let stake_pubkey = Pubkey::new_unique(); + + genesis_config.accounts.insert( + vote_pubkey, + vote_state::create_v4_account_with_authorized( + &vote_pubkey, + &Pubkey::new_unique(), + &Pubkey::new_unique(), + None, + 0, + 100_000_000_000, + ) + .into(), + ); + + let stake_lamports = 1_000_000_000_000; + let stake_account = stake_utils::create_stake_account( + &stake_pubkey, + &vote_pubkey, + &vote_state::create_v4_account_with_authorized( + &vote_pubkey, + &Pubkey::new_unique(), + &Pubkey::new_unique(), + None, + 0, + 100_000_000_000, + ), + &genesis_config.rent, + stake_lamports, + ); + genesis_config + .accounts + .insert(stake_pubkey, stake_account.into()); + } + + let bank = Arc::new(Bank::new_for_tests(&genesis_config)); + let slots_per_epoch = bank.epoch_schedule().slots_per_epoch; + { + let cache = bank.epoch_rewards_calculation_cache.lock().unwrap(); + assert!( + !cache.contains_key(&bank.parent_hash()), + "cache should be empty" + ); + } + + let bank_fork1 = + Bank::new_from_parent(Arc::clone(&bank), &Pubkey::default(), slots_per_epoch); + { + let cache = bank_fork1.epoch_rewards_calculation_cache.lock().unwrap(); + assert!( + cache.contains_key(&bank_fork1.parent_hash()), + "cache should be populated" + ); + } + + let bank_fork2 = Bank::new_from_parent(bank, &Pubkey::default(), slots_per_epoch); + { + let cache = bank_fork2.epoch_rewards_calculation_cache.lock().unwrap(); + assert!( + cache.contains_key(&bank_fork2.parent_hash()), + "cache should be populated" + ); + } + } }