LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 355 842 42.2 %
Date: 2026-03-19 18:19:27 Functions: 87 204 42.6 %

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

Generated by: LCOV version 1.14