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

          Line data    Source code
       1             : #include "ag_pool.h"
       2             : #include "ag_slot_state.h"
       3             : #include "ag_finality_tracker.h"
       4             : #include "ag_parent_ready_tracker.h"
       5             : 
       6             : #define QUEUE_NAME pool_events
       7           0 : #define QUEUE_T    ag_pool_event_t
       8             : #include "../../util/tmpl/fd_queue_dynamic.c"
       9             : 
      10             : /* repair_events are notifications that a block has reached SafeToNotar
      11             :    (or stronger) status.  It does not necessarily trigger actual repair
      12             :    requests if we already have the block... (that is the responsibility
      13             :    of the repair tile to answer).  */
      14             : 
      15             : #define QUEUE_NAME repair_events
      16           0 : #define QUEUE_T    ag_block_id_t
      17             : #include "../../util/tmpl/fd_queue_dynamic.c"
      18             : 
      19             : /* Protocol bounds (< 20% byzantine stake) on what burst adds push onto
      20             :    queues drained to empty before the next add:
      21             : 
      22             :    - IMPLICITLY_FINALIZED / SKIPPED: once per slot of the window
      23             :    - SAFE_TO_NOTAR of waiting children: 3 hashes per child slot (stake
      24             :      arithmetic), and correct nodes notar a child only at its parent's
      25             :      slot+1 or a window start, for each of up to burst certified
      26             :      parents
      27             :    - PARENT_READY: once per window start (ready flag), plus one per add
      28             :      moving the window
      29             :    - per add: 4 of CERT_CREATED, SAFE_TO_NOTAR and SAFE_TO_SKIP
      30             :    - repair: 4 block ids per add (3 created certs, 1 missing block) */
      31             : 
      32           0 : #define POOL_EVENT_MAX(slot_max,burst) ( (slot_max)                                                                 \
      33           0 :                                        + 3UL*( ((slot_max)+AG_SLOTS_PER_WINDOW-1UL)/AG_SLOTS_PER_WINDOW + (burst) ) \
      34           0 :                                        +     ( ((slot_max)+AG_SLOTS_PER_WINDOW-1UL)/AG_SLOTS_PER_WINDOW + (burst) ) \
      35           0 :                                        + 4UL*(burst) )
      36           0 : #define REPAIR_EVENT_MAX(burst)        ( 4UL*(burst) )
      37             : 
      38             : struct slot_state_ele {
      39             :   ulong           slot;
      40             :   ulong           next;
      41             :   ag_slot_state_t slot_state;
      42             : };
      43             : typedef struct slot_state_ele slot_state_ele_t;
      44             : 
      45             : #define POOL_NAME slot_state_pool
      46           0 : #define POOL_T    slot_state_ele_t
      47             : #include "../../util/tmpl/fd_pool.c"
      48             : 
      49             : #define MAP_NAME               slot_state_map
      50             : #define MAP_ELE_T              slot_state_ele_t
      51           0 : #define MAP_KEY                slot
      52             : #define MAP_KEY_T              ulong
      53           0 : #define MAP_KEY_EQ(k0,k1)      ((*(k0))==(*(k1)))
      54           0 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash( (*(key)) ^ (seed) ))
      55           0 : #define MAP_NEXT               next
      56             : #include "../../util/tmpl/fd_map_chain.c"
      57             : 
      58             : struct s2n_waiting_parent_cert_ele {
      59             :   ag_block_id_t parent;
      60             :   ulong         next;
      61             :   ulong         prev;
      62             :   ag_block_id_t child;
      63             : };
      64             : typedef struct s2n_waiting_parent_cert_ele s2n_waiting_parent_cert_ele_t;
      65             : 
      66             : #define POOL_NAME s2n_waiting_parent_cert_pool
      67           0 : #define POOL_T    s2n_waiting_parent_cert_ele_t
      68             : #include "../../util/tmpl/fd_pool.c"
      69             : 
      70             : #define MAP_NAME                           s2n_waiting_parent_cert_map
      71           0 : #define MAP_ELE_T                          s2n_waiting_parent_cert_ele_t
      72           0 : #define MAP_KEY                            parent.slot
      73           0 : #define MAP_KEY_HASH(key,seed)             (fd_ulong_hash( (*(key)) ^ (seed) ))
      74           0 : #define MAP_NEXT                           next
      75             : #define MAP_MULTI                          1
      76             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
      77             : #include "../../util/tmpl/fd_map_chain.c"
      78             : 
      79             : struct slot_states {
      80             :   slot_state_ele_t * pool;
      81             :   slot_state_map_t * map;
      82             :   ag_slot_states_t   shared;
      83             : };
      84             : typedef struct slot_states slot_states_t;
      85             : 
      86             : struct s2n_waiting_parent_cert {
      87             :   s2n_waiting_parent_cert_ele_t * pool;
      88             :   s2n_waiting_parent_cert_map_t * map;
      89             : };
      90             : typedef struct s2n_waiting_parent_cert s2n_waiting_parent_cert_t;
      91             : 
      92             : struct __attribute__((aligned(128UL))) ag_pool {
      93             :   ulong slot_max;
      94             : 
      95             :   slot_states_t *             slot_states;
      96             :   ag_parent_ready_tracker_t * parent_ready_tracker;
      97             :   ag_finality_tracker_t *     finality_tracker;
      98             :   s2n_waiting_parent_cert_t * s2n_waiting_parent_cert;
      99             : 
     100             :   ag_pool_event_t * pool_events;
     101             :   ag_block_id_t *   repair_events;
     102             : 
     103             :   struct {
     104             :     ag_parent_ready_t * parent_readys;
     105             :     ag_block_id_t *     implicitly_finalized;
     106             :     ulong *             implicitly_skipped;
     107             :   } scratch;
     108             : };
     109             : 
     110             : ulong
     111           0 : ag_pool_align( void ) {
     112           0 :   return alignof(ag_pool_t);
     113           0 : }
     114             : 
     115             : ulong
     116             : ag_pool_footprint( ulong slot_max,
     117           0 :                    ulong burst ) {
     118           0 :   if( FD_UNLIKELY( slot_max<AG_SLOTS_PER_WINDOW+AG_REWARD_SLOT_DELTA || slot_max>UINT_MAX/(AG_VAT_MAX*AG_SLOT_VOTE_SIG_MAX) ) ) return 0UL;
     119             : 
     120           0 :   ulong slot_chain_cnt  = slot_state_map_chain_cnt_est( slot_max );
     121           0 :   ulong sig_max         = slot_max*AG_VAT_MAX*AG_SLOT_VOTE_SIG_MAX;
     122           0 :   ulong notar_max       = slot_max*AG_VAT_MAX;
     123           0 :   ulong notar_chain_cnt = ag_vote_stake_map_chain_cnt_est( notar_max );
     124           0 :   ulong nf_max          = slot_max*AG_NOTAR_FALLBACK_HASH_MAX;
     125           0 :   ulong nf_chain_cnt    = ag_vote_stake_map_chain_cnt_est( nf_max );
     126           0 :   ulong s2n_max         = slot_max*AG_EQVOC_BLOCK_HASH_MAX;
     127           0 :   ulong s2n_chain_cnt   = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
     128           0 :   ulong ready_max       = slot_max + AG_SLOTS_PER_WINDOW + 1UL; /* [root-AG_REWARD_SLOT_DELTA-AG_SLOTS_PER_WINDOW, root+slot_max-AG_REWARD_SLOT_DELTA] */
     129           0 :   ulong event_max       = POOL_EVENT_MAX( slot_max, burst );
     130           0 :   ulong repair_max      = REPAIR_EVENT_MAX( burst );
     131             : 
     132           0 :   return FD_LAYOUT_FINI(
     133           0 :     FD_LAYOUT_APPEND(
     134           0 :     FD_LAYOUT_APPEND(
     135           0 :     FD_LAYOUT_APPEND(
     136           0 :     FD_LAYOUT_APPEND(
     137           0 :     FD_LAYOUT_APPEND(
     138           0 :     FD_LAYOUT_APPEND(
     139           0 :     FD_LAYOUT_APPEND(
     140           0 :     FD_LAYOUT_APPEND(
     141           0 :     FD_LAYOUT_APPEND(
     142           0 :     FD_LAYOUT_APPEND(
     143           0 :     FD_LAYOUT_APPEND(
     144           0 :     FD_LAYOUT_APPEND(
     145           0 :     FD_LAYOUT_APPEND(
     146           0 :     FD_LAYOUT_APPEND(
     147           0 :     FD_LAYOUT_APPEND(
     148           0 :     FD_LAYOUT_APPEND(
     149           0 :     FD_LAYOUT_APPEND(
     150           0 :     FD_LAYOUT_APPEND(
     151           0 :     FD_LAYOUT_APPEND(
     152           0 :     FD_LAYOUT_INIT,
     153           0 :       alignof(ag_pool_t),                   sizeof(ag_pool_t)                                       ),
     154           0 :       alignof(slot_states_t),               sizeof(slot_states_t)                                   ),
     155           0 :       slot_state_pool_align(),              slot_state_pool_footprint( slot_max )                   ),
     156           0 :       slot_state_map_align(),               slot_state_map_footprint ( slot_chain_cnt )             ),
     157           0 :       fd_bls_sig_pool_align(),              fd_bls_sig_pool_footprint( sig_max )                    ),
     158           0 :       ag_vote_stake_pool_align(),           ag_vote_stake_pool_footprint( notar_max )               ),
     159           0 :       ag_vote_stake_map_align(),            ag_vote_stake_map_footprint ( notar_chain_cnt )         ),
     160           0 :       ag_vote_stake_pool_align(),           ag_vote_stake_pool_footprint( nf_max )                  ),
     161           0 :       ag_vote_stake_map_align(),            ag_vote_stake_map_footprint ( nf_chain_cnt )            ),
     162           0 :       ag_parent_ready_tracker_align(),      ag_parent_ready_tracker_footprint( ready_max )          ),
     163           0 :       ag_finality_tracker_align(),          ag_finality_tracker_footprint( slot_max )               ),
     164           0 :       alignof(s2n_waiting_parent_cert_t),   sizeof(s2n_waiting_parent_cert_t)                       ),
     165           0 :       s2n_waiting_parent_cert_pool_align(), s2n_waiting_parent_cert_pool_footprint( s2n_max )       ),
     166           0 :       s2n_waiting_parent_cert_map_align(),  s2n_waiting_parent_cert_map_footprint ( s2n_chain_cnt ) ),
     167           0 :       pool_events_align(),                  pool_events_footprint( event_max )                      ),
     168           0 :       repair_events_align(),                repair_events_footprint( repair_max )                   ),
     169           0 :       alignof(ag_parent_ready_t),           sizeof(ag_parent_ready_t) * slot_max                    ),
     170           0 :       alignof(ag_block_id_t),               sizeof(ag_block_id_t)     * slot_max                    ),
     171           0 :       alignof(ulong),                       sizeof(ulong)             * slot_max                    ),
     172           0 :     ag_pool_align() );
     173           0 : }
     174             : 
     175             : void *
     176             : ag_pool_new( void * mem,
     177             :              ulong  slot_max,
     178             :              ulong  burst,
     179           0 :              ulong  seed ) {
     180           0 :   if( FD_UNLIKELY( !mem ) ) {
     181           0 :     FD_LOG_WARNING(( "NULL mem" ));
     182           0 :     return NULL;
     183           0 :   }
     184           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, ag_pool_align() ) ) ) {
     185           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     186           0 :     return NULL;
     187           0 :   }
     188           0 :   ulong footprint = ag_pool_footprint( slot_max, burst );
     189           0 :   if( FD_UNLIKELY( !footprint ) ) {
     190           0 :     FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
     191           0 :     return NULL;
     192           0 :   }
     193           0 :   fd_memset( mem, 0, footprint );
     194             : 
     195           0 :   ulong s2n_max         = slot_max*AG_EQVOC_BLOCK_HASH_MAX;
     196           0 :   ulong slot_chain_cnt  = slot_state_map_chain_cnt_est( slot_max );
     197           0 :   ulong sig_max         = slot_max*AG_VAT_MAX*AG_SLOT_VOTE_SIG_MAX;
     198           0 :   ulong notar_max       = slot_max*AG_VAT_MAX;
     199           0 :   ulong notar_chain_cnt = ag_vote_stake_map_chain_cnt_est( notar_max );
     200           0 :   ulong nf_max          = slot_max*AG_NOTAR_FALLBACK_HASH_MAX;
     201           0 :   ulong nf_chain_cnt    = ag_vote_stake_map_chain_cnt_est( nf_max );
     202           0 :   ulong s2n_chain_cnt   = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
     203           0 :   ulong ready_max       = slot_max + AG_SLOTS_PER_WINDOW + 1UL;
     204           0 :   ulong event_max       = POOL_EVENT_MAX( slot_max, burst );
     205           0 :   ulong repair_max      = REPAIR_EVENT_MAX( burst );
     206             : 
     207             : 
     208           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     209           0 :   ag_pool_t * pool                         = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_pool_t),                   sizeof(ag_pool_t)                                       );
     210           0 :   void *      slot_states                  = FD_SCRATCH_ALLOC_APPEND( l, alignof(slot_states_t),               sizeof(slot_states_t)                                   );
     211           0 :   void *      slot_state_pool              = FD_SCRATCH_ALLOC_APPEND( l, slot_state_pool_align(),              slot_state_pool_footprint( slot_max )                   );
     212           0 :   void *      slot_state_map               = FD_SCRATCH_ALLOC_APPEND( l, slot_state_map_align(),               slot_state_map_footprint ( slot_chain_cnt )             );
     213           0 :   void *      bls_sig_pool                 = FD_SCRATCH_ALLOC_APPEND( l, fd_bls_sig_pool_align(),              fd_bls_sig_pool_footprint( sig_max )                    );
     214           0 :   void *      notar_hash_stake_pool        = FD_SCRATCH_ALLOC_APPEND( l, ag_vote_stake_pool_align(),           ag_vote_stake_pool_footprint( notar_max )               );
     215           0 :   void *      notar_hash_stake_map         = FD_SCRATCH_ALLOC_APPEND( l, ag_vote_stake_map_align(),            ag_vote_stake_map_footprint ( notar_chain_cnt )         );
     216           0 :   void *      nf_hash_stake_pool           = FD_SCRATCH_ALLOC_APPEND( l, ag_vote_stake_pool_align(),           ag_vote_stake_pool_footprint( nf_max )                  );
     217           0 :   void *      nf_hash_stake_map            = FD_SCRATCH_ALLOC_APPEND( l, ag_vote_stake_map_align(),            ag_vote_stake_map_footprint ( nf_chain_cnt )            );
     218           0 :   void *      parent_ready_tracker         = FD_SCRATCH_ALLOC_APPEND( l, ag_parent_ready_tracker_align(),      ag_parent_ready_tracker_footprint( ready_max )          );
     219           0 :   void *      finality_tracker             = FD_SCRATCH_ALLOC_APPEND( l, ag_finality_tracker_align(),          ag_finality_tracker_footprint( slot_max )               );
     220           0 :   void *      s2n_waiting_parent_cert      = FD_SCRATCH_ALLOC_APPEND( l, alignof(s2n_waiting_parent_cert_t),   sizeof(s2n_waiting_parent_cert_t)                       );
     221           0 :   void *      s2n_waiting_parent_cert_pool = FD_SCRATCH_ALLOC_APPEND( l, s2n_waiting_parent_cert_pool_align(), s2n_waiting_parent_cert_pool_footprint( s2n_max )       );
     222           0 :   void *      s2n_waiting_parent_cert_map  = FD_SCRATCH_ALLOC_APPEND( l, s2n_waiting_parent_cert_map_align(),  s2n_waiting_parent_cert_map_footprint ( s2n_chain_cnt ) );
     223           0 :   void *      pool_events                  = FD_SCRATCH_ALLOC_APPEND( l, pool_events_align(),                  pool_events_footprint( event_max )                      );
     224           0 :   void *      repair_events                = FD_SCRATCH_ALLOC_APPEND( l, repair_events_align(),                repair_events_footprint( repair_max )                   );
     225           0 :   void *      parent_ready_scratch         = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_parent_ready_t),           sizeof(ag_parent_ready_t) * slot_max                    );
     226           0 :   void *      implicitly_finalized_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_block_id_t),               sizeof(ag_block_id_t)     * slot_max                    );
     227           0 :   void *      implicitly_skipped_scratch   = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong),                       sizeof(ulong)             * slot_max                    );
     228           0 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, ag_pool_align() ) == (ulong)mem + footprint );
     229             : 
     230           0 :   pool->slot_states                                        = (slot_states_t *)slot_states;
     231           0 :   pool->slot_states->pool                                  = slot_state_pool_join   ( slot_state_pool_new   ( slot_state_pool,       slot_max              ) );
     232           0 :   pool->slot_states->map                                   = slot_state_map_join    ( slot_state_map_new    ( slot_state_map,        slot_chain_cnt, seed  ) );
     233           0 :   pool->slot_states->shared.bls_sig_pool                   = fd_bls_sig_pool_join   ( fd_bls_sig_pool_new   ( bls_sig_pool,          sig_max               ) );
     234           0 :   pool->slot_states->shared.notar_hash_stake_pool          = ag_vote_stake_pool_join( ag_vote_stake_pool_new( notar_hash_stake_pool, notar_max             ) );
     235           0 :   pool->slot_states->shared.notar_hash_stake_map           = ag_vote_stake_map_join ( ag_vote_stake_map_new ( notar_hash_stake_map,  notar_chain_cnt, seed ) );
     236           0 :   pool->slot_states->shared.notar_fallback_hash_stake_pool = ag_vote_stake_pool_join( ag_vote_stake_pool_new( nf_hash_stake_pool,    nf_max                ) );
     237           0 :   pool->slot_states->shared.notar_fallback_hash_stake_map  = ag_vote_stake_map_join ( ag_vote_stake_map_new ( nf_hash_stake_map,     nf_chain_cnt, seed    ) );
     238           0 :   pool->slot_states->shared.prev_epoch_info                = NULL;
     239           0 :   pool->slot_states->shared.prev_epoch_rank                = USHORT_MAX;
     240           0 :   pool->slot_states->shared.prev_epoch_slot                = ULONG_MAX;
     241           0 :   pool->slot_states->shared.curr_epoch_info                = NULL;
     242           0 :   pool->slot_states->shared.curr_epoch_rank                = USHORT_MAX;
     243           0 :   pool->slot_states->shared.curr_epoch_slot                = ULONG_MAX;
     244           0 :   pool->slot_states->shared.next_epoch_info                = NULL;
     245           0 :   pool->slot_states->shared.next_epoch_rank                = USHORT_MAX;
     246           0 :   pool->slot_states->shared.next_epoch_slot                = ULONG_MAX;
     247           0 :   pool->slot_states->shared.bls_verify_cnt                 = 0UL;
     248             : 
     249           0 :   pool->parent_ready_tracker = ag_parent_ready_tracker_join( ag_parent_ready_tracker_new( parent_ready_tracker, ready_max, seed ) );
     250             : 
     251           0 :   pool->finality_tracker = ag_finality_tracker_join( ag_finality_tracker_new( finality_tracker, slot_max, seed ) );
     252             : 
     253           0 :   pool->s2n_waiting_parent_cert       = (s2n_waiting_parent_cert_t *)s2n_waiting_parent_cert;
     254           0 :   pool->s2n_waiting_parent_cert->pool = s2n_waiting_parent_cert_pool_join( s2n_waiting_parent_cert_pool_new( s2n_waiting_parent_cert_pool, s2n_max                            ) );
     255           0 :   pool->s2n_waiting_parent_cert->map  = s2n_waiting_parent_cert_map_join ( s2n_waiting_parent_cert_map_new ( s2n_waiting_parent_cert_map,  s2n_chain_cnt, seed ) );
     256             : 
     257           0 :   pool->pool_events   = pool_events_join  ( pool_events_new  ( pool_events,   event_max  ) );
     258           0 :   pool->repair_events = repair_events_join( repair_events_new( repair_events, repair_max ) );
     259             : 
     260           0 :   pool->slot_max = slot_max;
     261             : 
     262             : 
     263           0 :   pool->scratch.parent_readys        = (ag_parent_ready_t *)parent_ready_scratch;
     264           0 :   pool->scratch.implicitly_finalized = (ag_block_id_t *)implicitly_finalized_scratch;
     265           0 :   pool->scratch.implicitly_skipped   = (ulong *)implicitly_skipped_scratch;
     266             : 
     267             : 
     268           0 :   return mem;
     269           0 : }
     270             : 
     271             : ag_pool_t *
     272           0 : ag_pool_join( void * mem ) {
     273           0 :   ag_pool_t * pool = (ag_pool_t *)mem;
     274           0 :   if( FD_UNLIKELY( !pool ) ) {
     275           0 :     FD_LOG_WARNING(( "NULL mem" ));
     276           0 :     return NULL;
     277           0 :   }
     278           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)pool, ag_pool_align() ) ) ) {
     279           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     280           0 :     return NULL;
     281           0 :   }
     282           0 :   return pool;
     283           0 : }
     284             : 
     285             : void *
     286           0 : ag_pool_leave( ag_pool_t const * pool ) {
     287           0 :   if( FD_UNLIKELY( !pool ) ) {
     288           0 :     FD_LOG_WARNING(( "NULL pool" ));
     289           0 :     return NULL;
     290           0 :   }
     291           0 :   return (void *)pool;
     292           0 : }
     293             : 
     294             : void *
     295           0 : ag_pool_delete( void * mem ) {
     296           0 :   if( FD_UNLIKELY( !mem ) ) {
     297           0 :     FD_LOG_WARNING(( "NULL mem" ));
     298           0 :     return NULL;
     299           0 :   }
     300           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, ag_pool_align() ) ) ) {
     301           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     302           0 :     return NULL;
     303           0 :   }
     304           0 :   return mem;
     305           0 : }
     306             : 
     307             : static void
     308             : pool_init( ag_pool_t *           self,
     309           0 :            ag_block_id_t const * root ) {
     310           0 :   ag_finality_tracker_init( self->finality_tracker, root->slot );
     311           0 :   self->parent_ready_tracker->root = fd_ulong_sat_sub( root->slot, AG_REWARD_SLOT_DELTA+AG_SLOTS_PER_WINDOW );
     312           0 : }
     313             : 
     314             : static void
     315             : pool_push_parent_readys( ag_pool_t * self,
     316           0 :                          ulong       ready_cnt ) {
     317           0 :   for( ulong i=0UL; i<ready_cnt; i++ ) {
     318           0 :     pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
     319           0 :   }
     320           0 : }
     321             : 
     322             : void
     323             : ag_pool_from_snapshot( ag_pool_t *           self,
     324           0 :                        ag_block_id_t const * root ) {
     325           0 :   pool_init( self, root );
     326           0 :   pool_push_parent_readys( self, ag_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, root, self->scratch.parent_readys ) );
     327           0 : }
     328             : 
     329             : void
     330             : ag_pool_from_genesis( ag_pool_t *           self,
     331           0 :                       ag_block_id_t const * genesis ) {
     332           0 :   pool_init( self, genesis );
     333           0 :   pool_push_parent_readys( self, ag_parent_ready_tracker_mark_root( self->parent_ready_tracker, genesis, self->scratch.parent_readys ) );
     334           0 : }
     335             : 
     336             : void
     337           0 : ag_pool_fini( ag_pool_t * self ) {
     338           0 :   ag_finality_tracker_fini( self->finality_tracker );
     339           0 :   self->parent_ready_tracker->root = ULONG_MAX;
     340           0 : }
     341             : 
     342             : FD_FN_PURE ag_pool_metrics_t
     343           0 : ag_pool_metrics( ag_pool_t const * self ) {
     344           0 :   return (ag_pool_metrics_t){
     345           0 :     .slot_state_pool_used = slot_state_pool_used( self->slot_states->pool ),
     346           0 :     .slot_state_pool_free = slot_state_pool_free( self->slot_states->pool ),
     347           0 :     .finalized_slot       = ag_pool_finalized_slot( self ),
     348           0 :     .pool_events_cnt      = pool_events_cnt( self->pool_events ),
     349           0 :     .bls_verify_cnt       = self->slot_states->shared.bls_verify_cnt
     350           0 :   };
     351           0 : }
     352             : 
     353             : static ag_slot_state_t *
     354             : slot_state( ag_pool_t * self,
     355           0 :             ulong       slot ) {
     356           0 :   slot_state_ele_t * ele = slot_state_map_ele_query( self->slot_states->map, &slot, NULL, self->slot_states->pool );
     357           0 :   if( FD_LIKELY( ele ) ) return &ele->slot_state;
     358             : 
     359           0 :   ag_slot_states_t *      states = &self->slot_states->shared;
     360           0 :   ag_epoch_info_t const * info   = fd_ptr_if  ( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if  ( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     361           0 :   ulong                   rank   = fd_ulong_if( slot>=states->next_epoch_slot, states->next_epoch_rank, fd_ulong_if( slot>=states->curr_epoch_slot, states->curr_epoch_rank, states->prev_epoch_rank ) );
     362             : 
     363           0 :   FD_TEST( info );
     364           0 :   FD_TEST( slot_state_pool_free( self->slot_states->pool ) );
     365             : 
     366           0 :   ele       = slot_state_pool_ele_acquire( self->slot_states->pool );
     367           0 :   ele->slot = slot;
     368           0 :   ag_slot_state_null( &ele->slot_state, states );
     369           0 :   ele->slot_state.slot     = slot;
     370           0 :   ele->slot_state.own_rank = rank;
     371           0 :   slot_state_map_ele_insert( self->slot_states->map, ele, self->slot_states->pool );
     372           0 :   return &ele->slot_state;
     373           0 : }
     374             : 
     375             : static inline ag_finalization_event_t
     376           0 : finalization_event_default( ag_pool_t * self ) {
     377           0 :   return ag_finalization_event_default( self->scratch.implicitly_finalized, self->scratch.implicitly_skipped );
     378           0 : }
     379             : 
     380             : static void
     381             : handle_finalization( ag_pool_t *                     self,
     382           0 :                      ag_finalization_event_t const * event ) {
     383           0 :   FD_TEST( pool_events_avail( self->pool_events )>=event->implicitly_finalized_cnt+event->implicitly_skipped_cnt );
     384           0 :   for( ulong i=0UL; i<event->implicitly_finalized_cnt; i++ ) pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_IMPLICITLY_FINALIZED, .implicitly_finalized = event->implicitly_finalized[i] } );
     385           0 :   for( ulong i=0UL; i<event->implicitly_skipped_cnt;   i++ ) pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_IMPLICITLY_SKIPPED,   .implicitly_skipped   = event->implicitly_skipped  [i] } );
     386           0 :   ulong first_unpruned_slot = ag_finality_tracker_first_unpruned_slot( self->finality_tracker );
     387           0 :   ulong retained_slot       = fd_ulong_sat_sub( first_unpruned_slot, AG_REWARD_SLOT_DELTA );
     388           0 :   for( ulong slot = fd_ulong_sat_sub( self->parent_ready_tracker->root, AG_REWARD_SLOT_DELTA ); slot<retained_slot; slot++ ) {
     389           0 :     slot_state_ele_t * ele = slot_state_map_ele_remove( self->slot_states->map, &slot, NULL, self->slot_states->pool );
     390           0 :     if( FD_LIKELY( ele ) ) {
     391           0 :       ag_slot_state_null( &ele->slot_state, &self->slot_states->shared );
     392           0 :       slot_state_pool_ele_release( self->slot_states->pool, ele );
     393           0 :     }
     394           0 :     s2n_waiting_parent_cert_ele_t * waiter;
     395           0 :     while( (waiter = s2n_waiting_parent_cert_map_ele_remove( self->s2n_waiting_parent_cert->map, &slot, NULL, self->s2n_waiting_parent_cert->pool )) ) s2n_waiting_parent_cert_pool_ele_release( self->s2n_waiting_parent_cert->pool, waiter );
     396           0 :   }
     397           0 :   ag_parent_ready_tracker_prune( self->parent_ready_tracker, fd_ulong_sat_sub( first_unpruned_slot, AG_REWARD_SLOT_DELTA+AG_SLOTS_PER_WINDOW ) );
     398           0 : }
     399             : 
     400             : static void
     401             : add_verified_cert( ag_pool_t *       self,
     402             :                    ag_cert_t const * cert,
     403           0 :                    fd_bls_set_t *    bad ) {
     404           0 :   ulong slot = ag_cert_slot( cert );
     405             : 
     406           0 :   ag_slot_state_add_cert( slot_state( self, slot ), cert );
     407             : 
     408           0 :   switch( cert->kind ) {
     409           0 :   case AG_CERT_KIND_FINAL: {
     410           0 :     ag_finalization_event_t finalization_event = finalization_event_default( self );
     411           0 :     ag_finality_tracker_mark_finalized( self->finality_tracker, slot, &finalization_event );
     412           0 :     handle_finalization( self, &finalization_event );
     413           0 :     break;
     414           0 :   }
     415             : 
     416           0 :   case AG_CERT_KIND_FAST_FINAL:
     417           0 :   case AG_CERT_KIND_NOTAR:
     418           0 :   case AG_CERT_KIND_NOTAR_FALLBACK: {
     419           0 :     uchar const * block_hash = ag_cert_block_hash( cert );
     420           0 :     ag_block_id_t block_id   = ag_block_id( slot, block_hash );
     421           0 :     if( FD_UNLIKELY( cert->kind==AG_CERT_KIND_FAST_FINAL ) ) {
     422           0 :       ag_finalization_event_t finalization_event = finalization_event_default( self );
     423           0 :       ag_finality_tracker_mark_fast_finalized( self->finality_tracker, &block_id, &finalization_event );
     424           0 :       handle_finalization( self, &finalization_event );
     425           0 :     }
     426           0 :     if( FD_LIKELY( cert->kind==AG_CERT_KIND_NOTAR ) ) {
     427           0 :       ag_finalization_event_t finalization_event = finalization_event_default( self );
     428           0 :       ag_finality_tracker_mark_notarized( self->finality_tracker, &block_id, &finalization_event );
     429           0 :       handle_finalization( self, &finalization_event );
     430           0 :     }
     431             : 
     432           0 :     ag_slot_states_t *              states = &self->slot_states->shared;
     433           0 :     s2n_waiting_parent_cert_ele_t * next;
     434           0 :     for( s2n_waiting_parent_cert_ele_t * child = s2n_waiting_parent_cert_map_ele_query( self->s2n_waiting_parent_cert->map, &slot, NULL, self->s2n_waiting_parent_cert->pool );
     435           0 :                                          child;
     436           0 :                                          child = next ) {
     437           0 :       next = (s2n_waiting_parent_cert_ele_t *)s2n_waiting_parent_cert_map_ele_next_const( child, NULL, self->s2n_waiting_parent_cert->pool );
     438           0 :       if( FD_LIKELY( !ag_block_id_eq( &child->parent, &block_id ) ) ) continue;
     439           0 :       ag_block_id_t child_id = child->child; /* copy before the release below */
     440           0 :       s2n_waiting_parent_cert_map_ele_remove_fast( self->s2n_waiting_parent_cert->map, child, self->s2n_waiting_parent_cert->pool );
     441           0 :       s2n_waiting_parent_cert_pool_ele_release( self->s2n_waiting_parent_cert->pool, child );
     442             : 
     443           0 :       ag_slot_state_t * child_state = slot_state( self, child_id.slot );
     444           0 :       fd_bls_set_t      bad_child[ fd_bls_set_word_cnt ];
     445           0 :       int output = ag_slot_state_notify_parent_certified( child_state, states, child_id.hash, bad_child );
     446           0 :       if( FD_LIKELY( fd_ptr_if( child_id.slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( child_id.slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) )==fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) ) ) ) fd_bls_set_union( bad, bad, bad_child );
     447           0 :       switch( output ) {
     448           0 :       case -1: repair_events_push( self->repair_events, child_id ); break;
     449           0 :       case  0: break;
     450           0 :       case  1: pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = child_id } ); break;
     451           0 :       }
     452           0 :     }
     453             : 
     454           0 :     ulong ready_cnt = ag_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, &block_id, self->scratch.parent_readys );
     455           0 :     for( ulong i=0UL; i<ready_cnt; i++ ) {
     456           0 :       pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
     457           0 :     }
     458             : 
     459           0 :     repair_events_push( self->repair_events, block_id );
     460           0 :     break;
     461           0 :   }
     462             : 
     463           0 :   case AG_CERT_KIND_SKIP: {
     464           0 :     ulong ready_cnt = ag_parent_ready_tracker_mark_skipped( self->parent_ready_tracker, slot, self->scratch.parent_readys );
     465           0 :     for( ulong i=0UL; i<ready_cnt; i++ ) {
     466           0 :       pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
     467           0 :     }
     468           0 :     break;
     469           0 :   }
     470             : 
     471           0 :   default:
     472           0 :     FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
     473           0 :   }
     474             : 
     475           0 :   ag_pool_event_t event = { .kind = AG_POOL_EVENT_CERT_CREATED, .cert_created = *cert };
     476           0 :   pool_events_push( self->pool_events, event );
     477           0 : }
     478             : 
     479             : void
     480             : ag_pool_advance_epoch( ag_pool_t *             self,
     481             :                        ag_epoch_info_t const * epoch_info,
     482             :                        ulong                   epoch_rank,
     483           0 :                        ulong                   epoch_slot ) {
     484           0 :   ag_slot_states_t * states = &self->slot_states->shared;
     485           0 :   if( FD_UNLIKELY( !states->curr_epoch_info ) ) {
     486           0 :     states->curr_epoch_info = epoch_info;
     487           0 :     states->curr_epoch_rank = epoch_rank;
     488           0 :     states->curr_epoch_slot = epoch_slot;
     489           0 :   } else if( FD_UNLIKELY( !states->next_epoch_info ) ) {
     490           0 :     states->next_epoch_info = epoch_info;
     491           0 :     states->next_epoch_rank = epoch_rank;
     492           0 :     states->next_epoch_slot = epoch_slot;
     493           0 :   } else {
     494           0 :     states->prev_epoch_info = states->curr_epoch_info;
     495           0 :     states->prev_epoch_slot = states->curr_epoch_slot;
     496           0 :     states->prev_epoch_rank = states->curr_epoch_rank;
     497           0 :     states->curr_epoch_info = states->next_epoch_info;
     498           0 :     states->curr_epoch_slot = states->next_epoch_slot;
     499           0 :     states->curr_epoch_rank = states->next_epoch_rank;
     500           0 :     states->next_epoch_info = epoch_info;
     501           0 :     states->next_epoch_rank = epoch_rank;
     502           0 :     states->next_epoch_slot = epoch_slot;
     503           0 :   }
     504           0 : }
     505             : 
     506             : void
     507             : ag_pool_set_rank( ag_pool_t * self,
     508             :                   ulong       epoch_slot,
     509           0 :                   ulong       epoch_rank ) {
     510           0 :   ag_slot_states_t * states = &self->slot_states->shared;
     511           0 :   if     ( epoch_slot==states->prev_epoch_slot ) states->prev_epoch_rank = epoch_rank;
     512           0 :   else if( epoch_slot==states->curr_epoch_slot ) states->curr_epoch_rank = epoch_rank;
     513           0 :   else if( epoch_slot==states->next_epoch_slot ) states->next_epoch_rank = epoch_rank;
     514           0 :   else FD_LOG_CRIT(( "no epoch starts at slot %lu", epoch_slot ));
     515             : 
     516           0 :   slot_state_map_t * map  = self->slot_states->map;
     517           0 :   slot_state_ele_t * pool = self->slot_states->pool;
     518           0 :   for( slot_state_map_iter_t iter = slot_state_map_iter_init( map, pool );
     519           0 :                                    !slot_state_map_iter_done( iter, map, pool );
     520           0 :                              iter = slot_state_map_iter_next( iter, map, pool ) ) {
     521           0 :     ag_slot_state_t * slot_state = &slot_state_map_iter_ele( iter, map, pool )->slot_state;
     522           0 :     ulong             slot       = slot_state->slot;
     523           0 :     ulong             rank       = fd_ulong_if( slot>=states->next_epoch_slot, states->next_epoch_rank, fd_ulong_if( slot>=states->curr_epoch_slot, states->curr_epoch_rank, states->prev_epoch_rank ) );
     524           0 :     if( FD_UNLIKELY( slot_state->own_rank!=rank ) ) ag_slot_state_set_own_rank( slot_state, rank );
     525           0 :   }
     526           0 : }
     527             : 
     528             : int
     529             : ag_pool_add_cert( ag_pool_t *       self,
     530             :                   ag_cert_t const * cert,
     531             :                   int               verified,
     532           0 :                   fd_bls_set_t *    bad ) {
     533           0 :   ulong slot = ag_cert_slot( cert );
     534           0 :   fd_bls_set_null( bad );
     535             : 
     536           0 :   ulong slot_far_in_future = ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - AG_REWARD_SLOT_DELTA;
     537           0 :   if( FD_UNLIKELY( slot<ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) ) ) return AG_POOL_ERR_SLOT_TOO_OLD;
     538           0 :   if( FD_UNLIKELY( slot>=slot_far_in_future                                            ) ) return AG_POOL_ERR_SLOT_TOO_NEW;
     539             : 
     540           0 :   ag_slot_states_t const * states     = &self->slot_states->shared;
     541           0 :   ag_epoch_info_t const *  epoch_info = fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     542           0 :   if( FD_UNLIKELY( !epoch_info ) ) return fd_int_if( slot<states->curr_epoch_slot, AG_POOL_ERR_SLOT_TOO_OLD, AG_POOL_ERR_SLOT_TOO_NEW );
     543             : 
     544           0 :   ag_slot_state_t * state = slot_state( self, slot );
     545           0 :   int duplicate = 0;
     546           0 :   switch( cert->kind ) {
     547           0 :   case AG_CERT_KIND_FINAL:          duplicate = state->certs.finalize.slot!=ULONG_MAX;                                break;
     548           0 :   case AG_CERT_KIND_FAST_FINAL:     duplicate = state->certs.fast_finalize.slot!=ULONG_MAX;                           break;
     549           0 :   case AG_CERT_KIND_NOTAR:          duplicate = state->certs.notar.slot!=ULONG_MAX;                                   break;
     550           0 :   case AG_CERT_KIND_NOTAR_FALLBACK: duplicate = ag_slot_state_is_notar_fallback( state, ag_cert_block_hash( cert ) ); break;
     551           0 :   case AG_CERT_KIND_SKIP:           duplicate = state->certs.skip.slot!=ULONG_MAX;                                    break;
     552           0 :   default:                          FD_LOG_CRIT(( "unreachable" ));
     553           0 :   }
     554           0 :   if( FD_UNLIKELY( duplicate ) ) return AG_POOL_ERR_DUPLICATE;
     555             : 
     556           0 :   int err = AG_CERT_VERIFY_SUCCESS;
     557           0 :   if( FD_LIKELY( !verified ) ) err = ag_cert_verify( cert, epoch_info );
     558           0 :   self->slot_states->shared.bls_verify_cnt += (ulong)( !verified && err!=AG_CERT_VERIFY_ERR_THRESHOLD );
     559           0 :   if( FD_UNLIKELY( err ) ) return AG_POOL_ERR_CERT_VERIFY;
     560             : 
     561           0 :   switch( cert->kind ) { /* a skip cert excludes finalization certs, Lemmas 23 and 28 */
     562           0 :   case AG_CERT_KIND_FINAL:
     563           0 :   case AG_CERT_KIND_FAST_FINAL:     FD_CHECK_CRIT( state->certs.skip.slot==ULONG_MAX, "consensus safety violation" );                                                   break;
     564           0 :   case AG_CERT_KIND_NOTAR:
     565           0 :   case AG_CERT_KIND_NOTAR_FALLBACK:                                                                                                                                     break;
     566           0 :   case AG_CERT_KIND_SKIP:           FD_CHECK_CRIT( state->certs.finalize.slot==ULONG_MAX && state->certs.fast_finalize.slot==ULONG_MAX, "consensus safety violation" ); break;
     567           0 :   default:                          FD_LOG_CRIT(( "unreachable" ));
     568           0 :   }
     569             : 
     570           0 :   add_verified_cert( self, cert, bad );
     571           0 :   return AG_POOL_SUCCESS;
     572           0 : }
     573             : 
     574             : int
     575             : ag_pool_add_vote( ag_pool_t *       self,
     576             :                   ag_vote_t const * vote,
     577             :                   int               verified,
     578             :                   fd_bls_set_t *    bad,
     579           0 :                   uchar *           quorum_reached ) {
     580           0 :   ulong slot = ag_vote_slot( vote );
     581           0 :   fd_bls_set_null( bad );
     582           0 :   *quorum_reached = 0;
     583             : 
     584           0 :   ulong first_unpruned_slot = ag_finality_tracker_first_unpruned_slot( self->finality_tracker );
     585           0 :   ulong retained_slot       = fd_ulong_sat_sub( first_unpruned_slot, AG_REWARD_SLOT_DELTA );
     586           0 :   ulong slot_far_in_future  = first_unpruned_slot + self->slot_max - AG_REWARD_SLOT_DELTA;
     587           0 :   if( FD_UNLIKELY( slot<retained_slot       ) ) return AG_POOL_ERR_SLOT_TOO_OLD;
     588           0 :   if( FD_UNLIKELY( slot>=slot_far_in_future ) ) return AG_POOL_ERR_SLOT_TOO_NEW;
     589           0 :   ag_slot_states_t *      states     = &self->slot_states->shared;
     590           0 :   ag_epoch_info_t const * epoch_info = fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     591           0 :   if( FD_UNLIKELY( !epoch_info ) ) return fd_int_if( slot<states->curr_epoch_slot, AG_POOL_ERR_SLOT_TOO_OLD, AG_POOL_ERR_SLOT_TOO_NEW );
     592             : 
     593           0 :   ulong             voter       = ag_vote_rank( vote );
     594           0 :   ag_slot_state_t * slot_state_ = slot_state( self, slot );
     595           0 :   ulong             voter_stake = ag_epoch_info_validator( epoch_info, voter )->stake;
     596             : 
     597           0 :   if       ( FD_UNLIKELY( ag_slot_state_check_slashable_offence( slot_state_, states, vote )!=AG_SLASHABLE_NONE ) ) {
     598           0 :     return AG_POOL_ERR_SLASHABLE;
     599           0 :   } else if( FD_UNLIKELY( ag_slot_state_should_ignore_vote( slot_state_, states, vote ) ) ) {
     600           0 :     return AG_POOL_ERR_DUPLICATE;
     601           0 :   }
     602           0 :   if( FD_UNLIKELY( !verified && !blst_p2_affine_in_g2( ag_vote_sig( vote ) ) ) ) {
     603           0 :     fd_bls_set_insert( bad, voter );
     604           0 :     return AG_POOL_SUCCESS;
     605           0 :   }
     606             : 
     607           0 :   ag_cert_t     cert_events  [ AG_SLOT_STATE_OUT_CERT_MAX   ]; ulong cert_event_cnt;
     608           0 :   ag_block_id_t safe_to_notar[ AG_SLOT_STATE_OUT_EVENT_MAX  ]; ulong safe_to_notar_cnt;
     609           0 :   ulong         safe_to_skip [ AG_SLOT_STATE_OUT_EVENT_MAX  ]; ulong safe_to_skip_cnt;
     610           0 :   ag_block_id_t repair_events[ AG_SLOT_STATE_OUT_REPAIR_MAX ]; ulong repair_event_cnt;
     611           0 :   ag_slot_state_add_vote( slot_state_, states, vote, voter_stake, cert_events, &cert_event_cnt, safe_to_notar, &safe_to_notar_cnt, safe_to_skip, &safe_to_skip_cnt, repair_events, &repair_event_cnt, bad );
     612             : 
     613           0 :   for( ulong i=0UL; i<cert_event_cnt;    i++ ) { add_verified_cert( self, &cert_events[i], bad ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, (int)cert_events[i].kind ); }
     614           0 :   for( ulong i=0UL; i<safe_to_notar_cnt; i++ ) { pool_events_push  ( self->pool_events,   (ag_pool_event_t){ .kind = AG_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = safe_to_notar[i] } ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, AG_POOL_QUORUM_REACHED_SAFE_TO_NOTAR ); }
     615           0 :   for( ulong i=0UL; i<safe_to_skip_cnt;  i++ ) { pool_events_push  ( self->pool_events,   (ag_pool_event_t){ .kind = AG_POOL_EVENT_SAFE_TO_SKIP,  .safe_to_skip  = safe_to_skip [i] } ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, AG_POOL_QUORUM_REACHED_SAFE_TO_SKIP  ); }
     616           0 :   for( ulong i=0UL; i<repair_event_cnt;  i++ ) { repair_events_push( self->repair_events, repair_events[i] ); }
     617           0 :   return AG_POOL_SUCCESS;
     618           0 : }
     619             : 
     620             : ag_slot_state_t const *
     621             : ag_pool_slot_state( ag_pool_t const * self,
     622           0 :                     ulong             slot ) {
     623           0 :   slot_state_ele_t const * ele = slot_state_map_ele_query_const( self->slot_states->map, &slot, NULL, self->slot_states->pool );
     624           0 :   return ele ? &ele->slot_state : NULL;
     625           0 : }
     626             : 
     627             : ag_slot_states_t const *
     628           0 : ag_pool_slot_states( ag_pool_t const * self ) {
     629           0 :   return &self->slot_states->shared;
     630           0 : }
     631             : 
     632             : int
     633             : ag_pool_add_block( ag_pool_t *           self,
     634             :                    ag_block_id_t const * block_id,
     635             :                    ag_block_id_t const * parent_id,
     636           0 :                    fd_bls_set_t *        bad ) {
     637           0 :   fd_bls_set_null( bad );
     638             : 
     639           0 :   ulong         slot        = block_id->slot;
     640           0 :   uchar const * block_hash  = block_id->hash;
     641           0 :   ulong         parent_slot = parent_id->slot;
     642           0 :   uchar const * parent_hash = parent_id->hash;
     643             : 
     644           0 :   ulong slot_far_in_future = ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - AG_REWARD_SLOT_DELTA;
     645           0 :   if( FD_UNLIKELY( slot<ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) ) ) return AG_POOL_ERR_SLOT_TOO_OLD;
     646           0 :   if( FD_UNLIKELY( slot>=slot_far_in_future                                            ) ) return AG_POOL_ERR_SLOT_TOO_NEW;
     647           0 :   ag_slot_states_t * states = &self->slot_states->shared;
     648           0 :   if( FD_UNLIKELY( !fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) ) ) ) return fd_int_if( slot<states->curr_epoch_slot, AG_POOL_ERR_SLOT_TOO_OLD, AG_POOL_ERR_SLOT_TOO_NEW );
     649             : 
     650           0 :   ag_finalization_event_t finalization_event = finalization_event_default( self );
     651           0 :   ag_finality_tracker_add_parent( self->finality_tracker, block_id, parent_id, &finalization_event );
     652           0 :   handle_finalization( self, &finalization_event );
     653           0 :   if( FD_UNLIKELY( slot<fd_ulong_sat_sub( ag_finality_tracker_first_unpruned_slot( self->finality_tracker ), AG_REWARD_SLOT_DELTA ) ) ) return AG_POOL_SUCCESS; /* pruned by the parent link */
     654             : 
     655           0 :   ag_slot_state_notify_parent_known( slot_state( self, slot ), block_hash );
     656           0 :   slot_state_ele_t * parent_state_ = slot_state_map_ele_query( self->slot_states->map, &parent_slot, NULL, self->slot_states->pool );
     657           0 :   ag_slot_state_t *  parent_state  = parent_state_ ? &parent_state_->slot_state : NULL;
     658           0 :   if( FD_LIKELY( parent_state && ag_slot_state_is_notar_fallback_or_stronger( parent_state, parent_hash ) ) ) {
     659           0 :     int output = ag_slot_state_notify_parent_certified( slot_state( self, slot ), states, block_hash, bad );
     660           0 :     switch( output ) {
     661           0 :     case -1: repair_events_push( self->repair_events, *block_id ); return AG_POOL_SUCCESS;
     662           0 :     case  0: return AG_POOL_SUCCESS;
     663           0 :     case  1: pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = *block_id } ); return AG_POOL_SUCCESS;
     664           0 :     }
     665           0 :   }
     666             : 
     667           0 :   if( FD_UNLIKELY( parent_slot<fd_ulong_sat_sub( ag_finality_tracker_first_unpruned_slot( self->finality_tracker ), AG_REWARD_SLOT_DELTA ) ) ) return AG_POOL_SUCCESS; /* the parent can no longer certify */
     668           0 :   for( s2n_waiting_parent_cert_ele_t const * ele = s2n_waiting_parent_cert_map_ele_query_const( self->s2n_waiting_parent_cert->map, &parent_slot, NULL, self->s2n_waiting_parent_cert->pool );
     669           0 :                                              ele;
     670           0 :                                              ele = s2n_waiting_parent_cert_map_ele_next_const( ele, NULL, self->s2n_waiting_parent_cert->pool ) ) {
     671           0 :     if( FD_UNLIKELY( ag_block_id_eq( &ele->child, block_id ) ) ) return AG_POOL_SUCCESS;
     672           0 :   }
     673           0 :   FD_TEST( s2n_waiting_parent_cert_pool_free( self->s2n_waiting_parent_cert->pool ) );
     674           0 :   s2n_waiting_parent_cert_ele_t * ele = s2n_waiting_parent_cert_pool_ele_acquire( self->s2n_waiting_parent_cert->pool );
     675           0 :   ele->parent = *parent_id;
     676           0 :   ele->child  = *block_id;
     677           0 :   s2n_waiting_parent_cert_map_ele_insert( self->s2n_waiting_parent_cert->map, ele, self->s2n_waiting_parent_cert->pool );
     678             : 
     679           0 :   return AG_POOL_SUCCESS;
     680           0 : }
     681             : 
     682             : FD_STATIC_ASSERT( AG_NOTAR_FALLBACK_CERT_MAX==4UL && AG_NOTAR_FALLBACK_VOTE_MAX==3UL, standstill_iter_idx );
     683             : 
     684             : ag_pool_standstill_iter_t *
     685             : ag_pool_standstill_iter_init( ag_pool_standstill_iter_t * iter,
     686           0 :                               ag_pool_t const *           self ) {
     687           0 :   iter->finalized = ag_pool_finalized_slot( self );
     688           0 :   iter->slot      = iter->finalized;
     689           0 :   iter->idx       = 0UL;
     690           0 :   return ag_pool_standstill_iter_next( iter, self );
     691           0 : }
     692             : 
     693             : ag_pool_standstill_iter_t *
     694             : ag_pool_standstill_iter_next( ag_pool_standstill_iter_t * iter,
     695           0 :                               ag_pool_t const *           self ) {
     696           0 :   ulong slot_far_in_future = ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - AG_REWARD_SLOT_DELTA;
     697           0 :   for( ; iter->slot<slot_far_in_future; iter->slot++, iter->idx = 0UL ) {
     698           0 :     slot_state_ele_t const * slot_state_ = slot_state_map_ele_query_const( self->slot_states->map, &iter->slot, NULL, self->slot_states->pool );
     699           0 :     if( FD_UNLIKELY( !slot_state_ ) ) continue;
     700             : 
     701           0 :     ag_slot_certs_t const * certs         = &slot_state_->slot_state.certs;
     702           0 :     ag_slot_votes_t const * votes         = &slot_state_->slot_state.votes;
     703           0 :     ulong                   slot          = slot_state_->slot_state.slot;
     704           0 :     ulong                   own_rank      = slot_state_->slot_state.own_rank;
     705           0 :     ushort                  shred_version = slot_state_->slot_state.shred_version;
     706           0 :     ulong                   idx           = iter->idx;
     707           0 :     int                     finalized     = slot==iter->finalized;
     708           0 :     int                     fast_final    = certs->fast_finalize.slot!=ULONG_MAX;
     709           0 :     int                     final         = certs->finalize.slot!=ULONG_MAX && ( !finalized || ( !fast_final && certs->notar.slot!=ULONG_MAX ) );
     710           0 :     int                     notar         = certs->notar.slot   !=ULONG_MAX && ( !finalized || final );
     711           0 :     ulong                   nf_cert       = fd_ulong_max( idx, 3UL  ) - 3UL;
     712           0 :     ulong                   nf_vote       = fd_ulong_max( idx, 11UL ) - 11UL;
     713             : 
     714           0 :     iter->is_vote = 0;
     715           0 :     if( FD_UNLIKELY( idx<1UL && final      ) ) { iter->idx = 1UL; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_FINAL,      .final      = certs->finalize      }; return iter; }
     716           0 :     if( FD_UNLIKELY( idx<2UL && fast_final ) ) { iter->idx = 2UL; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_FAST_FINAL, .fast_final = certs->fast_finalize }; return iter; }
     717           0 :     if( FD_LIKELY  ( idx<3UL && notar      ) ) { iter->idx = 3UL; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR,      .notar      = certs->notar         }; return iter; }
     718           0 :     if( FD_UNLIKELY( finalized ) ) continue;
     719           0 :     if( FD_UNLIKELY( nf_cert<certs->notar_fallback_cnt ) ) { iter->idx = 4UL+nf_cert; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = certs->notar_fallback[ nf_cert ] }; return iter; }
     720           0 :     if( FD_UNLIKELY( idx<8UL && certs->skip.slot!=ULONG_MAX ) ) { iter->idx = 8UL; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_SKIP, .skip = certs->skip }; return iter; }
     721             : 
     722           0 :     if( FD_UNLIKELY( own_rank==USHORT_MAX ) ) continue; /* unstaked */
     723           0 :     iter->is_vote = 1;
     724           0 :     if( FD_UNLIKELY( idx<9UL && fd_bls_set_test( votes->notar_set, own_rank ) ) ) {
     725           0 :       iter->idx  = 9UL;
     726           0 :       iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_NOTAR, .notar = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->notar_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } };
     727           0 :       memcpy( iter->vote.notar.block_hash, ag_vote_stake_pool_ele_const( self->slot_states->shared.notar_hash_stake_pool, votes->notar_hash_stake_idx[ own_rank ] )->block_id.hash, sizeof(ag_block_hash_t) );
     728           0 :       return iter;
     729           0 :     }
     730           0 :     if( FD_LIKELY  ( idx<10UL && fd_bls_set_test( votes->finalize_agg.set, own_rank ) ) ) { iter->idx = 10UL; iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_FINAL, .final = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->finalize_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
     731           0 :     if( FD_UNLIKELY( idx<11UL && fd_bls_set_test( votes->skip_agg.set,     own_rank ) ) ) { iter->idx = 11UL; iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_SKIP,  .skip  = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->skip_sig_idx    [ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
     732           0 :     if( FD_UNLIKELY( nf_vote<votes->notar_fallback_sig_cnt[ own_rank ] ) ) {
     733           0 :       iter->idx  = 12UL+nf_vote;
     734           0 :       iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_NOTAR_FALLBACK, .notar_fallback = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->notar_fallback_sig_idx[ own_rank ][ nf_vote ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } };
     735           0 :       memcpy( iter->vote.notar_fallback.block_hash, ag_vote_stake_pool_ele_const( self->slot_states->shared.notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ own_rank ][ nf_vote ] )->block_id.hash, sizeof(ag_block_hash_t) );
     736           0 :       return iter;
     737           0 :     }
     738           0 :     if( FD_UNLIKELY( idx<15UL && fd_bls_set_test( votes->skip_fallback_agg.set, own_rank ) ) ) { iter->idx = 15UL; iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_SKIP_FALLBACK, .skip_fallback = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->skip_fallback_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
     739           0 :   }
     740           0 :   iter->slot = ULONG_MAX;
     741           0 :   return iter;
     742           0 : }
     743             : 
     744             : FD_FN_PURE int
     745           0 : ag_pool_standstill_iter_done( ag_pool_standstill_iter_t const * iter ) {
     746           0 :   return iter->slot==ULONG_MAX;
     747           0 : }
     748             : 
     749             : FD_FN_PURE ulong
     750           0 : ag_pool_finalized_slot( ag_pool_t const * self ) {
     751           0 :   return ag_finality_tracker_highest_finalized_slot( self->finality_tracker );
     752           0 : }
     753             : 
     754             : FD_FN_PURE ulong
     755           0 : ag_pool_highest_parent_ready( ag_pool_t const * self ) {
     756           0 :   return self->parent_ready_tracker->highest_ready;
     757           0 : }
     758             : 
     759             : FD_FN_PURE uchar const *
     760           0 : ag_pool_finalized_block_hash( ag_pool_t const * self ) {
     761           0 :   return ag_finality_tracker_highest_finalized_block_hash( self->finality_tracker );
     762           0 : }
     763             : 
     764             : ulong
     765             : ag_pool_parents_ready( ag_pool_t const * self,
     766             :                        ulong             slot,
     767             :                        ag_block_id_t *   out,
     768           0 :                        ulong             out_max ) {
     769           0 :   return ag_parent_ready_tracker_parents_ready( self->parent_ready_tracker, slot, out, out_max );
     770           0 : }
     771             : 
     772             : ag_block_id_t
     773             : ag_pool_wait_for_parent_ready( ag_pool_t * self,
     774           0 :                                ulong       slot ) {
     775           0 :   return ag_parent_ready_tracker_wait_for_parent_ready( self->parent_ready_tracker, slot );
     776           0 : }
     777             : 
     778             : int
     779             : ag_pool_poll_pool_event( ag_pool_t *       self,
     780           0 :                          ag_pool_event_t * event ) {
     781           0 :   if( FD_LIKELY( pool_events_empty( self->pool_events ) ) ) return 0;
     782           0 :   *event = pool_events_pop( self->pool_events );
     783           0 :   if( FD_LIKELY( event->kind!=AG_POOL_EVENT_PARENT_READY ) ) return 1;
     784           0 :   ag_parent_ready_state_t * state = ag_parent_ready_state_map_ele_query( self->parent_ready_tracker->states.map, &event->parent_ready.slot, NULL, self->parent_ready_tracker->states.pool );
     785           0 :   if( FD_LIKELY( state ) ) state->ready = 0; /* NULL if pruned while queued */
     786           0 :   return 1;
     787           0 : }
     788             : 
     789             : int
     790             : ag_pool_poll_repair_event( ag_pool_t *     self,
     791           0 :                            ag_block_id_t * block ) {
     792           0 :   if( FD_LIKELY( repair_events_empty( self->repair_events ) ) ) return 0;
     793           0 :   *block = repair_events_pop( self->repair_events );
     794           0 :   return 1;
     795           0 : }

Generated by: LCOV version 1.14