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