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