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