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