LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_vote_states.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 148 281 52.7 %
Date: 2025-10-27 04:40:00 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          90 : 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          90 :   return fd_ulong_max( fd_ulong_max( fd_vote_state_map_align(),
      35          90 :                        fd_vote_state_pool_align() ), alignof(fd_vote_states_t) );
      36          90 : }
      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           6 :   if( FD_UNLIKELY( !vote_state ) ) {
     197           0 :     FD_LOG_CRIT(( "unable to acquire vote state" ));
     198           0 :   }
     199             : 
     200           6 :   vote_state->vote_account = *vote_account;
     201           6 :   vote_state->stake        = 0UL;
     202           6 :   vote_state->stake_t_2    = 0UL;
     203             : 
     204           6 :   if( FD_UNLIKELY( !fd_vote_state_map_ele_insert(
     205           6 :         vote_state_map,
     206           6 :         vote_state,
     207           6 :         vote_state_pool ) ) ) {
     208           0 :     FD_LOG_CRIT(( "unable to insert stake delegation into map" ));
     209           0 :   }
     210           6 :   return vote_state;
     211           6 : }
     212             : 
     213             : void
     214             : fd_vote_states_remove( fd_vote_states_t *  vote_states,
     215           3 :                        fd_pubkey_t const * vote_account ) {
     216           3 :   fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
     217           3 :   fd_vote_state_map_t * vote_state_map  = fd_vote_states_get_map( vote_states );
     218           3 :   if( FD_UNLIKELY( !vote_state_pool ) ) {
     219           0 :     FD_LOG_CRIT(( "unable to retrieve join to stake delegation pool" ));
     220           0 :   }
     221           3 :   if( FD_UNLIKELY( !vote_state_map ) ) {
     222           0 :     FD_LOG_CRIT(( "unable to retrieve join to stake delegation map" ));
     223           0 :   }
     224             : 
     225           3 :   ulong vote_state_idx = fd_vote_state_map_idx_query_const(
     226           3 :       vote_state_map,
     227           3 :       vote_account,
     228           3 :       ULONG_MAX,
     229           3 :       vote_state_pool );
     230           3 :   if( FD_UNLIKELY( vote_state_idx == ULONG_MAX ) ) {
     231             :     /* The vote state was not found, nothing to do. */
     232           0 :     return;
     233           0 :   }
     234             : 
     235           3 :   fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, vote_state_idx );
     236           3 :   if( FD_UNLIKELY( !vote_state ) ) {
     237           0 :     FD_LOG_CRIT(( "unable to retrieve vote state" ));
     238           0 :   }
     239             : 
     240           3 :   ulong idx = fd_vote_state_map_idx_remove( vote_state_map, vote_account, ULONG_MAX, vote_state_pool );
     241           3 :   if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
     242           0 :     FD_LOG_CRIT(( "unable to remove vote state" ));
     243           0 :   }
     244             : 
     245             :   /* Set vote state's next_ pointer to the null idx. */
     246           3 :   vote_state->next_ = fd_vote_state_pool_idx_null( vote_state_pool );
     247             : 
     248           3 :   fd_vote_state_pool_idx_release( vote_state_pool, vote_state_idx );
     249           3 : }
     250             : 
     251             : fd_vote_state_ele_t *
     252             : fd_vote_states_update_from_account( fd_vote_states_t *  vote_states,
     253             :                                     fd_pubkey_t const * vote_account,
     254             :                                     uchar const *       account_data,
     255           0 :                                     ulong               account_data_len ) {
     256             : 
     257             :   /* TODO: Instead of doing this messy + unbounded decode, it should be
     258             :      replaced with a more efficient decode that just reads the fields
     259             :      we need directly. */
     260             : 
     261           0 :   fd_bincode_decode_ctx_t ctx = {
     262           0 :     .data    = account_data,
     263           0 :     .dataend = account_data + account_data_len,
     264           0 :   };
     265             : 
     266           0 :   uchar __attribute__((aligned(FD_VOTE_STATE_VERSIONED_ALIGN))) vote_state_versioned[ FD_VOTE_STATE_VERSIONED_FOOTPRINT ];
     267             : 
     268           0 :   fd_vote_state_versioned_t * vsv = fd_vote_state_versioned_decode( vote_state_versioned, &ctx );
     269           0 :   if( FD_UNLIKELY( vsv==NULL ) ) {
     270           0 :     FD_LOG_CRIT(( "unable to decode vote state versioned" ));
     271           0 :   }
     272             : 
     273           0 :   fd_pubkey_t node_account;
     274           0 :   uchar       commission;
     275           0 :   long        last_vote_timestamp;
     276           0 :   ulong       last_vote_slot;
     277             : 
     278           0 :   switch( vsv->discriminant ) {
     279           0 :   case fd_vote_state_versioned_enum_v0_23_5:
     280           0 :     node_account        = vsv->inner.v0_23_5.node_pubkey;
     281           0 :     commission          = vsv->inner.v0_23_5.commission;
     282           0 :     last_vote_timestamp = vsv->inner.v0_23_5.last_timestamp.timestamp;
     283           0 :     last_vote_slot      = vsv->inner.v0_23_5.last_timestamp.slot;
     284           0 :     break;
     285           0 :   case fd_vote_state_versioned_enum_v1_14_11:
     286           0 :     node_account        = vsv->inner.v1_14_11.node_pubkey;
     287           0 :     commission          = vsv->inner.v1_14_11.commission;
     288           0 :     last_vote_timestamp = vsv->inner.v1_14_11.last_timestamp.timestamp;
     289           0 :     last_vote_slot      = vsv->inner.v1_14_11.last_timestamp.slot;
     290           0 :     break;
     291           0 :   case fd_vote_state_versioned_enum_current:
     292           0 :     node_account        = vsv->inner.current.node_pubkey;
     293           0 :     commission          = vsv->inner.current.commission;
     294           0 :     last_vote_timestamp = vsv->inner.current.last_timestamp.timestamp;
     295           0 :     last_vote_slot      = vsv->inner.current.last_timestamp.slot;
     296           0 :     break;
     297           0 :   default:
     298           0 :     __builtin_unreachable();
     299           0 :   }
     300             : 
     301           0 :   fd_vote_state_ele_t * vote_state = fd_vote_states_update( vote_states, vote_account );
     302             : 
     303           0 :   vote_state->node_account        = node_account;
     304           0 :   vote_state->commission          = commission;
     305           0 :   vote_state->last_vote_timestamp = last_vote_timestamp;
     306           0 :   vote_state->last_vote_slot      = last_vote_slot;
     307             : 
     308           0 :   return vote_state;
     309           0 : }
     310             : 
     311             : void
     312           3 : fd_vote_states_reset_stakes( fd_vote_states_t * vote_states ) {
     313           3 :   fd_vote_state_ele_t * vote_state_pool = fd_vote_states_get_pool( vote_states );
     314           3 :   fd_vote_state_map_t * vote_state_map  = fd_vote_states_get_map( vote_states );
     315           3 :   if( FD_UNLIKELY( !vote_state_pool ) ) {
     316           0 :     FD_LOG_CRIT(( "unable to retrieve join to vote state pool" ));
     317           0 :   }
     318           3 :   if( FD_UNLIKELY( !vote_state_map ) ) {
     319           0 :     FD_LOG_CRIT(( "unable to retrieve join to vote state map" ));
     320           0 :   }
     321             : 
     322           3 :   for( fd_vote_state_map_iter_t iter = fd_vote_state_map_iter_init( vote_state_map, vote_state_pool );
     323           9 :        !fd_vote_state_map_iter_done( iter, vote_state_map, vote_state_pool );
     324           6 :        iter = fd_vote_state_map_iter_next( iter, vote_state_map, vote_state_pool ) ) {
     325           6 :     ulong idx = fd_vote_state_map_iter_idx( iter, vote_state_map, vote_state_pool );
     326             : 
     327           6 :     fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( vote_state_pool, idx );
     328           6 :     if( FD_UNLIKELY( !vote_state ) ) {
     329           0 :       FD_LOG_CRIT(( "unable to retrieve vote state" ));
     330           0 :     }
     331             : 
     332           6 :     vote_state->stake     = 0UL;
     333           6 :     vote_state->stake_t_2 = 0UL;
     334           6 :   }
     335           3 : }
     336             : 
     337             : fd_vote_state_ele_t *
     338             : fd_vote_states_query( fd_vote_states_t const * vote_states,
     339          12 :                       fd_pubkey_t const *      vote_account ) {
     340             : 
     341             :   /* map_chain's _ele_query function isn't safe for concurrent access.
     342             :      The solution is to use the idx_query_const function, which is safe
     343             :      for concurrent access.  The caller is still responsible for
     344             :      synchronizing concurrent writers to the fd_vote_state_ele_t. */
     345          12 :   ulong idx = fd_vote_state_map_idx_query_const(
     346          12 :       fd_vote_states_get_map( vote_states ),
     347          12 :       vote_account,
     348          12 :       ULONG_MAX,
     349          12 :       fd_vote_states_get_pool( vote_states ) );
     350          12 :   if( FD_UNLIKELY( idx==ULONG_MAX ) ) {
     351           3 :     return NULL;
     352           3 :   }
     353             : 
     354           9 :   fd_vote_state_ele_t * vote_state = fd_vote_state_pool_ele( fd_vote_states_get_pool( vote_states ), idx );
     355           9 :   if( FD_UNLIKELY( !vote_state ) ) {
     356           0 :     FD_LOG_CRIT(( "unable to retrieve vote state" ));
     357           0 :   }
     358             : 
     359           9 :   return vote_state;
     360           9 : }
     361             : 
     362             : /* fd_vote_states_query_const is the same as fd_vote_states but instead
     363             :    returns a const pointer. */
     364             : 
     365             : fd_vote_state_ele_t const *
     366             : fd_vote_states_query_const( fd_vote_states_t const * vote_states,
     367           0 :                             fd_pubkey_t const *      vote_account ) {
     368           0 :   return fd_vote_state_map_ele_query_const(
     369           0 :       fd_vote_states_get_map( vote_states ),
     370           0 :       vote_account,
     371           0 :       NULL,
     372           0 :       fd_vote_states_get_pool( vote_states ) );
     373           0 : }
     374             : 
     375             : ulong
     376           0 : fd_vote_states_max( fd_vote_states_t const * vote_states ) {
     377           0 :   return vote_states->max_vote_accounts_;
     378           0 : }
     379             : 
     380             : ulong
     381           9 : fd_vote_states_cnt( fd_vote_states_t const * vote_states ) {
     382           9 :   return fd_vote_state_pool_used( fd_vote_states_get_pool( vote_states ) );
     383           9 : }
     384             : 
     385             : fd_vote_state_ele_t *
     386           0 : fd_vote_states_iter_ele( fd_vote_states_iter_t * iter ) {
     387           0 :   ulong idx = fd_vote_state_map_iter_idx( iter->iter, iter->map, iter->pool );
     388           0 :   return fd_vote_state_pool_ele( iter->pool, idx );
     389           0 : }
     390             : 
     391             : fd_vote_states_iter_t *
     392             : fd_vote_states_iter_init( fd_vote_states_iter_t *  iter,
     393           0 :                           fd_vote_states_t const * vote_states ) {
     394           0 :   if( FD_UNLIKELY( !iter ) ) {
     395           0 :     FD_LOG_CRIT(( "NULL iter_mem" ));
     396           0 :   }
     397           0 :   if( FD_UNLIKELY( !vote_states ) ) {
     398           0 :     FD_LOG_CRIT(( "NULL vote_states" ));
     399           0 :   }
     400             : 
     401           0 :   iter->map  = fd_vote_states_get_map( vote_states );
     402           0 :   iter->pool = fd_vote_states_get_pool( vote_states );
     403           0 :   iter->iter = fd_vote_state_map_iter_init( iter->map, iter->pool );
     404             : 
     405           0 :   return iter;
     406           0 : }
     407             : 
     408             : int
     409           0 : fd_vote_states_iter_done( fd_vote_states_iter_t * iter ) {
     410           0 :   return fd_vote_state_map_iter_done( iter->iter, iter->map, iter->pool );
     411           0 : }
     412             : 
     413             : void
     414           0 : fd_vote_states_iter_next( fd_vote_states_iter_t * iter ) {
     415           0 :   iter->iter = fd_vote_state_map_iter_next( iter->iter, iter->map, iter->pool );
     416           0 : }

Generated by: LCOV version 1.14