LCOV - code coverage report
Current view: top level - choreo/votor - ag_parent_ready_tracker.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 210 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 17 0.0 %

          Line data    Source code
       1             : #include "ag_parent_ready_tracker.h"
       2             : 
       3             : ulong
       4           0 : ag_parent_ready_tracker_align( void ) {
       5           0 :   return alignof(ag_parent_ready_tracker_t);
       6           0 : }
       7             : 
       8             : ulong
       9           0 : ag_parent_ready_tracker_footprint( ulong slot_max ) {
      10           0 :   ulong chain_cnt = ag_parent_ready_state_map_chain_cnt_est( slot_max );
      11           0 :   return FD_LAYOUT_FINI(
      12           0 :     FD_LAYOUT_APPEND(
      13           0 :     FD_LAYOUT_APPEND(
      14           0 :     FD_LAYOUT_APPEND(
      15           0 :     FD_LAYOUT_INIT,
      16           0 :       alignof(ag_parent_ready_tracker_t), sizeof(ag_parent_ready_tracker_t)                            ),
      17           0 :       ag_parent_ready_state_pool_align(), ag_parent_ready_state_pool_footprint( slot_max )             ),
      18           0 :       ag_parent_ready_state_map_align(),  ag_parent_ready_state_map_footprint ( chain_cnt )            ),
      19           0 :     ag_parent_ready_tracker_align() );
      20           0 : }
      21             : 
      22             : void *
      23             : ag_parent_ready_tracker_new( void * shmem,
      24             :                              ulong  slot_max,
      25           0 :                              ulong  seed ) {
      26           0 :   if( FD_UNLIKELY( !shmem ) ) {
      27           0 :     FD_LOG_WARNING(( "NULL mem" ));
      28           0 :     return NULL;
      29           0 :   }
      30             : 
      31           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, ag_parent_ready_tracker_align() ) ) ) {
      32           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      33           0 :     return NULL;
      34           0 :   }
      35             : 
      36           0 :   ulong footprint = ag_parent_ready_tracker_footprint( slot_max );
      37           0 :   if( FD_UNLIKELY( !footprint ) ) {
      38           0 :     FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
      39           0 :     return NULL;
      40           0 :   }
      41             : 
      42           0 :   fd_memset( shmem, 0, footprint );
      43             : 
      44           0 :   ulong chain_cnt = ag_parent_ready_state_map_chain_cnt_est( slot_max );
      45             : 
      46           0 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
      47           0 :   ag_parent_ready_tracker_t * tracker    = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_parent_ready_tracker_t), sizeof(ag_parent_ready_tracker_t)                 );
      48           0 :   void *                      state_pool = FD_SCRATCH_ALLOC_APPEND( l, ag_parent_ready_state_pool_align(), ag_parent_ready_state_pool_footprint( slot_max )  );
      49           0 :   void *                      state_map  = FD_SCRATCH_ALLOC_APPEND( l, ag_parent_ready_state_map_align(),  ag_parent_ready_state_map_footprint ( chain_cnt )           );
      50           0 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, ag_parent_ready_tracker_align() ) == (ulong)shmem + footprint );
      51             : 
      52           0 :   tracker->root          = ULONG_MAX;
      53           0 :   tracker->highest_ready = 0UL;
      54           0 :   tracker->states.pool = ag_parent_ready_state_pool_join( ag_parent_ready_state_pool_new( state_pool, slot_max        ) );
      55           0 :   tracker->states.map  = ag_parent_ready_state_map_join ( ag_parent_ready_state_map_new ( state_map,  chain_cnt, seed ) );
      56             : 
      57             : 
      58           0 :   return shmem;
      59           0 : }
      60             : 
      61             : ag_parent_ready_tracker_t *
      62           0 : ag_parent_ready_tracker_join( void * shtracker ) {
      63           0 :   ag_parent_ready_tracker_t * tracker = (ag_parent_ready_tracker_t *)shtracker;
      64             : 
      65           0 :   if( FD_UNLIKELY( !tracker ) ) {
      66           0 :     FD_LOG_WARNING(( "NULL tracker" ));
      67           0 :     return NULL;
      68           0 :   }
      69             : 
      70           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)tracker, ag_parent_ready_tracker_align() ) ) ) {
      71           0 :     FD_LOG_WARNING(( "misaligned tracker" ));
      72           0 :     return NULL;
      73           0 :   }
      74             : 
      75           0 :   return tracker;
      76           0 : }
      77             : 
      78             : void *
      79           0 : ag_parent_ready_tracker_leave( ag_parent_ready_tracker_t const * tracker ) {
      80           0 :   if( FD_UNLIKELY( !tracker ) ) {
      81           0 :     FD_LOG_WARNING(( "NULL tracker" ));
      82           0 :     return NULL;
      83           0 :   }
      84           0 :   return (void *)tracker;
      85           0 : }
      86             : 
      87             : void *
      88           0 : ag_parent_ready_tracker_delete( void * shtracker ) {
      89           0 :   if( FD_UNLIKELY( !shtracker ) ) {
      90           0 :     FD_LOG_WARNING(( "NULL tracker" ));
      91           0 :     return NULL;
      92           0 :   }
      93             : 
      94           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtracker, ag_parent_ready_tracker_align() ) ) ) {
      95           0 :     FD_LOG_WARNING(( "misaligned tracker" ));
      96           0 :     return NULL;
      97           0 :   }
      98             : 
      99           0 :   return shtracker;
     100           0 : }
     101             : 
     102             : static ag_parent_ready_state_t *
     103             : slot_state( ag_parent_ready_tracker_t * self,
     104           0 :             ulong                       slot ) {
     105           0 :   ag_parent_ready_state_t * state = ag_parent_ready_state_map_ele_query( self->states.map, &slot, NULL, self->states.pool );
     106           0 :   if( FD_LIKELY( state ) ) return state;
     107             : 
     108           0 :   ag_parent_ready_state_t * pool = self->states.pool;
     109           0 :   if( FD_UNLIKELY( !ag_parent_ready_state_pool_free( pool ) ) ) {
     110           0 :     FD_LOG_ERR(( "parent_ready_tracker: state pool exhausted (slot_max exceeded) at slot %lu", slot ));
     111           0 :   }
     112             : 
     113           0 :   state                       = ag_parent_ready_state_pool_ele_acquire( pool );
     114           0 :   state->slot                 = slot;
     115           0 :   state->skip                 = 0;
     116           0 :   state->notar_fallbacks_cnt  = (uchar)0;
     117           0 :   state->b_lo                 = ULONG_MAX;
     118           0 :   state->parent_ready_lo.slot = ULONG_MAX;
     119           0 :   state->ready                = 0;
     120           0 :   ag_parent_ready_state_map_ele_insert( self->states.map, state, pool );
     121           0 :   return state;
     122           0 : }
     123             : 
     124             : static void
     125             : add_to_ready( ag_parent_ready_tracker_t * self,
     126             :               ulong                       slot,
     127             :               ag_block_id_t const *       parent,
     128             :               ag_parent_ready_t *         newly_certified,
     129           0 :               ulong *                     newly_certified_cnt ) {
     130           0 :   ag_parent_ready_state_t * state = slot_state( self, slot );
     131           0 :   if( FD_LIKELY( state->parent_ready_lo.slot==ULONG_MAX || ag_block_id_lt( parent, &state->parent_ready_lo ) ) ) state->parent_ready_lo = *parent;
     132           0 :   self->highest_ready = fd_ulong_max( self->highest_ready, slot );
     133           0 :   if( FD_UNLIKELY( state->ready ) ) return;
     134           0 :   state->ready = 1;
     135           0 :   newly_certified[ *newly_certified_cnt ].slot   = slot;
     136           0 :   newly_certified[ *newly_certified_cnt ].parent = state->parent_ready_lo;
     137           0 :   (*newly_certified_cnt)++;
     138           0 : }
     139             : 
     140             : ulong
     141             : ag_parent_ready_tracker_mark_notar_fallback( ag_parent_ready_tracker_t * self,
     142             :                                              ag_block_id_t const *       block_id,
     143           0 :                                              ag_parent_ready_t *         newly_certified ) {
     144           0 :   ulong newly_certified_cnt = 0UL;
     145             : 
     146           0 :   ulong         slot = block_id->slot;
     147           0 :   uchar const * hash = block_id->hash;
     148             : 
     149           0 :   if( FD_UNLIKELY( slot < self->root ) ) return 0UL;
     150             : 
     151           0 :   ag_parent_ready_state_t * state = slot_state( self, slot );
     152           0 :   for( ulong i=0UL; i<state->notar_fallbacks_cnt; i++ ) {
     153           0 :     if( FD_UNLIKELY( 0==memcmp( state->notar_fallbacks[i], hash, sizeof(ag_block_hash_t) ) ) ) return 0UL;
     154           0 :   }
     155           0 :   FD_CHECK_CRIT( state->notar_fallbacks_cnt<AG_NOTAR_FALLBACK_CERT_MAX, "consensus safety violation" ); /* Lemma 48 */
     156           0 :   memcpy( state->notar_fallbacks[ state->notar_fallbacks_cnt++ ], hash, sizeof(ag_block_hash_t) );
     157             : 
     158           0 :   for( ulong s=slot+1UL; ; s++ ) {
     159           0 :     if( FD_UNLIKELY( ag_is_start_of_window( s ) ) ) add_to_ready( self, s, block_id, newly_certified, &newly_certified_cnt );
     160           0 :     ag_parent_ready_state_t const * next = ag_parent_ready_state_map_ele_query_const( self->states.map, &s, NULL, self->states.pool );
     161           0 :     if( FD_LIKELY( !next || !next->skip ) ) break;
     162           0 :   }
     163           0 :   return newly_certified_cnt;
     164           0 : }
     165             : 
     166             : ulong
     167             : ag_parent_ready_tracker_mark_root( ag_parent_ready_tracker_t * self,
     168             :                                    ag_block_id_t const *       root,
     169           0 :                                    ag_parent_ready_t *         newly_certified ) {
     170           0 :   ulong newly_certified_cnt = ag_parent_ready_tracker_mark_notar_fallback( self, root, newly_certified );
     171           0 :   ag_parent_ready_state_t * next = slot_state( self, root->slot+1UL );
     172           0 :   if( FD_LIKELY( next->parent_ready_lo.slot==ULONG_MAX ) ) next->parent_ready_lo = *root;
     173           0 :   return newly_certified_cnt;
     174           0 : }
     175             : 
     176             : ulong
     177             : ag_parent_ready_tracker_mark_skipped( ag_parent_ready_tracker_t * self,
     178             :                                       ulong                       skipped_slot,
     179           0 :                                       ag_parent_ready_t *         newly_certified ) {
     180           0 :   ulong newly_certified_cnt = 0UL;
     181             : 
     182           0 :   if( FD_UNLIKELY( skipped_slot < self->root ) ) return 0UL;
     183           0 :   ag_parent_ready_state_t * state = slot_state( self, skipped_slot );
     184           0 :   if( FD_UNLIKELY( state->skip ) ) return 0UL;
     185           0 :   state->skip = 1;
     186             : 
     187             :   /* The skip extends every chain ending at skipped_slot down to b_lo,
     188             :      the highest slot below skipped_slot that is not skipped.  The
     189             :      newly ready parents are the notar fallbacks of [b_lo,skipped_slot).
     190             :      Setting b_lo makes all of them ready, and parent_ready_lo is set to
     191             :      their minimum. */
     192             : 
     193           0 :   ag_block_id_t parent = { .slot = ULONG_MAX };
     194           0 :   ulong         b_lo   = ULONG_MAX;
     195           0 :   ulong         first  = ag_first_slot_in_window( skipped_slot );
     196           0 :   for( ulong slot=skipped_slot; slot>fd_ulong_max( first, self->root ); ) {
     197           0 :     slot--;
     198           0 :     ag_parent_ready_state_t const * prev = ag_parent_ready_state_map_ele_query_const( self->states.map, &slot, NULL, self->states.pool );
     199           0 :     for( ulong i=0UL; prev && i<prev->notar_fallbacks_cnt; i++ ) {
     200           0 :       ag_block_id_t nf = ag_block_id( slot, prev->notar_fallbacks[i] );
     201           0 :       if( parent.slot==ULONG_MAX || ag_block_id_lt( &nf, &parent ) ) parent = nf;
     202           0 :     }
     203           0 :     if( FD_LIKELY( !prev || !prev->skip ) ) { b_lo = slot; break; }
     204           0 :   }
     205           0 :   if( FD_UNLIKELY( b_lo==ULONG_MAX ) ) { /* [first,skipped_slot) all skipped, the window start knows the rest */
     206           0 :     ag_parent_ready_state_t const * start = ag_parent_ready_state_map_ele_query_const( self->states.map, &first, NULL, self->states.pool );
     207           0 :     b_lo = ( start && start->b_lo!=ULONG_MAX ) ? start->b_lo : fd_ulong_sat_sub( first, 1UL );
     208           0 :     if( start && start->parent_ready_lo.slot!=ULONG_MAX && start->parent_ready_lo.slot>=self->root && ( parent.slot==ULONG_MAX || ag_block_id_lt( &start->parent_ready_lo, &parent ) ) ) parent = start->parent_ready_lo;
     209           0 :   }
     210             : 
     211           0 :   for( ulong s=skipped_slot+1UL; ; s++ ) {
     212           0 :     if( FD_UNLIKELY( ag_is_start_of_window( s ) ) ) {
     213           0 :       slot_state( self, s )->b_lo = b_lo;
     214           0 :       if( FD_LIKELY( parent.slot!=ULONG_MAX ) ) add_to_ready( self, s, &parent, newly_certified, &newly_certified_cnt );
     215           0 :     }
     216           0 :     ag_parent_ready_state_t const * next = ag_parent_ready_state_map_ele_query_const( self->states.map, &s, NULL, self->states.pool );
     217           0 :     if( FD_LIKELY( !next || !next->skip ) ) break;
     218           0 :   }
     219           0 :   return newly_certified_cnt;
     220           0 : }
     221             : 
     222             : ulong
     223             : ag_parent_ready_tracker_parents_ready( ag_parent_ready_tracker_t const * self,
     224             :                                        ulong                             slot,
     225             :                                        ag_block_id_t *                   out,
     226           0 :                                        ulong                             out_max ) {
     227           0 :   if( FD_UNLIKELY( !ag_is_start_of_window( slot ) || slot<=self->root ) ) return 0UL;
     228             : 
     229           0 :   ag_parent_ready_state_t const * state = ag_parent_ready_state_map_ele_query_const( self->states.map, &slot, NULL, self->states.pool );
     230           0 :   ulong                           b_lo  = ( state && state->b_lo!=ULONG_MAX ) ? state->b_lo : slot-1UL;
     231             : 
     232           0 :   ulong cnt = 0UL;
     233           0 :   for( ulong s=fd_ulong_max( b_lo, self->root ); s<slot; s++ ) {
     234           0 :     ag_parent_ready_state_t const * nf = ag_parent_ready_state_map_ele_query_const( self->states.map, &s, NULL, self->states.pool );
     235           0 :     for( ulong i=0UL; nf && i<nf->notar_fallbacks_cnt; i++ ) {
     236           0 :       if( FD_LIKELY( cnt<out_max ) ) out[ cnt ] = ag_block_id( s, nf->notar_fallbacks[i] );
     237           0 :       cnt++;
     238           0 :     }
     239           0 :   }
     240           0 :   return cnt;
     241           0 : }
     242             : 
     243             : int
     244             : ag_parent_ready_tracker_is_parent_ready( ag_parent_ready_tracker_t const * self,
     245             :                                          ulong                             slot,
     246           0 :                                          ag_block_id_t const *             parent ) {
     247           0 :   if( FD_UNLIKELY( !ag_is_start_of_window( slot ) || parent->slot>=slot || parent->slot<self->root ) ) return 0;
     248             : 
     249           0 :   ag_parent_ready_state_t const * state = ag_parent_ready_state_map_ele_query_const( self->states.map, &slot, NULL, self->states.pool );
     250           0 :   ulong                           b_lo  = ( state && state->b_lo!=ULONG_MAX ) ? state->b_lo : slot-1UL;
     251           0 :   if( FD_UNLIKELY( parent->slot<b_lo ) ) return 0;
     252             : 
     253           0 :   ag_parent_ready_state_t const * nf = ag_parent_ready_state_map_ele_query_const( self->states.map, &parent->slot, NULL, self->states.pool );
     254           0 :   for( ulong i=0UL; nf && i<nf->notar_fallbacks_cnt; i++ ) {
     255           0 :     if( FD_UNLIKELY( 0==memcmp( nf->notar_fallbacks[i], parent->hash, sizeof(ag_block_hash_t) ) ) ) return 1;
     256           0 :   }
     257           0 :   return 0;
     258           0 : }
     259             : 
     260             : ag_block_id_t
     261             : ag_parent_ready_tracker_wait_for_parent_ready( ag_parent_ready_tracker_t const * self,
     262           0 :                                                ulong                             slot ) {
     263           0 :   ag_parent_ready_state_t const * state = ag_parent_ready_state_map_ele_query_const( self->states.map, &slot, NULL, self->states.pool );
     264           0 :   if( FD_UNLIKELY( !state || state->parent_ready_lo.slot<self->root ) ) return (ag_block_id_t){ .slot = ULONG_MAX };
     265           0 :   return state->parent_ready_lo;
     266           0 : }
     267             : 
     268             : void
     269             : ag_parent_ready_tracker_prune( ag_parent_ready_tracker_t * self,
     270           0 :                                ulong                       new_root ) {
     271           0 :   ag_parent_ready_state_map_t * map  = self->states.map;
     272           0 :   ag_parent_ready_state_t *     pool = self->states.pool;
     273           0 :   for( ulong slot=self->root; slot<new_root; slot++ ) {
     274           0 :     ag_parent_ready_state_t * ele = ag_parent_ready_state_map_ele_remove( map, &slot, NULL, pool );
     275           0 :     if( FD_LIKELY( ele ) ) ag_parent_ready_state_pool_ele_release( pool, ele );
     276           0 :   }
     277           0 :   self->root = new_root;
     278             : 
     279             :   /* A window start whose lowest ready parent was pruned takes the
     280             :      lowest of its ready parents [b_lo,slot) still retained. */
     281             : 
     282           0 :   for( ag_parent_ready_state_map_iter_t iter = ag_parent_ready_state_map_iter_init( map, pool );
     283           0 :                                               !ag_parent_ready_state_map_iter_done( iter, map, pool );
     284           0 :                                         iter = ag_parent_ready_state_map_iter_next( iter, map, pool ) ) {
     285           0 :     ag_parent_ready_state_t * state = ag_parent_ready_state_map_iter_ele( iter, map, pool );
     286           0 :     if( FD_LIKELY( state->parent_ready_lo.slot==ULONG_MAX || state->parent_ready_lo.slot>=new_root ) ) continue;
     287           0 :     ulong b_lo = state->b_lo!=ULONG_MAX ? state->b_lo : state->slot-1UL;
     288           0 :     state->parent_ready_lo.slot = ULONG_MAX;
     289           0 :     for( ulong s=fd_ulong_max( b_lo, new_root ); s<state->slot; s++ ) {
     290           0 :       ag_parent_ready_state_t const * nf = ag_parent_ready_state_map_ele_query_const( map, &s, NULL, pool );
     291           0 :       for( ulong i=0UL; nf && i<nf->notar_fallbacks_cnt; i++ ) {
     292           0 :         ag_block_id_t id = ag_block_id( s, nf->notar_fallbacks[i] );
     293           0 :         if( state->parent_ready_lo.slot==ULONG_MAX || ag_block_id_lt( &id, &state->parent_ready_lo ) ) state->parent_ready_lo = id;
     294           0 :       }
     295           0 :     }
     296           0 :   }
     297           0 : }

Generated by: LCOV version 1.14