LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 689 972 70.9 %
Date: 2026-01-08 05:16:19 Functions: 73 206 35.4 %

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

Generated by: LCOV version 1.14