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