LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 637 874 72.9 %
Date: 2026-02-22 05:44:04 Functions: 77 202 38.1 %

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

Generated by: LCOV version 1.14