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 12 : #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 4 : #define MAP_KEY index_key
33 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp( k0, k1, sizeof(index_key_t) ))
34 8 : #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(index_key_t) ))
35 4 : #define MAP_NEXT next
36 28 : #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 24 : #define POOL_T fork_t
46 36 : #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 4 : get_fork_pool( fd_stake_rewards_t const * stake_rewards ) {
86 4 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->fork_pool_offset );
87 4 : }
88 : static inline index_ele_t *
89 6 : get_index_pool( fd_stake_rewards_t const * stake_rewards ) {
90 6 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->index_pool_offset );
91 6 : }
92 : static inline index_map_t *
93 8 : get_index_map( fd_stake_rewards_t const * stake_rewards ) {
94 8 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->index_map_offset );
95 8 : }
96 :
97 : static inline partition_ele_t *
98 : get_partition_ele( fd_stake_rewards_t const * stake_rewards,
99 : uchar fork_idx,
100 8 : uint ele_cnt ) {
101 :
102 8 : return fd_type_pun( (uchar *)stake_rewards + stake_rewards->partitions_offset +
103 8 : (fork_idx * stake_rewards->max_stake_accounts * sizeof(partition_ele_t)) +
104 8 : (ele_cnt * sizeof(partition_ele_t)) );
105 8 : }
106 :
107 : ulong
108 300 : fd_stake_rewards_align( void ) {
109 300 : return FD_STAKE_REWARDS_ALIGN;
110 300 : }
111 :
112 : ulong
113 : fd_stake_rewards_footprint( ulong max_stake_accounts,
114 : ulong expected_stake_accs,
115 48 : ulong max_fork_width ) {
116 48 : ulong map_chain_cnt = index_map_chain_cnt_est( expected_stake_accs );
117 :
118 48 : ulong l = FD_LAYOUT_INIT;
119 48 : l = FD_LAYOUT_APPEND( l, fd_stake_rewards_align(), sizeof(fd_stake_rewards_t) );
120 48 : l = FD_LAYOUT_APPEND( l, fork_pool_align(), fork_pool_footprint( max_fork_width ) );
121 48 : l = FD_LAYOUT_APPEND( l, alignof(index_ele_t), sizeof(index_ele_t) * max_stake_accounts );
122 48 : l = FD_LAYOUT_APPEND( l, index_map_align(), index_map_footprint( map_chain_cnt ) );
123 48 : 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 48 : return FD_LAYOUT_FINI( l, fd_stake_rewards_align() );
129 48 : }
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 12 : ulong seed ) {
137 12 : if( FD_UNLIKELY( !shmem ) ) {
138 0 : FD_LOG_WARNING(( "NULL shmem" ));
139 0 : return NULL;
140 0 : }
141 12 : 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 12 : ulong map_chain_cnt = index_map_chain_cnt_est( expected_stake_accs );
147 :
148 12 : FD_SCRATCH_ALLOC_INIT( l, shmem );
149 12 : fd_stake_rewards_t * stake_rewards = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_rewards_align(), sizeof(fd_stake_rewards_t) );
150 12 : void * fork_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(), fork_pool_footprint( max_fork_width ) );
151 12 : void * index_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(index_ele_t), sizeof(index_ele_t) * max_stake_accounts );
152 12 : void * index_map_mem = FD_SCRATCH_ALLOC_APPEND( l, index_map_align(), index_map_footprint( map_chain_cnt ) );
153 12 : 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 12 : if( FD_UNLIKELY( !fork_pool ) ) {
157 0 : FD_LOG_WARNING(( "Failed to create fork pool" ));
158 0 : return NULL;
159 0 : }
160 12 : stake_rewards->fork_pool_offset = (ulong)fork_pool - (ulong)shmem;
161 :
162 12 : stake_rewards->index_pool_offset = (ulong)index_pool_mem - (ulong)shmem;
163 :
164 12 : index_map_t * index_map = index_map_join( index_map_new( index_map_mem, map_chain_cnt, seed ) );
165 12 : if( FD_UNLIKELY( !index_map ) ) {
166 0 : FD_LOG_WARNING(( "Failed to create index map" ));
167 0 : return NULL;
168 0 : }
169 12 : stake_rewards->index_map_offset = (ulong)index_map - (ulong)shmem;
170 12 : stake_rewards->partitions_offset = (ulong)partitions_mem - (ulong)shmem;
171 12 : stake_rewards->max_stake_accounts = max_stake_accounts;
172 12 : stake_rewards->epoch = ULONG_MAX;
173 12 : stake_rewards->total_ele_used = 0UL;
174 :
175 12 : FD_COMPILER_MFENCE();
176 12 : FD_VOLATILE( stake_rewards->magic ) = FD_STAKE_REWARDS_MAGIC;
177 12 : FD_COMPILER_MFENCE();
178 :
179 12 : return shmem;
180 12 : }
181 :
182 : fd_stake_rewards_t *
183 12 : fd_stake_rewards_join( void * shmem ) {
184 12 : if( FD_UNLIKELY( !shmem ) ) {
185 0 : FD_LOG_WARNING(( "NULL shmem" ));
186 0 : return NULL;
187 0 : }
188 :
189 12 : 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 12 : fd_stake_rewards_t * stake_rewards = (fd_stake_rewards_t *)shmem;
195 12 : 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 12 : return stake_rewards;
200 12 : }
201 :
202 : uchar
203 : fd_stake_rewards_init( fd_stake_rewards_t * stake_rewards,
204 : ulong epoch,
205 : fd_hash_t const * parent_blockhash,
206 : ulong starting_block_height,
207 4 : uint partitions_cnt ) {
208 4 : index_map_t * index_map = get_index_map( stake_rewards );
209 4 : fork_t * fork_pool = get_fork_pool( stake_rewards );
210 :
211 : /* If this is the first reference to the stake rewards, we need to
212 : reset the backing map and pool all the forks will share. */
213 4 : if( FD_LIKELY( stake_rewards->epoch!=epoch ) ) {
214 4 : fork_pool_reset( fork_pool );
215 4 : index_map_reset( index_map );
216 4 : stake_rewards->epoch = epoch;
217 4 : stake_rewards->total_ele_used = 0UL;
218 4 : }
219 :
220 4 : uchar fork_idx = (uchar)fork_pool_idx_acquire( fork_pool );
221 :
222 4 : stake_rewards->parent_blockhash = *parent_blockhash;
223 :
224 4 : stake_rewards->fork_info[fork_idx].partition_cnt = partitions_cnt;
225 4 : stake_rewards->fork_info[fork_idx].starting_block_height = starting_block_height;
226 4 : stake_rewards->fork_info[fork_idx].ele_cnt = 0UL;
227 4 : stake_rewards->fork_info[fork_idx].total_stake_rewards = 0UL;
228 4 : memset( stake_rewards->fork_info[fork_idx].partition_idxs_head, 0xFF, sizeof(stake_rewards->fork_info[fork_idx].partition_idxs_head) );
229 4 : memset( stake_rewards->fork_info[fork_idx].partition_idxs_tail, 0xFF, sizeof(stake_rewards->fork_info[fork_idx].partition_idxs_tail) );
230 :
231 4 : return fork_idx;
232 4 : }
233 :
234 : void
235 : fd_stake_rewards_insert( fd_stake_rewards_t * stake_rewards,
236 : uchar fork_idx,
237 : fd_pubkey_t const * pubkey,
238 : ulong lamports,
239 4 : ulong credits_observed ) {
240 4 : index_ele_t * index_ele = get_index_pool( stake_rewards );
241 4 : index_map_t * index_map = get_index_map( stake_rewards );
242 :
243 4 : index_key_t index_key = {
244 4 : .pubkey = *pubkey,
245 4 : .lamports = lamports,
246 4 : .credits_observed = credits_observed,
247 4 : };
248 :
249 4 : uint index = (uint)index_map_idx_query( index_map, &index_key, UINT_MAX, index_ele );
250 4 : if( FD_LIKELY( index==UINT_MAX ) ) {
251 4 : index = stake_rewards->total_ele_used;
252 4 : stake_rewards->total_ele_used++;
253 4 : if( FD_UNLIKELY( index>=stake_rewards->max_stake_accounts ) ) {
254 0 : FD_LOG_CRIT(( "invariant violation: index>=stake_rewards->max_stake_accounts" ));
255 0 : }
256 4 : index_ele_t * ele = (index_ele_t *)index_ele + index;
257 4 : ele->index_key = index_key;
258 4 : index_map_ele_insert( index_map, ele, index_ele );
259 4 : }
260 :
261 : /* We have an invariant that there can never be more than 8192 entries
262 : in a partition. */
263 4 : fd_siphash13_t sip[1] = {0};
264 4 : fd_siphash13_t * hasher = fd_siphash13_init( sip, 0UL, 0UL );
265 4 : hasher = fd_siphash13_append( hasher, stake_rewards->parent_blockhash.hash, sizeof(fd_hash_t) );
266 4 : fd_siphash13_append( hasher, (uchar const *)pubkey->uc, sizeof(fd_pubkey_t) );
267 4 : ulong hash64 = fd_siphash13_fini( hasher );
268 :
269 4 : ulong partition_index = (ulong)((uint128)stake_rewards->fork_info[fork_idx].partition_cnt * (uint128) hash64 / ((uint128)ULONG_MAX + 1));
270 :
271 4 : uint curr_fork_len = stake_rewards->fork_info[fork_idx].ele_cnt;
272 4 : if( FD_UNLIKELY( curr_fork_len>=stake_rewards->max_stake_accounts ) ) {
273 0 : FD_LOG_CRIT(( "invariant violation: curr_fork_len>=stake_rewards->max_stake_accounts" ));
274 0 : }
275 :
276 4 : partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, curr_fork_len );
277 4 : partition_ele->index = index;
278 4 : partition_ele->next = UINT_MAX;
279 :
280 4 : int is_first_ele = stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_index] == UINT_MAX;
281 :
282 4 : if( FD_LIKELY( !is_first_ele ) ) {
283 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] );
284 0 : prev_partition_ele->next = curr_fork_len;
285 0 : stake_rewards->fork_info[fork_idx].partition_idxs_tail[partition_index] = curr_fork_len;
286 4 : } else {
287 4 : stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_index] = curr_fork_len;
288 4 : stake_rewards->fork_info[fork_idx].partition_idxs_tail[partition_index] = curr_fork_len;
289 4 : }
290 :
291 4 : stake_rewards->fork_info[fork_idx].ele_cnt++;
292 4 : stake_rewards->fork_info[fork_idx].total_stake_rewards += lamports;
293 4 : }
294 :
295 : void
296 : fd_stake_rewards_iter_init( fd_stake_rewards_t * stake_rewards,
297 : uchar fork_idx,
298 2 : uint partition_idx ) {
299 2 : uint first_fork_idx = stake_rewards->fork_info[fork_idx].partition_idxs_head[partition_idx];
300 2 : stake_rewards->iter_curr_fork_idx = first_fork_idx;
301 2 : }
302 :
303 : void
304 : fd_stake_rewards_iter_next( fd_stake_rewards_t * stake_rewards,
305 2 : uchar fork_idx ) {
306 2 : partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, stake_rewards->iter_curr_fork_idx );
307 2 : stake_rewards->iter_curr_fork_idx = partition_ele->next;
308 2 : }
309 :
310 : int
311 4 : fd_stake_rewards_iter_done( fd_stake_rewards_t * stake_rewards ) {
312 4 : return stake_rewards->iter_curr_fork_idx == UINT_MAX;
313 4 : }
314 :
315 : void
316 : fd_stake_rewards_iter_ele( fd_stake_rewards_t * stake_rewards,
317 : uchar fork_idx,
318 : fd_pubkey_t * pubkey_out,
319 : ulong * lamports_out,
320 2 : ulong * credits_observed_out ) {
321 2 : partition_ele_t * partition_ele = get_partition_ele( stake_rewards, fork_idx, stake_rewards->iter_curr_fork_idx );
322 :
323 2 : index_ele_t * index_ele = get_index_pool( stake_rewards ) + partition_ele->index;
324 2 : *pubkey_out = index_ele->index_key.pubkey;
325 2 : *lamports_out = index_ele->index_key.lamports;
326 2 : *credits_observed_out = index_ele->index_key.credits_observed;
327 2 : }
328 :
329 : ulong
330 : fd_stake_rewards_total_rewards( fd_stake_rewards_t const * stake_rewards,
331 2 : uchar fork_idx ) {
332 2 : return stake_rewards->fork_info[fork_idx].total_stake_rewards;
333 2 : }
334 :
335 : uint
336 : fd_stake_rewards_num_partitions( fd_stake_rewards_t const * stake_rewards,
337 33 : uchar fork_idx ) {
338 33 : return stake_rewards->fork_info[fork_idx].partition_cnt;
339 33 : }
340 :
341 : ulong
342 : fd_stake_rewards_starting_block_height( fd_stake_rewards_t const * stake_rewards,
343 31 : uchar fork_idx ) {
344 31 : return stake_rewards->fork_info[fork_idx].starting_block_height;
345 31 : }
346 :
347 : ulong
348 : fd_stake_rewards_exclusive_ending_block_height( fd_stake_rewards_t const * stake_rewards,
349 31 : uchar fork_idx ) {
350 31 : return stake_rewards->fork_info[fork_idx].starting_block_height + stake_rewards->fork_info[fork_idx].partition_cnt;
351 31 : }
|