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