LCOV - code coverage report
Current view: top level - flamenco/leaders - fd_leaders.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 74 84 88.1 %
Date: 2025-08-07 04:53:16 Functions: 5 6 83.3 %

          Line data    Source code
       1             : #include "fd_leaders.h"
       2             : #include "../../ballet/chacha20/fd_chacha20rng.h"
       3             : #include "../../ballet/wsample/fd_wsample.h"
       4             : 
       5             : #define SORT_NAME sort_vote_weights_by_stake_id
       6    16194411 : #define SORT_KEY_T fd_vote_stake_weight_t
       7    24863502 : #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    15805968 : #define SORT_KEY_T fd_vote_stake_weight_t
      12    24197487 : #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             : ulong
      16           0 : fd_epoch_leaders_align( void ) {
      17           0 :   return FD_EPOCH_LEADERS_ALIGN;
      18           0 : }
      19             : 
      20             : FD_FN_CONST ulong
      21             : fd_epoch_leaders_footprint( ulong pub_cnt,
      22        6351 :                             ulong slot_cnt ) {
      23        6351 :   if( FD_UNLIKELY( ( pub_cnt  ==     0UL     )
      24        6351 :                  | ( pub_cnt   >UINT_MAX-3UL )
      25        6351 :                  | ( slot_cnt==     0UL  ) ) )
      26           0 :     return 0UL;
      27        6351 :   return FD_EPOCH_LEADERS_FOOTPRINT( pub_cnt, slot_cnt );
      28        6351 : }
      29             : 
      30             : void *
      31             : fd_epoch_leaders_new( void  *                  shmem,
      32             :                       ulong                    epoch,
      33             :                       ulong                    slot0,
      34             :                       ulong                    slot_cnt,
      35             :                       ulong                    pub_cnt,
      36             :                       fd_vote_stake_weight_t * stakes,
      37             :                       ulong                    excluded_stake,
      38        6336 :                       ulong                    vote_keyed_lsched ) {
      39        6336 :   (void)vote_keyed_lsched;
      40        6336 :   if( FD_UNLIKELY( !shmem ) ) {
      41           0 :     FD_LOG_WARNING(( "NULL shmem" ));
      42           0 :     return NULL;
      43           0 :   }
      44             : 
      45        6336 :   ulong laddr = (ulong)shmem;
      46        6336 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( laddr, FD_EPOCH_LEADERS_ALIGN ) ) ) {
      47           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
      48           0 :     return NULL;
      49           0 :   }
      50             : 
      51             :   /* This code can be be removed when enable_vote_address_leader_schedule is
      52             :      enabled and cleared.
      53             :      And, as a consequence, stakes can be made const. */
      54        6336 :   if( FD_LIKELY( vote_keyed_lsched==0 ) ) {
      55             :     /* Sort [(vote, id, stake)] by id, so we can dedup */
      56        6330 :     sort_vote_weights_by_id_inplace( stakes, pub_cnt );
      57             : 
      58             :     /* Dedup entries, aggregating stake */
      59        6330 :     ulong j=0UL;
      60     1320012 :     for( ulong i=1UL; i<pub_cnt; i++ ) {
      61     1313682 :       fd_pubkey_t * pre = &stakes[ j ].id_key;
      62     1313682 :       fd_pubkey_t * cur = &stakes[ i ].id_key;
      63     1313682 :       if( 0==memcmp( pre, cur, sizeof(fd_pubkey_t) ) ) {
      64          30 :         stakes[ j ].stake += stakes[ i ].stake;
      65     1313652 :       } else {
      66     1313652 :         ++j;
      67     1313652 :         stakes[ j ].stake = stakes[ i ].stake;
      68     1313652 :         memcpy( stakes[ j ].id_key.uc, stakes[ i ].id_key.uc, sizeof(fd_pubkey_t) );
      69             :         /* vote doesn't matter */
      70     1313652 :       }
      71     1313682 :     }
      72        6330 :     pub_cnt = fd_ulong_min( pub_cnt, j+1 );
      73             : 
      74             :     /* Sort [(vote, id, stake)] by stake then id, as expected */
      75        6330 :     sort_vote_weights_by_stake_id_inplace( stakes, pub_cnt );
      76        6330 :   }
      77             : 
      78             :   /* The eventual layout that we want is:
      79             :      struct                   (align=8, footprint=48)
      80             :      list of indices          (align=4, footprint=4*ceil(slot_cnt/4))
      81             :      (up to 60 bytes of padding to align to 64)
      82             :      list of pubkeys          (align=32, footprint=32*pub_cnt)
      83             :      the indeterminate pubkey (align=32, footprint=32)
      84             :      (possibly 32 bytes of padding to align to 64)
      85             : 
      86             :      but in order to generate the list of indices, we want to use
      87             :      wsample, which needs some memory to work.  Turns out that we
      88             :      probably have all the memory we need right here in shmem, we just
      89             :      need to be careful about how we use it; for most of the values of
      90             :      pub_cnt we care about, wsample's footprint is less than 32*pub_cnt.
      91             : 
      92             :      This works out because we can delay copying the pubkeys until we're
      93             :      done with the wsample object.  There's a lot of type punning going
      94             :      on here, so watch out. */
      95        6336 :   ulong sched_cnt = (slot_cnt+FD_EPOCH_SLOTS_PER_ROTATION-1UL)/FD_EPOCH_SLOTS_PER_ROTATION;
      96             : 
      97        6336 :   fd_epoch_leaders_t * leaders = (fd_epoch_leaders_t *)fd_type_pun( (void *)laddr );
      98        6336 :   laddr += sizeof(fd_epoch_leaders_t);
      99             : 
     100        6336 :   laddr  = fd_ulong_align_up( laddr, alignof(uint) );
     101        6336 :   uint * sched     = (uint *)fd_type_pun( (void *)laddr );
     102        6336 :   laddr += sizeof(uint)*sched_cnt;
     103             : 
     104        6336 :   laddr  = fd_ulong_align_up( laddr, fd_ulong_max( sizeof(fd_pubkey_t), FD_WSAMPLE_ALIGN ) );
     105             :   /* These two alias, like a union.  We don't need pubkeys until we're
     106             :      done with wsample. */
     107        6336 :   void        * wsample_mem = (void        *)fd_type_pun( (void *)laddr );
     108        6336 :   fd_pubkey_t * pubkeys     = (fd_pubkey_t *)fd_type_pun( (void *)laddr );
     109             : 
     110        6336 :   FD_TEST( laddr+fd_wsample_footprint( pub_cnt, 0 )<=(ulong)wsample_mem + fd_epoch_leaders_footprint( pub_cnt, slot_cnt ) );
     111             : 
     112             :   /* Create and seed ChaCha20Rng */
     113        6336 :   fd_chacha20rng_t _rng[1];
     114        6336 :   fd_chacha20rng_t * rng = fd_chacha20rng_join( fd_chacha20rng_new( _rng, FD_CHACHA20RNG_MODE_MOD ) );
     115        6336 :   uchar key[ 32 ] = {0};
     116        6336 :   memcpy( key, &epoch, sizeof(ulong) );
     117        6336 :   fd_chacha20rng_init( rng, key );
     118             : 
     119        6336 :   void * _wsample = fd_wsample_new_init( wsample_mem, rng, pub_cnt, 0, FD_WSAMPLE_HINT_POWERLAW_NOREMOVE );
     120     1326330 :   for( ulong i=0UL; i<pub_cnt; i++ ) _wsample = fd_wsample_new_add( _wsample, stakes[i].stake );
     121        6336 :   fd_wsample_t * wsample = fd_wsample_join( fd_wsample_new_fini( _wsample, excluded_stake ) );
     122             : 
     123             :   /* Generate samples.  We need uints, so we can't use sample_many.  Map
     124             :      any FD_WSAMPLE_INDETERMINATE values to pub_cnt. */
     125      891462 :   for( ulong i=0UL; i<sched_cnt; i++ ) sched[ i ] = (uint)fd_ulong_min( fd_wsample_sample( wsample ), pub_cnt );
     126             : 
     127             :   /* Clean up the wsample object */
     128        6336 :   fd_wsample_delete( fd_wsample_leave( wsample ) );
     129        6336 :   fd_chacha20rng_delete( fd_chacha20rng_leave( rng ) );
     130             : 
     131             :   /* Now we can use the space for the pubkeys */
     132     1326330 :   for( ulong i=0UL; i<pub_cnt; i++ ) memcpy( pubkeys+i, &stakes[ i ].id_key, 32UL );
     133             : 
     134             :   /* copy indeterminate leader to the last spot */
     135        6336 :   static const uchar fd_indeterminate_leader[32] = { FD_INDETERMINATE_LEADER };
     136        6336 :   memcpy( pubkeys+pub_cnt, fd_indeterminate_leader, 32UL );
     137             : 
     138             :   /* Construct the final struct */
     139        6336 :   leaders->epoch     = epoch;
     140        6336 :   leaders->slot0     = slot0;
     141        6336 :   leaders->slot_cnt  = slot_cnt;
     142        6336 :   leaders->pub       = pubkeys;
     143        6336 :   leaders->pub_cnt   = pub_cnt;
     144        6336 :   leaders->sched     = sched;
     145        6336 :   leaders->sched_cnt = sched_cnt;
     146             : 
     147        6336 :   return (void *)shmem;
     148        6336 : }
     149             : 
     150             : fd_epoch_leaders_t *
     151        6336 : fd_epoch_leaders_join( void * shleaders ) {
     152        6336 :   return (fd_epoch_leaders_t *)shleaders;
     153        6336 : }
     154             : 
     155             : void *
     156        6207 : fd_epoch_leaders_leave( fd_epoch_leaders_t * leaders ) {
     157        6207 :   return (void *)leaders;
     158        6207 : }
     159             : 
     160             : void *
     161        6207 : fd_epoch_leaders_delete( void * shleaders ) {
     162        6207 :   return shleaders;
     163        6207 : }

Generated by: LCOV version 1.14