@@ -6,25 +6,149 @@ use {solana_pubkey::Pubkey, std::collections::HashMap};
66/// The total epoch stake is calculated by summing all vote account stakes.
77pub type EpochStake = HashMap < Pubkey , u64 > ;
88
9- /// Create an `EpochStake` instance with a few mocked-out vote accounts to
10- /// achieve the provided total stake.
9+ /// Create an `EpochStake` instance with a few mocked-out entries ( vote accounts
10+ /// with stake) to achieve the provided total stake.
1111pub fn create_mock_epoch_stake ( target_total : u64 ) -> EpochStake {
12+ const BASE_STAKE_PER_ACCOUNT : u64 = 100_000_000_000 ; // 100 SOL
13+
1214 let mut epoch_stake = HashMap :: new ( ) ;
1315
1416 if target_total == 0 {
1517 return epoch_stake;
1618 }
1719
18- let num_accounts = target_total. div_ceil ( 1_000_000_000 ) ;
19-
20- let base_stake = target_total / num_accounts;
21- let remainder = target_total % num_accounts;
20+ let num_accounts = target_total / BASE_STAKE_PER_ACCOUNT ;
21+ let remainder = target_total % BASE_STAKE_PER_ACCOUNT ;
2222
23- std:: iter:: repeat_n ( base_stake, num_accounts as usize - 1 )
24- . chain ( std:: iter:: once ( base_stake + remainder) )
25- . for_each ( |stake| {
26- epoch_stake. insert ( Pubkey :: new_unique ( ) , stake) ;
27- } ) ;
23+ if num_accounts == 0 {
24+ epoch_stake. insert ( Pubkey :: new_unique ( ) , target_total) ;
25+ } else {
26+ std:: iter:: repeat_n ( BASE_STAKE_PER_ACCOUNT , num_accounts as usize - 1 )
27+ . chain ( std:: iter:: once ( BASE_STAKE_PER_ACCOUNT + remainder) )
28+ . for_each ( |stake| {
29+ epoch_stake. insert ( Pubkey :: new_unique ( ) , stake) ;
30+ } ) ;
31+ }
2832
2933 epoch_stake
3034}
35+
36+ #[ cfg( test) ]
37+ mod tests {
38+ use super :: * ;
39+
40+ #[ test]
41+ fn test_zero_stake ( ) {
42+ let epoch_stake = create_mock_epoch_stake ( 0 ) ;
43+ assert_eq ! ( epoch_stake. len( ) , 0 ) ;
44+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 0 ) ;
45+ }
46+
47+ #[ test]
48+ fn test_num_accounts_zero ( ) {
49+ // Target < 100 SOL, results in num_accounts = 0
50+ // Should create single account with full amount
51+
52+ // 1 lamport
53+ let epoch_stake = create_mock_epoch_stake ( 1 ) ;
54+ assert_eq ! ( epoch_stake. len( ) , 1 ) ;
55+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 1 ) ;
56+
57+ // 50 SOL
58+ let epoch_stake = create_mock_epoch_stake ( 50_000_000_000 ) ;
59+ assert_eq ! ( epoch_stake. len( ) , 1 ) ;
60+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 50_000_000_000 ) ;
61+
62+ // 99.999999999 SOL
63+ let epoch_stake = create_mock_epoch_stake ( 99_999_999_999 ) ;
64+ assert_eq ! ( epoch_stake. len( ) , 1 ) ;
65+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 99_999_999_999 ) ;
66+ }
67+
68+ #[ test]
69+ fn test_num_accounts_one ( ) {
70+ // 100 SOL <= target < 200 SOL, results in num_accounts = 1
71+
72+ // Exactly 100 SOL, no remainder
73+ let epoch_stake = create_mock_epoch_stake ( 100_000_000_000 ) ;
74+ assert_eq ! ( epoch_stake. len( ) , 1 ) ;
75+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 100_000_000_000 ) ;
76+ assert ! ( epoch_stake. values( ) . all( |& s| s == 100_000_000_000 ) ) ;
77+
78+ // 150 SOL, with remainder
79+ let epoch_stake = create_mock_epoch_stake ( 150_000_000_000 ) ;
80+ assert_eq ! ( epoch_stake. len( ) , 1 ) ;
81+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 150_000_000_000 ) ;
82+ assert ! ( epoch_stake. values( ) . all( |& s| s == 150_000_000_000 ) ) ;
83+
84+ // 199.999999999 SOL, with remainder
85+ let epoch_stake = create_mock_epoch_stake ( 199_999_999_999 ) ;
86+ assert_eq ! ( epoch_stake. len( ) , 1 ) ;
87+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 199_999_999_999 ) ;
88+ }
89+
90+ #[ test]
91+ fn test_num_accounts_two ( ) {
92+ // 200 SOL <= target < 300 SOL, results in num_accounts = 2
93+
94+ // Exactly 200 SOL, no remainder -> [100, 100]
95+ let epoch_stake = create_mock_epoch_stake ( 200_000_000_000 ) ;
96+ assert_eq ! ( epoch_stake. len( ) , 2 ) ;
97+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 200_000_000_000 ) ;
98+ assert ! ( epoch_stake. values( ) . all( |& s| s == 100_000_000_000 ) ) ;
99+
100+ // 250 SOL, with remainder -> [100, 150]
101+ let epoch_stake = create_mock_epoch_stake ( 250_000_000_000 ) ;
102+ assert_eq ! ( epoch_stake. len( ) , 2 ) ;
103+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 250_000_000_000 ) ;
104+ let mut stakes: Vec < u64 > = epoch_stake. values ( ) . copied ( ) . collect ( ) ;
105+ stakes. sort ( ) ;
106+ assert_eq ! ( stakes, vec![ 100_000_000_000 , 150_000_000_000 ] ) ;
107+
108+ // 299.999999999 SOL, with remainder -> [100, 199.999999999]
109+ let epoch_stake = create_mock_epoch_stake ( 299_999_999_999 ) ;
110+ assert_eq ! ( epoch_stake. len( ) , 2 ) ;
111+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 299_999_999_999 ) ;
112+ let mut stakes: Vec < u64 > = epoch_stake. values ( ) . copied ( ) . collect ( ) ;
113+ stakes. sort ( ) ;
114+ assert_eq ! ( stakes, vec![ 100_000_000_000 , 199_999_999_999 ] ) ;
115+ }
116+
117+ #[ test]
118+ fn test_num_accounts_greater_than_two ( ) {
119+ // target >= 300 SOL, results in num_accounts > 2
120+
121+ // Exactly 300 SOL, no remainder -> [100, 100, 100]
122+ let epoch_stake = create_mock_epoch_stake ( 300_000_000_000 ) ;
123+ assert_eq ! ( epoch_stake. len( ) , 3 ) ;
124+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 300_000_000_000 ) ;
125+ assert ! ( epoch_stake. values( ) . all( |& s| s == 100_000_000_000 ) ) ;
126+
127+ // 350 SOL, with remainder -> [100, 100, 150]
128+ let epoch_stake = create_mock_epoch_stake ( 350_000_000_000 ) ;
129+ assert_eq ! ( epoch_stake. len( ) , 3 ) ;
130+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 350_000_000_000 ) ;
131+ let mut stakes: Vec < u64 > = epoch_stake. values ( ) . copied ( ) . collect ( ) ;
132+ stakes. sort ( ) ;
133+ assert_eq ! (
134+ stakes,
135+ vec![ 100_000_000_000 , 100_000_000_000 , 150_000_000_000 ]
136+ ) ;
137+
138+ // 1000 SOL, no remainder -> [100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
139+ let epoch_stake = create_mock_epoch_stake ( 1_000_000_000_000 ) ;
140+ assert_eq ! ( epoch_stake. len( ) , 10 ) ;
141+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 1_000_000_000_000 ) ;
142+ assert ! ( epoch_stake. values( ) . all( |& s| s == 100_000_000_000 ) ) ;
143+
144+ // 1234.567890123 SOL, with remainder
145+ let epoch_stake = create_mock_epoch_stake ( 1_234_567_890_123 ) ;
146+ assert_eq ! ( epoch_stake. len( ) , 12 ) ;
147+ assert_eq ! ( epoch_stake. values( ) . sum:: <u64 >( ) , 1_234_567_890_123 ) ;
148+ let mut stakes: Vec < u64 > = epoch_stake. values ( ) . copied ( ) . collect ( ) ;
149+ stakes. sort ( ) ;
150+ // Should have 11 accounts with 100 SOL and 1 account with 134.567890123 SOL
151+ assert_eq ! ( stakes. iter( ) . filter( |&&s| s == 100_000_000_000 ) . count( ) , 11 ) ;
152+ assert_eq ! ( stakes. iter( ) . filter( |&&s| s == 134_567_890_123 ) . count( ) , 1 ) ;
153+ }
154+ }
0 commit comments