Line data Source code
1 : #include "fd_epoch_rewards.h"
2 : #include "../../ballet/siphash13/fd_siphash13.h"
3 :
4 : #define POOL_NAME fd_epoch_stake_reward_pool
5 36 : #define POOL_T fd_epoch_stake_reward_t
6 : #include "../../util/tmpl/fd_pool.c"
7 :
8 : #define MAP_NAME fd_epoch_stake_reward_map
9 : #define MAP_KEY_T fd_pubkey_t
10 : #define MAP_ELE_T fd_epoch_stake_reward_t
11 9 : #define MAP_KEY stake_pubkey
12 0 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
13 18 : #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) ))
14 18 : #define MAP_NEXT next_map
15 : #include "../../util/tmpl/fd_map_chain.c"
16 :
17 : ulong
18 198 : fd_epoch_rewards_align( void ) {
19 198 : return FD_EPOCH_REWARDS_ALIGN;
20 198 : }
21 :
22 : ulong
23 24 : fd_epoch_rewards_footprint( ulong stake_account_max ) {
24 24 : ulong chain_cnt_est = fd_epoch_stake_reward_map_chain_cnt_est( stake_account_max );
25 :
26 24 : ulong l = FD_LAYOUT_INIT;
27 24 : l = FD_LAYOUT_APPEND( l, fd_epoch_rewards_align(), sizeof(fd_epoch_rewards_t) );
28 24 : l = FD_LAYOUT_APPEND( l, fd_epoch_stake_reward_pool_align(), fd_epoch_stake_reward_pool_footprint( stake_account_max ) );
29 24 : l = FD_LAYOUT_APPEND( l, fd_epoch_stake_reward_map_align(), fd_epoch_stake_reward_map_footprint( chain_cnt_est ) );
30 24 : l = FD_LAYOUT_APPEND( l, fd_epoch_stake_reward_dlist_align(), fd_epoch_stake_reward_dlist_footprint() * FD_REWARDS_MAX_PARTITIONS );
31 24 : return FD_LAYOUT_FINI( l, fd_epoch_rewards_align() );
32 24 : }
33 :
34 : void *
35 9 : fd_epoch_rewards_new( void * shmem, ulong stake_account_max ) {
36 :
37 9 : if( FD_UNLIKELY( !shmem ) ) {
38 3 : FD_LOG_WARNING(( "NULL mem" ));
39 3 : return NULL;
40 3 : }
41 :
42 6 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_epoch_rewards_align() ) ) ) {
43 0 : FD_LOG_WARNING(( "misaligned mem" ));
44 0 : return NULL;
45 0 : }
46 :
47 6 : FD_SCRATCH_ALLOC_INIT( l, shmem );
48 6 : fd_epoch_rewards_t * epoch_rewards = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_rewards_align(), sizeof(fd_epoch_rewards_t) );
49 0 : memset( epoch_rewards, 0, sizeof(fd_epoch_rewards_t) );
50 :
51 6 : void * pool = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_pool_align(), fd_epoch_stake_reward_pool_footprint( stake_account_max ) );
52 0 : epoch_rewards->pool_offset = (ulong)pool - (ulong)shmem;
53 6 : if( FD_UNLIKELY( !fd_epoch_stake_reward_pool_new( pool, stake_account_max ) ) ) {
54 0 : FD_LOG_WARNING(( "bad pool" ));
55 0 : return NULL;
56 0 : }
57 :
58 6 : ulong chain_cnt_est = fd_epoch_stake_reward_map_chain_cnt_est( stake_account_max );
59 6 : void * map = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_map_align(), fd_epoch_stake_reward_map_footprint( chain_cnt_est ) );
60 0 : epoch_rewards->map_offset = (ulong)map - (ulong)shmem;
61 6 : if( FD_UNLIKELY( !fd_epoch_stake_reward_map_new( map, chain_cnt_est, 0UL ) ) ) {
62 0 : FD_LOG_WARNING(( "bad map" ));
63 0 : return NULL;
64 0 : }
65 :
66 4404 : for( ulong i=0UL; i<FD_REWARDS_MAX_PARTITIONS; i++ ) {
67 4398 : void * dlist = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_dlist_align(), fd_epoch_stake_reward_dlist_footprint() );
68 4398 : if( i==0UL ) epoch_rewards->dlists_offset = (ulong)dlist - (ulong)shmem;
69 4398 : if( FD_UNLIKELY( !fd_epoch_stake_reward_dlist_new( dlist ) ) ) {
70 0 : FD_LOG_WARNING(( "bad dlist at idx %lu", i ));
71 0 : return NULL;
72 0 : }
73 4398 : }
74 :
75 6 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_epoch_rewards_align() ) != (ulong)shmem+fd_epoch_rewards_footprint( stake_account_max ) ) ) {
76 0 : FD_LOG_WARNING(( "bad footprint" ));
77 0 : return NULL;
78 0 : }
79 :
80 :
81 6 : FD_COMPILER_MFENCE();
82 6 : epoch_rewards->magic = FD_EPOCH_REWARDS_MAGIC;
83 6 : FD_COMPILER_MFENCE();
84 :
85 6 : epoch_rewards->stake_account_max = stake_account_max;
86 :
87 6 : return shmem;
88 6 : }
89 :
90 : fd_epoch_rewards_t *
91 15 : fd_epoch_rewards_join( void * shmem ) {
92 15 : if( FD_UNLIKELY( !shmem ) ) {
93 3 : FD_LOG_WARNING(( "NULL mem" ));
94 3 : return NULL;
95 3 : }
96 :
97 12 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_epoch_rewards_align() ) ) ) {
98 0 : FD_LOG_WARNING(( "misaligned mem" ));
99 0 : return NULL;
100 0 : }
101 :
102 12 : FD_SCRATCH_ALLOC_INIT( l, shmem );
103 12 : fd_epoch_rewards_t * epoch_rewards = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_rewards_align(), sizeof(fd_epoch_rewards_t) );
104 0 : ulong stake_account_max = epoch_rewards->stake_account_max;
105 :
106 12 : void * pool = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_pool_align(), fd_epoch_stake_reward_pool_footprint( stake_account_max ) );
107 12 : if( FD_UNLIKELY( !fd_epoch_stake_reward_pool_join( pool ) ) ) {
108 0 : FD_LOG_WARNING(( "bad pool" ));
109 0 : return NULL;
110 0 : }
111 :
112 12 : ulong chain_cnt_est = fd_epoch_stake_reward_map_chain_cnt_est( stake_account_max );
113 12 : void * map = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_map_align(), fd_epoch_stake_reward_map_footprint( chain_cnt_est ) );
114 12 : if( FD_UNLIKELY( !fd_epoch_stake_reward_map_join( map ) ) ) {
115 0 : FD_LOG_WARNING(( "bad map" ));
116 0 : return NULL;
117 0 : }
118 :
119 8808 : for( ulong i=0UL; i<FD_REWARDS_MAX_PARTITIONS; i++ ) {
120 8796 : void * dlist = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_stake_reward_dlist_align(), fd_epoch_stake_reward_dlist_footprint() );
121 8796 : if( FD_UNLIKELY( !fd_epoch_stake_reward_dlist_join( dlist ) ) ) {
122 0 : FD_LOG_WARNING(( "bad dlist at idx %lu", i ));
123 0 : return NULL;
124 0 : }
125 8796 : }
126 :
127 12 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_epoch_rewards_align() )!=(ulong)shmem+fd_epoch_rewards_footprint( stake_account_max ) ) ) {
128 0 : FD_LOG_WARNING(( "bad footprint" ));
129 0 : return NULL;
130 0 : }
131 :
132 12 : if( FD_UNLIKELY( epoch_rewards->magic!=FD_EPOCH_REWARDS_MAGIC ) ) {
133 6 : FD_LOG_WARNING(( "bad magic" ));
134 6 : return NULL;
135 6 : }
136 :
137 6 : return epoch_rewards;
138 12 : }
139 :
140 : void *
141 6 : fd_epoch_rewards_leave( fd_epoch_rewards_t const * epoch_rewards ) {
142 6 : return (void *)epoch_rewards;
143 6 : }
144 :
145 : void *
146 3 : fd_epoch_rewards_delete( void * epoch_rewards_shmem ) {
147 3 : fd_epoch_rewards_t * epoch_rewards = (fd_epoch_rewards_t *)epoch_rewards_shmem;
148 :
149 3 : if( FD_UNLIKELY( !epoch_rewards ) ) {
150 0 : FD_LOG_WARNING(( "NULL epoch_rewards" ));
151 0 : return NULL;
152 0 : }
153 :
154 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)epoch_rewards, fd_epoch_rewards_align() ) ) ) {
155 0 : FD_LOG_WARNING(( "misaligned epoch_rewards" ));
156 0 : return NULL;
157 0 : }
158 :
159 3 : if( FD_UNLIKELY( epoch_rewards->magic != FD_EPOCH_REWARDS_MAGIC ) ) {
160 0 : FD_LOG_WARNING(( "bad magic" ));
161 0 : return NULL;
162 0 : }
163 :
164 3 : epoch_rewards->magic = 0UL;
165 :
166 3 : return epoch_rewards_shmem;
167 3 : }
168 :
169 : static inline fd_epoch_stake_reward_dlist_t *
170 15 : fd_epoch_rewards_get_partition_index( fd_epoch_rewards_t const * epoch_rewards, ulong idx ) {
171 15 : if( FD_UNLIKELY( idx>=epoch_rewards->num_partitions ) ) {
172 0 : FD_LOG_WARNING(( "idx: %lu is greater than num_partitions: %lu", idx, epoch_rewards->num_partitions ));
173 0 : return NULL;
174 0 : }
175 :
176 15 : fd_epoch_stake_reward_dlist_t * dlist_idx_zero = (fd_epoch_stake_reward_dlist_t *)((uchar *)epoch_rewards + epoch_rewards->dlists_offset);
177 15 : fd_epoch_stake_reward_dlist_t * partition_dlist = fd_epoch_stake_reward_dlist_join( dlist_idx_zero + idx );
178 15 : return partition_dlist;
179 15 : }
180 :
181 : static inline fd_epoch_stake_reward_t *
182 18 : fd_epoch_rewards_get_stake_reward_pool( fd_epoch_rewards_t const * epoch_rewards ) {
183 18 : return fd_epoch_stake_reward_pool_join( (uchar *)epoch_rewards + epoch_rewards->pool_offset );
184 18 : }
185 :
186 : static inline fd_epoch_stake_reward_map_t *
187 12 : fd_epoch_rewards_get_stake_reward_map( fd_epoch_rewards_t const * epoch_rewards ) {
188 12 : return fd_epoch_stake_reward_map_join( (uchar *)epoch_rewards + epoch_rewards->map_offset );
189 12 : }
190 :
191 : void
192 : fd_epoch_rewards_insert( fd_epoch_rewards_t * epoch_rewards,
193 : fd_pubkey_t const * pubkey,
194 : ulong credits,
195 9 : ulong lamports ) {
196 9 : fd_epoch_stake_reward_t * stake_reward_pool = fd_epoch_rewards_get_stake_reward_pool( epoch_rewards );
197 9 : fd_epoch_stake_reward_map_t * stake_reward_map = fd_epoch_rewards_get_stake_reward_map( epoch_rewards );
198 :
199 9 : if( FD_UNLIKELY( fd_epoch_stake_reward_map_ele_query( stake_reward_map, pubkey, NULL, stake_reward_pool ) ) ) {
200 0 : FD_LOG_CRIT(( "invariant violation: stake reward entry already exists" ));
201 0 : }
202 :
203 9 : fd_epoch_stake_reward_t * stake_reward = fd_epoch_stake_reward_pool_ele_acquire( stake_reward_pool );
204 :
205 9 : stake_reward->stake_pubkey = *pubkey;
206 9 : stake_reward->credits_observed = credits;
207 9 : stake_reward->lamports = lamports;
208 :
209 9 : fd_epoch_stake_reward_map_ele_insert( stake_reward_map, stake_reward, stake_reward_pool );
210 :
211 9 : epoch_rewards->total_stake_rewards += lamports;
212 9 : epoch_rewards->stake_rewards_cnt++;
213 :
214 9 : }
215 :
216 : void
217 : fd_epoch_rewards_hash_into_partitions( fd_epoch_rewards_t * epoch_rewards,
218 : fd_hash_t const * parent_blockhash,
219 3 : ulong num_partitions ) {
220 :
221 3 : fd_epoch_stake_reward_t * stake_reward_pool = fd_epoch_rewards_get_stake_reward_pool( epoch_rewards );
222 3 : fd_epoch_stake_reward_map_t * stake_reward_map = fd_epoch_rewards_get_stake_reward_map( epoch_rewards );
223 :
224 3 : epoch_rewards->num_partitions = num_partitions;
225 :
226 3 : for( fd_epoch_stake_reward_map_iter_t iter = fd_epoch_stake_reward_map_iter_init( stake_reward_map, stake_reward_pool );
227 12 : !fd_epoch_stake_reward_map_iter_done( iter, stake_reward_map, stake_reward_pool );
228 9 : iter = fd_epoch_stake_reward_map_iter_next( iter, stake_reward_map, stake_reward_pool ) ) {
229 :
230 9 : fd_epoch_stake_reward_t * stake_reward = fd_epoch_stake_reward_map_iter_ele( iter, stake_reward_map, stake_reward_pool );
231 :
232 9 : fd_siphash13_t sip[1] = {0};
233 9 : fd_siphash13_t * hasher = fd_siphash13_init( sip, 0UL, 0UL );
234 9 : hasher = fd_siphash13_append( hasher, parent_blockhash->hash, sizeof(fd_hash_t) );
235 9 : fd_siphash13_append( hasher, (uchar const *)stake_reward->stake_pubkey.uc, sizeof(fd_pubkey_t) );
236 9 : ulong hash64 = fd_siphash13_fini( hasher );
237 :
238 : /* Now get the correct dlist based on the hash. */
239 9 : ulong partition_index = (ulong)((uint128)num_partitions * (uint128) hash64 / ((uint128)ULONG_MAX + 1));
240 :
241 9 : fd_epoch_stake_reward_dlist_t * partition_dlist = fd_epoch_rewards_get_partition_index( epoch_rewards, partition_index );
242 :
243 9 : fd_epoch_stake_reward_dlist_ele_push_tail( partition_dlist, stake_reward, stake_reward_pool );
244 9 : }
245 3 : }
246 :
247 : fd_epoch_stake_reward_t *
248 9 : fd_epoch_rewards_iter_ele( fd_epoch_rewards_iter_t * iter ) {
249 9 : return fd_epoch_stake_reward_dlist_iter_ele( iter->iter, iter->dlist, iter->pool );
250 9 : }
251 :
252 : fd_epoch_rewards_iter_t *
253 : fd_epoch_rewards_iter_init( fd_epoch_rewards_iter_t * iter,
254 : fd_epoch_rewards_t const * epoch_rewards,
255 6 : ulong partition_idx ) {
256 6 : iter->pool = fd_epoch_rewards_get_stake_reward_pool( epoch_rewards );
257 6 : iter->dlist = fd_epoch_rewards_get_partition_index( epoch_rewards, partition_idx );
258 6 : iter->iter = fd_epoch_stake_reward_dlist_iter_fwd_init( iter->dlist, iter->pool );
259 6 : return iter;
260 6 : }
261 :
262 : int
263 15 : fd_epoch_rewards_iter_done( fd_epoch_rewards_iter_t * iter ) {
264 15 : return fd_epoch_stake_reward_dlist_iter_done( iter->iter, iter->dlist, iter->pool );
265 15 : }
266 :
267 : void
268 9 : fd_epoch_rewards_iter_next( fd_epoch_rewards_iter_t * iter ) {
269 9 : iter->iter = fd_epoch_stake_reward_dlist_iter_fwd_next( iter->iter, iter->dlist, iter->pool );
270 9 : }
|