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