LCOV - code coverage report
Current view: top level - choreo/votor - ag_parent_ready_tracker.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 190 216 88.0 %
Date: 2026-09-17 04:28:31 Functions: 20 20 100.0 %

          Line data    Source code
       1             : #include "ag_parent_ready_tracker.h"
       2             : 
       3             : ulong
       4        2232 : ag_parent_ready_tracker_align( void ) {
       5        2232 :   return alignof(ag_parent_ready_tracker_t);
       6        2232 : }
       7             : 
       8             : ulong
       9         522 : ag_parent_ready_tracker_footprint( ulong slot_max ) {
      10         522 :   slot_max = fd_ulong_pow2_up( slot_max );
      11         522 :   ulong chain_cnt = ag_parent_ready_state_map_chain_cnt_est( slot_max );
      12         522 :   return FD_LAYOUT_FINI(
      13         522 :     FD_LAYOUT_APPEND(
      14         522 :     FD_LAYOUT_APPEND(
      15         522 :     FD_LAYOUT_APPEND(
      16         522 :     FD_LAYOUT_INIT,
      17         522 :       alignof(ag_parent_ready_tracker_t), sizeof(ag_parent_ready_tracker_t)                            ),
      18         522 :       ag_parent_ready_state_pool_align(), ag_parent_ready_state_pool_footprint( slot_max )             ),
      19         522 :       ag_parent_ready_state_map_align(),  ag_parent_ready_state_map_footprint ( chain_cnt )            ),
      20         522 :     ag_parent_ready_tracker_align() );
      21         522 : }
      22             : 
      23             : void *
      24             : ag_parent_ready_tracker_new( void * shmem,
      25             :                              ulong  slot_max,
      26         147 :                              ulong  seed ) {
      27         147 :   if( FD_UNLIKELY( !shmem ) ) {
      28           0 :     FD_LOG_WARNING(( "NULL mem" ));
      29           0 :     return NULL;
      30           0 :   }
      31             : 
      32         147 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, ag_parent_ready_tracker_align() ) ) ) {
      33           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      34           0 :     return NULL;
      35           0 :   }
      36             : 
      37         147 :   ulong footprint = ag_parent_ready_tracker_footprint( slot_max );
      38         147 :   if( FD_UNLIKELY( !footprint ) ) {
      39           0 :     FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
      40           0 :     return NULL;
      41           0 :   }
      42             : 
      43         147 :   slot_max = fd_ulong_pow2_up( slot_max );
      44             : 
      45         147 :   fd_memset( shmem, 0, footprint );
      46             : 
      47         147 :   ulong chain_cnt = ag_parent_ready_state_map_chain_cnt_est( slot_max );
      48             : 
      49         147 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
      50         147 :   ag_parent_ready_tracker_t * tracker    = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_parent_ready_tracker_t), sizeof(ag_parent_ready_tracker_t)                 );
      51         147 :   void *                      state_pool = FD_SCRATCH_ALLOC_APPEND( l, ag_parent_ready_state_pool_align(), ag_parent_ready_state_pool_footprint( slot_max )  );
      52         147 :   void *                      state_map  = FD_SCRATCH_ALLOC_APPEND( l, ag_parent_ready_state_map_align(),  ag_parent_ready_state_map_footprint ( chain_cnt )           );
      53         147 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, ag_parent_ready_tracker_align() ) == (ulong)shmem + footprint );
      54             : 
      55         147 :   tracker->root        = ULONG_MAX;
      56         147 :   tracker->states.pool = ag_parent_ready_state_pool_join( ag_parent_ready_state_pool_new( state_pool, slot_max        ) );
      57         147 :   tracker->states.map  = ag_parent_ready_state_map_join ( ag_parent_ready_state_map_new ( state_map,  chain_cnt, seed ) );
      58             : 
      59             : 
      60         147 :   return shmem;
      61         147 : }
      62             : 
      63             : ag_parent_ready_tracker_t *
      64         147 : ag_parent_ready_tracker_join( void * shtracker ) {
      65         147 :   ag_parent_ready_tracker_t * tracker = (ag_parent_ready_tracker_t *)shtracker;
      66             : 
      67         147 :   if( FD_UNLIKELY( !tracker ) ) {
      68           0 :     FD_LOG_WARNING(( "NULL tracker" ));
      69           0 :     return NULL;
      70           0 :   }
      71             : 
      72         147 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)tracker, ag_parent_ready_tracker_align() ) ) ) {
      73           0 :     FD_LOG_WARNING(( "misaligned tracker" ));
      74           0 :     return NULL;
      75           0 :   }
      76             : 
      77         147 :   return tracker;
      78         147 : }
      79             : 
      80             : void *
      81          30 : ag_parent_ready_tracker_leave( ag_parent_ready_tracker_t const * tracker ) {
      82          30 :   if( FD_UNLIKELY( !tracker ) ) {
      83           0 :     FD_LOG_WARNING(( "NULL tracker" ));
      84           0 :     return NULL;
      85           0 :   }
      86          30 :   return (void *)tracker;
      87          30 : }
      88             : 
      89             : void *
      90          30 : ag_parent_ready_tracker_delete( void * shtracker ) {
      91          30 :   if( FD_UNLIKELY( !shtracker ) ) {
      92           0 :     FD_LOG_WARNING(( "NULL tracker" ));
      93           0 :     return NULL;
      94           0 :   }
      95             : 
      96          30 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtracker, ag_parent_ready_tracker_align() ) ) ) {
      97           0 :     FD_LOG_WARNING(( "misaligned tracker" ));
      98           0 :     return NULL;
      99           0 :   }
     100             : 
     101          30 :   return shtracker;
     102          30 : }
     103             : 
     104             : static void
     105             : add_to_ready( ag_parent_ready_state_t * state,
     106         180 :               ag_block_id_t const *     id ) {
     107         180 :   if( FD_LIKELY( !state->is_ready ) ) {
     108         171 :     state->ready_ids[ 0 ] = *id;
     109         171 :     state->ready_id_cnt   = 1UL;
     110         171 :     state->is_ready       = 1;
     111         171 :   } else {
     112           9 :     state->ready_ids[ state->ready_id_cnt++ ] = *id;
     113           9 :   }
     114         180 : }
     115             : 
     116             : static ag_block_id_t
     117          30 : wait_for_parent_ready( ag_parent_ready_state_t * state ) {
     118          30 :   if( FD_UNLIKELY( state->is_ready ) ) {
     119          27 :     ag_block_id_t arg_min = state->ready_ids[0];
     120          36 :     for( ulong i=1; i<state->ready_id_cnt; i++ ) {
     121           9 :       ag_block_id_t const * id = &state->ready_ids[i];
     122           9 :       if( FD_UNLIKELY( id->slot<arg_min.slot ) || ( id->slot==arg_min.slot && 0>memcmp( id->hash, arg_min.hash, sizeof(ag_block_hash_t) ) ) ) {
     123           6 :         arg_min = *id;
     124           6 :       }
     125           9 :     }
     126          27 :     return arg_min;
     127          27 :   } else {
     128           3 :     return (ag_block_id_t){ .slot = ULONG_MAX };
     129           3 :   }
     130          30 : }
     131             : 
     132             : static ag_parent_ready_state_t *
     133             : slot_state( ag_parent_ready_tracker_t * self,
     134        2448 :             ulong                       slot ) {
     135        2448 :   ag_parent_ready_state_t * state = ag_parent_ready_state_map_ele_query( self->states.map, &slot, NULL, self->states.pool );
     136        2448 :   if( FD_LIKELY( state ) ) return state;
     137             : 
     138         822 :   ag_parent_ready_state_t * pool = self->states.pool;
     139         822 :   if( FD_UNLIKELY( !ag_parent_ready_state_pool_free( pool ) ) ) {
     140           0 :     FD_LOG_ERR(( "parent_ready_tracker: state pool exhausted (slot_max exceeded) at slot %lu", slot ));
     141           0 :   }
     142             : 
     143         822 :   state                      = ag_parent_ready_state_pool_ele_acquire( pool );
     144         822 :   state->slot                = slot;
     145         822 :   state->skip                = 0;
     146         822 :   state->notar_fallbacks_cnt = (uchar)0;
     147         822 :   state->is_ready            = 0;
     148         822 :   state->ready_id_cnt        = 0UL;
     149         822 :   ag_parent_ready_state_map_ele_insert( self->states.map, state, pool );
     150         822 :   return state;
     151         822 : }
     152             : 
     153             : void
     154             : ag_parent_ready_tracker_mark_notar_fallback( ag_parent_ready_tracker_t * self,
     155             :                                              ag_block_id_t const *       id,
     156             :                                              ag_parent_ready_t *         newly_certified,
     157        1137 :                                              ulong *                     newly_certified_cnt ) {
     158        1137 :   *newly_certified_cnt = 0UL;
     159             : 
     160        1137 :   ulong         slot = id->slot;
     161        1137 :   uchar const * hash = id->hash;
     162             : 
     163        1137 :   if( FD_UNLIKELY( slot < self->root ) ) return;
     164             : 
     165        1134 :   ag_parent_ready_state_t * state = slot_state( self, slot );
     166        1134 :   for( ulong i=0UL; i<state->notar_fallbacks_cnt; i++ ) {
     167         612 :     if( FD_UNLIKELY( 0==memcmp( state->notar_fallbacks[i], hash, sizeof(ag_block_hash_t) ) ) ) return;
     168         612 :   }
     169         522 :   memcpy( state->notar_fallbacks[ state->notar_fallbacks_cnt++ ], hash, sizeof(ag_block_hash_t) );
     170             : 
     171         540 :   for( ulong slot_=slot+1; ; slot_++ ) {
     172         540 :     ag_parent_ready_state_t * state_ = slot_state( self, slot_ );
     173         540 :     if( FD_UNLIKELY( ag_is_start_of_window( slot_ ) ) ) {
     174         126 :       add_to_ready( state_, id );
     175         126 :       newly_certified[ *newly_certified_cnt ].slot   = slot_;
     176         126 :       newly_certified[ *newly_certified_cnt ].parent = *id;
     177         126 :       (*newly_certified_cnt)++;
     178         126 :     }
     179         540 :     if( FD_LIKELY( !state_->skip ) ) break;
     180         540 :   }
     181         522 :   return;
     182        1134 : }
     183             : 
     184             : void
     185             : ag_parent_ready_tracker_mark_skipped( ag_parent_ready_tracker_t * self,
     186             :                                       ulong                       marked_slot,
     187             :                                       ag_parent_ready_t *         newly_certified,
     188         165 :                                       ulong *                     newly_certified_cnt ) {
     189         165 :   *newly_certified_cnt = 0UL;
     190             : 
     191         165 :   if( FD_UNLIKELY( marked_slot < self->root ) ) return;
     192         165 :   ag_parent_ready_state_t * state = slot_state( self, marked_slot );
     193         165 :   if( FD_UNLIKELY( state->skip ) ) return;
     194         165 :   state->skip = 1;
     195             : 
     196         165 :   ag_block_id_t potential_parents[ AG_SLOTS_PER_WINDOW*AG_NOTAR_FALLBACK_CERT_MAX ];
     197         165 :   ulong         potential_cnt = 0UL;
     198             : 
     199         492 :   for( ulong slot=marked_slot; slot>=fd_ulong_max( ag_first_slot_in_window( marked_slot ), self->root ); slot-- ) {
     200         429 :     ag_parent_ready_state_t * state = slot_state( self, slot );
     201             : 
     202         429 :     if( FD_LIKELY( slot!=marked_slot ) ) {
     203         336 :       for( ulong i=0UL; i<state->notar_fallbacks_cnt; i++ ) {
     204          72 :         FD_TEST( potential_cnt < AG_SLOTS_PER_WINDOW*AG_NOTAR_FALLBACK_CERT_MAX );
     205          72 :         potential_parents[ potential_cnt ] = ag_block_id( slot, state->notar_fallbacks[i] );
     206          72 :         potential_cnt++;
     207          72 :       }
     208         264 :     }
     209             : 
     210         429 :     if( FD_LIKELY( !state->skip ) ) break;
     211             : 
     212         390 :     for( ulong i=0UL; i<state->ready_id_cnt; i++ ) {
     213          63 :       FD_TEST( potential_cnt < AG_SLOTS_PER_WINDOW*AG_NOTAR_FALLBACK_CERT_MAX );
     214          63 :       potential_parents[ potential_cnt ] = state->ready_ids[i];
     215          63 :       potential_cnt++;
     216          63 :     }
     217         327 :   }
     218             : 
     219         180 :   for( ulong s=marked_slot+1UL; ; s++ ) {
     220         180 :     ag_parent_ready_state_t * fstate = slot_state( self, s );
     221         180 :     if( FD_UNLIKELY( ag_is_start_of_window( s ) ) ) {
     222         105 :       for( ulong i=0UL; i<potential_cnt; i++ ) {
     223          48 :         add_to_ready( fstate, &potential_parents[i] );
     224          48 :         FD_TEST( *newly_certified_cnt < ag_parent_ready_state_pool_max( self->states.pool ) ); /* caller sized for slot_max */
     225          48 :         newly_certified[ *newly_certified_cnt ].slot   = s;
     226          48 :         newly_certified[ *newly_certified_cnt ].parent = potential_parents[i];
     227          48 :         (*newly_certified_cnt)++;
     228          48 :       }
     229          57 :     }
     230         180 :     if( FD_LIKELY( !fstate->skip ) ) break;
     231         180 :   }
     232         165 :   return;
     233         165 : }
     234             : 
     235             : static void
     236             : keep_highest( ag_parent_ready_t *       best,
     237             :               ag_parent_ready_t const * ready,
     238         405 :               ulong                     ready_cnt ) {
     239         441 :   for( ulong i=0UL; i<ready_cnt; i++ ) {
     240          36 :     if( FD_LIKELY( best->slot==ULONG_MAX || ready[i].slot>=best->slot ) ) *best = ready[i];
     241          36 :   }
     242         405 : }
     243             : 
     244             : ag_parent_ready_t
     245             : ag_parent_ready_tracker_handle_finalization( ag_parent_ready_tracker_t *     self,
     246             :                                              ag_finalization_event_t const * event,
     247             :                                              ag_parent_ready_t *             newly_certified,
     248         756 :                                              ulong *                         newly_certified_cnt ) {
     249         756 :   ag_parent_ready_t best;
     250         756 :   fd_memset( &best, 0, sizeof(ag_parent_ready_t) );
     251         756 :   best.slot = ULONG_MAX;
     252             : 
     253         756 :   if( FD_LIKELY( event->finalized.slot!=ULONG_MAX ) ) {
     254         369 :     ag_parent_ready_tracker_mark_notar_fallback( self, &event->finalized, newly_certified, newly_certified_cnt );
     255         369 :     keep_highest( &best, newly_certified, *newly_certified_cnt );
     256         369 :   }
     257             : 
     258         780 :   for( ulong j=0UL; j<event->implicitly_finalized_cnt; j++ ) {
     259          24 :     ag_parent_ready_tracker_mark_notar_fallback( self, &event->implicitly_finalized[j], newly_certified, newly_certified_cnt );
     260          24 :     keep_highest( &best, newly_certified, *newly_certified_cnt );
     261          24 :   }
     262             : 
     263         768 :   for( ulong j=0UL; j<event->implicitly_skipped_cnt; j++ ) {
     264          12 :     ag_parent_ready_tracker_mark_skipped( self, event->implicitly_skipped[j], newly_certified, newly_certified_cnt );
     265          12 :     keep_highest( &best, newly_certified, *newly_certified_cnt );
     266          12 :   }
     267             : 
     268         756 :   return best;
     269         756 : }
     270             : 
     271             : ag_block_id_t const *
     272             : ag_parent_ready_tracker_parents_ready( ag_parent_ready_tracker_t * self,
     273             :                                        ulong                       slot,
     274          48 :                                        ulong *                     cnt ) {
     275          48 :   ag_parent_ready_state_t * state = ag_parent_ready_state_map_ele_query( self->states.map, &slot, NULL, self->states.pool );
     276          48 :   if( FD_UNLIKELY( !state ) ) { *cnt = 0UL; return NULL; }
     277          39 :   *cnt = state->ready_id_cnt;
     278          39 :   return state->ready_ids;
     279          48 : }
     280             : 
     281             : ag_block_id_t
     282             : ag_parent_ready_tracker_wait_for_parent_ready( ag_parent_ready_tracker_t * self,
     283          21 :                                                ulong                       slot ) {
     284          21 :   ag_parent_ready_state_t * state = ag_parent_ready_state_map_ele_query( self->states.map, &slot, NULL, self->states.pool );
     285          21 :   if( FD_UNLIKELY( !state ) ) return (ag_block_id_t){ .slot = ULONG_MAX };
     286          12 :   return wait_for_parent_ready( state );
     287          21 : }
     288             : 
     289             : void
     290             : ag_parent_ready_tracker_prune( ag_parent_ready_tracker_t * self,
     291         723 :                                ulong                       new_root ) {
     292         723 :   ag_parent_ready_state_map_t * map  = self->states.map;
     293         723 :   ag_parent_ready_state_t *     pool = self->states.pool;
     294        1083 :   for( ulong slot=self->root; slot<new_root; slot++ ) {
     295         360 :     ag_parent_ready_state_t * ele = ag_parent_ready_state_map_ele_remove( map, &slot, NULL, pool );
     296         360 :     if( FD_LIKELY( ele ) ) ag_parent_ready_state_pool_ele_release( pool, ele );
     297         360 :   }
     298         723 :   self->root = new_root;
     299         723 : }

Generated by: LCOV version 1.14