LCOV - code coverage report
Current view: top level - flamenco/leaders - fd_leaders.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 111 124 89.5 %
Date: 2026-03-24 05:48:28 Functions: 6 7 85.7 %

          Line data    Source code
       1             : #include "fd_leaders.h"
       2             : #include "../../ballet/chacha/fd_chacha_rng.h"
       3             : #include "../../ballet/wsample/fd_wsample.h"
       4             : 
       5             : #define SORT_NAME sort_vote_weights_by_stake_id
       6    71960895 : #define SORT_KEY_T fd_vote_stake_weight_t
       7   114719655 : #define SORT_BEFORE(a,b) ((a).stake > (b).stake ? 1 : ((a).stake < (b).stake ? 0 : memcmp( (a).id_key.uc, (b).id_key.uc, 32UL )>0))
       8             : #include "../../util/tmpl/fd_sort.c"
       9             : 
      10             : #define SORT_NAME sort_vote_weights_by_id
      11    68508501 : #define SORT_KEY_T fd_vote_stake_weight_t
      12   110332995 : #define SORT_BEFORE(a,b) (memcmp( (a).id_key.uc, (b).id_key.uc, 32UL )>0)
      13             : #include "../../util/tmpl/fd_sort.c"
      14             : 
      15             : #define SORT_NAME sort_weights_by_stake_id
      16    46274751 : #define SORT_KEY_T fd_stake_weight_t
      17    74540490 : #define SORT_BEFORE(a,b) ((a).stake > (b).stake ? 1 : ((a).stake < (b).stake ? 0 : memcmp( (a).key.uc, (b).key.uc, 32UL )>0))
      18             : #include "../../util/tmpl/fd_sort.c"
      19             : 
      20             : #define SORT_NAME sort_weights_by_id
      21    43028889 : #define SORT_KEY_T fd_stake_weight_t
      22    70222491 : #define SORT_BEFORE(a,b) (memcmp( (a).key.uc, (b).key.uc, 32UL )>0)
      23             : #include "../../util/tmpl/fd_sort.c"
      24             : 
      25             : ulong
      26             : compute_id_weights_from_vote_weights( fd_stake_weight_t *            stake_weight,
      27             :                                       fd_vote_stake_weight_t const * vote_stake_weight,
      28         234 :                                       ulong                          staked_cnt ) {
      29             : 
      30             :   /* Copy from input message [(vote, id, stake)] into old format [(id, stake)]. */
      31         234 :   ulong idx = 0UL;
      32     2713563 :   for( ulong i=0UL; i<staked_cnt; i++ ) {
      33     2713329 :     memcpy( stake_weight[ idx ].key.uc, vote_stake_weight[ i ].id_key.uc, sizeof(fd_pubkey_t) );
      34     2713329 :     stake_weight[ idx ].stake = vote_stake_weight[ i ].stake;
      35     2713329 :     idx++;
      36     2713329 :   }
      37             : 
      38             :   /* Sort [(id, stake)] by id, so we can dedup */
      39         234 :   sort_weights_by_id_inplace( stake_weight, idx );
      40             : 
      41             :   /* Dedup entries, aggregating stake */
      42         234 :   ulong j=0UL;
      43     2713329 :   for( ulong i=1UL; i<idx; i++ ) {
      44     2713095 :     fd_pubkey_t * pre = &stake_weight[ j ].key;
      45     2713095 :     fd_pubkey_t * cur = &stake_weight[ i ].key;
      46     2713095 :     if( 0==memcmp( pre, cur, sizeof(fd_pubkey_t) ) ) {
      47          60 :       stake_weight[ j ].stake += stake_weight[ i ].stake;
      48     2713035 :     } else {
      49     2713035 :       ++j;
      50     2713035 :       stake_weight[ j ].stake = stake_weight[ i ].stake;
      51     2713035 :       memcpy( stake_weight[ j ].key.uc, stake_weight[ i ].key.uc, sizeof(fd_pubkey_t) );
      52     2713035 :     }
      53     2713095 :   }
      54         234 :   ulong staked_cnt_by_id = fd_ulong_min( idx, j+1 );
      55             : 
      56             :   /* Sort [(id, stake)] by stake then id, as expected */
      57         234 :   sort_weights_by_stake_id_inplace( stake_weight, staked_cnt_by_id );
      58             : 
      59         234 :   return staked_cnt_by_id;
      60         234 : }
      61             : 
      62             : ulong
      63           0 : fd_epoch_leaders_align( void ) {
      64           0 :   return FD_EPOCH_LEADERS_ALIGN;
      65           0 : }
      66             : 
      67             : FD_FN_CONST ulong
      68             : fd_epoch_leaders_footprint( ulong pub_cnt,
      69       13437 :                             ulong slot_cnt ) {
      70       13437 :   if( FD_UNLIKELY( ( pub_cnt  ==     0UL     )
      71       13437 :                  | ( pub_cnt   >UINT_MAX-3UL )
      72       13437 :                  | ( slot_cnt==     0UL  ) ) )
      73           0 :     return 0UL;
      74       13437 :   return FD_EPOCH_LEADERS_FOOTPRINT( pub_cnt, slot_cnt );
      75       13437 : }
      76             : 
      77             : void *
      78             : fd_epoch_leaders_new( void  *                  shmem,
      79             :                       ulong                    epoch,
      80             :                       ulong                    slot0,
      81             :                       ulong                    slot_cnt,
      82             :                       ulong                    pub_cnt,
      83             :                       fd_vote_stake_weight_t * stakes,
      84             :                       ulong                    excluded_stake,
      85        6711 :                       ulong                    vote_keyed_lsched ) {
      86        6711 :   if( FD_UNLIKELY( !shmem ) ) {
      87           0 :     FD_LOG_WARNING(( "NULL shmem" ));
      88           0 :     return NULL;
      89           0 :   }
      90             : 
      91        6711 :   ulong laddr = (ulong)shmem;
      92        6711 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( laddr, FD_EPOCH_LEADERS_ALIGN ) ) ) {
      93           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
      94           0 :     return NULL;
      95           0 :   }
      96             : 
      97        6711 :   if( FD_UNLIKELY( !pub_cnt ) ) {
      98           0 :     FD_LOG_WARNING(( "pub_cnt is 0" ));
      99           0 :     return NULL;
     100           0 :   }
     101             : 
     102             :   /* This code can be be removed when enable_vote_address_leader_schedule is
     103             :      enabled and cleared.
     104             :      And, as a consequence, stakes can be made const. */
     105        6711 :   if( FD_LIKELY( vote_keyed_lsched==0 ) ) {
     106             :     /* Sort [(vote, id, stake)] by id, so we can dedup */
     107        6699 :     sort_vote_weights_by_id_inplace( stakes, pub_cnt );
     108             : 
     109             :     /* Dedup entries, aggregating stake */
     110        6699 :     ulong j=0UL;
     111     4568559 :     for( ulong i=1UL; i<pub_cnt; i++ ) {
     112     4561860 :       fd_pubkey_t * pre = &stakes[ j ].id_key;
     113     4561860 :       fd_pubkey_t * cur = &stakes[ i ].id_key;
     114     4561860 :       if( 0==memcmp( pre, cur, sizeof(fd_pubkey_t) ) ) {
     115          60 :         stakes[ j ].stake += stakes[ i ].stake;
     116     4561800 :       } else {
     117     4561800 :         ++j;
     118     4561800 :         stakes[ j ].stake = stakes[ i ].stake;
     119     4561800 :         memcpy( stakes[ j ].id_key.uc, stakes[ i ].id_key.uc, sizeof(fd_pubkey_t) );
     120             :         /* vote doesn't matter */
     121     4561800 :       }
     122     4561860 :     }
     123        6699 :     pub_cnt = fd_ulong_min( pub_cnt, j+1 );
     124             : 
     125             :     /* Sort [(vote, id, stake)] by stake then id, as expected */
     126        6699 :     sort_vote_weights_by_stake_id_inplace( stakes, pub_cnt );
     127        6699 :   }
     128             : 
     129             :   /* The eventual layout that we want is:
     130             :      struct                   (align=8, footprint=48)
     131             :      list of indices          (align=4, footprint=4*ceil(slot_cnt/4))
     132             :      (up to 60 bytes of padding to align to 64)
     133             :      list of pubkeys          (align=32, footprint=32*pub_cnt)
     134             :      the indeterminate pubkey (align=32, footprint=32)
     135             :      leader membership bitset (align=8, footprint=8*ceil((pub_cnt+1)/64))
     136             :      (possibly 32 bytes of padding to align to 64)
     137             : 
     138             :      but in order to generate the list of indices, we want to use
     139             :      wsample, which needs some memory to work.  Turns out that we
     140             :      probably have all the memory we need right here in shmem, we just
     141             :      need to be careful about how we use it; for most of the values of
     142             :      pub_cnt we care about, wsample's footprint is less than 32*pub_cnt.
     143             : 
     144             :      This works out because we can delay copying the pubkeys until we're
     145             :      done with the wsample object.  There's a lot of type punning going
     146             :      on here, so watch out. */
     147        6711 :   ulong sched_cnt = (slot_cnt+FD_EPOCH_SLOTS_PER_ROTATION-1UL)/FD_EPOCH_SLOTS_PER_ROTATION;
     148             : 
     149        6711 :   ulong leader_bits_word_cnt = FD_EPOCH_LEADERS_BITSET_WORD_CNT( pub_cnt );
     150             : 
     151        6711 :   fd_epoch_leaders_t * leaders = (fd_epoch_leaders_t *)fd_type_pun( (void *)laddr );
     152        6711 :   laddr += sizeof(fd_epoch_leaders_t);
     153             : 
     154        6711 :   laddr  = fd_ulong_align_up( laddr, alignof(uint) );
     155        6711 :   uint * sched     = (uint *)fd_type_pun( (void *)laddr );
     156        6711 :   laddr += sizeof(uint)*sched_cnt;
     157             : 
     158        6711 :   laddr  = fd_ulong_align_up( laddr, fd_ulong_max( sizeof(fd_pubkey_t), FD_WSAMPLE_ALIGN ) );
     159             :   /* These two alias, like a union.  We don't need pubkeys until we're
     160             :      done with wsample. */
     161        6711 :   void        * wsample_mem = (void        *)fd_type_pun( (void *)laddr );
     162        6711 :   fd_pubkey_t * pubkeys     = (fd_pubkey_t *)fd_type_pun( (void *)laddr );
     163             : 
     164        6711 :   FD_TEST( laddr+fd_wsample_footprint( pub_cnt, 0 )<=(ulong)wsample_mem + fd_epoch_leaders_footprint( pub_cnt, slot_cnt ) );
     165             : 
     166             :   /* Create and seed ChaCha20Rng */
     167        6711 :   fd_chacha_rng_t _rng[1];
     168        6711 :   fd_chacha_rng_t * rng = fd_chacha_rng_join( fd_chacha_rng_new( _rng, FD_CHACHA_RNG_MODE_MOD ) );
     169        6711 :   uchar key[ 32 ] = {0};
     170        6711 :   memcpy( key, &epoch, sizeof(ulong) );
     171        6711 :   fd_chacha_rng_init( rng, key, FD_CHACHA_RNG_ALGO_CHACHA20 );
     172             : 
     173        6711 :   void * _wsample = fd_wsample_new_init( wsample_mem, rng, pub_cnt, 0, FD_WSAMPLE_HINT_POWERLAW_NOREMOVE );
     174     4575234 :   for( ulong i=0UL; i<pub_cnt; i++ ) _wsample = fd_wsample_new_add( _wsample, stakes[i].stake );
     175        6711 :   fd_wsample_t * wsample = fd_wsample_join( fd_wsample_new_fini( _wsample, excluded_stake ) );
     176        6711 :   FD_TEST( wsample );
     177             : 
     178             :   /* Generate samples.  We need uints, so we can't use sample_many.  Map
     179             :      any FD_WSAMPLE_INDETERMINATE values to pub_cnt. */
     180     7396803 :   for( ulong i=0UL; i<sched_cnt; i++ ) sched[ i ] = (uint)fd_ulong_min( fd_wsample_sample( wsample ), pub_cnt );
     181             : 
     182             :   /* Clean up the wsample object */
     183        6711 :   fd_wsample_delete( fd_wsample_leave( wsample ) );
     184        6711 :   fd_chacha_rng_delete( fd_chacha_rng_leave( rng ) );
     185             : 
     186             :   /* Now we can use the space for the pubkeys */
     187     4575234 :   for( ulong i=0UL; i<pub_cnt; i++ ) memcpy( pubkeys+i, &stakes[ i ].id_key, 32UL );
     188             : 
     189             :   /* copy indeterminate leader to the last spot */
     190        6711 :   static const uchar fd_indeterminate_leader[32] = { FD_INDETERMINATE_LEADER };
     191        6711 :   memcpy( pubkeys+pub_cnt, fd_indeterminate_leader, 32UL );
     192             : 
     193        6711 :   ulong leader_bits_laddr = fd_ulong_align_up( (ulong)(pubkeys+pub_cnt+1UL), alignof(ulong) );
     194        6711 :   ulong * leader_bits = (ulong *)fd_type_pun( (void *)leader_bits_laddr );
     195             : 
     196        6711 :   FD_TEST( leader_bits_laddr + leader_bits_word_cnt*sizeof(ulong) <= (ulong)shmem + fd_epoch_leaders_footprint( pub_cnt, slot_cnt ) );
     197       81753 :   for( ulong i=0UL; i<leader_bits_word_cnt; i++ ) leader_bits[i] = 0UL;
     198     7396803 :   for( ulong i=0UL; i<sched_cnt; i++ ) leader_bits[ sched[i]>>6 ] |= (1UL<<(sched[i]&63UL));
     199             : 
     200             :   /* Construct the final struct */
     201        6711 :   leaders->epoch                = epoch;
     202        6711 :   leaders->slot0                = slot0;
     203        6711 :   leaders->slot_cnt             = slot_cnt;
     204        6711 :   leaders->pub                  = pubkeys;
     205        6711 :   leaders->pub_cnt              = pub_cnt;
     206        6711 :   leaders->sched                = sched;
     207        6711 :   leaders->sched_cnt            = sched_cnt;
     208        6711 :   leaders->leader_bits          = leader_bits;
     209        6711 :   leaders->leader_bits_word_cnt = leader_bits_word_cnt;
     210             : 
     211        6711 :   return (void *)shmem;
     212        6711 : }
     213             : 
     214             : fd_epoch_leaders_t *
     215        6711 : fd_epoch_leaders_join( void * shleaders ) {
     216        6711 :   return (fd_epoch_leaders_t *)shleaders;
     217        6711 : }
     218             : 
     219             : void *
     220        6366 : fd_epoch_leaders_leave( fd_epoch_leaders_t * leaders ) {
     221        6366 :   return (void *)leaders;
     222        6366 : }
     223             : 
     224             : void *
     225        6366 : fd_epoch_leaders_delete( void * shleaders ) {
     226        6366 :   return shleaders;
     227        6366 : }

Generated by: LCOV version 1.14