Line data Source code
1 : #include "fd_stake_rewards.h"
2 : #include "fd_rewards_base.h"
3 : #include "../../ballet/siphash13/fd_siphash13.h"
4 :
5 : #define MAX_SUPPORTED_FORKS (128UL)
6 :
7 369 : #define FD_STAKE_REWARDS_MAGIC (0xF17EDA2CE757A4E0) /* FIREDANCER STAKE V0 */
8 :
9 : struct index_key {
10 : fd_pubkey_t pubkey;
11 : ulong lamports;
12 : ulong credits_observed;
13 : };
14 : typedef struct index_key index_key_t;
15 :
16 : struct index_ele {
17 : union {
18 : struct {
19 : fd_pubkey_t pubkey;
20 : ulong lamports;
21 : ulong credits_observed;
22 : };
23 : index_key_t index_key;
24 : };
25 : uint next;
26 : };
27 : typedef struct index_ele index_ele_t;
28 :
29 : #define MAP_NAME index_map
30 : #define MAP_KEY_T index_key_t
31 : #define MAP_ELE_T index_ele_t
32 27 : #define MAP_KEY index_key
33 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp( k0, k1, sizeof(index_key_t) ))
34 54 : #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(index_key_t) ))
35 27 : #define MAP_NEXT next
36 519 : #define MAP_IDX_T uint
37 : #include "../../util/tmpl/fd_map_chain.c"
38 :
39 : struct fork {
40 : int next;
41 : };
42 : typedef struct fork fork_t;
43 :
44 : #define POOL_NAME fork_pool
45 738 : #define POOL_T fork_t
46 849 : #define POOL_NEXT next
47 : #define POOL_IDX_T int
48 : #include "../../util/tmpl/fd_pool.c"
49 :
50 : struct partition_ele {
51 : uint index;
52 : uint next;
53 : };
54 : typedef struct partition_ele partition_ele_t;
55 :
56 : struct fork_info {
57 : uint ele_cnt;
58 : uint partition_cnt;
59 : uint partition_idxs_head[MAX_PARTITIONS_PER_EPOCH];
60 : uint partition_idxs_tail[MAX_PARTITIONS_PER_EPOCH];
61 : ulong starting_block_height;
62 : ulong total_stake_rewards;
63 :
64 : };
65 : typedef struct fork_info fork_info_t;
66 :
67 : struct fd_stake_rewards {
68 : ulong magic;
69 : uint total_ele_used;
70 : ulong max_stake_accounts;
71 : fork_info_t fork_info[MAX_SUPPORTED_FORKS];
72 : ulong fork_pool_offset;
73 : ulong index_pool_offset;
74 : ulong index_map_offset;
75 : ulong partitions_offset;
76 : ulong epoch;
77 :
78 : /* Temporary storage for the current stake reward being computed. */
79 : fd_hash_t parent_blockhash;
80 : uint iter_curr_fork_idx;
81 : };
82 : typedef struct fd_stake_rewards fd_stake_rewards_t;
83 :
84 : static inline fork_t *
85 69 : get_fork_pool( fd_stake_rewards_t const * stake_rewards ) {
86 69 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->fork_pool_offset );
87 69 : }
88 : static inline index_ele_t *
89 33 : get_index_pool( fd_stake_rewards_t const * stake_rewards ) {
90 33 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->index_pool_offset );
91 33 : }
92 : static inline index_map_t *
93 96 : get_index_map( fd_stake_rewards_t const * stake_rewards ) {
94 96 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->index_map_offset );
95 96 : }
96 :
97 : static inline partition_ele_t *
98 : get_partition_ele( fd_stake_rewards_t const * stake_rewards,
99 : uchar fork_idx,
100 39 : uint ele_cnt ) {
101 :
102 39 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->partitions_offset +
103 39 : (fork_idx * stake_rewards->max_stake_accounts * sizeof(partition_ele_t)) +
104 39 : (ele_cnt * sizeof(partition_ele_t)) );
105 39 : }
106 :
107 : ulong
108 9558 : fd_stake_rewards_align( void ) {
109 9558 : return FD_STAKE_REWARDS_ALIGN;
110 9558 : }
111 :
112 : ulong
113 : fd_stake_rewards_footprint( ulong max_stake_accounts,
114 : ulong expected_stake_accs,
115 1470 : ulong max_fork_width ) {
116 1470 : ulong map_chain_cnt = index_map_chain_cnt_est( expected_stake_accs );
117 :
118 1470 : ulong l = FD_LAYOUT_INIT;
119 1470 : l = FD_LAYOUT_APPEND( l, fd_stake_rewards_align(), sizeof(fd_stake_rewards_t) );
120 1470 : l = FD_LAYOUT_APPEND( l, fork_pool_align(), fork_pool_footprint( max_fork_width ) );
121 1470 : l = FD_LAYOUT_APPEND( l, alignof(index_ele_t), sizeof(index_ele_t) * max_stake_accounts );
122 1470 : l = FD_LAYOUT_APPEND( l, index_map_align(), index_map_footprint( map_chain_cnt ) );
123 1470 : l = FD_LAYOUT_APPEND( l, alignof(partition_ele_t), max_fork_width * max_stake_accounts * sizeof(partition_ele_t) );
124 :
125 : /* we take advantage of the fact that the number of partitions * 8192
126 : is always == fd_ulong_align_up( max_stake_accounts, 8192UL ) */
127 :
128 1470 : return FD_LAYOUT_FINI( l, fd_stake_rewards_align() );
129 1470 : }
130 :
131 : void *
132 : fd_stake_rewards_new( void * shmem,
133 : ulong max_stake_accounts,
134 : ulong expected_stake_accs,
135 : ulong max_fork_width,
136 369 : ulong seed ) {
137 369 : if( FD_UNLIKELY( !shmem ) ) {
138 0 : FD_LOG_WARNING(( "NULL shmem" ));
139 0 : return NULL;
140 0 : }
141 369 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_stake_rewards_align() ) ) ) {
142 0 : FD_LOG_WARNING(( "misaligned shmem" ));
143 0 : return NULL;
144 0 : }
145 :
146 369 : ulong map_chain_cnt = index_map_chain_cnt_est( expected_stake_accs );
147 :
148 369 : FD_SCRATCH_ALLOC_INIT( l, shmem );
149 369 : fd_stake_rewards_t * stake_rewards = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_rewards_align(), sizeof(fd_stake_rewards_t) );
150 369 : void * fork_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(), fork_pool_footprint( max_fork_width ) );
151 369 : void * index_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(index_ele_t), sizeof(index_ele_t) * max_stake_accounts );
152 369 : void * index_map_mem = FD_SCRATCH_ALLOC_APPEND( l, index_map_align(), index_map_footprint( map_chain_cnt ) );
153 369 : void * partitions_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(partition_ele_t), max_fork_width * max_stake_accounts * sizeof(partition_ele_t) );
154 :
155 0 : fork_t * fork_pool = fork_pool_join( fork_pool_new( fork_pool_mem, max_fork_width ) );
156 369 : if( FD_UNLIKELY( !fork_pool ) ) {
157 0 : FD_LOG_WARNING(( "Failed to create fork pool" ));
158 0 : return NULL;
159 0 : }
160 369 : stake_rewards->fork_pool_offset = (ulong)fork_pool - (ulong)shmem;
161 :
162 369 : stake_rewards->index_pool_offset = (ulong)index_pool_mem - (ulong)shmem;
163 :
164 369 : index_map_t * index_map = index_map_join( index_map_new( index_map_mem, map_chain_cnt, seed ) );
165 369 : if( FD_UNLIKELY( !index_map ) ) {
166 0 : FD_LOG_WARNING(( "Failed to create index map" ));
167 0 : return NULL;
168 0 : }
169 369 : stake_rewards->index_map_offset = (ulong)index_map - (ulong)shmem;
170 369 : stake_rewards->partitions_offset = (ulong)partitions_mem - (ulong)shmem;
171 369 : stake_rewards->max_stake_accounts = max_stake_accounts;
172 369 : stake_rewards->epoch = ULONG_MAX;
173 369 : stake_rewards->total_ele_used = 0UL;
174 :
175 369 : FD_COMPILER_MFENCE();
176 369 : FD_VOLATILE( stake_rewards->magic ) = FD_STAKE_REWARDS_MAGIC;
177 369 : FD_COMPILER_MFENCE();
178 :
179 369 : return shmem;
180 369 : }
181 :
182 : fd_stake_rewards_t *
183 738 : fd_stake_rewards_join( void * shmem ) {
184 738 : if( FD_UNLIKELY( !shmem ) ) {
185 0 : FD_LOG_WARNING(( "NULL shmem" ));
186 0 : return NULL;
187 0 : }
188 :
189 738 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_stake_rewards_align() ) ) ) {
190 0 : FD_LOG_WARNING(( "misaligned shmem" ));
191 0 : return NULL;
192 0 : }
193 :
194 738 : fd_stake_rewards_t * stake_rewards = (fd_stake_rewards_t *)shmem;
195 738 : if( FD_UNLIKELY( stake_rewards->magic != FD_STAKE_REWARDS_MAGIC ) ) {
196 0 : FD_LOG_WARNING(( "Invalid stake rewards magic" ));
197 0 : return NULL;
198 0 : }
199 738 : return stake_rewards;
200 738 : }
201 :
202 : void
203 42 : fd_stake_rewards_clear( fd_stake_rewards_t * stake_rewards ) {
204 42 : fork_pool_reset( get_fork_pool( stake_rewards ) );
205 42 : index_map_reset( get_index_map( stake_rewards ) );
206 42 : stake_rewards->epoch = ULONG_MAX;
207 42 : stake_rewards->total_ele_used = 0UL;
208 42 : }
209 :
210 : uchar
211 : fd_stake_rewards_init( fd_stake_rewards_t * stake_rewards,
212 : ulong epoch,
213 : fd_hash_t const * parent_blockhash,
214 : ulong starting_block_height,
215 27 : uint partitions_cnt ) {
216 27 : index_map_t * index_map = get_index_map( stake_rewards );
217 27 : fork_t * fork_pool = get_fork_pool( stake_rewards );
218 :
219 : /* If this is the first reference to the stake rewards, we need to
220 : reset the backing map and pool all the forks will share. */
221 27 : if( FD_LIKELY( stake_rewards->epoch!=epoch ) ) {
222 27 : fork_pool_reset( fork_pool );
223 27 : index_map_reset( index_map );
224 27 : stake_rewards->epoch = epoch;
225 27 : stake_rewards->total_ele_used = 0UL;
226 27 : }
227 :
228 27 : uchar fork_idx = (uchar)fork_pool_idx_acquire( fork_pool );
229 :
230 27 : stake_rewards->parent_blockhash = *parent_blockhash;
231 :
232 27 : stake_rewards->fork_info[fork_idx].partition_cnt = partitions_cnt;
233 27 : stake_rewards->fork_info[fork_idx].starting_block_height = starting_block_height;
234 27 : stake_rewards->fork_info[fork_idx].ele_cnt = 0UL;
235 27 : stake_rewards->fork_info[fork_idx].total_stake_rewards = 0UL;
236 27 : memset( stake_rewards->fork_info[fork_idx].partition_idxs_head, 0xFF, sizeof(stake_rewards->fork_info[fork_idx].partition_idxs_head) );
237 27 : memset( stake_rewards->fork_info[fork_idx].partition_idxs_tail, 0xFF, sizeof(stake_rewards->fork_info[fork_idx].partition_idxs_tail) );
238 :
239 27 : return fork_idx;
240 27 : }
241 :
242 : void
243 : fd_stake_rewards_insert( fd_stake_rewards_t * stake_rewards,
244 : uchar fork_idx,
245 : fd_pubkey_t const * pubkey,
246 : ulong lamports,
247 27 : ulong credits_observed ) {
248 27 : index_ele_t * index_ele = get_index_pool( stake_rewards );
249 27 : index_map_t * index_map = get_index_map( stake_rewards );
250 :
251 27 : index_key_t index_key = {
252 27 : .pubkey = *pubkey,
253 27 : .lamports = lamports,
254 27 : .credits_observed = credits_observed,
255 27 : };
256 :
257 27 : uint index = (uint)index_map_idx_query( index_map, &index_key, UINT_MAX, index_ele );
258 27 : if( FD_LIKELY( index==UINT_MAX ) ) {
259 27 : index = stake_rewards->total_ele_used;
260 27 : stake_rewards->total_ele_used++;
261 27 : if( FD_UNLIKELY( index>=stake_rewards->max_stake_accounts ) ) {
262 0 : FD_LOG_CRIT(( "invariant violation: index>=stake_rewards->max_stake_accounts" ));
263 0 : }
264 27 : index_ele_t * ele = (index_ele_t *)index_ele + index;
265 27 : ele->index_key = index_key;
266 27 : index_map_ele_insert( index_map, ele, index_ele );
267 27 : }
268 :
269 : /* We have an invariant that there can never be more than 8192 entries
270 : in a partition. */
271 27 : fd_siphash13_t sip[1] = {0};
272 27 : fd_siphash13_t * hasher = fd_siphash13_init( sip, 0UL, 0UL );
273 27 : hasher = fd_siphash13_append( hasher, stake_rewards->parent_blockhash.hash, sizeof(fd_hash_t) );
274 27 : fd_siphash13_append( hasher, (uchar const *)pubkey->uc, sizeof(fd_pubkey_t) );
275 27 : ulong hash64 = fd_siphash13_fini( hasher );
276 :
277 27 : ulong partition_index = (ulong)((uint128)stake_rewards->fork_info[fork_idx].partition_cnt * (uint128) hash64 / ((uint128)ULONG_MAX + 1));
278 :
279 27 : uint curr_fork_len = stake_rewards->fork_info[fork_idx].ele_cnt;
280 27 : if( FD_UNLIKELY( curr_fork_len>=stake_rewards->max_stake_accounts ) ) {
281 0 : FD_LOG_CRIT(( "invariant violation: curr_fork_len>=stake_rewards->max_stake_accounts" ));
282 0 : }
283 :
284 27 : partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, curr_fork_len );
285 27 : partition_ele->index = index;
286 27 : partition_ele->next = UINT_MAX;
287 :
288 27 : int is_first_ele = stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_index] == UINT_MAX;
289 :
290 27 : if( FD_LIKELY( !is_first_ele ) ) {
291 0 : partition_ele_t * prev_partition_ele = get_partition_ele( stake_rewards, fork_idx, stake_rewards->fork_info[fork_idx].partition_idxs_tail[partition_index] );
292 0 : prev_partition_ele->next = curr_fork_len;
293 0 : stake_rewards->fork_info[fork_idx].partition_idxs_tail[partition_index] = curr_fork_len;
294 27 : } else {
295 27 : stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_index] = curr_fork_len;
296 27 : stake_rewards->fork_info[fork_idx].partition_idxs_tail[partition_index] = curr_fork_len;
297 27 : }
298 :
299 27 : stake_rewards->fork_info[fork_idx].ele_cnt++;
300 27 : stake_rewards->fork_info[fork_idx].total_stake_rewards += lamports;
301 27 : }
302 :
303 : void
304 : fd_stake_rewards_iter_init( fd_stake_rewards_t * stake_rewards,
305 : uchar fork_idx,
306 6 : uint partition_idx ) {
307 6 : uint first_fork_idx = stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_idx];
308 6 : stake_rewards->iter_curr_fork_idx = first_fork_idx;
309 6 : }
310 :
311 : void
312 : fd_stake_rewards_iter_next( fd_stake_rewards_t * stake_rewards,
313 6 : uchar fork_idx ) {
314 6 : partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, stake_rewards->iter_curr_fork_idx );
315 6 : stake_rewards->iter_curr_fork_idx = partition_ele->next;
316 6 : }
317 :
318 : int
319 12 : fd_stake_rewards_iter_done( fd_stake_rewards_t * stake_rewards ) {
320 12 : return stake_rewards->iter_curr_fork_idx == UINT_MAX;
321 12 : }
322 :
323 : void
324 : fd_stake_rewards_iter_ele( fd_stake_rewards_t * stake_rewards,
325 : uchar fork_idx,
326 : fd_pubkey_t * pubkey_out,
327 : ulong * lamports_out,
328 6 : ulong * credits_observed_out ) {
329 6 : partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, stake_rewards->iter_curr_fork_idx );
330 :
331 6 : index_ele_t * index_ele = get_index_pool( stake_rewards ) + partition_ele->index;
332 6 : *pubkey_out = index_ele->index_key.pubkey;
333 6 : *lamports_out = index_ele->index_key.lamports;
334 6 : *credits_observed_out = index_ele->index_key.credits_observed;
335 6 : }
336 :
337 : ulong
338 : fd_stake_rewards_total_rewards( fd_stake_rewards_t const * stake_rewards,
339 27 : uchar fork_idx ) {
340 27 : return stake_rewards->fork_info[fork_idx].total_stake_rewards;
341 27 : }
342 :
343 : uint
344 : fd_stake_rewards_num_partitions( fd_stake_rewards_t const * stake_rewards,
345 60 : uchar fork_idx ) {
346 60 : return stake_rewards->fork_info[fork_idx].partition_cnt;
347 60 : }
348 :
349 : ulong
350 : fd_stake_rewards_starting_block_height( fd_stake_rewards_t const * stake_rewards,
351 33 : uchar fork_idx ) {
352 33 : return stake_rewards->fork_info[fork_idx].starting_block_height;
353 33 : }
354 :
355 : ulong
356 : fd_stake_rewards_exclusive_ending_block_height( fd_stake_rewards_t const * stake_rewards,
357 33 : uchar fork_idx ) {
358 33 : return stake_rewards->fork_info[fork_idx].starting_block_height + stake_rewards->fork_info[fork_idx].partition_cnt;
359 33 : }
|