LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_vote_states.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 147 278 52.9 %
Date: 2025-11-29 04:46:19 Functions: 11 18 61.1 %

          Line data    Source code
       1             : #include "fd_vote_states.h"
       2             : #include "../types/fd_types.h"
       3             : #include "../runtime/program/fd_vote_program.h"
       4             : 
       5             : #define POOL_NAME fd_vote_state_pool
       6          51 : #define POOL_T    fd_vote_state_ele_t
       7          39 : #define POOL_NEXT next_
       8             : #include "../../util/tmpl/fd_pool.c"
       9             : 
      10             : #define MAP_NAME               fd_vote_state_map
      11             : #define MAP_KEY_T              fd_pubkey_t
      12             : #define MAP_ELE_T              fd_vote_state_ele_t
      13           6 : #define MAP_KEY                vote_account
      14          15 : #define MAP_KEY_EQ(k0,k1)      (fd_pubkey_eq( k0, k1 ))
      15          30 : #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) ))
      16          15 : #define MAP_NEXT               next_
      17             : #include "../../util/tmpl/fd_map_chain.c"
      18             : 
      19             : static fd_vote_state_ele_t *
      20          42 : fd_vote_states_get_pool( fd_vote_states_t const * vote_states ) {
      21          42 :   return fd_vote_state_pool_join( (uchar *)vote_states + vote_states->pool_offset_ );
      22          42 : }
      23             : 
      24             : static fd_vote_state_map_t *
      25          24 : fd_vote_states_get_map( fd_vote_states_t const * vote_states ) {
      26          24 :   return fd_vote_state_map_join( (uchar *)vote_states + vote_states->map_offset_ );
      27          24 : }
      28             : 
      29             : ulong
      30          84 : fd_vote_states_align( void ) {
      31             :   /* The align of the struct should be the max of the align of the data
      32             :      structures that it contains.  In this case, this is the map, the
      33             :      pool, and the struct itself. */
      34          84 :   return fd_ulong_max( fd_ulong_max( fd_vote_state_map_align(),
      35          84 :                        fd_vote_state_pool_align() ), alignof(fd_vote_states_t) );
      36          84 : }
      37             : 
      38             : ulong
      39          12 : fd_vote_states_footprint( ulong max_vote_accounts ) {
      40             : 
      41          12 :   ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( max_vote_accounts );
      42             : 
      43          12 :   ulong l = FD_LAYOUT_INIT;
      44          12 :   l = FD_LAYOUT_APPEND( l,  fd_vote_states_align(),     sizeof(fd_vote_states_t) );
      45          12 :   l = FD_LAYOUT_APPEND( l,  fd_vote_state_pool_align(), fd_vote_state_pool_footprint( max_vote_accounts ) );
      46          12 :   l = FD_LAYOUT_APPEND( l,  fd_vote_state_map_align(),  fd_vote_state_map_footprint( map_chain_cnt ) );
      47          12 :   return FD_LAYOUT_FINI( l, fd_vote_states_align() );
      48          12 : }
      49             : 
      50             : void *
      51             : fd_vote_states_new( void * mem,
      52             :                     ulong  max_vote_accounts,
      53           9 :                     ulong  seed ) {
      54           9 :   if( FD_UNLIKELY( !mem ) ) {
      55           3 :     FD_LOG_WARNING(( "NULL mem" ));
      56           3 :     return NULL;
      57           3 :   }
      58             : 
      59           6 :   if( FD_UNLIKELY( !max_vote_accounts ) ) {
      60           3 :     FD_LOG_WARNING(( "max_vote_accounts is 0" ));
      61           3 :     return NULL;
      62           3 :   }
      63             : 
      64           3 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_states_align() ) ) ) {
      65           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      66           0 :     return NULL;
      67           0 :   }
      68             : 
      69           3 :   ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( max_vote_accounts );
      70             : 
      71           3 :   FD_SCRATCH_ALLOC_INIT( l, mem );
      72           3 :   fd_vote_states_t * vote_states = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(),     sizeof(fd_vote_states_t) );
      73           3 :   void *             pool_mem    = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( max_vote_accounts ) );
      74           3 :   void *             map_mem     = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_map_align(),  fd_vote_state_map_footprint( map_chain_cnt ) );
      75             : 
      76           3 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_states_align() )!=(ulong)mem+fd_vote_states_footprint( max_vote_accounts ) ) ) {
      77           0 :     FD_LOG_WARNING(( "fd_vote_states_new: bad layout" ));
      78           0 :     return NULL;
      79           0 :   }
      80             : 
      81           3 :   vote_states->max_vote_accounts_ = max_vote_accounts;
      82           3 :   vote_states->pool_offset_       = (ulong)pool_mem - (ulong)mem;
      83           3 :   vote_states->map_offset_        = (ulong)map_mem - (ulong)mem;
      84             : 
      85           3 :   fd_vote_state_ele_t * vote_states_pool = fd_vote_state_pool_join( fd_vote_state_pool_new( pool_mem, max_vote_accounts ) );
      86           3 :   if( FD_UNLIKELY( !vote_states_pool ) ) {
      87           0 :     FD_LOG_WARNING(( "Failed to create vote states pool" ));
      88           0 :     return NULL;
      89           0 :   }
      90             : 
      91          33 :   for( ulong i=0UL; i<max_vote_accounts; i++ ) {
      92          30 :     fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_states_pool, i );
      93          30 :     vote_state->idx = i;
      94          30 :   }
      95             : 
      96           3 :   if( FD_UNLIKELY( !fd_vote_state_map_join( fd_vote_state_map_new( map_mem, map_chain_cnt, seed ) ) ) ) {
      97           0 :     FD_LOG_WARNING(( "Failed to create vote states map" ));
      98           0 :     return NULL;
      99           0 :   }
     100             : 
     101           3 :   FD_COMPILER_MFENCE();
     102           3 :   FD_VOLATILE( vote_states->magic ) = FD_VOTE_STATES_MAGIC;
     103           3 :   FD_COMPILER_MFENCE();
     104             : 
     105           3 :   return mem;
     106           3 : }
     107             : 
     108             : fd_vote_states_t *
     109           9 : fd_vote_states_join( void * mem ) {
     110           9 :   if( FD_UNLIKELY( !mem ) ) {
     111           3 :     FD_LOG_WARNING(( "NULL mem" ));
     112           3 :     return NULL;
     113           3 :   }
     114             : 
     115           6 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_vote_states_align() ) ) ) {
     116           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     117           0 :     return NULL;
     118           0 :   }
     119             : 
     120           6 :   fd_vote_states_t * vote_states = (fd_vote_states_t *)mem;
     121             : 
     122           6 :   if( FD_UNLIKELY( vote_states->magic != FD_VOTE_STATES_MAGIC ) ) {
     123           3 :     FD_LOG_WARNING(( "Invalid vote states magic" ));
     124           3 :     return NULL;
     125           3 :   }
     126             : 
     127           3 :   ulong map_chain_cnt = fd_vote_state_map_chain_cnt_est( vote_states->max_vote_accounts_ );
     128           3 :   FD_SCRATCH_ALLOC_INIT( l, vote_states );
     129           3 :   vote_states     = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_states_align(),     sizeof(fd_vote_states_t) );
     130           3 :   void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_pool_align(), fd_vote_state_pool_footprint( vote_states->max_vote_accounts_ ) );
     131           3 :   void * map_mem  = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_state_map_align(),  fd_vote_state_map_footprint( map_chain_cnt ) );
     132             : 
     133           3 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_vote_states_align() )!=(ulong)mem+fd_vote_states_footprint( vote_states->max_vote_accounts_ ) ) ) {
     134           0 :     FD_LOG_WARNING(( "fd_vote_states_join: bad layout" ));
     135           0 :     return NULL;
     136           0 :   }
     137             : 
     138           3 :   if( FD_UNLIKELY( !fd_vote_state_pool_join( pool_mem ) ) ) {
     139           0 :     FD_LOG_WARNING(( "Failed to join vote states pool" ));
     140           0 :     return NULL;
     141           0 :   }
     142             : 
     143           3 :   if( FD_UNLIKELY( !fd_vote_state_map_join( map_mem ) ) ) {
     144           0 :     FD_LOG_WARNING(( "Failed to join vote states map" ));
     145           0 :     return NULL;
     146           0 :   }
     147             : 
     148           3 :   return vote_states;
     149           3 : }
     150             : 
     151             : fd_vote_state_ele_t *
     152             : fd_vote_states_update( fd_vote_states_t *  vote_states,
     153           6 :                        fd_pubkey_t const * vote_account ) {
     154             : 
     155           6 :   fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
     156           6 :   fd_vote_state_map_t * vote_state_map  = fd_vote_states_get_map( vote_states );
     157             : 
     158           6 :   if( FD_UNLIKELY( !vote_state_pool ) ) {
     159           0 :     FD_LOG_CRIT(( "unable to retrieve join to vote state pool" ));
     160           0 :   }
     161           6 :   if( FD_UNLIKELY( !vote_state_map ) ) {
     162           0 :     FD_LOG_CRIT(( "unable to retrieve join to vote state map" ));
     163           0 :   }
     164             : 
     165             :   /* First, handle the case where the vote state already exists
     166             :      and we just need to update the entry.  The reason we do a const idx
     167             :      query is to allow fd_vote_states_update to be called while
     168             :      iterating over the map.  It is unsafe to call
     169             :      fd_vote_state_map_ele_query() during iteration, but we only
     170             :      need to change fields which are not used for pool/map management. */
     171             : 
     172           6 :   ulong idx = fd_vote_state_map_idx_query_const(
     173           6 :       vote_state_map,
     174           6 :       vote_account,
     175           6 :       ULONG_MAX,
     176           6 :       vote_state_pool );
     177             : 
     178           6 :   if( idx!=ULONG_MAX ) {
     179           0 :     fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, idx );
     180           0 :     if( FD_UNLIKELY( !vote_state ) ) {
     181           0 :       FD_LOG_CRIT(( "unable to retrieve vote state" ));
     182           0 :     }
     183             : 
     184             :     /* TODO: can do something smarter where we only update the
     185             :        comission and the credits coresponding to the new epoch. */
     186           0 :     return vote_state;
     187           0 :   }
     188             : 
     189             :   /* If the vote state does not exist, we need to create a new entry. */
     190             :   /* Otherwise, try to acquire a new node and populate it. */
     191           6 :   if( FD_UNLIKELY( !fd_vote_state_pool_free( vote_state_pool ) ) ) {
     192           0 :     FD_LOG_CRIT(( "no free vote states in pool" ));
     193           0 :   }
     194             : 
     195           6 :   fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele_acquire( vote_state_pool );
     196             : 
     197           6 :   vote_state->vote_account = *vote_account;
     198           6 :   vote_state->stake        = 0UL;
     199           6 :   vote_state->stake_t_1    = 0UL;
     200           6 :   vote_state->stake_t_2    = 0UL;
     201             : 
     202           6 :   if( FD_UNLIKELY( !fd_vote_state_map_ele_insert(
     203           6 :         vote_state_map,
     204           6 :         vote_state,
     205           6 :         vote_state_pool ) ) ) {
     206           0 :     FD_LOG_CRIT(( "unable to insert stake delegation into map" ));
     207           0 :   }
     208           6 :   return vote_state;
     209           6 : }
     210             : 
     211             : void
     212             : fd_vote_states_remove( fd_vote_states_t *  vote_states,
     213           3 :                        fd_pubkey_t const * vote_account ) {
     214           3 :   fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
     215           3 :   fd_vote_state_map_t * vote_state_map  = fd_vote_states_get_map( vote_states );
     216           3 :   if( FD_UNLIKELY( !vote_state_pool ) ) {
     217           0 :     FD_LOG_CRIT(( "unable to retrieve join to stake delegation pool" ));
     218           0 :   }
     219           3 :   if( FD_UNLIKELY( !vote_state_map ) ) {
     220           0 :     FD_LOG_CRIT(( "unable to retrieve join to stake delegation map" ));
     221           0 :   }
     222             : 
     223           3 :   ulong vote_state_idx = fd_vote_state_map_idx_query_const(
     224           3 :       vote_state_map,
     225           3 :       vote_account,
     226           3 :       ULONG_MAX,
     227           3 :       vote_state_pool );
     228           3 :   if( FD_UNLIKELY( vote_state_idx == ULONG_MAX ) ) {
     229             :     /* The vote state was not found, nothing to do. */
     230           0 :     return;
     231           0 :   }
     232             : 
     233           3 :   fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, vote_state_idx );
     234           3 :   if( FD_UNLIKELY( !vote_state ) ) {
     235           0 :     FD_LOG_CRIT(( "unable to retrieve vote state" ));
     236           0 :   }
     237             : 
     238           3 :   ulong idx = fd_vote_state_map_idx_remove( vote_state_map, vote_account, ULONG_MAX, vote_state_pool );
     239           3 :   if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
     240           0 :     FD_LOG_CRIT(( "unable to remove vote state" ));
     241           0 :   }
     242             : 
     243             :   /* Set vote state's next_ pointer to the null idx. */
     244           3 :   vote_state->next_ = fd_vote_state_pool_idx_null( vote_state_pool );
     245             : 
     246           3 :   fd_vote_state_pool_idx_release( vote_state_pool, vote_state_idx );
     247           3 : }
     248             : 
     249             : fd_vote_state_ele_t *
     250             : fd_vote_states_update_from_account( fd_vote_states_t *  vote_states,
     251             :                                     fd_pubkey_t const * vote_account,
     252             :                                     uchar const *       account_data,
     253           0 :                                     ulong               account_data_len ) {
     254             : 
     255             :   /* TODO: Instead of doing this messy + unbounded decode, it should be
     256             :      replaced with a more efficient decode that just reads the fields
     257             :      we need directly. */
     258             : 
     259           0 :   fd_bincode_decode_ctx_t ctx = {
     260           0 :     .data    = account_data,
     261           0 :     .dataend = account_data + account_data_len,
     262           0 :   };
     263             : 
     264           0 :   uchar __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))) vote_state_versioned[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ];
     265             : 
     266           0 :   fd_vote_state_versioned_t * vsv = fd_vote_state_versioned_decode( vote_state_versioned, &ctx );
     267           0 :   if( FD_UNLIKELY( vsv==NULL ) ) {
     268           0 :     FD_LOG_CRIT(( "unable to decode vote state versioned" ));
     269           0 :   }
     270             : 
     271           0 :   fd_pubkey_t node_account;
     272           0 :   uchar       commission;
     273           0 :   long        last_vote_timestamp;
     274           0 :   ulong       last_vote_slot;
     275             : 
     276           0 :   switch( vsv->discriminant ) {
     277           0 :   case fd_vote_state_versioned_enum_v0_23_5:
     278           0 :     node_account        = vsv->inner.v0_23_5.node_pubkey;
     279           0 :     commission          = vsv->inner.v0_23_5.commission;
     280           0 :     last_vote_timestamp = vsv->inner.v0_23_5.last_timestamp.timestamp;
     281           0 :     last_vote_slot      = vsv->inner.v0_23_5.last_timestamp.slot;
     282           0 :     break;
     283           0 :   case fd_vote_state_versioned_enum_v1_14_11:
     284           0 :     node_account        = vsv->inner.v1_14_11.node_pubkey;
     285           0 :     commission          = vsv->inner.v1_14_11.commission;
     286           0 :     last_vote_timestamp = vsv->inner.v1_14_11.last_timestamp.timestamp;
     287           0 :     last_vote_slot      = vsv->inner.v1_14_11.last_timestamp.slot;
     288           0 :     break;
     289           0 :   case fd_vote_state_versioned_enum_current:
     290           0 :     node_account        = vsv->inner.current.node_pubkey;
     291           0 :     commission          = vsv->inner.current.commission;
     292           0 :     last_vote_timestamp = vsv->inner.current.last_timestamp.timestamp;
     293           0 :     last_vote_slot      = vsv->inner.current.last_timestamp.slot;
     294           0 :     break;
     295           0 :   default:
     296           0 :     __builtin_unreachable();
     297           0 :   }
     298             : 
     299           0 :   fd_vote_state_ele_t * vote_state = fd_vote_states_update( vote_states, vote_account );
     300             : 
     301           0 :   vote_state->node_account        = node_account;
     302           0 :   vote_state->commission          = commission;
     303           0 :   vote_state->last_vote_timestamp = last_vote_timestamp;
     304           0 :   vote_state->last_vote_slot      = last_vote_slot;
     305             : 
     306           0 :   return vote_state;
     307           0 : }
     308             : 
     309             : void
     310           3 : fd_vote_states_reset_stakes( fd_vote_states_t * vote_states ) {
     311           3 :   fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
     312           3 :   fd_vote_state_map_t * vote_state_map  = fd_vote_states_get_map( vote_states );
     313           3 :   if( FD_UNLIKELY( !vote_state_pool ) ) {
     314           0 :     FD_LOG_CRIT(( "unable to retrieve join to vote state pool" ));
     315           0 :   }
     316           3 :   if( FD_UNLIKELY( !vote_state_map ) ) {
     317           0 :     FD_LOG_CRIT(( "unable to retrieve join to vote state map" ));
     318           0 :   }
     319             : 
     320           3 :   for( fd_vote_state_map_iter_t iter = fd_vote_state_map_iter_init( vote_state_map, vote_state_pool );
     321           9 :        !fd_vote_state_map_iter_done( iter, vote_state_map, vote_state_pool );
     322           6 :        iter = fd_vote_state_map_iter_next( iter, vote_state_map, vote_state_pool ) ) {
     323           6 :     ulong idx = fd_vote_state_map_iter_idx( iter, vote_state_map, vote_state_pool );
     324             : 
     325           6 :     fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, idx );
     326           6 :     if( FD_UNLIKELY( !vote_state ) ) {
     327           0 :       FD_LOG_CRIT(( "unable to retrieve vote state" ));
     328           0 :     }
     329             : 
     330           6 :     vote_state->stake = 0UL;
     331           6 :   }
     332           3 : }
     333             : 
     334             : fd_vote_state_ele_t *
     335             : fd_vote_states_query( fd_vote_states_t const * vote_states,
     336          12 :                       fd_pubkey_t const *      vote_account ) {
     337             : 
     338             :   /* map_chain's _ele_query function isn't safe for concurrent access.
     339             :      The solution is to use the idx_query_const function, which is safe
     340             :      for concurrent access.  The caller is still responsible for
     341             :      synchronizing concurrent writers to the fd_vote_state_ele_t. */
     342          12 :   ulong idx = fd_vote_state_map_idx_query_const(
     343          12 :       fd_vote_states_get_map( vote_states ),
     344          12 :       vote_account,
     345          12 :       ULONG_MAX,
     346          12 :       fd_vote_states_get_pool( vote_states ) );
     347          12 :   if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
     348           3 :     return NULL;
     349           3 :   }
     350             : 
     351           9 :   fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( fd_vote_states_get_pool( vote_states ), idx );
     352           9 :   if( FD_UNLIKELY( !vote_state ) ) {
     353           0 :     FD_LOG_CRIT(( "unable to retrieve vote state" ));
     354           0 :   }
     355             : 
     356           9 :   return vote_state;
     357           9 : }
     358             : 
     359             : /* fd_vote_states_query_const is the same as fd_vote_states but instead
     360             :    returns a const pointer. */
     361             : 
     362             : fd_vote_state_ele_t const *
     363             : fd_vote_states_query_const( fd_vote_states_t const * vote_states,
     364           0 :                             fd_pubkey_t const *      vote_account ) {
     365           0 :   return fd_vote_state_map_ele_query_const(
     366           0 :       fd_vote_states_get_map( vote_states ),
     367           0 :       vote_account,
     368           0 :       NULL,
     369           0 :       fd_vote_states_get_pool( vote_states ) );
     370           0 : }
     371             : 
     372             : ulong
     373           0 : fd_vote_states_max( fd_vote_states_t const * vote_states ) {
     374           0 :   return vote_states->max_vote_accounts_;
     375           0 : }
     376             : 
     377             : ulong
     378           9 : fd_vote_states_cnt( fd_vote_states_t const * vote_states ) {
     379           9 :   return fd_vote_state_pool_used( fd_vote_states_get_pool( vote_states ) );
     380           9 : }
     381             : 
     382             : fd_vote_state_ele_t *
     383           0 : fd_vote_states_iter_ele( fd_vote_states_iter_t * iter ) {
     384           0 :   ulong idx = fd_vote_state_map_iter_idx( iter->iter, iter->map, iter->pool );
     385           0 :   return fd_vote_state_pool_ele( iter->pool, idx );
     386           0 : }
     387             : 
     388             : fd_vote_states_iter_t *
     389             : fd_vote_states_iter_init( fd_vote_states_iter_t *  iter,
     390           0 :                           fd_vote_states_t const * vote_states ) {
     391           0 :   if( FD_UNLIKELY( !iter ) ) {
     392           0 :     FD_LOG_CRIT(( "NULL iter_mem" ));
     393           0 :   }
     394           0 :   if( FD_UNLIKELY( !vote_states ) ) {
     395           0 :     FD_LOG_CRIT(( "NULL vote_states" ));
     396           0 :   }
     397             : 
     398           0 :   iter->map  = fd_vote_states_get_map( vote_states );
     399           0 :   iter->pool = fd_vote_states_get_pool( vote_states );
     400           0 :   iter->iter = fd_vote_state_map_iter_init( iter->map, iter->pool );
     401             : 
     402           0 :   return iter;
     403           0 : }
     404             : 
     405             : int
     406           0 : fd_vote_states_iter_done( fd_vote_states_iter_t * iter ) {
     407           0 :   return fd_vote_state_map_iter_done( iter->iter, iter->map, iter->pool );
     408           0 : }
     409             : 
     410             : void
     411           0 : fd_vote_states_iter_next( fd_vote_states_iter_t * iter ) {
     412           0 :   iter->iter = fd_vote_state_map_iter_next( iter->iter, iter->map, iter->pool );
     413           0 : }

Generated by: LCOV version 1.14