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

          Line data    Source code
       1             : #include "ag_slot_state.h"
       2             : #include "ag_vote_serde.h"
       3             : 
       4           0 : #define AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR  (0)
       5           0 : #define AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK  (1)
       6           0 : #define AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES (2)
       7             : 
       8             : static int
       9             : block_hash_set_contains( ag_block_hash_set_t const * set,
      10           0 :                          ag_block_hash_t const       block_hash ) {
      11           0 :   for( ulong i=0UL; i<set->cnt; i++ ) {
      12           0 :     if( FD_LIKELY( !memcmp( set->hash[i], block_hash, sizeof(ag_block_hash_t) ) ) ) return 1;
      13           0 :   }
      14           0 :   return 0;
      15           0 : }
      16             : 
      17             : static void
      18             : block_hash_set_insert( ag_block_hash_set_t * set,
      19           0 :                        ag_block_hash_t const block_hash ) {
      20           0 :   if( FD_UNLIKELY( block_hash_set_contains( set, block_hash ) ) ) return;
      21           0 :   FD_TEST( set->cnt < AG_EQVOC_BLOCK_HASH_MAX );
      22           0 :   memcpy( set->hash[ set->cnt++ ], block_hash, sizeof(ag_block_hash_t) );
      23           0 : }
      24             : 
      25             : static void
      26             : block_hash_set_remove( ag_block_hash_set_t * set,
      27           0 :                        ag_block_hash_t const block_hash ) {
      28           0 :   for( ulong i=0UL; i<set->cnt; i++ ) {
      29           0 :     if( FD_LIKELY( !memcmp( set->hash[i], block_hash, sizeof(ag_block_hash_t) ) ) ) {
      30           0 :       set->cnt--;
      31           0 :       if( FD_UNLIKELY( i!=set->cnt ) ) memcpy( set->hash[i], set->hash[ set->cnt ], sizeof(ag_block_hash_t) );
      32           0 :       return;
      33           0 :     }
      34           0 :   }
      35           0 : }
      36             : 
      37             : static int
      38             : verify_subtract_votes( ag_slot_state_t *     self,
      39             :                        ag_slot_states_t *    states,
      40             :                        uint                  kind,
      41             :                        ag_block_hash_t const block_hash,
      42             :                        fd_bls_agg_t *        agg,
      43           0 :                        fd_bls_set_t *        bad ) {
      44           0 :   ag_epoch_info_t const * epoch_info                     = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
      45           0 :   ag_slot_votes_t *       votes                          = &self->votes;
      46           0 :   ag_vote_stake_map_t *   notar_hash_stake_map           = states->notar_hash_stake_map;
      47           0 :   ag_vote_stake_map_t *   notar_fallback_hash_stake_map  = states->notar_fallback_hash_stake_map;
      48           0 :   ag_vote_stake_ele_t *   notar_hash_stake_pool          = states->notar_hash_stake_pool;
      49           0 :   ag_vote_stake_ele_t *   notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
      50             : 
      51           0 :   fd_bls_sig_t sigs[ AG_VAT_MAX ];
      52           0 :   fd_bls_set_t set [ fd_bls_set_word_cnt ];
      53           0 :   switch( kind ) {
      54           0 :   case AG_VOTE_KIND_SKIP:
      55           0 :   case AG_VOTE_KIND_SKIP_FALLBACK:
      56           0 :   case AG_VOTE_KIND_FINAL:
      57           0 :     ag_slot_state_sigs( self, states, kind, NULL, agg->set, sigs );
      58           0 :     break;
      59           0 :   case AG_VOTE_KIND_NOTAR:
      60           0 :     fd_bls_set_null( set );
      61           0 :     for( ulong rank = fd_bls_set_const_iter_init( votes->notar_set );
      62           0 :                      !fd_bls_set_const_iter_done( rank );
      63           0 :                rank = fd_bls_set_const_iter_next( votes->notar_set, rank ) ) {
      64           0 :       if( FD_LIKELY( !memcmp( ag_vote_stake_pool_ele( notar_hash_stake_pool, votes->notar_hash_stake_idx[ rank ] )->block_id.hash, block_hash, sizeof(ag_block_hash_t) ) ) ) fd_bls_set_insert( set, rank );
      65           0 :     }
      66           0 :     ag_slot_state_sigs( self, states, kind, block_hash, set, sigs );
      67           0 :     fd_bls_agg_null( agg );
      68           0 :     fd_bls_agg_construct( agg, epoch_info->pubkeys, sigs, set );
      69           0 :     break;
      70           0 :   case AG_VOTE_KIND_NOTAR_FALLBACK:
      71           0 :     fd_bls_set_null( set );
      72           0 :     for( ulong rank=0UL; rank<epoch_info->validator_cnt; rank++ ) {
      73           0 :       for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
      74           0 :         if( FD_LIKELY( !memcmp( ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ rank ][ j ] )->block_id.hash, block_hash, sizeof(ag_block_hash_t) ) ) ) fd_bls_set_insert( set, rank );
      75           0 :       }
      76           0 :     }
      77           0 :     ag_slot_state_sigs( self, states, kind, block_hash, set, sigs );
      78           0 :     fd_bls_agg_null( agg );
      79           0 :     fd_bls_agg_construct( agg, epoch_info->pubkeys, sigs, set );
      80           0 :     break;
      81           0 :   default: FD_LOG_CRIT(( "unreachable" ));
      82           0 :   }
      83             : 
      84           0 :   uchar msg[ AG_VOTE_SIGNING_SER_MAX ];
      85           0 :   ulong msg_sz = ag_vote_signing_ser( kind, self->slot, block_hash, self->shred_version, msg );
      86           0 :   states->bls_verify_cnt++;
      87           0 :   int   err    = fd_bls_agg_verify_subtract( agg, msg, msg_sz, epoch_info->pubkeys, sigs, bad );
      88           0 :   if( FD_LIKELY( !err && fd_bls_set_is_null( bad ) ) ) return 1;
      89             : 
      90           0 :   ag_block_id_t key;
      91           0 :   ulong         id = ULONG_MAX;
      92           0 :   switch( kind ) {
      93           0 :   case AG_VOTE_KIND_NOTAR:          key = ag_block_id( self->slot, block_hash ); id = ag_vote_stake_map_idx_query_const( notar_hash_stake_map,          &key, ULONG_MAX, notar_hash_stake_pool          ); break;
      94           0 :   case AG_VOTE_KIND_NOTAR_FALLBACK: key = ag_block_id( self->slot, block_hash ); id = ag_vote_stake_map_idx_query_const( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool ); break;
      95           0 :   default: break;
      96           0 :   }
      97             : 
      98           0 :   for( ulong rank = fd_bls_set_const_iter_init( bad );
      99           0 :                    !fd_bls_set_const_iter_done( rank );
     100           0 :              rank = fd_bls_set_const_iter_next( bad, rank ) ) {
     101           0 :     ulong stake = ag_epoch_info_validator( epoch_info, rank )->stake;
     102           0 :     switch( kind ) {
     103           0 :     case AG_VOTE_KIND_NOTAR:
     104           0 :       ag_vote_stake_pool_ele( notar_hash_stake_pool, id )->stake -= stake;
     105           0 :       votes->notar_or_skip_stake -= stake;
     106           0 :       fd_bls_set_remove( votes->notar_set, rank );
     107           0 :       fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_sig_idx[ rank ] );
     108           0 :       break;
     109           0 :     case AG_VOTE_KIND_NOTAR_FALLBACK: {
     110           0 :       ulong j;
     111           0 :       for( j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
     112           0 :         if( FD_UNLIKELY( votes->notar_fallback_hash_stake_idx[ rank ][ j ]==id ) ) break;
     113           0 :       }
     114           0 :       FD_CHECK_CRIT( j<votes->notar_fallback_sig_cnt[ rank ], "invariant violation" );
     115           0 :       ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, id )->stake -= stake;
     116           0 :       fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_fallback_sig_idx[ rank ][ j ] );
     117           0 :       votes->notar_fallback_sig_cnt[ rank ]--;
     118           0 :       votes->notar_fallback_sig_idx       [ rank ][ j ] = votes->notar_fallback_sig_idx       [ rank ][ votes->notar_fallback_sig_cnt[ rank ] ];
     119           0 :       votes->notar_fallback_hash_stake_idx[ rank ][ j ] = votes->notar_fallback_hash_stake_idx[ rank ][ votes->notar_fallback_sig_cnt[ rank ] ];
     120           0 :       break;
     121           0 :     }
     122           0 :     case AG_VOTE_KIND_SKIP:
     123           0 :       votes->skip_stake -= stake;
     124           0 :       votes->notar_or_skip_stake -= stake;
     125           0 :       fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_sig_idx[ rank ] );
     126           0 :       break;
     127           0 :     case AG_VOTE_KIND_SKIP_FALLBACK:
     128           0 :       votes->skip_fallback_stake -= stake;
     129           0 :       fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_fallback_sig_idx[ rank ] );
     130           0 :       break;
     131           0 :     case AG_VOTE_KIND_FINAL:
     132           0 :       votes->finalize_stake -= stake;
     133           0 :       fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->finalize_sig_idx[ rank ] );
     134           0 :       break;
     135           0 :     default:
     136           0 :       FD_LOG_CRIT(( "unreachable" ));
     137           0 :     }
     138           0 :   }
     139             : 
     140           0 :   if( FD_UNLIKELY( kind==AG_VOTE_KIND_NOTAR && fd_bls_set_is_null( agg->set ) ) ) {
     141           0 :     ulong * link = &votes->notar_hash_stake_head;
     142           0 :     ulong   j;
     143           0 :     for( j=0UL; j<votes->notar_hash_stake_cnt; j++ ) {
     144           0 :       if( FD_UNLIKELY( *link==id ) ) break;
     145           0 :       link = &ag_vote_stake_pool_ele( notar_hash_stake_pool, *link )->prev;
     146           0 :     }
     147           0 :     FD_CHECK_CRIT( j<votes->notar_hash_stake_cnt, "invariant violation" );
     148           0 :     *link = ag_vote_stake_pool_ele( notar_hash_stake_pool, id )->prev;
     149           0 :     votes->notar_hash_stake_cnt--;
     150           0 :     ag_vote_stake_map_idx_remove( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     151           0 :     ag_vote_stake_pool_idx_release( notar_hash_stake_pool, id );
     152           0 :   }
     153           0 :   if( FD_UNLIKELY( kind==AG_VOTE_KIND_NOTAR_FALLBACK && fd_bls_set_is_null( agg->set ) ) ) {
     154           0 :     ulong * link = &votes->notar_fallback_hash_stake_head;
     155           0 :     ulong   j;
     156           0 :     for( j=0UL; j<votes->notar_fallback_hash_stake_cnt; j++ ) {
     157           0 :       if( FD_UNLIKELY( *link==id ) ) break;
     158           0 :       link = &ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, *link )->prev;
     159           0 :     }
     160           0 :     FD_CHECK_CRIT( j<votes->notar_fallback_hash_stake_cnt, "invariant violation" );
     161           0 :     *link = ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, id )->prev;
     162           0 :     votes->notar_fallback_hash_stake_cnt--;
     163           0 :     ag_vote_stake_map_idx_remove( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool );
     164           0 :     ag_vote_stake_pool_idx_release( notar_fallback_hash_stake_pool, id );
     165           0 :   }
     166           0 :   if( FD_UNLIKELY( kind==AG_VOTE_KIND_NOTAR ) ) {
     167           0 :     votes->top_notar_stake = 0UL;
     168           0 :     ulong i = votes->notar_hash_stake_head;
     169           0 :     for( ulong j=0UL; j<votes->notar_hash_stake_cnt; j++ ) {
     170           0 :       if( FD_LIKELY( ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->stake>votes->top_notar_stake ) ) { /* FIXME slow */
     171           0 :         votes->top_notar_stake = ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->stake;
     172           0 :         memcpy( votes->top_notar_hash, ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->block_id.hash, sizeof(ag_block_hash_t) );
     173           0 :       }
     174           0 :       i = ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->prev;
     175           0 :     }
     176           0 :   }
     177           0 :   return !err;
     178           0 : }
     179             : 
     180             : static int
     181             : check_safe_to_notar( ag_slot_state_t *     self,
     182             :                      ag_slot_states_t *    states,
     183             :                      ag_block_hash_t const block_hash,
     184           0 :                      fd_bls_set_t *        bad ) {
     185           0 :   ag_epoch_info_t const * epoch_info            = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     186           0 :   ag_slot_votes_t *       votes                 = &self->votes;
     187           0 :   ag_vote_stake_map_t *   notar_hash_stake_map  = states->notar_hash_stake_map;
     188           0 :   ag_vote_stake_ele_t *   notar_hash_stake_pool = states->notar_hash_stake_pool;
     189           0 :   ag_block_id_t           key                   = ag_block_id( self->slot, block_hash );
     190           0 :   ulong                   notar                 = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     191           0 :   ulong                   notar_stake           = notar!=ULONG_MAX ? ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
     192           0 :   ulong                   skip_stake            = votes->skip_stake;
     193             : 
     194           0 :   if( FD_UNLIKELY( !ag_epoch_info_is_weakest_quorum( epoch_info, notar_stake ) ) ) {
     195           0 :     return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     196           0 :   }
     197           0 :   if( FD_UNLIKELY( !ag_epoch_info_is_weak_quorum( epoch_info, notar_stake ) && !ag_epoch_info_is_quorum( epoch_info, notar_stake + skip_stake ) ) ) {
     198           0 :     block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
     199           0 :     return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     200           0 :   }
     201             : 
     202           0 :   ag_parent_status_t const * parent = NULL;
     203           0 :   for( ulong i=0UL; i<self->parents_cnt; i++ ) {
     204           0 :     if( FD_LIKELY( !memcmp( self->parents[i].hash, block_hash, sizeof(ag_block_hash_t) ) ) ) { parent = &self->parents[i]; break; }
     205           0 :   }
     206           0 :   if( FD_UNLIKELY( !parent                                  ) ) return AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK;
     207           0 :   if( FD_UNLIKELY( parent->kind!=AG_PARENT_STATUS_CERTIFIED ) ) return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     208             : 
     209           0 :   ulong own = self->own_rank;
     210             : 
     211           0 :   int safe_to_notar = 0;
     212           0 :   if( FD_LIKELY( own!=USHORT_MAX ) ) { /* must be staked */
     213           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, own ) ) ) safe_to_notar = 1;
     214           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, own ) ) ) {
     215           0 :       if( FD_UNLIKELY( !memcmp( ag_vote_stake_pool_ele( notar_hash_stake_pool, votes->notar_hash_stake_idx[ own ] )->block_id.hash, block_hash, sizeof(ag_block_hash_t) ) ) ) return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     216           0 :       safe_to_notar = 1;
     217           0 :     }
     218           0 :   }
     219           0 :   if( FD_UNLIKELY( !safe_to_notar ) ) {
     220           0 :     block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
     221           0 :     return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     222           0 :   }
     223             : 
     224           0 :   fd_bls_agg_t notar_agg[1];
     225           0 :   fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
     226           0 :   verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
     227           0 :   fd_bls_set_union( bad, bad, bad_notar );
     228           0 :   if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
     229           0 :     fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
     230           0 :     verify_subtract_votes( self, states, AG_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
     231           0 :     fd_bls_set_union( bad, bad, bad_skip );
     232           0 :   }
     233           0 :   notar       = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     234           0 :   notar_stake = notar!=ULONG_MAX ? ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
     235           0 :   skip_stake  = votes->skip_stake;
     236           0 :   if( FD_UNLIKELY( !ag_epoch_info_is_weakest_quorum( epoch_info, notar_stake ) || ( !ag_epoch_info_is_weak_quorum( epoch_info, notar_stake ) && !ag_epoch_info_is_quorum( epoch_info, notar_stake + skip_stake ) ) ) ) {
     237           0 :     block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
     238           0 :     return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     239           0 :   }
     240             : 
     241           0 :   block_hash_set_remove( &self->pending_safe_to_notar, block_hash );
     242           0 :   block_hash_set_insert( &self->sent_safe_to_notar,    block_hash );
     243           0 :   return AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR;
     244           0 : }
     245             : 
     246             : static int
     247             : check_safe_to_skip( ag_slot_state_t *  self,
     248             :                     ag_slot_states_t * states,
     249           0 :                     fd_bls_set_t *     bad ) {
     250           0 :   ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     251           0 :   ag_slot_votes_t *       votes      = &self->votes;
     252           0 :   if( FD_LIKELY( self->sent_safe_to_skip
     253           0 :                  || !ag_epoch_info_is_weak_quorum( epoch_info, votes->notar_or_skip_stake - votes->top_notar_stake )
     254           0 :                  || self->own_rank==USHORT_MAX /* must be staked */ ) ) return 0;
     255           0 :   if( FD_LIKELY( !fd_bls_set_test( votes->notar_set, self->own_rank ) ) ) return 0;
     256             : 
     257           0 :   ag_vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
     258           0 :   ulong                 i                     = votes->notar_hash_stake_head;
     259           0 :   for( ulong j=votes->notar_hash_stake_cnt; j>0UL; j-- ) {
     260           0 :     ag_block_id_t key  = ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->block_id;
     261           0 :     ulong         prev = ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->prev;
     262           0 :     fd_bls_agg_t  notar_agg[1];
     263           0 :     fd_bls_set_t  bad_notar[ fd_bls_set_word_cnt ];
     264           0 :     verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR, key.hash, notar_agg, bad_notar );
     265           0 :     fd_bls_set_union( bad, bad, bad_notar );
     266           0 :     i = prev;
     267           0 :   }
     268           0 :   if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
     269           0 :     fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
     270           0 :     verify_subtract_votes( self, states, AG_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
     271           0 :     fd_bls_set_union( bad, bad, bad_skip );
     272           0 :   }
     273           0 :   if( FD_UNLIKELY( !ag_epoch_info_is_weak_quorum( epoch_info, votes->notar_or_skip_stake - votes->top_notar_stake ) || !fd_bls_set_test( votes->notar_set, self->own_rank ) ) ) return 0;
     274           0 :   self->sent_safe_to_skip = 1;
     275           0 :   return 1;
     276           0 : }
     277             : 
     278             : static int
     279             : count_notar_stake( ag_slot_state_t *       self,
     280             :                    ag_slot_states_t *      states,
     281             :                    ag_vote_notar_t const * vote,
     282             :                    ulong                   stake,
     283             :                    ag_cert_t *             out_cert_events,
     284             :                    ulong *                 out_cert_event_cnt,
     285             :                    ag_block_id_t *         out_safe_to_notar,
     286             :                    ulong *                 out_safe_to_notar_cnt,
     287             :                    ulong *                 out_safe_to_skip,
     288             :                    ulong *                 out_safe_to_skip_cnt,
     289             :                    ag_block_id_t *         out_repair_events,
     290             :                    ulong *                 out_repair_event_cnt,
     291           0 :                    fd_bls_set_t *          bad ) {
     292           0 :   ag_epoch_info_t const * epoch_info    = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     293           0 :   ulong                   slot          = vote->slot;
     294           0 :   uchar const *           block_hash    = vote->block_hash;
     295           0 :   ag_block_id_t           key           = ag_block_id( self->slot, block_hash );
     296           0 :   ulong                   rank          = vote->rank;
     297           0 :   ushort                  shred_version = vote->shred_version;
     298             : 
     299           0 :   ag_slot_votes_t *     votes                          = &self->votes;
     300           0 :   ag_vote_stake_map_t * notar_hash_stake_map           = states->notar_hash_stake_map;
     301           0 :   ag_vote_stake_map_t * notar_fallback_hash_stake_map  = states->notar_fallback_hash_stake_map;
     302           0 :   ag_vote_stake_ele_t * notar_hash_stake_pool          = states->notar_hash_stake_pool;
     303           0 :   ag_vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
     304           0 :   ulong                 notar                          = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     305           0 :   if( FD_UNLIKELY( notar==ULONG_MAX ) ) {
     306           0 :     FD_TEST( ag_vote_stake_pool_free( notar_hash_stake_pool ) );
     307           0 :     notar = ag_vote_stake_pool_idx_acquire( notar_hash_stake_pool );
     308           0 :     ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->block_id = key;
     309           0 :     ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake    = 0UL;
     310           0 :     ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->prev     = votes->notar_hash_stake_cnt ? votes->notar_hash_stake_head : ag_vote_stake_pool_idx_null( notar_hash_stake_pool );
     311           0 :     ag_vote_stake_map_idx_insert( notar_hash_stake_map, notar, notar_hash_stake_pool );
     312           0 :     votes->notar_hash_stake_head = notar;
     313           0 :     votes->notar_hash_stake_cnt++;
     314           0 :   }
     315           0 :   votes->notar_hash_stake_idx[ rank ] = (uint)notar;
     316           0 :   ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake += stake;
     317             : 
     318           0 :   ulong notar_stake           = ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     319           0 :   votes->notar_or_skip_stake += stake;
     320           0 :   if( FD_LIKELY( notar_stake>votes->top_notar_stake ) ) {
     321           0 :     votes->top_notar_stake = notar_stake;
     322           0 :     memcpy( votes->top_notar_hash, block_hash, sizeof(ag_block_hash_t) );
     323           0 :   }
     324             : 
     325           0 :   if( FD_UNLIKELY( !block_hash_set_contains( &self->sent_safe_to_notar, block_hash ) ) ) {
     326           0 :     switch( check_safe_to_notar( self, states, block_hash, bad ) ) {
     327           0 :     case AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:
     328           0 :       out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = ag_block_id( slot, block_hash );
     329           0 :       break;
     330           0 :     case AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
     331           0 :       ulong j; for( j=0UL; j<*out_repair_event_cnt; j++ ) if( FD_UNLIKELY( !memcmp( out_repair_events[j].hash, block_hash, sizeof(ag_block_hash_t) ) ) ) break;
     332           0 :       if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = ag_block_id( slot, block_hash );
     333           0 :       break;
     334           0 :     }
     335           0 :     default: break;
     336           0 :     }
     337           0 :   }
     338           0 :   if( FD_UNLIKELY( check_safe_to_skip( self, states, bad ) ) ) {
     339           0 :     out_safe_to_skip[ (*out_safe_to_skip_cnt)++ ] = slot;
     340           0 :   }
     341           0 :   if( FD_UNLIKELY( !fd_bls_set_is_null( bad ) ) ) {
     342           0 :     notar = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     343           0 :     if( FD_UNLIKELY( notar==ULONG_MAX || !fd_bls_set_test( votes->notar_set, rank ) ) ) return 0;
     344           0 :     notar_stake = ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     345           0 :   }
     346             : 
     347           0 :   ulong nf       = ag_vote_stake_map_idx_query_const( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool );
     348           0 :   ulong nf_stake = nf!=ULONG_MAX ? ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake : 0UL;
     349             : 
     350           0 :   int          notar_verified          = 0;
     351           0 :   int          notar_fallback_verified = 0;
     352           0 :   fd_bls_agg_t notar_agg[1];
     353           0 :   fd_bls_agg_t nf_agg[1];
     354             : 
     355           0 :   if( FD_UNLIKELY( ( ag_epoch_info_is_quorum       ( epoch_info, nf_stake + notar_stake ) && !ag_slot_state_is_notar_fallback( self, block_hash ) ) ||
     356           0 :                    ( ag_epoch_info_is_quorum       ( epoch_info, notar_stake            ) && self->certs.notar.slot==ULONG_MAX                    ) ||
     357           0 :                    ( ag_epoch_info_is_strong_quorum( epoch_info, notar_stake            ) && self->certs.fast_finalize.slot==ULONG_MAX            ) ) ) {
     358           0 :     fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
     359           0 :     notar_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
     360           0 :     fd_bls_set_union( bad, bad, bad_notar );
     361           0 :     if( FD_UNLIKELY( fd_bls_set_test( bad_notar, rank ) ) ) return 0;
     362             : 
     363           0 :     notar_stake = ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     364           0 :     if( FD_LIKELY( nf!=ULONG_MAX && !ag_slot_state_is_notar_fallback( self, block_hash ) ) ) {
     365           0 :       fd_bls_set_t bad_nf[ fd_bls_set_word_cnt ];
     366           0 :       notar_fallback_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR_FALLBACK, block_hash, nf_agg, bad_nf );
     367           0 :       fd_bls_set_union( bad, bad, bad_nf );
     368           0 :     }
     369           0 :   }
     370             : 
     371           0 :   if( FD_UNLIKELY( !ag_slot_state_is_notar_fallback( self, block_hash ) ) ) {
     372           0 :     ag_cert_notar_fallback_t cert = { .slot = slot, .shred_version = shred_version }; memcpy( cert.block_hash, block_hash, sizeof(ag_block_hash_t) );
     373             : 
     374           0 :     if( FD_LIKELY( notar_verified ) ) {
     375           0 :       cert.agg_notar = *notar_agg;
     376           0 :       cert.stake += ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     377           0 :     }
     378           0 :     if( FD_LIKELY( notar_fallback_verified ) ) {
     379           0 :       cert.agg_notar_fallback = *nf_agg;
     380           0 :       cert.stake += ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake;
     381           0 :     }
     382           0 :     if( FD_LIKELY( ag_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
     383           0 :       out_cert_events[( *out_cert_event_cnt )++] = ( ag_cert_t ){ .kind = AG_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = cert };
     384           0 :     }
     385           0 :   }
     386             : 
     387           0 :   if( FD_UNLIKELY( notar_verified && ag_epoch_info_is_quorum( epoch_info, notar_stake ) && self->certs.notar.slot==ULONG_MAX ) ) {
     388           0 :     ag_cert_notar_t cert = { .slot = slot, .shred_version = shred_version, .stake = notar_stake, .agg = *notar_agg }; memcpy( cert.block_hash, block_hash, sizeof(ag_block_hash_t) );
     389           0 :     out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR, .notar = cert };
     390           0 :   }
     391             : 
     392           0 :   if( FD_UNLIKELY( notar_verified && ag_epoch_info_is_strong_quorum( epoch_info, notar_stake ) && self->certs.fast_finalize.slot==ULONG_MAX ) ) {
     393           0 :     ag_cert_fast_final_t cert = { .slot = slot, .shred_version = shred_version, .stake = notar_stake, .agg = *notar_agg }; memcpy( cert.block_hash, block_hash, sizeof(ag_block_hash_t) );
     394           0 :     out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_FAST_FINAL, .fast_final = cert };
     395           0 :   }
     396             : 
     397           0 :   return 1;
     398           0 : }
     399             : 
     400             : static int
     401             : count_notar_fallback_stake( ag_slot_state_t *                self,
     402             :                             ag_slot_states_t *               states,
     403             :                             ag_vote_notar_fallback_t const * vote,
     404             :                             ulong                            stake,
     405             :                             ag_cert_t *                      out_cert_events,
     406             :                             ulong *                          out_cert_event_cnt,
     407           0 :                             fd_bls_set_t *                   bad ) {
     408           0 :   ag_epoch_info_t const * epoch_info    = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     409           0 :   ulong                   slot          = vote->slot;
     410           0 :   uchar const *           block_hash    = vote->block_hash;
     411           0 :   ag_block_id_t           key           = ag_block_id( self->slot, block_hash );
     412           0 :   ulong                   rank          = vote->rank;
     413           0 :   ushort                  shred_version = vote->shred_version;
     414             : 
     415           0 :   ag_slot_votes_t *     votes                          = &self->votes;
     416           0 :   ag_vote_stake_map_t * notar_hash_stake_map           = states->notar_hash_stake_map;
     417           0 :   ag_vote_stake_map_t * notar_fallback_hash_stake_map  = states->notar_fallback_hash_stake_map;
     418           0 :   ag_vote_stake_ele_t * notar_hash_stake_pool          = states->notar_hash_stake_pool;
     419           0 :   ag_vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
     420           0 :   ulong                 nf                             = ag_vote_stake_map_idx_query_const( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool );
     421           0 :   if( FD_UNLIKELY( nf==ULONG_MAX ) ) {
     422           0 :     FD_TEST( ag_vote_stake_pool_free( notar_fallback_hash_stake_pool ) );
     423           0 :     nf = ag_vote_stake_pool_idx_acquire( notar_fallback_hash_stake_pool );
     424           0 :     ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->block_id = key;
     425           0 :     ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake    = 0UL;
     426           0 :     ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->prev     = votes->notar_fallback_hash_stake_cnt ? votes->notar_fallback_hash_stake_head : ag_vote_stake_pool_idx_null( notar_fallback_hash_stake_pool );
     427           0 :     ag_vote_stake_map_idx_insert( notar_fallback_hash_stake_map, nf, notar_fallback_hash_stake_pool );
     428           0 :     votes->notar_fallback_hash_stake_head = nf;
     429           0 :     votes->notar_fallback_hash_stake_cnt++;
     430           0 :   }
     431           0 :   votes->notar_fallback_hash_stake_idx[ rank ][ votes->notar_fallback_sig_cnt[ rank ]-1UL ] = (uint)nf;
     432           0 :   ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake += stake;
     433             : 
     434           0 :   ulong notar       = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     435           0 :   ulong nf_stake    = ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake;
     436           0 :   ulong notar_stake = notar!=ULONG_MAX ? ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
     437           0 :   if( FD_UNLIKELY( ag_epoch_info_is_quorum( epoch_info, nf_stake + notar_stake ) && !ag_slot_state_is_notar_fallback( self, block_hash ) ) ) {
     438           0 :     fd_bls_agg_t nf_agg[1];
     439           0 :     fd_bls_agg_t notar_agg[1];
     440           0 :     fd_bls_set_t bad_nf[ fd_bls_set_word_cnt ];
     441           0 :     int nf_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR_FALLBACK, block_hash, nf_agg, bad_nf );
     442           0 :     fd_bls_set_union( bad, bad, bad_nf );
     443           0 :     if( FD_UNLIKELY( fd_bls_set_test( bad_nf, rank ) ) ) return 0;
     444           0 :     int notar_verified = 0;
     445           0 :     if( FD_LIKELY( notar!=ULONG_MAX ) ) {
     446           0 :       fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
     447           0 :       notar_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
     448           0 :       fd_bls_set_union( bad, bad, bad_notar );
     449           0 :     }
     450             : 
     451           0 :     ag_cert_notar_fallback_t cert = { .slot = slot, .shred_version = shred_version }; memcpy( cert.block_hash, block_hash, sizeof(ag_block_hash_t) );
     452             : 
     453           0 :     if( FD_LIKELY( notar_verified ) ) {
     454           0 :       cert.agg_notar = *notar_agg;
     455           0 :       cert.stake += ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     456           0 :     }
     457           0 :     if( FD_LIKELY( nf_verified ) ) {
     458           0 :       cert.agg_notar_fallback = *nf_agg;
     459           0 :       cert.stake += ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake;
     460           0 :     }
     461           0 :     if( FD_LIKELY( ag_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
     462           0 :       out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = cert };
     463           0 :     }
     464           0 :   }
     465             : 
     466           0 :   return 1;
     467           0 : }
     468             : 
     469             : static int
     470             : count_skip_stake( ag_slot_state_t *  self,
     471             :                   ag_slot_states_t * states,
     472             :                   ag_vote_t const *  vote,
     473             :                   ulong              stake,
     474             :                   ag_cert_t *        out_cert_events,
     475             :                   ulong *            out_cert_event_cnt,
     476             :                   ag_block_id_t *    out_safe_to_notar,
     477             :                   ulong *            out_safe_to_notar_cnt,
     478             :                   ulong *            out_safe_to_skip,
     479             :                   ulong *            out_safe_to_skip_cnt,
     480             :                   ag_block_id_t *    out_repair_events,
     481             :                   ulong *            out_repair_event_cnt,
     482           0 :                   fd_bls_set_t *     bad ) {
     483           0 :   ag_epoch_info_t const * epoch_info    = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     484           0 :   int                     fallback      = vote->kind==AG_VOTE_KIND_SKIP_FALLBACK;
     485           0 :   ulong                   slot          = ag_vote_slot( vote );
     486           0 :   ulong                   rank          = ag_vote_rank( vote );
     487           0 :   ushort                  shred_version = ag_vote_shred_version( vote );
     488           0 :   fd_bls_sig_t const *    sig           = ag_vote_sig( vote );
     489           0 :   fd_bls_pub_t const *    pub           = ag_epoch_info_pubkey( epoch_info, rank );
     490             : 
     491           0 :   ag_slot_votes_t * votes = &self->votes;
     492           0 :   if( FD_UNLIKELY( fallback ) ) { votes->skip_fallback_stake += stake; blst_p1_add_or_double_affine( &votes->skip_fallback_agg.pub, &votes->skip_fallback_agg.pub, pub ); blst_p2_add_or_double_affine( &votes->skip_fallback_agg.sig, &votes->skip_fallback_agg.sig, sig ); fd_bls_set_insert( votes->skip_fallback_agg.set, rank ); }
     493           0 :   else                          { votes->skip_stake          += stake; blst_p1_add_or_double_affine( &votes->skip_agg.pub,          &votes->skip_agg.pub,          pub ); blst_p2_add_or_double_affine( &votes->skip_agg.sig,          &votes->skip_agg.sig,          sig ); fd_bls_set_insert( votes->skip_agg.set,          rank ); }
     494             : 
     495           0 :   ag_block_hash_set_t pending = self->pending_safe_to_notar;
     496           0 :   for( ulong i=0UL; i<pending.cnt; i++ ) {
     497           0 :     if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, pending.hash[i] ) ) ) continue;
     498           0 :     switch( check_safe_to_notar( self, states, pending.hash[i], bad ) ) {
     499           0 :     case AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:
     500           0 :       out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = ag_block_id( slot, pending.hash[i] );
     501           0 :       break;
     502           0 :     case AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
     503           0 :       ulong j; for( j=0UL; j<*out_repair_event_cnt; j++ ) if( FD_UNLIKELY( !memcmp( out_repair_events[j].hash, pending.hash[i], sizeof(ag_block_hash_t) ) ) ) break;
     504           0 :       if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = ag_block_id( slot, pending.hash[i] );
     505           0 :       break;
     506           0 :     }
     507           0 :     default:
     508           0 :       break;
     509           0 :     }
     510           0 :   }
     511             : 
     512           0 :   if( FD_UNLIKELY( check_safe_to_skip( self, states, bad ) ) ) {
     513           0 :     out_safe_to_skip[ (*out_safe_to_skip_cnt)++ ] = slot;
     514           0 :   }
     515           0 :   if( FD_UNLIKELY( !fd_bls_set_is_null( bad ) && !fd_bls_set_test( fallback ? votes->skip_fallback_agg.set : votes->skip_agg.set, rank ) ) ) return 0;
     516             : 
     517           0 :   ulong total_skip_stake = votes->skip_stake + votes->skip_fallback_stake;
     518             : 
     519           0 :   int skip_verified          = 0;
     520           0 :   int skip_fallback_verified = 0;
     521           0 :   if( FD_UNLIKELY( ag_epoch_info_is_quorum( epoch_info, total_skip_stake ) && self->certs.skip.slot==ULONG_MAX ) ) {
     522           0 :     if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
     523           0 :       fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
     524           0 :       skip_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
     525           0 :       fd_bls_set_union( bad, bad, bad_skip );
     526           0 :     }
     527           0 :     if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_fallback_agg.set ) ) ) {
     528           0 :       fd_bls_set_t bad_sf[ fd_bls_set_word_cnt ];
     529           0 :       skip_fallback_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_SKIP_FALLBACK, NULL, &votes->skip_fallback_agg, bad_sf );
     530           0 :       fd_bls_set_union( bad, bad, bad_sf );
     531           0 :     }
     532           0 :     if( FD_UNLIKELY( !fd_bls_set_test( fallback ? votes->skip_fallback_agg.set : votes->skip_agg.set, rank ) ) ) return 0;
     533             : 
     534           0 :     ag_cert_skip_t cert = { .slot = slot, .shred_version = shred_version };
     535             : 
     536           0 :     if( FD_LIKELY( skip_verified ) ) {
     537           0 :       cert.agg_skip = votes->skip_agg;
     538           0 :       cert.stake += votes->skip_stake;
     539           0 :     }
     540           0 :     if( FD_LIKELY( skip_fallback_verified ) ) {
     541           0 :       cert.agg_skip_fallback = votes->skip_fallback_agg;
     542           0 :       cert.stake += votes->skip_fallback_stake;
     543           0 :     }
     544           0 :     if( FD_LIKELY( ag_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
     545           0 :       out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_SKIP, .skip = cert };
     546           0 :     }
     547           0 :   }
     548             : 
     549           0 :   return 1;
     550           0 : }
     551             : 
     552             : static int
     553             : count_finalize_stake( ag_slot_state_t *       self,
     554             :                       ag_slot_states_t *      states,
     555             :                       ag_vote_final_t const * vote,
     556             :                       ulong                   stake,
     557             :                       ag_cert_t *             out_cert_events,
     558             :                       ulong *                 out_cert_event_cnt,
     559           0 :                       fd_bls_set_t *          bad ) {
     560           0 :   ag_epoch_info_t const * epoch_info    = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     561           0 :   ulong                   slot          = vote->slot;
     562           0 :   ulong                   rank          = vote->rank;
     563           0 :   ushort                  shred_version = vote->shred_version;
     564           0 :   fd_bls_sig_t const *    sig           = &vote->sig;
     565           0 :   fd_bls_pub_t const *    pub           = ag_epoch_info_pubkey( epoch_info, rank );
     566             : 
     567           0 :   ag_slot_votes_t * votes = &self->votes;
     568           0 :   votes->finalize_stake += stake;
     569           0 :   blst_p1_add_or_double_affine( &votes->finalize_agg.pub, &votes->finalize_agg.pub, pub );
     570           0 :   blst_p2_add_or_double_affine( &votes->finalize_agg.sig, &votes->finalize_agg.sig, sig );
     571           0 :   fd_bls_set_insert( votes->finalize_agg.set, rank );
     572           0 :   if( FD_UNLIKELY( ag_epoch_info_is_quorum( epoch_info, votes->finalize_stake ) && self->certs.finalize.slot==ULONG_MAX ) ) {
     573           0 :     fd_bls_set_t bad_final[ fd_bls_set_word_cnt ];
     574           0 :     int finalize_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_FINAL, NULL, &votes->finalize_agg, bad_final );
     575           0 :     fd_bls_set_union( bad, bad, bad_final );
     576           0 :     if( FD_UNLIKELY( fd_bls_set_test( bad_final, rank ) ) ) return 0;
     577           0 :     if( FD_UNLIKELY( !finalize_verified || !ag_epoch_info_is_quorum( epoch_info, votes->finalize_stake ) ) ) return 1;
     578           0 :     ag_cert_final_t cert = { .slot = slot, .shred_version = shred_version, .stake = votes->finalize_stake, .agg = votes->finalize_agg };
     579           0 :     out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_FINAL, .final = cert };
     580           0 :   }
     581             : 
     582           0 :   return 1;
     583           0 : }
     584             : 
     585             : void
     586             : ag_slot_state_null( ag_slot_state_t *  self,
     587           0 :                     ag_slot_states_t * states ) {
     588           0 :   ag_slot_votes_t * votes = &self->votes;
     589           0 :   for( ulong rank = fd_bls_set_const_iter_init( votes->notar_set );              !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->notar_set,              rank ) ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_sig_idx        [ rank ] );
     590           0 :   for( ulong rank = fd_bls_set_const_iter_init( votes->skip_agg.set );          !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->skip_agg.set,          rank ) ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_sig_idx         [ rank ] );
     591           0 :   for( ulong rank = fd_bls_set_const_iter_init( votes->skip_fallback_agg.set ); !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->skip_fallback_agg.set, rank ) ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_fallback_sig_idx[ rank ] );
     592           0 :   for( ulong rank = fd_bls_set_const_iter_init( votes->finalize_agg.set );      !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->finalize_agg.set,      rank ) ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->finalize_sig_idx     [ rank ] );
     593           0 :   for( ulong rank=0UL; rank<AG_VAT_MAX; rank++ ) {
     594           0 :     for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_fallback_sig_idx[ rank ][ j ] );
     595           0 :   }
     596           0 :   ulong notar = votes->notar_hash_stake_head;
     597           0 :   for( ulong i=0UL; i<votes->notar_hash_stake_cnt; i++ ) {
     598           0 :     ulong prev = ag_vote_stake_pool_ele( states->notar_hash_stake_pool, notar )->prev;
     599           0 :     ag_vote_stake_map_idx_remove( states->notar_hash_stake_map, &ag_vote_stake_pool_ele( states->notar_hash_stake_pool, notar )->block_id, ULONG_MAX, states->notar_hash_stake_pool );
     600           0 :     ag_vote_stake_pool_idx_release( states->notar_hash_stake_pool, notar );
     601           0 :     notar = prev;
     602           0 :   }
     603           0 :   votes->notar_hash_stake_cnt = 0UL;
     604           0 :   fd_bls_set_null( votes->notar_set );
     605           0 :   ulong nf = votes->notar_fallback_hash_stake_head;
     606           0 :   for( ulong i=0UL; i<votes->notar_fallback_hash_stake_cnt; i++ ) {
     607           0 :     ulong prev = ag_vote_stake_pool_ele( states->notar_fallback_hash_stake_pool, nf )->prev;
     608           0 :     ag_vote_stake_map_idx_remove( states->notar_fallback_hash_stake_map, &ag_vote_stake_pool_ele( states->notar_fallback_hash_stake_pool, nf )->block_id, ULONG_MAX, states->notar_fallback_hash_stake_pool );
     609           0 :     ag_vote_stake_pool_idx_release( states->notar_fallback_hash_stake_pool, nf );
     610           0 :     nf = prev;
     611           0 :   }
     612           0 :   votes->notar_fallback_hash_stake_cnt = 0UL;
     613           0 :   fd_memset( votes->notar_fallback_sig_cnt, 0, sizeof(votes->notar_fallback_sig_cnt) );
     614           0 :   votes->skip_stake = 0UL;
     615           0 :   fd_bls_agg_null( &votes->skip_agg );
     616           0 :   votes->skip_fallback_stake = 0UL;
     617           0 :   fd_bls_agg_null( &votes->skip_fallback_agg );
     618           0 :   votes->finalize_stake = 0UL;
     619           0 :   fd_bls_agg_null( &votes->finalize_agg );
     620           0 :   votes->notar_or_skip_stake = 0UL;
     621           0 :   votes->top_notar_stake     = 0UL;
     622           0 :   fd_memset( votes->top_notar_hash, 0, sizeof(ag_block_hash_t) );
     623             : 
     624           0 :   self->certs.notar.slot         = ULONG_MAX;
     625           0 :   self->certs.notar_fallback_cnt = 0UL;
     626           0 :   self->certs.skip.slot          = ULONG_MAX;
     627           0 :   self->certs.fast_finalize.slot = ULONG_MAX;
     628           0 :   self->certs.finalize.slot      = ULONG_MAX;
     629             : 
     630           0 :   self->parents_cnt               = 0UL;
     631           0 :   self->pending_safe_to_notar.cnt = 0UL;
     632           0 :   self->sent_safe_to_notar.cnt    = 0UL;
     633           0 :   self->sent_safe_to_skip         = 0;
     634           0 : }
     635             : 
     636             : void
     637             : ag_slot_state_set_own_rank( ag_slot_state_t * self,
     638           0 :                             ulong             own_rank ) {
     639           0 :   self->own_rank = own_rank;
     640           0 : }
     641             : 
     642             : FD_FN_PURE int
     643             : ag_slot_state_is_notar_fallback( ag_slot_state_t const * self,
     644           0 :                                  ag_block_hash_t const   block_hash ) {
     645           0 :   ag_slot_certs_t const * c = &self->certs;
     646           0 :   for( ulong i=0UL; i<c->notar_fallback_cnt; i++ ) {
     647           0 :     if( FD_LIKELY( !memcmp( c->notar_fallback[i].block_hash, block_hash, sizeof(ag_block_hash_t) ) ) ) return 1;
     648           0 :   }
     649           0 :   return 0;
     650           0 : }
     651             : 
     652             : FD_FN_PURE int
     653             : ag_slot_state_is_notar_fallback_or_stronger( ag_slot_state_t const * self,
     654           0 :                                              ag_block_hash_t const   block_hash ) {
     655           0 :   int has_notar_cert      = self->certs.notar.slot !=ULONG_MAX && !memcmp( self->certs.notar.block_hash, block_hash, sizeof(ag_block_hash_t) );
     656           0 :   int has_fast_final_cert = self->certs.fast_finalize.slot!=ULONG_MAX && !memcmp( self->certs.fast_finalize.block_hash, block_hash, sizeof(ag_block_hash_t) );
     657           0 :   return has_notar_cert || has_fast_final_cert || ag_slot_state_is_notar_fallback( self, block_hash );
     658           0 : }
     659             : 
     660             : void
     661             : ag_slot_state_add_cert( ag_slot_state_t * self,
     662           0 :                         ag_cert_t const * cert ) {
     663           0 :   switch( cert->kind ) {
     664           0 :   case AG_CERT_KIND_NOTAR: {
     665           0 :     self->certs.notar = cert->notar;
     666           0 :     break;
     667           0 :   }
     668           0 :   case AG_CERT_KIND_NOTAR_FALLBACK: {
     669           0 :     ag_cert_notar_fallback_t const * n = &cert->notar_fallback;
     670           0 :     if( FD_LIKELY( !ag_slot_state_is_notar_fallback( self, n->block_hash ) ) ) {
     671           0 :       FD_CHECK_CRIT( self->certs.notar_fallback_cnt<AG_NOTAR_FALLBACK_CERT_MAX, "consensus safety violation" ); /* Lemma 48 */
     672           0 :       self->certs.notar_fallback[ self->certs.notar_fallback_cnt++ ] = *n;
     673           0 :     }
     674           0 :     break;
     675           0 :   }
     676           0 :   case AG_CERT_KIND_SKIP: {
     677           0 :     self->certs.skip = cert->skip;
     678           0 :     break;
     679           0 :   }
     680           0 :   case AG_CERT_KIND_FAST_FINAL: {
     681           0 :     self->certs.fast_finalize = cert->fast_final;
     682           0 :     break;
     683           0 :   }
     684           0 :   case AG_CERT_KIND_FINAL: {
     685           0 :     self->certs.finalize = cert->final;
     686           0 :     break;
     687           0 :   }
     688           0 :   default:
     689           0 :     FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
     690           0 :   }
     691           0 : }
     692             : 
     693             : int
     694             : ag_slot_state_add_vote( ag_slot_state_t *  self,
     695             :                         ag_slot_states_t * states,
     696             :                         ag_vote_t const *  vote,
     697             :                         ulong              stake,
     698             :                         ag_cert_t *        out_cert_events,
     699             :                         ulong *            out_cert_event_cnt,
     700             :                         ag_block_id_t *    out_safe_to_notar,
     701             :                         ulong *            out_safe_to_notar_cnt,
     702             :                         ulong *            out_safe_to_skip,
     703             :                         ulong *            out_safe_to_skip_cnt,
     704             :                         ag_block_id_t *    out_repair_events,
     705             :                         ulong *            out_repair_event_cnt,
     706           0 :                         fd_bls_set_t *     bad ) {
     707           0 :   ulong slot = ag_vote_slot( vote );
     708           0 :   ulong rank = ag_vote_rank( vote );
     709             : 
     710           0 :   fd_bls_sig_t const * sig = ag_vote_sig( vote );
     711             : 
     712           0 :   FD_TEST( fd_bls_sig_pool_free( states->bls_sig_pool ) );
     713           0 :   uint sig_idx = (uint)fd_bls_sig_pool_idx_acquire( states->bls_sig_pool );
     714           0 :   fd_bls_sig_pool_ele( states->bls_sig_pool, sig_idx )->sig = *sig;
     715             : 
     716           0 :   *out_cert_event_cnt = 0UL; *out_safe_to_notar_cnt = 0UL; *out_safe_to_skip_cnt = 0UL; *out_repair_event_cnt = 0UL; fd_bls_set_null( bad );
     717           0 :   self->shred_version = ag_vote_shred_version( vote );
     718           0 :   int err;
     719           0 :   switch( vote->kind ) {
     720           0 :   case AG_VOTE_KIND_NOTAR:
     721           0 :     self->votes.notar_sig_idx[ rank ] = sig_idx;
     722           0 :     fd_bls_set_insert( self->votes.notar_set, rank );
     723           0 :     err = count_notar_stake( self, states, &vote->notar, stake, out_cert_events, out_cert_event_cnt, out_safe_to_notar, out_safe_to_notar_cnt, out_safe_to_skip, out_safe_to_skip_cnt, out_repair_events, out_repair_event_cnt, bad );
     724           0 :     break;
     725           0 :   case AG_VOTE_KIND_NOTAR_FALLBACK:
     726           0 :     FD_TEST( self->votes.notar_fallback_sig_cnt[ rank ]<AG_NOTAR_FALLBACK_VOTE_MAX );
     727           0 :     self->votes.notar_fallback_sig_idx[ rank ][ self->votes.notar_fallback_sig_cnt[ rank ] ] = sig_idx;
     728           0 :     self->votes.notar_fallback_sig_cnt[ rank ]++;
     729           0 :     err = count_notar_fallback_stake( self, states, &vote->notar_fallback, stake, out_cert_events, out_cert_event_cnt, bad );
     730           0 :     break;
     731           0 :   case AG_VOTE_KIND_SKIP:
     732           0 :     self->votes.skip_sig_idx[ rank ] = sig_idx;
     733           0 :     self->votes.notar_or_skip_stake += stake;
     734           0 :     err = count_skip_stake( self, states, vote, stake, out_cert_events, out_cert_event_cnt, out_safe_to_notar, out_safe_to_notar_cnt, out_safe_to_skip, out_safe_to_skip_cnt, out_repair_events, out_repair_event_cnt, bad );
     735           0 :     break;
     736           0 :   case AG_VOTE_KIND_SKIP_FALLBACK:
     737           0 :     self->votes.skip_fallback_sig_idx[ rank ] = sig_idx;
     738           0 :     err = count_skip_stake( self, states, vote, stake, out_cert_events, out_cert_event_cnt, out_safe_to_notar, out_safe_to_notar_cnt, out_safe_to_skip, out_safe_to_skip_cnt, out_repair_events, out_repair_event_cnt, bad );
     739           0 :     break;
     740           0 :   case AG_VOTE_KIND_FINAL:
     741           0 :     self->votes.finalize_sig_idx[ rank ] = sig_idx;
     742           0 :     err = count_finalize_stake( self, states, &vote->final, stake, out_cert_events, out_cert_event_cnt, bad );
     743           0 :     break;
     744           0 :   default:
     745           0 :     FD_LOG_CRIT(( "unreachable" ));
     746           0 :   }
     747             : 
     748           0 :   if( FD_UNLIKELY( rank==self->own_rank ) ) {
     749           0 :     ag_block_hash_set_t pending = self->pending_safe_to_notar;
     750           0 :     for( ulong i=0UL; i<pending.cnt; i++ ) {
     751           0 :       if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, pending.hash[i] ) ) ) continue;
     752           0 :       switch( check_safe_to_notar( self, states, pending.hash[i], bad ) ) {
     753           0 :       case AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR: out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = ag_block_id( slot, pending.hash[i] ); break;
     754           0 :       case AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
     755           0 :         ulong j; for( j=0UL; j<*out_repair_event_cnt; j++ ) if( FD_UNLIKELY( !memcmp( out_repair_events[j].hash, pending.hash[i], sizeof(ag_block_hash_t) ) ) ) break;
     756           0 :         if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = ag_block_id( slot, pending.hash[i] );
     757           0 :         break;
     758           0 :       }
     759           0 :       default: break;
     760           0 :       }
     761           0 :     }
     762           0 :   }
     763             : 
     764           0 :   return err;
     765           0 : }
     766             : 
     767             : void
     768             : ag_slot_state_notify_parent_known( ag_slot_state_t *     self,
     769           0 :                                    ag_block_hash_t const block_hash ) {
     770           0 :   for( ulong i=0UL; i<self->parents_cnt; i++ ) {
     771           0 :     if( FD_UNLIKELY( !memcmp( self->parents[i].hash, block_hash, sizeof(ag_block_hash_t) ) ) ) return;
     772           0 :   }
     773           0 :   FD_TEST( self->parents_cnt < AG_EQVOC_BLOCK_HASH_MAX );
     774           0 :   ag_parent_status_t * e = &self->parents[ self->parents_cnt++ ];
     775           0 :   memcpy( e->hash, block_hash, sizeof(ag_block_hash_t) );
     776           0 :   e->kind = AG_PARENT_STATUS_KNOWN;
     777           0 : }
     778             : 
     779             : int
     780             : ag_slot_state_notify_parent_certified( ag_slot_state_t *     self,
     781             :                                        ag_slot_states_t *    states,
     782             :                                        ag_block_hash_t const block_hash,
     783           0 :                                        fd_bls_set_t *        bad ) {
     784           0 :   fd_bls_set_null( bad );
     785           0 :   ag_parent_status_t * parent = NULL;
     786           0 :   for( ulong i=0UL; i<self->parents_cnt; i++ ) {
     787           0 :     if( FD_LIKELY( !memcmp( self->parents[i].hash, block_hash, sizeof(ag_block_hash_t) ) ) ) { parent = &self->parents[i]; break; }
     788           0 :   }
     789           0 :   FD_TEST( parent );
     790           0 :   parent->kind = AG_PARENT_STATUS_CERTIFIED;
     791             : 
     792           0 :   if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, block_hash ) ) ) return 0;
     793             : 
     794           0 :   switch( check_safe_to_notar( self, states, block_hash, bad ) ) {
     795           0 :   case AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK:  return -1; /* unreachable: this block's parents entry was found above */
     796           0 :   case AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES: return  0;
     797           0 :   case AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:  return  1;
     798           0 :   default:                                     FD_LOG_CRIT(( "unreachable" ));
     799           0 :   }
     800           0 : }
     801             : 
     802             : FD_FN_PURE int
     803             : ag_slot_state_check_slashable_offence( ag_slot_state_t const *  self,
     804             :                                        ag_slot_states_t const * states,
     805           0 :                                        ag_vote_t const *        vote ) {
     806           0 :   ulong voter = ag_vote_rank( vote );
     807           0 :   ag_slot_votes_t const * votes = &self->votes;
     808             : 
     809           0 :   switch( vote->kind ) {
     810           0 :   case AG_VOTE_KIND_NOTAR: {
     811           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, voter ) ) ) {
     812           0 :       return AG_SLASHABLE_SKIP_AND_NOTARIZE;
     813           0 :     }
     814           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) {
     815           0 :       if( FD_UNLIKELY( memcmp( ag_vote_stake_pool_ele_const( states->notar_hash_stake_pool, votes->notar_hash_stake_idx[ voter ] )->block_id.hash, vote->notar.block_hash, sizeof(ag_block_hash_t) ) ) ) return AG_SLASHABLE_NOTAR_DIFFERENT_HASH;
     816           0 :     }
     817           0 :     break;
     818           0 :   }
     819             : 
     820           0 :   case AG_VOTE_KIND_NOTAR_FALLBACK:
     821           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->finalize_agg.set, voter ) ) ) {
     822           0 :       return AG_SLASHABLE_NOTAR_FALLBACK_AND_FINALIZE;
     823           0 :     } else if( FD_UNLIKELY( votes->notar_fallback_sig_cnt[ voter ]>=AG_NOTAR_FALLBACK_VOTE_MAX ) ) {
     824           0 :       return AG_SLASHABLE_NOTAR_FALLBACK_OVER_THREE;
     825           0 :     }
     826           0 :     break;
     827             : 
     828           0 :   case AG_VOTE_KIND_SKIP:
     829           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->finalize_agg.set, voter ) ) ) {
     830           0 :       return AG_SLASHABLE_SKIP_AND_FINALIZE;
     831           0 :     }
     832           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) return AG_SLASHABLE_SKIP_AND_NOTARIZE;
     833           0 :     break;
     834             : 
     835           0 :   case AG_VOTE_KIND_SKIP_FALLBACK:
     836           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->finalize_agg.set, voter ) ) ) {
     837           0 :       return AG_SLASHABLE_SKIP_AND_FINALIZE;
     838           0 :     }
     839           0 :     break;
     840             : 
     841           0 :   case AG_VOTE_KIND_FINAL: {
     842           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, voter ) || fd_bls_set_test( votes->skip_fallback_agg.set, voter ) ) ) {
     843           0 :       return AG_SLASHABLE_SKIP_AND_FINALIZE;
     844           0 :     }
     845           0 :     if( FD_UNLIKELY( votes->notar_fallback_sig_cnt[ voter ] ) ) {
     846           0 :       return AG_SLASHABLE_NOTAR_FALLBACK_AND_FINALIZE;
     847           0 :     }
     848           0 :     break;
     849           0 :   }
     850             : 
     851           0 :   default:
     852           0 :     FD_LOG_ERR(( "invalid vote kind %u", vote->kind ));
     853           0 :   }
     854           0 :   return AG_SLASHABLE_NONE;
     855           0 : }
     856             : 
     857             : FD_FN_PURE int
     858             : ag_slot_state_should_ignore_vote( ag_slot_state_t const *  self,
     859             :                                   ag_slot_states_t const * states,
     860           0 :                                   ag_vote_t const *        vote ) {
     861           0 :   ulong                       voter                          = ag_vote_rank( vote );
     862           0 :   ag_slot_votes_t const *     votes                          = &self->votes;
     863           0 :   ag_vote_stake_ele_t const * notar_hash_stake_pool          = states->notar_hash_stake_pool;
     864           0 :   ag_vote_stake_ele_t const * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
     865           0 :   switch( vote->kind ) {
     866           0 :   case AG_VOTE_KIND_NOTAR: {
     867           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) return 1;
     868             : 
     869           0 :     uchar const * hash = vote->notar.block_hash;
     870           0 :     for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ voter ]; j++ ) {
     871           0 :       if( FD_UNLIKELY( !memcmp( ag_vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ voter ][ j ] )->block_id.hash, hash, sizeof(ag_block_hash_t) ) ) ) return 1;
     872           0 :     }
     873           0 :     return 0;
     874           0 :   }
     875           0 :   case AG_VOTE_KIND_NOTAR_FALLBACK: {
     876           0 :     uchar const * hash = vote->notar_fallback.block_hash;
     877           0 :     for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ voter ]; j++ ) {
     878           0 :       if( FD_UNLIKELY( !memcmp( ag_vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ voter ][ j ] )->block_id.hash, hash, sizeof(ag_block_hash_t) ) ) ) return 1;
     879           0 :     }
     880             : 
     881           0 :     return fd_bls_set_test( votes->notar_set, voter ) && !memcmp( ag_vote_stake_pool_ele_const( notar_hash_stake_pool, votes->notar_hash_stake_idx[ voter ] )->block_id.hash, hash, sizeof(ag_block_hash_t) );
     882           0 :   }
     883           0 :   case AG_VOTE_KIND_SKIP:
     884           0 :   case AG_VOTE_KIND_SKIP_FALLBACK:
     885           0 :     return fd_bls_set_test( votes->skip_agg.set, voter ) || fd_bls_set_test( votes->skip_fallback_agg.set, voter );
     886           0 :   case AG_VOTE_KIND_FINAL:
     887           0 :     return fd_bls_set_test( votes->finalize_agg.set, voter );
     888           0 :   default:
     889           0 :     FD_LOG_ERR(( "invalid vote kind %u", vote->kind ));
     890           0 :   }
     891           0 :   return 0;
     892           0 : }
     893             : 
     894             : fd_bls_agg_t *
     895             : ag_slot_state_notar_reward_agg( ag_slot_state_t const *  self,
     896             :                                 ag_slot_states_t const * states,
     897           0 :                                 fd_bls_agg_t *           out ) {
     898           0 :   ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
     899           0 :   ag_block_id_t           key        = ag_block_id( self->slot, self->votes.top_notar_hash );
     900           0 :   ulong                   top        = ag_vote_stake_map_idx_query_const( states->notar_hash_stake_map, &key, ULONG_MAX, states->notar_hash_stake_pool );
     901           0 :   if( FD_UNLIKELY( top==ULONG_MAX ) ) return NULL;
     902             : 
     903           0 :   fd_bls_set_t set[ fd_bls_set_word_cnt ];
     904           0 :   fd_bls_set_null( set );
     905           0 :   for( ulong rank = fd_bls_set_const_iter_init( self->votes.notar_set );
     906           0 :                    !fd_bls_set_const_iter_done( rank );
     907           0 :              rank = fd_bls_set_const_iter_next( self->votes.notar_set, rank ) ) {
     908           0 :     if( FD_LIKELY( self->votes.notar_hash_stake_idx[ rank ]==top ) ) fd_bls_set_insert( set, rank );
     909           0 :   }
     910           0 :   fd_bls_sig_t sigs[ AG_VAT_MAX ];
     911           0 :   ag_slot_state_sigs( self, states, AG_VOTE_KIND_NOTAR, key.hash, set, sigs );
     912           0 :   fd_bls_agg_null( out );
     913           0 :   fd_bls_agg_construct( out, epoch_info->pubkeys, sigs, set );
     914           0 :   return out;
     915           0 : }
     916             : 
     917             : FD_FN_PURE fd_bls_agg_t const *
     918           0 : ag_slot_state_skip_reward_agg( ag_slot_state_t const * self ) {
     919           0 :   return fd_bls_set_is_null( self->votes.skip_agg.set ) ? NULL : &self->votes.skip_agg;
     920           0 : }
     921             : 
     922             : fd_bls_sig_t *
     923             : ag_slot_state_sigs( ag_slot_state_t const *  self,
     924             :                     ag_slot_states_t const * states,
     925             :                     uint                     kind,
     926             :                     ag_block_hash_t const    block_hash,
     927             :                     fd_bls_set_t const *     set,
     928           0 :                     fd_bls_sig_t *           out ) {
     929           0 :   ag_slot_votes_t const *     votes                          = &self->votes;
     930           0 :   ag_vote_stake_ele_t const * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
     931           0 :   for( ulong rank = fd_bls_set_const_iter_init( set );
     932           0 :                    !fd_bls_set_const_iter_done( rank );
     933           0 :              rank = fd_bls_set_const_iter_next( set, rank ) ) {
     934           0 :     ulong idx;
     935           0 :     switch( kind ) {
     936           0 :     case AG_VOTE_KIND_NOTAR:         idx = votes->notar_sig_idx        [ rank ]; break;
     937           0 :     case AG_VOTE_KIND_SKIP:          idx = votes->skip_sig_idx         [ rank ]; break;
     938           0 :     case AG_VOTE_KIND_SKIP_FALLBACK: idx = votes->skip_fallback_sig_idx[ rank ]; break;
     939           0 :     case AG_VOTE_KIND_FINAL:         idx = votes->finalize_sig_idx     [ rank ]; break;
     940           0 :     case AG_VOTE_KIND_NOTAR_FALLBACK: {
     941           0 :       ulong j;
     942           0 :       for( j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
     943           0 :         if( FD_UNLIKELY( !memcmp( ag_vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ rank ][ j ] )->block_id.hash, block_hash, sizeof(ag_block_hash_t) ) ) ) break;
     944           0 :       }
     945           0 :       FD_CHECK_CRIT( j<votes->notar_fallback_sig_cnt[ rank ], "invariant violation" );
     946           0 :       idx = votes->notar_fallback_sig_idx[ rank ][ j ];
     947           0 :       break;
     948           0 :     }
     949           0 :     default: FD_LOG_CRIT(( "unreachable" ));
     950           0 :     }
     951           0 :     out[ rank ] = fd_bls_sig_pool_ele_const( states->bls_sig_pool, idx )->sig;
     952           0 :   }
     953           0 :   return out;
     954           0 : }

Generated by: LCOV version 1.14