Line data Source code
1 : #include "../tower/fd_tower.h"
2 : #include "fd_hfork.h"
3 :
4 : /* fd_hfork maintains four pools and four maps:
5 :
6 : bhm_pool (capacity max = per_vtr_max * vtr_max): pool of bhm_t
7 : elements, where bhm stands for "bank hash matcher". Each
8 : bhm tracks the aggregate stake and vote count for a
9 : particular (block_id, bank_hash) pair.
10 :
11 : bhm_map (capacity max): maps bhm_key_t (block_id, bank_hash) ->
12 : bhm_t for O(1) lookup of a specific bank hash matcher.
13 :
14 : blk_pool (capacity max): pool of blk_t elements. Each blk_t stores
15 : per-block metadata (our_bank_hash, replayed, dead, matched,
16 : mismatched, checked) and owns a bhm_dlist of all bhm entries sharing the
17 : same block_id.
18 :
19 : blk_map (capacity max): maps block_id -> blk_t for O(1) lookup of
20 : per-block metadata.
21 :
22 : vte_pool (capacity max): pool of vte_t elements. Each vte
23 : records a single vote (block_id, bank_hash, slot, stake)
24 : from a voter, stored in that voter's vte_dlist. When a
25 : voter's vte_dlist reaches per_vtr_max entries, the oldest vte
26 : is popped and its stake contribution is subtracted from
27 : the corresponding bhm.
28 :
29 : vte_map (capacity max): maps vte_key_t (addr, block_id) -> vte_t
30 : for O(1) check of whether a voter has already voted for a
31 : given block_id. If they have, the vote is ignored.
32 :
33 : vtr_map (capacity vtr_max): maps addr -> vtr_t, tracking each
34 : known voter. vtr entries are explicitly managed by
35 : fd_hfork_update_voters when the epoch stake set changes.
36 : Each vtr has a pre-allocated vte_dlist that tracks the
37 : voter's recent votes in FIFO order.
38 :
39 : vtr_map blk_map
40 : map[0] +--------------------+ map[0] +--------------------+
41 : | (vtr_t) { | | (blk_t) { |
42 : | .addr = X, | | .block_id = A, |
43 : | ... | | ... |
44 : | .vte_dlist = ... | | .bhm_dlist = ... |
45 : | } | | } |
46 : map[1] +--------------------+ map[1] +--------------------+
47 : | (vtr_t) { | | (blk_t) { |
48 : | .addr = Y, | | .block_id = B |
49 : | ... | | ... |
50 : | .vte_dlist = + | | .bhm_dlist = + |
51 : | } | | | } | |
52 : +----------------|---+ +----------------|---+ |
53 : | |
54 : | |
55 : | |
56 : | bhm_dlist <------+
57 : | +--------------+--------------+--------------+
58 : | | (bhm_t) { | (bhm_t) { | (bhm_t) { |
59 : | | .key = A0, | .key = A1, | .key = A2, |
60 : | | ... | ... | ... |
61 : | | } | } | } |
62 : | +--------------+--------------+--------------+
63 : |
64 : V
65 : vte_dlist
66 : +------------------------+------------------------+------------------------+
67 : | (vte_t) { | (vte_t) { | (vte_t) { |
68 : | .key.addr = Y, | .key.addr = Y, | .key.addr = Y, |
69 : | .key.block_id = A, | .key.block_id = C, | .key.block_id = B, |
70 : | .bank_hash = A0, | .bank_hash = C0, | .bank_hash = B0, |
71 : | ... | ... | ... |
72 : | } | } | } |
73 : +------------------------+------------------------+------------------------+
74 : oldest newest
75 :
76 : vte_map prevents a voter from voting for the same block_id twice.
77 : The adversary is bounded because when vte_cnt == per_vtr_max, the
78 : oldest vte is popped and its stake is subtracted from the matching
79 : bhm. */
80 :
81 : typedef struct {
82 : fd_hash_t block_id;
83 : fd_hash_t bank_hash;
84 : } bhm_key_t;
85 :
86 : struct bhm {
87 : bhm_key_t key; /* bhm_map key */
88 : ulong next; /* pool next */
89 : struct {
90 : ulong prev;
91 : ulong next;
92 : } map;
93 : struct {
94 : ulong prev;
95 : ulong next;
96 : } dlist; /* bhm_dlist (owned by blk_t) */
97 : ulong slot;
98 : ulong stake;
99 : ulong vtr_cnt;
100 : };
101 : typedef struct bhm bhm_t;
102 :
103 : #define POOL_NAME bhm_pool
104 18 : #define POOL_T bhm_t
105 : #include "../../util/tmpl/fd_pool.c"
106 :
107 : #define MAP_NAME bhm_map
108 6 : #define MAP_ELE_T bhm_t
109 : #define MAP_KEY_T bhm_key_t
110 153 : #define MAP_PREV map.prev
111 291 : #define MAP_NEXT map.next
112 210 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->block_id.key,(k1)->block_id.key,32UL) & \
113 210 : !memcmp((k0)->bank_hash.key,(k1)->bank_hash.key,32UL))
114 123 : #define MAP_KEY_HASH(key,seed) ((ulong)((key)->block_id.ul[1]^(key)->bank_hash.ul[1]^(seed)))
115 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
116 : #include "../../util/tmpl/fd_map_chain.c"
117 :
118 : #define DLIST_NAME bhm_dlist
119 : #define DLIST_ELE_T bhm_t
120 36 : #define DLIST_PREV dlist.prev
121 39 : #define DLIST_NEXT dlist.next
122 : #include "../../util/tmpl/fd_dlist.c"
123 :
124 : typedef fd_hfork_blk_t blk_t;
125 :
126 : #define POOL_NAME blk_pool
127 27 : #define POOL_T blk_t
128 : #include "../../util/tmpl/fd_pool.c"
129 :
130 : #define MAP_NAME blk_map
131 6 : #define MAP_ELE_T blk_t
132 : #define MAP_KEY_T fd_hash_t
133 27 : #define MAP_KEY block_id
134 57 : #define MAP_PREV prev
135 96 : #define MAP_NEXT next
136 75 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->key,(k1)->key,32UL))
137 78 : #define MAP_KEY_HASH(key,seed) ((ulong)((key)->ul[1]^(seed)))
138 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
139 : #include "../../util/tmpl/fd_map_chain.c"
140 :
141 : typedef struct {
142 : fd_pubkey_t addr;
143 : fd_hash_t block_id;
144 : } vte_key_t;
145 :
146 : struct vte {
147 : vte_key_t key; /* vte_map key: (addr, block_id) */
148 : ulong next;
149 : struct {
150 : ulong prev;
151 : ulong next;
152 : } vte_map;
153 : struct {
154 : ulong prev;
155 : ulong next;
156 : } dlist;
157 : fd_hash_t bank_hash;
158 : ulong slot;
159 : ulong stake;
160 : };
161 : typedef struct vte vte_t;
162 :
163 : #define POOL_NAME vte_pool
164 18 : #define POOL_T vte_t
165 : #include "../../util/tmpl/fd_pool.c"
166 :
167 : #define MAP_NAME vte_map
168 6 : #define MAP_ELE_T vte_t
169 : #define MAP_KEY_T vte_key_t
170 63 : #define MAP_PREV vte_map.prev
171 96 : #define MAP_NEXT vte_map.next
172 54 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->addr.key,(k1)->addr.key,32UL) & \
173 54 : !memcmp((k0)->block_id.key,(k1)->block_id.key,32UL))
174 69 : #define MAP_KEY_HASH(key,seed) ((ulong)((key)->addr.ul[1]^(key)->block_id.ul[1]^(seed)))
175 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
176 : #include "../../util/tmpl/fd_map_chain.c"
177 :
178 : #define DLIST_NAME vte_dlist
179 : #define DLIST_ELE_T vte_t
180 33 : #define DLIST_PREV dlist.prev
181 39 : #define DLIST_NEXT dlist.next
182 : #include "../../util/tmpl/fd_dlist.c"
183 :
184 : struct vtr {
185 : fd_pubkey_t addr;
186 : ulong next; /* pool next; reused as kept flag during update_voters */
187 : struct {
188 : ulong prev;
189 : ulong next;
190 : } map;
191 : struct {
192 : ulong prev;
193 : ulong next;
194 : } dlist;
195 : ulong vte_cnt;
196 : vte_dlist_t * vte_dlist;
197 : };
198 : typedef struct vtr vtr_t;
199 :
200 : #define POOL_NAME vtr_pool
201 27 : #define POOL_T vtr_t
202 : #include "../../util/tmpl/fd_pool.c"
203 :
204 : #define MAP_NAME vtr_map
205 0 : #define MAP_ELE_T vtr_t
206 : #define MAP_KEY_T fd_pubkey_t
207 30 : #define MAP_KEY addr
208 105 : #define MAP_PREV map.prev
209 141 : #define MAP_NEXT map.next
210 96 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->key,(k1)->key,sizeof(fd_pubkey_t)))
211 66 : #define MAP_KEY_HASH(key,seed) ((ulong)((key)->ul[1]^(seed)))
212 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
213 : #include "../../util/tmpl/fd_map_chain.c"
214 :
215 : #define DLIST_NAME vtr_dlist
216 : #define DLIST_ELE_T vtr_t
217 30 : #define DLIST_PREV dlist.prev
218 30 : #define DLIST_NEXT dlist.next
219 : #include "../../util/tmpl/fd_dlist.c"
220 :
221 : struct __attribute__((aligned(128UL))) fd_hfork {
222 : ulong max;
223 : ulong per_vtr_max;
224 : ulong vtr_max;
225 : bhm_t * bhm_pool;
226 : bhm_map_t * bhm_map;
227 : blk_t * blk_pool;
228 : blk_map_t * blk_map;
229 : vte_t * vte_pool;
230 : vte_map_t * vte_map;
231 : vtr_t * vtr_pool;
232 : vtr_map_t * vtr_map;
233 : vtr_dlist_t * vtr_dlist;
234 : };
235 : typedef struct fd_hfork fd_hfork_t;
236 :
237 :
238 : /* bhm_remove removes a bhm from bhm_map, its owning blk's bhm_dlist,
239 : and releases it back to bhm_pool. If the blk has no remaining bhm
240 : entries, the blk is also removed and released. */
241 :
242 : static void
243 : bhm_remove( fd_hfork_t * hfork,
244 6 : bhm_t * bhm ) {
245 6 : blk_t * blk = blk_map_ele_query( hfork->blk_map, &bhm->key.block_id, NULL, hfork->blk_pool );
246 6 : if( FD_LIKELY( blk ) ) {
247 6 : bhm_dlist_ele_remove( blk->bhm_dlist, bhm, hfork->bhm_pool );
248 6 : blk->bhm_cnt--;
249 6 : if( FD_UNLIKELY( !blk->bhm_cnt ) ) {
250 6 : blk_map_ele_remove_fast( hfork->blk_map, blk, hfork->blk_pool );
251 6 : blk_pool_ele_release( hfork->blk_pool, blk );
252 6 : }
253 6 : }
254 6 : bhm_map_ele_remove_fast( hfork->bhm_map, bhm, hfork->bhm_pool );
255 6 : bhm_pool_ele_release( hfork->bhm_pool, bhm );
256 6 : }
257 :
258 : static int
259 : check( blk_t * blk,
260 : bhm_t * bhm,
261 36 : ulong total_stake ) {
262 :
263 36 : if( FD_UNLIKELY( blk->flag ) ) return blk->flag;
264 36 : if( FD_UNLIKELY( !blk->replayed ) ) return 0;
265 3 : if( FD_UNLIKELY( !total_stake ) ) return 0;
266 :
267 3 : double pct = (double)bhm->stake * 100.0 / (double)total_stake;
268 3 : if( FD_UNLIKELY( pct < 52.0 ) ) return 0;
269 :
270 3 : if( FD_UNLIKELY( blk->dead ) ) return -1;
271 3 : if( FD_UNLIKELY( 0!=memcmp( &blk->our_bank_hash, &bhm->key.bank_hash, 32UL ) ) ) return -1;
272 3 : return 1;
273 3 : }
274 :
275 : ulong
276 90 : fd_hfork_align( void ) {
277 90 : return 128UL;
278 90 : }
279 :
280 : ulong
281 : fd_hfork_footprint( ulong per_vtr_max,
282 18 : ulong vtr_max ) {
283 :
284 18 : ulong max = per_vtr_max * vtr_max;
285 :
286 18 : ulong l = FD_LAYOUT_INIT;
287 18 : l = FD_LAYOUT_APPEND( l, alignof(fd_hfork_t), sizeof(fd_hfork_t) );
288 18 : l = FD_LAYOUT_APPEND( l, bhm_pool_align(), bhm_pool_footprint( max ) );
289 18 : l = FD_LAYOUT_APPEND( l, bhm_map_align(), bhm_map_footprint( bhm_map_chain_cnt_est( max ) ) );
290 18 : l = FD_LAYOUT_APPEND( l, blk_pool_align(), blk_pool_footprint( max ) );
291 18 : l = FD_LAYOUT_APPEND( l, blk_map_align(), blk_map_footprint( blk_map_chain_cnt_est( max ) ) );
292 18 : l = FD_LAYOUT_APPEND( l, vte_pool_align(), vte_pool_footprint( max ) );
293 18 : l = FD_LAYOUT_APPEND( l, vte_map_align(), vte_map_footprint( vte_map_chain_cnt_est( max ) ) );
294 18 : l = FD_LAYOUT_APPEND( l, vtr_pool_align(), vtr_pool_footprint( vtr_max ) );
295 18 : l = FD_LAYOUT_APPEND( l, vtr_map_align(), vtr_map_footprint( vtr_map_chain_cnt_est( vtr_max ) ) );
296 18 : l = FD_LAYOUT_APPEND( l, vtr_dlist_align(), vtr_dlist_footprint() );
297 438 : for( ulong i = 0UL; i < max; i++ ) {
298 420 : l = FD_LAYOUT_APPEND( l, bhm_dlist_align(), bhm_dlist_footprint() );
299 420 : }
300 78 : for( ulong i = 0UL; i < vtr_max; i++ ) {
301 60 : l = FD_LAYOUT_APPEND( l, vte_dlist_align(), vte_dlist_footprint() );
302 60 : }
303 18 : return FD_LAYOUT_FINI( l, fd_hfork_align() );
304 18 : }
305 :
306 : void *
307 : fd_hfork_new( void * shmem,
308 : ulong per_vtr_max,
309 : ulong vtr_max,
310 9 : ulong seed ) {
311 :
312 9 : if( FD_UNLIKELY( !shmem ) ) {
313 0 : FD_LOG_WARNING(( "NULL mem" ));
314 0 : return NULL;
315 0 : }
316 :
317 9 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_hfork_align() ) ) ) {
318 0 : FD_LOG_WARNING(( "misaligned mem" ));
319 0 : return NULL;
320 0 : }
321 :
322 9 : ulong footprint = fd_hfork_footprint( per_vtr_max, vtr_max );
323 9 : if( FD_UNLIKELY( !footprint ) ) {
324 0 : FD_LOG_WARNING(( "bad per_vtr_max (%lu) or vtr_max (%lu)", per_vtr_max, vtr_max ));
325 0 : return NULL;
326 0 : }
327 :
328 9 : fd_memset( shmem, 0, footprint );
329 :
330 9 : ulong max = per_vtr_max * vtr_max;
331 :
332 9 : FD_SCRATCH_ALLOC_INIT( l, shmem );
333 9 : fd_hfork_t * hfork = FD_SCRATCH_ALLOC_APPEND( l, fd_hfork_align(), sizeof(fd_hfork_t) );
334 9 : void * bhm_pool = FD_SCRATCH_ALLOC_APPEND( l, bhm_pool_align(), bhm_pool_footprint( max ) );
335 9 : void * bhm_map = FD_SCRATCH_ALLOC_APPEND( l, bhm_map_align(), bhm_map_footprint( bhm_map_chain_cnt_est( max ) ) );
336 9 : void * blk_pool = FD_SCRATCH_ALLOC_APPEND( l, blk_pool_align(), blk_pool_footprint( max ) );
337 9 : void * blk_map = FD_SCRATCH_ALLOC_APPEND( l, blk_map_align(), blk_map_footprint( blk_map_chain_cnt_est( max ) ) );
338 9 : void * vte_pool = FD_SCRATCH_ALLOC_APPEND( l, vte_pool_align(), vte_pool_footprint( max ) );
339 9 : void * vte_map = FD_SCRATCH_ALLOC_APPEND( l, vte_map_align(), vte_map_footprint( vte_map_chain_cnt_est( max ) ) );
340 9 : void * vtr_pool = FD_SCRATCH_ALLOC_APPEND( l, vtr_pool_align(), vtr_pool_footprint( vtr_max ) );
341 9 : void * vtr_map = FD_SCRATCH_ALLOC_APPEND( l, vtr_map_align(), vtr_map_footprint( vtr_map_chain_cnt_est( vtr_max ) ) );
342 9 : void * vtr_dlist = FD_SCRATCH_ALLOC_APPEND( l, vtr_dlist_align(), vtr_dlist_footprint() );
343 :
344 0 : hfork->max = max;
345 9 : hfork->per_vtr_max = per_vtr_max;
346 9 : hfork->vtr_max = vtr_max;
347 9 : hfork->bhm_pool = bhm_pool_new( bhm_pool, max );
348 9 : hfork->bhm_map = bhm_map_new( bhm_map, bhm_map_chain_cnt_est( max ), seed );
349 9 : hfork->blk_pool = blk_pool_new( blk_pool, max );
350 9 : hfork->blk_map = blk_map_new( blk_map, blk_map_chain_cnt_est( max ), seed );
351 9 : hfork->vte_pool = vte_pool_new( vte_pool, max );
352 9 : hfork->vte_map = vte_map_new( vte_map, vte_map_chain_cnt_est( max ), seed );
353 9 : hfork->vtr_pool = vtr_pool_new( vtr_pool, vtr_max );
354 9 : hfork->vtr_map = vtr_map_new( vtr_map, vtr_map_chain_cnt_est( vtr_max ), seed );
355 9 : hfork->vtr_dlist = vtr_dlist_new( vtr_dlist );
356 :
357 9 : blk_t * blk_join = blk_pool_join( hfork->blk_pool );
358 219 : for( ulong i = 0UL; i < max; i++ ) {
359 210 : void * bhm_dlist = FD_SCRATCH_ALLOC_APPEND( l, bhm_dlist_align(), bhm_dlist_footprint() );
360 0 : blk_join[i].bhm_cnt = 0;
361 210 : blk_join[i].bhm_dlist = bhm_dlist_new( bhm_dlist );
362 210 : }
363 :
364 9 : vtr_t * vtr_join = vtr_pool_join( hfork->vtr_pool );
365 39 : for( ulong i = 0UL; i < vtr_max; i++ ) {
366 30 : void * vte_dlist = FD_SCRATCH_ALLOC_APPEND( l, vte_dlist_align(), vte_dlist_footprint() );
367 0 : vtr_join[i].vte_cnt = 0;
368 30 : vtr_join[i].vte_dlist = vte_dlist_new( vte_dlist );
369 30 : }
370 9 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_hfork_align() ) == (ulong)shmem + footprint );
371 9 : return shmem;
372 9 : }
373 :
374 : fd_hfork_t *
375 9 : fd_hfork_join( void * shhfork ) {
376 9 : fd_hfork_t * hfork = (fd_hfork_t *)shhfork;
377 :
378 9 : if( FD_UNLIKELY( !hfork ) ) {
379 0 : FD_LOG_WARNING(( "NULL hfork" ));
380 0 : return NULL;
381 0 : }
382 :
383 9 : if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)hfork, fd_hfork_align() ) ) ) {
384 0 : FD_LOG_WARNING(( "misaligned hfork" ));
385 0 : return NULL;
386 0 : }
387 :
388 9 : hfork->bhm_pool = bhm_pool_join( hfork->bhm_pool );
389 9 : hfork->bhm_map = bhm_map_join( hfork->bhm_map );
390 9 : hfork->blk_pool = blk_pool_join( hfork->blk_pool );
391 9 : hfork->blk_map = blk_map_join( hfork->blk_map );
392 9 : hfork->vte_pool = vte_pool_join( hfork->vte_pool );
393 9 : hfork->vte_map = vte_map_join( hfork->vte_map );
394 9 : hfork->vtr_pool = vtr_pool_join( hfork->vtr_pool );
395 9 : hfork->vtr_map = vtr_map_join( hfork->vtr_map );
396 9 : hfork->vtr_dlist = vtr_dlist_join( hfork->vtr_dlist );
397 219 : for( ulong i = 0UL; i < hfork->max; i++ ) {
398 210 : hfork->blk_pool[i].bhm_dlist = bhm_dlist_join( hfork->blk_pool[i].bhm_dlist );
399 210 : }
400 39 : for( ulong i = 0UL; i < hfork->vtr_max; i++ ) {
401 30 : hfork->vtr_pool[i].vte_dlist = vte_dlist_join( hfork->vtr_pool[i].vte_dlist );
402 30 : }
403 :
404 9 : return hfork;
405 9 : }
406 :
407 : void *
408 9 : fd_hfork_leave( fd_hfork_t const * hfork ) {
409 :
410 9 : if( FD_UNLIKELY( !hfork ) ) {
411 0 : FD_LOG_WARNING(( "NULL hfork" ));
412 0 : return NULL;
413 0 : }
414 :
415 9 : return (void *)hfork;
416 9 : }
417 :
418 : void *
419 9 : fd_hfork_delete( void * hfork ) {
420 :
421 9 : if( FD_UNLIKELY( !hfork ) ) {
422 0 : FD_LOG_WARNING(( "NULL hfork" ));
423 0 : return NULL;
424 0 : }
425 :
426 9 : if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)hfork, fd_hfork_align() ) ) ) {
427 0 : FD_LOG_WARNING(( "misaligned hfork" ));
428 0 : return NULL;
429 0 : }
430 :
431 9 : return hfork;
432 9 : }
433 :
434 : static blk_t *
435 : blk_insert( fd_hfork_t * hfork,
436 21 : fd_hash_t const * block_id ) {
437 21 : blk_t * blk = blk_pool_ele_acquire( hfork->blk_pool );
438 21 : blk->block_id = *block_id;
439 : /* blk->our_bank_hash */
440 21 : blk->replayed = 0; /* set by record_our_bank_hash */
441 21 : blk->dead = 0; /* set by record_our_bank_hash */
442 21 : blk->flag = 0; /* set by check: -1 mismatch, 0 unchecked, 1 match */
443 21 : blk->bhm_cnt = 0;
444 21 : blk_map_ele_insert( hfork->blk_map, blk, hfork->blk_pool );
445 21 : return blk;
446 21 : }
447 :
448 : int
449 : fd_hfork_count_vote( fd_hfork_t * hfork,
450 : fd_pubkey_t const * vote_acc,
451 : fd_hash_t const * block_id,
452 : fd_hash_t const * bank_hash,
453 : ulong slot,
454 : ulong stake,
455 36 : ulong total_stake ) {
456 :
457 : /* Get the vtr. If not in the voter set, ignore. */
458 :
459 36 : vtr_t * vtr = vtr_map_ele_query( hfork->vtr_map, vote_acc, NULL, hfork->vtr_pool );
460 36 : if( FD_UNLIKELY( !vtr ) ) return FD_HFORK_ERR_UNKNOWN_VTR;
461 :
462 : /* If voter already voted for this block_id, ignore. */
463 :
464 36 : bhm_key_t bhm_key = { .block_id = *block_id, .bank_hash = *bank_hash };
465 36 : vte_key_t vte_key = { .addr = *vote_acc, .block_id = *block_id };
466 36 : if( FD_UNLIKELY( vte_map_ele_query_const( hfork->vte_map, &vte_key, NULL, hfork->vte_pool ) ) ) return FD_HFORK_ERR_ALREADY_VOTED;
467 :
468 : /* Only process newer votes (by vote slot) from a given voter. */
469 :
470 33 : if( FD_UNLIKELY( vtr->vte_cnt && vte_dlist_ele_peek_tail_const( vtr->vte_dlist, hfork->vte_pool )->slot >= slot ) ) return FD_HFORK_ERR_VOTE_TOO_OLD;
471 :
472 : /* If voter has reached their quota, evict their oldest vote. */
473 :
474 33 : if( FD_UNLIKELY( vtr->vte_cnt==hfork->per_vtr_max ) ) {
475 6 : vte_t * evicted_vte = vte_dlist_ele_pop_head( vtr->vte_dlist, hfork->vte_pool );
476 6 : vte_map_ele_remove_fast( hfork->vte_map, evicted_vte, hfork->vte_pool );
477 :
478 6 : bhm_key_t evicted_xid = { .block_id = evicted_vte->key.block_id, .bank_hash = evicted_vte->bank_hash };
479 6 : bhm_t * bhm = bhm_map_ele_query( hfork->bhm_map, &evicted_xid, NULL, hfork->bhm_pool );
480 6 : bhm->stake -= evicted_vte->stake;
481 6 : bhm->vtr_cnt--;
482 6 : if( FD_UNLIKELY( !bhm->vtr_cnt ) ) bhm_remove( hfork, bhm );
483 6 : vte_pool_ele_release( hfork->vte_pool, evicted_vte );
484 6 : vtr->vte_cnt--;
485 6 : }
486 :
487 : /* Find or insert the blk for this block_id. */
488 :
489 33 : blk_t * blk = blk_map_ele_query( hfork->blk_map, block_id, NULL, hfork->blk_pool );
490 33 : if( FD_UNLIKELY( !blk ) ) blk = blk_insert( hfork, block_id );
491 :
492 : /* Find or insert the bhm for this (block_id, bank_hash). */
493 :
494 33 : bhm_t * bhm = bhm_map_ele_query( hfork->bhm_map, &bhm_key, NULL, hfork->bhm_pool );
495 33 : if( FD_UNLIKELY( !bhm ) ) {
496 30 : bhm = bhm_pool_ele_acquire( hfork->bhm_pool );
497 30 : bhm->key = bhm_key;
498 30 : bhm->slot = slot;
499 30 : bhm->stake = 0UL;
500 30 : bhm->vtr_cnt = 0UL;
501 30 : bhm_map_ele_insert( hfork->bhm_map, bhm, hfork->bhm_pool );
502 30 : blk->bhm_cnt++;
503 30 : bhm_dlist_ele_push_tail( blk->bhm_dlist, bhm, hfork->bhm_pool );
504 30 : }
505 :
506 : /* Push the vote onto the vtr. */
507 :
508 33 : vte_t * vte = vte_pool_ele_acquire( hfork->vte_pool );
509 33 : vte->key = vte_key;
510 33 : vte->bank_hash = *bank_hash;
511 33 : vte->slot = slot;
512 33 : vte->stake = stake;
513 33 : vte_map_ele_insert( hfork->vte_map, vte, hfork->vte_pool );
514 33 : vte_dlist_ele_push_tail( vtr->vte_dlist, vte, hfork->vte_pool );
515 33 : vtr->vte_cnt++;
516 :
517 33 : bhm->vtr_cnt++;
518 33 : bhm->stake += stake;
519 :
520 : /* Check for hard forks. */
521 :
522 33 : blk->flag = check( blk, bhm, total_stake );
523 33 : return blk->flag;
524 33 : }
525 :
526 : fd_hfork_blk_t *
527 : fd_hfork_record_our_bank_hash( fd_hfork_t * hfork,
528 : fd_hash_t const * block_id,
529 : fd_hash_t const * bank_hash,
530 3 : ulong total_stake ) {
531 :
532 3 : blk_t * blk = blk_map_ele_query( hfork->blk_map, block_id, NULL, hfork->blk_pool );
533 3 : if( FD_LIKELY( !blk ) ) blk = blk_insert( hfork, block_id );
534 3 : if( FD_LIKELY( bank_hash ) ) blk->our_bank_hash = *bank_hash;
535 3 : blk->replayed = 1;
536 3 : blk->dead = !bank_hash;
537 :
538 : /* Check all bhm entries for this block_id. */
539 :
540 3 : for( bhm_dlist_iter_t iter = bhm_dlist_iter_fwd_init( blk->bhm_dlist, hfork->bhm_pool );
541 6 : !bhm_dlist_iter_done( iter, blk->bhm_dlist, hfork->bhm_pool );
542 3 : iter = bhm_dlist_iter_fwd_next( iter, blk->bhm_dlist, hfork->bhm_pool ) ) {
543 3 : bhm_t * bhm = bhm_dlist_iter_ele( iter, blk->bhm_dlist, hfork->bhm_pool );
544 3 : blk->flag = check( blk, bhm, total_stake );
545 3 : }
546 3 : return blk;
547 3 : }
548 :
549 : void
550 : fd_hfork_update_voters( fd_hfork_t * hfork,
551 0 : fd_tower_voters_t const * tower_voters ) {
552 :
553 0 : for( vtr_dlist_iter_t iter = vtr_dlist_iter_fwd_init( hfork->vtr_dlist, hfork->vtr_pool );
554 0 : !vtr_dlist_iter_done( iter, hfork->vtr_dlist, hfork->vtr_pool );
555 0 : iter = vtr_dlist_iter_fwd_next( iter, hfork->vtr_dlist, hfork->vtr_pool ) ) {
556 0 : hfork->vtr_pool[iter].next = 1; /* mark for removal */
557 0 : }
558 :
559 : /* Move all voters in the new tower_voters set to the back of the
560 : dlist. We mark them by setting their `next` field to null. */
561 :
562 0 : for( fd_tower_voters_iter_t iter = fd_tower_voters_iter_init( tower_voters );
563 0 : !fd_tower_voters_iter_done( tower_voters, iter );
564 0 : iter = fd_tower_voters_iter_next( tower_voters, iter ) ) {
565 0 : fd_pubkey_t const * vote_acc = &fd_tower_voters_iter_ele_const( tower_voters, iter )->vote_acc;
566 0 : vtr_t * vtr = vtr_map_ele_query( hfork->vtr_map, vote_acc, NULL, hfork->vtr_pool );
567 0 : if( FD_UNLIKELY( !vtr ) ) {
568 0 : vtr = vtr_pool_ele_acquire( hfork->vtr_pool );
569 0 : vtr->addr = *vote_acc;
570 0 : vtr->vte_cnt = 0;
571 0 : vtr_map_ele_insert( hfork->vtr_map, vtr, hfork->vtr_pool );
572 0 : } else {
573 0 : vtr_dlist_ele_remove( hfork->vtr_dlist, vtr, hfork->vtr_pool );
574 0 : }
575 0 : vtr->next = 0; /* unmark for removal */
576 0 : vtr_dlist_ele_push_tail( hfork->vtr_dlist, vtr, hfork->vtr_pool );
577 0 : }
578 :
579 : /* Pop unwanted voters from the head until we hit a kept voter. */
580 :
581 0 : while( FD_LIKELY( !vtr_dlist_is_empty( hfork->vtr_dlist, hfork->vtr_pool ) ) ) {
582 0 : vtr_t * vtr = vtr_dlist_ele_pop_head( hfork->vtr_dlist, hfork->vtr_pool );
583 0 : if( FD_UNLIKELY( !vtr->next ) ) { /* can short-circuit since all the existing and new voters were appended */
584 0 : vtr_dlist_ele_push_tail( hfork->vtr_dlist, vtr, hfork->vtr_pool );
585 0 : break;
586 0 : }
587 0 : while( FD_LIKELY( !vte_dlist_is_empty( vtr->vte_dlist, hfork->vte_pool ) ) ) {
588 0 : vte_t * vte = vte_dlist_ele_pop_head( vtr->vte_dlist, hfork->vte_pool );
589 0 : vte_map_ele_remove_fast( hfork->vte_map, vte, hfork->vte_pool );
590 :
591 0 : bhm_key_t vte_xid = { .block_id = vte->key.block_id, .bank_hash = vte->bank_hash };
592 0 : bhm_t * bhm = bhm_map_ele_query( hfork->bhm_map, &vte_xid, NULL, hfork->bhm_pool );
593 0 : if( FD_LIKELY( bhm ) ) {
594 0 : bhm->stake -= vte->stake;
595 0 : bhm->vtr_cnt--;
596 0 : if( FD_UNLIKELY( !bhm->vtr_cnt ) ) bhm_remove( hfork, bhm );
597 0 : }
598 :
599 0 : vte_pool_ele_release( hfork->vte_pool, vte );
600 0 : }
601 0 : vtr_map_ele_remove_fast( hfork->vtr_map, vtr, hfork->vtr_pool );
602 0 : vtr_pool_ele_release( hfork->vtr_pool, vtr );
603 0 : }
604 0 : }
|