LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 687 968 71.0 %
Date: 2026-02-01 06:05:50 Functions: 78 205 38.0 %

          Line data    Source code
       1             : #include "fd_bank.h"
       2             : #include "fd_runtime_const.h"
       3             : 
       4             : ulong
       5           0 : fd_bank_align( void ) {
       6           0 :   return alignof(fd_bank_t);
       7           0 : }
       8             : 
       9             : ulong
      10           0 : fd_bank_footprint( void ) {
      11           0 :   ulong l = FD_LAYOUT_INIT;
      12           0 :   l = FD_LAYOUT_APPEND( l, fd_bank_align(), sizeof(fd_bank_t) );
      13           0 :   return FD_LAYOUT_FINI( l, fd_bank_align() );
      14           0 : }
      15             : 
      16             : fd_epoch_rewards_t const *
      17          48 : fd_bank_epoch_rewards_query( fd_bank_t * bank ) {
      18             :   /* If the pool element hasn't been setup yet, then return NULL */
      19          48 :   fd_bank_epoch_rewards_t * epoch_rewards_pool = fd_bank_get_epoch_rewards_pool( bank->data );
      20          48 :   if( FD_UNLIKELY( epoch_rewards_pool==NULL ) ) {
      21           0 :     FD_LOG_CRIT(( "NULL epoch rewards pool" ));
      22           0 :   }
      23          48 :   if( FD_UNLIKELY( bank->data->epoch_rewards_pool_idx==fd_bank_epoch_rewards_pool_idx_null( epoch_rewards_pool ) ) ) {
      24           6 :     return NULL;
      25           6 :   }
      26          42 :   fd_bank_epoch_rewards_t * bank_epoch_rewards = fd_bank_epoch_rewards_pool_ele( epoch_rewards_pool, bank->data->epoch_rewards_pool_idx );
      27          42 :   return fd_type_pun_const( bank_epoch_rewards->data );
      28          48 : }
      29             : 
      30             : fd_epoch_rewards_t *
      31          90 : fd_bank_epoch_rewards_modify( fd_bank_t * bank ) {
      32             :   /* If the dirty flag is set, then we already have a pool element
      33             :      that was copied over for the current bank. We can simply just
      34             :      query the pool element and return it. */
      35          90 :   fd_bank_epoch_rewards_t * epoch_rewards_pool = fd_bank_get_epoch_rewards_pool( bank->data );
      36          90 :   if( FD_UNLIKELY( epoch_rewards_pool==NULL ) ) {
      37           0 :     FD_LOG_CRIT(( "NULL epoch rewards pool" ));
      38           0 :   }
      39          90 :   if( bank->data->epoch_rewards_dirty ) {
      40          60 :     fd_bank_epoch_rewards_t * bank_epoch_rewards = fd_bank_epoch_rewards_pool_ele( epoch_rewards_pool, bank->data->epoch_rewards_pool_idx );
      41          60 :     return fd_type_pun( bank_epoch_rewards->data );
      42          60 :   }
      43          30 :   fd_rwlock_write( &bank->locks->epoch_rewards_pool_lock );
      44          30 :   if( FD_UNLIKELY( !fd_bank_epoch_rewards_pool_free( epoch_rewards_pool ) ) ) {
      45           0 :     FD_LOG_CRIT(( "Failed to acquire epoch rewards pool element: pool is full" ));
      46           0 :   }
      47          30 :   fd_bank_epoch_rewards_t * child_epoch_rewards = fd_bank_epoch_rewards_pool_ele_acquire( epoch_rewards_pool );
      48          30 :   fd_rwlock_unwrite( &bank->locks->epoch_rewards_pool_lock );
      49             :   /* If the dirty flag has not been set yet, we need to allocated a
      50             :      new pool element and copy over the data from the parent idx.
      51             :      We also need to mark the dirty flag. */
      52          30 :   ulong child_idx = fd_bank_epoch_rewards_pool_idx( epoch_rewards_pool, child_epoch_rewards );
      53          30 :   if( bank->data->epoch_rewards_pool_idx!=fd_bank_epoch_rewards_pool_idx_null( epoch_rewards_pool ) ) {
      54           6 :     fd_bank_epoch_rewards_t * parent_epoch_rewards = fd_bank_epoch_rewards_pool_ele( epoch_rewards_pool, bank->data->epoch_rewards_pool_idx );
      55           6 :     fd_memcpy( child_epoch_rewards->data, parent_epoch_rewards->data, FD_EPOCH_REWARDS_FOOTPRINT );
      56           6 :   }
      57          30 :   bank->data->epoch_rewards_pool_idx = child_idx;
      58          30 :   bank->data->epoch_rewards_dirty    = 1;
      59          30 :   return fd_type_pun( child_epoch_rewards->data );
      60          30 : }
      61             : 
      62             : fd_epoch_leaders_t const *
      63           0 : fd_bank_epoch_leaders_query( fd_bank_t const * bank ) {
      64             :   /* If the pool element hasn't been setup yet, then return NULL */
      65           0 :   fd_bank_epoch_leaders_t * epoch_leaders_pool = fd_bank_get_epoch_leaders_pool( bank->data );
      66           0 :   if( FD_UNLIKELY( epoch_leaders_pool==NULL ) ) {
      67           0 :     FD_LOG_CRIT(( "NULL epoch leaders pool" ));
      68           0 :   }
      69           0 :   if( bank->data->epoch_leaders_pool_idx==fd_bank_epoch_leaders_pool_idx_null( epoch_leaders_pool ) ) {
      70           0 :     return NULL;
      71           0 :   }
      72           0 :   fd_bank_epoch_leaders_t * bank_epoch_leaders = fd_bank_epoch_leaders_pool_ele( epoch_leaders_pool, bank->data->epoch_leaders_pool_idx );
      73           0 :   return fd_type_pun_const( bank_epoch_leaders->data );
      74           0 : }
      75             : 
      76             : fd_epoch_leaders_t *
      77           9 : fd_bank_epoch_leaders_modify( fd_bank_t * bank ) {
      78             :   /* If the dirty flag is set, then we already have a pool element
      79             :      that was copied over for the current bank. We can simply just
      80             :      query the pool element and return it. */
      81           9 :   fd_bank_epoch_leaders_t * epoch_leaders_pool = fd_bank_get_epoch_leaders_pool( bank->data );
      82           9 :   if( FD_UNLIKELY( epoch_leaders_pool==NULL ) ) {
      83           0 :     FD_LOG_CRIT(( "NULL epoch leaders pool" ));
      84           0 :   }
      85           9 :   if( bank->data->epoch_leaders_dirty ) {
      86           0 :     fd_bank_epoch_leaders_t * bank_epoch_leaders = fd_bank_epoch_leaders_pool_ele( epoch_leaders_pool, bank->data->epoch_leaders_pool_idx );
      87           0 :     return fd_type_pun( bank_epoch_leaders->data );
      88           0 :   }
      89           9 :   fd_rwlock_write( &bank->locks->epoch_leaders_pool_lock );
      90           9 :   if( FD_UNLIKELY( !fd_bank_epoch_leaders_pool_free( epoch_leaders_pool ) ) ) {
      91           0 :     FD_LOG_CRIT(( "Failed to acquire epoch leaders pool element: pool is full" ));
      92           0 :   }
      93           9 :   fd_bank_epoch_leaders_t * child_epoch_leaders = fd_bank_epoch_leaders_pool_ele_acquire( epoch_leaders_pool );
      94           9 :   fd_rwlock_unwrite( &bank->locks->epoch_leaders_pool_lock );
      95             :   /* If the dirty flag has not been set yet, we need to allocated a
      96             :      new pool element and copy over the data from the parent idx.
      97             :      We also need to mark the dirty flag. */
      98           9 :   ulong child_idx = fd_bank_epoch_leaders_pool_idx( epoch_leaders_pool, child_epoch_leaders );
      99           9 :   if( bank->data->epoch_leaders_pool_idx!=fd_bank_epoch_leaders_pool_idx_null( epoch_leaders_pool ) ) {
     100           3 :     fd_bank_epoch_leaders_t * parent_epoch_leaders = fd_bank_epoch_leaders_pool_ele( epoch_leaders_pool, bank->data->epoch_leaders_pool_idx );
     101           3 :     fd_memcpy( child_epoch_leaders->data, parent_epoch_leaders->data, FD_EPOCH_LEADERS_MAX_FOOTPRINT );
     102           3 :   }
     103           9 :   bank->data->epoch_leaders_pool_idx = child_idx;
     104           9 :   bank->data->epoch_leaders_dirty    = 1;
     105           9 :   return fd_type_pun( child_epoch_leaders->data );
     106           9 : }
     107             : 
     108             : fd_vote_states_t const *
     109         144 : fd_bank_vote_states_locking_query( fd_bank_t * bank ) {
     110         144 :   fd_rwlock_read( &bank->locks->vote_states_lock[ bank->data->idx ] );
     111             :   /* If the pool element hasn't been setup yet, then return NULL */
     112         144 :   fd_bank_vote_states_t * vote_states_pool = fd_bank_get_vote_states_pool( bank->data );
     113         144 :   if( FD_UNLIKELY( vote_states_pool==NULL ) ) {
     114           0 :     FD_LOG_CRIT(( "NULL vote states pool" ));
     115           0 :   }
     116         144 :   if( FD_UNLIKELY( bank->data->vote_states_pool_idx==fd_bank_vote_states_pool_idx_null( vote_states_pool ) ) ) {
     117           0 :     FD_LOG_CRIT(( "vote states pool element not set" ));
     118           0 :   }
     119         144 :   fd_bank_vote_states_t * bank_vote_states = fd_bank_vote_states_pool_ele( vote_states_pool, bank->data->vote_states_pool_idx );
     120         144 :   return fd_type_pun_const( bank_vote_states->data );
     121         144 : }
     122             : 
     123             : void
     124         144 : fd_bank_vote_states_end_locking_query( fd_bank_t * bank ) {
     125         144 :   fd_rwlock_unread( &bank->locks->vote_states_lock[ bank->data->idx ] );
     126         144 : }
     127             : 
     128             : fd_vote_states_t *
     129          96 : fd_bank_vote_states_locking_modify( fd_bank_t * bank ) {
     130          96 :   fd_rwlock_write( &bank->locks->vote_states_lock[ bank->data->idx ] );
     131             :   /* If the dirty flag is set, then we already have a pool element
     132             :      that was copied over for the current bank. We can simply just
     133             :      query the pool element and return it. */
     134          96 :   fd_bank_vote_states_t * vote_states_pool = fd_bank_get_vote_states_pool( bank->data );
     135          96 :   if( FD_UNLIKELY( vote_states_pool==NULL ) ) {
     136           0 :     FD_LOG_CRIT(( "NULL vote states pool" ));
     137           0 :   }
     138          96 :   if( bank->data->vote_states_dirty ) {
     139          63 :     fd_bank_vote_states_t * bank_vote_states = fd_bank_vote_states_pool_ele( vote_states_pool, bank->data->vote_states_pool_idx );
     140          63 :     return fd_type_pun( bank_vote_states->data );
     141          63 :   }
     142          33 :   fd_rwlock_write( &bank->locks->vote_states_pool_lock );
     143          33 :   if( FD_UNLIKELY( !fd_bank_vote_states_pool_free( vote_states_pool ) ) ) {
     144           0 :     FD_LOG_CRIT(( "Failed to acquire vote states pool element: pool is full" ));
     145           0 :   }
     146          33 :   fd_bank_vote_states_t * child_vote_states = fd_bank_vote_states_pool_ele_acquire( vote_states_pool );
     147          33 :   fd_rwlock_unwrite( &bank->locks->vote_states_pool_lock );
     148             :   /* If the dirty flag has not been set yet, we need to allocated a
     149             :      new pool element and copy over the data from the parent idx.
     150             :      We also need to mark the dirty flag. */
     151          33 :   ulong child_idx = fd_bank_vote_states_pool_idx( vote_states_pool, child_vote_states );
     152          33 :   fd_bank_vote_states_t * parent_vote_states = fd_bank_vote_states_pool_ele( vote_states_pool, bank->data->vote_states_pool_idx );
     153          33 :   fd_memcpy( child_vote_states->data, parent_vote_states->data, FD_VOTE_STATES_FOOTPRINT );
     154          33 :   bank->data->vote_states_pool_idx = child_idx;
     155          33 :   bank->data->vote_states_dirty    = 1;
     156          33 :   return fd_type_pun( child_vote_states->data );
     157          33 : }
     158             : 
     159             : void
     160          96 : fd_bank_vote_states_end_locking_modify( fd_bank_t * bank ) {
     161          96 :   fd_rwlock_unwrite( &bank->locks->vote_states_lock[ bank->data->idx ] );
     162          96 : }
     163             : 
     164             : fd_vote_states_t const *
     165          42 : fd_bank_vote_states_prev_query( fd_bank_t * bank ) {
     166             :   /* If the pool element hasn't been setup yet, then return NULL */
     167          42 :   fd_bank_vote_states_prev_t * vote_states_prev_pool = fd_bank_get_vote_states_prev_pool( bank->data );
     168          42 :   if( FD_UNLIKELY( vote_states_prev_pool==NULL ) ) {
     169           0 :     FD_LOG_CRIT(( "NULL vote states prev pool" ));
     170           0 :   }
     171          42 :   if( FD_UNLIKELY( bank->data->vote_states_prev_pool_idx==fd_bank_vote_states_prev_pool_idx_null( vote_states_prev_pool ) ) ) {
     172           0 :     FD_LOG_CRIT(( "vote states prev pool element not set" ));
     173           0 :   }
     174          42 :   fd_bank_vote_states_prev_t * bank_vote_states_prev = fd_bank_vote_states_prev_pool_ele( vote_states_prev_pool, bank->data->vote_states_prev_pool_idx );
     175          42 :   return fd_type_pun_const( bank_vote_states_prev->data );
     176          42 : }
     177             : 
     178             : fd_vote_states_t *
     179          42 : fd_bank_vote_states_prev_modify( fd_bank_t * bank ) {
     180             :   /* If the dirty flag is set, then we already have a pool element
     181             :      that was copied over for the current bank. We can simply just
     182             :      query the pool element and return it. */
     183          42 :   fd_bank_vote_states_prev_t * vote_states_prev_pool = fd_bank_get_vote_states_prev_pool( bank->data );
     184          42 :   if( FD_UNLIKELY( vote_states_prev_pool==NULL ) ) {
     185           0 :     FD_LOG_CRIT(( "NULL vote states prev pool" ));
     186           0 :   }
     187          42 :   if( bank->data->vote_states_prev_dirty ) {
     188           6 :     fd_bank_vote_states_prev_t * bank_vote_states_prev = fd_bank_vote_states_prev_pool_ele( vote_states_prev_pool, bank->data->vote_states_prev_pool_idx );
     189           6 :     return fd_type_pun( bank_vote_states_prev->data );
     190           6 :   }
     191          36 :   fd_rwlock_write( &bank->locks->vote_states_prev_pool_lock );
     192          36 :   if( FD_UNLIKELY( !fd_bank_vote_states_prev_pool_free( vote_states_prev_pool ) ) ) {
     193           0 :     FD_LOG_CRIT(( "Failed to acquire vote states prev pool element: pool is full" ));
     194           0 :   }
     195          36 :   fd_bank_vote_states_prev_t * child_vote_states_prev = fd_bank_vote_states_prev_pool_ele_acquire( vote_states_prev_pool );
     196          36 :   fd_rwlock_unwrite( &bank->locks->vote_states_prev_pool_lock );
     197             :   /* If the dirty flag has not been set yet, we need to allocated a
     198             :      new pool element and copy over the data from the parent idx.
     199             :      We also need to mark the dirty flag. */
     200          36 :   ulong child_idx = fd_bank_vote_states_prev_pool_idx( vote_states_prev_pool, child_vote_states_prev );
     201          36 :   fd_bank_vote_states_prev_t * parent_vote_states_prev = fd_bank_vote_states_prev_pool_ele( vote_states_prev_pool, bank->data->vote_states_prev_pool_idx );
     202          36 :   fd_memcpy( child_vote_states_prev->data, parent_vote_states_prev->data, FD_VOTE_STATES_FOOTPRINT );
     203          36 :   bank->data->vote_states_prev_pool_idx = child_idx;
     204          36 :   bank->data->vote_states_prev_dirty    = 1;
     205          36 :   return fd_type_pun( child_vote_states_prev->data );
     206          36 : }
     207             : 
     208             : fd_vote_states_t const *
     209          30 : fd_bank_vote_states_prev_prev_query( fd_bank_t * bank ) {
     210             :   /* If the pool element hasn't been setup yet, then return NULL */
     211          30 :   fd_bank_vote_states_prev_prev_t * vote_states_prev_prev_pool = fd_bank_get_vote_states_prev_prev_pool( bank->data );
     212          30 :   if( FD_UNLIKELY( vote_states_prev_prev_pool==NULL ) ) {
     213           0 :     FD_LOG_CRIT(( "NULL vote states prev prev pool" ));
     214           0 :   }
     215          30 :   if( FD_UNLIKELY( bank->data->vote_states_prev_prev_pool_idx==fd_bank_vote_states_prev_prev_pool_idx_null( vote_states_prev_prev_pool ) ) ) {
     216           0 :     FD_LOG_CRIT(( "vote states prev prev pool element not set" ));
     217           0 :   }
     218          30 :   fd_bank_vote_states_prev_prev_t * bank_vote_states_prev_prev = fd_bank_vote_states_prev_prev_pool_ele( vote_states_prev_prev_pool, bank->data->vote_states_prev_prev_pool_idx );
     219          30 :   return fd_type_pun_const( bank_vote_states_prev_prev->data );
     220          30 : }
     221             : 
     222             : fd_vote_states_t *
     223          33 : fd_bank_vote_states_prev_prev_modify( fd_bank_t * bank ) {
     224             :   /* If the dirty flag is set, then we already have a pool element
     225             :      that was copied over for the current bank. We can simply just
     226             :      query the pool element and return it. */
     227          33 :   fd_bank_vote_states_prev_prev_t * vote_states_prev_prev_pool = fd_bank_get_vote_states_prev_prev_pool( bank->data );
     228          33 :   if( FD_UNLIKELY( vote_states_prev_prev_pool==NULL ) ) {
     229           0 :     FD_LOG_CRIT(( "NULL vote states prev prev pool" ));
     230           0 :   }
     231          33 :   if( bank->data->vote_states_prev_prev_dirty ) {
     232           3 :     fd_bank_vote_states_prev_prev_t * bank_vote_states_prev_prev = fd_bank_vote_states_prev_prev_pool_ele( vote_states_prev_prev_pool, bank->data->vote_states_prev_prev_pool_idx );
     233           3 :     return fd_type_pun( bank_vote_states_prev_prev->data );
     234           3 :   }
     235          30 :   fd_rwlock_write( &bank->locks->vote_states_prev_prev_pool_lock );
     236          30 :   if( FD_UNLIKELY( !fd_bank_vote_states_prev_prev_pool_free( vote_states_prev_prev_pool ) ) ) {
     237           0 :     FD_LOG_CRIT(( "Failed to acquire vote states prev prev pool element: pool is full" ));
     238           0 :   }
     239          30 :   fd_bank_vote_states_prev_prev_t * child_vote_states_prev_prev = fd_bank_vote_states_prev_prev_pool_ele_acquire( vote_states_prev_prev_pool );
     240          30 :   fd_rwlock_unwrite( &bank->locks->vote_states_prev_prev_pool_lock );
     241             :   /* If the dirty flag has not been set yet, we need to allocated a
     242             :      new pool element and copy over the data from the parent idx.
     243             :      We also need to mark the dirty flag. */
     244          30 :   ulong child_idx = fd_bank_vote_states_prev_prev_pool_idx( vote_states_prev_prev_pool, child_vote_states_prev_prev );
     245          30 :   fd_bank_vote_states_prev_prev_t * parent_vote_states_prev_prev = fd_bank_vote_states_prev_prev_pool_ele( vote_states_prev_prev_pool, bank->data->vote_states_prev_prev_pool_idx );
     246          30 :   fd_memcpy( child_vote_states_prev_prev->data, parent_vote_states_prev_prev->data, FD_VOTE_STATES_FOOTPRINT );
     247          30 :   bank->data->vote_states_prev_prev_pool_idx = child_idx;
     248          30 :   bank->data->vote_states_prev_prev_dirty    = 1;
     249          30 :   return fd_type_pun( child_vote_states_prev_prev->data );
     250          30 : }
     251             : 
     252             : fd_cost_tracker_t *
     253          66 : fd_bank_cost_tracker_locking_modify( fd_bank_t * bank ) {
     254          66 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( bank->data );
     255          66 :   FD_TEST( bank->data->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) );
     256          66 :   uchar * cost_tracker_mem = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, bank->data->cost_tracker_pool_idx )->data;
     257          66 :   FD_TEST( cost_tracker_mem );
     258          66 :   fd_rwlock_write( &bank->locks->cost_tracker_lock[ bank->data->idx ] );
     259          66 :   return fd_type_pun( cost_tracker_mem );
     260          66 : }
     261             : 
     262             : void
     263          66 : fd_bank_cost_tracker_end_locking_modify( fd_bank_t * bank ) {
     264          66 :   fd_rwlock_unwrite( &bank->locks->cost_tracker_lock[ bank->data->idx ] );
     265          66 : }
     266             : 
     267             : fd_cost_tracker_t const *
     268           0 : fd_bank_cost_tracker_locking_query( fd_bank_t * bank ) {
     269           0 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( bank->data );
     270           0 :   FD_TEST( bank->data->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) );
     271           0 :   uchar * cost_tracker_mem = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, bank->data->cost_tracker_pool_idx )->data;
     272           0 :   FD_TEST( cost_tracker_mem );
     273           0 :   fd_rwlock_read( &bank->locks->cost_tracker_lock[ bank->data->idx ] );
     274           0 :   return fd_type_pun_const( cost_tracker_mem );
     275           0 : }
     276             : 
     277             : void
     278           0 : fd_bank_cost_tracker_end_locking_query( fd_bank_t * bank ) {
     279           0 :   fd_rwlock_unread( &bank->locks->cost_tracker_lock[ bank->data->idx ] );
     280           0 : }
     281             : 
     282             : fd_lthash_value_t const *
     283           0 : fd_bank_lthash_locking_query( fd_bank_t * bank ) {
     284           0 :   fd_rwlock_read( &bank->locks->lthash_lock[ bank->data->idx ] );
     285           0 :   return &bank->data->non_cow.lthash;
     286           0 : }
     287             : 
     288             : void
     289           0 : fd_bank_lthash_end_locking_query( fd_bank_t * bank ) {
     290           0 :   fd_rwlock_unread( &bank->locks->lthash_lock[ bank->data->idx ] );
     291           0 : }
     292             : 
     293             : fd_lthash_value_t *
     294        1110 : fd_bank_lthash_locking_modify( fd_bank_t * bank ) {
     295        1110 :   fd_rwlock_write( &bank->locks->lthash_lock[ bank->data->idx ] );
     296        1110 :   return &bank->data->non_cow.lthash;
     297        1110 : }
     298             : 
     299             : void
     300        1110 : fd_bank_lthash_end_locking_modify( fd_bank_t * bank ) {
     301        1110 :   fd_rwlock_unwrite( &bank->locks->lthash_lock[ bank->data->idx ] );
     302        1110 : }
     303             : 
     304             : /* Bank accesssors */
     305             : 
     306             : #define X(type, name)                                                   \
     307             :   type const *                                                          \
     308        9468 :   fd_bank_##name##_query( fd_bank_t const * bank ) {                    \
     309        9468 :     return (type const *)fd_type_pun_const( bank->data->non_cow.name ); \
     310        9468 :   }                                                                     \
     311             :   type *                                                                \
     312       17016 :   fd_bank_##name##_modify( fd_bank_t * bank ) {                         \
     313       17016 :     return (type *)fd_type_pun( bank->data->non_cow.name );             \
     314       17016 :   }                                                                     \
     315             :   void                                                                  \
     316        2277 :   fd_bank_##name##_set( fd_bank_t * bank, type value ) {                \
     317        2277 :     FD_STORE( type, bank->data->non_cow.name, value );                  \
     318        2277 :   }                                                                     \
     319             :   type                                                                  \
     320        9906 :   fd_bank_##name##_get( fd_bank_t const * bank ) {                      \
     321        9906 :     type val = FD_LOAD( type, bank->data->non_cow.name );               \
     322        9906 :     return val;                                                         \
     323        9906 :   }
     324             : FD_BANKS_ITER(X)
     325             : #undef X
     326             : 
     327             : /**********************************************************************/
     328             : 
     329             : ulong
     330        5199 : fd_banks_align( void ) {
     331             :   /* TODO: The magic number here can probably be removed. */
     332        5199 :   return 128UL;
     333        5199 : }
     334             : 
     335             : ulong
     336             : fd_banks_footprint( ulong max_total_banks,
     337         612 :                     ulong max_fork_width ) {
     338             : 
     339             :   /* max_fork_width is used in the macro below. */
     340             : 
     341         612 :   ulong l = FD_LAYOUT_INIT;
     342         612 :   l = FD_LAYOUT_APPEND( l, fd_banks_align(),                           sizeof(fd_banks_data_t) );
     343         612 :   l = FD_LAYOUT_APPEND( l, fd_banks_pool_align(),                      fd_banks_pool_footprint( max_total_banks ) );
     344         612 :   l = FD_LAYOUT_APPEND( l, fd_bank_epoch_rewards_pool_align(),         fd_bank_epoch_rewards_pool_footprint( max_fork_width ) );
     345         612 :   l = FD_LAYOUT_APPEND( l, fd_bank_epoch_leaders_pool_align(),         fd_bank_epoch_leaders_pool_footprint( max_fork_width ) );
     346         612 :   l = FD_LAYOUT_APPEND( l, fd_bank_vote_states_pool_align(),           fd_bank_vote_states_pool_footprint( max_total_banks ) );
     347         612 :   l = FD_LAYOUT_APPEND( l, fd_bank_vote_states_prev_pool_align(),      fd_bank_vote_states_prev_pool_footprint( max_fork_width ) );
     348         612 :   l = FD_LAYOUT_APPEND( l, fd_bank_vote_states_prev_prev_pool_align(), fd_bank_vote_states_prev_prev_pool_footprint( max_fork_width ) );
     349         612 :   l = FD_LAYOUT_APPEND( l, fd_bank_cost_tracker_pool_align(),          fd_bank_cost_tracker_pool_footprint( max_fork_width ) );
     350         612 :   return FD_LAYOUT_FINI( l, fd_banks_align() );
     351         612 : }
     352             : 
     353             : void *
     354             : fd_banks_new( void * shmem,
     355             :               ulong  max_total_banks,
     356             :               ulong  max_fork_width,
     357             :               int    larger_max_cost_per_block,
     358         306 :               ulong  seed ) {
     359         306 :   if( FD_UNLIKELY( !shmem ) ) {
     360           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     361           0 :     return NULL;
     362           0 :   }
     363             : 
     364         306 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_banks_align() ) ) ) {
     365           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     366           0 :     return NULL;
     367           0 :   }
     368             : 
     369         306 :   if( FD_UNLIKELY( max_total_banks>=FD_BANKS_MAX_BANKS ) ) {
     370           0 :     FD_LOG_WARNING(( "max_total_banks is too large" ));
     371           0 :     return NULL;
     372           0 :   }
     373             : 
     374         306 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     375         306 :   fd_banks_data_t * banks_data                = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(),                           sizeof(fd_banks_data_t) );
     376         306 :   void *       pool_mem                       = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(),                      fd_banks_pool_footprint( max_total_banks ) );
     377         306 :   void *       epoch_rewards_pool_mem         = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_epoch_rewards_pool_align(),         fd_bank_epoch_rewards_pool_footprint( max_fork_width ) );
     378         306 :   void *       epoch_leaders_pool_mem         = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_epoch_leaders_pool_align(),         fd_bank_epoch_leaders_pool_footprint( max_fork_width ) );
     379         306 :   void *       vote_states_pool_mem           = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_vote_states_pool_align(),           fd_bank_vote_states_pool_footprint( max_total_banks ) );
     380         306 :   void *       vote_states_prev_pool_mem      = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_vote_states_prev_pool_align(),      fd_bank_vote_states_prev_pool_footprint( max_fork_width ) );
     381         306 :   void *       vote_states_prev_prev_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_vote_states_prev_prev_pool_align(), fd_bank_vote_states_prev_prev_pool_footprint( max_fork_width ) );
     382         306 :   void *       cost_tracker_pool_mem          = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_cost_tracker_pool_align(),          fd_bank_cost_tracker_pool_footprint( max_fork_width ) );
     383             : 
     384         306 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() ) != (ulong)banks_data + fd_banks_footprint( max_total_banks, max_fork_width ) ) ) {
     385           0 :     FD_LOG_WARNING(( "fd_banks_new: bad layout" ));
     386           0 :     return NULL;
     387           0 :   }
     388             : 
     389         306 :   void * pool = fd_banks_pool_new( pool_mem, max_total_banks );
     390         306 :   if( FD_UNLIKELY( !pool ) ) {
     391           0 :     FD_LOG_WARNING(( "Failed to create bank pool" ));
     392           0 :     return NULL;
     393           0 :   }
     394             : 
     395         306 :   fd_bank_data_t * bank_pool = fd_banks_pool_join( pool );
     396         306 :   if( FD_UNLIKELY( !bank_pool ) ) {
     397           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     398           0 :     return NULL;
     399           0 :   }
     400             : 
     401             :   /* Mark all of the banks as not initialized. */
     402         732 :   for( ulong i=0UL; i<max_total_banks; i++ ) {
     403         426 :     fd_bank_data_t * bank = fd_banks_pool_ele( bank_pool, i );
     404         426 :     if( FD_UNLIKELY( !bank ) ) {
     405           0 :       FD_LOG_WARNING(( "Failed to get bank" ));
     406           0 :       return NULL;
     407           0 :     }
     408         426 :     bank->flags = 0UL;
     409         426 :   }
     410             : 
     411             :   /* Assign offset of the bank pool to the banks object. */
     412             : 
     413         306 :   fd_banks_set_bank_pool( banks_data, bank_pool );
     414             : 
     415             :   /* Create the pools for the non-inlined fields.  Also new() and join()
     416             :      each of the elements in the pool as well as set up the lock for
     417             :      each of the pools. */
     418             : 
     419         306 :   fd_bank_epoch_rewards_t * epoch_rewards_pool = fd_bank_epoch_rewards_pool_join( fd_bank_epoch_rewards_pool_new( epoch_rewards_pool_mem, max_fork_width ) );
     420         306 :   if( FD_UNLIKELY( !epoch_rewards_pool ) ) {
     421           0 :     FD_LOG_WARNING(( "Failed to create epoch rewards pool" ));
     422           0 :     return NULL;
     423           0 :   }
     424         306 :   fd_banks_set_epoch_rewards_pool( banks_data, epoch_rewards_pool );
     425         660 :   for( ulong i=0UL; i<max_fork_width; i++ ) {
     426         354 :     fd_bank_epoch_rewards_t * epoch_rewards = fd_bank_epoch_rewards_pool_ele( epoch_rewards_pool, i );
     427         354 :     if( FD_UNLIKELY( !fd_epoch_rewards_join( fd_epoch_rewards_new( epoch_rewards->data, FD_RUNTIME_MAX_STAKE_ACCOUNTS, seed ) ) ) ) {
     428           0 :       FD_LOG_WARNING(( "Failed to create epoch rewards" ));
     429           0 :       return NULL;
     430           0 :     }
     431         354 :   }
     432             : 
     433         306 :   fd_bank_epoch_leaders_t * epoch_leaders_pool = fd_bank_epoch_leaders_pool_join( fd_bank_epoch_leaders_pool_new( epoch_leaders_pool_mem, max_fork_width ) );
     434         306 :   if( FD_UNLIKELY( !epoch_leaders_pool ) ) {
     435           0 :     FD_LOG_WARNING(( "Failed to create epoch leaders pool" ));
     436           0 :     return NULL;
     437           0 :   }
     438         306 :   fd_banks_set_epoch_leaders_pool( banks_data, epoch_leaders_pool );
     439             : 
     440         306 :   fd_bank_vote_states_t * vote_states_pool = fd_bank_vote_states_pool_join( fd_bank_vote_states_pool_new( vote_states_pool_mem, max_total_banks ) );
     441         306 :   if( FD_UNLIKELY( !vote_states_pool ) ) {
     442           0 :     FD_LOG_WARNING(( "Failed to create vote states pool" ));
     443           0 :     return NULL;
     444           0 :   }
     445         306 :   fd_banks_set_vote_states_pool( banks_data, vote_states_pool );
     446         306 :   fd_bank_vote_states_t * vote_states = fd_bank_vote_states_pool_ele( vote_states_pool, 0UL );
     447         306 :   if( FD_UNLIKELY( !fd_vote_states_join( fd_vote_states_new( vote_states->data, FD_RUNTIME_MAX_VOTE_ACCOUNTS, seed ) ) ) ) {
     448           0 :     FD_LOG_WARNING(( "Failed to create vote states" ));
     449           0 :     return NULL;
     450           0 :   }
     451             : 
     452         306 :   fd_bank_vote_states_prev_t * vote_states_prev_pool = fd_bank_vote_states_prev_pool_join( fd_bank_vote_states_prev_pool_new( vote_states_prev_pool_mem, max_fork_width ) );
     453         306 :   if( FD_UNLIKELY( !vote_states_prev_pool ) ) {
     454           0 :     FD_LOG_WARNING(( "Failed to create vote states prev pool" ));
     455           0 :     return NULL;
     456           0 :   }
     457         306 :   fd_banks_set_vote_states_prev_pool( banks_data, vote_states_prev_pool );
     458         306 :   fd_bank_vote_states_prev_t * vote_states_prev = fd_bank_vote_states_prev_pool_ele( vote_states_prev_pool, 0UL );
     459         306 :   if( FD_UNLIKELY( !fd_vote_states_join( fd_vote_states_new( vote_states_prev->data, FD_RUNTIME_MAX_VOTE_ACCOUNTS, seed ) ) ) ) {
     460           0 :     FD_LOG_WARNING(( "Failed to create vote states prev" ));
     461           0 :     return NULL;
     462           0 :   }
     463             : 
     464         306 :   fd_bank_vote_states_prev_prev_t * vote_states_prev_prev_pool = fd_bank_vote_states_prev_prev_pool_join( fd_bank_vote_states_prev_prev_pool_new( vote_states_prev_prev_pool_mem, max_fork_width ) );
     465         306 :   if( FD_UNLIKELY( !vote_states_prev_prev_pool ) ) {
     466           0 :     FD_LOG_WARNING(( "Failed to create vote states prev prev pool" ));
     467           0 :     return NULL;
     468           0 :   }
     469         306 :   fd_banks_set_vote_states_prev_prev_pool( banks_data, vote_states_prev_prev_pool );
     470         306 :   fd_bank_vote_states_prev_prev_t * vote_states_prev_prev = fd_bank_vote_states_prev_prev_pool_ele( vote_states_prev_prev_pool, 0UL );
     471         306 :   if( FD_UNLIKELY( !fd_vote_states_join( fd_vote_states_new( vote_states_prev_prev->data, FD_RUNTIME_MAX_VOTE_ACCOUNTS, seed ) ) ) ) {
     472           0 :     FD_LOG_WARNING(( "Failed to create vote states prev prev" ));
     473           0 :     return NULL;
     474           0 :   }
     475             : 
     476         306 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_cost_tracker_pool_join( fd_bank_cost_tracker_pool_new( cost_tracker_pool_mem, max_fork_width ) );
     477         306 :   if( FD_UNLIKELY( !cost_tracker_pool ) ) {
     478           0 :     FD_LOG_WARNING(( "Failed to create cost tracker pool" ));
     479           0 :     return NULL;
     480           0 :   }
     481         306 :   fd_banks_set_cost_tracker_pool( banks_data, cost_tracker_pool );
     482         660 :   for( ulong i=0UL; i<max_fork_width; i++ ) {
     483         354 :     fd_bank_cost_tracker_t * cost_tracker = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, i );
     484         354 :     if( FD_UNLIKELY( !fd_cost_tracker_join( fd_cost_tracker_new( cost_tracker->data, larger_max_cost_per_block, seed ) ) ) ) {
     485           0 :       FD_LOG_WARNING(( "Failed to create cost tracker" ));
     486           0 :       return NULL;
     487           0 :     }
     488         354 :   }
     489             : 
     490             :   /* For each bank, we need to set the offset of the pools and locks
     491             :      for each of the non-inlined fields. */
     492             : 
     493         732 :   for( ulong i=0UL; i<max_total_banks; i++ ) {
     494             : 
     495         426 :     fd_bank_data_t * bank = fd_banks_pool_ele( bank_pool, i );
     496             : 
     497         426 :     fd_bank_epoch_rewards_t * epoch_rewards_pool = fd_banks_get_epoch_rewards_pool( banks_data );
     498         426 :     fd_bank_set_epoch_rewards_pool( bank, epoch_rewards_pool );
     499             : 
     500         426 :     fd_bank_epoch_leaders_t * epoch_leaders_pool = fd_banks_get_epoch_leaders_pool( banks_data );
     501         426 :     fd_bank_set_epoch_leaders_pool( bank, epoch_leaders_pool );
     502             : 
     503         426 :     fd_bank_vote_states_t * vote_states_pool = fd_banks_get_vote_states_pool( banks_data );
     504         426 :     fd_bank_set_vote_states_pool( bank, vote_states_pool );
     505             : 
     506         426 :     fd_bank_vote_states_prev_t * vote_states_prev_pool = fd_banks_get_vote_states_prev_pool( banks_data );
     507         426 :     fd_bank_set_vote_states_prev_pool( bank, vote_states_prev_pool );
     508             : 
     509         426 :     fd_bank_vote_states_prev_prev_t * vote_states_prev_prev_pool = fd_banks_get_vote_states_prev_prev_pool( banks_data );
     510         426 :     fd_bank_set_vote_states_prev_prev_pool( bank, vote_states_prev_prev_pool );
     511             : 
     512         426 :     fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks_data );
     513         426 :     fd_bank_set_cost_tracker_pool( bank, cost_tracker_pool );
     514             : 
     515         426 :     if( FD_UNLIKELY( !fd_stake_delegations_join( fd_stake_delegations_new( bank->stake_delegations_delta, seed, FD_STAKE_DELEGATIONS_MAX_PER_SLOT, 1 ) ) ) ) {
     516           0 :       FD_LOG_WARNING(( "Failed to create stake delegations" ));
     517           0 :       return NULL;
     518           0 :     }
     519         426 :   }
     520             : 
     521         306 :   banks_data->max_total_banks = max_total_banks;
     522         306 :   banks_data->max_fork_width  = max_fork_width;
     523         306 :   banks_data->root_idx        = ULONG_MAX;
     524         306 :   banks_data->bank_seq        = 0UL;  /* FIXME randomize across runs? */
     525             : 
     526         306 :   if( FD_UNLIKELY( !fd_stake_delegations_new( banks_data->stake_delegations_root, 0UL, FD_RUNTIME_MAX_STAKE_ACCOUNTS, 0 ) ) ) {
     527           0 :     FD_LOG_WARNING(( "Unable to create stake delegations root" ));
     528           0 :     return NULL;
     529           0 :   }
     530             : 
     531         306 :   FD_COMPILER_MFENCE();
     532         306 :   FD_VOLATILE( banks_data->magic ) = FD_BANKS_MAGIC;
     533         306 :   FD_COMPILER_MFENCE();
     534             : 
     535         306 :   return shmem;
     536         306 : }
     537             : 
     538             : fd_banks_t *
     539             : fd_banks_join( fd_banks_t * banks_ljoin,
     540             :                void *       banks_data_mem,
     541         309 :                void *       banks_locks_mem ) {
     542         309 :   fd_banks_data_t *  banks_data  = (fd_banks_data_t *)banks_data_mem;
     543         309 :   fd_banks_locks_t * banks_locks = (fd_banks_locks_t *)banks_locks_mem;
     544             : 
     545         309 :   if( FD_UNLIKELY( !banks_data ) ) {
     546           0 :     FD_LOG_WARNING(( "NULL banks data" ));
     547           0 :     return NULL;
     548           0 :   }
     549             : 
     550         309 :   if( FD_UNLIKELY( !banks_locks ) ) {
     551           3 :     FD_LOG_WARNING(( "NULL banks locks" ));
     552           3 :     return NULL;
     553           3 :   }
     554             : 
     555         306 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)banks_data, fd_banks_align() ) ) ) {
     556           0 :     FD_LOG_WARNING(( "misaligned banks" ));
     557           0 :     return NULL;
     558           0 :   }
     559             : 
     560         306 :   if( FD_UNLIKELY( banks_data->magic!=FD_BANKS_MAGIC ) ) {
     561           0 :     FD_LOG_WARNING(( "Invalid banks magic" ));
     562           0 :     return NULL;
     563           0 :   }
     564             : 
     565         306 :   FD_SCRATCH_ALLOC_INIT( l, banks_data );
     566         306 :   banks_data                            = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(),                           sizeof(fd_banks_data_t) );
     567         306 :   void * pool_mem                       = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(),                      fd_banks_pool_footprint( banks_data->max_total_banks ) );
     568         306 :   void * epoch_rewards_pool_mem         = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_epoch_rewards_pool_align(),         fd_bank_epoch_rewards_pool_footprint( banks_data->max_fork_width ) );
     569         306 :   void * epoch_leaders_pool_mem         = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_epoch_leaders_pool_align(),         fd_bank_epoch_leaders_pool_footprint( banks_data->max_fork_width ) );
     570         306 :   void * vote_states_pool_mem           = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_vote_states_pool_align(),           fd_bank_vote_states_pool_footprint( banks_data->max_total_banks ) );
     571         306 :   void * vote_states_prev_pool_mem      = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_vote_states_prev_pool_align(),      fd_bank_vote_states_prev_pool_footprint( banks_data->max_fork_width ) );
     572         306 :   void * vote_states_prev_prev_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_vote_states_prev_prev_pool_align(), fd_bank_vote_states_prev_prev_pool_footprint( banks_data->max_fork_width ) );
     573         306 :   void * cost_tracker_pool_mem          = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_cost_tracker_pool_align(),          fd_bank_cost_tracker_pool_footprint( banks_data->max_fork_width ) );
     574             : 
     575         306 :   FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() );
     576             : 
     577         306 :   fd_bank_data_t * banks_pool = fd_banks_get_bank_pool( banks_data );
     578         306 :   if( FD_UNLIKELY( !banks_pool ) ) {
     579           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     580           0 :     return NULL;
     581           0 :   }
     582             : 
     583         306 :   if( FD_UNLIKELY( banks_pool!=fd_banks_pool_join( pool_mem ) ) ) {
     584           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     585           0 :     return NULL;
     586           0 :   }
     587             : 
     588         306 :   fd_bank_epoch_rewards_t * epoch_rewards_pool = fd_banks_get_epoch_rewards_pool( banks_data );
     589         306 :   if( FD_UNLIKELY( !epoch_rewards_pool ) ) {
     590           0 :     FD_LOG_WARNING(( "Failed to join epoch rewards pool" ));
     591           0 :     return NULL;
     592           0 :   }
     593             : 
     594         306 :   if( FD_UNLIKELY( epoch_rewards_pool!=fd_bank_epoch_rewards_pool_join( epoch_rewards_pool_mem ) ) ) {
     595           0 :     FD_LOG_WARNING(( "Failed to join epoch rewards pool" ));
     596           0 :     return NULL;
     597           0 :   }
     598             : 
     599         306 :   fd_bank_epoch_leaders_t * epoch_leaders_pool = fd_banks_get_epoch_leaders_pool( banks_data );
     600         306 :   if( FD_UNLIKELY( !epoch_leaders_pool ) ) {
     601           0 :     FD_LOG_WARNING(( "Failed to join epoch leaders pool" ));
     602           0 :     return NULL;
     603           0 :   }
     604             : 
     605         306 :   if( FD_UNLIKELY( epoch_leaders_pool!=fd_bank_epoch_leaders_pool_join( epoch_leaders_pool_mem ) ) ) {
     606           0 :     FD_LOG_WARNING(( "Failed to join epoch leaders pool" ));
     607           0 :     return NULL;
     608           0 :   }
     609             : 
     610         306 :   fd_bank_vote_states_t * vote_states_pool = fd_banks_get_vote_states_pool( banks_data );
     611         306 :   if( FD_UNLIKELY( !vote_states_pool ) ) {
     612           0 :     FD_LOG_WARNING(( "Failed to join vote states pool" ));
     613           0 :     return NULL;
     614           0 :   }
     615             : 
     616         306 :   if( FD_UNLIKELY( vote_states_pool!=fd_bank_vote_states_pool_join( vote_states_pool_mem ) ) ) {
     617           0 :     FD_LOG_WARNING(( "Failed to join vote states pool" ));
     618           0 :     return NULL;
     619           0 :   }
     620             : 
     621         306 :   fd_bank_vote_states_prev_t * vote_states_prev_pool = fd_banks_get_vote_states_prev_pool( banks_data );
     622         306 :   if( FD_UNLIKELY( !vote_states_prev_pool ) ) {
     623           0 :     FD_LOG_WARNING(( "Failed to join vote states prev pool" ));
     624           0 :     return NULL;
     625           0 :   }
     626             : 
     627         306 :   if( FD_UNLIKELY( vote_states_prev_pool!=fd_bank_vote_states_prev_pool_join( vote_states_prev_pool_mem ) ) ) {
     628           0 :     FD_LOG_WARNING(( "Failed to join vote states prev pool" ));
     629           0 :     return NULL;
     630           0 :   }
     631             : 
     632         306 :   fd_bank_vote_states_prev_prev_t * vote_states_prev_prev_pool = fd_banks_get_vote_states_prev_prev_pool( banks_data );
     633         306 :   if( FD_UNLIKELY( !vote_states_prev_prev_pool ) ) {
     634           0 :     FD_LOG_WARNING(( "Failed to join vote states prev prev pool" ));
     635           0 :     return NULL;
     636           0 :   }
     637             : 
     638         306 :   if( FD_UNLIKELY( vote_states_prev_prev_pool!=fd_bank_vote_states_prev_prev_pool_join( vote_states_prev_prev_pool_mem ) ) ) {
     639           0 :     FD_LOG_WARNING(( "Failed to join vote states prev prev pool" ));
     640           0 :     return NULL;
     641           0 :   }
     642             : 
     643             : 
     644         306 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks_data );
     645         306 :   if( FD_UNLIKELY( !cost_tracker_pool ) ) {
     646           0 :     FD_LOG_WARNING(( "Failed to join cost tracker pool" ));
     647           0 :     return NULL;
     648           0 :   }
     649             : 
     650         306 :   if( FD_UNLIKELY( cost_tracker_pool!=fd_bank_cost_tracker_pool_join( cost_tracker_pool_mem ) ) ) {
     651           0 :     FD_LOG_WARNING(( "Failed to join cost tracker pool" ));
     652           0 :     return NULL;
     653           0 :   }
     654             : 
     655         306 :   banks_ljoin->data  = banks_data;
     656         306 :   banks_ljoin->locks = banks_locks;
     657             : 
     658         306 :   return banks_ljoin;
     659         306 : }
     660             : 
     661             : fd_bank_t *
     662             : fd_banks_init_bank( fd_bank_t *  bank_l,
     663         306 :                     fd_banks_t * banks ) {
     664             : 
     665         306 :   if( FD_UNLIKELY( !banks ) ) {
     666           0 :     FD_LOG_WARNING(( "NULL banks" ));
     667           0 :     return NULL;
     668           0 :   }
     669             : 
     670         306 :   fd_bank_data_t * bank_pool = fd_banks_get_bank_pool( banks->data );
     671             : 
     672         306 :   fd_rwlock_write( &banks->locks->banks_lock );
     673             : 
     674         306 :   if( FD_UNLIKELY( !fd_banks_pool_free( bank_pool ) ) ) {
     675           0 :     FD_LOG_WARNING(( "Failed to acquire bank" ));
     676           0 :     fd_rwlock_unwrite( &banks->locks->banks_lock );
     677           0 :     return NULL;
     678           0 :   }
     679         306 :   fd_bank_data_t * bank = fd_banks_pool_ele_acquire( bank_pool );
     680         306 :   bank->bank_seq = FD_ATOMIC_FETCH_AND_ADD( &banks->data->bank_seq, 1UL );
     681             : 
     682         306 :   fd_memset( &bank->non_cow, 0, sizeof(bank->non_cow) );
     683             : 
     684         306 :   ulong null_idx    = fd_banks_pool_idx_null( bank_pool );
     685         306 :   bank->idx         = fd_banks_pool_idx( bank_pool, bank );
     686         306 :   bank->next        = null_idx;
     687         306 :   bank->parent_idx  = null_idx;
     688         306 :   bank->child_idx   = null_idx;
     689         306 :   bank->sibling_idx = null_idx;
     690             : 
     691             :   /* For all non-inlined fields make sure that each field is marked
     692             :      as not dirty and that the locks are initialized. */
     693             : 
     694         306 :   bank_l->data  = bank;
     695         306 :   bank_l->locks = banks->locks;
     696             : 
     697         306 :   bank->epoch_rewards_pool_idx         = fd_bank_epoch_rewards_pool_idx_null( fd_banks_get_epoch_rewards_pool( banks->data ) );
     698         306 :   bank->epoch_rewards_dirty            = 0;
     699             : 
     700         306 :   bank->epoch_leaders_pool_idx         = fd_bank_epoch_leaders_pool_idx_null( fd_banks_get_epoch_leaders_pool( banks->data ) );
     701         306 :   bank->epoch_leaders_dirty            = 0;
     702             : 
     703         306 :   bank->vote_states_pool_idx           = fd_bank_vote_states_pool_idx( fd_banks_get_vote_states_pool( banks->data ), fd_bank_vote_states_pool_ele_acquire( fd_banks_get_vote_states_pool( banks->data ) ) );
     704         306 :   bank->vote_states_dirty              = 1;
     705         306 :   fd_rwlock_new( &bank_l->locks->vote_states_lock[ bank->idx ] );
     706             : 
     707         306 :   bank->vote_states_prev_pool_idx      = fd_bank_vote_states_prev_pool_idx( fd_banks_get_vote_states_prev_pool( banks->data ), fd_bank_vote_states_prev_pool_ele_acquire( fd_banks_get_vote_states_prev_pool( banks->data ) ) );
     708         306 :   bank->vote_states_prev_dirty         = 1;
     709             : 
     710         306 :   bank->vote_states_prev_prev_pool_idx = fd_bank_vote_states_prev_prev_pool_idx( fd_banks_get_vote_states_prev_prev_pool( banks->data ), fd_bank_vote_states_prev_prev_pool_ele_acquire( fd_banks_get_vote_states_prev_prev_pool( banks->data ) ) );
     711         306 :   bank->vote_states_prev_prev_dirty    = 1;
     712             : 
     713         306 :   bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank ) );
     714         306 :   fd_rwlock_new( &bank_l->locks->cost_tracker_lock[ bank->idx ] );
     715             : 
     716         306 :   bank->stake_delegations_delta_dirty = 0;
     717         306 :   fd_rwlock_new( &bank_l->locks->stake_delegations_delta_lock[ bank->idx ] );
     718             : 
     719         306 :   fd_rwlock_new( &bank_l->locks->lthash_lock[ bank->idx ] );
     720             : 
     721         306 :   bank->flags |= FD_BANK_FLAGS_INIT | FD_BANK_FLAGS_REPLAYABLE | FD_BANK_FLAGS_FROZEN;
     722         306 :   bank->refcnt = 0UL;
     723             : 
     724         306 :   bank->first_fec_set_received_nanos      = fd_log_wallclock();
     725         306 :   bank->preparation_begin_nanos           = 0L;
     726         306 :   bank->first_transaction_scheduled_nanos = 0L;
     727         306 :   bank->last_transaction_finished_nanos   = 0L;
     728             : 
     729             :   /* Now that the node is inserted, update the root */
     730             : 
     731         306 :   banks->data->root_idx = bank->idx;
     732             : 
     733         306 :   fd_rwlock_unwrite( &banks->locks->banks_lock );
     734         306 :   bank_l->data  = bank;
     735         306 :   bank_l->locks = banks->locks;
     736         306 :   return bank_l;
     737         306 : }
     738             : 
     739             : fd_bank_t *
     740             : fd_banks_clone_from_parent( fd_bank_t *  bank_l,
     741             :                             fd_banks_t * banks,
     742         114 :                             ulong        child_bank_idx ) {
     743         114 :   fd_rwlock_write( &banks->locks->banks_lock );
     744             : 
     745         114 :   fd_bank_data_t * bank_pool = fd_banks_get_bank_pool( banks->data );
     746         114 :   if( FD_UNLIKELY( !bank_pool ) ) {
     747           0 :     FD_LOG_CRIT(( "invariant violation: failed to get bank pool" ));
     748           0 :   }
     749             : 
     750             :   /* Make sure that the bank is valid. */
     751             : 
     752         114 :   fd_bank_data_t * child_bank = fd_banks_pool_ele( bank_pool, child_bank_idx );
     753         114 :   if( FD_UNLIKELY( !child_bank ) ) {
     754           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu does not exist", child_bank_idx ));
     755           0 :   }
     756         114 :   if( FD_UNLIKELY( !(child_bank->flags&FD_BANK_FLAGS_INIT) ) ) {
     757           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu is not initialized", child_bank_idx ));
     758           0 :   }
     759             : 
     760             :   /* Then make sure that the parent bank is valid and frozen. */
     761             : 
     762         114 :   fd_bank_data_t * parent_bank = fd_banks_pool_ele( bank_pool, child_bank->parent_idx );
     763         114 :   if( FD_UNLIKELY( !parent_bank ) ) {
     764           0 :     FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu does not exist", child_bank->parent_idx ));
     765           0 :   }
     766         114 :   if( FD_UNLIKELY( !(parent_bank->flags&FD_BANK_FLAGS_FROZEN) ) ) {
     767           0 :     FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu is not frozen", child_bank->parent_idx ));
     768           0 :   }
     769             : 
     770             :   /* If the parent bank is dead, mark the child bank as dead and don't
     771             :      bother copying over any other fields. */
     772         114 :   if( FD_UNLIKELY( parent_bank->flags & FD_BANK_FLAGS_DEAD ) ) {
     773           0 :     child_bank->flags |= FD_BANK_FLAGS_DEAD;
     774           0 :     fd_rwlock_unwrite( &banks->locks->banks_lock );
     775           0 :     bank_l->data  = child_bank;
     776           0 :     bank_l->locks = banks->locks;
     777           0 :     return bank_l;
     778           0 :   }
     779             : 
     780             :   /* We can simply copy over all of the data in the bank struct that
     781             :      is not used for internal tracking that is laid out contiguously. */
     782             : 
     783         114 :   child_bank->non_cow = parent_bank->non_cow;
     784             : 
     785             :   /* For the other fields reset the state from the parent bank. */
     786             : 
     787         114 :   child_bank->epoch_rewards_dirty    = 0;
     788         114 :   child_bank->epoch_rewards_pool_idx = parent_bank->epoch_rewards_pool_idx;
     789             : 
     790         114 :   child_bank->epoch_leaders_dirty    = 0;
     791         114 :   child_bank->epoch_leaders_pool_idx = parent_bank->epoch_leaders_pool_idx;
     792             : 
     793         114 :   child_bank->vote_states_dirty    = 0;
     794         114 :   child_bank->vote_states_pool_idx = parent_bank->vote_states_pool_idx;
     795         114 :   fd_rwlock_new( &bank_l->locks->vote_states_lock[ child_bank->idx ] );
     796             : 
     797         114 :   child_bank->vote_states_prev_dirty    = 0;
     798         114 :   child_bank->vote_states_prev_pool_idx = parent_bank->vote_states_prev_pool_idx;
     799             : 
     800         114 :   child_bank->vote_states_prev_prev_dirty    = 0;
     801         114 :   child_bank->vote_states_prev_prev_pool_idx = parent_bank->vote_states_prev_prev_pool_idx;
     802             : 
     803             :   /* The stake delegation delta needs to be reset. */
     804             : 
     805         114 :   child_bank->stake_delegations_delta_dirty = 0;
     806         114 :   fd_rwlock_new( &bank_l->locks->stake_delegations_delta_lock[ child_bank->idx ] );
     807             : 
     808             :   /* The cost tracker pool needs to be set for the child bank and then
     809             :      a cost tracker pool element needs to be acquired. */
     810             : 
     811         114 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( child_bank );
     812         114 :   if( FD_UNLIKELY( fd_bank_cost_tracker_pool_free( cost_tracker_pool )==0UL ) ) {
     813           0 :     FD_LOG_CRIT(( "invariant violation: no free cost tracker pool elements" ));
     814           0 :   }
     815         114 :   child_bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_acquire( cost_tracker_pool );
     816         114 :   fd_rwlock_new( &bank_l->locks->cost_tracker_lock[ child_bank->idx ] );
     817             : 
     818             :   /* The lthash has already been copied over, we just need to initialize
     819             :      the lock for the current bank. */
     820             : 
     821         114 :   fd_rwlock_new( &bank_l->locks->lthash_lock[ child_bank->idx ] );
     822             : 
     823             :   /* At this point, the child bank is replayable. */
     824         114 :   child_bank->flags |= FD_BANK_FLAGS_REPLAYABLE;
     825             : 
     826         114 :   child_bank->refcnt = 0UL;
     827             : 
     828         114 :   fd_rwlock_unwrite( &banks->locks->banks_lock );
     829             : 
     830         114 :   bank_l->locks = banks->locks;
     831         114 :   bank_l->data  = child_bank;
     832         114 :   return bank_l;
     833         114 : }
     834             : 
     835             : /* Apply a fd_stake_delegations_t into the root. This assumes that there
     836             :    are no in-between, un-applied banks between the root and the bank
     837             :    being applied. This also assumes that the stake delegation object
     838             :    that is being applied is a delta. */
     839             : 
     840             : static inline void
     841             : fd_banks_stake_delegations_apply_delta( fd_bank_data_t *         bank,
     842         219 :                                         fd_stake_delegations_t * stake_delegations_base ) {
     843             : 
     844         219 :   if( !bank->stake_delegations_delta_dirty ) {
     845         162 :     return;
     846         162 :   }
     847             : 
     848          57 :   fd_stake_delegations_t * stake_delegations_delta = fd_stake_delegations_join( bank->stake_delegations_delta );
     849          57 :   if( FD_UNLIKELY( !stake_delegations_delta ) ) {
     850           0 :     FD_LOG_CRIT(( "Failed to join stake delegations delta" ));
     851           0 :   }
     852             : 
     853          57 :   fd_stake_delegations_iter_t iter_[1];
     854          57 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations_delta );
     855         141 :        !fd_stake_delegations_iter_done( iter );
     856          84 :        fd_stake_delegations_iter_next( iter ) ) {
     857          84 :     fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
     858          84 :     if( FD_LIKELY( !stake_delegation->is_tombstone ) ) {
     859          72 :       fd_stake_delegations_update(
     860          72 :           stake_delegations_base,
     861          72 :           &stake_delegation->stake_account,
     862          72 :           &stake_delegation->vote_account,
     863          72 :           stake_delegation->stake,
     864          72 :           stake_delegation->activation_epoch,
     865          72 :           stake_delegation->deactivation_epoch,
     866          72 :           stake_delegation->credits_observed,
     867          72 :           stake_delegation->warmup_cooldown_rate
     868          72 :       );
     869          72 :     } else {
     870          12 :       fd_stake_delegations_remove( stake_delegations_base, &stake_delegation->stake_account );
     871          12 :     }
     872          84 :   }
     873          57 : }
     874             : 
     875             : /* fd_bank_stake_delegation_apply_deltas applies all of the stake
     876             :    delegations for the entire direct ancestry from the bank to the
     877             :    root into a full fd_stake_delegations_t object. */
     878             : 
     879             : static inline void
     880             : fd_bank_stake_delegation_apply_deltas( fd_banks_t *             banks,
     881             :                                        fd_bank_t *              bank,
     882         102 :                                        fd_stake_delegations_t * stake_delegations ) {
     883             : 
     884             :   /* Naively what we want to do is iterate from the old root to the new
     885             :      root and apply the delta to the full state iteratively. */
     886             : 
     887             :   /* First, gather all of the pool indicies that we want to apply deltas
     888             :      for in reverse order starting from the new root. We want to exclude
     889             :      the old root since its delta has been applied previously. */
     890         102 :   ulong pool_indicies[ banks->data->max_total_banks ];
     891         102 :   ulong pool_indicies_len = 0UL;
     892             : 
     893         102 :   fd_bank_data_t * bank_pool = fd_banks_get_bank_pool( banks->data );
     894             : 
     895         102 :   ulong curr_idx = fd_banks_pool_idx( bank_pool, bank->data );
     896         321 :   while( curr_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
     897         219 :     pool_indicies[pool_indicies_len++] = curr_idx;
     898         219 :     fd_bank_data_t * curr_bank = fd_banks_pool_ele( bank_pool, curr_idx );
     899         219 :     curr_idx = curr_bank->parent_idx;
     900         219 :   }
     901             : 
     902             :   /* We have populated all of the indicies that we need to apply deltas
     903             :      from in reverse order. */
     904             : 
     905         321 :   for( ulong i=pool_indicies_len; i>0; i-- ) {
     906         219 :     ulong idx = pool_indicies[i-1UL];
     907         219 :     fd_banks_stake_delegations_apply_delta( fd_banks_pool_ele( bank_pool, idx ), stake_delegations );
     908         219 :   }
     909         102 : }
     910             : 
     911             : fd_stake_delegations_t *
     912          54 : fd_bank_stake_delegations_frontier_query( fd_banks_t * banks, fd_bank_t * bank ) {
     913             : 
     914          54 :   fd_rwlock_write( &banks->locks->banks_lock );
     915             : 
     916             :   /* First copy the rooted state into the frontier. */
     917          54 :   memcpy( banks->data->stake_delegations_frontier, banks->data->stake_delegations_root, FD_STAKE_DELEGATIONS_FOOTPRINT );
     918             : 
     919             :   /* Now apply all of the updates from the bank and all of its
     920             :      ancestors in order to the frontier. */
     921          54 :   fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( banks->data->stake_delegations_frontier );
     922          54 :   fd_bank_stake_delegation_apply_deltas( banks, bank, stake_delegations );
     923             : 
     924          54 :   fd_rwlock_unwrite( &banks->locks->banks_lock );
     925             : 
     926          54 :   return stake_delegations;
     927          54 : }
     928             : 
     929             : fd_stake_delegations_t *
     930           3 : fd_banks_stake_delegations_root_query( fd_banks_t * banks ) {
     931           3 :   return fd_stake_delegations_join( banks->data->stake_delegations_root );
     932           3 : }
     933             : 
     934             : void
     935             : fd_banks_advance_root( fd_banks_t * banks,
     936          48 :                        ulong        root_bank_idx ) {
     937             : 
     938          48 :   fd_rwlock_write( &banks->locks->banks_lock );
     939             : 
     940          48 :   fd_bank_data_t * bank_pool = fd_banks_get_bank_pool( banks->data );
     941             : 
     942          48 :   ulong null_idx = fd_banks_pool_idx_null( bank_pool );
     943             : 
     944             :   /* We want to replace the old root with the new root. This means we
     945             :      have to remove banks that aren't descendants of the new root. */
     946             : 
     947          48 :   fd_bank_t old_root[1];
     948          48 :   if( FD_UNLIKELY( !fd_banks_root( old_root, banks ) ) ) {
     949           0 :     FD_LOG_CRIT(( "invariant violation: old root is NULL" ));
     950           0 :   }
     951             : 
     952          48 :   if( FD_UNLIKELY( old_root->data->refcnt!=0UL ) ) {
     953           0 :     FD_LOG_CRIT(( "refcnt for old root bank at index %lu is nonzero: %lu", old_root->data->idx, old_root->data->refcnt ));
     954           0 :   }
     955             : 
     956          48 :   fd_bank_t new_root[1];
     957          48 :   if( FD_UNLIKELY( !fd_banks_bank_query( new_root, banks, root_bank_idx ) ) ) {
     958           0 :     FD_LOG_CRIT(( "invariant violation: new root is NULL" ));
     959           0 :   }
     960             : 
     961          48 :   if( FD_UNLIKELY( new_root->data->parent_idx!=old_root->data->idx ) ) {
     962           0 :     FD_LOG_CRIT(( "invariant violation: trying to advance root bank by more than one" ));
     963           0 :   }
     964             : 
     965          48 :   fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( banks->data->stake_delegations_root );
     966          48 :   fd_bank_stake_delegation_apply_deltas( banks, new_root, stake_delegations );
     967          48 :   new_root->data->stake_delegations_delta_dirty = 0;
     968             : 
     969             :   /* Now that the deltas have been applied, we can remove all nodes
     970             :      that are not direct descendants of the new root. */
     971          48 :   fd_bank_data_t * head = fd_banks_pool_ele( bank_pool, old_root->data->idx );
     972          48 :   head->next            = fd_banks_pool_idx_null( bank_pool );
     973          48 :   fd_bank_data_t * tail = head;
     974             : 
     975         129 :   while( head ) {
     976          81 :     fd_bank_data_t * child = fd_banks_pool_ele( bank_pool, head->child_idx );
     977             : 
     978         162 :     while( FD_LIKELY( child ) ) {
     979             : 
     980          81 :       if( FD_LIKELY( child!=new_root->data ) ) {
     981          33 :         if( FD_UNLIKELY( child->refcnt!=0UL ) ) {
     982           0 :           FD_LOG_CRIT(( "refcnt for child bank at index %lu is %lu", child->idx, child->refcnt ));
     983           0 :         }
     984             : 
     985             :         /* Update tail pointers */
     986          33 :         tail->next = child->idx;
     987          33 :         tail       = fd_banks_pool_ele( bank_pool, tail->next );
     988          33 :         tail->next = fd_banks_pool_idx_null( bank_pool );
     989             : 
     990          33 :       }
     991             : 
     992          81 :       child = fd_banks_pool_ele( bank_pool, child->sibling_idx );
     993          81 :     }
     994             : 
     995          81 :     fd_bank_data_t * next = fd_banks_pool_ele( bank_pool, head->next );
     996             : 
     997             :     /* For the non-inlined CoW fields, if we are pruning a bank away and
     998             :        it holds ownership of a pool element, we need to release it if
     999             :        the new root isn't using the same pool element.  If it is, we
    1000             :        transfer ownership of this pool index to the new root. */
    1001             : 
    1002          81 :     fd_bank_epoch_rewards_t * epoch_rewards_pool = fd_bank_get_epoch_rewards_pool( new_root->data );
    1003          81 :     if( head->epoch_rewards_dirty ) {
    1004          18 :       if( head->epoch_rewards_pool_idx!=new_root->data->epoch_rewards_pool_idx ) {
    1005           6 :         fd_rwlock_write( &banks->locks->epoch_rewards_pool_lock );
    1006           6 :         fd_bank_epoch_rewards_pool_idx_release( epoch_rewards_pool, head->epoch_rewards_pool_idx );
    1007           6 :         fd_rwlock_unwrite( &banks->locks->epoch_rewards_pool_lock );
    1008          12 :       } else {
    1009          12 :         new_root->data->epoch_rewards_dirty = 1;
    1010          12 :       }
    1011          18 :     }
    1012             : 
    1013          81 :     fd_bank_epoch_leaders_t * epoch_leaders_pool = fd_bank_get_epoch_leaders_pool( new_root->data );
    1014          81 :     if( head->epoch_leaders_dirty ) {
    1015           9 :       if( head->epoch_leaders_pool_idx!=new_root->data->epoch_leaders_pool_idx ) {
    1016           3 :         fd_rwlock_write( &banks->locks->epoch_leaders_pool_lock );
    1017           3 :         fd_bank_epoch_leaders_pool_idx_release( epoch_leaders_pool, head->epoch_leaders_pool_idx );
    1018           3 :         fd_rwlock_unwrite( &banks->locks->epoch_leaders_pool_lock );
    1019           6 :       } else {
    1020           6 :         new_root->data->epoch_leaders_dirty = 1;
    1021           6 :       }
    1022           9 :     }
    1023             : 
    1024          81 :     fd_bank_vote_states_t * vote_states_pool = fd_bank_get_vote_states_pool( new_root->data );
    1025          81 :     if( head->vote_states_dirty ) {
    1026          48 :       if( head->vote_states_pool_idx!=new_root->data->vote_states_pool_idx ) {
    1027           9 :         fd_rwlock_write( &banks->locks->vote_states_pool_lock );
    1028           9 :         fd_bank_vote_states_pool_idx_release( vote_states_pool, head->vote_states_pool_idx );
    1029           9 :         fd_rwlock_unwrite( &banks->locks->vote_states_pool_lock );
    1030          39 :       } else {
    1031          39 :         new_root->data->vote_states_dirty = 1;
    1032          39 :       }
    1033          48 :     }
    1034             : 
    1035          81 :     fd_bank_vote_states_prev_t * vote_states_prev_pool = fd_bank_get_vote_states_prev_pool( new_root->data );
    1036          81 :     if( head->vote_states_prev_dirty ) {
    1037          48 :       if( head->vote_states_prev_pool_idx!=new_root->data->vote_states_prev_pool_idx ) {
    1038           9 :         fd_rwlock_write( &banks->locks->vote_states_prev_pool_lock );
    1039           9 :         fd_bank_vote_states_prev_pool_idx_release( vote_states_prev_pool, head->vote_states_prev_pool_idx );
    1040           9 :         fd_rwlock_unwrite( &banks->locks->vote_states_prev_pool_lock );
    1041          39 :       } else {
    1042          39 :         new_root->data->vote_states_prev_dirty = 1;
    1043          39 :       }
    1044          48 :     }
    1045             : 
    1046          81 :     fd_bank_vote_states_prev_prev_t * vote_states_prev_prev_pool = fd_bank_get_vote_states_prev_prev_pool( new_root->data );
    1047          81 :     if( head->vote_states_prev_prev_dirty ) {
    1048          48 :       if( head->vote_states_prev_prev_pool_idx!=new_root->data->vote_states_prev_prev_pool_idx ) {
    1049           9 :         fd_rwlock_write( &banks->locks->vote_states_prev_prev_pool_lock );
    1050           9 :         fd_bank_vote_states_prev_prev_pool_idx_release( vote_states_prev_prev_pool, head->vote_states_prev_prev_pool_idx );
    1051           9 :         fd_rwlock_unwrite( &banks->locks->vote_states_prev_prev_pool_lock );
    1052          39 :       } else {
    1053          39 :         new_root->data->vote_states_prev_prev_dirty = 1;
    1054          39 :       }
    1055          48 :     }
    1056             : 
    1057             :     /* It is possible for a bank that never finished replaying to be
    1058             :        pruned away.  If the bank was never frozen, then it's possible
    1059             :        that the bank still owns a cost tracker pool element.  If this
    1060             :        is the case, we need to release the pool element. */
    1061          81 :     if( head->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( head ) ) ) {
    1062           0 :       FD_TEST( !(head->flags&FD_BANK_FLAGS_FROZEN) && head->flags&FD_BANK_FLAGS_REPLAYABLE );
    1063           0 :       FD_LOG_DEBUG(( "releasing cost tracker pool element for bank at index %lu", head->idx ));
    1064           0 :       fd_bank_cost_tracker_pool_idx_release( fd_bank_get_cost_tracker_pool( head ), head->cost_tracker_pool_idx );
    1065           0 :       head->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( head ) );
    1066           0 :     }
    1067             : 
    1068          81 :     head->flags = 0UL;
    1069          81 :     fd_banks_pool_ele_release( bank_pool, head );
    1070          81 :     head = next;
    1071          81 :   }
    1072             : 
    1073          48 :   new_root->data->parent_idx = null_idx;
    1074          48 :   banks->data->root_idx      = new_root->data->idx;
    1075             : 
    1076          48 :   fd_rwlock_unwrite( &banks->locks->banks_lock );
    1077          48 : }
    1078             : 
    1079             : /* Is the fork tree starting at the given bank entirely eligible for
    1080             :    pruning?  Returns 1 for yes, 0 for no.
    1081             : 
    1082             :    See comment in fd_replay_tile.c for more details on safe pruning. */
    1083             : static int
    1084             : fd_banks_subtree_can_be_pruned( fd_bank_data_t * bank_pool,
    1085          27 :                                 fd_bank_data_t * bank ) {
    1086          27 :   if( FD_UNLIKELY( !bank ) ) {
    1087           0 :     FD_LOG_CRIT(( "invariant violation: bank is NULL" ));
    1088           0 :   }
    1089             : 
    1090          27 :   if( bank->refcnt!=0UL ) {
    1091           3 :     return 0;
    1092           3 :   }
    1093             : 
    1094             :   /* Recursively check all children. */
    1095          24 :   ulong child_idx = bank->child_idx;
    1096          33 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
    1097           9 :     fd_bank_data_t * child = fd_banks_pool_ele( bank_pool, child_idx );
    1098           9 :     if( !fd_banks_subtree_can_be_pruned( bank_pool, child ) ) {
    1099           0 :       return 0;
    1100           0 :     }
    1101           9 :     child_idx = child->sibling_idx;
    1102           9 :   }
    1103             : 
    1104          24 :   return 1;
    1105          24 : }
    1106             : 
    1107             : /* Mark everything in the fork tree starting at the given bank dead. */
    1108             : 
    1109             : static void
    1110             : fd_banks_subtree_mark_dead( fd_bank_data_t * bank_pool,
    1111          36 :                             fd_bank_data_t * bank ) {
    1112          36 :   if( FD_UNLIKELY( !bank ) ) {
    1113           0 :     FD_LOG_CRIT(( "invariant violation: bank is NULL" ));
    1114           0 :   }
    1115          36 :   if( FD_UNLIKELY( bank->flags & FD_BANK_FLAGS_ROOTED ) ) {
    1116           0 :     FD_LOG_CRIT(( "invariant violation: bank for idx %lu is rooted", bank->idx ));
    1117           0 :   }
    1118             : 
    1119          36 :   bank->flags |= FD_BANK_FLAGS_DEAD;
    1120             : 
    1121             :   /* Recursively mark all children as dead. */
    1122          36 :   ulong child_idx = bank->child_idx;
    1123          51 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
    1124          15 :     fd_bank_data_t * child = fd_banks_pool_ele( bank_pool, child_idx );
    1125          15 :     fd_banks_subtree_mark_dead( bank_pool, child );
    1126          15 :     child_idx = child->sibling_idx;
    1127          15 :   }
    1128          36 : }
    1129             : 
    1130             : int
    1131             : fd_banks_advance_root_prepare( fd_banks_t * banks,
    1132             :                                ulong        target_bank_idx,
    1133          15 :                                ulong *      advanceable_bank_idx_out ) {
    1134             :   /* TODO: An optimization here is to do a single traversal of the tree
    1135             :      that would mark minority forks as dead while accumulating
    1136             :      refcnts to determine which bank is the highest advanceable. */
    1137             : 
    1138          15 :   fd_bank_data_t * bank_pool = fd_banks_get_bank_pool( banks->data );
    1139          15 :   fd_rwlock_read( &banks->locks->banks_lock );
    1140             : 
    1141          15 :   fd_bank_t root[1];
    1142          15 :   if( FD_UNLIKELY( !fd_banks_root( root, banks ) ) ) {
    1143           0 :     FD_LOG_WARNING(( "failed to get root bank" ));
    1144           0 :     fd_rwlock_unread( &banks->locks->banks_lock );
    1145           0 :     return 0;
    1146           0 :   }
    1147             : 
    1148             :   /* Early exit if target is the same as the old root. */
    1149          15 :   if( FD_UNLIKELY( root->data->idx==target_bank_idx ) ) {
    1150           0 :     FD_LOG_WARNING(( "target bank_idx %lu is the same as the old root's bank index %lu", target_bank_idx, root->data->idx ));
    1151           0 :     fd_rwlock_unread( &banks->locks->banks_lock );
    1152           0 :     return 0;
    1153           0 :   }
    1154             : 
    1155             :   /* Early exit if the root bank still has a reference to it, we can't
    1156             :      advance from it unti it's released. */
    1157          15 :   if( FD_UNLIKELY( root->data->refcnt!=0UL ) ) {
    1158           0 :     fd_rwlock_unread( &banks->locks->banks_lock );
    1159           0 :     return 0;
    1160           0 :   }
    1161             : 
    1162          15 :   fd_bank_data_t * target_bank = fd_banks_pool_ele( bank_pool, target_bank_idx );
    1163          15 :   if( FD_UNLIKELY( !target_bank ) ) {
    1164           0 :     FD_LOG_CRIT(( "failed to get bank for valid pool idx %lu", target_bank_idx ));
    1165           0 :   }
    1166             : 
    1167             :   /* Mark every node from the target bank up through its parents to the
    1168             :      root as being rooted.  We also need to figure out the oldest,
    1169             :      non-rooted ancestor of the target bank since we only want to
    1170             :      advance our root bank by one. */
    1171          15 :   fd_bank_data_t * curr = target_bank;
    1172          15 :   fd_bank_data_t * prev = NULL;
    1173          57 :   while( curr && curr!=root->data ) {
    1174          42 :     curr->flags |= FD_BANK_FLAGS_ROOTED;
    1175          42 :     prev         = curr;
    1176          42 :     curr         = fd_banks_pool_ele( bank_pool, curr->parent_idx );
    1177          42 :   }
    1178             : 
    1179             :   /* If we didn't reach the old root or there is no parent, target is
    1180             :      not a descendant. */
    1181          15 :   if( FD_UNLIKELY( !curr || prev->parent_idx!=root->data->idx ) ) {
    1182           0 :     FD_LOG_CRIT(( "invariant violation: target bank_idx %lu is not a direct descendant of root bank_idx %lu %lu %lu", target_bank_idx, root->data->idx, prev->idx, prev->parent_idx ));
    1183           0 :   }
    1184             : 
    1185          15 :   curr = root->data;
    1186          33 :   while( curr && (curr->flags&FD_BANK_FLAGS_ROOTED) && curr!=target_bank ) { /* curr!=target_bank to avoid abandoning good forks. */
    1187          18 :     fd_bank_data_t * rooted_child = NULL;
    1188          18 :     ulong            child_idx    = curr->child_idx;
    1189          57 :     while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
    1190          39 :       fd_bank_data_t * child_bank = fd_banks_pool_ele( bank_pool, child_idx );
    1191          39 :       if( child_bank->flags&FD_BANK_FLAGS_ROOTED ) {
    1192          18 :         rooted_child = child_bank;
    1193          21 :       } else {
    1194             :         /* This is a minority fork. */
    1195          21 :         fd_banks_subtree_mark_dead( bank_pool, child_bank );
    1196          21 :       }
    1197          39 :       child_idx = child_bank->sibling_idx;
    1198          39 :     }
    1199          18 :     curr = rooted_child;
    1200          18 :   }
    1201             : 
    1202             :   /* We should mark the old root bank as dead. */
    1203          15 :   root->data->flags |= FD_BANK_FLAGS_DEAD;
    1204             : 
    1205             :   /* We will at most advance our root bank by one.  This means we can
    1206             :      advance our root bank by one if each of the siblings of the
    1207             :      potential new root are eligible for pruning.  Each of the sibling
    1208             :      subtrees can be pruned if the subtrees have no active references on
    1209             :      their bank. */
    1210          15 :   ulong advance_candidate_idx = prev->idx;
    1211          15 :   ulong child_idx = root->data->child_idx;
    1212          42 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
    1213          30 :     fd_bank_data_t * child_bank = fd_banks_pool_ele( bank_pool, child_idx );
    1214          30 :     if( child_idx!=advance_candidate_idx ) {
    1215          18 :       if( !fd_banks_subtree_can_be_pruned( bank_pool, child_bank ) ) {
    1216           3 :         fd_rwlock_unread( &banks->locks->banks_lock );
    1217           3 :         return 0;
    1218           3 :       }
    1219          18 :     }
    1220          27 :     child_idx = child_bank->sibling_idx;
    1221          27 :   }
    1222             : 
    1223          12 :   *advanceable_bank_idx_out = advance_candidate_idx;
    1224          12 :   fd_rwlock_unread( &banks->locks->banks_lock );
    1225          12 :   return 1;
    1226          15 : }
    1227             : 
    1228             : fd_bank_t *
    1229             : fd_banks_new_bank( fd_bank_t *  bank_l,
    1230             :                    fd_banks_t * banks,
    1231             :                    ulong        parent_bank_idx,
    1232         114 :                    long         now ) {
    1233             : 
    1234         114 :   fd_rwlock_write( &banks->locks->banks_lock );
    1235             : 
    1236         114 :   fd_bank_data_t * bank_pool = fd_banks_get_bank_pool( banks->data );
    1237         114 :   if( FD_UNLIKELY( !bank_pool ) ) {
    1238           0 :     FD_LOG_CRIT(( "invariant violation: failed to get bank pool" ));
    1239           0 :   }
    1240             : 
    1241         114 :   if( FD_UNLIKELY( fd_banks_pool_free( bank_pool )==0UL ) ) {
    1242           0 :     FD_LOG_CRIT(( "invariant violation: no free bank indices available" ));
    1243           0 :   }
    1244             : 
    1245         114 :   ulong child_bank_idx = fd_banks_pool_idx_acquire( bank_pool );
    1246             : 
    1247             :   /* Make sure that the bank is valid. */
    1248             : 
    1249         114 :   fd_bank_data_t * child_bank = fd_banks_pool_ele( bank_pool, child_bank_idx );
    1250         114 :   if( FD_UNLIKELY( !child_bank ) ) {
    1251           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu does not exist", child_bank_idx ));
    1252           0 :   }
    1253         114 :   if( FD_UNLIKELY( child_bank->flags&FD_BANK_FLAGS_INIT ) ) {
    1254           0 :     FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu is already initialized", child_bank_idx ));
    1255           0 :   }
    1256             : 
    1257         114 :   ulong null_idx = fd_banks_pool_idx_null( bank_pool );
    1258             : 
    1259         114 :   child_bank->bank_seq    = FD_ATOMIC_FETCH_AND_ADD( &banks->data->bank_seq, 1UL );
    1260         114 :   child_bank->idx         = child_bank_idx;
    1261         114 :   child_bank->parent_idx  = null_idx;
    1262         114 :   child_bank->child_idx   = null_idx;
    1263         114 :   child_bank->sibling_idx = null_idx;
    1264         114 :   child_bank->next        = null_idx;
    1265         114 :   child_bank->flags       = FD_BANK_FLAGS_INIT;
    1266             : 
    1267             :   /* Then make sure that the parent bank is valid and frozen. */
    1268             : 
    1269         114 :   fd_bank_data_t * parent_bank = fd_banks_pool_ele( bank_pool, parent_bank_idx );
    1270         114 :   if( FD_UNLIKELY( !parent_bank ) ) {
    1271           0 :     FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu does not exist", parent_bank_idx ));
    1272           0 :   }
    1273         114 :   if( FD_UNLIKELY( !(parent_bank->flags&FD_BANK_FLAGS_INIT) ) ) {
    1274           0 :     FD_LOG_CRIT(( "Invariant violation: parent bank with index %lu is uninitialized", parent_bank_idx ));
    1275           0 :   }
    1276             : 
    1277             :   /* Link node->parent */
    1278             : 
    1279         114 :   child_bank->parent_idx = parent_bank_idx;
    1280             : 
    1281             :   /* Link parent->node and sibling->node */
    1282             : 
    1283         114 :   if( FD_LIKELY( parent_bank->child_idx==null_idx ) ) {
    1284             : 
    1285             :     /* This is the first child so set as left-most child */
    1286             : 
    1287          87 :     parent_bank->child_idx = child_bank_idx;
    1288             : 
    1289          87 :   } else {
    1290             :     /* Already have children so iterate to right-most sibling. */
    1291             : 
    1292          27 :     fd_bank_data_t * curr_bank = fd_banks_pool_ele( bank_pool, parent_bank->child_idx );
    1293          27 :     if( FD_UNLIKELY( !curr_bank ) ) {
    1294           0 :       FD_LOG_CRIT(( "Invariant violation: child bank for bank index %lu does not exist", parent_bank->child_idx ));
    1295           0 :     }
    1296          33 :     while( curr_bank->sibling_idx != null_idx ) curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->sibling_idx );
    1297             : 
    1298             :     /* Link to right-most sibling. */
    1299             : 
    1300          27 :     curr_bank->sibling_idx = child_bank_idx;
    1301          27 :   }
    1302             : 
    1303         114 :   child_bank->epoch_rewards_dirty           = 0;
    1304         114 :   child_bank->epoch_leaders_dirty           = 0;
    1305         114 :   child_bank->vote_states_dirty             = 0;
    1306         114 :   child_bank->vote_states_prev_dirty        = 0;
    1307         114 :   child_bank->vote_states_prev_prev_dirty   = 0;
    1308         114 :   child_bank->stake_delegations_delta_dirty = 0;
    1309             : 
    1310         114 :   child_bank->first_fec_set_received_nanos      = now;
    1311         114 :   child_bank->first_transaction_scheduled_nanos = 0L;
    1312         114 :   child_bank->last_transaction_finished_nanos   = 0L;
    1313             : 
    1314         114 :   fd_rwlock_unwrite( &banks->locks->banks_lock );
    1315         114 :   bank_l->data  = child_bank;
    1316         114 :   bank_l->locks = banks->locks;
    1317         114 :   return bank_l;
    1318         114 : }
    1319             : 
    1320             : void
    1321             : fd_banks_mark_bank_dead( fd_banks_t * banks,
    1322           0 :                          fd_bank_t *  bank ) {
    1323           0 :   fd_rwlock_write( &banks->locks->banks_lock );
    1324             : 
    1325           0 :   fd_banks_subtree_mark_dead( fd_banks_get_bank_pool( banks->data ), bank->data );
    1326             : 
    1327           0 :   fd_rwlock_unwrite( &banks->locks->banks_lock );
    1328           0 : }
    1329             : 
    1330             : void
    1331             : fd_banks_mark_bank_frozen( fd_banks_t * banks,
    1332          90 :                            fd_bank_t *  bank ) {
    1333          90 :   if( FD_UNLIKELY( bank->data->flags&FD_BANK_FLAGS_FROZEN ) ) {
    1334           0 :     FD_LOG_CRIT(( "invariant violation: bank for idx %lu is already frozen", bank->data->idx ));
    1335           0 :   }
    1336             : 
    1337          90 :   fd_rwlock_write( &banks->locks->banks_lock );
    1338          90 :   bank->data->flags |= FD_BANK_FLAGS_FROZEN;
    1339             : 
    1340          90 :   if( FD_UNLIKELY( bank->data->cost_tracker_pool_idx==fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank->data ) ) ) ) {
    1341           0 :     FD_LOG_CRIT(( "invariant violation: cost tracker pool index is null" ));
    1342           0 :   }
    1343          90 :   fd_bank_cost_tracker_pool_idx_release( fd_bank_get_cost_tracker_pool( bank->data ), bank->data->cost_tracker_pool_idx );
    1344          90 :   bank->data->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank->data ) );
    1345          90 :   fd_rwlock_unwrite( &banks->locks->banks_lock );
    1346          90 : }
    1347             : 
    1348             : void
    1349             : fd_banks_clear_bank( fd_banks_t * banks,
    1350             :                      fd_bank_t *  bank,
    1351           3 :                      ulong        max_vote_accounts ) {
    1352             : 
    1353           3 :   fd_rwlock_read( &banks->locks->banks_lock );
    1354             :   /* Get the parent bank. */
    1355           3 :   fd_bank_data_t * parent_bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks->data ), bank->data->parent_idx );
    1356             : 
    1357           3 :   fd_memset( &bank->data->non_cow, 0, sizeof(bank->data->non_cow) );
    1358             : 
    1359           3 :   fd_bank_epoch_rewards_t * epoch_rewards_pool = fd_bank_get_epoch_rewards_pool( bank->data );
    1360           3 :   if( bank->data->epoch_rewards_dirty ) {
    1361           0 :     fd_bank_epoch_rewards_pool_idx_release( epoch_rewards_pool, bank->data->epoch_rewards_pool_idx );
    1362           0 :     bank->data->epoch_rewards_dirty = 0;
    1363           0 :     bank->data->epoch_rewards_pool_idx = parent_bank ? parent_bank->epoch_rewards_pool_idx : fd_bank_epoch_rewards_pool_idx_null( epoch_rewards_pool );
    1364           0 :   }
    1365             : 
    1366           3 :   fd_bank_epoch_leaders_t * epoch_leaders_pool = fd_bank_get_epoch_leaders_pool( bank->data );
    1367           3 :   if( bank->data->epoch_leaders_dirty ) {
    1368           0 :     fd_bank_epoch_leaders_pool_idx_release( epoch_leaders_pool, bank->data->epoch_leaders_pool_idx );
    1369           0 :     bank->data->epoch_leaders_dirty = 0;
    1370           0 :     bank->data->epoch_leaders_pool_idx = parent_bank ? parent_bank->epoch_leaders_pool_idx : fd_bank_epoch_leaders_pool_idx_null( epoch_leaders_pool );
    1371           0 :   }
    1372             : 
    1373           3 :   bank->data->vote_states_dirty = 1;
    1374           3 :   fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_locking_modify( bank ), max_vote_accounts, 999UL ) );
    1375           3 :   fd_bank_vote_states_end_locking_modify( bank );
    1376             : 
    1377           3 :   bank->data->vote_states_prev_dirty = 1;
    1378           3 :   fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_modify( bank ), max_vote_accounts, 999UL ) );
    1379             : 
    1380           3 :   bank->data->vote_states_prev_prev_dirty = 1;
    1381           3 :   fd_vote_states_join( fd_vote_states_new( fd_bank_vote_states_prev_prev_modify( bank ), max_vote_accounts, 999UL ) );
    1382             : 
    1383             :   /* We need to acquire a cost tracker element. */
    1384           3 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( bank->data );
    1385           3 :   if( FD_UNLIKELY( bank->data->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) ) ) {
    1386           3 :     fd_bank_cost_tracker_pool_idx_release( cost_tracker_pool, bank->data->cost_tracker_pool_idx );
    1387           3 :   }
    1388           3 :   bank->data->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_acquire( cost_tracker_pool );
    1389           3 :   fd_rwlock_unwrite( &banks->locks->cost_tracker_lock[ bank->data->idx ] );
    1390             : 
    1391           3 :   bank->data->stake_delegations_delta_dirty = 0;
    1392           3 :   fd_rwlock_unwrite( &banks->locks->stake_delegations_delta_lock[ bank->data->idx ] );
    1393             : 
    1394           3 :   fd_rwlock_unread( &banks->locks->banks_lock );
    1395           3 : }
    1396             : 
    1397             : void
    1398         366 : fd_banks_locks_init( fd_banks_locks_t * locks ) {
    1399         366 :   fd_rwlock_new( &locks->banks_lock );
    1400         366 :   fd_rwlock_new( &locks->epoch_rewards_pool_lock );
    1401         366 :   fd_rwlock_new( &locks->epoch_leaders_pool_lock );
    1402         366 :   fd_rwlock_new( &locks->vote_states_pool_lock );
    1403         366 :   fd_rwlock_new( &locks->vote_states_prev_pool_lock );
    1404         366 :   fd_rwlock_new( &locks->vote_states_prev_prev_pool_lock );
    1405             : 
    1406      749934 :   for( ulong i=0UL; i<FD_BANKS_MAX_BANKS; i++ ) {
    1407      749568 :     fd_rwlock_new( &locks->lthash_lock[i] );
    1408      749568 :     fd_rwlock_new( &locks->cost_tracker_lock[i] );
    1409      749568 :     fd_rwlock_new( &locks->stake_delegations_delta_lock[i] );
    1410      749568 :     fd_rwlock_new( &locks->vote_states_lock[i] );
    1411      749568 :   }
    1412         366 : }

Generated by: LCOV version 1.14