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