LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 591 715 82.7 %
Date: 2026-04-10 06:33:15 Functions: 52 60 86.7 %

          Line data    Source code
       1             : #include "fd_bank.h"
       2             : #include "fd_runtime_const.h"
       3             : #include "../rewards/fd_stake_rewards.h"
       4             : 
       5             : fd_lthash_value_t const *
       6          78 : fd_bank_lthash_locking_query( fd_bank_t * bank ) {
       7          78 :   fd_rwlock_read( &bank->lthash_lock );
       8          78 :   return &bank->f.lthash;
       9          78 : }
      10             : 
      11             : void
      12          78 : fd_bank_lthash_end_locking_query( fd_bank_t * bank ) {
      13          78 :   fd_rwlock_unread( &bank->lthash_lock );
      14          78 : }
      15             : 
      16             : fd_lthash_value_t *
      17        1995 : fd_bank_lthash_locking_modify( fd_bank_t * bank ) {
      18        1995 :   fd_rwlock_write( &bank->lthash_lock );
      19        1995 :   return &bank->f.lthash;
      20        1995 : }
      21             : 
      22             : void
      23        1995 : fd_bank_lthash_end_locking_modify( fd_bank_t * bank ) {
      24        1995 :   fd_rwlock_unwrite( &bank->lthash_lock );
      25        1995 : }
      26             : 
      27             : ulong
      28        5703 : fd_banks_align( void ) {
      29        5703 :   return FD_BANKS_ALIGN;
      30        5703 : }
      31             : 
      32             : static fd_bank_t *
      33        8475 : fd_banks_get_bank_pool( fd_banks_t * banks_data ) {
      34        8475 :   return fd_type_pun( (uchar *)banks_data + banks_data->pool_offset );
      35        8475 : }
      36             : 
      37             : static fd_bank_idx_seq_t *
      38         273 : fd_banks_get_dead_banks_deque( fd_banks_t * banks_data ) {
      39         273 :   return fd_type_pun( (uchar *)banks_data + banks_data->dead_banks_deque_offset );
      40         273 : }
      41             : 
      42             : static fd_epoch_leaders_t *
      43         804 : fd_banks_get_epoch_leaders( fd_banks_t * banks_data ) {
      44         804 :   return fd_type_pun( (uchar *)banks_data + banks_data->epoch_leaders_offset );
      45         804 : }
      46             : 
      47             : static fd_stake_delegations_t *
      48        2238 : fd_banks_get_stake_delegations( fd_banks_t * banks_data ) {
      49        2238 :   return fd_type_pun( (uchar *)banks_data + banks_data->stake_delegations_offset );
      50        2238 : }
      51             : 
      52             : static fd_bank_cost_tracker_t *
      53        2370 : fd_banks_get_cost_tracker_pool( fd_banks_t * banks_data ) {
      54        2370 :   return fd_type_pun( (uchar *)banks_data + banks_data->cost_tracker_pool_offset );
      55        2370 : }
      56             : 
      57             : static fd_stake_rewards_t *
      58         810 : fd_banks_get_stake_rewards( fd_banks_t * banks_data ) {
      59         810 :   return fd_type_pun( (uchar *)banks_data + banks_data->stake_rewards_offset );
      60         810 : }
      61             : 
      62             : static fd_vote_stakes_t *
      63        1620 : fd_banks_get_vote_stakes( fd_banks_t * banks_data ) {
      64        1620 :   return fd_type_pun( (uchar *)banks_data + banks_data->vote_stakes_pool_offset );
      65        1620 : }
      66             : 
      67             : static fd_epoch_credits_t *
      68         270 : fd_banks_get_epoch_credits( fd_banks_t * banks_data ) {
      69         270 :   return fd_type_pun( (uchar *)banks_data + banks_data->epoch_credits_offset );
      70         270 : }
      71             : 
      72             : static fd_stashed_commission_t *
      73           0 : fd_banks_get_snapshot_commission_t_3( fd_banks_t * banks_data ) {
      74           0 :   return fd_type_pun( (uchar *)banks_data + banks_data->snapshot_commission_t_3_offset );
      75           0 : }
      76             : 
      77             : fd_epoch_credits_t *
      78         270 : fd_bank_epoch_credits( fd_bank_t * bank ) {
      79         270 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
      80         270 :   return fd_banks_get_epoch_credits( banks_data );
      81         270 : }
      82             : 
      83             : ulong *
      84           0 : fd_bank_epoch_credits_len( fd_bank_t * bank ) {
      85           0 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
      86           0 :   return &banks_data->epoch_credits_len;
      87           0 : }
      88             : 
      89             : fd_stashed_commission_t *
      90           0 : fd_bank_snapshot_commission_t_3( fd_bank_t * bank ) {
      91           0 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
      92           0 :   return fd_banks_get_snapshot_commission_t_3( banks_data );
      93           0 : }
      94             : 
      95             : ulong *
      96           0 : fd_bank_snapshot_commission_t_3_len( fd_bank_t * bank ) {
      97           0 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
      98           0 :   return &banks_data->snapshot_commission_t_3_len;
      99           0 : }
     100             : 
     101             : fd_vote_stakes_t *
     102         843 : fd_bank_vote_stakes( fd_bank_t const * bank ) {
     103         843 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
     104         843 :   return fd_banks_get_vote_stakes( banks_data );
     105         843 : }
     106             : 
     107             : fd_stake_delegations_t *
     108          90 : fd_bank_stake_delegations_modify( fd_bank_t * bank ) {
     109          90 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
     110          90 :   return fd_banks_get_stake_delegations( banks_data );
     111          90 : }
     112             : 
     113             : fd_stake_rewards_t const *
     114         129 : fd_bank_stake_rewards_query( fd_bank_t * bank ) {
     115         129 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
     116         129 :   return fd_type_pun_const( fd_banks_get_stake_rewards( banks_data ) );
     117         129 : }
     118             : 
     119             : fd_stake_rewards_t *
     120         486 : fd_bank_stake_rewards_modify( fd_bank_t * bank ) {
     121         486 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
     122         486 :   return fd_banks_get_stake_rewards( banks_data );
     123         486 : }
     124             : 
     125             : fd_epoch_leaders_t const *
     126          18 : fd_bank_epoch_leaders_query( fd_bank_t const * bank ) {
     127          18 :   if( FD_UNLIKELY( bank->epoch_leaders_idx==ULONG_MAX ) ) {
     128           0 :     return NULL;
     129           0 :   }
     130          18 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
     131          18 :   return (fd_epoch_leaders_t const *)fd_type_pun( (uchar *)fd_banks_get_epoch_leaders( banks_data ) + bank->epoch_leaders_idx * banks_data->epoch_leaders_footprint );
     132          18 : }
     133             : 
     134             : fd_epoch_leaders_t *
     135         453 : fd_bank_epoch_leaders_modify( fd_bank_t * bank ) {
     136         453 :   ulong idx = bank->f.epoch % 2UL;
     137         453 :   bank->epoch_leaders_idx = idx;
     138         453 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
     139         453 :   return (fd_epoch_leaders_t *)fd_type_pun( (uchar *)fd_banks_get_epoch_leaders( banks_data ) + idx * banks_data->epoch_leaders_footprint );
     140         453 : }
     141             : 
     142             : fd_top_votes_t const *
     143         129 : fd_bank_top_votes_t_1_query( fd_bank_t const * bank ) {
     144         129 :   return fd_type_pun_const( bank->top_votes_t_1_mem );
     145         129 : }
     146             : 
     147             : fd_top_votes_t *
     148         309 : fd_bank_top_votes_t_1_modify( fd_bank_t * bank ) {
     149         309 :   return fd_type_pun( bank->top_votes_t_1_mem );
     150         309 : }
     151             : 
     152             : fd_top_votes_t const *
     153         414 : fd_bank_top_votes_t_2_query( fd_bank_t const * bank ) {
     154         414 :   return fd_type_pun_const( bank->top_votes_t_2_mem );
     155         414 : }
     156             : 
     157             : fd_top_votes_t *
     158         363 : fd_bank_top_votes_t_2_modify( fd_bank_t * bank ) {
     159         363 :   return fd_type_pun( bank->top_votes_t_2_mem );
     160         363 : }
     161             : 
     162             : fd_cost_tracker_t *
     163         321 : fd_bank_cost_tracker_modify( fd_bank_t * bank ) {
     164         321 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
     165         321 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks_data );
     166         321 :   FD_TEST( bank->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) );
     167         321 :   uchar * cost_tracker_mem = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, bank->cost_tracker_pool_idx )->data;
     168         321 :   return fd_type_pun( cost_tracker_mem );
     169         321 : }
     170             : 
     171             : fd_cost_tracker_t const *
     172           0 : fd_bank_cost_tracker_query( fd_bank_t * bank ) {
     173           0 :   fd_banks_t * banks_data = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
     174           0 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks_data );
     175           0 :   FD_TEST( bank->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) );
     176           0 :   uchar * cost_tracker_mem = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, bank->cost_tracker_pool_idx )->data;
     177           0 :   return fd_type_pun_const( cost_tracker_mem );
     178           0 : }
     179             : 
     180             : fd_bank_t *
     181        4629 : fd_banks_root( fd_banks_t * banks ) {
     182        4629 :   return fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), banks->root_idx );
     183        4629 : }
     184             : 
     185             : fd_bank_t *
     186             : fd_banks_bank_query( fd_banks_t * banks,
     187         999 :                      ulong        bank_idx ) {
     188         999 :   fd_bank_t * bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank_idx );
     189         999 :   if( FD_UNLIKELY( bank->state==FD_BANK_STATE_INACTIVE ) ) return NULL;
     190         954 :   return bank;
     191         999 : }
     192             : 
     193             : fd_bank_t *
     194             : fd_banks_get_parent( fd_banks_t * banks,
     195           0 :                      fd_bank_t *  bank ) {
     196           0 :   if( FD_UNLIKELY( bank->parent_idx==ULONG_MAX ) ) return NULL;
     197           0 :   return fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank->parent_idx );
     198           0 : }
     199             : 
     200             : int
     201           0 : fd_banks_is_full( fd_banks_t * banks ) {
     202           0 :   return fd_banks_pool_free( fd_banks_get_bank_pool( banks ) )==0UL ||
     203           0 :          fd_bank_cost_tracker_pool_free( fd_banks_get_cost_tracker_pool( banks ) )==0UL;
     204           0 : }
     205             : 
     206             : ulong
     207           9 : fd_banks_pool_used_cnt( fd_banks_t * banks ) {
     208           9 :   return fd_banks_pool_used( fd_banks_get_bank_pool( banks ) );
     209           9 : }
     210             : 
     211             : ulong
     212         345 : fd_banks_pool_max_cnt( fd_banks_t * banks ) {
     213         345 :   return fd_banks_pool_max( fd_banks_get_bank_pool( banks ) );
     214         345 : }
     215             : 
     216             : void
     217             : fd_banks_stake_delegations_evict_bank_fork( fd_banks_t * banks,
     218           0 :                                             fd_bank_t *  bank ) {
     219           0 :   if( bank->stake_delegations_fork_id!=USHORT_MAX ) {
     220           0 :     fd_stake_delegations_t * sd = fd_banks_get_stake_delegations( banks );
     221           0 :     fd_stake_delegations_evict_fork( sd, bank->stake_delegations_fork_id );
     222           0 :     bank->stake_delegations_fork_id = USHORT_MAX;
     223           0 :   }
     224           0 : }
     225             : 
     226             : ulong
     227             : fd_banks_footprint( ulong max_total_banks,
     228             :                     ulong max_fork_width,
     229             :                     ulong max_stake_accounts,
     230         675 :                     ulong max_vote_accounts ) {
     231             : 
     232             :   /* max_fork_width is used in the macro below. */
     233             : 
     234         675 :   ulong epoch_leaders_footprint = FD_EPOCH_LEADERS_FOOTPRINT( max_vote_accounts, FD_RUNTIME_SLOTS_PER_EPOCH );
     235         675 :   ulong expected_stake_accounts = fd_ulong_min( max_stake_accounts, FD_RUNTIME_EXPECTED_STAKE_ACCOUNTS );
     236         675 :   ulong expected_vote_accounts  = fd_ulong_min( max_vote_accounts, FD_RUNTIME_EXPECTED_VOTE_ACCOUNTS );
     237             : 
     238         675 :   ulong l = FD_LAYOUT_INIT;
     239         675 :   l = FD_LAYOUT_APPEND( l, fd_banks_align(),                  sizeof(fd_banks_t) );
     240         675 :   l = FD_LAYOUT_APPEND( l, fd_stake_delegations_align(),      fd_stake_delegations_footprint( max_stake_accounts, expected_stake_accounts, max_total_banks ) );
     241         675 :   l = FD_LAYOUT_APPEND( l, FD_EPOCH_LEADERS_ALIGN,            2UL * epoch_leaders_footprint );
     242         675 :   l = FD_LAYOUT_APPEND( l, fd_banks_pool_align(),             fd_banks_pool_footprint( max_total_banks ) );
     243         675 :   l = FD_LAYOUT_APPEND( l, fd_banks_dead_align(),             fd_banks_dead_footprint() );
     244         675 :   l = FD_LAYOUT_APPEND( l, fd_bank_cost_tracker_pool_align(), fd_bank_cost_tracker_pool_footprint( max_fork_width ) );
     245         675 :   l = FD_LAYOUT_APPEND( l, fd_stake_rewards_align(),          fd_stake_rewards_footprint( max_stake_accounts, expected_stake_accounts, max_fork_width ) );
     246         675 :   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 ) );
     247         675 :   l = FD_LAYOUT_APPEND( l, alignof(fd_epoch_credits_t),       sizeof(fd_epoch_credits_t) * max_vote_accounts );
     248         675 :   l = FD_LAYOUT_APPEND( l, alignof(fd_stashed_commission_t),  sizeof(fd_stashed_commission_t) * max_vote_accounts );
     249         675 :   return FD_LAYOUT_FINI( l, fd_banks_align() );
     250         675 : }
     251             : 
     252             : void *
     253             : fd_banks_new( void * shmem,
     254             :               ulong  max_total_banks,
     255             :               ulong  max_fork_width,
     256             :               ulong  max_stake_accounts,
     257             :               ulong  max_vote_accounts,
     258             :               int    larger_max_cost_per_block,
     259         333 :               ulong  seed ) {
     260         333 :   if( FD_UNLIKELY( !shmem ) ) {
     261           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     262           0 :     return NULL;
     263           0 :   }
     264             : 
     265         333 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_banks_align() ) ) ) {
     266           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     267           0 :     return NULL;
     268           0 :   }
     269             : 
     270         333 :   if( FD_UNLIKELY( max_total_banks>FD_BANKS_MAX_BANKS ) ) {
     271           0 :     FD_LOG_WARNING(( "max_total_banks is too large" ));
     272           0 :     return NULL;
     273           0 :   }
     274         333 :   if( FD_UNLIKELY( max_fork_width>FD_BANKS_MAX_BANKS ) ) {
     275           0 :     FD_LOG_WARNING(( "max_fork_width is too large" ));
     276           0 :     return NULL;
     277           0 :   }
     278             : 
     279         333 :   ulong epoch_leaders_footprint = FD_EPOCH_LEADERS_FOOTPRINT( max_vote_accounts, FD_RUNTIME_SLOTS_PER_EPOCH );
     280         333 :   ulong expected_stake_accounts = fd_ulong_min( max_stake_accounts, FD_RUNTIME_EXPECTED_STAKE_ACCOUNTS );
     281         333 :   ulong expected_vote_accounts  = fd_ulong_min( max_vote_accounts, FD_RUNTIME_EXPECTED_VOTE_ACCOUNTS );
     282             : 
     283         333 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     284         333 :   fd_banks_t * banks_data              = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(),                  sizeof(fd_banks_t) );
     285         333 :   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 ) );
     286         333 :   void *       epoch_leaders_mem       = FD_SCRATCH_ALLOC_APPEND( l, FD_EPOCH_LEADERS_ALIGN,            2UL * epoch_leaders_footprint );
     287         333 :   void *       pool_mem                = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(),             fd_banks_pool_footprint( max_total_banks ) );
     288         333 :   void *       dead_banks_deque_mem    = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_dead_align(),             fd_banks_dead_footprint() );
     289         333 :   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 ) );
     290         333 :   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 ) );
     291         333 :   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 ) );
     292         333 :   void *       epoch_credits_mem       = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_epoch_credits_t),       sizeof(fd_epoch_credits_t) * max_vote_accounts );
     293         333 :   void *       snapshot_commission_t_3 = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_stashed_commission_t),  sizeof(fd_stashed_commission_t) * max_vote_accounts );
     294             : 
     295         333 :   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 ) ) ) {
     296           0 :     FD_LOG_WARNING(( "fd_banks_new: bad layout" ));
     297           0 :     return NULL;
     298           0 :   }
     299             : 
     300         333 :   void * pool = fd_banks_pool_new( pool_mem, max_total_banks );
     301         333 :   if( FD_UNLIKELY( !pool ) ) {
     302           0 :     FD_LOG_WARNING(( "Failed to create bank pool" ));
     303           0 :     return NULL;
     304           0 :   }
     305             : 
     306         333 :   fd_bank_t * bank_pool = fd_banks_pool_join( pool );
     307         333 :   if( FD_UNLIKELY( !bank_pool ) ) {
     308           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     309           0 :     return NULL;
     310           0 :   }
     311             : 
     312         333 :   fd_bank_idx_seq_t * banks_dead_deque = fd_banks_dead_join( fd_banks_dead_new( dead_banks_deque_mem ) );
     313         333 :   if( FD_UNLIKELY( !banks_dead_deque ) ) {
     314           0 :     FD_LOG_WARNING(( "Failed to create banks dead deque" ));
     315           0 :     return NULL;
     316           0 :   }
     317         333 :   banks_data->dead_banks_deque_offset = (ulong)banks_dead_deque - (ulong)banks_data;
     318             : 
     319         333 :   banks_data->epoch_leaders_offset           = (ulong)epoch_leaders_mem - (ulong)banks_data;
     320         333 :   banks_data->epoch_leaders_footprint        = epoch_leaders_footprint;
     321         333 :   banks_data->pool_offset                    = (ulong)bank_pool - (ulong)banks_data;
     322         333 :   banks_data->epoch_credits_offset           = (ulong)epoch_credits_mem - (ulong)banks_data;
     323         333 :   banks_data->snapshot_commission_t_3_offset = (ulong)snapshot_commission_t_3 - (ulong)banks_data;
     324             : 
     325             :   /* Create the pools for the non-inlined fields.  Also new() and join()
     326             :      each of the elements in the pool as well as set up the lock for
     327             :      each of the pools. */
     328             : 
     329         333 :   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 ) );
     330         333 :   if( FD_UNLIKELY( !stake_delegations ) ) {
     331           0 :     FD_LOG_WARNING(( "Unable to create stake delegations root" ));
     332           0 :     return NULL;
     333           0 :   }
     334         333 :   banks_data->stake_delegations_offset = (ulong)stake_delegations - (ulong)banks_data;
     335             : 
     336         333 :   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 ) );
     337         333 :   if( FD_UNLIKELY( !cost_tracker_pool ) ) {
     338           0 :     FD_LOG_WARNING(( "Failed to create cost tracker pool" ));
     339           0 :     return NULL;
     340           0 :   }
     341         333 :   banks_data->cost_tracker_pool_offset = (ulong)cost_tracker_pool - (ulong)banks_data;
     342             : 
     343         837 :   for( ulong i=0UL; i<max_fork_width; i++ ) {
     344         504 :     fd_bank_cost_tracker_t * cost_tracker = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, i );
     345         504 :     if( FD_UNLIKELY( !fd_cost_tracker_join( fd_cost_tracker_new( cost_tracker->data, larger_max_cost_per_block, seed ) ) ) ) {
     346           0 :       FD_LOG_WARNING(( "Failed to create cost tracker" ));
     347           0 :       return NULL;
     348           0 :     }
     349         504 :   }
     350             : 
     351         333 :   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 ) );
     352         333 :   if( FD_UNLIKELY( !stake_rewards ) ) {
     353           0 :     FD_LOG_WARNING(( "Failed to create stake rewards" ));
     354           0 :     return NULL;
     355           0 :   }
     356         333 :   banks_data->stake_rewards_offset = (ulong)stake_rewards - (ulong)banks_data;
     357             : 
     358             : 
     359         333 :   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 ) );
     360         333 :   if( FD_UNLIKELY( !vote_stakes ) ) {
     361           0 :     FD_LOG_WARNING(( "Failed to create vote stakes" ));
     362           0 :     return NULL;
     363           0 :   }
     364         333 :   banks_data->vote_stakes_pool_offset = (ulong)vote_stakes - (ulong)banks_data;
     365             : 
     366             :   /* For each bank, set the offset back to banks_data and initialize
     367             :      per-bank state. */
     368             : 
     369         333 :   fd_bank_cost_tracker_t * cost_tracker_pool_init = fd_banks_get_cost_tracker_pool( banks_data );
     370             : 
     371        1365 :   for( ulong i=0UL; i<max_total_banks; i++ ) {
     372             : 
     373        1032 :     fd_bank_t * bank = fd_banks_pool_ele( bank_pool, i );
     374             : 
     375        1032 :     fd_rwlock_new( &bank->lthash_lock );
     376             : 
     377        1032 :     bank->idx               = i;
     378        1032 :     bank->state             = FD_BANK_STATE_INACTIVE;
     379        1032 :     bank->banks_data_offset = (ulong)bank - (ulong)banks_data;
     380             : 
     381        1032 :     if( i==0UL ) {
     382         333 :       FD_TEST( fd_top_votes_join( fd_top_votes_new( bank->top_votes_t_1_mem, FD_RUNTIME_MAX_VOTE_ACCOUNTS_VAT, seed ) ) );
     383         333 :       FD_TEST( fd_top_votes_join( fd_top_votes_new( bank->top_votes_t_2_mem, FD_RUNTIME_MAX_VOTE_ACCOUNTS_VAT, seed ) ) );
     384         333 :     }
     385             : 
     386        1032 :     bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool_init );
     387        1032 :   }
     388             : 
     389         333 :   banks_data->max_total_banks    = max_total_banks;
     390         333 :   banks_data->max_fork_width     = max_fork_width;
     391         333 :   banks_data->max_stake_accounts = max_stake_accounts;
     392         333 :   banks_data->max_vote_accounts  = max_vote_accounts;
     393         333 :   banks_data->root_idx           = ULONG_MAX;
     394         333 :   banks_data->bank_seq           = 0UL;
     395             : 
     396         333 :   FD_COMPILER_MFENCE();
     397         333 :   FD_VOLATILE( banks_data->magic ) = FD_BANKS_MAGIC;
     398         333 :   FD_COMPILER_MFENCE();
     399             : 
     400         333 :   return shmem;
     401         333 : }
     402             : 
     403             : fd_banks_t *
     404         333 : fd_banks_join( void * banks_data_mem ) {
     405         333 :   fd_banks_t * banks_data  = (fd_banks_t *)banks_data_mem;
     406             : 
     407         333 :   if( FD_UNLIKELY( !banks_data ) ) {
     408           0 :     FD_LOG_WARNING(( "NULL banks data" ));
     409           0 :     return NULL;
     410           0 :   }
     411             : 
     412         333 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)banks_data, fd_banks_align() ) ) ) {
     413           0 :     FD_LOG_WARNING(( "misaligned banks" ));
     414           0 :     return NULL;
     415           0 :   }
     416             : 
     417         333 :   if( FD_UNLIKELY( banks_data->magic!=FD_BANKS_MAGIC ) ) {
     418           0 :     FD_LOG_WARNING(( "Invalid banks magic" ));
     419           0 :     return NULL;
     420           0 :   }
     421             : 
     422         333 :   ulong expected_stake_accounts = fd_ulong_min( banks_data->max_stake_accounts, FD_RUNTIME_EXPECTED_STAKE_ACCOUNTS );
     423         333 :   ulong expected_vote_accounts  = fd_ulong_min( banks_data->max_vote_accounts, FD_RUNTIME_EXPECTED_VOTE_ACCOUNTS );
     424             : 
     425         333 :   FD_SCRATCH_ALLOC_INIT( l, banks_data );
     426         333 :   banks_data                   = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(),                  sizeof(fd_banks_t) );
     427         333 :   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 ) );
     428         333 :   void * epoch_leaders_mem     = FD_SCRATCH_ALLOC_APPEND( l, FD_EPOCH_LEADERS_ALIGN,            2UL * banks_data->epoch_leaders_footprint );
     429         333 :   void * pool_mem              = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(),             fd_banks_pool_footprint( banks_data->max_total_banks ) );
     430         333 :   void * dead_banks_deque_mem  = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_dead_align(),             fd_banks_dead_footprint() );
     431         333 :   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 ) );
     432         333 :   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 ) );
     433         333 :   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 ) );
     434         333 :   void * epoch_credits_mem     = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_epoch_credits_t),       sizeof(fd_epoch_credits_t) * banks_data->max_vote_accounts );
     435         333 :   void * snapshot_commission   = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_stashed_commission_t),  sizeof(fd_stashed_commission_t) * banks_data->max_vote_accounts );
     436           0 :   (void)epoch_credits_mem;
     437         333 :   (void)snapshot_commission;
     438             : 
     439         333 :   FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() );
     440             : 
     441         333 :   fd_bank_t * banks_pool = fd_banks_get_bank_pool( banks_data );
     442         333 :   if( FD_UNLIKELY( !banks_pool ) ) {
     443           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     444           0 :     return NULL;
     445           0 :   }
     446             : 
     447         333 :   if( FD_UNLIKELY( banks_pool!=fd_banks_pool_join( pool_mem ) ) ) {
     448           0 :     FD_LOG_WARNING(( "Failed to join bank pool" ));
     449           0 :     return NULL;
     450           0 :   }
     451             : 
     452         333 :   fd_bank_idx_seq_t * banks_dead_deque = fd_banks_dead_join( dead_banks_deque_mem );
     453         333 :   if( FD_UNLIKELY( !banks_dead_deque ) ) {
     454           0 :     FD_LOG_WARNING(( "Failed to join banks dead deque" ));
     455           0 :     return NULL;
     456           0 :   }
     457             : 
     458         333 :   if( FD_UNLIKELY( epoch_leaders_mem!=fd_banks_get_epoch_leaders( banks_data ) ) ) {
     459           0 :     FD_LOG_WARNING(( "Failed to join epoch leaders mem" ));
     460           0 :     return NULL;
     461           0 :   }
     462             : 
     463         333 :   if( FD_UNLIKELY( stake_delegations_mem!=fd_banks_get_stake_delegations( banks_data ) ) ) {
     464           0 :     FD_LOG_WARNING(( "Failed to join stake delegations root mem" ));
     465           0 :     return NULL;
     466           0 :   }
     467             : 
     468         333 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks_data );
     469         333 :   if( FD_UNLIKELY( !cost_tracker_pool ) ) {
     470           0 :     FD_LOG_WARNING(( "Failed to join cost tracker pool" ));
     471           0 :     return NULL;
     472           0 :   }
     473             : 
     474         333 :   if( FD_UNLIKELY( cost_tracker_pool!=fd_bank_cost_tracker_pool_join( cost_tracker_pool_mem ) ) ) {
     475           0 :     FD_LOG_WARNING(( "Failed to join cost tracker pool" ));
     476           0 :     return NULL;
     477           0 :   }
     478             : 
     479         333 :   if( FD_UNLIKELY( !fd_stake_rewards_join( stake_rewards_mem ) ) ) {
     480           0 :     FD_LOG_WARNING(( "Failed to join stake rewards" ));
     481           0 :     return NULL;
     482           0 :   }
     483             : 
     484         333 :   if( FD_UNLIKELY( !fd_vote_stakes_join( vote_stakes_mem ) ) ) {
     485           0 :     FD_LOG_WARNING(( "Failed to join vote stakes" ));
     486           0 :     return NULL;
     487           0 :   }
     488             : 
     489         333 :   return banks_data;
     490         333 : }
     491             : 
     492             : fd_bank_t *
     493         498 : fd_banks_init_bank( fd_banks_t * banks ) {
     494             : 
     495         498 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     496         498 :   FD_CRIT( fd_banks_pool_free( bank_pool )!=0UL, "invariant violation: no free bank pool elements" );
     497             : 
     498         498 :   fd_bank_t * bank = fd_banks_pool_ele_acquire( bank_pool );
     499         498 :   bank->bank_seq = FD_ATOMIC_FETCH_AND_ADD( &banks->bank_seq, 1UL );
     500             : 
     501         498 :   ulong null_idx    = fd_banks_pool_idx_null( bank_pool );
     502         498 :   bank->idx         = fd_banks_pool_idx( bank_pool, bank );
     503         498 :   bank->next        = null_idx;
     504         498 :   bank->parent_idx  = null_idx;
     505         498 :   bank->child_idx   = null_idx;
     506         498 :   bank->sibling_idx = null_idx;
     507             : 
     508         498 :   fd_memset( &bank->f, 0, sizeof(bank->f) );
     509         498 :   bank->stake_rewards_fork_id             = UCHAR_MAX;
     510         498 :   bank->stake_delegations_fork_id         = USHORT_MAX;
     511         498 :   bank->epoch_leaders_idx                 = ULONG_MAX;
     512         498 :   bank->cost_tracker_pool_idx             = fd_bank_cost_tracker_pool_idx_null( fd_banks_get_cost_tracker_pool( banks ) );
     513         498 :   bank->first_fec_set_received_nanos      = fd_log_wallclock();
     514         498 :   bank->preparation_begin_nanos           = 0L;
     515         498 :   bank->first_transaction_scheduled_nanos = 0L;
     516         498 :   bank->last_transaction_finished_nanos   = 0L;
     517         498 :   bank->block_completed_nanos             = 0L;
     518             : 
     519         498 :   fd_vote_stakes_t * vote_stakes = fd_banks_get_vote_stakes( banks );
     520         498 :   bank->vote_stakes_fork_id      = fd_vote_stakes_get_root_idx( vote_stakes );
     521             : 
     522         498 :   fd_stake_delegations_t * stake_delegations = fd_banks_get_stake_delegations( banks );
     523         498 :   bank->stake_delegations_fork_id            = fd_stake_delegations_new_fork( stake_delegations );
     524             : 
     525         498 :   bank->state  = FD_BANK_STATE_FROZEN;
     526         498 :   bank->refcnt = 0UL;
     527             : 
     528         498 :   banks->root_idx = bank->idx;
     529             : 
     530         498 :   return bank;
     531         498 : }
     532             : 
     533             : fd_bank_t *
     534             : fd_banks_clone_from_parent( fd_banks_t * banks,
     535         423 :                             ulong        child_bank_idx ) {
     536             : 
     537         423 :   fd_bank_t * bank_pool  = fd_banks_get_bank_pool( banks );
     538         423 :   fd_bank_t * child_bank = fd_banks_pool_ele( bank_pool, child_bank_idx );
     539         423 :   FD_CRIT( child_bank->state==FD_BANK_STATE_INIT, "invariant violation: bank is not initialized" );
     540             : 
     541         423 :   fd_bank_t * parent_bank = fd_banks_pool_ele( bank_pool, child_bank->parent_idx );
     542         423 :   FD_CRIT( parent_bank->state==FD_BANK_STATE_FROZEN, "invariant violation: parent bank is not frozen" );
     543             : 
     544         423 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks );
     545         423 :   FD_CRIT( fd_bank_cost_tracker_pool_free( cost_tracker_pool )!=0UL, "invariant violation: no free cost tracker pool elements" );
     546         423 :   child_bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_acquire( cost_tracker_pool );
     547             : 
     548         423 :   fd_memcpy( child_bank->top_votes_t_1_mem, parent_bank->top_votes_t_1_mem, FD_TOP_VOTES_MAX_FOOTPRINT );
     549         423 :   fd_memcpy( child_bank->top_votes_t_2_mem, parent_bank->top_votes_t_2_mem, FD_TOP_VOTES_MAX_FOOTPRINT );
     550             : 
     551         423 :   child_bank->f                          = parent_bank->f;
     552         423 :   child_bank->epoch_leaders_idx          = parent_bank->epoch_leaders_idx;
     553         423 :   child_bank->vote_stakes_fork_id        = parent_bank->vote_stakes_fork_id;
     554         423 :   child_bank->stake_rewards_fork_id      = parent_bank->stake_rewards_fork_id;
     555         423 :   child_bank->stake_delegations_fork_id  = fd_stake_delegations_new_fork( fd_banks_get_stake_delegations( banks ) );
     556         423 :   child_bank->f.block_height             = parent_bank->f.block_height + 1UL;
     557         423 :   child_bank->f.tick_height              = parent_bank->f.max_tick_height;
     558         423 :   child_bank->f.parent_slot              = parent_bank->f.slot;
     559         423 :   child_bank->f.parent_signature_cnt     = parent_bank->f.signature_count;
     560         423 :   child_bank->f.prev_bank_hash           = parent_bank->f.bank_hash;
     561         423 :   child_bank->f.execution_fees           = 0UL;
     562         423 :   child_bank->f.priority_fees            = 0UL;
     563         423 :   child_bank->f.tips                     = 0UL;
     564         423 :   child_bank->f.signature_count          = 0UL;
     565         423 :   child_bank->f.total_compute_units_used = 0UL;
     566         423 :   child_bank->f.shred_cnt                = 0UL;
     567         423 :   child_bank->f.txn_count                = 0UL;
     568         423 :   child_bank->f.nonvote_txn_count        = 0UL;
     569         423 :   child_bank->f.failed_txn_count         = 0UL;
     570         423 :   child_bank->f.nonvote_failed_txn_count = 0UL;
     571         423 :   child_bank->f.identity_vote_idx        = ULONG_MAX;
     572             : 
     573         423 :   child_bank->state = FD_BANK_STATE_REPLAYABLE;
     574             : 
     575         423 :   return child_bank;
     576         423 : }
     577             : 
     578             : /* fd_bank_stake_delegation_apply_deltas applies all of the stake
     579             :    delegations for the entire direct ancestry from the bank to the
     580             :    root into a full fd_stake_delegations_t object. */
     581             : 
     582             : static inline void
     583             : fd_bank_stake_delegation_apply_deltas( fd_banks_t * banks,
     584          81 :                                        fd_bank_t *  bank ) {
     585             : 
     586          81 :   fd_stake_delegations_t * stake_delegations = fd_banks_get_stake_delegations( banks );
     587             : 
     588             :   /* The stake_delegations root has crossed an epoch boundary.  The
     589             :      stake totals for the current root need to be updated. */
     590          81 :   fd_bank_t * old_root = fd_banks_root( banks );
     591          81 :   if( old_root->f.epoch!=bank->f.epoch ) {
     592          18 :     stake_delegations->effective_stake    = bank->f.total_epoch_stake;
     593          18 :     stake_delegations->activating_stake   = bank->f.total_activating_stake;
     594          18 :     stake_delegations->deactivating_stake = bank->f.total_deactivating_stake;
     595          18 :   }
     596             : 
     597             :   /* Naively what we want to do is iterate from the old root to the new
     598             :      root and apply the delta to the full state iteratively. */
     599             : 
     600             :   /* First, gather all of the pool indicies that we want to apply deltas
     601             :      for in reverse order starting from the new root. We want to exclude
     602             :      the old root since its delta has been applied previously. */
     603          81 :   ushort pool_indices[ banks->max_total_banks ];
     604          81 :   ulong  pool_indices_len = 0UL;
     605             : 
     606          81 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     607             : 
     608          81 :   fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, bank->idx );
     609         243 :   while( !!curr_bank ) {
     610         162 :     if( curr_bank->stake_delegations_fork_id!=USHORT_MAX ) {
     611         123 :       pool_indices[pool_indices_len++] = curr_bank->stake_delegations_fork_id;
     612         123 :     }
     613         162 :     curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->parent_idx );
     614         162 :   }
     615             : 
     616             :   /* We have populated all of the indicies that we need to apply deltas
     617             :      from in reverse order. */
     618             : 
     619          81 :   fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history_join_const( &bank->f.sysvar_cache );
     620         204 :   for( ulong i=pool_indices_len; i>0; i-- ) {
     621         123 :     ushort idx = pool_indices[i-1UL];
     622         123 :     fd_stake_delegations_apply_fork_delta( bank->f.epoch, stake_history, &bank->f.warmup_cooldown_rate_epoch, stake_delegations, idx );
     623         123 :   }
     624          81 : }
     625             : 
     626             : static inline void
     627             : fd_bank_stake_delegation_mark_deltas( fd_banks_t *             banks,
     628             :                                       fd_bank_t *              bank,
     629         159 :                                       fd_stake_delegations_t * stake_delegations ) {
     630             : 
     631         159 :   ushort pool_indices[ banks->max_total_banks ];
     632         159 :   ulong  pool_indices_len = 0UL;
     633             : 
     634         159 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     635             : 
     636         159 :   fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, bank->idx );
     637         489 :   while( !!curr_bank ) {
     638         330 :     if( curr_bank->stake_delegations_fork_id!=USHORT_MAX ) {
     639         306 :       pool_indices[pool_indices_len++] = curr_bank->stake_delegations_fork_id;
     640         306 :     }
     641         330 :     curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->parent_idx );
     642         330 :   }
     643             : 
     644         159 :   fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history_join_const( &bank->f.sysvar_cache );
     645             : 
     646         465 :   for( ulong i=pool_indices_len; i>0; i-- ) {
     647         306 :     ushort idx = pool_indices[i-1UL];
     648         306 :     fd_stake_delegations_mark_delta( stake_delegations, bank->f.epoch, stake_history, &bank->f.warmup_cooldown_rate_epoch, idx );
     649         306 :   }
     650         159 : }
     651             : 
     652             : static inline void
     653             : fd_bank_stake_delegation_unmark_deltas( fd_banks_t *             banks,
     654             :                                         fd_bank_t *              bank,
     655         159 :                                         fd_stake_delegations_t * stake_delegations ) {
     656             : 
     657         159 :   ushort pool_indices[ banks->max_total_banks ];
     658         159 :   ulong  pool_indices_len = 0UL;
     659             : 
     660         159 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     661             : 
     662         159 :   fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, bank->idx );
     663         489 :   while( !!curr_bank ) {
     664         330 :     if( curr_bank->stake_delegations_fork_id!=USHORT_MAX ) {
     665         306 :       pool_indices[pool_indices_len++] = curr_bank->stake_delegations_fork_id;
     666         306 :     }
     667         330 :     curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->parent_idx );
     668         330 :   }
     669             : 
     670         159 :   fd_stake_history_t const * stake_history = fd_sysvar_cache_stake_history_join_const( &bank->f.sysvar_cache );
     671             : 
     672         465 :   for( ulong i=pool_indices_len; i>0; i-- ) {
     673         306 :     ushort idx = pool_indices[i-1UL];
     674         306 :     fd_stake_delegations_unmark_delta( stake_delegations, bank->f.epoch-1UL, stake_history, &bank->f.warmup_cooldown_rate_epoch, idx );
     675         306 :   }
     676         159 : }
     677             : 
     678             : 
     679             : fd_stake_delegations_t *
     680             : fd_bank_stake_delegations_frontier_query( fd_banks_t * banks,
     681         159 :                                           fd_bank_t *  bank ) {
     682         159 :   fd_stake_delegations_t * stake_delegations = fd_banks_get_stake_delegations( banks );
     683         159 :   fd_bank_stake_delegation_mark_deltas( banks, bank, stake_delegations );
     684             : 
     685         159 :   return stake_delegations;
     686         159 : }
     687             : 
     688             : void
     689             : fd_bank_stake_delegations_end_frontier_query( fd_banks_t * banks,
     690         159 :                                               fd_bank_t *  bank ) {
     691         159 :   fd_stake_delegations_t * stake_delegations = fd_banks_get_stake_delegations( banks );
     692         159 :   fd_bank_stake_delegation_unmark_deltas( banks, bank, stake_delegations );
     693         159 : }
     694             : 
     695             : 
     696             : fd_stake_delegations_t *
     697         195 : fd_banks_stake_delegations_root_query( fd_banks_t * banks ) {
     698         195 :   return fd_banks_get_stake_delegations( banks );
     699         195 : }
     700             : 
     701             : void
     702             : fd_banks_advance_root( fd_banks_t * banks,
     703          81 :                        ulong        root_bank_idx ) {
     704             : 
     705          81 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     706             : 
     707             :   /* We want to replace the old root with the new root. This means we
     708             :      have to remove banks that aren't descendants of the new root. */
     709             : 
     710          81 :   fd_bank_t * old_root = fd_banks_root( banks );
     711          81 :   FD_CRIT( old_root->refcnt==0UL, "refcnt for old root bank is nonzero" );
     712             : 
     713          81 :   fd_bank_t * new_root = fd_banks_pool_ele( bank_pool, root_bank_idx );
     714             : 
     715          81 :   fd_bank_stake_delegation_apply_deltas( banks, new_root );
     716             : 
     717          81 :   fd_stake_delegations_t * stake_delegations = fd_banks_get_stake_delegations( banks );
     718          81 :   fd_stake_delegations_evict_fork( stake_delegations, new_root->stake_delegations_fork_id );
     719          81 :   new_root->stake_delegations_fork_id = USHORT_MAX;
     720             : 
     721             :   /* Now that the deltas have been applied, we can remove all nodes
     722             :      that are not direct descendants of the new root. */
     723          81 :   fd_bank_t * head = fd_banks_pool_ele( bank_pool, old_root->idx );
     724          81 :   head->next       = ULONG_MAX;
     725          81 :   fd_bank_t * tail = head;
     726             : 
     727         210 :   while( head ) {
     728         129 :     fd_bank_t * child = fd_banks_pool_ele( bank_pool, head->child_idx );
     729             : 
     730         258 :     while( FD_LIKELY( child ) ) {
     731             : 
     732         129 :       if( FD_LIKELY( child!=new_root ) ) {
     733          48 :         if( FD_UNLIKELY( child->refcnt!=0UL ) ) {
     734           0 :           FD_LOG_CRIT(( "refcnt for child bank at index %lu is %lu", child->idx, child->refcnt ));
     735           0 :         }
     736             : 
     737             :         /* Update tail pointers */
     738          48 :         tail->next = child->idx;
     739          48 :         tail       = fd_banks_pool_ele( bank_pool, tail->next );
     740          48 :         tail->next = fd_banks_pool_idx_null( bank_pool );
     741          48 :       }
     742             : 
     743         129 :       child = fd_banks_pool_ele( bank_pool, child->sibling_idx );
     744         129 :     }
     745             : 
     746         129 :     fd_bank_t * next = fd_banks_pool_ele( bank_pool, head->next );
     747             : 
     748             :     /* It is possible for a bank that never finished replaying to be
     749             :        pruned away.  If the bank was never frozen, then it's possible
     750             :        that the bank still owns a cost tracker pool element.  If this
     751             :        is the case, we need to release the pool element. */
     752         129 :     fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks );
     753         129 :     if( head->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) ) {
     754           9 :       FD_LOG_DEBUG(( "releasing cost tracker pool element for bank at index %lu", head->idx ));
     755           9 :       fd_bank_cost_tracker_pool_idx_release( cost_tracker_pool, head->cost_tracker_pool_idx );
     756           9 :       head->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool );
     757           9 :     }
     758             : 
     759         129 :     head->stake_rewards_fork_id = UCHAR_MAX;
     760         129 :     head->vote_stakes_fork_id = USHORT_MAX;
     761             : 
     762         129 :     if( head->stake_delegations_fork_id!=USHORT_MAX ) {
     763          84 :       fd_stake_delegations_evict_fork( stake_delegations, head->stake_delegations_fork_id );
     764          84 :       head->stake_delegations_fork_id = USHORT_MAX;
     765          84 :     }
     766             : 
     767         129 :     head->state = FD_BANK_STATE_INACTIVE;
     768         129 :     fd_banks_pool_ele_release( bank_pool, head );
     769         129 :     head = next;
     770         129 :   }
     771             : 
     772             :   /* new_root is detached from old_root and becomes the only root.
     773             :      Clear sibling_idx too so traversals cannot follow a stale link to
     774             :      a bank index that was just pruned and later reused. */
     775          81 :   new_root->parent_idx  = ULONG_MAX;
     776          81 :   new_root->sibling_idx = ULONG_MAX;
     777          81 :   banks->root_idx       = new_root->idx;
     778             : 
     779          81 :   fd_vote_stakes_t * vote_stakes = fd_banks_get_vote_stakes( banks );
     780          81 :   fd_vote_stakes_advance_root( vote_stakes, new_root->vote_stakes_fork_id );
     781          81 : }
     782             : 
     783             : /* Is the fork tree starting at the given bank entirely eligible for
     784             :    pruning?  Returns 1 for yes, 0 for no.
     785             : 
     786             :    See comment in fd_replay_tile.c for more details on safe pruning. */
     787             : static int
     788             : fd_banks_subtree_can_be_pruned( fd_bank_t * bank_pool,
     789          27 :                                 fd_bank_t * bank ) {
     790             : 
     791          27 :   if( bank->refcnt!=0UL ) return 0;
     792             : 
     793             :   /* Recursively check all children. */
     794          24 :   ulong child_idx = bank->child_idx;
     795          33 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
     796           9 :     fd_bank_t * child = fd_banks_pool_ele( bank_pool, child_idx );
     797           9 :     if( !fd_banks_subtree_can_be_pruned( bank_pool, child ) ) return 0;
     798           9 :     child_idx = child->sibling_idx;
     799           9 :   }
     800             : 
     801          24 :   return 1;
     802          24 : }
     803             : 
     804             : int
     805             : fd_banks_advance_root_prepare( fd_banks_t * banks,
     806             :                                ulong        target_bank_idx,
     807          15 :                                ulong *      advanceable_bank_idx_out ) {
     808             :   /* TODO: An optimization here is to do a single traversal of the tree
     809             :      that would mark minority forks as dead while accumulating
     810             :      refcnts to determine which bank is the highest advanceable. */
     811             : 
     812          15 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     813             : 
     814          15 :   fd_bank_t * root = fd_banks_root( banks );
     815             : 
     816             :   /* Early exit if target is the same as the old root. */
     817          15 :   if( FD_UNLIKELY( root->idx==target_bank_idx ) ) {
     818           0 :     FD_LOG_WARNING(( "target bank_idx %lu is the same as the old root's bank index %lu", target_bank_idx, root->idx ));
     819           0 :     return 0;
     820           0 :   }
     821             : 
     822             :   /* Early exit if the root bank still has a reference to it, we can't
     823             :      advance from it unti it's released. */
     824          15 :   if( FD_UNLIKELY( root->refcnt!=0UL ) ) {
     825           0 :     return 0;
     826           0 :   }
     827             : 
     828          15 :   fd_bank_t * target_bank = fd_banks_pool_ele( bank_pool, target_bank_idx );
     829             : 
     830             :   /* Walk from target_bank up to root, recording the direct child of
     831             :      root on the path (prev).  We only advance root by one level. */
     832             : 
     833          15 :   fd_bank_t * curr = target_bank;
     834          15 :   fd_bank_t * prev = NULL;
     835          57 :   while( curr && curr!=root ) {
     836          42 :     prev = curr;
     837          42 :     curr = fd_banks_pool_ele( bank_pool, curr->parent_idx );
     838          42 :   }
     839             : 
     840             :   /* If we didn't reach the old root or there is no parent, target is
     841             :      not a descendant. */
     842          15 :   if( FD_UNLIKELY( !curr || prev->parent_idx!=root->idx ) ) {
     843           0 :     FD_LOG_CRIT(( "invariant violation: target bank_idx %lu is not a direct descendant of root bank_idx %lu %lu %lu", target_bank_idx, root->idx, prev->idx, prev->parent_idx ));
     844           0 :   }
     845             : 
     846             :   /* We will at most advance our root bank by one.  This means we can
     847             :      advance our root bank by one if each of the siblings of the
     848             :      potential new root are eligible for pruning.  Each of the sibling
     849             :      subtrees can be pruned if the subtrees have no active references on
     850             :      their bank. */
     851          15 :   ulong advance_candidate_idx = prev->idx;
     852          15 :   ulong child_idx = root->child_idx;
     853          42 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
     854          30 :     fd_bank_t * child_bank = fd_banks_pool_ele( bank_pool, child_idx );
     855          30 :     if( child_idx!=advance_candidate_idx ) {
     856          18 :       if( !fd_banks_subtree_can_be_pruned( bank_pool, child_bank ) ) {
     857           3 :         return 0;
     858           3 :       }
     859          18 :     }
     860          27 :     child_idx = child_bank->sibling_idx;
     861          27 :   }
     862             : 
     863          12 :   *advanceable_bank_idx_out = advance_candidate_idx;
     864          12 :   return 1;
     865          15 : }
     866             : 
     867             : fd_bank_t *
     868             : fd_banks_new_bank( fd_banks_t * banks,
     869             :                    ulong        parent_bank_idx,
     870         444 :                    long         now ) {
     871             : 
     872         444 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
     873         444 :   FD_CRIT( fd_banks_pool_free( bank_pool )!=0UL, "invariant violation: no free bank indices available" );
     874             : 
     875         444 :   ulong       child_bank_idx = fd_banks_pool_idx_acquire( bank_pool );
     876         444 :   fd_bank_t * child_bank     = fd_banks_pool_ele( bank_pool, child_bank_idx );
     877         444 :   FD_CRIT( child_bank->state==FD_BANK_STATE_INACTIVE, "invariant violation: bank for bank index is already initialized" );
     878             : 
     879         444 :   ulong null_idx = fd_banks_pool_idx_null( bank_pool );
     880             : 
     881         444 :   child_bank->bank_seq    = FD_ATOMIC_FETCH_AND_ADD( &banks->bank_seq, 1UL );
     882         444 :   child_bank->parent_idx  = null_idx;
     883         444 :   child_bank->child_idx   = null_idx;
     884         444 :   child_bank->sibling_idx = null_idx;
     885         444 :   child_bank->next        = null_idx;
     886         444 :   child_bank->state       = FD_BANK_STATE_INIT;
     887         444 :   child_bank->refcnt      = 0UL;
     888             : 
     889         444 :   child_bank->stake_delegations_fork_id = USHORT_MAX;
     890             : 
     891             :   /* Then make sure that the parent bank is valid and frozen. */
     892             : 
     893         444 :   fd_bank_t * parent_bank = fd_banks_pool_ele( bank_pool, parent_bank_idx );
     894         444 :   FD_CRIT( parent_bank->state!=FD_BANK_STATE_INACTIVE && parent_bank->state!=FD_BANK_STATE_DEAD, "invariant violation: parent bank is dead or inactive" );
     895             : 
     896             :   /* Link node->parent */
     897         444 :   child_bank->parent_idx = parent_bank_idx;
     898             :   /* Link parent->node and sibling->node */
     899         444 :   if( FD_LIKELY( parent_bank->child_idx==null_idx ) ) {
     900             :     /* This is the first child so set as left-most child */
     901         372 :     parent_bank->child_idx = child_bank_idx;
     902             : 
     903         372 :   } else {
     904             :     /* Already have children so iterate to right-most sibling. */
     905          72 :     fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, parent_bank->child_idx );
     906          99 :     while( curr_bank->sibling_idx != null_idx ) curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->sibling_idx );
     907             :     /* Link to right-most sibling. */
     908          72 :     curr_bank->sibling_idx = child_bank_idx;
     909          72 :   }
     910             : 
     911         444 :   child_bank->first_fec_set_received_nanos      = now;
     912         444 :   child_bank->first_transaction_scheduled_nanos = 0L;
     913         444 :   child_bank->last_transaction_finished_nanos   = 0L;
     914             : 
     915         444 :   return child_bank;
     916         444 : }
     917             : 
     918             : /* Mark everything in the fork tree starting at the given bank dead. */
     919             : 
     920             : static void
     921             : fd_banks_subtree_mark_dead( fd_banks_t * banks,
     922             :                             fd_bank_t *  bank_pool,
     923          30 :                             fd_bank_t *  bank ) {
     924          30 :   if( FD_UNLIKELY( !bank ) ) FD_LOG_CRIT(( "invariant violation: bank is NULL" ));
     925             : 
     926          30 :   bank->state = FD_BANK_STATE_DEAD;
     927          30 :   fd_banks_dead_push_head( fd_banks_get_dead_banks_deque( banks ), (fd_bank_idx_seq_t){ .idx = bank->idx, .seq = bank->bank_seq } );
     928             : 
     929             :   /* Recursively mark all children as dead. */
     930          30 :   ulong child_idx = bank->child_idx;
     931          36 :   while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
     932           6 :     fd_bank_t * child = fd_banks_pool_ele( bank_pool, child_idx );
     933           6 :     fd_banks_subtree_mark_dead( banks, bank_pool, child );
     934           6 :     child_idx = child->sibling_idx;
     935           6 :   }
     936          30 : }
     937             : 
     938             : void
     939             : fd_banks_mark_bank_dead( fd_banks_t * banks,
     940          24 :                          ulong        bank_idx ) {
     941          24 :   fd_bank_t * bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank_idx );
     942          24 :   fd_banks_subtree_mark_dead( banks, fd_banks_get_bank_pool( banks ), bank );
     943          24 : }
     944             : 
     945             : int
     946             : fd_banks_prune_one_dead_bank( fd_banks_t *                   banks,
     947          48 :                               fd_banks_prune_cancel_info_t * cancel ) {
     948          48 :   fd_bank_idx_seq_t * dead_banks_queue = fd_banks_get_dead_banks_deque( banks );
     949          48 :   fd_bank_t *         bank_pool        = fd_banks_get_bank_pool( banks );
     950          48 :   ulong               null_idx         = fd_banks_pool_idx_null( bank_pool );
     951          51 :   while( !fd_banks_dead_empty( dead_banks_queue ) ) {
     952          30 :     fd_bank_idx_seq_t * head = fd_banks_dead_peek_head( dead_banks_queue );
     953          30 :     fd_bank_t *         bank = fd_banks_pool_ele( bank_pool, head->idx );
     954          30 :     if( bank->state==FD_BANK_STATE_INACTIVE || bank->bank_seq!=head->seq ) {
     955           3 :       fd_banks_dead_pop_head( dead_banks_queue );
     956           3 :       continue;
     957          27 :     } else if( bank->refcnt!=0UL ) {
     958           3 :       break;
     959           3 :     }
     960             : 
     961          24 :     FD_LOG_DEBUG(( "pruning dead bank (idx=%lu)", bank->idx ));
     962             : 
     963          24 :     int started_replaying = bank->stake_delegations_fork_id!=USHORT_MAX;
     964             : 
     965             :     /* There are a few cases to consider:
     966             :        1. The to-be-pruned bank is the left-most child of the parent.
     967             :           This means that the parent bank's child idx is the
     968             :           to-be-pruned bank.  In this case, we can simply make the
     969             :           left-most sibling of the to-be-pruned bank the new left-most
     970             :           child (set parent's banks child idx to the sibling).  The
     971             :           sibling pointer can be null if the to-be-pruned bank is an
     972             :           only child of the parent.
     973             :        2. The to-be-pruned bank is some right child of the parent.  In
     974             :           this case, the child bank which has a sibling pointer to the
     975             :           to-be-pruned bank needs to be updated to point to the sibling
     976             :           of the to-be-pruned bank.  The sibling can even be null if the
     977             :           to-be-pruned bank is the right-most child of the parent.
     978             :     */
     979             : 
     980          24 :     FD_TEST( bank->child_idx==null_idx );
     981          24 :     fd_bank_t * parent_bank = fd_banks_pool_ele( bank_pool, bank->parent_idx );
     982          24 :     if( parent_bank->child_idx==bank->idx ) {
     983             :       /* Case 1: left-most child */
     984          12 :       parent_bank->child_idx = bank->sibling_idx;
     985          12 :     } else {
     986             :       /* Case 2: some right child */
     987          12 :       fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, parent_bank->child_idx );
     988          18 :       while( curr_bank->sibling_idx!=bank->idx ) curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->sibling_idx );
     989          12 :       curr_bank->sibling_idx = bank->sibling_idx;
     990          12 :     }
     991          24 :     bank->parent_idx  = null_idx;
     992          24 :     bank->sibling_idx = null_idx;
     993             : 
     994          24 :     if( FD_UNLIKELY( bank->cost_tracker_pool_idx!=null_idx ) ) {
     995          15 :       fd_bank_cost_tracker_pool_idx_release( fd_banks_get_cost_tracker_pool( banks ), bank->cost_tracker_pool_idx );
     996          15 :       bank->cost_tracker_pool_idx = null_idx;
     997          15 :     }
     998             : 
     999          24 :     fd_stake_delegations_t * stake_delegations = fd_banks_get_stake_delegations( banks );
    1000          24 :     fd_stake_delegations_evict_fork( stake_delegations, bank->stake_delegations_fork_id );
    1001          24 :     bank->stake_delegations_fork_id = USHORT_MAX;
    1002             : 
    1003          24 :     bank->stake_rewards_fork_id = UCHAR_MAX;
    1004             : 
    1005          24 :     if( FD_LIKELY( started_replaying ) ) {
    1006          18 :       cancel->txncache_fork_id = bank->txncache_fork_id;
    1007          18 :       cancel->slot             = bank->f.slot;
    1008          18 :       cancel->bank_idx         = bank->idx;
    1009          18 :     }
    1010             : 
    1011          24 :     bank->state = FD_BANK_STATE_INACTIVE;
    1012             : 
    1013          24 :     fd_banks_pool_ele_release( bank_pool, bank );
    1014          24 :     fd_banks_dead_pop_head( dead_banks_queue );
    1015          24 :     return 1+started_replaying;
    1016          24 :   }
    1017          24 :   return 0;
    1018          48 : }
    1019             : 
    1020             : void
    1021         120 : fd_banks_mark_bank_frozen( fd_bank_t * bank ) {
    1022         120 :   fd_banks_t * banks = fd_type_pun( (uchar *)bank - bank->banks_data_offset );
    1023             : 
    1024         120 :   FD_CRIT( bank->state==FD_BANK_STATE_REPLAYABLE, "invariant violation: bank is not replayable" );
    1025         120 :   bank->state = FD_BANK_STATE_FROZEN;
    1026             : 
    1027         120 :   FD_CRIT( bank->cost_tracker_pool_idx!=ULONG_MAX, "invariant violation: cost tracker pool index is null" );
    1028         120 :   fd_bank_cost_tracker_pool_idx_release( fd_banks_get_cost_tracker_pool( banks ), bank->cost_tracker_pool_idx );
    1029         120 :   bank->cost_tracker_pool_idx = ULONG_MAX;
    1030         120 : }
    1031             : 
    1032             : static void
    1033             : fd_banks_get_frontier_private( fd_bank_t * bank_pool,
    1034             :                                ulong       bank_idx,
    1035             :                                ulong *     frontier_indices_out,
    1036         144 :                                ulong *     frontier_cnt_out ) {
    1037         144 :   if( bank_idx==fd_banks_pool_idx_null( bank_pool ) ) return;
    1038             : 
    1039          90 :   fd_bank_t * bank = fd_banks_pool_ele( bank_pool, bank_idx );
    1040             : 
    1041          90 :   if( bank->child_idx==fd_banks_pool_idx_null( bank_pool ) ) {
    1042          45 :     if( bank->state!=FD_BANK_STATE_FROZEN && bank->state!=FD_BANK_STATE_DEAD ) {
    1043          36 :       frontier_indices_out[*frontier_cnt_out] = bank->idx;
    1044          36 :       (*frontier_cnt_out)++;
    1045          36 :     }
    1046          45 :   } else {
    1047          45 :     fd_banks_get_frontier_private( bank_pool, bank->child_idx, frontier_indices_out, frontier_cnt_out );
    1048          45 :   }
    1049          90 :   fd_banks_get_frontier_private( bank_pool, bank->sibling_idx, frontier_indices_out, frontier_cnt_out );
    1050          90 : }
    1051             : 
    1052             : void
    1053             : fd_banks_get_frontier( fd_banks_t * banks,
    1054             :                        ulong *      frontier_indices_out,
    1055           9 :                        ulong *      frontier_cnt_out ) {
    1056           9 :   *frontier_cnt_out = 0UL;
    1057           9 :   fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
    1058           9 :   fd_banks_get_frontier_private( bank_pool, banks->root_idx, frontier_indices_out, frontier_cnt_out );
    1059           9 : }
    1060             : 
    1061             : void
    1062             : fd_banks_clear_bank( fd_banks_t * banks,
    1063             :                      fd_bank_t *  bank,
    1064           3 :                      ulong        max_vote_accounts ) {
    1065             : 
    1066           3 :   fd_memset( &bank->f, 0, sizeof(bank->f) );
    1067             : 
    1068           3 :   fd_top_votes_init( fd_type_pun( bank->top_votes_t_1_mem ) );
    1069           3 :   fd_top_votes_init( fd_type_pun( bank->top_votes_t_2_mem ) );
    1070             : 
    1071             :   /* We need to acquire a cost tracker element. */
    1072           3 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks );
    1073           3 :   if( FD_UNLIKELY( bank->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) ) ) {
    1074           3 :     fd_bank_cost_tracker_pool_idx_release( cost_tracker_pool, bank->cost_tracker_pool_idx );
    1075           3 :   }
    1076           3 :   bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_acquire( cost_tracker_pool );
    1077             : 
    1078           3 :   fd_vote_stakes_t * vote_stakes = fd_banks_get_vote_stakes( banks );
    1079           3 :   fd_vote_stakes_new( vote_stakes, max_vote_accounts, max_vote_accounts, banks->max_fork_width, 999UL );
    1080           3 : }
    1081             : 
    1082             : void
    1083         195 : fd_banks_clear( fd_banks_t * banks ) {
    1084             : 
    1085         195 :   fd_bank_t *              bank_pool         = fd_banks_get_bank_pool( banks );
    1086         195 :   fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks );
    1087             : 
    1088        3315 :   for( ulong i=0UL; i<banks->max_total_banks; i++ ) {
    1089        3120 :     fd_bank_t * bank = fd_banks_pool_ele( bank_pool, i );
    1090        3120 :     bank->state                 = FD_BANK_STATE_INACTIVE;
    1091        3120 :     bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool );
    1092        3120 :   }
    1093             : 
    1094         195 :   fd_banks_pool_reset( bank_pool );
    1095         195 :   fd_bank_cost_tracker_pool_reset( cost_tracker_pool );
    1096         195 :   fd_banks_dead_remove_all( fd_banks_get_dead_banks_deque( banks ) );
    1097             : 
    1098         195 :   fd_vote_stakes_reset( fd_banks_get_vote_stakes( banks ) );
    1099         195 :   fd_stake_delegations_reset( fd_banks_get_stake_delegations( banks ) );
    1100             : 
    1101         195 :   fd_stake_rewards_clear( fd_banks_get_stake_rewards( banks ) );
    1102             : 
    1103             :   banks->root_idx = ULONG_MAX;
    1104         195 :   banks->bank_seq = 0UL;
    1105         195 : }

Generated by: LCOV version 1.14