LCOV - code coverage report
Current view: top level - choreo/votor - fd_votor_pool.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1387 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 114 0.0 %

          Line data    Source code
       1             : #include "fd_votor_pool.h"
       2             : #include "fd_finality_tracker.h"
       3             : #include "fd_parent_ready_tracker.h"
       4             : #include "fd_vote_serde.h"
       5             : 
       6           0 : #define PARENT_STATUS_KNOWN     (1)
       7           0 : #define PARENT_STATUS_CERTIFIED (2)
       8             : 
       9           0 : #define SLASHABLE_OFFENCE_NONE                        (0)
      10           0 : #define SLASHABLE_OFFENCE_NOTAR_DIFFERENT_HASH        (1)
      11           0 : #define SLASHABLE_OFFENCE_SKIP_AND_NOTARIZE           (2)
      12           0 : #define SLASHABLE_OFFENCE_SKIP_AND_FINALIZE           (3)
      13           0 : #define SLASHABLE_OFFENCE_NOTAR_FALLBACK_AND_FINALIZE (4)
      14             : 
      15           0 : #define SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR  (0)
      16           0 : #define SAFE_TO_NOTAR_STATUS_MISSING_BLOCK  (1)
      17           0 : #define SAFE_TO_NOTAR_STATUS_AWAITING_VOTES (2)
      18             : 
      19             : #define SLOT_STATE_OUT_CERT_MAX   (3UL)
      20             : #define SLOT_STATE_OUT_EVENT_MAX  (3UL)
      21             : #define SLOT_STATE_OUT_REPAIR_MAX (3UL)
      22             : 
      23           0 : #define NOTAR_FALLBACK_HASH_MAX (FD_VOTOR_VAT_MAX*FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX)
      24             : 
      25           0 : #define STANDSTILL_FINAL               (0UL)
      26           0 : #define STANDSTILL_FAST_FINAL          (1UL)
      27           0 : #define STANDSTILL_NOTAR               (2UL)
      28           0 : #define STANDSTILL_NOTAR_FALLBACK      (3UL)
      29           0 : #define STANDSTILL_SKIP                (STANDSTILL_NOTAR_FALLBACK+FD_VOTOR_NOTAR_FALLBACK_CERT_MAX)
      30           0 : #define STANDSTILL_NOTAR_VOTE          (STANDSTILL_SKIP+1UL)
      31           0 : #define STANDSTILL_FINAL_VOTE          (STANDSTILL_NOTAR_VOTE+1UL)
      32           0 : #define STANDSTILL_SKIP_VOTE           (STANDSTILL_FINAL_VOTE+1UL)
      33           0 : #define STANDSTILL_NOTAR_FALLBACK_VOTE (STANDSTILL_SKIP_VOTE+1UL)
      34           0 : #define STANDSTILL_SKIP_FALLBACK_VOTE  (STANDSTILL_NOTAR_FALLBACK_VOTE+FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX)
      35             : 
      36             : struct __attribute__((aligned(64UL))) vote_stake_ele {
      37             :   fd_block_id_t block_id;
      38             :   uint          next;
      39             :   uint          prev;
      40             :   ulong         stake;
      41             : };
      42             : typedef struct vote_stake_ele vote_stake_ele_t;
      43             : FD_STATIC_ASSERT( sizeof(vote_stake_ele_t)==64UL, vote_stake_ele );
      44             : 
      45             : #define POOL_NAME  vote_stake_pool
      46           0 : #define POOL_T     vote_stake_ele_t
      47           0 : #define POOL_NEXT  next
      48             : #define POOL_IDX_T uint
      49             : #define POOL_LAZY  1
      50             : #include "../../util/tmpl/fd_pool.c"
      51             : 
      52             : #define MAP_NAME               vote_stake_map
      53             : #define MAP_ELE_T              vote_stake_ele_t
      54           0 : #define MAP_KEY                block_id
      55             : #define MAP_KEY_T              fd_block_id_t
      56           0 : #define MAP_IDX_T              uint
      57           0 : #define MAP_NEXT               next
      58           0 : #define MAP_KEY_EQ(k0,k1)      (fd_block_id_eq((k0),(k1)))
      59           0 : #define MAP_KEY_HASH(key,seed) (fd_hash((seed),(key),sizeof(fd_block_id_t)))
      60             : #include "../../util/tmpl/fd_map_chain.c"
      61             : 
      62             : struct parent_status {
      63             :   fd_block_hash_t hash;
      64             :   int             kind;
      65             : };
      66             : typedef struct parent_status parent_status_t;
      67             : 
      68             : struct block_hash_set {
      69             :   fd_block_hash_t hash[ FD_VOTOR_EQVOC_MAX ];
      70             :   ulong           cnt;
      71             : };
      72             : typedef struct block_hash_set block_hash_set_t;
      73             : 
      74             : union bls_sig_ele {
      75             :   fd_bls_sig_t sig;
      76             :   ulong        next;
      77             : };
      78             : typedef union bls_sig_ele bls_sig_ele_t;
      79             : FD_STATIC_ASSERT( sizeof(bls_sig_ele_t)==sizeof(fd_bls_sig_t), bls_sig_ele );
      80             : 
      81             : #define POOL_NAME bls_sig_pool
      82           0 : #define POOL_T    bls_sig_ele_t
      83             : #include "../../util/tmpl/fd_pool.c"
      84             : 
      85             : struct slot_votes {
      86             :   fd_bls_set_t    notar_set                    [ fd_bls_set_word_cnt ];
      87             :   uint            notar_sig_idx                [ FD_VOTOR_VAT_MAX ];
      88             :   uint            notar_hash_stake_idx         [ FD_VOTOR_VAT_MAX ];
      89             :   uint            notar_hash_stake_head;
      90             :   ulong           notar_hash_stake_cnt;
      91             :   uint            notar_fallback_sig_idx       [ FD_VOTOR_VAT_MAX ][ FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX ];
      92             :   uint            notar_fallback_hash_stake_idx[ FD_VOTOR_VAT_MAX ][ FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX ];
      93             :   uint            notar_fallback_hash_stake_head;
      94             :   ulong           notar_fallback_hash_stake_cnt;
      95             :   uchar           notar_fallback_sig_cnt       [ FD_VOTOR_VAT_MAX ];
      96             :   ulong           skip_stake;
      97             :   uint            skip_sig_idx                 [ FD_VOTOR_VAT_MAX ];
      98             :   fd_bls_agg_t    skip_agg;
      99             :   ulong           skip_fallback_stake;
     100             :   uint            skip_fallback_sig_idx        [ FD_VOTOR_VAT_MAX ];
     101             :   fd_bls_agg_t    skip_fallback_agg;
     102             :   ulong           final_stake;
     103             :   uint            final_sig_idx                [ FD_VOTOR_VAT_MAX ];
     104             :   fd_bls_agg_t    final_agg;
     105             :   ulong           notar_or_skip_stake;
     106             :   ulong           top_notar_stake;
     107             :   fd_block_hash_t top_notar_hash;
     108             : };
     109             : typedef struct slot_votes slot_votes_t;
     110             : 
     111             : struct slot_certs {
     112             :   fd_cert_notar_t          notar;
     113             :   fd_cert_notar_fallback_t notar_fallback[ FD_VOTOR_NOTAR_FALLBACK_CERT_MAX ];
     114             :   fd_cert_skip_t           skip;
     115             :   fd_cert_fast_final_t     fast_final;
     116             :   fd_cert_final_t          final;
     117             :   uchar                    notar_fallback_cnt;
     118             : };
     119             : typedef struct slot_certs slot_certs_t;
     120             : 
     121             : struct slot_state {
     122             :   slot_votes_t votes;
     123             :   slot_certs_t certs;
     124             : 
     125             :   parent_status_t parents[ FD_VOTOR_EQVOC_MAX ];
     126             :   ulong           parents_cnt;
     127             : 
     128             :   block_hash_set_t pending_safe_to_notar;
     129             :   block_hash_set_t verified_safe_to_notar; /* verified votes alone meet its safe-to-notar threshold */
     130             :   block_hash_set_t sent_safe_to_notar;
     131             :   int              sent_safe_to_skip;
     132             : 
     133             :   ulong  slot;
     134             :   ulong  own_rank;
     135             :   ushort shred_version;
     136             : };
     137             : typedef struct slot_state slot_state_t;
     138             : 
     139             : struct slot_state_ele {
     140             :   ulong        slot;
     141             :   ulong        next;
     142             :   slot_state_t slot_state;
     143             : };
     144             : typedef struct slot_state_ele slot_state_ele_t;
     145             : 
     146             : #define POOL_NAME slot_state_pool
     147           0 : #define POOL_T    slot_state_ele_t
     148             : #include "../../util/tmpl/fd_pool.c"
     149             : 
     150             : #define MAP_NAME               slot_state_map
     151             : #define MAP_ELE_T              slot_state_ele_t
     152           0 : #define MAP_KEY                slot
     153             : #define MAP_KEY_T              ulong
     154           0 : #define MAP_KEY_EQ(k0,k1)      ((*(k0))==(*(k1)))
     155           0 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash((*(key))^(seed)))
     156           0 : #define MAP_NEXT               next
     157             : #include "../../util/tmpl/fd_map_chain.c"
     158             : 
     159             : struct slot_states {
     160             :   slot_state_ele_t *      pool;
     161             :   slot_state_map_t *      map;
     162             : 
     163             :   bls_sig_ele_t *         bls_sig_pool;
     164             :   vote_stake_ele_t *      notar_hash_stake_pool;
     165             :   vote_stake_map_t *      notar_hash_stake_map;
     166             :   vote_stake_ele_t *      notar_fallback_hash_stake_pool;
     167             :   vote_stake_map_t *      notar_fallback_hash_stake_map;
     168             :   fd_epoch_info_t const * prev_epoch_info;
     169             :   ulong                   prev_epoch_rank;
     170             :   ulong                   prev_epoch_slot;
     171             :   fd_epoch_info_t const * curr_epoch_info;
     172             :   ulong                   curr_epoch_rank;
     173             :   ulong                   curr_epoch_slot;
     174             :   fd_epoch_info_t const * next_epoch_info;
     175             :   ulong                   next_epoch_rank;
     176             :   ulong                   next_epoch_slot;
     177             :   ulong                   bls_verify_cnt;
     178             : };
     179             : typedef struct slot_states slot_states_t;
     180             : 
     181             : static int
     182             : block_hash_set_contains( block_hash_set_t const * self,
     183           0 :                          fd_block_hash_t const *  block_hash ) {
     184           0 :   for( ulong i=0UL; i<self->cnt; i++ ) {
     185           0 :     if( FD_LIKELY( !memcmp( &self->hash[i], block_hash, sizeof(fd_block_hash_t) ) ) ) return 1;
     186           0 :   }
     187           0 :   return 0;
     188           0 : }
     189             : 
     190             : static void
     191             : block_hash_set_insert( block_hash_set_t *      self,
     192           0 :                        fd_block_hash_t const * block_hash ) {
     193           0 :   if( FD_UNLIKELY( block_hash_set_contains( self, block_hash ) ) ) return;
     194           0 :   FD_TEST( self->cnt < FD_VOTOR_EQVOC_MAX );
     195           0 :   memcpy( &self->hash[ self->cnt++ ], block_hash, sizeof(fd_block_hash_t) );
     196           0 : }
     197             : 
     198             : static void
     199             : block_hash_set_remove( block_hash_set_t *      self,
     200           0 :                        fd_block_hash_t const * block_hash ) {
     201           0 :   for( ulong i=0UL; i<self->cnt; i++ ) {
     202           0 :     if( FD_LIKELY( !memcmp( &self->hash[i], block_hash, sizeof(fd_block_hash_t) ) ) ) {
     203           0 :       self->cnt--;
     204           0 :       if( FD_UNLIKELY( i!=self->cnt ) ) memcpy( &self->hash[i], &self->hash[ self->cnt ], sizeof(fd_block_hash_t) );
     205           0 :       return;
     206           0 :     }
     207           0 :   }
     208           0 : }
     209             : 
     210             : /* Definition 13. SlotState::is_notar_fallback */
     211             : 
     212             : FD_FN_PURE static int
     213             : slot_state_is_notar_fallback( slot_state_t const *    self,
     214           0 :                               fd_block_hash_t const * block_hash ) {
     215           0 :   slot_certs_t const * c = &self->certs;
     216           0 :   for( ulong i=0UL; i<c->notar_fallback_cnt; i++ ) {
     217           0 :     if( FD_LIKELY( !memcmp( &c->notar_fallback[i].block_hash, block_hash, sizeof(fd_block_hash_t) ) ) ) return 1;
     218           0 :   }
     219           0 :   return 0;
     220           0 : }
     221             : 
     222             : /* Definition 13. SlotState::is_notar_fallback_or_stronger */
     223             : 
     224             : FD_FN_PURE static int
     225             : slot_state_is_notar_fallback_or_stronger( slot_state_t const *    self,
     226           0 :                                           fd_block_hash_t const * block_hash ) {
     227           0 :   int has_notar_cert      = self->certs.notar.slot !=ULONG_MAX && !memcmp( &self->certs.notar.block_hash, block_hash, sizeof(fd_block_hash_t) );
     228           0 :   int has_fast_final_cert = self->certs.fast_final.slot!=ULONG_MAX && !memcmp( &self->certs.fast_final.block_hash, block_hash, sizeof(fd_block_hash_t) );
     229           0 :   return has_notar_cert || has_fast_final_cert || slot_state_is_notar_fallback( self, block_hash );
     230           0 : }
     231             : 
     232             : /* Copies the kind vote sigs of the ranks in set into out[ rank ].
     233             :    block_hash selects the sig for notar fallback; for notar the caller
     234             :    filters set by hash. */
     235             : 
     236             : static fd_bls_sig_t *
     237             : slot_state_sigs( slot_state_t const *    self,
     238             :                  slot_states_t const *   states,
     239             :                  uint                    kind,
     240             :                  fd_block_hash_t const * block_hash,
     241             :                  fd_bls_set_t const *    set,
     242           0 :                  fd_bls_sig_t *          out ) {
     243           0 :   slot_votes_t const *     votes                          = &self->votes;
     244           0 :   vote_stake_ele_t const * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
     245           0 :   for( ulong rank = fd_bls_set_const_iter_init( set );
     246           0 :                    !fd_bls_set_const_iter_done( rank );
     247           0 :              rank = fd_bls_set_const_iter_next( set, rank ) ) {
     248           0 :     ulong idx;
     249           0 :     switch( kind ) {
     250           0 :     case FD_VOTE_KIND_NOTAR:         idx = votes->notar_sig_idx        [ rank ]; break;
     251           0 :     case FD_VOTE_KIND_SKIP:          idx = votes->skip_sig_idx         [ rank ]; break;
     252           0 :     case FD_VOTE_KIND_SKIP_FALLBACK: idx = votes->skip_fallback_sig_idx[ rank ]; break;
     253           0 :     case FD_VOTE_KIND_FINAL:         idx = votes->final_sig_idx        [ rank ]; break;
     254           0 :     case FD_VOTE_KIND_NOTAR_FALLBACK: {
     255           0 :       ulong j;
     256           0 :       for( j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
     257           0 :         if( FD_UNLIKELY( !memcmp( &vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ rank ][ j ] )->block_id.hash, block_hash, sizeof(fd_block_hash_t) ) ) ) break;
     258           0 :       }
     259           0 :       FD_CHECK_CRIT( j<votes->notar_fallback_sig_cnt[ rank ], "invariant violation" );
     260           0 :       idx = votes->notar_fallback_sig_idx[ rank ][ j ];
     261           0 :       break;
     262           0 :     }
     263           0 :     default: FD_LOG_CRIT(( "unreachable" ));
     264           0 :     }
     265           0 :     out[ rank ] = bls_sig_pool_ele_const( states->bls_sig_pool, idx )->sig;
     266           0 :   }
     267           0 :   return out;
     268           0 : }
     269             : 
     270             : static int
     271             : verify_subtract_votes( slot_state_t *          self,
     272             :                        slot_states_t *         states,
     273             :                        uint                    kind,
     274             :                        fd_block_hash_t const * block_hash,
     275             :                        fd_bls_agg_t *          agg,
     276           0 :                        fd_bls_set_t *          bad ) {
     277           0 :   fd_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 ) );
     278           0 :   slot_votes_t *          votes                          = &self->votes;
     279           0 :   vote_stake_map_t *      notar_hash_stake_map           = states->notar_hash_stake_map;
     280           0 :   vote_stake_map_t *      notar_fallback_hash_stake_map  = states->notar_fallback_hash_stake_map;
     281           0 :   vote_stake_ele_t *      notar_hash_stake_pool          = states->notar_hash_stake_pool;
     282           0 :   vote_stake_ele_t *      notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
     283             : 
     284           0 :   fd_bls_sig_t sigs[ FD_VOTOR_VAT_MAX ];
     285           0 :   fd_bls_set_t set [ fd_bls_set_word_cnt ];
     286           0 :   switch( kind ) {
     287           0 :   case FD_VOTE_KIND_SKIP:
     288           0 :   case FD_VOTE_KIND_SKIP_FALLBACK:
     289           0 :   case FD_VOTE_KIND_FINAL:
     290           0 :     slot_state_sigs( self, states, kind, NULL, agg->set, sigs );
     291           0 :     break;
     292           0 :   case FD_VOTE_KIND_NOTAR:
     293           0 :     fd_bls_set_null( set );
     294           0 :     for( ulong rank = fd_bls_set_const_iter_init( votes->notar_set );
     295           0 :                      !fd_bls_set_const_iter_done( rank );
     296           0 :                rank = fd_bls_set_const_iter_next( votes->notar_set, rank ) ) {
     297           0 :       if( FD_LIKELY( !memcmp( &vote_stake_pool_ele( notar_hash_stake_pool, votes->notar_hash_stake_idx[ rank ] )->block_id.hash, block_hash, sizeof(fd_block_hash_t) ) ) ) fd_bls_set_insert( set, rank );
     298           0 :     }
     299           0 :     slot_state_sigs( self, states, kind, block_hash, set, sigs );
     300           0 :     fd_bls_agg_null( agg );
     301           0 :     fd_bls_agg_construct( agg, epoch_info->pubkeys, sigs, set );
     302           0 :     break;
     303           0 :   case FD_VOTE_KIND_NOTAR_FALLBACK:
     304           0 :     fd_bls_set_null( set );
     305           0 :     for( ulong rank=0UL; rank<epoch_info->validator_cnt; rank++ ) {
     306           0 :       for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
     307           0 :         if( FD_LIKELY( !memcmp( &vote_stake_pool_ele( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ rank ][ j ] )->block_id.hash, block_hash, sizeof(fd_block_hash_t) ) ) ) fd_bls_set_insert( set, rank );
     308           0 :       }
     309           0 :     }
     310           0 :     slot_state_sigs( self, states, kind, block_hash, set, sigs );
     311           0 :     fd_bls_agg_null( agg );
     312           0 :     fd_bls_agg_construct( agg, epoch_info->pubkeys, sigs, set );
     313           0 :     break;
     314           0 :   default: FD_LOG_CRIT(( "unreachable" ));
     315           0 :   }
     316             : 
     317           0 :   uchar msg[ FD_VOTE_SIGNING_SER_MAX ];
     318           0 :   ulong msg_sz = fd_vote_signing_ser( kind, self->slot, block_hash, self->shred_version, msg );
     319           0 :   states->bls_verify_cnt++;
     320           0 :   int   err    = fd_bls_agg_verify_subtract( agg, msg, msg_sz, epoch_info->pubkeys, sigs, bad );
     321           0 :   if( FD_LIKELY( !err && fd_bls_set_is_null( bad ) ) ) return 1;
     322             : 
     323           0 :   fd_block_id_t key;
     324           0 :   ulong         id = ULONG_MAX;
     325           0 :   switch( kind ) {
     326           0 :   case FD_VOTE_KIND_NOTAR:          key = fd_block_id( self->slot, block_hash->uc ); id = vote_stake_map_idx_query_const( notar_hash_stake_map,          &key, ULONG_MAX, notar_hash_stake_pool          ); break;
     327           0 :   case FD_VOTE_KIND_NOTAR_FALLBACK: key = fd_block_id( self->slot, block_hash->uc ); id = vote_stake_map_idx_query_const( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool ); break;
     328           0 :   default: break;
     329           0 :   }
     330             : 
     331           0 :   for( ulong rank = fd_bls_set_const_iter_init( bad );
     332           0 :                    !fd_bls_set_const_iter_done( rank );
     333           0 :              rank = fd_bls_set_const_iter_next( bad, rank ) ) {
     334           0 :     ulong stake = fd_epoch_info_validator( epoch_info, rank )->stake;
     335           0 :     switch( kind ) {
     336           0 :     case FD_VOTE_KIND_NOTAR:
     337           0 :       vote_stake_pool_ele( notar_hash_stake_pool, id )->stake -= stake;
     338           0 :       votes->notar_or_skip_stake -= stake;
     339           0 :       fd_bls_set_remove( votes->notar_set, rank );
     340           0 :       bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_sig_idx[ rank ] );
     341           0 :       break;
     342           0 :     case FD_VOTE_KIND_NOTAR_FALLBACK: {
     343           0 :       ulong j;
     344           0 :       for( j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
     345           0 :         if( FD_UNLIKELY( votes->notar_fallback_hash_stake_idx[ rank ][ j ]==id ) ) break;
     346           0 :       }
     347           0 :       FD_CHECK_CRIT( j<votes->notar_fallback_sig_cnt[ rank ], "invariant violation" );
     348           0 :       vote_stake_pool_ele( notar_fallback_hash_stake_pool, id )->stake -= stake;
     349           0 :       bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_fallback_sig_idx[ rank ][ j ] );
     350           0 :       votes->notar_fallback_sig_cnt[ rank ]--;
     351           0 :       votes->notar_fallback_sig_idx       [ rank ][ j ] = votes->notar_fallback_sig_idx       [ rank ][ votes->notar_fallback_sig_cnt[ rank ] ];
     352           0 :       votes->notar_fallback_hash_stake_idx[ rank ][ j ] = votes->notar_fallback_hash_stake_idx[ rank ][ votes->notar_fallback_sig_cnt[ rank ] ];
     353           0 :       break;
     354           0 :     }
     355           0 :     case FD_VOTE_KIND_SKIP:
     356           0 :       votes->skip_stake -= stake;
     357           0 :       votes->notar_or_skip_stake -= stake;
     358           0 :       bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_sig_idx[ rank ] );
     359           0 :       break;
     360           0 :     case FD_VOTE_KIND_SKIP_FALLBACK:
     361           0 :       votes->skip_fallback_stake -= stake;
     362           0 :       bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_fallback_sig_idx[ rank ] );
     363           0 :       break;
     364           0 :     case FD_VOTE_KIND_FINAL:
     365           0 :       votes->final_stake -= stake;
     366           0 :       bls_sig_pool_idx_release( states->bls_sig_pool, votes->final_sig_idx[ rank ] );
     367           0 :       break;
     368           0 :     default:
     369           0 :       FD_LOG_CRIT(( "unreachable" ));
     370           0 :     }
     371           0 :   }
     372             : 
     373           0 :   if( FD_UNLIKELY( kind==FD_VOTE_KIND_NOTAR && fd_bls_set_is_null( agg->set ) ) ) {
     374           0 :     uint *  link = &votes->notar_hash_stake_head;
     375           0 :     ulong   j;
     376           0 :     for( j=0UL; j<votes->notar_hash_stake_cnt; j++ ) {
     377           0 :       if( FD_UNLIKELY( *link==id ) ) break;
     378           0 :       link = &vote_stake_pool_ele( notar_hash_stake_pool, *link )->prev;
     379           0 :     }
     380           0 :     FD_CHECK_CRIT( j<votes->notar_hash_stake_cnt, "invariant violation" );
     381           0 :     *link = vote_stake_pool_ele( notar_hash_stake_pool, id )->prev;
     382           0 :     votes->notar_hash_stake_cnt--;
     383           0 :     vote_stake_map_idx_remove( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     384           0 :     vote_stake_pool_idx_release( notar_hash_stake_pool, id );
     385           0 :   }
     386           0 :   if( FD_UNLIKELY( kind==FD_VOTE_KIND_NOTAR_FALLBACK && fd_bls_set_is_null( agg->set ) ) ) {
     387           0 :     uint *  link = &votes->notar_fallback_hash_stake_head;
     388           0 :     ulong   j;
     389           0 :     for( j=0UL; j<votes->notar_fallback_hash_stake_cnt; j++ ) {
     390           0 :       if( FD_UNLIKELY( *link==id ) ) break;
     391           0 :       link = &vote_stake_pool_ele( notar_fallback_hash_stake_pool, *link )->prev;
     392           0 :     }
     393           0 :     FD_CHECK_CRIT( j<votes->notar_fallback_hash_stake_cnt, "invariant violation" );
     394           0 :     *link = vote_stake_pool_ele( notar_fallback_hash_stake_pool, id )->prev;
     395           0 :     votes->notar_fallback_hash_stake_cnt--;
     396           0 :     vote_stake_map_idx_remove( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool );
     397           0 :     vote_stake_pool_idx_release( notar_fallback_hash_stake_pool, id );
     398           0 :   }
     399           0 :   if( FD_UNLIKELY( kind==FD_VOTE_KIND_NOTAR ) ) {
     400           0 :     votes->top_notar_stake = 0UL;
     401           0 :     ulong i = votes->notar_hash_stake_head;
     402           0 :     for( ulong j=0UL; j<votes->notar_hash_stake_cnt; j++ ) {
     403           0 :       if( FD_LIKELY( vote_stake_pool_ele( notar_hash_stake_pool, i )->stake>votes->top_notar_stake ) ) { /* FIXME slow */
     404           0 :         votes->top_notar_stake = vote_stake_pool_ele( notar_hash_stake_pool, i )->stake;
     405           0 :         memcpy( &votes->top_notar_hash, &vote_stake_pool_ele( notar_hash_stake_pool, i )->block_id.hash, sizeof(fd_block_hash_t) );
     406           0 :       }
     407           0 :       i = vote_stake_pool_ele( notar_hash_stake_pool, i )->prev;
     408           0 :     }
     409           0 :   }
     410           0 :   return !err;
     411           0 : }
     412             : 
     413             : static int
     414             : check_safe_to_notar( slot_state_t *          self,
     415             :                      slot_states_t *         states,
     416             :                      fd_block_hash_t const * block_hash,
     417           0 :                      fd_bls_set_t *          bad ) {
     418           0 :   fd_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 ) );
     419           0 :   slot_votes_t *          votes                 = &self->votes;
     420           0 :   vote_stake_map_t *      notar_hash_stake_map  = states->notar_hash_stake_map;
     421           0 :   vote_stake_ele_t *      notar_hash_stake_pool = states->notar_hash_stake_pool;
     422           0 :   fd_block_id_t           key                   = fd_block_id( self->slot, block_hash->uc );
     423           0 :   ulong                   notar                 = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     424           0 :   ulong                   notar_stake           = notar!=ULONG_MAX ? vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
     425           0 :   ulong                   skip_stake            = votes->skip_stake;
     426             : 
     427           0 :   if( FD_UNLIKELY( !fd_epoch_info_is_weakest_quorum( epoch_info, notar_stake ) ) ) {
     428           0 :     return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     429           0 :   }
     430           0 :   if( FD_UNLIKELY( !fd_epoch_info_is_weak_quorum( epoch_info, notar_stake ) && !fd_epoch_info_is_quorum( epoch_info, notar_stake + skip_stake ) ) ) {
     431           0 :     block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
     432           0 :     return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     433           0 :   }
     434             : 
     435           0 :   parent_status_t const * parent = NULL;
     436           0 :   for( ulong i=0UL; i<self->parents_cnt; i++ ) {
     437           0 :     if( FD_LIKELY( !memcmp( &self->parents[i].hash, block_hash, sizeof(fd_block_hash_t) ) ) ) { parent = &self->parents[i]; break; }
     438           0 :   }
     439           0 :   if( FD_UNLIKELY( !parent ) ) return SAFE_TO_NOTAR_STATUS_MISSING_BLOCK;
     440             : 
     441           0 :   ulong own = self->own_rank;
     442             : 
     443           0 :   int safe_to_notar = 0;
     444           0 :   if( FD_LIKELY( own!=USHORT_MAX ) ) { /* must be staked */
     445           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, own ) ) ) safe_to_notar = 1;
     446           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, own ) ) ) {
     447           0 :       if( FD_UNLIKELY( !memcmp( &vote_stake_pool_ele( notar_hash_stake_pool, votes->notar_hash_stake_idx[ own ] )->block_id.hash, block_hash, sizeof(fd_block_hash_t) ) ) ) return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     448           0 :       safe_to_notar = 1;
     449           0 :     }
     450           0 :   }
     451           0 :   if( FD_UNLIKELY( !safe_to_notar ) ) {
     452           0 :     block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
     453           0 :     return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     454           0 :   }
     455             : 
     456           0 :   if( FD_LIKELY( !block_hash_set_contains( &self->verified_safe_to_notar, block_hash ) ) ) {
     457           0 :     fd_bls_agg_t notar_agg[1];
     458           0 :     fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
     459           0 :     verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
     460           0 :     fd_bls_set_union( bad, bad, bad_notar );
     461           0 :     if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
     462           0 :       fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
     463           0 :       verify_subtract_votes( self, states, FD_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
     464           0 :       fd_bls_set_union( bad, bad, bad_skip );
     465           0 :     }
     466           0 :     notar       = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     467           0 :     notar_stake = notar!=ULONG_MAX ? vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
     468           0 :     skip_stake  = votes->skip_stake;
     469           0 :     if( FD_UNLIKELY( !fd_epoch_info_is_weakest_quorum( epoch_info, notar_stake ) || ( !fd_epoch_info_is_weak_quorum( epoch_info, notar_stake ) && !fd_epoch_info_is_quorum( epoch_info, notar_stake + skip_stake ) ) ) ) {
     470           0 :       block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
     471           0 :       return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     472           0 :     }
     473           0 :     block_hash_set_insert( &self->verified_safe_to_notar, block_hash );
     474           0 :   }
     475           0 :   if( FD_UNLIKELY( parent->kind!=PARENT_STATUS_CERTIFIED ) ) return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
     476             : 
     477           0 :   block_hash_set_remove( &self->pending_safe_to_notar, block_hash );
     478           0 :   block_hash_set_insert( &self->sent_safe_to_notar,    block_hash );
     479           0 :   return SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR;
     480           0 : }
     481             : 
     482             : static int
     483             : check_safe_to_skip( slot_state_t *  self,
     484             :                     slot_states_t * states,
     485           0 :                     fd_bls_set_t *  bad ) {
     486           0 :   fd_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 ) );
     487           0 :   slot_votes_t *          votes      = &self->votes;
     488           0 :   if( FD_LIKELY( self->sent_safe_to_skip
     489           0 :                  || !fd_epoch_info_is_weak_quorum( epoch_info, votes->notar_or_skip_stake - votes->top_notar_stake )
     490           0 :                  || self->own_rank==USHORT_MAX /* must be staked */ ) ) return 0;
     491           0 :   if( FD_LIKELY( !fd_bls_set_test( votes->notar_set, self->own_rank ) ) ) return 0;
     492             : 
     493           0 :   if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
     494           0 :     fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
     495           0 :     verify_subtract_votes( self, states, FD_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
     496           0 :     fd_bls_set_union( bad, bad, bad_skip );
     497           0 :   }
     498           0 :   if( FD_LIKELY( fd_epoch_info_is_weak_quorum( epoch_info, votes->skip_stake ) ) ) {
     499           0 :     self->sent_safe_to_skip = 1;
     500           0 :     return 1;
     501           0 :   }
     502             : 
     503           0 :   vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
     504           0 :   ulong              i                     = votes->notar_hash_stake_head;
     505           0 :   for( ulong j=votes->notar_hash_stake_cnt; j>0UL; j-- ) {
     506           0 :     fd_block_id_t key  = vote_stake_pool_ele( notar_hash_stake_pool, i )->block_id;
     507           0 :     ulong         prev = vote_stake_pool_ele( notar_hash_stake_pool, i )->prev;
     508           0 :     fd_bls_agg_t  notar_agg[1];
     509           0 :     fd_bls_set_t  bad_notar[ fd_bls_set_word_cnt ];
     510           0 :     verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR, &key.hash, notar_agg, bad_notar );
     511           0 :     fd_bls_set_union( bad, bad, bad_notar );
     512           0 :     i = prev;
     513           0 :   }
     514           0 :   if( FD_UNLIKELY( !fd_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;
     515           0 :   self->sent_safe_to_skip = 1;
     516           0 :   return 1;
     517           0 : }
     518             : 
     519             : static int
     520             : count_notar_stake( slot_state_t *          self,
     521             :                    slot_states_t *         states,
     522             :                    fd_vote_notar_t const * vote,
     523             :                    ulong                   stake,
     524             :                    fd_cert_t *             out_cert_events,
     525             :                    ulong *                 out_cert_event_cnt,
     526             :                    fd_block_id_t *         out_safe_to_notar,
     527             :                    ulong *                 out_safe_to_notar_cnt,
     528             :                    ulong *                 out_safe_to_skip,
     529             :                    ulong *                 out_safe_to_skip_cnt,
     530             :                    fd_block_id_t *         out_repair_events,
     531             :                    ulong *                 out_repair_event_cnt,
     532           0 :                    fd_bls_set_t *          bad ) {
     533           0 :   fd_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 ) );
     534           0 :   ulong                   slot          = vote->slot;
     535           0 :   fd_block_hash_t const * block_hash    = &vote->block_hash;
     536           0 :   fd_block_id_t           key           = fd_block_id( self->slot, block_hash->uc );
     537           0 :   ulong                   rank          = vote->rank;
     538           0 :   ushort                  shred_version = vote->shred_version;
     539             : 
     540           0 :   slot_votes_t *     votes                          = &self->votes;
     541           0 :   vote_stake_map_t * notar_hash_stake_map           = states->notar_hash_stake_map;
     542           0 :   vote_stake_map_t * notar_fallback_hash_stake_map  = states->notar_fallback_hash_stake_map;
     543           0 :   vote_stake_ele_t * notar_hash_stake_pool          = states->notar_hash_stake_pool;
     544           0 :   vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
     545           0 :   ulong              notar                          = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     546           0 :   if( FD_UNLIKELY( notar==ULONG_MAX ) ) {
     547           0 :     FD_TEST( vote_stake_pool_free( notar_hash_stake_pool ) );
     548           0 :     notar = vote_stake_pool_idx_acquire( notar_hash_stake_pool );
     549           0 :     vote_stake_pool_ele( notar_hash_stake_pool, notar )->block_id = key;
     550           0 :     vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake    = 0UL;
     551           0 :     vote_stake_pool_ele( notar_hash_stake_pool, notar )->prev     = (uint)( votes->notar_hash_stake_cnt ? votes->notar_hash_stake_head : vote_stake_pool_idx_null( notar_hash_stake_pool ) );
     552           0 :     vote_stake_map_idx_insert( notar_hash_stake_map, notar, notar_hash_stake_pool );
     553           0 :     votes->notar_hash_stake_head = (uint)notar;
     554           0 :     votes->notar_hash_stake_cnt++;
     555           0 :   }
     556           0 :   votes->notar_hash_stake_idx[ rank ] = (uint)notar;
     557           0 :   vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake += stake;
     558             : 
     559           0 :   ulong notar_stake           = vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     560           0 :   votes->notar_or_skip_stake += stake;
     561           0 :   if( FD_LIKELY( notar_stake>votes->top_notar_stake ) ) {
     562           0 :     votes->top_notar_stake = notar_stake;
     563           0 :     memcpy( &votes->top_notar_hash, block_hash, sizeof(fd_block_hash_t) );
     564           0 :   }
     565             : 
     566           0 :   if( FD_UNLIKELY( !block_hash_set_contains( &self->sent_safe_to_notar, block_hash ) ) ) {
     567           0 :     switch( check_safe_to_notar( self, states, block_hash, bad ) ) {
     568           0 :     case SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:
     569           0 :       out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = fd_block_id( slot, block_hash->uc );
     570           0 :       break;
     571           0 :     case SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
     572           0 :       ulong j; for( j=0UL; j<*out_repair_event_cnt; j++ ) if( FD_UNLIKELY( !memcmp( &out_repair_events[j].hash, block_hash, sizeof(fd_block_hash_t) ) ) ) break;
     573           0 :       if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = fd_block_id( slot, block_hash->uc );
     574           0 :       break;
     575           0 :     }
     576           0 :     default: break;
     577           0 :     }
     578           0 :   }
     579           0 :   if( FD_UNLIKELY( check_safe_to_skip( self, states, bad ) ) ) {
     580           0 :     out_safe_to_skip[ (*out_safe_to_skip_cnt)++ ] = slot;
     581           0 :   }
     582           0 :   if( FD_UNLIKELY( !fd_bls_set_is_null( bad ) ) ) {
     583           0 :     notar = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     584           0 :     if( FD_UNLIKELY( notar==ULONG_MAX || !fd_bls_set_test( votes->notar_set, rank ) ) ) return 0;
     585           0 :     notar_stake = vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     586           0 :   }
     587             : 
     588           0 :   vote_stake_ele_t const * nf_ele   = vote_stake_map_ele_query_const( notar_fallback_hash_stake_map, &key, NULL, notar_fallback_hash_stake_pool );
     589           0 :   ulong                    nf_stake = nf_ele ? nf_ele->stake : 0UL;
     590             : 
     591           0 :   int          notar_verified          = 0;
     592           0 :   int          notar_fallback_verified = 0;
     593           0 :   int          newly_notar             = 0;
     594           0 :   fd_bls_agg_t notar_agg[1];
     595           0 :   fd_bls_agg_t nf_agg[1];
     596             : 
     597           0 :   if( FD_UNLIKELY( ( fd_epoch_info_is_quorum       ( epoch_info, nf_stake + notar_stake ) && !slot_state_is_notar_fallback_or_stronger( self, block_hash ) ) ||
     598           0 :                    ( fd_epoch_info_is_quorum       ( epoch_info, notar_stake            ) && self->certs.notar.slot==ULONG_MAX                             ) ||
     599           0 :                    ( fd_epoch_info_is_strong_quorum( epoch_info, notar_stake            ) && self->certs.fast_final.slot==ULONG_MAX                        ) ) ) {
     600           0 :     fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
     601           0 :     notar_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
     602           0 :     fd_bls_set_union( bad, bad, bad_notar );
     603           0 :     if( FD_UNLIKELY( fd_bls_set_test( bad_notar, rank ) ) ) return 0;
     604             : 
     605           0 :     notar_stake = vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     606           0 :     newly_notar = notar_verified && fd_epoch_info_is_quorum( epoch_info, notar_stake ) && self->certs.notar.slot==ULONG_MAX;
     607           0 :     if( FD_LIKELY( nf_ele && !newly_notar && !slot_state_is_notar_fallback_or_stronger( self, block_hash ) ) ) {
     608           0 :       fd_bls_set_t bad_nf[ fd_bls_set_word_cnt ];
     609           0 :       notar_fallback_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR_FALLBACK, block_hash, nf_agg, bad_nf );
     610           0 :       fd_bls_set_union( bad, bad, bad_nf );
     611           0 :     }
     612           0 :   }
     613             : 
     614           0 :   if( FD_UNLIKELY( !newly_notar && !slot_state_is_notar_fallback_or_stronger( self, block_hash ) ) ) {
     615           0 :     fd_cert_notar_fallback_t cert = { .slot = slot, .shred_version = shred_version }; memcpy( &cert.block_hash, block_hash, sizeof(fd_block_hash_t) );
     616             : 
     617           0 :     if( FD_LIKELY( notar_verified ) ) {
     618           0 :       cert.agg_notar = *notar_agg;
     619           0 :       cert.stake += vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     620           0 :     }
     621           0 :     if( FD_LIKELY( notar_fallback_verified ) ) {
     622           0 :       cert.agg_notar_fallback = *nf_agg;
     623           0 :       cert.stake += nf_ele->stake;
     624           0 :     }
     625           0 :     if( FD_LIKELY( fd_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
     626           0 :       out_cert_events[( *out_cert_event_cnt )++] = ( fd_cert_t ){ .kind = FD_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = cert };
     627           0 :     }
     628           0 :   }
     629             : 
     630           0 :   if( FD_UNLIKELY( newly_notar ) ) {
     631           0 :     fd_cert_notar_t cert = { .slot = slot, .shred_version = shred_version, .stake = notar_stake, .agg = *notar_agg }; memcpy( &cert.block_hash, block_hash, sizeof(fd_block_hash_t) );
     632           0 :     out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_NOTAR, .notar = cert };
     633           0 :   }
     634             : 
     635           0 :   if( FD_UNLIKELY( notar_verified && fd_epoch_info_is_strong_quorum( epoch_info, notar_stake ) && self->certs.fast_final.slot==ULONG_MAX ) ) {
     636           0 :     fd_cert_fast_final_t cert = { .slot = slot, .shred_version = shred_version, .stake = notar_stake, .agg = *notar_agg }; memcpy( &cert.block_hash, block_hash, sizeof(fd_block_hash_t) );
     637           0 :     out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_FAST_FINAL, .fast_final = cert };
     638           0 :   }
     639             : 
     640           0 :   return 1;
     641           0 : }
     642             : 
     643             : static int
     644             : count_notar_fallback_stake( slot_state_t *                   self,
     645             :                             slot_states_t *                  states,
     646             :                             fd_vote_notar_fallback_t const * vote,
     647             :                             ulong                            stake,
     648             :                             fd_cert_t *                      out_cert_events,
     649             :                             ulong *                          out_cert_event_cnt,
     650           0 :                             fd_bls_set_t *                   bad ) {
     651           0 :   fd_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 ) );
     652           0 :   ulong                   slot          = vote->slot;
     653           0 :   fd_block_hash_t const * block_hash    = &vote->block_hash;
     654           0 :   fd_block_id_t           key           = fd_block_id( self->slot, block_hash->uc );
     655           0 :   ulong                   rank          = vote->rank;
     656           0 :   ushort                  shred_version = vote->shred_version;
     657             : 
     658           0 :   slot_votes_t *     votes                          = &self->votes;
     659           0 :   vote_stake_map_t * notar_hash_stake_map           = states->notar_hash_stake_map;
     660           0 :   vote_stake_map_t * notar_fallback_hash_stake_map  = states->notar_fallback_hash_stake_map;
     661           0 :   vote_stake_ele_t * notar_hash_stake_pool          = states->notar_hash_stake_pool;
     662           0 :   vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
     663           0 :   vote_stake_ele_t * nf_ele                         = vote_stake_map_ele_query( notar_fallback_hash_stake_map, &key, NULL, notar_fallback_hash_stake_pool );
     664           0 :   if( FD_UNLIKELY( !nf_ele ) ) {
     665           0 :     FD_TEST( vote_stake_pool_free( notar_fallback_hash_stake_pool ) );
     666           0 :     nf_ele           = vote_stake_pool_ele_acquire( notar_fallback_hash_stake_pool );
     667           0 :     nf_ele->block_id = key;
     668           0 :     nf_ele->stake    = 0UL;
     669           0 :     nf_ele->prev     = (uint)( votes->notar_fallback_hash_stake_cnt ? votes->notar_fallback_hash_stake_head : vote_stake_pool_idx_null( notar_fallback_hash_stake_pool ) );
     670           0 :     vote_stake_map_ele_insert( notar_fallback_hash_stake_map, nf_ele, notar_fallback_hash_stake_pool );
     671           0 :     votes->notar_fallback_hash_stake_head = (uint)vote_stake_pool_idx( notar_fallback_hash_stake_pool, nf_ele );
     672           0 :     votes->notar_fallback_hash_stake_cnt++;
     673           0 :   }
     674           0 :   votes->notar_fallback_hash_stake_idx[ rank ][ votes->notar_fallback_sig_cnt[ rank ]-1UL ] = (uint)vote_stake_pool_idx( notar_fallback_hash_stake_pool, nf_ele );
     675           0 :   nf_ele->stake += stake;
     676             : 
     677           0 :   ulong notar       = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
     678           0 :   ulong nf_stake    = nf_ele->stake;
     679           0 :   ulong notar_stake = notar!=ULONG_MAX ? vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
     680           0 :   if( FD_UNLIKELY( fd_epoch_info_is_quorum( epoch_info, nf_stake + notar_stake ) && !slot_state_is_notar_fallback_or_stronger( self, block_hash ) ) ) {
     681           0 :     fd_bls_agg_t nf_agg[1];
     682           0 :     fd_bls_agg_t notar_agg[1];
     683           0 :     fd_bls_set_t bad_nf[ fd_bls_set_word_cnt ];
     684           0 :     int nf_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR_FALLBACK, block_hash, nf_agg, bad_nf );
     685           0 :     fd_bls_set_union( bad, bad, bad_nf );
     686           0 :     if( FD_UNLIKELY( fd_bls_set_test( bad_nf, rank ) ) ) return 0;
     687           0 :     int notar_verified = 0;
     688           0 :     if( FD_LIKELY( notar!=ULONG_MAX ) ) {
     689           0 :       fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
     690           0 :       notar_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
     691           0 :       fd_bls_set_union( bad, bad, bad_notar );
     692           0 :     }
     693             : 
     694           0 :     fd_cert_notar_fallback_t cert = { .slot = slot, .shred_version = shred_version }; memcpy( &cert.block_hash, block_hash, sizeof(fd_block_hash_t) );
     695             : 
     696           0 :     if( FD_LIKELY( notar_verified ) ) {
     697           0 :       cert.agg_notar = *notar_agg;
     698           0 :       cert.stake += vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
     699           0 :     }
     700           0 :     if( FD_LIKELY( nf_verified ) ) {
     701           0 :       cert.agg_notar_fallback = *nf_agg;
     702           0 :       cert.stake += nf_ele->stake;
     703           0 :     }
     704           0 :     if( FD_LIKELY( fd_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
     705           0 :       out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = cert };
     706           0 :     }
     707           0 :   }
     708             : 
     709           0 :   return 1;
     710           0 : }
     711             : 
     712             : static int
     713             : count_skip_stake( slot_state_t *    self,
     714             :                   slot_states_t *   states,
     715             :                   fd_vote_t const * vote,
     716             :                   ulong             stake,
     717             :                   fd_cert_t *       out_cert_events,
     718             :                   ulong *           out_cert_event_cnt,
     719             :                   fd_block_id_t *   out_safe_to_notar,
     720             :                   ulong *           out_safe_to_notar_cnt,
     721             :                   ulong *           out_safe_to_skip,
     722             :                   ulong *           out_safe_to_skip_cnt,
     723             :                   fd_block_id_t *   out_repair_events,
     724             :                   ulong *           out_repair_event_cnt,
     725           0 :                   fd_bls_set_t *    bad ) {
     726           0 :   fd_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 ) );
     727           0 :   int                     fallback      = vote->kind==FD_VOTE_KIND_SKIP_FALLBACK;
     728           0 :   ulong                   slot          = fd_vote_slot( vote );
     729           0 :   ulong                   rank          = fd_vote_rank( vote );
     730           0 :   ushort                  shred_version = fd_vote_shred_version( vote );
     731           0 :   fd_bls_sig_t const *    sig           = fd_vote_sig( vote );
     732           0 :   fd_bls_pub_t const *    pub           = fd_epoch_info_pubkey( epoch_info, rank );
     733             : 
     734           0 :   slot_votes_t * votes = &self->votes;
     735           0 :   if( FD_UNLIKELY( fallback ) ) { votes->skip_fallback_stake += stake; fd_bls_pub_add( &votes->skip_fallback_agg.pub, &votes->skip_fallback_agg.pub, pub ); fd_bls_sig_add( &votes->skip_fallback_agg.sig, &votes->skip_fallback_agg.sig, sig ); fd_bls_set_insert( votes->skip_fallback_agg.set, rank ); }
     736           0 :   else                          { votes->skip_stake          += stake; fd_bls_pub_add( &votes->skip_agg.pub,          &votes->skip_agg.pub,          pub ); fd_bls_sig_add( &votes->skip_agg.sig,          &votes->skip_agg.sig,          sig ); fd_bls_set_insert( votes->skip_agg.set,          rank ); }
     737             : 
     738           0 :   block_hash_set_t pending = self->pending_safe_to_notar;
     739           0 :   for( ulong i=0UL; i<pending.cnt; i++ ) {
     740           0 :     if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, &pending.hash[i] ) ) ) continue;
     741           0 :     switch( check_safe_to_notar( self, states, &pending.hash[i], bad ) ) {
     742           0 :     case SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:
     743           0 :       out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = fd_block_id( slot, pending.hash[i].uc );
     744           0 :       break;
     745           0 :     case SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
     746           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(fd_block_hash_t) ) ) ) break;
     747           0 :       if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = fd_block_id( slot, pending.hash[i].uc );
     748           0 :       break;
     749           0 :     }
     750           0 :     default:
     751           0 :       break;
     752           0 :     }
     753           0 :   }
     754             : 
     755           0 :   if( FD_UNLIKELY( check_safe_to_skip( self, states, bad ) ) ) {
     756           0 :     out_safe_to_skip[ (*out_safe_to_skip_cnt)++ ] = slot;
     757           0 :   }
     758           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;
     759             : 
     760           0 :   ulong total_skip_stake = votes->skip_stake + votes->skip_fallback_stake;
     761             : 
     762           0 :   int skip_verified          = 0;
     763           0 :   int skip_fallback_verified = 0;
     764           0 :   if( FD_UNLIKELY( fd_epoch_info_is_quorum( epoch_info, total_skip_stake ) && self->certs.skip.slot==ULONG_MAX ) ) {
     765           0 :     if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
     766           0 :       fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
     767           0 :       skip_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
     768           0 :       fd_bls_set_union( bad, bad, bad_skip );
     769           0 :     }
     770           0 :     if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_fallback_agg.set ) ) ) {
     771           0 :       fd_bls_set_t bad_sf[ fd_bls_set_word_cnt ];
     772           0 :       skip_fallback_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_SKIP_FALLBACK, NULL, &votes->skip_fallback_agg, bad_sf );
     773           0 :       fd_bls_set_union( bad, bad, bad_sf );
     774           0 :     }
     775           0 :     if( FD_UNLIKELY( !fd_bls_set_test( fallback ? votes->skip_fallback_agg.set : votes->skip_agg.set, rank ) ) ) return 0;
     776             : 
     777           0 :     fd_cert_skip_t cert = { .slot = slot, .shred_version = shred_version };
     778             : 
     779           0 :     if( FD_LIKELY( skip_verified ) ) {
     780           0 :       cert.agg_skip = votes->skip_agg;
     781           0 :       cert.stake += votes->skip_stake;
     782           0 :     }
     783           0 :     if( FD_LIKELY( skip_fallback_verified ) ) {
     784           0 :       cert.agg_skip_fallback = votes->skip_fallback_agg;
     785           0 :       cert.stake += votes->skip_fallback_stake;
     786           0 :     }
     787           0 :     if( FD_LIKELY( fd_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
     788           0 :       out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_SKIP, .skip = cert };
     789           0 :     }
     790           0 :   }
     791             : 
     792           0 :   return 1;
     793           0 : }
     794             : 
     795             : static int
     796             : count_final_stake( slot_state_t *          self,
     797             :                    slot_states_t *         states,
     798             :                    fd_vote_final_t const * vote,
     799             :                    ulong                   stake,
     800             :                    fd_cert_t *             out_cert_events,
     801             :                    ulong *                 out_cert_event_cnt,
     802           0 :                    fd_bls_set_t *          bad ) {
     803           0 :   fd_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 ) );
     804           0 :   ulong                   slot          = vote->slot;
     805           0 :   ulong                   rank          = vote->rank;
     806           0 :   ushort                  shred_version = vote->shred_version;
     807           0 :   fd_bls_sig_t const *    sig           = &vote->sig;
     808           0 :   fd_bls_pub_t const *    pub           = fd_epoch_info_pubkey( epoch_info, rank );
     809             : 
     810           0 :   slot_votes_t * votes = &self->votes;
     811           0 :   votes->final_stake += stake;
     812           0 :   fd_bls_pub_add( &votes->final_agg.pub, &votes->final_agg.pub, pub );
     813           0 :   fd_bls_sig_add( &votes->final_agg.sig, &votes->final_agg.sig, sig );
     814           0 :   fd_bls_set_insert( votes->final_agg.set, rank );
     815           0 :   if( FD_UNLIKELY( fd_epoch_info_is_quorum( epoch_info, votes->final_stake ) && self->certs.final.slot==ULONG_MAX ) ) {
     816           0 :     fd_bls_set_t bad_final[ fd_bls_set_word_cnt ];
     817           0 :     int final_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_FINAL, NULL, &votes->final_agg, bad_final );
     818           0 :     fd_bls_set_union( bad, bad, bad_final );
     819           0 :     if( FD_UNLIKELY( fd_bls_set_test( bad_final, rank ) ) ) return 0;
     820           0 :     if( FD_UNLIKELY( !final_verified || !fd_epoch_info_is_quorum( epoch_info, votes->final_stake ) ) ) return 1;
     821           0 :     fd_cert_final_t cert = { .slot = slot, .shred_version = shred_version, .stake = votes->final_stake, .agg = votes->final_agg };
     822           0 :     out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_FINAL, .final = cert };
     823           0 :   }
     824             : 
     825           0 :   return 1;
     826           0 : }
     827             : 
     828             : /* Resets self to an empty slot state.  self must be zeroed memory or a
     829             :    slot state. */
     830             : 
     831             : static void
     832             : slot_state_null( slot_state_t *  self,
     833           0 :                  slot_states_t * states ) {
     834           0 :   slot_votes_t * votes = &self->votes;
     835           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 ) ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_sig_idx        [ rank ] );
     836           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 ) ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_sig_idx         [ rank ] );
     837           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 ) ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_fallback_sig_idx[ rank ] );
     838           0 :   for( ulong rank = fd_bls_set_const_iter_init( votes->final_agg.set );         !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->final_agg.set,         rank ) ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->final_sig_idx     [ rank ] );
     839           0 :   for( ulong rank=0UL; rank<FD_VOTOR_VAT_MAX; rank++ ) {
     840           0 :     for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_fallback_sig_idx[ rank ][ j ] );
     841           0 :   }
     842           0 :   ulong notar = votes->notar_hash_stake_head;
     843           0 :   for( ulong i=0UL; i<votes->notar_hash_stake_cnt; i++ ) {
     844           0 :     ulong prev = vote_stake_pool_ele( states->notar_hash_stake_pool, notar )->prev;
     845           0 :     vote_stake_map_idx_remove( states->notar_hash_stake_map, &vote_stake_pool_ele( states->notar_hash_stake_pool, notar )->block_id, ULONG_MAX, states->notar_hash_stake_pool );
     846           0 :     vote_stake_pool_idx_release( states->notar_hash_stake_pool, notar );
     847           0 :     notar = prev;
     848           0 :   }
     849           0 :   votes->notar_hash_stake_cnt = 0UL;
     850           0 :   fd_bls_set_null( votes->notar_set );
     851           0 :   ulong nf_idx = votes->notar_fallback_hash_stake_head;
     852           0 :   for( ulong i=0UL; i<votes->notar_fallback_hash_stake_cnt; i++ ) {
     853           0 :     ulong prev = vote_stake_pool_ele( states->notar_fallback_hash_stake_pool, nf_idx )->prev;
     854           0 :     vote_stake_map_idx_remove( states->notar_fallback_hash_stake_map, &vote_stake_pool_ele( states->notar_fallback_hash_stake_pool, nf_idx )->block_id, ULONG_MAX, states->notar_fallback_hash_stake_pool );
     855           0 :     vote_stake_pool_idx_release( states->notar_fallback_hash_stake_pool, nf_idx );
     856           0 :     nf_idx = prev;
     857           0 :   }
     858           0 :   votes->notar_fallback_hash_stake_cnt = 0UL;
     859           0 :   fd_memset( votes->notar_fallback_sig_cnt, 0, sizeof(votes->notar_fallback_sig_cnt) );
     860           0 :   votes->skip_stake = 0UL;
     861           0 :   fd_bls_agg_null( &votes->skip_agg );
     862           0 :   votes->skip_fallback_stake = 0UL;
     863           0 :   fd_bls_agg_null( &votes->skip_fallback_agg );
     864           0 :   votes->final_stake = 0UL;
     865           0 :   fd_bls_agg_null( &votes->final_agg );
     866           0 :   votes->notar_or_skip_stake = 0UL;
     867           0 :   votes->top_notar_stake     = 0UL;
     868           0 :   fd_memset( &votes->top_notar_hash, 0, sizeof(fd_block_hash_t) );
     869             : 
     870           0 :   self->certs.notar.slot         = ULONG_MAX;
     871           0 :   self->certs.notar_fallback_cnt = 0UL;
     872           0 :   self->certs.skip.slot          = ULONG_MAX;
     873           0 :   self->certs.fast_final.slot = ULONG_MAX;
     874           0 :   self->certs.final.slot      = ULONG_MAX;
     875             : 
     876           0 :   self->parents_cnt                = 0UL;
     877           0 :   self->pending_safe_to_notar.cnt  = 0UL;
     878           0 :   self->verified_safe_to_notar.cnt = 0UL;
     879           0 :   self->sent_safe_to_notar.cnt     = 0UL;
     880           0 :   self->sent_safe_to_skip          = 0;
     881           0 : }
     882             : 
     883             : /* Replaces our rank own_rank (USHORT_MAX if unstaked). */
     884             : 
     885             : /* Definition 13. SlotState::add_cert */
     886             : 
     887             : static void
     888             : slot_state_add_cert( slot_state_t *    self,
     889           0 :                      fd_cert_t const * cert ) {
     890           0 :   switch( cert->kind ) {
     891           0 :   case FD_CERT_KIND_NOTAR: {
     892           0 :     self->certs.notar = cert->notar;
     893           0 :     break;
     894           0 :   }
     895           0 :   case FD_CERT_KIND_NOTAR_FALLBACK: {
     896           0 :     fd_cert_notar_fallback_t const * n = &cert->notar_fallback;
     897           0 :     if( FD_LIKELY( !slot_state_is_notar_fallback( self, &n->block_hash ) ) ) {
     898           0 :       FD_CHECK_CRIT( self->certs.notar_fallback_cnt<FD_VOTOR_NOTAR_FALLBACK_CERT_MAX, "consensus safety violation" ); /* Lemma 48 */
     899           0 :       self->certs.notar_fallback[ self->certs.notar_fallback_cnt++ ] = *n;
     900           0 :     }
     901           0 :     break;
     902           0 :   }
     903           0 :   case FD_CERT_KIND_SKIP: {
     904           0 :     self->certs.skip = cert->skip;
     905           0 :     break;
     906           0 :   }
     907           0 :   case FD_CERT_KIND_FAST_FINAL: {
     908           0 :     self->certs.fast_final = cert->fast_final;
     909           0 :     break;
     910           0 :   }
     911           0 :   case FD_CERT_KIND_FINAL: {
     912           0 :     self->certs.final = cert->final;
     913           0 :     break;
     914           0 :   }
     915           0 :   default:
     916           0 :     FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
     917           0 :   }
     918           0 : }
     919             : 
     920             : /* Definition 12. SlotState::add_vote */
     921             : 
     922             : static int
     923             : slot_state_add_vote( slot_state_t *    self,
     924             :                      slot_states_t *   states,
     925             :                      fd_vote_t const * vote,
     926             :                      ulong             stake,
     927             :                      fd_cert_t *       out_cert_events,
     928             :                      ulong *           out_cert_event_cnt,
     929             :                      fd_block_id_t *   out_safe_to_notar,
     930             :                      ulong *           out_safe_to_notar_cnt,
     931             :                      ulong *           out_safe_to_skip,
     932             :                      ulong *           out_safe_to_skip_cnt,
     933             :                      fd_block_id_t *   out_repair_events,
     934             :                      ulong *           out_repair_event_cnt,
     935           0 :                      fd_bls_set_t *    bad ) {
     936           0 :   ulong slot = fd_vote_slot( vote );
     937           0 :   ulong rank = fd_vote_rank( vote );
     938             : 
     939           0 :   fd_bls_sig_t const * sig = fd_vote_sig( vote );
     940             : 
     941           0 :   FD_TEST( bls_sig_pool_free( states->bls_sig_pool ) );
     942           0 :   uint sig_idx = (uint)bls_sig_pool_idx_acquire( states->bls_sig_pool );
     943           0 :   bls_sig_pool_ele( states->bls_sig_pool, sig_idx )->sig = *sig;
     944             : 
     945           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 );
     946           0 :   self->shred_version = fd_vote_shred_version( vote );
     947           0 :   int err;
     948           0 :   switch( vote->kind ) {
     949           0 :   case FD_VOTE_KIND_NOTAR:
     950           0 :     self->votes.notar_sig_idx[ rank ] = sig_idx;
     951           0 :     fd_bls_set_insert( self->votes.notar_set, rank );
     952           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 );
     953           0 :     break;
     954           0 :   case FD_VOTE_KIND_NOTAR_FALLBACK:
     955           0 :     FD_TEST( self->votes.notar_fallback_sig_cnt[ rank ]<FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX );
     956           0 :     self->votes.notar_fallback_sig_idx[ rank ][ self->votes.notar_fallback_sig_cnt[ rank ] ] = sig_idx;
     957           0 :     self->votes.notar_fallback_sig_cnt[ rank ]++;
     958           0 :     err = count_notar_fallback_stake( self, states, &vote->notar_fallback, stake, out_cert_events, out_cert_event_cnt, bad );
     959           0 :     break;
     960           0 :   case FD_VOTE_KIND_SKIP:
     961           0 :     self->votes.skip_sig_idx[ rank ] = sig_idx;
     962           0 :     self->votes.notar_or_skip_stake += stake;
     963           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 );
     964           0 :     break;
     965           0 :   case FD_VOTE_KIND_SKIP_FALLBACK:
     966           0 :     self->votes.skip_fallback_sig_idx[ rank ] = sig_idx;
     967           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 );
     968           0 :     break;
     969           0 :   case FD_VOTE_KIND_FINAL:
     970           0 :     self->votes.final_sig_idx[ rank ] = sig_idx;
     971           0 :     err = count_final_stake( self, states, &vote->final, stake, out_cert_events, out_cert_event_cnt, bad );
     972           0 :     break;
     973           0 :   default:
     974           0 :     FD_LOG_CRIT(( "unreachable" ));
     975           0 :   }
     976             : 
     977           0 :   if( FD_UNLIKELY( rank==self->own_rank ) ) {
     978           0 :     block_hash_set_t pending = self->pending_safe_to_notar;
     979           0 :     for( ulong i=0UL; i<pending.cnt; i++ ) {
     980           0 :       if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, &pending.hash[i] ) ) ) continue;
     981           0 :       switch( check_safe_to_notar( self, states, &pending.hash[i], bad ) ) {
     982           0 :       case SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR: out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = fd_block_id( slot, pending.hash[i].uc ); break;
     983           0 :       case SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
     984           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(fd_block_hash_t) ) ) ) break;
     985           0 :         if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = fd_block_id( slot, pending.hash[i].uc );
     986           0 :         break;
     987           0 :       }
     988           0 :       default: break;
     989           0 :       }
     990           0 :     }
     991           0 :   }
     992             : 
     993           0 :   return err;
     994           0 : }
     995             : 
     996             : /* Definition 16, SafeToNotar. SlotState::notify_parent_known */
     997             : 
     998             : static void
     999             : slot_state_notify_parent_known( slot_state_t *          self,
    1000             :                                 slot_states_t *         states,
    1001             :                                 fd_block_hash_t const * block_hash,
    1002           0 :                                 fd_bls_set_t *          bad ) {
    1003           0 :   fd_bls_set_null( bad );
    1004           0 :   for( ulong i=0UL; i<self->parents_cnt; i++ ) {
    1005           0 :     if( FD_UNLIKELY( !memcmp( &self->parents[i].hash, block_hash, sizeof(fd_block_hash_t) ) ) ) return;
    1006           0 :   }
    1007           0 :   FD_TEST( self->parents_cnt < FD_VOTOR_EQVOC_MAX );
    1008           0 :   parent_status_t * e = &self->parents[ self->parents_cnt++ ];
    1009           0 :   memcpy( &e->hash, block_hash, sizeof(fd_block_hash_t) );
    1010           0 :   e->kind = PARENT_STATUS_KNOWN;
    1011             : 
    1012           0 :   check_safe_to_notar( self, states, block_hash, bad );
    1013           0 : }
    1014             : 
    1015             : /* Definition 16, SafeToNotar. SlotState::notify_parent_certified */
    1016             : 
    1017             : static int
    1018             : slot_state_notify_parent_certified( slot_state_t *          self,
    1019             :                                     slot_states_t *         states,
    1020             :                                     fd_block_hash_t const * block_hash,
    1021           0 :                                     fd_bls_set_t *          bad ) {
    1022           0 :   fd_bls_set_null( bad );
    1023           0 :   parent_status_t * parent = NULL;
    1024           0 :   for( ulong i=0UL; i<self->parents_cnt; i++ ) {
    1025           0 :     if( FD_LIKELY( !memcmp( &self->parents[i].hash, block_hash, sizeof(fd_block_hash_t) ) ) ) { parent = &self->parents[i]; break; }
    1026           0 :   }
    1027           0 :   FD_TEST( parent );
    1028           0 :   parent->kind = PARENT_STATUS_CERTIFIED;
    1029             : 
    1030           0 :   if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, block_hash ) ) ) return 0;
    1031             : 
    1032           0 :   switch( check_safe_to_notar( self, states, block_hash, bad ) ) {
    1033           0 :   case SAFE_TO_NOTAR_STATUS_MISSING_BLOCK:  return -1; /* unreachable: this block's parents entry was found above */
    1034           0 :   case SAFE_TO_NOTAR_STATUS_AWAITING_VOTES: return  0;
    1035           0 :   case SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:  return  1;
    1036           0 :   default:                                  FD_LOG_CRIT(( "unreachable" ));
    1037           0 :   }
    1038           0 : }
    1039             : 
    1040             : /* SlotState::check_slashable_offence */
    1041             : 
    1042             : FD_FN_PURE static int
    1043             : slot_state_check_slashable_offence( slot_state_t const *  self,
    1044             :                                     slot_states_t const * states,
    1045           0 :                                     fd_vote_t const *     vote ) {
    1046           0 :   ulong voter = fd_vote_rank( vote );
    1047           0 :   slot_votes_t const * votes = &self->votes;
    1048             : 
    1049           0 :   switch( vote->kind ) {
    1050           0 :   case FD_VOTE_KIND_NOTAR: {
    1051           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, voter ) ) ) {
    1052           0 :       return SLASHABLE_OFFENCE_SKIP_AND_NOTARIZE;
    1053           0 :     }
    1054           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) {
    1055           0 :       if( FD_UNLIKELY( memcmp( &vote_stake_pool_ele_const( states->notar_hash_stake_pool, votes->notar_hash_stake_idx[ voter ] )->block_id.hash, &vote->notar.block_hash, sizeof(fd_block_hash_t) ) ) ) return SLASHABLE_OFFENCE_NOTAR_DIFFERENT_HASH;
    1056           0 :     }
    1057           0 :     break;
    1058           0 :   }
    1059             : 
    1060           0 :   case FD_VOTE_KIND_NOTAR_FALLBACK:
    1061           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->final_agg.set, voter ) ) ) {
    1062           0 :       return SLASHABLE_OFFENCE_NOTAR_FALLBACK_AND_FINALIZE;
    1063           0 :     }
    1064           0 :     break;
    1065             : 
    1066           0 :   case FD_VOTE_KIND_SKIP:
    1067           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->final_agg.set, voter ) ) ) {
    1068           0 :       return SLASHABLE_OFFENCE_SKIP_AND_FINALIZE;
    1069           0 :     }
    1070           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) return SLASHABLE_OFFENCE_SKIP_AND_NOTARIZE;
    1071           0 :     break;
    1072             : 
    1073           0 :   case FD_VOTE_KIND_SKIP_FALLBACK:
    1074           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->final_agg.set, voter ) ) ) {
    1075           0 :       return SLASHABLE_OFFENCE_SKIP_AND_FINALIZE;
    1076           0 :     }
    1077           0 :     break;
    1078             : 
    1079           0 :   case FD_VOTE_KIND_FINAL: {
    1080           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, voter ) || fd_bls_set_test( votes->skip_fallback_agg.set, voter ) ) ) {
    1081           0 :       return SLASHABLE_OFFENCE_SKIP_AND_FINALIZE;
    1082           0 :     }
    1083           0 :     if( FD_UNLIKELY( votes->notar_fallback_sig_cnt[ voter ] ) ) {
    1084           0 :       return SLASHABLE_OFFENCE_NOTAR_FALLBACK_AND_FINALIZE;
    1085           0 :     }
    1086           0 :     break;
    1087           0 :   }
    1088             : 
    1089           0 :   default:
    1090           0 :     FD_LOG_ERR(( "invalid vote kind %u", vote->kind ));
    1091           0 :   }
    1092           0 :   return SLASHABLE_OFFENCE_NONE;
    1093           0 : }
    1094             : 
    1095             : /* Definition 12. SlotState::should_ignore_vote */
    1096             : 
    1097             : FD_FN_PURE static int
    1098             : slot_state_should_ignore_vote( slot_state_t const *  self,
    1099             :                                slot_states_t const * states,
    1100           0 :                                fd_vote_t const *     vote ) {
    1101           0 :   ulong                    voter                          = fd_vote_rank( vote );
    1102           0 :   slot_votes_t const *     votes                          = &self->votes;
    1103           0 :   vote_stake_ele_t const * notar_hash_stake_pool          = states->notar_hash_stake_pool;
    1104           0 :   vote_stake_ele_t const * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
    1105           0 :   switch( vote->kind ) {
    1106           0 :   case FD_VOTE_KIND_NOTAR: {
    1107           0 :     if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) return 1;
    1108             : 
    1109           0 :     fd_block_hash_t const * hash = &vote->notar.block_hash;
    1110           0 :     for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ voter ]; j++ ) {
    1111           0 :       if( FD_UNLIKELY( !memcmp( &vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ voter ][ j ] )->block_id.hash, hash, sizeof(fd_block_hash_t) ) ) ) return 1;
    1112           0 :     }
    1113           0 :     return 0;
    1114           0 :   }
    1115           0 :   case FD_VOTE_KIND_NOTAR_FALLBACK: {
    1116           0 :     if( FD_UNLIKELY( votes->notar_fallback_sig_cnt[ voter ]>=FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX ) ) return 1;
    1117             : 
    1118           0 :     fd_block_hash_t const * hash = &vote->notar_fallback.block_hash;
    1119           0 :     for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ voter ]; j++ ) {
    1120           0 :       if( FD_UNLIKELY( !memcmp( &vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ voter ][ j ] )->block_id.hash, hash, sizeof(fd_block_hash_t) ) ) ) return 1;
    1121           0 :     }
    1122             : 
    1123           0 :     return fd_bls_set_test( votes->notar_set, voter ) && !memcmp( &vote_stake_pool_ele_const( notar_hash_stake_pool, votes->notar_hash_stake_idx[ voter ] )->block_id.hash, hash, sizeof(fd_block_hash_t) );
    1124           0 :   }
    1125           0 :   case FD_VOTE_KIND_SKIP:
    1126           0 :   case FD_VOTE_KIND_SKIP_FALLBACK:
    1127           0 :     return fd_bls_set_test( votes->skip_agg.set, voter ) || fd_bls_set_test( votes->skip_fallback_agg.set, voter );
    1128           0 :   case FD_VOTE_KIND_FINAL:
    1129           0 :     return fd_bls_set_test( votes->final_agg.set, voter );
    1130           0 :   default:
    1131           0 :     FD_LOG_ERR(( "invalid vote kind %u", vote->kind ));
    1132           0 :   }
    1133           0 :   return 0;
    1134           0 : }
    1135             : 
    1136             : /* Accessors for reward certs */
    1137             : 
    1138             : static fd_bls_agg_t *
    1139             : slot_state_notar_reward_agg( slot_state_t const *  self,
    1140             :                              slot_states_t const * states,
    1141           0 :                              fd_bls_agg_t *        out ) {
    1142           0 :   fd_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 ) );
    1143           0 :   fd_block_id_t           key        = fd_block_id( self->slot, self->votes.top_notar_hash.uc );
    1144           0 :   ulong                   top        = vote_stake_map_idx_query_const( states->notar_hash_stake_map, &key, ULONG_MAX, states->notar_hash_stake_pool );
    1145           0 :   if( FD_UNLIKELY( top==ULONG_MAX ) ) return NULL;
    1146             : 
    1147           0 :   fd_bls_set_t set[ fd_bls_set_word_cnt ];
    1148           0 :   fd_bls_set_null( set );
    1149           0 :   for( ulong rank = fd_bls_set_const_iter_init( self->votes.notar_set );
    1150           0 :                    !fd_bls_set_const_iter_done( rank );
    1151           0 :              rank = fd_bls_set_const_iter_next( self->votes.notar_set, rank ) ) {
    1152           0 :     if( FD_LIKELY( self->votes.notar_hash_stake_idx[ rank ]==top ) ) fd_bls_set_insert( set, rank );
    1153           0 :   }
    1154           0 :   fd_bls_sig_t sigs[ FD_VOTOR_VAT_MAX ];
    1155           0 :   slot_state_sigs( self, states, FD_VOTE_KIND_NOTAR, &key.hash, set, sigs );
    1156           0 :   fd_bls_agg_null( out );
    1157           0 :   fd_bls_agg_construct( out, epoch_info->pubkeys, sigs, set );
    1158           0 :   return out;
    1159           0 : }
    1160             : 
    1161             : FD_FN_PURE static fd_bls_agg_t const *
    1162           0 : slot_state_skip_reward_agg( slot_state_t const * self ) {
    1163           0 :   return fd_bls_set_is_null( self->votes.skip_agg.set ) ? NULL : &self->votes.skip_agg;
    1164           0 : }
    1165             : 
    1166             : #define QUEUE_NAME pool_events
    1167           0 : #define QUEUE_T    fd_votor_pool_event_t
    1168             : #include "../../util/tmpl/fd_queue_dynamic.c"
    1169             : 
    1170             : /* repair_events are notifications that a block has reached SafeToNotar
    1171             :    (or stronger) status.  It does not necessarily trigger actual repair
    1172             :    requests if we already have the block... (that is the responsibility
    1173             :    of the repair tile to answer).  */
    1174             : 
    1175             : #define QUEUE_NAME repair_events
    1176           0 : #define QUEUE_T    fd_block_id_t
    1177             : #include "../../util/tmpl/fd_queue_dynamic.c"
    1178             : 
    1179             : /* Protocol bounds (< 20% byzantine stake) on what burst adds push onto
    1180             :    queues drained to empty before the next add:
    1181             : 
    1182             :    - IMPLICITLY_FINALIZED / SKIPPED: once per slot of the window
    1183             :    - SAFE_TO_NOTAR of waiting children: 3 hashes per child slot (stake
    1184             :      arithmetic), and correct nodes notar a child only at its parent's
    1185             :      slot+1 or a window start, for each of up to burst certified
    1186             :      parents
    1187             :    - PARENT_READY: once per window start (ready flag), plus one per add
    1188             :      moving the window
    1189             :    - per add: 4 of CERT_CREATED, SAFE_TO_NOTAR and SAFE_TO_SKIP
    1190             :    - repair: 4 block ids per add (3 created certs, 1 missing block) */
    1191             : 
    1192           0 : #define POOL_EVENT_MAX(slot_max,burst) ( (slot_max)                                                                             \
    1193           0 :                                        + 3UL*( ((slot_max)+FD_VOTOR_SLOTS_PER_WINDOW-1UL)/FD_VOTOR_SLOTS_PER_WINDOW + (burst) ) \
    1194           0 :                                        +     ( ((slot_max)+FD_VOTOR_SLOTS_PER_WINDOW-1UL)/FD_VOTOR_SLOTS_PER_WINDOW + (burst) ) \
    1195           0 :                                        + 4UL*(burst) )
    1196           0 : #define REPAIR_EVENT_MAX(burst)        ( 4UL*(burst) )
    1197             : 
    1198             : struct s2n_waiting_parent_cert_ele {
    1199             :   fd_block_id_t parent;
    1200             :   ulong         next;
    1201             :   ulong         prev;
    1202             :   fd_block_id_t child;
    1203             : };
    1204             : typedef struct s2n_waiting_parent_cert_ele s2n_waiting_parent_cert_ele_t;
    1205             : 
    1206             : #define POOL_NAME s2n_waiting_parent_cert_pool
    1207           0 : #define POOL_T    s2n_waiting_parent_cert_ele_t
    1208             : #include "../../util/tmpl/fd_pool.c"
    1209             : 
    1210             : #define MAP_NAME                           s2n_waiting_parent_cert_map
    1211           0 : #define MAP_ELE_T                          s2n_waiting_parent_cert_ele_t
    1212           0 : #define MAP_KEY                            parent.slot
    1213           0 : #define MAP_KEY_HASH(key,seed)             (fd_ulong_hash((*(key))^(seed)))
    1214           0 : #define MAP_NEXT                           next
    1215             : #define MAP_MULTI                          1
    1216             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
    1217             : #include "../../util/tmpl/fd_map_chain.c"
    1218             : 
    1219             : 
    1220             : struct s2n_waiting_parent_cert {
    1221             :   s2n_waiting_parent_cert_ele_t * pool;
    1222             :   s2n_waiting_parent_cert_map_t * map;
    1223             : };
    1224             : typedef struct s2n_waiting_parent_cert s2n_waiting_parent_cert_t;
    1225             : 
    1226             : struct __attribute__((aligned(128UL))) fd_votor_pool {
    1227             :   ulong slot_max;
    1228             : 
    1229             :   slot_states_t *             slot_states;
    1230             :   fd_parent_ready_tracker_t * parent_ready_tracker;
    1231             :   fd_finality_tracker_t *     finality_tracker;
    1232             :   s2n_waiting_parent_cert_t * s2n_waiting_parent_cert;
    1233             : 
    1234             :   fd_votor_pool_event_t * pool_events;
    1235             :   fd_block_id_t *         repair_events;
    1236             : 
    1237             :   struct {
    1238             :     fd_parent_ready_t * parent_readys;
    1239             :     fd_block_id_t *     implicitly_finalized;
    1240             :     ulong *             implicitly_skipped;
    1241             :   } scratch;
    1242             : };
    1243             : 
    1244             : ulong
    1245           0 : fd_votor_pool_align( void ) {
    1246           0 :   return alignof(fd_votor_pool_t);
    1247           0 : }
    1248             : 
    1249             : ulong
    1250             : fd_votor_pool_footprint( ulong slot_max,
    1251           0 :                          ulong burst ) {
    1252           0 :   if( FD_UNLIKELY( slot_max<FD_VOTOR_SLOTS_PER_WINDOW+FD_VOTOR_REWARD_SLOT_DELTA || slot_max>UINT_MAX/(FD_VOTOR_VAT_MAX*FD_VOTOR_VOTES_PER_SLOT_MAX) ) ) return 0UL;
    1253             : 
    1254           0 :   ulong slot_chain_cnt  = slot_state_map_chain_cnt_est( slot_max );
    1255           0 :   ulong sig_max         = slot_max*FD_VOTOR_VAT_MAX*FD_VOTOR_VOTES_PER_SLOT_MAX;
    1256           0 :   ulong notar_max       = slot_max*FD_VOTOR_VAT_MAX;
    1257           0 :   ulong notar_chain_cnt = vote_stake_map_chain_cnt_est( notar_max );
    1258           0 :   ulong nf_max          = slot_max*NOTAR_FALLBACK_HASH_MAX;
    1259           0 :   ulong nf_chain_cnt    = vote_stake_map_chain_cnt_est( nf_max );
    1260           0 :   ulong s2n_max         = slot_max*FD_VOTOR_EQVOC_MAX;
    1261           0 :   ulong s2n_chain_cnt   = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
    1262           0 :   ulong ready_max       = slot_max + FD_VOTOR_SLOTS_PER_WINDOW + 1UL; /* [root-FD_VOTOR_REWARD_SLOT_DELTA-FD_VOTOR_SLOTS_PER_WINDOW, root+slot_max-FD_VOTOR_REWARD_SLOT_DELTA] */
    1263           0 :   ulong event_max       = POOL_EVENT_MAX( slot_max, burst );
    1264           0 :   ulong repair_max      = REPAIR_EVENT_MAX( burst );
    1265             : 
    1266           0 :   return FD_LAYOUT_FINI(
    1267           0 :     FD_LAYOUT_APPEND(
    1268           0 :     FD_LAYOUT_APPEND(
    1269           0 :     FD_LAYOUT_APPEND(
    1270           0 :     FD_LAYOUT_APPEND(
    1271           0 :     FD_LAYOUT_APPEND(
    1272           0 :     FD_LAYOUT_APPEND(
    1273           0 :     FD_LAYOUT_APPEND(
    1274           0 :     FD_LAYOUT_APPEND(
    1275           0 :     FD_LAYOUT_APPEND(
    1276           0 :     FD_LAYOUT_APPEND(
    1277           0 :     FD_LAYOUT_APPEND(
    1278           0 :     FD_LAYOUT_APPEND(
    1279           0 :     FD_LAYOUT_APPEND(
    1280           0 :     FD_LAYOUT_APPEND(
    1281           0 :     FD_LAYOUT_APPEND(
    1282           0 :     FD_LAYOUT_APPEND(
    1283           0 :     FD_LAYOUT_APPEND(
    1284           0 :     FD_LAYOUT_APPEND(
    1285           0 :     FD_LAYOUT_APPEND(
    1286           0 :     FD_LAYOUT_INIT,
    1287           0 :       alignof(fd_votor_pool_t),             sizeof(fd_votor_pool_t)                                 ),
    1288           0 :       alignof(slot_states_t),               sizeof(slot_states_t)                                   ),
    1289           0 :       slot_state_pool_align(),              slot_state_pool_footprint( slot_max )                   ),
    1290           0 :       slot_state_map_align(),               slot_state_map_footprint ( slot_chain_cnt )             ),
    1291           0 :       bls_sig_pool_align(),                 bls_sig_pool_footprint( sig_max )                       ),
    1292           0 :       vote_stake_pool_align(),              vote_stake_pool_footprint( notar_max )                  ),
    1293           0 :       vote_stake_map_align(),               vote_stake_map_footprint ( notar_chain_cnt )            ),
    1294           0 :       vote_stake_pool_align(),              vote_stake_pool_footprint( nf_max )                     ),
    1295           0 :       vote_stake_map_align(),               vote_stake_map_footprint ( nf_chain_cnt )               ),
    1296           0 :       fd_parent_ready_tracker_align(),      fd_parent_ready_tracker_footprint( ready_max )          ),
    1297           0 :       fd_finality_tracker_align(),          fd_finality_tracker_footprint( slot_max )               ),
    1298           0 :       alignof(s2n_waiting_parent_cert_t),   sizeof(s2n_waiting_parent_cert_t)                       ),
    1299           0 :       s2n_waiting_parent_cert_pool_align(), s2n_waiting_parent_cert_pool_footprint( s2n_max )       ),
    1300           0 :       s2n_waiting_parent_cert_map_align(),  s2n_waiting_parent_cert_map_footprint ( s2n_chain_cnt ) ),
    1301           0 :       pool_events_align(),                  pool_events_footprint( event_max )                      ),
    1302           0 :       repair_events_align(),                repair_events_footprint( repair_max )                   ),
    1303           0 :       alignof(fd_parent_ready_t),           sizeof(fd_parent_ready_t) * slot_max                    ),
    1304           0 :       alignof(fd_block_id_t),               sizeof(fd_block_id_t)     * slot_max                    ),
    1305           0 :       alignof(ulong),                       sizeof(ulong)             * slot_max                    ),
    1306           0 :     fd_votor_pool_align() );
    1307           0 : }
    1308             : 
    1309             : void *
    1310             : fd_votor_pool_new( void * mem,
    1311             :                    ulong  slot_max,
    1312             :                    ulong  burst,
    1313           0 :                    ulong  seed ) {
    1314           0 :   if( FD_UNLIKELY( !mem ) ) {
    1315           0 :     FD_LOG_WARNING(( "NULL mem" ));
    1316           0 :     return NULL;
    1317           0 :   }
    1318           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_votor_pool_align() ) ) ) {
    1319           0 :     FD_LOG_WARNING(( "misaligned mem" ));
    1320           0 :     return NULL;
    1321           0 :   }
    1322           0 :   ulong footprint = fd_votor_pool_footprint( slot_max, burst );
    1323           0 :   if( FD_UNLIKELY( !footprint ) ) {
    1324           0 :     FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
    1325           0 :     return NULL;
    1326           0 :   }
    1327           0 :   fd_memset( mem, 0, footprint );
    1328             : 
    1329           0 :   ulong s2n_max         = slot_max*FD_VOTOR_EQVOC_MAX;
    1330           0 :   ulong slot_chain_cnt  = slot_state_map_chain_cnt_est( slot_max );
    1331           0 :   ulong sig_max         = slot_max*FD_VOTOR_VAT_MAX*FD_VOTOR_VOTES_PER_SLOT_MAX;
    1332           0 :   ulong notar_max       = slot_max*FD_VOTOR_VAT_MAX;
    1333           0 :   ulong notar_chain_cnt = vote_stake_map_chain_cnt_est( notar_max );
    1334           0 :   ulong nf_max          = slot_max*NOTAR_FALLBACK_HASH_MAX;
    1335           0 :   ulong nf_chain_cnt    = vote_stake_map_chain_cnt_est( nf_max );
    1336           0 :   ulong s2n_chain_cnt   = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
    1337           0 :   ulong ready_max       = slot_max + FD_VOTOR_SLOTS_PER_WINDOW + 1UL;
    1338           0 :   ulong event_max       = POOL_EVENT_MAX( slot_max, burst );
    1339           0 :   ulong repair_max      = REPAIR_EVENT_MAX( burst );
    1340             : 
    1341             : 
    1342           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
    1343           0 :   fd_votor_pool_t * pool                         = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_votor_pool_t),             sizeof(fd_votor_pool_t)                                 );
    1344           0 :   void *            slot_states                  = FD_SCRATCH_ALLOC_APPEND( l, alignof(slot_states_t),               sizeof(slot_states_t)                                   );
    1345           0 :   void *            slot_state_pool              = FD_SCRATCH_ALLOC_APPEND( l, slot_state_pool_align(),              slot_state_pool_footprint( slot_max )                   );
    1346           0 :   void *            slot_state_map               = FD_SCRATCH_ALLOC_APPEND( l, slot_state_map_align(),               slot_state_map_footprint ( slot_chain_cnt )             );
    1347           0 :   void *            bls_sig_pool                 = FD_SCRATCH_ALLOC_APPEND( l, bls_sig_pool_align(),                 bls_sig_pool_footprint( sig_max )                       );
    1348           0 :   void *            notar_hash_stake_pool        = FD_SCRATCH_ALLOC_APPEND( l, vote_stake_pool_align(),              vote_stake_pool_footprint( notar_max )                  );
    1349           0 :   void *            notar_hash_stake_map         = FD_SCRATCH_ALLOC_APPEND( l, vote_stake_map_align(),               vote_stake_map_footprint ( notar_chain_cnt )            );
    1350           0 :   void *            nf_hash_stake_pool           = FD_SCRATCH_ALLOC_APPEND( l, vote_stake_pool_align(),              vote_stake_pool_footprint( nf_max )                     );
    1351           0 :   void *            nf_hash_stake_map            = FD_SCRATCH_ALLOC_APPEND( l, vote_stake_map_align(),               vote_stake_map_footprint ( nf_chain_cnt )               );
    1352           0 :   void *            parent_ready_tracker         = FD_SCRATCH_ALLOC_APPEND( l, fd_parent_ready_tracker_align(),      fd_parent_ready_tracker_footprint( ready_max )          );
    1353           0 :   void *            finality_tracker             = FD_SCRATCH_ALLOC_APPEND( l, fd_finality_tracker_align(),          fd_finality_tracker_footprint( slot_max )               );
    1354           0 :   void *            s2n_waiting_parent_cert      = FD_SCRATCH_ALLOC_APPEND( l, alignof(s2n_waiting_parent_cert_t),   sizeof(s2n_waiting_parent_cert_t)                       );
    1355           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 )       );
    1356           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 ) );
    1357           0 :   void *            pool_events                  = FD_SCRATCH_ALLOC_APPEND( l, pool_events_align(),                  pool_events_footprint( event_max )                      );
    1358           0 :   void *            repair_events                = FD_SCRATCH_ALLOC_APPEND( l, repair_events_align(),                repair_events_footprint( repair_max )                   );
    1359           0 :   void *            parent_ready_scratch         = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_parent_ready_t),           sizeof(fd_parent_ready_t) * slot_max                    );
    1360           0 :   void *            implicitly_finalized_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_block_id_t),               sizeof(fd_block_id_t)     * slot_max                    );
    1361           0 :   void *            implicitly_skipped_scratch   = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong),                       sizeof(ulong)             * slot_max                    );
    1362           0 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_votor_pool_align() ) == (ulong)mem + footprint );
    1363             : 
    1364           0 :   pool->slot_states                                 = (slot_states_t *)slot_states;
    1365           0 :   pool->slot_states->pool                           = slot_state_pool_join( slot_state_pool_new( slot_state_pool,       slot_max              ) );
    1366           0 :   pool->slot_states->map                            = slot_state_map_join ( slot_state_map_new ( slot_state_map,        slot_chain_cnt, seed  ) );
    1367           0 :   pool->slot_states->bls_sig_pool                   = bls_sig_pool_join   ( bls_sig_pool_new   ( bls_sig_pool,          sig_max               ) );
    1368           0 :   pool->slot_states->notar_hash_stake_pool          = vote_stake_pool_join( vote_stake_pool_new( notar_hash_stake_pool, notar_max             ) );
    1369           0 :   pool->slot_states->notar_hash_stake_map           = vote_stake_map_join ( vote_stake_map_new ( notar_hash_stake_map,  notar_chain_cnt, seed ) );
    1370           0 :   pool->slot_states->notar_fallback_hash_stake_pool = vote_stake_pool_join( vote_stake_pool_new( nf_hash_stake_pool,    nf_max                ) );
    1371           0 :   pool->slot_states->notar_fallback_hash_stake_map  = vote_stake_map_join ( vote_stake_map_new ( nf_hash_stake_map,     nf_chain_cnt, seed    ) );
    1372           0 :   pool->slot_states->prev_epoch_info                = NULL;
    1373           0 :   pool->slot_states->prev_epoch_rank                = USHORT_MAX;
    1374           0 :   pool->slot_states->prev_epoch_slot                = ULONG_MAX;
    1375           0 :   pool->slot_states->curr_epoch_info                = NULL;
    1376           0 :   pool->slot_states->curr_epoch_rank                = USHORT_MAX;
    1377           0 :   pool->slot_states->curr_epoch_slot                = ULONG_MAX;
    1378           0 :   pool->slot_states->next_epoch_info                = NULL;
    1379           0 :   pool->slot_states->next_epoch_rank                = USHORT_MAX;
    1380           0 :   pool->slot_states->next_epoch_slot                = ULONG_MAX;
    1381           0 :   pool->slot_states->bls_verify_cnt                 = 0UL;
    1382             : 
    1383           0 :   pool->parent_ready_tracker = fd_parent_ready_tracker_join( fd_parent_ready_tracker_new( parent_ready_tracker, ready_max, seed ) );
    1384             : 
    1385           0 :   pool->finality_tracker = fd_finality_tracker_join( fd_finality_tracker_new( finality_tracker, slot_max, seed ) );
    1386             : 
    1387           0 :   pool->s2n_waiting_parent_cert       = (s2n_waiting_parent_cert_t *)s2n_waiting_parent_cert;
    1388           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                            ) );
    1389           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 ) );
    1390             : 
    1391           0 :   pool->pool_events   = pool_events_join  ( pool_events_new  ( pool_events,   event_max  ) );
    1392           0 :   pool->repair_events = repair_events_join( repair_events_new( repair_events, repair_max ) );
    1393             : 
    1394           0 :   pool->slot_max = slot_max;
    1395             : 
    1396             : 
    1397           0 :   pool->scratch.parent_readys        = (fd_parent_ready_t *)parent_ready_scratch;
    1398           0 :   pool->scratch.implicitly_finalized = (fd_block_id_t *)implicitly_finalized_scratch;
    1399           0 :   pool->scratch.implicitly_skipped   = (ulong *)implicitly_skipped_scratch;
    1400             : 
    1401             : 
    1402           0 :   return mem;
    1403           0 : }
    1404             : 
    1405             : fd_votor_pool_t *
    1406           0 : fd_votor_pool_join( void * mem ) {
    1407           0 :   fd_votor_pool_t * pool = (fd_votor_pool_t *)mem;
    1408           0 :   if( FD_UNLIKELY( !pool ) ) {
    1409           0 :     FD_LOG_WARNING(( "NULL mem" ));
    1410           0 :     return NULL;
    1411           0 :   }
    1412           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)pool, fd_votor_pool_align() ) ) ) {
    1413           0 :     FD_LOG_WARNING(( "misaligned mem" ));
    1414           0 :     return NULL;
    1415           0 :   }
    1416           0 :   return pool;
    1417           0 : }
    1418             : 
    1419             : void *
    1420           0 : fd_votor_pool_leave( fd_votor_pool_t const * self ) {
    1421           0 :   if( FD_UNLIKELY( !self ) ) {
    1422           0 :     FD_LOG_WARNING(( "NULL pool" ));
    1423           0 :     return NULL;
    1424           0 :   }
    1425           0 :   return (void *)self;
    1426           0 : }
    1427             : 
    1428             : void *
    1429           0 : fd_votor_pool_delete( void * mem ) {
    1430           0 :   if( FD_UNLIKELY( !mem ) ) {
    1431           0 :     FD_LOG_WARNING(( "NULL mem" ));
    1432           0 :     return NULL;
    1433           0 :   }
    1434           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_votor_pool_align() ) ) ) {
    1435           0 :     FD_LOG_WARNING(( "misaligned mem" ));
    1436           0 :     return NULL;
    1437           0 :   }
    1438           0 :   return mem;
    1439           0 : }
    1440             : 
    1441             : FD_FN_PURE fd_votor_pool_metrics_t
    1442           0 : fd_votor_pool_metrics( fd_votor_pool_t const * self ) {
    1443           0 :   return (fd_votor_pool_metrics_t){
    1444           0 :     .slot_state_pool_used = slot_state_pool_used( self->slot_states->pool ),
    1445           0 :     .slot_state_pool_free = slot_state_pool_free( self->slot_states->pool ),
    1446           0 :     .finalized_slot       = fd_votor_pool_finalized_slot( self ),
    1447           0 :     .pool_events_cnt      = pool_events_cnt( self->pool_events ),
    1448           0 :     .bls_verify_cnt       = self->slot_states->bls_verify_cnt
    1449           0 :   };
    1450           0 : }
    1451             : 
    1452             : static void
    1453             : pool_init( fd_votor_pool_t *     self,
    1454           0 :            fd_block_id_t const * root ) {
    1455           0 :   fd_finality_tracker_init( self->finality_tracker, root->slot );
    1456           0 :   self->parent_ready_tracker->root = fd_ulong_sat_sub( root->slot, FD_VOTOR_REWARD_SLOT_DELTA+FD_VOTOR_SLOTS_PER_WINDOW );
    1457           0 : }
    1458             : 
    1459             : static void
    1460             : pool_push_parent_readys( fd_votor_pool_t * self,
    1461           0 :                          ulong             ready_cnt ) {
    1462           0 :   for( ulong i=0UL; i<ready_cnt; i++ ) {
    1463           0 :     pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
    1464           0 :   }
    1465           0 : }
    1466             : 
    1467             : void
    1468             : fd_votor_pool_from_snapshot( fd_votor_pool_t *     self,
    1469           0 :                              fd_block_id_t const * root ) {
    1470           0 :   pool_init( self, root );
    1471           0 :   pool_push_parent_readys( self, fd_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, root, self->scratch.parent_readys ) );
    1472           0 : }
    1473             : 
    1474             : void
    1475             : fd_votor_pool_from_genesis( fd_votor_pool_t *     self,
    1476           0 :                             fd_block_id_t const * genesis ) {
    1477           0 :   pool_init( self, genesis );
    1478           0 :   pool_push_parent_readys( self, fd_parent_ready_tracker_mark_root( self->parent_ready_tracker, genesis, self->scratch.parent_readys ) );
    1479           0 : }
    1480             : 
    1481             : static slot_state_t const *
    1482             : slot_state_query( fd_votor_pool_t const * self,
    1483           0 :                   ulong                   slot ) {
    1484           0 :   slot_state_ele_t const * ele = slot_state_map_ele_query_const( self->slot_states->map, &slot, NULL, self->slot_states->pool );
    1485           0 :   return ele ? &ele->slot_state : NULL;
    1486           0 : }
    1487             : 
    1488             : static slot_state_t *
    1489             : slot_state( fd_votor_pool_t * self,
    1490           0 :             ulong             slot ) {
    1491           0 :   slot_state_ele_t * ele = slot_state_map_ele_query( self->slot_states->map, &slot, NULL, self->slot_states->pool );
    1492           0 :   if( FD_LIKELY( ele ) ) return &ele->slot_state;
    1493             : 
    1494           0 :   slot_states_t *         states = self->slot_states;
    1495           0 :   fd_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 ) );
    1496           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 ) );
    1497             : 
    1498           0 :   FD_TEST( info );
    1499           0 :   FD_TEST( slot_state_pool_free( self->slot_states->pool ) );
    1500             : 
    1501           0 :   ele       = slot_state_pool_ele_acquire( self->slot_states->pool );
    1502           0 :   ele->slot = slot;
    1503           0 :   slot_state_null( &ele->slot_state, states );
    1504           0 :   ele->slot_state.slot     = slot;
    1505           0 :   ele->slot_state.own_rank = rank;
    1506           0 :   slot_state_map_ele_insert( self->slot_states->map, ele, self->slot_states->pool );
    1507           0 :   return &ele->slot_state;
    1508           0 : }
    1509             : 
    1510             : static inline fd_finalization_event_t
    1511           0 : finalization_event_default( fd_votor_pool_t * self ) {
    1512           0 :   return fd_finalization_event_default( self->scratch.implicitly_finalized, self->scratch.implicitly_skipped );
    1513           0 : }
    1514             : 
    1515             : static void
    1516             : handle_finalization( fd_votor_pool_t *               self,
    1517           0 :                      fd_finalization_event_t const * event ) {
    1518           0 :   FD_TEST( pool_events_avail( self->pool_events )>=event->implicitly_finalized_cnt+event->implicitly_skipped_cnt );
    1519           0 :   for( ulong i=0UL; i<event->implicitly_finalized_cnt; i++ ) pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_IMPLICITLY_FINALIZED, .implicitly_finalized = event->implicitly_finalized[i] } );
    1520           0 :   for( ulong i=0UL; i<event->implicitly_skipped_cnt;   i++ ) pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_IMPLICITLY_SKIPPED,   .implicitly_skipped   = event->implicitly_skipped  [i] } );
    1521           0 :   ulong first_unpruned_slot = fd_finality_tracker_first_unpruned_slot( self->finality_tracker );
    1522           0 :   ulong retained_slot       = fd_ulong_sat_sub( first_unpruned_slot, FD_VOTOR_REWARD_SLOT_DELTA );
    1523           0 :   for( ulong slot = fd_ulong_sat_sub( self->parent_ready_tracker->root, FD_VOTOR_REWARD_SLOT_DELTA ); slot<retained_slot; slot++ ) {
    1524           0 :     slot_state_ele_t * ele = slot_state_map_ele_remove( self->slot_states->map, &slot, NULL, self->slot_states->pool );
    1525           0 :     if( FD_LIKELY( ele ) ) {
    1526           0 :       slot_state_null( &ele->slot_state, self->slot_states );
    1527           0 :       slot_state_pool_ele_release( self->slot_states->pool, ele );
    1528           0 :     }
    1529           0 :     s2n_waiting_parent_cert_ele_t * waiter;
    1530           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 );
    1531           0 :   }
    1532           0 :   fd_parent_ready_tracker_prune( self->parent_ready_tracker, fd_ulong_sat_sub( first_unpruned_slot, FD_VOTOR_REWARD_SLOT_DELTA+FD_VOTOR_SLOTS_PER_WINDOW ) );
    1533           0 : }
    1534             : 
    1535             : static void
    1536             : add_verified_cert( fd_votor_pool_t * self,
    1537             :                    fd_cert_t const * cert,
    1538           0 :                    fd_bls_set_t *    bad ) {
    1539           0 :   ulong slot = fd_cert_slot( cert );
    1540             : 
    1541           0 :   slot_state_add_cert( slot_state( self, slot ), cert );
    1542             : 
    1543           0 :   switch( cert->kind ) {
    1544           0 :   case FD_CERT_KIND_FINAL: {
    1545           0 :     fd_finalization_event_t finalization_event = finalization_event_default( self );
    1546           0 :     fd_finality_tracker_mark_finalized( self->finality_tracker, slot, &finalization_event );
    1547           0 :     handle_finalization( self, &finalization_event );
    1548           0 :     break;
    1549           0 :   }
    1550           0 :   case FD_CERT_KIND_FAST_FINAL:
    1551           0 :   case FD_CERT_KIND_NOTAR:
    1552           0 :   case FD_CERT_KIND_NOTAR_FALLBACK: {
    1553           0 :     fd_block_hash_t const * block_hash = fd_cert_block_hash( cert );
    1554           0 :     fd_block_id_t           block_id   = fd_block_id( slot, block_hash->uc );
    1555           0 :     if( FD_UNLIKELY( cert->kind==FD_CERT_KIND_FAST_FINAL ) ) {
    1556           0 :       fd_finalization_event_t finalization_event = finalization_event_default( self );
    1557           0 :       fd_finality_tracker_mark_fast_finalized( self->finality_tracker, &block_id, &finalization_event );
    1558           0 :       handle_finalization( self, &finalization_event );
    1559           0 :     }
    1560           0 :     if( FD_LIKELY( cert->kind==FD_CERT_KIND_NOTAR ) ) {
    1561           0 :       fd_finalization_event_t finalization_event = finalization_event_default( self );
    1562           0 :       fd_finality_tracker_mark_notarized( self->finality_tracker, &block_id, &finalization_event );
    1563           0 :       handle_finalization( self, &finalization_event );
    1564           0 :     }
    1565             : 
    1566           0 :     slot_states_t *                 states = self->slot_states;
    1567           0 :     s2n_waiting_parent_cert_ele_t * next;
    1568           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 );
    1569           0 :                                          child;
    1570           0 :                                          child = next ) {
    1571           0 :       next = (s2n_waiting_parent_cert_ele_t *)s2n_waiting_parent_cert_map_ele_next_const( child, NULL, self->s2n_waiting_parent_cert->pool );
    1572           0 :       if( FD_LIKELY( !fd_block_id_eq( &child->parent, &block_id ) ) ) continue;
    1573           0 :       fd_block_id_t child_id = child->child; /* copy before the release below */
    1574           0 :       s2n_waiting_parent_cert_map_ele_remove_fast( self->s2n_waiting_parent_cert->map, child, self->s2n_waiting_parent_cert->pool );
    1575           0 :       s2n_waiting_parent_cert_pool_ele_release( self->s2n_waiting_parent_cert->pool, child );
    1576             : 
    1577           0 :       slot_state_t * child_state = slot_state( self, child_id.slot );
    1578           0 :       fd_bls_set_t   bad_child[ fd_bls_set_word_cnt ];
    1579           0 :       int output = slot_state_notify_parent_certified( child_state, states, &child_id.hash, bad_child );
    1580           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 );
    1581           0 :       switch( output ) {
    1582           0 :       case -1: repair_events_push( self->repair_events, child_id ); break;
    1583           0 :       case  0: break;
    1584           0 :       case  1: pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = child_id } ); break;
    1585           0 :       }
    1586           0 :     }
    1587             : 
    1588           0 :     ulong ready_cnt = fd_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, &block_id, self->scratch.parent_readys );
    1589           0 :     for( ulong i=0UL; i<ready_cnt; i++ ) {
    1590           0 :       pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
    1591           0 :     }
    1592             : 
    1593           0 :     repair_events_push( self->repair_events, block_id );
    1594           0 :     break;
    1595           0 :   }
    1596             : 
    1597           0 :   case FD_CERT_KIND_SKIP: {
    1598           0 :     ulong ready_cnt = fd_parent_ready_tracker_mark_skipped( self->parent_ready_tracker, slot, self->scratch.parent_readys );
    1599           0 :     for( ulong i=0UL; i<ready_cnt; i++ ) {
    1600           0 :       pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
    1601           0 :     }
    1602           0 :     break;
    1603           0 :   }
    1604             : 
    1605           0 :   default:
    1606           0 :     FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
    1607           0 :   }
    1608             : 
    1609           0 :   fd_votor_pool_event_t event = { .kind = FD_VOTOR_POOL_EVENT_CERT_CREATED, .cert_created = *cert };
    1610           0 :   pool_events_push( self->pool_events, event );
    1611           0 : }
    1612             : 
    1613             : void
    1614             : fd_votor_pool_advance_epoch( fd_votor_pool_t *       self,
    1615             :                              fd_epoch_info_t const * epoch_info,
    1616             :                              ulong                   epoch_rank,
    1617           0 :                              ulong                   epoch_slot ) {
    1618           0 :   slot_states_t * states = self->slot_states;
    1619           0 :   if( FD_UNLIKELY( !states->curr_epoch_info ) ) {
    1620           0 :     states->curr_epoch_info = epoch_info;
    1621           0 :     states->curr_epoch_rank = epoch_rank;
    1622           0 :     states->curr_epoch_slot = epoch_slot;
    1623           0 :   } else if( FD_UNLIKELY( !states->next_epoch_info ) ) {
    1624           0 :     states->next_epoch_info = epoch_info;
    1625           0 :     states->next_epoch_rank = epoch_rank;
    1626           0 :     states->next_epoch_slot = epoch_slot;
    1627           0 :   } else {
    1628           0 :     states->prev_epoch_info = states->curr_epoch_info;
    1629           0 :     states->prev_epoch_slot = states->curr_epoch_slot;
    1630           0 :     states->prev_epoch_rank = states->curr_epoch_rank;
    1631           0 :     states->curr_epoch_info = states->next_epoch_info;
    1632           0 :     states->curr_epoch_slot = states->next_epoch_slot;
    1633           0 :     states->curr_epoch_rank = states->next_epoch_rank;
    1634           0 :     states->next_epoch_info = epoch_info;
    1635           0 :     states->next_epoch_rank = epoch_rank;
    1636           0 :     states->next_epoch_slot = epoch_slot;
    1637           0 :   }
    1638           0 : }
    1639             : 
    1640             : void
    1641             : fd_votor_pool_update_own_rank( fd_votor_pool_t * self,
    1642             :                                ulong             epoch_slot,
    1643           0 :                                ulong             epoch_rank ) {
    1644           0 :   slot_states_t * states = self->slot_states;
    1645           0 :   if     ( epoch_slot==states->prev_epoch_slot ) states->prev_epoch_rank = epoch_rank;
    1646           0 :   else if( epoch_slot==states->curr_epoch_slot ) states->curr_epoch_rank = epoch_rank;
    1647           0 :   else if( epoch_slot==states->next_epoch_slot ) states->next_epoch_rank = epoch_rank;
    1648           0 :   else FD_LOG_CRIT(( "no epoch starts at slot %lu", epoch_slot ));
    1649             : 
    1650           0 :   slot_state_map_t * map  = self->slot_states->map;
    1651           0 :   slot_state_ele_t * pool = self->slot_states->pool;
    1652           0 :   for( slot_state_map_iter_t iter = slot_state_map_iter_init( map, pool );
    1653           0 :                                    !slot_state_map_iter_done( iter, map, pool );
    1654           0 :                              iter = slot_state_map_iter_next( iter, map, pool ) ) {
    1655           0 :     slot_state_t * slot_state = &slot_state_map_iter_ele( iter, map, pool )->slot_state;
    1656           0 :     ulong          slot       = slot_state->slot;
    1657           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 ) );
    1658             : 
    1659           0 :     slot_state->own_rank = rank;
    1660           0 :   }
    1661           0 : }
    1662             : 
    1663             : int
    1664             : fd_votor_pool_add_block( fd_votor_pool_t *     self,
    1665             :                          fd_block_id_t const * block_id,
    1666             :                          fd_block_id_t const * parent_id,
    1667           0 :                          fd_bls_set_t *        bad ) {
    1668           0 :   fd_bls_set_null( bad );
    1669             : 
    1670           0 :   ulong                   slot        = block_id->slot;
    1671           0 :   fd_block_hash_t const * block_hash  = &block_id->hash;
    1672           0 :   ulong                   parent_slot = parent_id->slot;
    1673           0 :   fd_block_hash_t const * parent_hash = &parent_id->hash;
    1674             : 
    1675           0 :   ulong slot_far_in_future = fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - FD_VOTOR_REWARD_SLOT_DELTA;
    1676           0 :   if( FD_UNLIKELY( slot<fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_OLD;
    1677           0 :   if( FD_UNLIKELY( slot>=slot_far_in_future                                            ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_NEW;
    1678           0 :   slot_states_t * states = self->slot_states;
    1679           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, FD_VOTOR_POOL_ERR_SLOT_TOO_OLD, FD_VOTOR_POOL_ERR_SLOT_TOO_NEW );
    1680             : 
    1681           0 :   fd_finalization_event_t finalization_event = finalization_event_default( self );
    1682           0 :   fd_finality_tracker_add_parent( self->finality_tracker, block_id, parent_id, &finalization_event );
    1683           0 :   handle_finalization( self, &finalization_event );
    1684           0 :   if( FD_UNLIKELY( slot<fd_ulong_sat_sub( fd_finality_tracker_first_unpruned_slot( self->finality_tracker ), FD_VOTOR_REWARD_SLOT_DELTA ) ) ) return FD_VOTOR_POOL_SUCCESS; /* pruned by the parent link */
    1685             : 
    1686           0 :   slot_state_notify_parent_known( slot_state( self, slot ), states, block_hash, bad );
    1687           0 :   slot_state_ele_t * parent_state_ = slot_state_map_ele_query( self->slot_states->map, &parent_slot, NULL, self->slot_states->pool );
    1688           0 :   slot_state_t *     parent_state  = parent_state_ ? &parent_state_->slot_state : NULL;
    1689           0 :   if( FD_LIKELY( parent_state && slot_state_is_notar_fallback_or_stronger( parent_state, parent_hash ) ) ) {
    1690           0 :     fd_bls_set_t bad_certified[ fd_bls_set_word_cnt ];
    1691           0 :     int output = slot_state_notify_parent_certified( slot_state( self, slot ), states, block_hash, bad_certified );
    1692           0 :     fd_bls_set_union( bad, bad, bad_certified );
    1693           0 :     switch( output ) {
    1694           0 :     case -1: repair_events_push( self->repair_events, *block_id ); return FD_VOTOR_POOL_SUCCESS;
    1695           0 :     case  0: return FD_VOTOR_POOL_SUCCESS;
    1696           0 :     case  1: pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = *block_id } ); return FD_VOTOR_POOL_SUCCESS;
    1697           0 :     }
    1698           0 :   }
    1699             : 
    1700           0 :   if( FD_UNLIKELY( parent_slot<fd_ulong_sat_sub( fd_finality_tracker_first_unpruned_slot( self->finality_tracker ), FD_VOTOR_REWARD_SLOT_DELTA ) ) ) return FD_VOTOR_POOL_SUCCESS; /* the parent can no longer certify */
    1701           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 );
    1702           0 :                                              ele;
    1703           0 :                                              ele = s2n_waiting_parent_cert_map_ele_next_const( ele, NULL, self->s2n_waiting_parent_cert->pool ) ) {
    1704           0 :     if( FD_UNLIKELY( fd_block_id_eq( &ele->child, block_id ) ) ) return FD_VOTOR_POOL_SUCCESS;
    1705           0 :   }
    1706           0 :   FD_TEST( s2n_waiting_parent_cert_pool_free( self->s2n_waiting_parent_cert->pool ) );
    1707           0 :   s2n_waiting_parent_cert_ele_t * ele = s2n_waiting_parent_cert_pool_ele_acquire( self->s2n_waiting_parent_cert->pool );
    1708           0 :   ele->parent = *parent_id;
    1709           0 :   ele->child  = *block_id;
    1710           0 :   s2n_waiting_parent_cert_map_ele_insert( self->s2n_waiting_parent_cert->map, ele, self->s2n_waiting_parent_cert->pool );
    1711             : 
    1712           0 :   return FD_VOTOR_POOL_SUCCESS;
    1713           0 : }
    1714             : 
    1715             : int
    1716             : fd_votor_pool_add_cert( fd_votor_pool_t * self,
    1717             :                         fd_cert_t const * cert,
    1718             :                         int               verified,
    1719           0 :                         fd_bls_set_t *    bad ) {
    1720           0 :   ulong slot = fd_cert_slot( cert );
    1721           0 :   fd_bls_set_null( bad );
    1722             : 
    1723           0 :   ulong slot_far_in_future = fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - FD_VOTOR_REWARD_SLOT_DELTA;
    1724           0 :   if( FD_UNLIKELY( slot<fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_OLD;
    1725           0 :   if( FD_UNLIKELY( slot>=slot_far_in_future                                            ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_NEW;
    1726             : 
    1727           0 :   slot_states_t const *   states     = self->slot_states;
    1728           0 :   fd_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 ) );
    1729           0 :   if( FD_UNLIKELY( !epoch_info ) ) return fd_int_if( slot<states->curr_epoch_slot, FD_VOTOR_POOL_ERR_SLOT_TOO_OLD, FD_VOTOR_POOL_ERR_SLOT_TOO_NEW );
    1730             : 
    1731           0 :   slot_state_t * state = slot_state( self, slot );
    1732           0 :   int duplicate = 0;
    1733           0 :   switch( cert->kind ) {
    1734           0 :   case FD_CERT_KIND_FINAL:          duplicate = state->certs.final.slot!=ULONG_MAX;                                break;
    1735           0 :   case FD_CERT_KIND_FAST_FINAL:     duplicate = state->certs.fast_final.slot!=ULONG_MAX;                           break;
    1736           0 :   case FD_CERT_KIND_NOTAR:          duplicate = state->certs.notar.slot!=ULONG_MAX;                                break;
    1737           0 :   case FD_CERT_KIND_NOTAR_FALLBACK: duplicate = slot_state_is_notar_fallback( state, fd_cert_block_hash( cert ) ); break;
    1738           0 :   case FD_CERT_KIND_SKIP:           duplicate = state->certs.skip.slot!=ULONG_MAX;                                 break;
    1739           0 :   default:                          FD_LOG_CRIT(( "unreachable" ));
    1740           0 :   }
    1741           0 :   if( FD_UNLIKELY( duplicate ) ) return FD_VOTOR_POOL_ERR_DUPLICATE;
    1742             : 
    1743           0 :   int err = FD_CERT_VERIFY_SUCCESS;
    1744           0 :   if( FD_LIKELY( !verified ) ) err = fd_cert_verify( cert, epoch_info );
    1745           0 :   self->slot_states->bls_verify_cnt += (ulong)( !verified && err!=FD_CERT_VERIFY_ERR_THRESHOLD );
    1746           0 :   if( FD_UNLIKELY( err ) ) return FD_VOTOR_POOL_ERR_CERT_VERIFY;
    1747             : 
    1748           0 :   switch( cert->kind ) { /* a skip cert excludes finalization certs, Lemmas 23 and 28 */
    1749           0 :   case FD_CERT_KIND_FINAL:
    1750           0 :   case FD_CERT_KIND_FAST_FINAL:     FD_CHECK_CRIT( state->certs.skip.slot==ULONG_MAX, "consensus safety violation" );                                                   break;
    1751           0 :   case FD_CERT_KIND_NOTAR:
    1752           0 :   case FD_CERT_KIND_NOTAR_FALLBACK:                                                                                                                                     break;
    1753           0 :   case FD_CERT_KIND_SKIP:           FD_CHECK_CRIT( state->certs.final.slot==ULONG_MAX && state->certs.fast_final.slot==ULONG_MAX, "consensus safety violation" ); break;
    1754           0 :   default:                          FD_LOG_CRIT(( "unreachable" ));
    1755           0 :   }
    1756             : 
    1757           0 :   add_verified_cert( self, cert, bad );
    1758           0 :   return FD_VOTOR_POOL_SUCCESS;
    1759           0 : }
    1760             : 
    1761             : int
    1762             : fd_votor_pool_add_vote( fd_votor_pool_t * self,
    1763             :                         fd_vote_t const * vote,
    1764             :                         int               verified,
    1765             :                         fd_bls_set_t *    bad,
    1766           0 :                         uchar *           quorum_reached ) {
    1767           0 :   ulong slot = fd_vote_slot( vote );
    1768           0 :   fd_bls_set_null( bad );
    1769           0 :   *quorum_reached = 0;
    1770             : 
    1771           0 :   ulong first_unpruned_slot = fd_finality_tracker_first_unpruned_slot( self->finality_tracker );
    1772           0 :   ulong retained_slot       = fd_ulong_sat_sub( first_unpruned_slot, FD_VOTOR_REWARD_SLOT_DELTA );
    1773           0 :   ulong slot_far_in_future  = first_unpruned_slot + self->slot_max - FD_VOTOR_REWARD_SLOT_DELTA;
    1774           0 :   if( FD_UNLIKELY( slot<retained_slot       ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_OLD;
    1775           0 :   if( FD_UNLIKELY( slot>=slot_far_in_future ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_NEW;
    1776           0 :   slot_states_t *         states     = self->slot_states;
    1777           0 :   fd_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 ) );
    1778           0 :   if( FD_UNLIKELY( !epoch_info ) ) return fd_int_if( slot<states->curr_epoch_slot, FD_VOTOR_POOL_ERR_SLOT_TOO_OLD, FD_VOTOR_POOL_ERR_SLOT_TOO_NEW );
    1779             : 
    1780           0 :   ulong          voter       = fd_vote_rank( vote );
    1781           0 :   slot_state_t * slot_state_ = slot_state( self, slot );
    1782           0 :   ulong          voter_stake = fd_epoch_info_validator( epoch_info, voter )->stake;
    1783             : 
    1784           0 :   if       ( FD_UNLIKELY( slot_state_check_slashable_offence( slot_state_, states, vote )!=SLASHABLE_OFFENCE_NONE ) ) {
    1785           0 :     return FD_VOTOR_POOL_ERR_SLASHABLE;
    1786           0 :   } else if( FD_UNLIKELY( slot_state_should_ignore_vote( slot_state_, states, vote ) ) ) {
    1787           0 :     return FD_VOTOR_POOL_ERR_DUPLICATE;
    1788           0 :   }
    1789           0 :   if( FD_UNLIKELY( !verified && !fd_bls_sig_validate( fd_vote_sig( vote ) ) ) ) return FD_VOTOR_POOL_ERR_VOTE_VERIFY;
    1790             : 
    1791           0 :   fd_cert_t     cert_events  [ SLOT_STATE_OUT_CERT_MAX   ]; ulong cert_event_cnt;
    1792           0 :   fd_block_id_t safe_to_notar[ SLOT_STATE_OUT_EVENT_MAX  ]; ulong safe_to_notar_cnt;
    1793           0 :   ulong         safe_to_skip [ SLOT_STATE_OUT_EVENT_MAX  ]; ulong safe_to_skip_cnt;
    1794           0 :   fd_block_id_t repair_events[ SLOT_STATE_OUT_REPAIR_MAX ]; ulong repair_event_cnt;
    1795           0 :   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 );
    1796             : 
    1797           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 ); }
    1798           0 :   for( ulong i=0UL; i<safe_to_notar_cnt; i++ ) { pool_events_push  ( self->pool_events,   (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = safe_to_notar[i] } ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, FD_VOTOR_POOL_QUORUM_REACHED_SAFE_TO_NOTAR ); }
    1799           0 :   for( ulong i=0UL; i<safe_to_skip_cnt;  i++ ) { pool_events_push  ( self->pool_events,   (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_SAFE_TO_SKIP,  .safe_to_skip  = safe_to_skip [i] } ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, FD_VOTOR_POOL_QUORUM_REACHED_SAFE_TO_SKIP  ); }
    1800           0 :   for( ulong i=0UL; i<repair_event_cnt;  i++ ) { repair_events_push( self->repair_events, repair_events[i] ); }
    1801           0 :   return FD_VOTOR_POOL_SUCCESS;
    1802           0 : }
    1803             : 
    1804             : fd_block_hash_t const *
    1805             : fd_votor_pool_agg_rewards( fd_votor_pool_t * self,
    1806             :                            ulong             slot,
    1807             :                            fd_bls_agg_t *    notar,
    1808             :                            fd_bls_agg_t *    skip,
    1809           0 :                            fd_bls_set_t *    bad ) {
    1810           0 :   fd_bls_agg_null( notar );
    1811           0 :   fd_bls_agg_null( skip );
    1812           0 :   fd_bls_set_null( bad );
    1813           0 :   slot_state_t const * state = slot_state_query( self, slot );
    1814           0 :   if( FD_UNLIKELY( !state ) ) return NULL;
    1815             : 
    1816           0 :   slot_states_t *         states     = self->slot_states;
    1817           0 :   fd_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 ) );
    1818           0 :   fd_block_hash_t const * hash       = NULL;
    1819           0 :   fd_bls_sig_t            sigs    [ FD_VOTOR_VAT_MAX        ];
    1820           0 :   fd_bls_set_t            skip_bad[ fd_bls_set_word_cnt     ];
    1821           0 :   uchar                   msg     [ FD_VOTE_SIGNING_SER_MAX ];
    1822           0 :   ulong                   msg_sz;
    1823             : 
    1824           0 :   if( FD_LIKELY( slot_state_notar_reward_agg( state, states, notar ) ) ) {
    1825           0 :     hash   = &state->votes.top_notar_hash;
    1826           0 :     slot_state_sigs( state, states, FD_VOTE_KIND_NOTAR, hash, notar->set, sigs );
    1827           0 :     msg_sz = fd_vote_signing_ser( FD_VOTE_KIND_NOTAR, slot, hash, state->shred_version, msg );
    1828           0 :     states->bls_verify_cnt++;
    1829           0 :     if( FD_UNLIKELY( fd_bls_agg_verify_subtract( notar, msg, msg_sz, epoch_info->pubkeys, sigs, bad ) ) ) { fd_bls_agg_null( notar ); hash = NULL; }
    1830           0 :   }
    1831             : 
    1832           0 :   fd_bls_agg_t const * skip_agg = slot_state_skip_reward_agg( state );
    1833           0 :   if( FD_LIKELY( skip_agg ) ) {
    1834           0 :     *skip  = *skip_agg;
    1835           0 :     slot_state_sigs( state, states, FD_VOTE_KIND_SKIP, NULL, skip->set, sigs );
    1836           0 :     msg_sz = fd_vote_signing_ser( FD_VOTE_KIND_SKIP, slot, NULL, state->shred_version, msg );
    1837           0 :     states->bls_verify_cnt++;
    1838           0 :     if( FD_UNLIKELY( fd_bls_agg_verify_subtract( skip, msg, msg_sz, epoch_info->pubkeys, sigs, skip_bad ) ) ) fd_bls_agg_null( skip );
    1839           0 :     fd_bls_set_union( bad, bad, skip_bad );
    1840           0 :   }
    1841           0 :   return hash;
    1842           0 : }
    1843             : 
    1844             : fd_cert_final_t *
    1845             : fd_votor_pool_final_cert( fd_votor_pool_t const * self,
    1846             :                           ulong                   slot,
    1847           0 :                           fd_cert_final_t *       out ) {
    1848           0 :   slot_state_t const * state = slot_state_query( self, slot );
    1849           0 :   if( FD_UNLIKELY( !state || state->certs.final.slot==ULONG_MAX ) ) return NULL;
    1850           0 :   *out = state->certs.final;
    1851           0 :   return out;
    1852           0 : }
    1853             : 
    1854             : fd_cert_notar_t *
    1855             : fd_votor_pool_notar_cert( fd_votor_pool_t const * self,
    1856             :                           ulong                   slot,
    1857           0 :                           fd_cert_notar_t *       out ) {
    1858           0 :   slot_state_t const * state = slot_state_query( self, slot );
    1859           0 :   if( FD_UNLIKELY( !state || state->certs.notar.slot==ULONG_MAX ) ) return NULL;
    1860           0 :   *out = state->certs.notar;
    1861           0 :   return out;
    1862           0 : }
    1863             : 
    1864             : fd_votor_pool_standstill_iter_t *
    1865             : fd_votor_pool_standstill_iter_init( fd_votor_pool_standstill_iter_t * iter,
    1866           0 :                                     fd_votor_pool_t const *           self ) {
    1867           0 :   iter->finalized = fd_votor_pool_finalized_slot( self );
    1868           0 :   iter->slot      = iter->finalized;
    1869           0 :   iter->idx       = 0UL;
    1870           0 :   return fd_votor_pool_standstill_iter_next( iter, self );
    1871           0 : }
    1872             : 
    1873             : fd_votor_pool_standstill_iter_t *
    1874             : fd_votor_pool_standstill_iter_next( fd_votor_pool_standstill_iter_t * iter,
    1875           0 :                                     fd_votor_pool_t const *           self ) {
    1876           0 :   ulong slot_far_in_future = fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - FD_VOTOR_REWARD_SLOT_DELTA;
    1877           0 :   for( ; iter->slot<slot_far_in_future; iter->slot++, iter->idx = 0UL ) {
    1878           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 );
    1879           0 :     if( FD_UNLIKELY( !slot_state_ ) ) continue;
    1880             : 
    1881           0 :     slot_certs_t const * certs         = &slot_state_->slot_state.certs;
    1882           0 :     slot_votes_t const * votes         = &slot_state_->slot_state.votes;
    1883           0 :     ulong                slot          = slot_state_->slot_state.slot;
    1884           0 :     ulong                own_rank      = slot_state_->slot_state.own_rank;
    1885           0 :     ushort               shred_version = slot_state_->slot_state.shred_version;
    1886           0 :     ulong                idx           = iter->idx;
    1887           0 :     int                  finalized     = slot==iter->finalized;
    1888           0 :     int                  fast_final    = certs->fast_final.slot!=ULONG_MAX;
    1889           0 :     int                  final         = certs->final.slot!=ULONG_MAX && ( !finalized || ( !fast_final && certs->notar.slot!=ULONG_MAX ) );
    1890           0 :     int                  notar         = certs->notar.slot   !=ULONG_MAX && ( !finalized || final );
    1891           0 :     ulong                nf_cert       = fd_ulong_max( idx, STANDSTILL_NOTAR_FALLBACK      ) - STANDSTILL_NOTAR_FALLBACK;
    1892           0 :     ulong                nf_vote       = fd_ulong_max( idx, STANDSTILL_NOTAR_FALLBACK_VOTE ) - STANDSTILL_NOTAR_FALLBACK_VOTE;
    1893             : 
    1894           0 :     iter->is_vote = 0;
    1895           0 :     if( FD_UNLIKELY( idx<=STANDSTILL_FINAL      && final      ) ) { iter->idx = STANDSTILL_FINAL+1UL;      iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_FINAL,      .final      = certs->final      }; return iter; }
    1896           0 :     if( FD_UNLIKELY( idx<=STANDSTILL_FAST_FINAL && fast_final ) ) { iter->idx = STANDSTILL_FAST_FINAL+1UL; iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_FAST_FINAL, .fast_final = certs->fast_final }; return iter; }
    1897           0 :     if( FD_LIKELY  ( idx<=STANDSTILL_NOTAR      && notar      ) ) { iter->idx = STANDSTILL_NOTAR+1UL;      iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_NOTAR,      .notar      = certs->notar         }; return iter; }
    1898           0 :     if( FD_UNLIKELY( finalized ) ) continue;
    1899           0 :     if( FD_UNLIKELY( nf_cert<certs->notar_fallback_cnt ) ) { iter->idx = STANDSTILL_NOTAR_FALLBACK+nf_cert+1UL; iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = certs->notar_fallback[ nf_cert ] }; return iter; }
    1900           0 :     if( FD_UNLIKELY( idx<=STANDSTILL_SKIP && certs->skip.slot!=ULONG_MAX ) ) { iter->idx = STANDSTILL_SKIP+1UL; iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_SKIP, .skip = certs->skip }; return iter; }
    1901             : 
    1902           0 :     if( FD_UNLIKELY( own_rank==USHORT_MAX ) ) continue; /* unstaked */
    1903           0 :     iter->is_vote = 1;
    1904           0 :     if( FD_UNLIKELY( idx<=STANDSTILL_NOTAR_VOTE && fd_bls_set_test( votes->notar_set, own_rank ) ) ) {
    1905           0 :       iter->idx  = STANDSTILL_NOTAR_VOTE+1UL;
    1906           0 :       iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_NOTAR, .notar = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->notar_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } };
    1907           0 :       memcpy( &iter->vote.notar.block_hash, &vote_stake_pool_ele_const( self->slot_states->notar_hash_stake_pool, votes->notar_hash_stake_idx[ own_rank ] )->block_id.hash, sizeof(fd_block_hash_t) );
    1908           0 :       return iter;
    1909           0 :     }
    1910           0 :     if( FD_LIKELY  ( idx<=STANDSTILL_FINAL_VOTE && fd_bls_set_test( votes->final_agg.set, own_rank ) ) ) { iter->idx = STANDSTILL_FINAL_VOTE+1UL; iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_FINAL, .final = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->final_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
    1911           0 :     if( FD_UNLIKELY( idx<=STANDSTILL_SKIP_VOTE  && fd_bls_set_test( votes->skip_agg.set,  own_rank ) ) ) { iter->idx = STANDSTILL_SKIP_VOTE+1UL;  iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_SKIP,  .skip  = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->skip_sig_idx [ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
    1912           0 :     if( FD_UNLIKELY( nf_vote<votes->notar_fallback_sig_cnt[ own_rank ] ) ) {
    1913           0 :       iter->idx  = STANDSTILL_NOTAR_FALLBACK_VOTE+nf_vote+1UL;
    1914           0 :       iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_NOTAR_FALLBACK, .notar_fallback = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->notar_fallback_sig_idx[ own_rank ][ nf_vote ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } };
    1915           0 :       memcpy( &iter->vote.notar_fallback.block_hash, &vote_stake_pool_ele_const( self->slot_states->notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ own_rank ][ nf_vote ] )->block_id.hash, sizeof(fd_block_hash_t) );
    1916           0 :       return iter;
    1917           0 :     }
    1918           0 :     if( FD_UNLIKELY( idx<=STANDSTILL_SKIP_FALLBACK_VOTE && fd_bls_set_test( votes->skip_fallback_agg.set, own_rank ) ) ) { iter->idx = STANDSTILL_SKIP_FALLBACK_VOTE+1UL; iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_SKIP_FALLBACK, .skip_fallback = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->skip_fallback_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
    1919           0 :   }
    1920           0 :   iter->slot = ULONG_MAX;
    1921           0 :   return iter;
    1922           0 : }
    1923             : 
    1924             : FD_FN_PURE int
    1925           0 : fd_votor_pool_standstill_iter_done( fd_votor_pool_standstill_iter_t const * iter ) {
    1926           0 :   return iter->slot==ULONG_MAX;
    1927           0 : }
    1928             : 
    1929             : FD_FN_PURE ulong
    1930           0 : fd_votor_pool_finalized_slot( fd_votor_pool_t const * self ) {
    1931           0 :   return fd_finality_tracker_highest_finalized_slot( self->finality_tracker );
    1932           0 : }
    1933             : 
    1934             : FD_FN_PURE ulong
    1935           0 : fd_votor_pool_highest_parent_ready( fd_votor_pool_t const * self ) {
    1936           0 :   return self->parent_ready_tracker->highest_ready;
    1937           0 : }
    1938             : 
    1939             : FD_FN_PURE fd_block_hash_t const *
    1940           0 : fd_votor_pool_finalized_block_hash( fd_votor_pool_t const * self ) {
    1941           0 :   return fd_finality_tracker_highest_finalized_block_hash( self->finality_tracker );
    1942           0 : }
    1943             : 
    1944             : ulong
    1945             : fd_votor_pool_parents_ready( fd_votor_pool_t const * self,
    1946             :                              ulong                   slot,
    1947             :                              fd_block_id_t *         out,
    1948           0 :                              ulong                   out_max ) {
    1949           0 :   return fd_parent_ready_tracker_parents_ready( self->parent_ready_tracker, slot, out, out_max );
    1950           0 : }
    1951             : 
    1952             : FD_FN_PURE fd_block_id_t
    1953             : fd_votor_pool_wait_for_parent_ready( fd_votor_pool_t const * self,
    1954           0 :                                      ulong                   slot ) {
    1955           0 :   return fd_parent_ready_tracker_wait_for_parent_ready( self->parent_ready_tracker, slot );
    1956           0 : }
    1957             : 
    1958             : int
    1959             : fd_votor_pool_poll_pool_event( fd_votor_pool_t *       self,
    1960           0 :                                fd_votor_pool_event_t * event ) {
    1961           0 :   if( FD_LIKELY( pool_events_empty( self->pool_events ) ) ) return 0;
    1962           0 :   *event = pool_events_pop( self->pool_events );
    1963           0 :   if( FD_LIKELY( event->kind!=FD_VOTOR_POOL_EVENT_PARENT_READY ) ) return 1;
    1964           0 :   fd_parent_ready_state_t * state = fd_parent_ready_state_map_ele_query( self->parent_ready_tracker->states.map, &event->parent_ready.slot, NULL, self->parent_ready_tracker->states.pool );
    1965           0 :   if( FD_LIKELY( state ) ) state->ready = 0; /* NULL if pruned while queued */
    1966           0 :   return 1;
    1967           0 : }
    1968             : 
    1969             : int
    1970             : fd_votor_pool_poll_repair_event( fd_votor_pool_t * self,
    1971           0 :                                  fd_block_id_t *   block ) {
    1972           0 :   if( FD_LIKELY( repair_events_empty( self->repair_events ) ) ) return 0;
    1973           0 :   *block = repair_events_pop( self->repair_events );
    1974           0 :   return 1;
    1975           0 : }

Generated by: LCOV version 1.14