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