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