@@ -672,8 +672,8 @@ mod tests {
672672 partitioned_epoch_rewards:: {
673673 tests:: {
674674 build_partitioned_stake_rewards, create_default_reward_bank,
675- create_reward_bank, create_reward_bank_with_specific_stakes, RewardBank ,
676- SLOTS_PER_EPOCH ,
675+ create_reward_bank, create_reward_bank_with_specific_stakes,
676+ populate_vote_accounts_with_votes , RewardBank , SLOTS_PER_EPOCH ,
677677 } ,
678678 EpochRewardPhase , EpochRewardStatus , PartitionedStakeRewards ,
679679 StartBlockHeightAndPartitionedRewards ,
@@ -682,7 +682,12 @@ mod tests {
682682 RewardInfo , VoteReward ,
683683 } ,
684684 stake_account:: StakeAccount ,
685+ <<<<<<< HEAD
685686 stakes:: Stakes ,
687+ =======
688+ stake_utils ,
689+ stakes:: { tests:: create_staked_node_accounts , Stakes } ,
690+ >>>>>>> 30 d8afd32 ( runtime : Add test for epoch boundary ( #8838 ) )
686691 } ,
687692 agave_feature_set:: FeatureSet ,
688693 rayon:: ThreadPoolBuilder ,
@@ -693,7 +698,10 @@ mod tests {
693698 solana_stake_interface:: state:: { Delegation , StakeStateV2 } ,
694699 solana_vote_interface:: state:: VoteStateV4 ,
695700 solana_vote_program:: vote_state ,
696- std:: sync:: { Arc , RwLockReadGuard } ,
701+ std:: {
702+ collections:: HashSet ,
703+ sync:: { Arc , RwLockReadGuard } ,
704+ } ,
697705 } ;
698706
699707 #[ test ]
@@ -1414,4 +1422,229 @@ mod tests {
14141422 assert_eq ! ( vote_reward_c. commission, 10 ) ;
14151423 assert_eq ! ( vote_reward_c. vote_rewards, 50 ) ;
14161424 }
1425+ <<<<<<< HEAD
1426+ =======
1427+
1428+ #[ test]
1429+ fn test_epoch_rewards_cache_multiple_forks( ) {
1430+ let ( mut genesis_config, _mint_keypair) =
1431+ create_genesis_config( 1_000_000 * LAMPORTS_PER_SOL ) ;
1432+
1433+ const NUM_STAKES : usize = 1000 ;
1434+
1435+ for _i in 0 ..NUM_STAKES {
1436+ let vote_pubkey = Pubkey :: new_unique( ) ;
1437+ let stake_pubkey = Pubkey :: new_unique( ) ;
1438+
1439+ genesis_config. accounts. insert(
1440+ vote_pubkey,
1441+ vote_state:: create_v4_account_with_authorized(
1442+ & vote_pubkey,
1443+ & Pubkey :: new_unique( ) ,
1444+ & Pubkey :: new_unique( ) ,
1445+ None ,
1446+ 0 ,
1447+ 100_000_000_000 ,
1448+ )
1449+ . into( ) ,
1450+ ) ;
1451+
1452+ let stake_lamports = 1_000_000_000_000 ;
1453+ let stake_account = stake_utils:: create_stake_account(
1454+ & stake_pubkey,
1455+ & vote_pubkey,
1456+ & vote_state:: create_v4_account_with_authorized(
1457+ & vote_pubkey,
1458+ & Pubkey :: new_unique( ) ,
1459+ & Pubkey :: new_unique( ) ,
1460+ None ,
1461+ 0 ,
1462+ 100_000_000_000 ,
1463+ ) ,
1464+ & genesis_config. rent,
1465+ stake_lamports,
1466+ ) ;
1467+ genesis_config
1468+ . accounts
1469+ . insert( stake_pubkey, stake_account. into( ) ) ;
1470+ }
1471+
1472+ let bank = Arc :: new( Bank :: new_for_tests( & genesis_config) ) ;
1473+ let slots_per_epoch = bank. epoch_schedule( ) . slots_per_epoch;
1474+ {
1475+ let cache = bank. epoch_rewards_calculation_cache. lock( ) . unwrap( ) ;
1476+ assert!(
1477+ !cache. contains_key( & bank. parent_hash( ) ) ,
1478+ "cache should be empty"
1479+ ) ;
1480+ }
1481+
1482+ let bank_fork1 =
1483+ Bank :: new_from_parent( Arc :: clone( & bank) , & Pubkey :: default( ) , slots_per_epoch) ;
1484+ {
1485+ let cache = bank_fork1. epoch_rewards_calculation_cache. lock( ) . unwrap( ) ;
1486+ assert ! (
1487+ cache. contains_key( & bank_fork1. parent_hash( ) ) ,
1488+ "cache should be populated"
1489+ ) ;
1490+ }
1491+
1492+ let bank_fork2 = Bank :: new_from_parent( bank, & Pubkey :: default( ) , slots_per_epoch) ;
1493+ {
1494+ let cache = bank_fork2. epoch_rewards_calculation_cache. lock( ) . unwrap( ) ;
1495+ assert ! (
1496+ cache. contains_key( & bank_fork2. parent_hash( ) ) ,
1497+ "cache should be populated"
1498+ ) ;
1499+ }
1500+ }
1501+
1502+ fn add_voters_and_populate(
1503+ bank: & Arc < Bank > ,
1504+ voters: & mut HashSet < Pubkey > ,
1505+ stakers: & mut HashSet < Pubkey > ,
1506+ count: usize,
1507+ stake_lamports: u64 ,
1508+ commission: u8 ,
1509+ ) {
1510+ for _ in 0 ..count {
1511+ let ( ( vote_pubkey, vote_account) , ( stake_pubkey, stake_account) ) =
1512+ create_staked_node_accounts( stake_lamports) ;
1513+ bank. store_account_and_update_capitalization( & vote_pubkey, & vote_account) ;
1514+ bank. store_account_and_update_capitalization( & stake_pubkey, & stake_account) ;
1515+ voters. insert( vote_pubkey) ;
1516+ stakers. insert( stake_pubkey) ;
1517+ }
1518+ populate_vote_accounts_with_votes( bank, voters. iter( ) . copied( ) , commission) ;
1519+ }
1520+
1521+ #[ allow( clippy:: too_many_arguments) ]
1522+ fn assert_cached_rewards(
1523+ bank: & Arc < Bank > ,
1524+ expected_cache_len: usize,
1525+ expected_voters: & HashSet < Pubkey > ,
1526+ expected_stakers: & HashSet < Pubkey > ,
1527+ expected_vote_rewards: u64,
1528+ expected_stake_rewards: u64 ,
1529+ expected_rewards: u64 ,
1530+ expected_points: u128 ,
1531+ parent_capitalization: Option < u64 > ,
1532+ ) {
1533+ let cache = bank. epoch_rewards_calculation_cache. lock( ) . unwrap( ) ;
1534+ assert_eq ! ( cache. len( ) , expected_cache_len) ;
1535+ let partitioned = cache. get( & bank. parent_hash( ) ) . unwrap( ) . as_ref( ) ;
1536+ let VoteRewardsAccounts {
1537+ accounts_with_rewards,
1538+ total_vote_rewards_lamports,
1539+ ..
1540+ } = & partitioned. vote_account_rewards;
1541+ let StakeRewardCalculation {
1542+ stake_rewards,
1543+ total_stake_rewards_lamports,
1544+ ..
1545+ } = & partitioned. stake_rewards;
1546+ let point_value = & partitioned . point_value;
1547+ let voters: HashSet < _ > = accounts_with_rewards
1548+ . iter( )
1549+ . map( |( pubkey, _reward, _acc) | * pubkey)
1550+ . collect( ) ;
1551+ let stakers: HashSet < _ > = stake_rewards
1552+ . rewards
1553+ . iter( )
1554+ . filter_map( |reward| reward. as_ref( ) )
1555+ . map( |reward| reward. stake_pubkey)
1556+ . collect( ) ;
1557+ assert_eq ! ( expected_voters, & voters) ;
1558+ assert_eq ! ( expected_stakers, & stakers) ;
1559+ assert_eq ! ( * total_vote_rewards_lamports, expected_vote_rewards) ;
1560+ assert_eq ! ( * total_stake_rewards_lamports, expected_stake_rewards) ;
1561+ assert_eq ! ( point_value. rewards, expected_rewards) ;
1562+ assert_eq ! ( point_value. points, expected_points) ;
1563+ if let Some ( parent_cap) = parent_capitalization {
1564+ assert_eq!( bank. capitalization( ) , parent_cap + expected_vote_rewards) ;
1565+ }
1566+ }
1567+
1568+ #[ test]
1569+ fn test_epoch_boundary( ) {
1570+ let delegations = 100 ;
1571+ let stake_lamports = 2_000_000_000 ;
1572+ let stakes: Vec < _ > = ( 0 ..delegations) . map( |_| stake_lamports) . collect( ) ;
1573+ let (
1574+ RewardBank {
1575+ bank : bank1,
1576+ voters,
1577+ stakers,
1578+ ..
1579+ } ,
1580+ _bank_forks,
1581+ ) = create_reward_bank_with_specific_stakes(
1582+ stakes,
1583+ PartitionedEpochRewardsConfig :: default( ) . stake_account_stores_per_block,
1584+ SLOTS_PER_EPOCH ,
1585+ ) ;
1586+ let mut voters: HashSet < _ > = voters. into_iter( ) . collect( ) ;
1587+ let mut stakers: HashSet < _ > = stakers. into_iter( ) . collect( ) ;
1588+
1589+ // The sysvar account holds the rent-exempt lamport added after
1590+ // reward calculation, so the bank capitalization exceeds the cached
1591+ // value by this amount.
1592+ let epoch_rewards_sysvar_balance = bank1. get_balance( & solana_sysvar:: epoch_rewards:: id( ) ) ;
1593+ assert_eq ! ( epoch_rewards_sysvar_balance, 1 ) ;
1594+
1595+ assert_cached_rewards(
1596+ & bank1,
1597+ 1 , // expected_cache_len
1598+ & voters, // expected_voters
1599+ & stakers, // expected_stakers
1600+ 0 , // expected_vote_rewards
1601+ 12300 , // expected_stake_rewards
1602+ 12392 , // expected_rewards
1603+ 8_400_000_000_000u128 , // expected_points
1604+ None , // parent_capitalization
1605+ ) ;
1606+
1607+ add_voters_and_populate( & bank1, & mut voters, & mut stakers, 5 , 5_000_000_000 , 10 ) ;
1608+ let parent_capitalization = bank1. capitalization( ) ;
1609+
1610+ let bank2 = Arc :: new( Bank :: new_from_parent(
1611+ Arc :: clone( & bank1) ,
1612+ & Pubkey :: default( ) ,
1613+ SLOTS_PER_EPOCH * 2 ,
1614+ ) ) ;
1615+
1616+ assert_cached_rewards(
1617+ & bank2,
1618+ 2 , // expected_cache_len
1619+ & voters, // expected_voters
1620+ & stakers, // expected_stakers
1621+ 1245 , // expected_vote_rewards
1622+ 11810 , // expected_stake_rewards
1623+ 13163 , // expected_rewards
1624+ 9_450_000_000_000u128 , // expected_points
1625+ Some ( parent_capitalization) , // parent_capitalization
1626+ ) ;
1627+
1628+ add_voters_and_populate( & bank2, & mut voters, & mut stakers, 10 , 8_000_000_000 , 10 ) ;
1629+ let parent_capitalization = bank2. capitalization( ) ;
1630+
1631+ let bank3 = Arc :: new( Bank :: new_from_parent(
1632+ Arc :: clone( & bank2) ,
1633+ & Pubkey :: default( ) ,
1634+ SLOTS_PER_EPOCH * 3 ,
1635+ ) ) ;
1636+
1637+ assert_cached_rewards(
1638+ & bank3,
1639+ 3 , // expected_cache_len
1640+ & voters, // expected_voters
1641+ & stakers, // expected_stakers
1642+ 1525 , // expected_vote_rewards
1643+ 13930 , // expected_stake_rewards
1644+ 15629 , // expected_rewards
1645+ 12_810_000_000_000u128 , // expected_points
1646+ Some ( parent_capitalization) , // parent_capitalization
1647+ ) ;
1648+ }
1649+ >>>>>>> 30 d8afd32 ( runtime: Add test for epoch boundary ( #8838 ) )
14171650}
0 commit comments