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