Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_fd_bank_h
2 : #define HEADER_fd_src_flamenco_runtime_fd_bank_h
3 :
4 : #include "../types/fd_types.h"
5 : #include "../leaders/fd_leaders.h"
6 : #include "../features/fd_features.h"
7 : #include "../rewards/fd_epoch_rewards.h"
8 : #include "../stakes/fd_stake_delegations.h"
9 : #include "../stakes/fd_vote_states.h"
10 : #include "../fd_rwlock.h"
11 : #include "fd_blockhashes.h"
12 : #include "sysvar/fd_sysvar_cache.h"
13 : #include "../../ballet/lthash/fd_lthash.h"
14 : #include "fd_txncache_shmem.h"
15 :
16 : FD_PROTOTYPES_BEGIN
17 :
18 15 : #define FD_BANKS_MAGIC (0XF17EDA2C7EBA2450) /* FIREDANCER BANKS V0 */
19 :
20 : /* TODO: Some optimizations, cleanups, future work:
21 : 1. Simple data types (ulong, int, etc) should be stored as their
22 : underlying type instead of a byte array.
23 : 3. Rename locks to suffix with _query_locking and _query_locking_end
24 : */
25 :
26 : /* A fd_bank_t struct is the representation of the bank state on Solana
27 : for a given block. More specifically, the bank state corresponds to
28 : all information needed during execution that is not stored on-chain,
29 : but is instead cached in a validator's memory. Each of these bank
30 : fields are repesented by a member of the fd_bank_t struct.
31 :
32 : Management of fd_bank_t structs must be fork-aware: the state of each
33 : fd_bank_t must be based on the fd_bank_t of its parent block. This
34 : state is managed by the fd_banks_t struct.
35 :
36 : In order to support fork-awareness, there are several key features
37 : that fd_banks_t and fd_bank_t MUST support:
38 : 1. Query for any non-rooted block's bank: create a fast lookup
39 : from bank index to bank
40 : 2. Be able to create a new bank for a given block from the bank of
41 : that block's parent and maintain some tree-like structure to
42 : track the parent-child relationships: copy the contents from a
43 : parent bank into a child bank.
44 : 3. Prune the set of active banks to keep the root updated as the
45 : network progresses: free resources of fd_bank_t structs that
46 : are are not direct descendants of the root bank (remove parents
47 : and any competing lineages).
48 : 4. Each bank will have field(s) that are concurrently read/write
49 : from multiple threads: add read-write locks to the fields that are
50 : concurrently written to.
51 : 5. In practice, a bank state for a given block can be very large and
52 : not all of the fields are written to every block. Therefore, it
53 : can be very expensive to copy the entire bank state for a given
54 : block each time a bank is created. In order to avoid large
55 : memcpys, we can use a CoW mechanism for certain fields.
56 : 6. In a similar vein, some fields are very large and are not written
57 : to very often, and are only read at the epoch boundary. The most
58 : notable example is the stake delegations cache. In order to
59 : handle this, we can use a delta-based approach where each bank
60 : only has a delta of the stake delegations. The root bank will own
61 : the full set of stake delegations. This means that the deltas are
62 : only applied to the root bank as each bank gets rooted. If the
63 : caller needs to access the full set of stake delegations for a
64 : given bank, they can assemble the full set of stake delegations by
65 : applying all of the deltas from the current bank and all of its
66 : ancestors up to the root bank.
67 :
68 : fd_banks_t is represented by a left-child, right-sibling n-ary tree
69 : (inspired by fd_ghost) to keep track of the parent-child fork tree.
70 : The underlying data structure is a pool of fd_bank_t structs. Banks
71 : are then accessed via an index into the bank pool (bank index).
72 :
73 : NOTE: The reason fd_banks_t is keyed by bank index and not by slot is
74 : to handle block equivocation: if there are two different blocks for
75 : the same slot, we need to be able to differentiate and handle both
76 : blocks against different banks. As mentioned above, the bank index is
77 : just an index into the bank pool. The caller is responsible for
78 : establishing a mapping from the bank index (which is managed by
79 : fd_banks_t) and runtime state (e.g. slot number).
80 :
81 : The fields in fd_bank_t can be categorized into two groups:
82 : 1. Simple fields: these are fields which don't need any special
83 : handling and are laid out contiguously in the fd_bank_t struct.
84 : These types are also templatized out and are defined in the
85 : FD_BANKS_ITER macro.
86 : 2. Complex fields: these are fields which need special handling
87 : (e.g. locking, copy on write semantics, delta-based semantics).
88 : These types are not templatized and are manually defined below.
89 :
90 : Each field that is CoW has its own memory pool. The memory
91 : corresponding to the field is not located in the fd_bank_t struct and
92 : is instead represented by a pool index and a dirty flag. If the field
93 : is modified, then the dirty flag is set, and an element of the pool
94 : is acquired and the data is copied over from the parent pool idx.
95 :
96 : Currently, there is a delta-based field, fd_stake_delegations_t.
97 : Each bank stores a delta-based representation in the form of an
98 : aligned uchar buffer. The full state is stored in fd_banks_t also as
99 : a uchar buffer which corresponds to the full state of stake
100 : delegations for the current root. fd_banks_t also reserves another
101 : buffer which can store the full state of the stake delegations.
102 :
103 : The cost tracker is allocated from a pool. The lifetime of a cost
104 : tracker element starts when the bank is linked to a parent with a
105 : call to fd_banks_clone_from_parent() which makes the bank replayable.
106 : The lifetime of a cost tracker element ends when the bank is marked
107 : dead or when the bank is frozen.
108 :
109 : The lthash is a simple field that is laid out contiguously in the
110 : fd_bank_t struct, but is not templatized and it has its own lock.
111 :
112 : So, when a bank is cloned from a parent, the non CoW fields are copied
113 : over and the CoW fields just copy over a pool index. The CoW behavior
114 : is completely abstracted away from the caller as callers have to
115 : query/modify fields using specific APIs.
116 :
117 : The memory for the banks is based off of two bounds:
118 : 1. the max number of unrooted blocks at any given time. Most fields
119 : can be bounded by this value.
120 : 2. the max number of forks that execute through any 1 block. We bound
121 : fields that are only written to at the epoch boundary by
122 : the max fork width that can execute through the boundary instead of
123 : by the max number of banks. See fd_banks_footprint() for more
124 : details.
125 :
126 : There are also some important states that a bank can be in:
127 : - Initialized: This bank has been created and linked to a parent bank
128 : index with a call to fd_banks_new_bank(). However, it is not yet
129 : replayable.
130 : - Replayable: This bank has inherited state from its parent and now
131 : transactions can be executed against it. For a bank to become
132 : replayable, it must've been initialized beforehand.
133 : - Dead: This bank has been marked as dead. This means that the block
134 : that this bank is associated with is invalid. A bank can be marked
135 : dead before, during, or after it has finished replaying. A bank
136 : can still be executing transactions while it is marked dead, but it
137 : shouldn't be dispatched any more work.
138 : - Frozen: This bank has been marked as frozen and no other tasks
139 : should be dispatched to it. Any bank-specific resources will be
140 : released (e.g. cost tracker element). A bank can be marked frozen
141 : if the bank has finished executing all of its transactions or if the
142 : bank is marked as dead and has no outstanding references. A bank
143 : can only be copied from a parent bank (fd_banks_clone_from_parent)
144 : if the parent bank has been frozen. The program will crash if this
145 : invariant is violated.
146 :
147 : The usage pattern is as follows:
148 :
149 : To create an initial bank:
150 : fd_bank_t * bank_init = fd_bank_init_bank( banks );
151 :
152 : To create a new bank. This simply provisions the memory for the bank
153 : but it should not be used to execute transactions against.
154 : ulong bank_index = fd_banks_new_bank( banks, parent_bank_index );
155 :
156 : To clone bank from parent banks. This makes a bank replayable by
157 : copying over the state from the parent bank into the child. It
158 : assumes that the bank index has been previously provisioned by a call
159 : to fd_banks_new_bank and that the parent bank index has been frozen.
160 : fd_bank_t * bank_clone = fd_banks_clone_from_parent( banks, bank_index );
161 :
162 : To ensure that the bank index we want to advance our root to is safe
163 : and that there are no outstanding references to the banks that are
164 : not descendants of the target bank.
165 : fd_banks_advance_root_prepare( banks, target_bank_idx, &advanceable_bank_idx_out );
166 :
167 : To advance the root bank. This assumes that the bank index is "safe"
168 : to advance to. This means that none of the ancestors of the bank
169 : index have a non-zero reference count.
170 : fd_bank_t * root_bank = fd_banks_advance_root( banks, bank_index );
171 :
172 : To query some arbitrary bank:
173 : fd_bank_t * bank_query = fd_banks_bank_query( banks, bank_index );
174 :
175 : To access the fields in the bank if they are templatized:
176 :
177 : fd_struct_t const * field = fd_bank_field_query( bank );
178 : OR
179 : fd_struct field = fd_bank_field_get( bank );
180 :
181 : fd_struct_t * field = fd_bank_field_modify( bank );
182 : OR
183 : fd_bank_field_set( bank, value );
184 :
185 : If the fields are not templatized, their accessor and modifier
186 : patterns vary and are documented below.
187 : */
188 :
189 : #define FD_BANKS_ITER(X) \
190 : /* type, name, footprint, align */ \
191 : X(fd_blockhashes_t, block_hash_queue, sizeof(fd_blockhashes_t), alignof(fd_blockhashes_t) ) /* Block hash queue */ \
192 : X(fd_fee_rate_governor_t, fee_rate_governor, sizeof(fd_fee_rate_governor_t), alignof(fd_fee_rate_governor_t) ) /* Fee rate governor */ \
193 : X(ulong, rbh_lamports_per_sig, sizeof(ulong), alignof(ulong) ) /* Recent Block Hashes lamports per signature */ \
194 : X(ulong, slot, sizeof(ulong), alignof(ulong) ) /* Slot */ \
195 : X(ulong, parent_slot, sizeof(ulong), alignof(ulong) ) /* Parent slot */ \
196 : X(ulong, capitalization, sizeof(ulong), alignof(ulong) ) /* Capitalization */ \
197 : X(ulong, transaction_count, sizeof(ulong), alignof(ulong) ) /* Transaction count */ \
198 : X(ulong, parent_signature_cnt, sizeof(ulong), alignof(ulong) ) /* Parent signature count */ \
199 : X(ulong, tick_height, sizeof(ulong), alignof(ulong) ) /* Tick height */ \
200 : X(ulong, max_tick_height, sizeof(ulong), alignof(ulong) ) /* Max tick height */ \
201 : X(ulong, hashes_per_tick, sizeof(ulong), alignof(ulong) ) /* Hashes per tick */ \
202 : X(fd_w_u128_t, ns_per_slot, sizeof(fd_w_u128_t), alignof(fd_w_u128_t) ) /* NS per slot */ \
203 : X(ulong, ticks_per_slot, sizeof(ulong), alignof(ulong) ) /* Ticks per slot */ \
204 : X(ulong, genesis_creation_time, sizeof(ulong), alignof(ulong) ) /* Genesis creation time */ \
205 : X(double, slots_per_year, sizeof(double), alignof(double) ) /* Slots per year */ \
206 : X(fd_inflation_t, inflation, sizeof(fd_inflation_t), alignof(fd_inflation_t) ) /* Inflation */ \
207 : X(ulong, cluster_type, sizeof(ulong), alignof(ulong) ) /* Cluster type */ \
208 : X(ulong, total_epoch_stake, sizeof(ulong), alignof(ulong) ) /* Total epoch stake */ \
209 : /* This is only used for the get_epoch_stake syscall. */ \
210 : /* If we are executing in epoch E, this is the total */ \
211 : /* stake at the end of epoch E-1. */ \
212 : X(ulong, block_height, sizeof(ulong), alignof(ulong) ) /* Block height */ \
213 : X(ulong, execution_fees, sizeof(ulong), alignof(ulong) ) /* Execution fees */ \
214 : X(ulong, priority_fees, sizeof(ulong), alignof(ulong) ) /* Priority fees */ \
215 : X(ulong, tips, sizeof(ulong), alignof(ulong) ) /* Tips collected */ \
216 : X(ulong, signature_count, sizeof(ulong), alignof(ulong) ) /* Signature count */ \
217 : X(fd_hash_t, poh, sizeof(fd_hash_t), alignof(fd_hash_t) ) /* PoH */ \
218 : X(fd_sol_sysvar_last_restart_slot_t, last_restart_slot, sizeof(fd_sol_sysvar_last_restart_slot_t), alignof(fd_sol_sysvar_last_restart_slot_t) ) /* Last restart slot */ \
219 : X(fd_hash_t, bank_hash, sizeof(fd_hash_t), alignof(fd_hash_t) ) /* Bank hash */ \
220 : X(fd_hash_t, prev_bank_hash, sizeof(fd_hash_t), alignof(fd_hash_t) ) /* Previous bank hash */ \
221 : X(fd_hash_t, genesis_hash, sizeof(fd_hash_t), alignof(fd_hash_t) ) /* Genesis hash */ \
222 : X(fd_epoch_schedule_t, epoch_schedule, sizeof(fd_epoch_schedule_t), alignof(fd_epoch_schedule_t) ) /* Epoch schedule */ \
223 : X(fd_rent_t, rent, sizeof(fd_rent_t), alignof(fd_rent_t) ) /* Rent */ \
224 : X(fd_sysvar_cache_t, sysvar_cache, sizeof(fd_sysvar_cache_t), alignof(fd_sysvar_cache_t) ) /* Sysvar cache */ \
225 : /* then there can be 100k unique leaders in the worst */ \
226 : /* case. We also can assume 432k slots per epoch. */ \
227 : X(fd_features_t, features, sizeof(fd_features_t), alignof(fd_features_t) ) /* Features */ \
228 : X(ulong, txn_count, sizeof(ulong), alignof(ulong) ) /* Transaction count */ \
229 : X(ulong, nonvote_txn_count, sizeof(ulong), alignof(ulong) ) /* Nonvote transaction count */ \
230 : X(ulong, failed_txn_count, sizeof(ulong), alignof(ulong) ) /* Failed transaction count */ \
231 : X(ulong, nonvote_failed_txn_count, sizeof(ulong), alignof(ulong) ) /* Nonvote failed transaction count */ \
232 : X(ulong, total_compute_units_used, sizeof(ulong), alignof(ulong) ) /* Total compute units used */ \
233 : X(ulong, slots_per_epoch, sizeof(ulong), alignof(ulong) ) /* Slots per epoch */ \
234 : X(ulong, shred_cnt, sizeof(ulong), alignof(ulong) ) /* Shred count */ \
235 : X(ulong, epoch, sizeof(ulong), alignof(ulong) ) /* Epoch */ \
236 : X(int, has_identity_vote, sizeof(int), alignof(int) ) /* Has identity vote */
237 :
238 : /* Defining pools for any CoW fields. */
239 :
240 : struct fd_bank_epoch_rewards {
241 : ulong next;
242 : uchar data[FD_EPOCH_REWARDS_FOOTPRINT] __attribute__((aligned(FD_EPOCH_REWARDS_ALIGN)));
243 : };
244 : typedef struct fd_bank_epoch_rewards fd_bank_epoch_rewards_t;
245 :
246 : struct fd_bank_epoch_leaders {
247 : ulong next;
248 : uchar data[FD_EPOCH_LEADERS_MAX_FOOTPRINT] __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN)));
249 : };
250 : typedef struct fd_bank_epoch_leaders fd_bank_epoch_leaders_t;
251 :
252 : struct fd_bank_vote_states {
253 : ulong next;
254 : uchar data[FD_VOTE_STATES_FOOTPRINT] __attribute__((aligned(FD_VOTE_STATES_ALIGN)));
255 : };
256 : typedef struct fd_bank_vote_states fd_bank_vote_states_t;
257 :
258 : struct fd_bank_vote_states_prev {
259 : ulong next;
260 : uchar data[FD_VOTE_STATES_FOOTPRINT] __attribute__((aligned(FD_VOTE_STATES_ALIGN)));
261 : };
262 : typedef struct fd_bank_vote_states_prev fd_bank_vote_states_prev_t;
263 :
264 : struct fd_bank_vote_states_prev_prev {
265 : ulong next;
266 : uchar data[FD_VOTE_STATES_FOOTPRINT] __attribute__((aligned(FD_VOTE_STATES_ALIGN)));
267 : };
268 : typedef struct fd_bank_vote_states_prev_prev fd_bank_vote_states_prev_prev_t;
269 :
270 : struct fd_bank_cost_tracker {
271 : ulong next;
272 : uchar data[FD_COST_TRACKER_FOOTPRINT] __attribute__((aligned(FD_COST_TRACKER_ALIGN)));
273 : };
274 : typedef struct fd_bank_cost_tracker fd_bank_cost_tracker_t;
275 :
276 : #define POOL_NAME fd_bank_epoch_leaders_pool
277 297 : #define POOL_T fd_bank_epoch_leaders_t
278 : #include "../../util/tmpl/fd_pool.c"
279 :
280 : #define POOL_NAME fd_bank_epoch_rewards_pool
281 333 : #define POOL_T fd_bank_epoch_rewards_t
282 : #include "../../util/tmpl/fd_pool.c"
283 :
284 : #define POOL_NAME fd_bank_vote_states_pool
285 387 : #define POOL_T fd_bank_vote_states_t
286 : #include "../../util/tmpl/fd_pool.c"
287 :
288 : #define POOL_NAME fd_bank_vote_states_prev_pool
289 336 : #define POOL_T fd_bank_vote_states_prev_t
290 : #include "../../util/tmpl/fd_pool.c"
291 :
292 : #define POOL_NAME fd_bank_vote_states_prev_prev_pool
293 312 : #define POOL_T fd_bank_vote_states_prev_prev_t
294 : #include "../../util/tmpl/fd_pool.c"
295 :
296 : #define POOL_NAME fd_bank_cost_tracker_pool
297 669 : #define POOL_T fd_bank_cost_tracker_t
298 : #include "../../util/tmpl/fd_pool.c"
299 :
300 : /* Initialized. Not yet replayable. */
301 261 : #define FD_BANK_FLAGS_INIT (0x00000001UL)
302 : /* Replayable. Implies that FD_BANK_FLAGS_INIT is also set. */
303 108 : #define FD_BANK_FLAGS_REPLAYABLE (0x00000002UL)
304 : /* Frozen. We finished replaying or because it was a snapshot/genesis
305 : loaded bank. Implies that FD_BANK_FLAGS_REPLAYABLE is also set. */
306 105 : #define FD_BANK_FLAGS_FROZEN (0x00000004UL)
307 : /* Dead. We stopped replaying it before we could finish it (e.g.
308 : invalid block or pruned minority fork). It is implied that
309 : FD_BANK_FLAGS_INIT is set, but not necessarily
310 : FD_BANK_FLAGS_REPLAYABLE. */
311 51 : #define FD_BANK_FLAGS_DEAD (0x00000008UL)
312 : /* Rooted. Part of the consnensus root fork. Implies that
313 : FD_BANK_FLAGS_FROZEN is also set. */
314 114 : #define FD_BANK_FLAGS_ROOTED (0x00000010UL)
315 :
316 : /* As mentioned above, the overall layout of the bank struct:
317 : - Fields used for internal pool/bank management
318 : - Non-Cow fields
319 : - CoW fields
320 : - Locks for CoW fields
321 :
322 : The CoW fields are laid out contiguously in the bank struct.
323 : The locks for the CoW fields are laid out contiguously after the
324 : CoW fields.
325 :
326 : (r) Field is owned by the replay tile, and should be updated only by
327 : the replay tile.
328 : */
329 :
330 : struct fd_bank {
331 :
332 : /* Fields used for internal pool and bank management */
333 : ulong idx; /* current fork idx of the bank (synchronized with the pool index) */
334 : ulong next; /* reserved for internal use by pool and fd_banks_advance_root */
335 : ulong parent_idx; /* index of the parent in the node pool */
336 : ulong child_idx; /* index of the left-child in the node pool */
337 : ulong sibling_idx; /* index of the right-sibling in the node pool */
338 : ulong flags; /* (r) keeps track of the state of the bank, as well as some configurations */
339 : ulong bank_seq; /* app-wide bank sequence number */
340 :
341 : ulong refcnt; /* (r) reference count on the bank, see replay for more details */
342 :
343 : fd_txncache_fork_id_t txncache_fork_id; /* fork id used by the txn cache */
344 :
345 : /* Timestamps written and read only by replay */
346 :
347 : long first_fec_set_received_nanos;
348 : long preparation_begin_nanos;
349 : long first_transaction_scheduled_nanos;
350 : long last_transaction_finished_nanos;
351 :
352 : /* First, layout all non-CoW fields contiguously. This is done to
353 : allow for cloning the bank state with a simple memcpy. Each
354 : non-CoW field is just represented as a byte array. */
355 :
356 : fd_rwlock_t lthash_lock;
357 :
358 : struct {
359 : fd_lthash_value_t lthash;
360 :
361 : #define X(type, name, footprint, align) uchar name[footprint] __attribute__((aligned(align)));
362 : FD_BANKS_ITER(X)
363 : #undef X
364 : } non_cow;
365 :
366 : /* Layout all information needed for non-templatized fields. */
367 :
368 : fd_rwlock_t cost_tracker_lock;
369 : ulong cost_tracker_pool_idx;
370 : ulong cost_tracker_pool_offset;
371 :
372 : fd_rwlock_t stake_delegations_delta_lock;
373 : int stake_delegations_delta_dirty;
374 : uchar stake_delegations_delta[FD_STAKE_DELEGATIONS_DELTA_FOOTPRINT] __attribute__((aligned(FD_STAKE_DELEGATIONS_ALIGN)));
375 :
376 : fd_rwlock_t vote_states_lock;
377 : int vote_states_dirty;
378 : ulong vote_states_pool_idx;
379 : ulong vote_states_pool_offset;
380 : ulong vote_states_pool_lock_offset;
381 :
382 : int epoch_rewards_dirty;
383 : ulong epoch_rewards_pool_idx;
384 : ulong epoch_rewards_pool_offset;
385 : ulong epoch_rewards_pool_lock_offset;
386 :
387 : int epoch_leaders_dirty;
388 : ulong epoch_leaders_pool_idx;
389 : ulong epoch_leaders_pool_offset;
390 : ulong epoch_leaders_pool_lock_offset;
391 :
392 : int vote_states_prev_dirty;
393 : ulong vote_states_prev_pool_idx;
394 : ulong vote_states_prev_pool_offset;
395 : ulong vote_states_prev_pool_lock_offset;
396 :
397 : int vote_states_prev_prev_dirty;
398 : ulong vote_states_prev_prev_pool_idx;
399 : ulong vote_states_prev_prev_pool_offset;
400 : ulong vote_states_prev_prev_pool_lock_offset;
401 : };
402 : typedef struct fd_bank fd_bank_t;
403 :
404 : static inline void
405 114 : fd_bank_set_epoch_rewards_pool( fd_bank_t * bank, fd_bank_epoch_rewards_t * epoch_rewards_pool ) {
406 114 : void * epoch_rewards_pool_mem = fd_bank_epoch_rewards_pool_leave( epoch_rewards_pool );
407 114 : if( FD_UNLIKELY( !epoch_rewards_pool_mem ) ) {
408 0 : FD_LOG_CRIT(( "Failed to leave epoch rewards pool" ));
409 0 : }
410 114 : bank->epoch_rewards_pool_offset = (ulong)epoch_rewards_pool_mem - (ulong)bank;
411 114 : }
412 :
413 : static inline fd_bank_epoch_rewards_t *
414 138 : fd_bank_get_epoch_rewards_pool( fd_bank_t * bank ) {
415 138 : return fd_bank_epoch_rewards_pool_join( (uchar *)bank + bank->epoch_rewards_pool_offset );
416 138 : }
417 :
418 : static inline void
419 114 : fd_bank_set_epoch_rewards_pool_lock( fd_bank_t * bank, fd_rwlock_t * rwlock ) {
420 114 : bank->epoch_rewards_pool_lock_offset = (ulong)rwlock - (ulong)bank;
421 114 : }
422 :
423 : static inline fd_rwlock_t *
424 18 : fd_bank_get_epoch_rewards_pool_lock( fd_bank_t * bank ) {
425 18 : return (fd_rwlock_t *)( (uchar *)bank + bank->epoch_rewards_pool_lock_offset );
426 18 : }
427 :
428 : static inline void
429 114 : fd_bank_set_epoch_leaders_pool( fd_bank_t * bank, fd_bank_epoch_leaders_t * epoch_leaders_pool ) {
430 114 : void * epoch_leaders_pool_mem = fd_bank_epoch_leaders_pool_leave( epoch_leaders_pool );
431 114 : if( FD_UNLIKELY( !epoch_leaders_pool_mem ) ) {
432 0 : FD_LOG_CRIT(( "Failed to leave epoch leaders pool" ));
433 0 : }
434 114 : bank->epoch_leaders_pool_offset = (ulong)epoch_leaders_pool_mem - (ulong)bank;
435 114 : }
436 :
437 : static inline fd_bank_epoch_leaders_t *
438 102 : fd_bank_get_epoch_leaders_pool( fd_bank_t * bank ) {
439 102 : return fd_bank_epoch_leaders_pool_join( (uchar *)bank + bank->epoch_leaders_pool_offset );
440 102 : }
441 :
442 : static inline void
443 114 : fd_bank_set_epoch_leaders_pool_lock( fd_bank_t * bank, fd_rwlock_t * rwlock ) {
444 114 : bank->epoch_leaders_pool_lock_offset = (ulong)rwlock - (ulong)bank;
445 114 : }
446 :
447 : static inline fd_rwlock_t *
448 18 : fd_bank_get_epoch_leaders_pool_lock( fd_bank_t * bank ) {
449 18 : return (fd_rwlock_t *)( (uchar *)bank + bank->epoch_leaders_pool_lock_offset );
450 18 : }
451 :
452 : static inline void
453 114 : fd_bank_set_vote_states_pool( fd_bank_t * bank, fd_bank_vote_states_t * vote_states_pool ) {
454 114 : void * vote_states_pool_mem = fd_bank_vote_states_pool_leave( vote_states_pool );
455 114 : if( FD_UNLIKELY( !vote_states_pool_mem ) ) {
456 0 : FD_LOG_CRIT(( "Failed to leave vote states pool" ));
457 0 : }
458 114 : bank->vote_states_pool_offset = (ulong)vote_states_pool_mem - (ulong)bank;
459 114 : }
460 :
461 : static inline fd_bank_vote_states_t *
462 177 : fd_bank_get_vote_states_pool( fd_bank_t * bank ) {
463 177 : return fd_bank_vote_states_pool_join( (uchar *)bank + bank->vote_states_pool_offset );
464 177 : }
465 :
466 : static inline void
467 114 : fd_bank_set_vote_states_pool_lock( fd_bank_t * bank, fd_rwlock_t * rwlock ) {
468 114 : bank->vote_states_pool_lock_offset = (ulong)rwlock - (ulong)bank;
469 114 : }
470 :
471 : static inline fd_rwlock_t *
472 24 : fd_bank_get_vote_states_pool_lock( fd_bank_t * bank ) {
473 24 : return (fd_rwlock_t *)( (uchar *)bank + bank->vote_states_pool_lock_offset );
474 24 : }
475 :
476 : static inline void
477 114 : fd_bank_set_vote_states_prev_pool( fd_bank_t * bank, fd_bank_vote_states_prev_t * vote_states_prev_pool ) {
478 114 : void * vote_states_prev_pool_mem = fd_bank_vote_states_prev_pool_leave( vote_states_prev_pool );
479 114 : if( FD_UNLIKELY( !vote_states_prev_pool_mem ) ) {
480 0 : FD_LOG_CRIT(( "Failed to leave vote states prev pool" ));
481 0 : }
482 114 : bank->vote_states_prev_pool_offset = (ulong)vote_states_prev_pool_mem - (ulong)bank;
483 114 : }
484 :
485 : static inline fd_bank_vote_states_prev_t *
486 126 : fd_bank_get_vote_states_prev_pool( fd_bank_t * bank ) {
487 126 : return fd_bank_vote_states_prev_pool_join( (uchar *)bank + bank->vote_states_prev_pool_offset );
488 126 : }
489 :
490 : static inline void
491 114 : fd_bank_set_vote_states_prev_pool_lock( fd_bank_t * bank, fd_rwlock_t * rwlock ) {
492 114 : bank->vote_states_prev_pool_lock_offset = (ulong)rwlock - (ulong)bank;
493 114 : }
494 :
495 : static inline fd_rwlock_t *
496 30 : fd_bank_get_vote_states_prev_pool_lock( fd_bank_t * bank ) {
497 30 : return (fd_rwlock_t *)( (uchar *)bank + bank->vote_states_prev_pool_lock_offset );
498 30 : }
499 :
500 : static inline void
501 114 : fd_bank_set_vote_states_prev_prev_pool( fd_bank_t * bank, fd_bank_vote_states_prev_prev_t * vote_states_prev_prev_pool ) {
502 114 : void * vote_states_prev_prev_pool_mem = fd_bank_vote_states_prev_prev_pool_leave( vote_states_prev_prev_pool );
503 114 : if( FD_UNLIKELY( !vote_states_prev_prev_pool_mem ) ) {
504 0 : FD_LOG_CRIT(( "Failed to leave vote states prev prev pool" ));
505 0 : }
506 114 : bank->vote_states_prev_prev_pool_offset = (ulong)vote_states_prev_prev_pool_mem - (ulong)bank;
507 114 : }
508 :
509 : static inline fd_bank_vote_states_prev_prev_t *
510 102 : fd_bank_get_vote_states_prev_prev_pool( fd_bank_t * bank ) {
511 102 : return fd_bank_vote_states_prev_prev_pool_join( (uchar *)bank + bank->vote_states_prev_prev_pool_offset );
512 102 : }
513 :
514 : static inline void
515 114 : fd_bank_set_vote_states_prev_prev_pool_lock( fd_bank_t * bank, fd_rwlock_t * rwlock ) {
516 114 : bank->vote_states_prev_prev_pool_lock_offset = (ulong)rwlock - (ulong)bank;
517 114 : }
518 :
519 : static inline fd_rwlock_t *
520 18 : fd_bank_get_vote_states_prev_prev_pool_lock( fd_bank_t * bank ) {
521 18 : return (fd_rwlock_t *)( (uchar *)bank + bank->vote_states_prev_prev_pool_lock_offset );
522 18 : }
523 :
524 : /* Do the same setup for the cost tracker pool. */
525 :
526 : static inline void
527 114 : fd_bank_set_cost_tracker_pool( fd_bank_t * bank, fd_bank_cost_tracker_t * cost_tracker_pool ) {
528 114 : void * cost_tracker_pool_mem = fd_bank_cost_tracker_pool_leave( cost_tracker_pool );
529 114 : if( FD_UNLIKELY( !cost_tracker_pool_mem ) ) {
530 0 : FD_LOG_CRIT(( "Failed to leave cost tracker pool" ));
531 0 : }
532 114 : bank->cost_tracker_pool_offset = (ulong)cost_tracker_pool_mem - (ulong)bank;
533 114 : }
534 :
535 : static inline fd_bank_cost_tracker_t *
536 489 : fd_bank_get_cost_tracker_pool( fd_bank_t * bank ) {
537 489 : return fd_bank_cost_tracker_pool_join( (uchar *)bank + bank->cost_tracker_pool_offset );
538 489 : }
539 :
540 : /* fd_bank_t is the alignment for the bank state. */
541 :
542 : ulong
543 : fd_bank_align( void );
544 :
545 : /* fd_bank_t is the footprint for the bank state. This does NOT
546 : include the footprint for the CoW state. */
547 :
548 : ulong
549 : fd_bank_footprint( void );
550 :
551 : /**********************************************************************/
552 : /* fd_banks_t is the main struct used to manage the bank state. It can
553 : be used to query/modify/clone/publish the bank state.
554 :
555 : fd_banks_t contains some metadata to a pool to manage the banks.
556 : It also contains pointers to the CoW pools.
557 :
558 : The data is laid out contiguously in memory starting from fd_banks_t;
559 : this can be seen in fd_banks_footprint(). */
560 :
561 : #define POOL_NAME fd_banks_pool
562 636 : #define POOL_T fd_bank_t
563 : #include "../../util/tmpl/fd_pool.c"
564 :
565 : struct fd_banks {
566 : ulong magic; /* ==FD_BANKS_MAGIC */
567 : ulong max_total_banks; /* Maximum number of banks */
568 : ulong max_fork_width; /* Maximum fork width executing through
569 : any given slot. */
570 : ulong root_idx; /* root idx */
571 : ulong bank_seq; /* app-wide bank sequence number */
572 :
573 : /* This lock is only used to serialize banks fork tree reads with
574 : respect to fork tree writes. In other words, tree traversals
575 : cannot happen at the same time as a tree pruning operation or a
576 : tree insertion operation. So the public APIs on banks take either
577 : a read lock or a write lock depending on what they do on the fork
578 : tree. For example, publishing takes a write lock, and bank lookups
579 : take a read lock. Notably, individual banks can still be
580 : concurrently accessed or modified, and this lock does not offer
581 : synchronization on individual fields within a bank. */
582 : fd_rwlock_t rwlock;
583 :
584 : ulong pool_offset; /* offset of pool from banks */
585 :
586 : ulong cost_tracker_pool_offset; /* offset of cost tracker pool from banks */
587 :
588 : ulong vote_states_pool_offset_;
589 :
590 : /* stake_delegations_root will be the full state of stake delegations
591 : for the current root. It can get updated in two ways:
592 : 1. On boot the snapshot will be directly read into the rooted
593 : stake delegations because we assume that any and all snapshots
594 : are a rooted slot.
595 : 2. Calls to fd_banks_publish() will apply all of the stake
596 : delegation deltas from each of the banks that are about to be
597 : published. */
598 :
599 : uchar stake_delegations_root[FD_STAKE_DELEGATIONS_FOOTPRINT] __attribute__((aligned(FD_STAKE_DELEGATIONS_ALIGN)));
600 :
601 : /* stake_delegations_frontier is reserved memory that can represent
602 : the full state of stake delegations for the current frontier. This
603 : is done by taking the stake_delegations_root and applying all of
604 : the deltas from the current bank and all of its ancestors up to the
605 : root bank. */
606 :
607 : uchar stake_delegations_frontier[FD_STAKE_DELEGATIONS_FOOTPRINT] __attribute__((aligned(FD_STAKE_DELEGATIONS_ALIGN)));
608 :
609 : /* Layout all CoW pools. */
610 :
611 : ulong epoch_rewards_pool_offset;
612 : fd_rwlock_t epoch_rewards_pool_lock;
613 :
614 : ulong epoch_leaders_pool_offset;
615 : fd_rwlock_t epoch_leaders_pool_lock;
616 :
617 : ulong vote_states_pool_offset;
618 : fd_rwlock_t vote_states_pool_lock;
619 :
620 : ulong vote_states_prev_pool_offset;
621 : fd_rwlock_t vote_states_prev_pool_lock;
622 :
623 : ulong vote_states_prev_prev_pool_offset;
624 : fd_rwlock_t vote_states_prev_prev_pool_lock;
625 : };
626 : typedef struct fd_banks fd_banks_t;
627 :
628 : /* Bank accesssors and mutators. Different accessors are emitted for
629 : different types depending on if the field has a lock or not. */
630 :
631 : fd_epoch_rewards_t const *
632 : fd_bank_epoch_rewards_query( fd_bank_t * bank );
633 :
634 : fd_epoch_rewards_t *
635 : fd_bank_epoch_rewards_modify( fd_bank_t * bank );
636 :
637 : fd_epoch_leaders_t const *
638 : fd_bank_epoch_leaders_query( fd_bank_t * bank );
639 :
640 : fd_epoch_leaders_t *
641 : fd_bank_epoch_leaders_modify( fd_bank_t * bank );
642 :
643 : fd_vote_states_t const *
644 : fd_bank_vote_states_prev_query( fd_bank_t * bank );
645 :
646 : fd_vote_states_t *
647 : fd_bank_vote_states_prev_modify( fd_bank_t * bank );
648 :
649 : fd_vote_states_t const *
650 : fd_bank_vote_states_prev_prev_query( fd_bank_t * bank );
651 :
652 : fd_vote_states_t *
653 : fd_bank_vote_states_prev_prev_modify( fd_bank_t * bank );
654 :
655 : fd_vote_states_t const *
656 : fd_bank_vote_states_locking_query( fd_bank_t * bank );
657 :
658 : void
659 : fd_bank_vote_states_end_locking_query( fd_bank_t * bank );
660 :
661 : fd_vote_states_t *
662 : fd_bank_vote_states_locking_modify( fd_bank_t * bank );
663 :
664 : void
665 : fd_bank_vote_states_end_locking_modify( fd_bank_t * bank );
666 :
667 : fd_cost_tracker_t *
668 : fd_bank_cost_tracker_locking_modify( fd_bank_t * bank );
669 :
670 : void
671 : fd_bank_cost_tracker_end_locking_modify( fd_bank_t * bank );
672 :
673 : fd_cost_tracker_t const *
674 : fd_bank_cost_tracker_locking_query( fd_bank_t * bank );
675 :
676 : void
677 : fd_bank_cost_tracker_end_locking_query( fd_bank_t * bank );
678 :
679 : fd_lthash_value_t const *
680 : fd_bank_lthash_locking_query( fd_bank_t * bank );
681 :
682 : void
683 : fd_bank_lthash_end_locking_query( fd_bank_t * bank );
684 :
685 : fd_lthash_value_t *
686 : fd_bank_lthash_locking_modify( fd_bank_t * bank );
687 :
688 : void
689 : fd_bank_lthash_end_locking_modify( fd_bank_t * bank );
690 :
691 : #define X(type, name, footprint, align) \
692 : void fd_bank_##name##_set( fd_bank_t * bank, type value ); \
693 : type fd_bank_##name##_get( fd_bank_t const * bank ); \
694 : type const * fd_bank_##name##_query( fd_bank_t const * bank ); \
695 : type * fd_bank_##name##_modify( fd_bank_t * bank );
696 : FD_BANKS_ITER(X)
697 : #undef X
698 :
699 : /* Each bank has a fd_stake_delegations_t object which is delta-based.
700 : The usage pattern is the same as other bank fields:
701 : 1. fd_bank_stake_delegations_delta_locking_modify( bank ) will return
702 : a mutable pointer to the stake delegations delta object. If the
703 : caller has not yet initialized the delta object, then it will
704 : be initialized. Because it is a delta it is not copied over from
705 : a parent bank.
706 : 2. fd_bank_stake_delegations_delta_locking_query( bank ) will return
707 : a const pointer to the stake delegations delta object. If the
708 : delta object has not been initialized, then NULL is returned.
709 : 3. fd_bank_stake_delegations_delta_locking_end_modify( bank ) will
710 : release the write lock on the object.
711 : 4. fd_bank_stake_delegations_delta_locking_end_query( bank ) will
712 : release a read lock on the object.
713 : */
714 :
715 : static inline fd_stake_delegations_t *
716 15 : fd_bank_stake_delegations_delta_locking_modify( fd_bank_t * bank ) {
717 15 : fd_rwlock_write( &bank->stake_delegations_delta_lock );
718 15 : if( !bank->stake_delegations_delta_dirty ) {
719 15 : bank->stake_delegations_delta_dirty = 1;
720 15 : fd_stake_delegations_init( fd_type_pun( bank->stake_delegations_delta ) );
721 15 : }
722 15 : return fd_type_pun( bank->stake_delegations_delta );
723 15 : }
724 :
725 : static inline void
726 15 : fd_bank_stake_delegations_delta_end_locking_modify( fd_bank_t * bank ) {
727 15 : fd_rwlock_unwrite( &bank->stake_delegations_delta_lock );
728 15 : }
729 :
730 : static inline fd_stake_delegations_t *
731 0 : fd_bank_stake_delegations_delta_locking_query( fd_bank_t * bank ) {
732 0 : fd_rwlock_read( &bank->stake_delegations_delta_lock );
733 0 : return bank->stake_delegations_delta_dirty ? fd_stake_delegations_join( bank->stake_delegations_delta ) : NULL;
734 0 : }
735 :
736 : static inline void
737 0 : fd_bank_stake_delegations_delta_end_locking_query( fd_bank_t * bank ) {
738 0 : fd_rwlock_unread( &bank->stake_delegations_delta_lock );
739 0 : }
740 :
741 : /* fd_bank_stake_delegations_frontier_query() will return a pointer to
742 : the full stake delegations for the current frontier. The caller is
743 : responsible that there are no concurrent readers or writers to
744 : the stake delegations returned by this function.
745 :
746 : Under the hood, the function copies the rooted stake delegations and
747 : applies all of the deltas for the direct ancestry from the current
748 : bank up to the rooted bank to the copy. */
749 :
750 : fd_stake_delegations_t *
751 : fd_bank_stake_delegations_frontier_query( fd_banks_t * banks,
752 : fd_bank_t * bank );
753 :
754 : /* fd_banks_stake_delegations_root_query() will return a pointer to the
755 : full stake delegations for the current root. This function should
756 : only be called on boot. */
757 :
758 : fd_stake_delegations_t *
759 : fd_banks_stake_delegations_root_query( fd_banks_t * banks );
760 :
761 : /* Simple getters and setters for the pools/maps in fd_banks_t. Notably,
762 : the pool for the fd_bank_t structs as well as a map and pool pair of
763 : the CoW structs in the banks. */
764 :
765 : static inline fd_bank_t *
766 588 : fd_banks_get_bank_pool( fd_banks_t const * banks ) {
767 588 : return fd_banks_pool_join( ((uchar *)banks + banks->pool_offset) );
768 588 : }
769 :
770 : static inline void
771 : fd_banks_set_bank_pool( fd_banks_t * banks,
772 15 : fd_bank_t * bank_pool ) {
773 15 : void * bank_pool_mem = fd_banks_pool_leave( bank_pool );
774 15 : if( FD_UNLIKELY( !bank_pool_mem ) ) {
775 0 : FD_LOG_CRIT(( "Failed to leave bank pool" ));
776 0 : }
777 15 : banks->pool_offset = (ulong)bank_pool_mem - (ulong)banks;
778 15 : }
779 :
780 : static inline fd_bank_epoch_rewards_t *
781 147 : fd_banks_get_epoch_rewards_pool( fd_banks_t * banks ) {
782 147 : return fd_bank_epoch_rewards_pool_join( (uchar *)banks + banks->epoch_rewards_pool_offset );
783 147 : }
784 :
785 : static inline void
786 15 : fd_banks_set_epoch_rewards_pool( fd_banks_t * banks, fd_bank_epoch_rewards_t * epoch_rewards_pool ) {
787 15 : void * epoch_rewards_pool_mem = fd_bank_epoch_rewards_pool_leave( epoch_rewards_pool );
788 15 : if( FD_UNLIKELY( !epoch_rewards_pool_mem ) ) {
789 0 : FD_LOG_CRIT(( "Failed to leave epoch rewards pool" ));
790 0 : }
791 15 : banks->epoch_rewards_pool_offset = (ulong)epoch_rewards_pool_mem - (ulong)banks;
792 15 : }
793 :
794 : static inline fd_bank_epoch_leaders_t *
795 147 : fd_banks_get_epoch_leaders_pool( fd_banks_t * banks ) {
796 147 : return fd_bank_epoch_leaders_pool_join( (uchar *)banks + banks->epoch_leaders_pool_offset );
797 147 : }
798 :
799 : static inline void
800 15 : fd_banks_set_epoch_leaders_pool( fd_banks_t * banks, fd_bank_epoch_leaders_t * epoch_leaders_pool ) {
801 15 : void * epoch_leaders_pool_mem = fd_bank_epoch_leaders_pool_leave( epoch_leaders_pool );
802 15 : if( FD_UNLIKELY( !epoch_leaders_pool_mem ) ) {
803 0 : FD_LOG_CRIT(( "Failed to leave epoch leaders pool" ));
804 0 : }
805 15 : banks->epoch_leaders_pool_offset = (ulong)epoch_leaders_pool_mem - (ulong)banks;
806 15 : }
807 :
808 : static inline fd_bank_vote_states_t *
809 162 : fd_banks_get_vote_states_pool( fd_banks_t * banks ) {
810 162 : return fd_bank_vote_states_pool_join( (uchar *)banks + banks->vote_states_pool_offset );
811 162 : }
812 :
813 : static inline void
814 15 : fd_banks_set_vote_states_pool( fd_banks_t * banks, fd_bank_vote_states_t * vote_states_pool ) {
815 15 : void * vote_states_pool_mem = fd_bank_vote_states_pool_leave( vote_states_pool );
816 15 : if( FD_UNLIKELY( !vote_states_pool_mem ) ) {
817 0 : FD_LOG_CRIT(( "Failed to leave vote states pool" ));
818 0 : }
819 15 : banks->vote_states_pool_offset = (ulong)vote_states_pool_mem - (ulong)banks;
820 15 : }
821 :
822 : static inline fd_bank_vote_states_prev_t *
823 162 : fd_banks_get_vote_states_prev_pool( fd_banks_t * banks ) {
824 162 : return fd_bank_vote_states_prev_pool_join( (uchar *)banks + banks->vote_states_prev_pool_offset );
825 162 : }
826 :
827 : static inline void
828 15 : fd_banks_set_vote_states_prev_pool( fd_banks_t * banks, fd_bank_vote_states_prev_t * vote_states_prev_pool ) {
829 15 : void * vote_states_prev_pool_mem = fd_bank_vote_states_prev_pool_leave( vote_states_prev_pool );
830 15 : if( FD_UNLIKELY( !vote_states_prev_pool_mem ) ) {
831 0 : FD_LOG_CRIT(( "Failed to leave vote states prev pool" ));
832 0 : }
833 15 : banks->vote_states_prev_pool_offset = (ulong)vote_states_prev_pool_mem - (ulong)banks;
834 15 : }
835 :
836 : static inline fd_bank_vote_states_prev_prev_t *
837 162 : fd_banks_get_vote_states_prev_prev_pool( fd_banks_t * banks ) {
838 162 : return fd_bank_vote_states_prev_prev_pool_join( (uchar *)banks + banks->vote_states_prev_prev_pool_offset );
839 162 : }
840 :
841 : static inline void
842 15 : fd_banks_set_vote_states_prev_prev_pool( fd_banks_t * banks, fd_bank_vote_states_prev_prev_t * vote_states_prev_prev_pool ) {
843 15 : void * vote_states_prev_prev_pool_mem = fd_bank_vote_states_prev_prev_pool_leave( vote_states_prev_prev_pool );
844 15 : if( FD_UNLIKELY( !vote_states_prev_prev_pool_mem ) ) {
845 0 : FD_LOG_CRIT(( "Failed to leave vote states prev prev pool" ));
846 0 : }
847 15 : banks->vote_states_prev_prev_pool_offset = (ulong)vote_states_prev_prev_pool_mem - (ulong)banks;
848 15 : }
849 :
850 : static inline fd_bank_cost_tracker_t *
851 132 : fd_banks_get_cost_tracker_pool( fd_banks_t * banks ) {
852 132 : return fd_bank_cost_tracker_pool_join( (uchar *)banks + banks->cost_tracker_pool_offset );
853 132 : }
854 :
855 : static inline void
856 : fd_banks_set_cost_tracker_pool( fd_banks_t * banks,
857 15 : fd_bank_cost_tracker_t * cost_tracker_pool ) {
858 15 : void * cost_tracker_pool_mem = fd_bank_cost_tracker_pool_leave( cost_tracker_pool );
859 15 : if( FD_UNLIKELY( !cost_tracker_pool_mem ) ) {
860 0 : FD_LOG_CRIT(( "Failed to leave cost tracker pool" ));
861 0 : }
862 15 : banks->cost_tracker_pool_offset = (ulong)cost_tracker_pool_mem - (ulong)banks;
863 15 : }
864 :
865 : /* fd_banks_root() and fd_banks_root_const() returns a non-const and
866 : const pointer to the root bank respectively. */
867 :
868 : FD_FN_PURE static inline fd_bank_t const *
869 0 : fd_banks_root_const( fd_banks_t const * banks ) {
870 0 : return fd_banks_pool_ele_const( fd_banks_get_bank_pool( banks ), banks->root_idx );
871 0 : }
872 :
873 : FD_FN_PURE static inline fd_bank_t *
874 69 : fd_banks_root( fd_banks_t * banks ) {
875 69 : return fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), banks->root_idx );
876 69 : }
877 :
878 : /* fd_banks_align() returns the alignment of fd_banks_t */
879 :
880 : ulong
881 : fd_banks_align( void );
882 :
883 : /* fd_banks_footprint() returns the footprint of fd_banks_t. This
884 : includes the struct itself but also the footprint for all of the
885 : pools.
886 :
887 : The footprint of fd_banks_t is determined by the total number
888 : of banks that the bank manages. This is an analog for the max number
889 : of unrooted blocks the bank can manage at any given time.
890 :
891 : We can also further bound the memory footprint of the banks by the
892 : max width of forks that can exist at any given time. The reason for
893 : this is that there are several large CoW structs that are only
894 : written to during the epoch boundary (e.g. epoch_rewards,
895 : epoch_stakes, etc.). These structs are read-only afterwards. This
896 : means if we also bound the max number of forks that can execute
897 : through the epoch boundary, we can bound the memory footprint of
898 : the banks. */
899 :
900 : ulong
901 : fd_banks_footprint( ulong max_total_banks,
902 : ulong max_fork_width );
903 :
904 : /* fd_banks_new() creates a new fd_banks_t struct. This function lays
905 : out the memory for all of the constituent fd_bank_t structs and
906 : pools depending on the max_total_banks and the max_fork_width for a
907 : given block. */
908 :
909 : void *
910 : fd_banks_new( void * mem,
911 : ulong max_total_banks,
912 : ulong max_fork_width,
913 : int larger_max_cost_per_block,
914 : ulong seed );
915 :
916 : /* fd_banks_join() joins a new fd_banks_t struct. */
917 :
918 : fd_banks_t *
919 : fd_banks_join( void * mem );
920 :
921 : /* fd_banks_leave() leaves a bank. */
922 :
923 : void *
924 : fd_banks_leave( fd_banks_t * banks );
925 :
926 : /* fd_banks_delete() deletes a bank. */
927 :
928 : void *
929 : fd_banks_delete( void * shmem );
930 :
931 : /* fd_banks_init_bank() initializes a new bank in the bank manager.
932 : This should only be used during bootup. This returns an initial
933 : fd_bank_t with the corresponding bank index set to 0. */
934 :
935 : fd_bank_t *
936 : fd_banks_init_bank( fd_banks_t * banks );
937 :
938 : /* fd_banks_get_bank_idx returns a bank for a given bank index. */
939 :
940 : static inline fd_bank_t *
941 : fd_banks_bank_mem_query( fd_banks_t * banks,
942 0 : ulong bank_idx ) {
943 0 : return fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank_idx );
944 0 : }
945 :
946 : static inline fd_bank_t *
947 : fd_banks_bank_query( fd_banks_t * banks,
948 153 : ulong bank_idx ) {
949 153 : fd_bank_t * bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank_idx );
950 153 : return (bank->flags&FD_BANK_FLAGS_INIT) ? bank : NULL;
951 153 : }
952 :
953 : static inline fd_bank_t *
954 : fd_banks_get_parent( fd_banks_t * banks,
955 0 : fd_bank_t * bank ) {
956 0 : return fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank->parent_idx );
957 0 : }
958 :
959 : /* fd_banks_clone_from_parent() clones a bank from a parent bank.
960 : This function links the child bank to its parent bank and copies
961 : over the data from the parent bank to the child. This function
962 : assumes that the child and parent banks both have been allocated.
963 : The parent bank must be frozen and the child bank must be initialized
964 : but not yet used.
965 :
966 : A more detailed note: not all of the data is copied over and this
967 : is a shallow clone. All of the CoW fields are not copied over and
968 : will only be done so if the caller explicitly calls
969 : fd_bank_{*}_modify(). This naming was chosen to emulate the
970 : semantics of the Agave client. */
971 :
972 : fd_bank_t *
973 : fd_banks_clone_from_parent( fd_banks_t * banks,
974 : ulong bank_idx );
975 :
976 : /* fd_banks_advance_root() advances the root bank to the bank manager.
977 : This should only be used when a bank is no longer needed and has no
978 : active refcnts. This will prune off the bank from the bank manager.
979 : It returns the new root bank. An invariant of this function is that
980 : the new root bank should be a child of the current root bank.
981 :
982 : All banks that are ancestors or siblings of the new root bank will be
983 : cancelled and their resources will be released back to the pool. */
984 :
985 : fd_bank_t const *
986 : fd_banks_advance_root( fd_banks_t * banks,
987 : ulong bank_idx );
988 :
989 : /* fd_bank_clear_bank() clears the contents of a bank. This should ONLY
990 : be used with banks that have no children and should only be used in
991 : testing and fuzzing.
992 :
993 : This function will memset all non-CoW fields to 0.
994 :
995 : For all CoW fields, we will reset the indices to its parent. */
996 :
997 : void
998 : fd_banks_clear_bank( fd_banks_t * banks,
999 : fd_bank_t * bank,
1000 : ulong max_vote_accounts );
1001 :
1002 : /* fd_banks_advance_root_prepare returns the highest block that can be
1003 : safely advanced between the current root of the fork tree and the
1004 : target block. See the note on safe publishing for more details. In
1005 : general, a node in the fork tree can be pruned if:
1006 : (1) the node itself can be pruned, and
1007 : (2) all subtrees (except for the one on the rooted fork) forking off
1008 : of the node can be pruned.
1009 : The highest publishable block is the highest block on the rooted fork
1010 : where the above is true, or the rooted child block of such if there
1011 : is one.
1012 :
1013 : This function assumes that the given target block has been rooted by
1014 : consensus. It will mark every block on the rooted fork as rooted, up
1015 : to the given target block. It will also mark minority forks as dead.
1016 :
1017 : Highest advanceable block is written to the out pointer. Returns 1
1018 : if the advanceable block can be advanced beyond the current root.
1019 : Returns 0 if no such block can be found. We will ONLY advance our
1020 : advanceable_bank_idx to a child of the current root. In order to
1021 : advance to the target bank, fd_banks_advance_root_prepare() must be
1022 : called repeatedly. */
1023 :
1024 : int
1025 : fd_banks_advance_root_prepare( fd_banks_t * banks,
1026 : ulong target_bank_idx,
1027 : ulong * advanceable_bank_idx_out );
1028 :
1029 : /* fd_banks_mark_bank_dead marks the current bank (and all of its
1030 : descendants) as dead. The caller is still responsible for handling
1031 : the behavior of the dead bank correctly. */
1032 :
1033 : void
1034 : fd_banks_mark_bank_dead( fd_banks_t * banks,
1035 : fd_bank_t * bank );
1036 :
1037 : /* fd_banks_mark_bank_frozen marks the current bank as frozen. This
1038 : should be done when the bank is no longer being updated: it should be
1039 : done at the end of a slot. This also releases the memory for the
1040 : cost tracker which only has to be persisted from the start of a slot
1041 : to the end. */
1042 :
1043 : void
1044 : fd_banks_mark_bank_frozen( fd_banks_t * banks,
1045 : fd_bank_t * bank );
1046 :
1047 : /* fd_banks_new_bank reserves a bank index for a new bank. New bank
1048 : indicies should always be available. After this function is called,
1049 : the bank will be linked to its parent bank, but not yet replayable.
1050 : After a call to fd_banks_clone_from_parent, the bank will be
1051 : replayable. This assumes that there is a parent bank which exists
1052 : and the there are available bank indices in the bank pool. */
1053 :
1054 : fd_bank_t *
1055 : fd_banks_new_bank( fd_banks_t * banks,
1056 : ulong parent_bank_idx,
1057 : long now );
1058 :
1059 :
1060 : /* fd_banks_is_full returns 1 if the banks are full, 0 otherwise. */
1061 :
1062 : static inline int
1063 0 : fd_banks_is_full( fd_banks_t * banks ) {
1064 0 : return fd_banks_pool_free( fd_banks_get_bank_pool( banks ) )==0UL;
1065 0 : }
1066 :
1067 : FD_PROTOTYPES_END
1068 :
1069 : #endif /* HEADER_fd_src_flamenco_runtime_fd_bank_h */
|