Line data Source code
1 : #include "fd_finality_tracker.h"
2 :
3 : struct finalization_notarized { fd_block_hash_t hash; };
4 : __extension__ struct finalization_final_pending_notar { };
5 : struct finalization_finalized { fd_block_hash_t hash; };
6 : struct finalization_implicitly_finalized { fd_block_hash_t hash; };
7 : __extension__ struct finalization_implicitly_skipped { };
8 :
9 : typedef struct finalization_notarized finalization_notarized_t;
10 : typedef struct finalization_final_pending_notar finalization_final_pending_notar_t;
11 : typedef struct finalization_finalized finalization_finalized_t;
12 : typedef struct finalization_implicitly_finalized finalization_implicitly_finalized_t;
13 : typedef struct finalization_implicitly_skipped finalization_implicitly_skipped_t;
14 :
15 : struct finalization_status {
16 : int kind;
17 : union {
18 : finalization_notarized_t notarized;
19 : finalization_final_pending_notar_t final_pending_notar;
20 : finalization_finalized_t finalized;
21 : finalization_implicitly_finalized_t implicitly_finalized;
22 : finalization_implicitly_skipped_t implicitly_skipped;
23 : };
24 : };
25 : typedef struct finalization_status finalization_status_t;
26 :
27 : struct status_ele {
28 : ulong slot;
29 : finalization_status_t status;
30 : ulong next;
31 : };
32 : typedef struct status_ele status_ele_t;
33 :
34 : #define POOL_NAME status_pool
35 0 : #define POOL_T status_ele_t
36 0 : #define POOL_NEXT next
37 : #include "../../util/tmpl/fd_pool.c"
38 :
39 : #define MAP_NAME status_map
40 : #define MAP_ELE_T status_ele_t
41 0 : #define MAP_KEY slot
42 : #define MAP_KEY_T ulong
43 0 : #define MAP_KEY_EQ(k0,k1) ((*(k0))==(*(k1)))
44 0 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash((*(key))^(seed)))
45 0 : #define MAP_NEXT next
46 : #include "../../util/tmpl/fd_map_chain.c"
47 :
48 : struct parent_ele {
49 : fd_block_id_t block_id;
50 : fd_block_id_t parent_block_id;
51 : ulong next;
52 : };
53 : typedef struct parent_ele parent_ele_t;
54 :
55 : #define POOL_NAME parent_pool
56 0 : #define POOL_T parent_ele_t
57 0 : #define POOL_NEXT next
58 : #include "../../util/tmpl/fd_pool.c"
59 :
60 : #define MAP_NAME parent_map
61 : #define MAP_ELE_T parent_ele_t
62 0 : #define MAP_KEY block_id
63 : #define MAP_KEY_T fd_block_id_t
64 0 : #define MAP_KEY_EQ(k0,k1) (fd_block_id_eq((k0),(k1)))
65 0 : #define MAP_KEY_HASH(key,seed) (fd_hash((seed),(key),sizeof(fd_block_id_t)))
66 0 : #define MAP_NEXT next
67 : #include "../../util/tmpl/fd_map_chain.c"
68 :
69 : struct status {
70 : status_ele_t * pool;
71 : status_map_t * map;
72 : };
73 : typedef struct status status_t;
74 :
75 : struct parents {
76 : parent_ele_t * pool;
77 : parent_map_t * map;
78 : };
79 : typedef struct parents parents_t;
80 :
81 : struct fd_finality_tracker {
82 : status_t status;
83 : parents_t parents;
84 : ulong highest_finalized_slot;
85 : ulong first_unpruned_slot;
86 : };
87 :
88 : FD_FN_PURE static fd_block_hash_t const *
89 0 : status_hash( finalization_status_t const * status ) {
90 0 : switch( status->kind ) {
91 0 : case FD_FINALIZATION_STATUS_NOTARIZED: return &status->notarized.hash;
92 0 : case FD_FINALIZATION_STATUS_FINALIZED: return &status->finalized.hash;
93 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED: return &status->implicitly_finalized.hash;
94 0 : case FD_FINALIZATION_STATUS_FINAL_PENDING_NOTAR: return NULL;
95 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED: return NULL;
96 0 : default: FD_LOG_CRIT(( "unreachable" ));
97 0 : }
98 0 : }
99 :
100 : static parent_ele_t *
101 : parent_below( fd_finality_tracker_t * self,
102 0 : ulong root ) {
103 0 : parent_map_t * map = self->parents.map;
104 0 : parent_ele_t * pool = self->parents.pool;
105 0 : for( parent_map_iter_t iter = parent_map_iter_init( map, pool );
106 0 : !parent_map_iter_done( iter, map, pool );
107 0 : iter = parent_map_iter_next( iter, map, pool ) ) {
108 0 : parent_ele_t * e = parent_map_iter_ele( iter, map, pool );
109 0 : if( FD_UNLIKELY( e->block_id.slot<root ) ) return e;
110 0 : }
111 0 : return NULL;
112 0 : }
113 :
114 : static void
115 0 : prune( fd_finality_tracker_t * self ) {
116 0 : ulong root = self->first_unpruned_slot;
117 0 : ulong next = root + 1UL;
118 0 : status_ele_t * se;
119 0 : while( ( se = status_map_ele_query( self->status.map, &next, NULL, self->status.pool ) ) &&
120 0 : ( se->status.kind==FD_FINALIZATION_STATUS_FINALIZED ||
121 0 : se->status.kind==FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED ||
122 0 : se->status.kind==FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED ) ) {
123 0 : self->first_unpruned_slot = next;
124 0 : next++;
125 0 : }
126 :
127 0 : for( ulong slot=root; slot<self->first_unpruned_slot; slot++ ) {
128 0 : status_ele_t * e = status_map_ele_remove( self->status.map, &slot, NULL, self->status.pool );
129 0 : if( FD_LIKELY( e ) ) status_pool_ele_release( self->status.pool, e );
130 0 : }
131 :
132 0 : parent_ele_t * pe;
133 0 : while( ( pe = parent_below( self, self->first_unpruned_slot ) ) ) {
134 0 : parent_map_ele_remove( self->parents.map, &pe->block_id, NULL, self->parents.pool );
135 0 : parent_pool_ele_release( self->parents.pool, pe );
136 0 : }
137 0 : }
138 :
139 : static void
140 : handle_implicitly_finalized( fd_finality_tracker_t * self,
141 : ulong source_slot,
142 : fd_block_id_t const * implicitly_finalized,
143 0 : fd_finalization_event_t * event ) {
144 :
145 0 : fd_block_id_t const * parent = implicitly_finalized;
146 0 : while( parent ) {
147 0 : fd_block_id_t const * implicitly_finalized = parent; /* intentional shadowing */
148 :
149 0 : FD_TEST( source_slot > implicitly_finalized->slot );
150 :
151 0 : if( FD_UNLIKELY( implicitly_finalized->slot<self->first_unpruned_slot ) ) return;
152 :
153 0 : for( ulong slot=implicitly_finalized->slot+1UL; slot<source_slot; slot++ ) {
154 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &slot, NULL, self->status.pool );
155 0 : if( FD_UNLIKELY( ele ) ) {
156 0 : switch( ele->status.kind ) {
157 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED:
158 0 : return;
159 0 : case FD_FINALIZATION_STATUS_NOTARIZED:
160 0 : break;
161 0 : case FD_FINALIZATION_STATUS_FINAL_PENDING_NOTAR:
162 0 : case FD_FINALIZATION_STATUS_FINALIZED:
163 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
164 0 : FD_LOG_CRIT(( "consensus safety violation" ));
165 0 : default:
166 0 : FD_LOG_CRIT(( "unreachable" ));
167 0 : }
168 0 : }
169 0 : if( FD_UNLIKELY( !ele ) ) {
170 0 : ele = status_pool_ele_acquire( self->status.pool );
171 0 : ele->slot = slot;
172 0 : status_map_ele_insert( self->status.map, ele, self->status.pool );
173 0 : }
174 0 : ele->status.kind = FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED;
175 0 : event->implicitly_skipped[ event->implicitly_skipped_cnt++ ] = slot;
176 0 : }
177 :
178 0 : ulong slot = implicitly_finalized->slot;
179 0 : fd_block_hash_t const * block_hash = &implicitly_finalized->hash;
180 :
181 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &slot, NULL, self->status.pool );
182 0 : if( FD_UNLIKELY( !ele ) ) {
183 0 : ele = status_pool_ele_acquire( self->status.pool );
184 0 : ele->slot = slot;
185 0 : status_map_ele_insert( self->status.map, ele, self->status.pool );
186 0 : } else {
187 0 : switch( ele->status.kind ) {
188 0 : case FD_FINALIZATION_STATUS_FINALIZED:
189 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
190 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), block_hash, sizeof(fd_block_hash_t) ), "consensus safety violation" );
191 0 : return;
192 0 : case FD_FINALIZATION_STATUS_NOTARIZED:
193 0 : case FD_FINALIZATION_STATUS_FINAL_PENDING_NOTAR:
194 0 : break;
195 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED:
196 0 : FD_LOG_CRIT(( "consensus safety violation" ));
197 0 : default:
198 0 : FD_LOG_CRIT(( "unreachable" ));
199 0 : }
200 0 : }
201 0 : ele->status.kind = FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED;
202 0 : memcpy( &ele->status.implicitly_finalized.hash, block_hash, sizeof(fd_block_hash_t) );
203 0 : event->implicitly_finalized[ event->implicitly_finalized_cnt++ ] = *implicitly_finalized;
204 :
205 0 : parent_ele_t * parent_ele = parent_map_ele_query( self->parents.map, implicitly_finalized, NULL, self->parents.pool );
206 0 : if( FD_UNLIKELY( !parent_ele ) ) return;
207 0 : source_slot = implicitly_finalized->slot;
208 0 : parent = &parent_ele->parent_block_id;
209 0 : }
210 0 : }
211 :
212 : static void
213 : handle_finalized_block( fd_finality_tracker_t * self,
214 : fd_block_id_t const * finalized,
215 0 : fd_finalization_event_t * event ) {
216 0 : ulong slot = finalized->slot;
217 0 : event->finalized = *finalized;
218 0 : self->highest_finalized_slot = fd_ulong_max( slot, self->highest_finalized_slot );
219 :
220 0 : parent_ele_t * _parent = parent_map_ele_query( self->parents.map, finalized, NULL, self->parents.pool );
221 0 : if( FD_LIKELY( _parent ) ) {
222 0 : fd_block_id_t parent = _parent->parent_block_id;
223 0 : handle_implicitly_finalized( self, slot, &parent, event );
224 0 : }
225 0 : prune( self );
226 0 : }
227 :
228 : ulong
229 0 : fd_finality_tracker_align( void ) {
230 0 : return alignof(fd_finality_tracker_t);
231 0 : }
232 :
233 : ulong
234 0 : fd_finality_tracker_footprint( ulong slot_max ) {
235 0 : slot_max = fd_ulong_pow2_up( slot_max );
236 0 : ulong parent_max = slot_max*FD_VOTOR_EQVOC_MAX;
237 0 : ulong status_chain_cnt = status_map_chain_cnt_est( slot_max );
238 0 : ulong parent_chain_cnt = parent_map_chain_cnt_est( parent_max );
239 0 : return FD_LAYOUT_FINI(
240 0 : FD_LAYOUT_APPEND(
241 0 : FD_LAYOUT_APPEND(
242 0 : FD_LAYOUT_APPEND(
243 0 : FD_LAYOUT_APPEND(
244 0 : FD_LAYOUT_APPEND(
245 0 : FD_LAYOUT_INIT,
246 0 : alignof(fd_finality_tracker_t), sizeof(fd_finality_tracker_t) ),
247 0 : status_pool_align(), status_pool_footprint ( slot_max ) ),
248 0 : status_map_align(), status_map_footprint ( status_chain_cnt ) ),
249 0 : parent_pool_align(), parent_pool_footprint ( parent_max ) ),
250 0 : parent_map_align(), parent_map_footprint ( parent_chain_cnt ) ),
251 0 : fd_finality_tracker_align() );
252 0 : }
253 :
254 : void *
255 : fd_finality_tracker_new( void * shmem,
256 : ulong slot_max,
257 0 : ulong seed ) {
258 0 : if( FD_UNLIKELY( !shmem ) ) {
259 0 : FD_LOG_WARNING(( "NULL mem" ));
260 0 : return NULL;
261 0 : }
262 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_finality_tracker_align() ) ) ) {
263 0 : FD_LOG_WARNING(( "misaligned mem" ));
264 0 : return NULL;
265 0 : }
266 0 : if( FD_UNLIKELY( !slot_max ) ) {
267 0 : FD_LOG_WARNING(( "zero slot_max" ));
268 0 : return NULL;
269 0 : }
270 :
271 0 : ulong footprint = fd_finality_tracker_footprint( slot_max );
272 0 : if( FD_UNLIKELY( !footprint ) ) {
273 0 : FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
274 0 : return NULL;
275 0 : }
276 :
277 0 : slot_max = fd_ulong_pow2_up( slot_max );
278 0 : ulong parent_max = slot_max*FD_VOTOR_EQVOC_MAX;
279 :
280 0 : fd_memset( shmem, 0, footprint );
281 :
282 0 : ulong status_chain_cnt = status_map_chain_cnt_est( slot_max );
283 0 : ulong parent_chain_cnt = parent_map_chain_cnt_est( parent_max );
284 :
285 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
286 0 : fd_finality_tracker_t * tracker = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_finality_tracker_t), sizeof(fd_finality_tracker_t) );
287 0 : void * status_pool = FD_SCRATCH_ALLOC_APPEND( l, status_pool_align(), status_pool_footprint ( slot_max ) );
288 0 : void * status_map = FD_SCRATCH_ALLOC_APPEND( l, status_map_align(), status_map_footprint ( status_chain_cnt ) );
289 0 : void * parent_pool = FD_SCRATCH_ALLOC_APPEND( l, parent_pool_align(), parent_pool_footprint ( parent_max ) );
290 0 : void * parent_map = FD_SCRATCH_ALLOC_APPEND( l, parent_map_align(), parent_map_footprint ( parent_chain_cnt ) );
291 0 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_finality_tracker_align() ) == (ulong)shmem + footprint );
292 :
293 0 : tracker->status.pool = status_pool_join( status_pool_new( status_pool, slot_max ) );
294 0 : tracker->status.map = status_map_join ( status_map_new ( status_map, status_chain_cnt, seed ) );
295 0 : tracker->parents.pool = parent_pool_join( parent_pool_new( parent_pool, parent_max ) );
296 0 : tracker->parents.map = parent_map_join ( parent_map_new ( parent_map, parent_chain_cnt, seed ) );
297 :
298 0 : tracker->highest_finalized_slot = ULONG_MAX;
299 0 : tracker->first_unpruned_slot = ULONG_MAX;
300 :
301 0 : return shmem;
302 0 : }
303 :
304 : void
305 : fd_finality_tracker_init( fd_finality_tracker_t * self,
306 0 : ulong slot ) {
307 0 : self->highest_finalized_slot = slot;
308 0 : self->first_unpruned_slot = slot;
309 0 : }
310 :
311 : void
312 0 : fd_finality_tracker_fini( fd_finality_tracker_t * self ) {
313 0 : self->highest_finalized_slot = ULONG_MAX;
314 0 : self->first_unpruned_slot = ULONG_MAX;
315 0 : }
316 :
317 : fd_finality_tracker_t *
318 0 : fd_finality_tracker_join( void * shtracker ) {
319 0 : fd_finality_tracker_t * tracker = (fd_finality_tracker_t *)shtracker;
320 0 : if( FD_UNLIKELY( !tracker ) ) {
321 0 : FD_LOG_WARNING(( "NULL tracker" ));
322 0 : return NULL;
323 0 : }
324 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)tracker, fd_finality_tracker_align() ) ) ) {
325 0 : FD_LOG_WARNING(( "misaligned tracker" ));
326 0 : return NULL;
327 0 : }
328 0 : return tracker;
329 0 : }
330 :
331 : void *
332 0 : fd_finality_tracker_leave( fd_finality_tracker_t const * self ) {
333 0 : if( FD_UNLIKELY( !self ) ) {
334 0 : FD_LOG_WARNING(( "NULL tracker" ));
335 0 : return NULL;
336 0 : }
337 0 : return (void *)self;
338 0 : }
339 :
340 : void *
341 0 : fd_finality_tracker_delete( void * shtracker ) {
342 0 : if( FD_UNLIKELY( !shtracker ) ) {
343 0 : FD_LOG_WARNING(( "NULL tracker" ));
344 0 : return NULL;
345 0 : }
346 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtracker, fd_finality_tracker_align() ) ) ) {
347 0 : FD_LOG_WARNING(( "misaligned tracker" ));
348 0 : return NULL;
349 0 : }
350 0 : return shtracker;
351 0 : }
352 :
353 : void
354 : fd_finality_tracker_add_parent( fd_finality_tracker_t * self,
355 : fd_block_id_t const * block,
356 : fd_block_id_t const * parent,
357 0 : fd_finalization_event_t * event ) {
358 0 : FD_TEST( block->slot > parent->slot );
359 :
360 0 : if( FD_UNLIKELY( block->slot<self->first_unpruned_slot ) ) return;
361 :
362 0 : parent_ele_t * pe = parent_map_ele_query( self->parents.map, block, NULL, self->parents.pool );
363 0 : if( FD_LIKELY( pe ) ) {
364 0 : FD_TEST( fd_block_id_eq( &pe->parent_block_id, parent ) );
365 0 : return;
366 0 : }
367 0 : {
368 0 : parent_ele_t * pool = self->parents.pool;
369 0 : FD_TEST( parent_pool_free( pool ) );
370 0 : pe = parent_pool_ele_acquire( pool );
371 0 : pe->block_id = *block;
372 0 : pe->parent_block_id = *parent;
373 0 : parent_map_ele_insert( self->parents.map, pe, pool );
374 0 : }
375 :
376 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &block->slot, NULL, self->status.pool );
377 0 : if( FD_UNLIKELY( !ele ) ) return;
378 :
379 0 : switch( ele->status.kind ) {
380 0 : case FD_FINALIZATION_STATUS_FINALIZED:
381 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
382 0 : if( FD_LIKELY( 0==memcmp( &block->hash, status_hash( &ele->status ), sizeof(fd_block_hash_t) ) ) ) {
383 0 : fd_block_id_t p = *parent;
384 0 : handle_implicitly_finalized( self, block->slot, &p, event );
385 0 : prune( self );
386 0 : }
387 0 : return;
388 0 : case FD_FINALIZATION_STATUS_NOTARIZED:
389 0 : case FD_FINALIZATION_STATUS_FINAL_PENDING_NOTAR:
390 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED:
391 0 : default:
392 0 : return;
393 0 : }
394 0 : }
395 :
396 : void
397 : fd_finality_tracker_mark_fast_finalized( fd_finality_tracker_t * self,
398 : fd_block_id_t const * block,
399 0 : fd_finalization_event_t * event ) {
400 0 : if( FD_UNLIKELY( block->slot<self->first_unpruned_slot ) ) return;
401 :
402 0 : ulong slot = block->slot;
403 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &slot, NULL, self->status.pool );
404 0 : if( FD_UNLIKELY( !ele ) ) {
405 0 : ele = status_pool_ele_acquire( self->status.pool );
406 0 : ele->slot = slot;
407 0 : status_map_ele_insert( self->status.map, ele, self->status.pool );
408 0 : } else {
409 0 : switch( ele->status.kind ) {
410 0 : case FD_FINALIZATION_STATUS_FINALIZED:
411 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
412 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), &block->hash, sizeof(fd_block_hash_t) ), "consensus safety violation" );
413 0 : return;
414 0 : case FD_FINALIZATION_STATUS_NOTARIZED:
415 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), &block->hash, sizeof(fd_block_hash_t) ), "consensus safety violation" );
416 0 : break;
417 0 : case FD_FINALIZATION_STATUS_FINAL_PENDING_NOTAR:
418 0 : break;
419 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED:
420 0 : FD_LOG_CRIT(( "consensus safety violation" ));
421 0 : default:
422 0 : FD_LOG_CRIT(( "unreachable" ));
423 0 : }
424 0 : }
425 0 : ele->status.kind = FD_FINALIZATION_STATUS_FINALIZED;
426 0 : memcpy( &ele->status.finalized.hash, &block->hash, sizeof(fd_block_hash_t) );
427 :
428 0 : handle_finalized_block( self, block, event );
429 0 : }
430 :
431 : void
432 : fd_finality_tracker_mark_notarized( fd_finality_tracker_t * self,
433 : fd_block_id_t const * block,
434 0 : fd_finalization_event_t * event ) {
435 0 : if( FD_UNLIKELY( block->slot<self->first_unpruned_slot ) ) return;
436 :
437 0 : ulong slot = block->slot;
438 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &slot, NULL, self->status.pool );
439 0 : if( FD_UNLIKELY( !ele ) ) {
440 0 : ele = status_pool_ele_acquire( self->status.pool );
441 0 : ele->slot = slot;
442 0 : ele->status.kind = FD_FINALIZATION_STATUS_NOTARIZED;
443 0 : memcpy( &ele->status.notarized.hash, &block->hash, sizeof(fd_block_hash_t) );
444 0 : status_map_ele_insert( self->status.map, ele, self->status.pool );
445 0 : return;
446 0 : }
447 :
448 0 : switch( ele->status.kind ) {
449 0 : case FD_FINALIZATION_STATUS_NOTARIZED:
450 0 : case FD_FINALIZATION_STATUS_FINALIZED:
451 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), &block->hash, sizeof(fd_block_hash_t) ), "consensus safety violation" );
452 0 : return;
453 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
454 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED:
455 0 : return;
456 0 : case FD_FINALIZATION_STATUS_FINAL_PENDING_NOTAR: {
457 0 : ele->status.kind = FD_FINALIZATION_STATUS_FINALIZED;
458 0 : memcpy( &ele->status.finalized.hash, &block->hash, sizeof(fd_block_hash_t) );
459 0 : handle_finalized_block( self, block, event );
460 0 : return;
461 0 : }
462 0 : default:
463 0 : FD_LOG_CRIT(( "unreachable" ));
464 0 : }
465 0 : }
466 :
467 : void
468 : fd_finality_tracker_mark_finalized( fd_finality_tracker_t * self,
469 : ulong slot,
470 0 : fd_finalization_event_t * event ) {
471 0 : if( FD_UNLIKELY( slot<self->first_unpruned_slot ) ) return;
472 :
473 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &slot, NULL, self->status.pool );
474 0 : if( FD_UNLIKELY( !ele ) ) {
475 0 : ele = status_pool_ele_acquire( self->status.pool );
476 0 : ele->slot = slot;
477 0 : ele->status.kind = FD_FINALIZATION_STATUS_FINAL_PENDING_NOTAR;
478 0 : status_map_ele_insert( self->status.map, ele, self->status.pool );
479 0 : return;
480 0 : }
481 :
482 0 : switch( ele->status.kind ) {
483 0 : case FD_FINALIZATION_STATUS_FINAL_PENDING_NOTAR:
484 0 : case FD_FINALIZATION_STATUS_FINALIZED:
485 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
486 0 : return;
487 0 : case FD_FINALIZATION_STATUS_NOTARIZED: {
488 0 : fd_block_id_t block = fd_block_id( slot, ele->status.notarized.hash.uc );
489 0 : ele->status.kind = FD_FINALIZATION_STATUS_FINALIZED;
490 0 : memcpy( &ele->status.finalized.hash, &block.hash, sizeof(fd_block_hash_t) );
491 0 : handle_finalized_block( self, &block, event );
492 0 : return;
493 0 : }
494 0 : case FD_FINALIZATION_STATUS_IMPLICITLY_SKIPPED:
495 0 : FD_LOG_CRIT(( "consensus safety violation" ));
496 0 : default:
497 0 : FD_LOG_CRIT(( "unreachable" ));
498 0 : }
499 0 : }
500 :
501 : ulong
502 0 : fd_finality_tracker_highest_finalized_slot( fd_finality_tracker_t const * self ) {
503 0 : return self->highest_finalized_slot;
504 0 : }
505 :
506 : FD_FN_PURE fd_block_hash_t const *
507 0 : fd_finality_tracker_highest_finalized_block_hash( fd_finality_tracker_t const * self ) {
508 0 : status_ele_t const * e = status_map_ele_query_const( self->status.map, &self->highest_finalized_slot, NULL, self->status.pool );
509 0 : if( FD_UNLIKELY( !e || ( e->status.kind!=FD_FINALIZATION_STATUS_FINALIZED && e->status.kind!=FD_FINALIZATION_STATUS_IMPLICITLY_FINALIZED ) ) ) return NULL;
510 0 : return status_hash( &e->status );
511 0 : }
512 :
513 : ulong
514 0 : fd_finality_tracker_first_unpruned_slot( fd_finality_tracker_t const * self ) {
515 0 : return self->first_unpruned_slot;
516 0 : }
517 :
518 : int
519 : fd_finality_tracker_status( fd_finality_tracker_t const * self,
520 : ulong slot,
521 0 : fd_block_hash_t * out_hash ) {
522 0 : status_ele_t const * e = status_map_ele_query_const( self->status.map, &slot, NULL, self->status.pool );
523 0 : if( FD_UNLIKELY( !e ) ) return -1;
524 0 : if( FD_LIKELY( out_hash ) ) {
525 0 : fd_block_hash_t const * hash = status_hash( &e->status );
526 0 : if( FD_LIKELY( hash ) ) memcpy( out_hash, hash, sizeof(fd_block_hash_t) );
527 0 : else fd_memset( out_hash, 0, sizeof(fd_block_hash_t) );
528 0 : }
529 0 : return e->status.kind;
530 0 : }
531 :
532 : int
533 : fd_finality_tracker_has_parent( fd_finality_tracker_t const * self,
534 0 : fd_block_id_t const * block ) {
535 0 : return !!parent_map_ele_query_const( self->parents.map, block, NULL, self->parents.pool );
536 0 : }
|