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