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 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 : 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 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 ag_block_id_t
64 0 : #define MAP_KEY_EQ(k0,k1) (ag_block_id_eq( (k0), (k1) ))
65 0 : #define MAP_KEY_HASH(key,seed) (fd_hash( (seed), (key), sizeof(ag_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 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 0 : status_hash( finalization_status_t const * status ) {
90 0 : switch( status->kind ) {
91 0 : case AG_FINALIZATION_STATUS_NOTARIZED: return status->notarized.hash;
92 0 : case AG_FINALIZATION_STATUS_FINALIZED: return status->finalized.hash;
93 0 : 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 0 : }
98 0 : }
99 :
100 : static parent_ele_t *
101 : parent_below( ag_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( ag_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==AG_FINALIZATION_STATUS_FINALIZED ||
121 0 : se->status.kind==AG_FINALIZATION_STATUS_IMPLICITLY_FINALIZED ||
122 0 : se->status.kind==AG_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( ag_finality_tracker_t * self,
141 : ulong source_slot,
142 : ag_block_id_t const * implicitly_finalized,
143 0 : ag_finalization_event_t * event ) {
144 :
145 0 : ag_block_id_t const * parent = implicitly_finalized;
146 0 : while( parent ) {
147 0 : ag_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 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 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 = AG_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 : uchar 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 AG_FINALIZATION_STATUS_FINALIZED:
189 0 : case AG_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
190 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), block_hash, sizeof(ag_block_hash_t) ), "consensus safety violation" );
191 0 : return;
192 0 : case AG_FINALIZATION_STATUS_NOTARIZED:
193 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), block_hash, sizeof(ag_block_hash_t) ), "consensus safety violation" );
194 0 : break;
195 0 : 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 0 : }
202 0 : }
203 0 : ele->status.kind = AG_FINALIZATION_STATUS_IMPLICITLY_FINALIZED;
204 0 : memcpy( ele->status.implicitly_finalized.hash, block_hash, sizeof(ag_block_hash_t) );
205 0 : event->implicitly_finalized[ event->implicitly_finalized_cnt++ ] = *implicitly_finalized;
206 :
207 0 : parent_ele_t * parent_ele = parent_map_ele_query( self->parents.map, implicitly_finalized, NULL, self->parents.pool );
208 0 : if( FD_UNLIKELY( !parent_ele ) ) return;
209 0 : source_slot = implicitly_finalized->slot;
210 0 : parent = &parent_ele->parent_block_id;
211 0 : }
212 0 : }
213 :
214 : static void
215 : handle_finalized_block( ag_finality_tracker_t * self,
216 : ag_block_id_t const * finalized,
217 0 : ag_finalization_event_t * event ) {
218 0 : ulong slot = finalized->slot;
219 0 : event->finalized = *finalized;
220 0 : self->highest_finalized_slot = fd_ulong_max( slot, self->highest_finalized_slot );
221 :
222 0 : parent_ele_t * _parent = parent_map_ele_query( self->parents.map, finalized, NULL, self->parents.pool );
223 0 : if( FD_LIKELY( _parent ) ) {
224 0 : ag_block_id_t parent = _parent->parent_block_id;
225 0 : handle_implicitly_finalized( self, slot, &parent, event );
226 0 : }
227 0 : prune( self );
228 0 : }
229 :
230 : ulong
231 0 : ag_finality_tracker_align( void ) {
232 0 : return alignof(ag_finality_tracker_t);
233 0 : }
234 :
235 : ulong
236 0 : ag_finality_tracker_footprint( ulong slot_max ) {
237 0 : slot_max = fd_ulong_pow2_up( slot_max );
238 0 : ulong parent_max = slot_max*AG_EQVOC_BLOCK_HASH_MAX;
239 0 : ulong status_chain_cnt = status_map_chain_cnt_est( slot_max );
240 0 : ulong parent_chain_cnt = parent_map_chain_cnt_est( parent_max );
241 0 : return FD_LAYOUT_FINI(
242 0 : FD_LAYOUT_APPEND(
243 0 : FD_LAYOUT_APPEND(
244 0 : FD_LAYOUT_APPEND(
245 0 : FD_LAYOUT_APPEND(
246 0 : FD_LAYOUT_APPEND(
247 0 : FD_LAYOUT_INIT,
248 0 : alignof(ag_finality_tracker_t), sizeof(ag_finality_tracker_t) ),
249 0 : status_pool_align(), status_pool_footprint ( slot_max ) ),
250 0 : status_map_align(), status_map_footprint ( status_chain_cnt ) ),
251 0 : parent_pool_align(), parent_pool_footprint ( parent_max ) ),
252 0 : parent_map_align(), parent_map_footprint ( parent_chain_cnt ) ),
253 0 : ag_finality_tracker_align() );
254 0 : }
255 :
256 : void *
257 : ag_finality_tracker_new( void * shmem,
258 : ulong slot_max,
259 0 : ulong seed ) {
260 0 : if( FD_UNLIKELY( !shmem ) ) {
261 0 : FD_LOG_WARNING(( "NULL mem" ));
262 0 : return NULL;
263 0 : }
264 0 : 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 0 : if( FD_UNLIKELY( !slot_max ) ) {
269 0 : FD_LOG_WARNING(( "zero slot_max" ));
270 0 : return NULL;
271 0 : }
272 :
273 0 : ulong footprint = ag_finality_tracker_footprint( slot_max );
274 0 : if( FD_UNLIKELY( !footprint ) ) {
275 0 : FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
276 0 : return NULL;
277 0 : }
278 :
279 0 : slot_max = fd_ulong_pow2_up( slot_max );
280 0 : ulong parent_max = slot_max*AG_EQVOC_BLOCK_HASH_MAX;
281 :
282 0 : fd_memset( shmem, 0, footprint );
283 :
284 0 : ulong status_chain_cnt = status_map_chain_cnt_est( slot_max );
285 0 : ulong parent_chain_cnt = parent_map_chain_cnt_est( parent_max );
286 :
287 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
288 0 : ag_finality_tracker_t * tracker = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_finality_tracker_t), sizeof(ag_finality_tracker_t) );
289 0 : void * status_pool = FD_SCRATCH_ALLOC_APPEND( l, status_pool_align(), status_pool_footprint ( slot_max ) );
290 0 : void * status_map = FD_SCRATCH_ALLOC_APPEND( l, status_map_align(), status_map_footprint ( status_chain_cnt ) );
291 0 : void * parent_pool = FD_SCRATCH_ALLOC_APPEND( l, parent_pool_align(), parent_pool_footprint ( parent_max ) );
292 0 : void * parent_map = FD_SCRATCH_ALLOC_APPEND( l, parent_map_align(), parent_map_footprint ( parent_chain_cnt ) );
293 0 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, ag_finality_tracker_align() ) == (ulong)shmem + footprint );
294 :
295 0 : tracker->status.pool = status_pool_join( status_pool_new( status_pool, slot_max ) );
296 0 : tracker->status.map = status_map_join ( status_map_new ( status_map, status_chain_cnt, seed ) );
297 0 : tracker->parents.pool = parent_pool_join( parent_pool_new( parent_pool, parent_max ) );
298 0 : tracker->parents.map = parent_map_join ( parent_map_new ( parent_map, parent_chain_cnt, seed ) );
299 :
300 0 : tracker->highest_finalized_slot = ULONG_MAX;
301 0 : tracker->first_unpruned_slot = ULONG_MAX;
302 :
303 0 : return shmem;
304 0 : }
305 :
306 : void
307 : ag_finality_tracker_init( ag_finality_tracker_t * self,
308 0 : ulong slot ) {
309 0 : self->highest_finalized_slot = slot;
310 0 : self->first_unpruned_slot = slot;
311 0 : }
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 0 : ag_finality_tracker_join( void * shtracker ) {
321 0 : ag_finality_tracker_t * tracker = (ag_finality_tracker_t *)shtracker;
322 0 : if( FD_UNLIKELY( !tracker ) ) {
323 0 : FD_LOG_WARNING(( "NULL tracker" ));
324 0 : return NULL;
325 0 : }
326 0 : 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 0 : return tracker;
331 0 : }
332 :
333 : void *
334 0 : ag_finality_tracker_leave( ag_finality_tracker_t const * tracker ) {
335 0 : if( FD_UNLIKELY( !tracker ) ) {
336 0 : FD_LOG_WARNING(( "NULL tracker" ));
337 0 : return NULL;
338 0 : }
339 0 : return (void *)tracker;
340 0 : }
341 :
342 : void *
343 0 : ag_finality_tracker_delete( void * shtracker ) {
344 0 : if( FD_UNLIKELY( !shtracker ) ) {
345 0 : FD_LOG_WARNING(( "NULL tracker" ));
346 0 : return NULL;
347 0 : }
348 0 : 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 0 : return shtracker;
353 0 : }
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 0 : ag_finalization_event_t * event ) {
360 0 : FD_TEST( block->slot > parent->slot );
361 :
362 0 : if( FD_UNLIKELY( block->slot<self->first_unpruned_slot ) ) return;
363 :
364 0 : parent_ele_t * pe = parent_map_ele_query( self->parents.map, block, NULL, self->parents.pool );
365 0 : if( FD_LIKELY( pe ) ) {
366 0 : FD_TEST( ag_block_id_eq( &pe->parent_block_id, parent ) );
367 0 : return;
368 0 : }
369 0 : {
370 0 : parent_ele_t * pool = self->parents.pool;
371 0 : FD_TEST( parent_pool_free( pool ) );
372 0 : pe = parent_pool_ele_acquire( pool );
373 0 : pe->block_id = *block;
374 0 : pe->parent_block_id = *parent;
375 0 : parent_map_ele_insert( self->parents.map, pe, pool );
376 0 : }
377 :
378 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &block->slot, NULL, self->status.pool );
379 0 : if( FD_UNLIKELY( !ele ) ) return;
380 :
381 0 : switch( ele->status.kind ) {
382 0 : case AG_FINALIZATION_STATUS_FINALIZED:
383 0 : case AG_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
384 0 : if( FD_LIKELY( 0==memcmp( block->hash, status_hash( &ele->status ), sizeof(ag_block_hash_t) ) ) ) {
385 0 : ag_block_id_t p = *parent;
386 0 : handle_implicitly_finalized( self, block->slot, &p, event );
387 0 : prune( self );
388 0 : }
389 0 : return;
390 0 : case AG_FINALIZATION_STATUS_NOTARIZED:
391 0 : case AG_FINALIZATION_STATUS_FINAL_PENDING_NOTAR:
392 0 : case AG_FINALIZATION_STATUS_IMPLICITLY_SKIPPED:
393 0 : default:
394 0 : return;
395 0 : }
396 0 : }
397 :
398 : void
399 : ag_finality_tracker_mark_fast_finalized( ag_finality_tracker_t * self,
400 : ag_block_id_t const * block,
401 0 : ag_finalization_event_t * event ) {
402 0 : if( FD_UNLIKELY( block->slot<self->first_unpruned_slot ) ) return;
403 :
404 0 : ulong slot = block->slot;
405 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &slot, NULL, self->status.pool );
406 0 : if( FD_UNLIKELY( !ele ) ) {
407 0 : ele = status_pool_ele_acquire( self->status.pool );
408 0 : ele->slot = slot;
409 0 : status_map_ele_insert( self->status.map, ele, self->status.pool );
410 0 : } else {
411 0 : switch( ele->status.kind ) {
412 0 : case AG_FINALIZATION_STATUS_FINALIZED:
413 0 : case AG_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
414 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), block->hash, sizeof(ag_block_hash_t) ), "consensus safety violation" );
415 0 : return;
416 0 : case AG_FINALIZATION_STATUS_NOTARIZED:
417 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), block->hash, sizeof(ag_block_hash_t) ), "consensus safety violation" );
418 0 : break;
419 0 : 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 0 : }
426 0 : }
427 0 : ele->status.kind = AG_FINALIZATION_STATUS_FINALIZED;
428 0 : memcpy( ele->status.finalized.hash, block->hash, sizeof(ag_block_hash_t) );
429 :
430 0 : handle_finalized_block( self, block, event );
431 0 : }
432 :
433 : void
434 : ag_finality_tracker_mark_notarized( ag_finality_tracker_t * self,
435 : ag_block_id_t const * block,
436 0 : ag_finalization_event_t * event ) {
437 0 : if( FD_UNLIKELY( block->slot<self->first_unpruned_slot ) ) return;
438 :
439 0 : ulong slot = block->slot;
440 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &slot, NULL, self->status.pool );
441 0 : if( FD_UNLIKELY( !ele ) ) {
442 0 : ele = status_pool_ele_acquire( self->status.pool );
443 0 : ele->slot = slot;
444 0 : ele->status.kind = AG_FINALIZATION_STATUS_NOTARIZED;
445 0 : memcpy( ele->status.notarized.hash, block->hash, sizeof(ag_block_hash_t) );
446 0 : status_map_ele_insert( self->status.map, ele, self->status.pool );
447 0 : return;
448 0 : }
449 :
450 0 : switch( ele->status.kind ) {
451 0 : case AG_FINALIZATION_STATUS_NOTARIZED:
452 0 : case AG_FINALIZATION_STATUS_FINALIZED:
453 0 : case AG_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
454 0 : FD_CHECK_CRIT( 0==memcmp( status_hash( &ele->status ), block->hash, sizeof(ag_block_hash_t) ), "consensus safety violation" );
455 0 : return;
456 0 : case AG_FINALIZATION_STATUS_IMPLICITLY_SKIPPED:
457 0 : return;
458 0 : case AG_FINALIZATION_STATUS_FINAL_PENDING_NOTAR: {
459 0 : ele->status.kind = AG_FINALIZATION_STATUS_FINALIZED;
460 0 : memcpy( ele->status.finalized.hash, block->hash, sizeof(ag_block_hash_t) );
461 0 : handle_finalized_block( self, block, event );
462 0 : return;
463 0 : }
464 0 : default:
465 0 : FD_LOG_CRIT(( "unreachable" ));
466 0 : }
467 0 : }
468 :
469 : void
470 : ag_finality_tracker_mark_finalized( ag_finality_tracker_t * self,
471 : ulong slot,
472 0 : ag_finalization_event_t * event ) {
473 0 : if( FD_UNLIKELY( slot<self->first_unpruned_slot ) ) return;
474 :
475 0 : status_ele_t * ele = status_map_ele_query( self->status.map, &slot, NULL, self->status.pool );
476 0 : if( FD_UNLIKELY( !ele ) ) {
477 0 : ele = status_pool_ele_acquire( self->status.pool );
478 0 : ele->slot = slot;
479 0 : ele->status.kind = AG_FINALIZATION_STATUS_FINAL_PENDING_NOTAR;
480 0 : status_map_ele_insert( self->status.map, ele, self->status.pool );
481 0 : return;
482 0 : }
483 :
484 0 : switch( ele->status.kind ) {
485 0 : case AG_FINALIZATION_STATUS_FINAL_PENDING_NOTAR:
486 0 : case AG_FINALIZATION_STATUS_FINALIZED:
487 0 : case AG_FINALIZATION_STATUS_IMPLICITLY_FINALIZED:
488 0 : return;
489 0 : case AG_FINALIZATION_STATUS_NOTARIZED: {
490 0 : ag_block_id_t block = ag_block_id( slot, ele->status.notarized.hash );
491 0 : ele->status.kind = AG_FINALIZATION_STATUS_FINALIZED;
492 0 : memcpy( ele->status.finalized.hash, block.hash, sizeof(ag_block_hash_t) );
493 0 : handle_finalized_block( self, &block, event );
494 0 : return;
495 0 : }
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 0 : }
501 0 : }
502 :
503 : ulong
504 0 : ag_finality_tracker_highest_finalized_slot( ag_finality_tracker_t const * self ) {
505 0 : return self->highest_finalized_slot;
506 0 : }
507 :
508 : FD_FN_PURE uchar const *
509 0 : ag_finality_tracker_highest_finalized_block_hash( ag_finality_tracker_t const * self ) {
510 0 : status_ele_t const * e = status_map_ele_query_const( self->status.map, &self->highest_finalized_slot, NULL, self->status.pool );
511 0 : if( FD_UNLIKELY( !e || ( e->status.kind!=AG_FINALIZATION_STATUS_FINALIZED && e->status.kind!=AG_FINALIZATION_STATUS_IMPLICITLY_FINALIZED ) ) ) return NULL;
512 0 : return status_hash( &e->status );
513 0 : }
514 :
515 : ulong
516 0 : ag_finality_tracker_first_unpruned_slot( ag_finality_tracker_t const * self ) {
517 0 : return self->first_unpruned_slot;
518 0 : }
519 :
520 : int
521 : ag_finality_tracker_status( ag_finality_tracker_t const * self,
522 : ulong slot,
523 0 : ag_block_hash_t out_hash ) {
524 0 : status_ele_t const * e = status_map_ele_query_const( self->status.map, &slot, NULL, self->status.pool );
525 0 : if( FD_UNLIKELY( !e ) ) return -1;
526 0 : 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 0 : return e->status.kind;
532 0 : }
533 :
534 : int
535 : ag_finality_tracker_has_parent( ag_finality_tracker_t const * self,
536 0 : ag_block_id_t const * block ) {
537 0 : return !!parent_map_ele_query_const( self->parents.map, block, NULL, self->parents.pool );
538 0 : }
|