LCOV - code coverage report
Current view: top level - choreo/votor - ag_finality_tracker.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 300 370 81.1 %
Date: 2026-09-17 04:28:31 Functions: 21 22 95.5 %

          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 : }

Generated by: LCOV version 1.14