Line data Source code
1 : #ifndef HEADER_fd_src_discof_replay_fd_replay_tile_private_h
2 : #define HEADER_fd_src_discof_replay_fd_replay_tile_private_h
3 :
4 : #include "fd_vote_tracker.h"
5 : #include "../../disco/fd_clock_tile.h"
6 : #include "../../disco/topo/fd_wksp_mon.h"
7 : #include "../../disco/store/fd_store.h"
8 : #include "../../disco/bundle/fd_bundle_crank.h"
9 : #include "../../disco/keyguard/fd_keyswitch.h"
10 : #include "../../disco/node_info/fd_node_info.h"
11 : #include "../../discof/poh/fd_poh.h"
12 : #include "../../discof/reasm/fd_reasm.h"
13 : #include "../../discof/repair/fd_repair_tile.h"
14 : #include "../../discof/replay/fd_sched.h"
15 : #include "../../discof/votor/fd_votor_tile.h"
16 : #include "../../flamenco/capture/fd_capture_ctx.h"
17 : #include "../../flamenco/genesis/fd_genesis_parse.h"
18 : #include "../../flamenco/leaders/fd_multi_epoch_leaders.h"
19 : #include "../../flamenco/progcache/fd_progcache.h"
20 : #include "../../flamenco/runtime/fd_bank.h"
21 : #include "../../flamenco/runtime/fd_txncache.h"
22 : #include "../../flamenco/runtime/tests/fd_dump_pb.h"
23 : #include "../../disco/events/generated/fd_event_gen.h"
24 : #include "../../util/fd_hash32.h"
25 : #include "../../ballet/bmtree/fd_bmtree.h"
26 : #include <stdio.h>
27 :
28 : struct fd_replay_in_link {
29 : fd_wksp_t * mem;
30 : ulong chunk0;
31 : ulong wmark;
32 : ulong mtu;
33 : };
34 :
35 : typedef struct fd_replay_in_link fd_replay_in_link_t;
36 :
37 : struct fd_replay_out_link {
38 : ulong idx;
39 : fd_wksp_t * mem;
40 : ulong chunk0;
41 : ulong wmark;
42 : ulong chunk;
43 : };
44 :
45 : typedef struct fd_replay_out_link fd_replay_out_link_t;
46 :
47 : /* fd_block_id_map is a simple map of block-ids to bank indices. The
48 : map sits on top of an array of fd_block_id_ele_t. This serves as a
49 : translation layer between block ids to bank indices. The data
50 : array is indexed by bank index and the latest observed merkle root
51 : for the bank index is stored in the array. Once the block id has
52 : been observed, the entry is keyed by the latest merkle root (aka the
53 : block id). */
54 :
55 : struct fd_block_id_ele {
56 : fd_hash_t latest_mr; /* merkle root of the latest observed FEC set. Under tower this is
57 : the block id once the block is complete. Under alpenglow this is
58 : NEVER the block id: on completion it holds the last FEC set's
59 : merkle root, which the next block's shreds chain off (the cmr). */
60 : fd_hash_t dmr; /* ALPENGLOW-ONLY block id (double merkle root). Only valid once
61 : block_id_seen is set. */
62 : ag_block_id_t block_info; /* unpopulated for non alpenglow, but key for the compound map under alpenglow */
63 : uint latest_fec_idx;
64 : int block_id_seen;
65 : ulong slot;
66 : ulong bank_seq;
67 : ulong next_;
68 : ulong ag_next_;
69 : uint fec_cnt;
70 : };
71 : typedef struct fd_block_id_ele fd_block_id_ele_t;
72 :
73 : struct fd_reception_stats {
74 : ulong slot;
75 : uint fec_set_idx;
76 : fd_fec_complete_metrics_t metrics;
77 : };
78 : typedef struct fd_reception_stats fd_reception_stats_t;
79 :
80 0 : #define FD_REPLAY_TXN_TIMING_SLOTS (16UL)
81 :
82 : struct fd_replay_txn_timing {
83 : long received_ns;
84 :
85 : long parsed_ticks;
86 : long sigverify_disp_ticks;
87 : long sigverify_done_ticks;
88 : long exec_disp_ticks;
89 : long exec_done_ticks;
90 : };
91 :
92 : typedef struct fd_replay_txn_timing fd_replay_txn_timing_t;
93 :
94 : struct fd_replay_txn_timing_slot {
95 : struct {
96 : ulong next;
97 : } pool;
98 :
99 : ulong cnt;
100 : };
101 :
102 : typedef struct fd_replay_txn_timing_slot fd_replay_txn_timing_slot_t;
103 :
104 : #define POOL_NAME fd_timing_slot_pool
105 0 : #define POOL_T fd_replay_txn_timing_slot_t
106 0 : #define POOL_NEXT pool.next
107 : #include "../../util/tmpl/fd_pool.c"
108 :
109 : #define MAP_NAME fd_block_id_map
110 : #define MAP_ELE_T fd_block_id_ele_t
111 : #define MAP_KEY_T fd_hash_t
112 0 : #define MAP_KEY latest_mr
113 0 : #define MAP_NEXT next_
114 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0),(k1), sizeof(fd_hash_t)))
115 0 : #define MAP_KEY_HASH(key,seed) (fd_hash32( (key)->uc, (seed) ))
116 : #include "../../util/tmpl/fd_map_chain.c"
117 :
118 : /* fd_ag_block_id_map indexes the same fd_block_id_ele_t array by the
119 : compound {slot, block_id} key. A block that is still being received
120 : through turbing is keyed by {slot, 0}: the DMR is only known once the
121 : slot-complete FEC arrives. FECs of a block thus resolve their bank
122 : via {slot, 0} without any per-FEC re-keying; on slot complete the
123 : entry is re-keyed once to the real {slot, block_id}.
124 :
125 : For blocks that were repaired due to a votor block id event, the
126 : block id is known at the slot start. So the entry is keyed by {slot,
127 : block_id} from the start and never rekeyed. */
128 :
129 : #define MAP_NAME fd_ag_block_id_map
130 : #define MAP_ELE_T fd_block_id_ele_t
131 : #define MAP_KEY_T ag_block_id_t
132 0 : #define MAP_KEY block_info
133 0 : #define MAP_NEXT ag_next_
134 0 : #define MAP_KEY_EQ(k0,k1) (ag_block_id_eq( (k0), (k1) ))
135 0 : #define MAP_KEY_HASH(key,seed) (fd_hash( (seed), (key), sizeof(ag_block_id_t) ))
136 : #include "../../util/tmpl/fd_map_chain.c"
137 :
138 : FD_STATIC_ASSERT( FD_EVENT_BLOCK_COMPLETED_TXN_TIMING_MAX>=FD_MAX_TXN_PER_SLOT, txn_timing_ships_full_block );
139 :
140 : struct fd_replay_tile {
141 : fd_wksp_t * wksp;
142 :
143 : uint rng_seed;
144 : fd_rng_t rng[ 1 ];
145 :
146 : fd_clock_tile_t clock[1];
147 :
148 : fd_progcache_join_t progcache[1];
149 :
150 : fd_accdb_t * accdb;
151 : fd_txncache_t * txncache;
152 : fd_store_t * store;
153 : fd_store_map_t map_join[1];
154 : int store_disk_fd;
155 : fd_banks_t * banks;
156 :
157 : /* This flag is 1 If we have seen a vote signature that our node has
158 : sent out get rooted at least one time. The value is 0 otherwise.
159 : We can't become leader and pack blocks until this flag has been
160 : set. This parallels the Agave 'has_new_vote_been_rooted'. */
161 : int identity_vote_rooted;
162 : int wait_for_vote_to_start_leader;
163 : int alpenglow;
164 :
165 : /* wfs_enabled is 1 if the validator is booted in
166 : wait_for_supermajority mode. In this mode replay (and, by extension,
167 : downstream consumers) is not allowed to make progress until 80% of
168 : the cluster has published their ContactInfo in Gossip with a
169 : shred version matching expected_shred_version. When this happens,
170 : wfs_complete will be set to 1. */
171 : int wfs_enabled;
172 : int wfs_complete;
173 :
174 : fd_hash_t expected_bank_hash;
175 :
176 : ulong blockhash_seed;
177 : ulong reasm_seed;
178 : fd_reasm_t * reasm;
179 : fd_reasm_fec_t * reasm_evicted; /* evicted FEC by reasm_insert must be stored in returnable_frag, and then drained in after_credit */
180 : fd_reception_stats_t * reception_stats;
181 : ulong reception_stats_cnt;
182 :
183 : /* When a bank is evicted, the replayable chain of FECs delivered from rotor is broken. i.e.
184 : we lose context for a parent we needed to replay off of. When this happens,
185 : on the first FEC we see we can't replay, we need to drain everything in the dcache
186 : until there's something we can replay off of. Set this flag to enter that state (i.e. ignore FECs
187 : until there's one we can replay. )*/
188 : int drain_rotor_fecs;
189 :
190 : fd_sched_t * sched;
191 : ulong in_cnt;
192 : ulong execrp_idle_cnt;
193 :
194 : ulong vote_tracker_seed;
195 : fd_vote_tracker_t * vote_tracker;
196 :
197 : uint has_genesis_hash:1;
198 : uint has_cluster_type:1;
199 : uint has_genesis_timestamp:1;
200 : uint has_expected_genesis_timestamp:1;
201 :
202 : char genesis_path[ PATH_MAX ];
203 : fd_hash_t genesis_hash[1];
204 : fd_genesis_t genesis[1];
205 : ulong cluster_type;
206 : ulong genesis_timestamp;
207 : ulong expected_genesis_timestamp;
208 :
209 : ulong hard_fork_cnt;
210 : fd_hard_fork_t hard_forks[ FD_HARD_FORKS_MAX ];
211 :
212 : ushort expected_shred_version; /* from config, 0 if unset */
213 : ushort ipecho_shred_version; /* from the entrypoints via ipecho, 0 until it answers */
214 : ushort shred_version; /* 0 until computed. the two above only cross-check
215 : it, and replay holds off executing until it is known. */
216 :
217 : ulong enable_features_cnt;
218 : char enable_features[ 16 ][ FD_BASE58_ENCODED_32_SZ ];
219 :
220 : /* A note on publishing ...
221 :
222 : The watermarks are used to publish our fork-aware structures. For
223 : example, store, banks, and txncache need to be published to release
224 : resources occupied by rooted or dead blocks. In general,
225 : publishing has the effect of pruning forks in those structures,
226 : indicating that it is ok to release the memory being occupied by
227 : the blocks on said forks. Tower is responsible for informing us of
228 : the latest block on the consensus rooted fork. As soon as we can,
229 : we should move the published root as close as possible to the
230 : latest consensus root, publishing/pruning everything on the fork
231 : tree along the way. That is, all the blocks that directly descend
232 : from the current published root (inclusive) to the new published
233 : root (exclusive) on the rooted fork, as well as all the minority
234 : forks that branch from said blocks.
235 :
236 : Ideally, we'd move the published root to the consensus root
237 : immediately upon receiving a new consensus root. However, that's
238 : not always safe to do. One thing we need to be careful about is
239 : making sure that there are no more users/consumers of
240 : soon-to-be-pruned blocks, lest a use-after-free occurs. This can
241 : be done by using a reference counter for each block. Any
242 : concurrent activity, such as transaction execution in the exec
243 : tiles, should retain a refcnt on the block for as
244 : long as it needs access to the shared fork-aware structures related
245 : to that block. Eventually, refcnt on a given block will drop down
246 : to 0 as the block either finishes replaying or gets marked as dead,
247 : and any other tile that has retained a refcnt on the block releases
248 : it. At that point, it becomes a candidate for pruning. The key to
249 : safe publishing then becomes figuring out how far we could advance
250 : the published root, such that every minority fork branching off of
251 : blocks in between the current published root (inclusive) and the
252 : new published root (exclusive) is safe to be pruned. This is a
253 : straightforward tree traversal, where if a block B on the rooted
254 : fork has refcnt 0, and all minority forks branching off of B also
255 : have refcnt 0, then B is safe to be pruned. We advance the
256 : published root to the farthest consecutively prunable block on the
257 : rooted fork. Note that reasm presents the replay tile with a clean
258 : view of the world where every block is chained off of a parent
259 : block. So there are no orpahned/dangling tree nodes to worry
260 : about. The world is a nice single tree as far as replay is
261 : concerned.
262 :
263 : In the following fork tree, every node is a block and the number in
264 : parentheses is the refcnt on the block. The chain marked with
265 : double slashes is the rooted fork. Suppose the published root is
266 : at block P, and consensus root is at block T. We can't publish
267 : past block P because Q has refcnt 1.
268 :
269 :
270 : P(0)
271 : / \\
272 : Q(1) A(0)
273 : / || \
274 : X(0) B(0) C(0)
275 : / || \
276 : Y(0) M(0) R(0)
277 : / || / \
278 : D(2) T(0) J(0) L(0)
279 : ||
280 : ..
281 : ..
282 : ..
283 : ||
284 : blocks we might be actively replaying
285 :
286 :
287 : When refcnt on Q drops to 0, we would be able to advance the
288 : published root to block M, because blocks P, A, and B, as well as
289 : all subtrees branching off of them, have refcnt 0, and therefore
290 : can be pruned. Block M itself cannot be pruned yet because its
291 : child block D has refcnt 2. After publishing/pruning, the fork
292 : tree would be:
293 :
294 :
295 : M(0)
296 : / ||
297 : D(2) T(0)
298 : ||
299 : ..
300 : ..
301 : ..
302 : ||
303 : blocks we might be actively replaying
304 :
305 :
306 : As a result, the shared fork-aware structures can free resources
307 : for blocks P, A, B, and all subtrees branching off of them.
308 :
309 : For the reference counting part, the replay tile is the sole entity
310 : that can update the refcnt. This ensures that all refcnt increment
311 : and decrement attempts are serialized at the replay tile, and that
312 : there are no racy resurrection of a soon-to-be-pruned block. If a
313 : refcnt increment request arrives after a block has been pruned,
314 : replay simply rejects the request.
315 :
316 : A note on the implementation of the above ...
317 :
318 : Upon receiving a new consensus root, we descend down the rooted
319 : fork from the current published root to the new consensus root. On
320 : each node/block of the rooted fork, we do a summation of the refcnt
321 : on the block and all the minority fork blocks branching from the
322 : block. If the summation is 0, the block is safe for pruning. We
323 : advance the published root to the far end of the consecutive run of
324 : 0 refcnt sums originating from the current published root. On our
325 : descent down the minority forks, we also mark any block that hasn't
326 : finished replaying as dead, so we don't waste time executing them.
327 : No more transactions shall be dispatched for execution from dead
328 : blocks.
329 :
330 : Blocks start out with a refcnt of 0. Other tiles may send a
331 : request to the replay tile for a reference on a block. The
332 : transaction dispatcher is another source of refcnt updates. On
333 : every dispatch of a transaction for block B, we increment the
334 : refcnt for B. And on every transaction finalization, we decrement
335 : the refcnt for B. This means that whenever the refcnt on a block
336 : is 0, there is no more reference on that block from the execution
337 : pipeline. While it might be tempting to simply increment the
338 : refcnt once when we start replaying a block, and decrement the
339 : refcnt once when we finish a block, this more fine-grained refcnt
340 : update strategy allows for aborting and potentially immediate
341 : pruning of blocks under interleaved block replay. Upon receiving a
342 : new consensus root, we can simply look at the refcnt on minority
343 : fork blocks, and a refcnt of 0 would imply that the block is safe
344 : for pruning, even if we haven't finished replaying it. Without the
345 : fine-grained refcnt, we would need to first stop dispatching from
346 : the aborted block, and then wait for a full drain of the execution
347 : pipeline to know for sure that there are no more in-flight
348 : transactions executing on the aborted block. Note that this will
349 : allow the refcnt on any block to transiently drop down to 0. We
350 : will not mistakenly prune an actively replaying block, aka a leaf
351 : node, that is chaining off of the rooted fork, because the
352 : consensus root is always an ancestor of the actively replaying tip.
353 : */
354 : fd_hash_t consensus_root; /* The most recent block to have reached max lockout in the tower. */
355 : ulong consensus_root_slot; /* slot number of the above. */
356 : fd_hash_t notified_root; /* The most recent consensus root sent to sched, RPC, and resolv. */
357 : ulong notified_root_slot; /* slot number of the above. */
358 : fd_bank_t * notified_root_bank; /* bank held by sched, RPC, and resolv for the notified root. */
359 : ulong published_root_slot; /* slot number of the published root. */
360 : ulong published_root_bank_idx; /* bank index of the published root. */
361 :
362 : /* Randomly generated block id for the initial genesis/snapshot slot.
363 : Used as a fallback when the snapshot manifest does not contain a
364 : block_id (block_id will be populated in Agave 4.1 snapshots and
365 : will be required in Agave 4.2). */
366 :
367 : fd_hash_t initial_block_id;
368 :
369 : /* The merkle root of the snapshot slot's block.
370 : Only present in snapshots generated by Agave >=4.1. */
371 :
372 : int has_manifest_block_id;
373 : fd_hash_t manifest_block_id;
374 :
375 : /* We need to maintain a tile-local mapping of block-ids to bank index
376 : and vice versa. This translation layer is needed for conversion
377 : since tower operates on block-ids and downstream consumers of FEC
378 : sets operate on bank indices. This mapping must happen both ways:
379 : 1. tower sends us block ids and we must map them to bank indices.
380 : 2. when a block is completed, we must map the bank index to a block
381 : id to send a slot complete message to tower. */
382 : ulong block_id_len;
383 : ulong max_live_slots;
384 : fd_block_id_ele_t * block_id_arr;
385 :
386 : fd_hash_t * fec_chain;
387 : ulong block_id_map_seed;
388 : fd_block_id_map_t * block_id_map;
389 :
390 : ulong ag_block_id_map_seed;
391 : fd_ag_block_id_map_t * ag_block_id_map;
392 :
393 : /* Capture-related configs */
394 : fd_capture_ctx_t * capture_ctx;
395 : FILE * capture_file;
396 : fd_capture_link_buf_t cap_repl_out[1];
397 :
398 : /* Protobuf dumping context for debugging runtime execution and
399 : collecting seed corpora. */
400 : fd_dump_proto_ctx_t * dump_proto_ctx;
401 :
402 : /* Per-txn lifecycle timing capture. The scheduler's txn_info_pool
403 : entries are recycled the moment a txn completes, so the ticks are
404 : copied out at completion time into a leased capture slot, indexed by
405 : the txn's position in the block. A full-depth buffer per live bank
406 : would be ~9.6 GiB, but only a handful of blocks replay concurrently,
407 : so a small pool of full-depth slots is leased to banks as they start
408 : replaying. A block that cannot get a slot (more than
409 : FD_REPLAY_TXN_TIMING_SLOTS blocks replaying at once) captures
410 : nothing. Slot depth is max_txn_per_slot, so the records sit in a
411 : side array sized at footprint time. */
412 : fd_replay_txn_timing_slot_t * timing_slot_pool; /* fd_pool, FD_REPLAY_TXN_TIMING_SLOTS elements */
413 : fd_replay_txn_timing_t * timing_rec; /* [FD_REPLAY_TXN_TIMING_SLOTS*max_txn_per_slot], slot i at i*max_txn_per_slot */
414 : ulong * timing_slot_of_bank; /* [max_live_slots] bank_idx -> pool idx or idx_null */
415 :
416 : fd_reasm_fec_t ** backfill_path; /* [max_shreds_per_block/FD_FEC_SHRED_CNT] scratch for backfill_fec_sets */
417 :
418 : /* Whether the runtime has been booted either from snapshot loading
419 : or from genesis. */
420 : int is_booted;
421 :
422 : /* Buffer to store vote towers that need to be published to the Tower
423 : tile. */
424 :
425 : fd_multi_epoch_leaders_t * mleaders;
426 :
427 : ulong max_txn_per_slot;
428 : ulong max_shreds_per_block;
429 :
430 : /* When we transition to becoming leader, we can only unbecome leader
431 : if we have received a block id from the FEC reassembler, and a
432 : message from PoH that the leader slot has ended. After both of
433 : these conditions are met, then we are free to unbecome leader.
434 : Exception: a slot aborted by a reset unbecomes leader on the PoH
435 : slot-ended message alone; no block id will ever arrive for it. */
436 : uint is_leader : 1;
437 : uint supports_leader : 1;
438 : int recv_poh;
439 :
440 : ulong leader_execution_fees; /* ALPENGLOW-ONLY */
441 : ulong leader_priority_fees; /* ALPENGLOW-ONLY */
442 : ulong leader_tips; /* ALPENGLOW-ONLY */
443 : fd_block_footer_t leader_footer[ 1 ];
444 :
445 : fd_votor_certed_t votor_final[ 1 ]; /* ALPENGLOW-ONLY: highest finalization, fast over slow at the same slot */
446 : fd_votor_leader_t votor_leader[ 1 ]; /* ALPENGLOW-ONLY: ParentReady trigger behind next_leader_slot */
447 : fd_votor_reward_t votor_reward[ FD_NUM_SLOTS_FOR_REWARD+AG_SLOTS_PER_WINDOW+1UL ];
448 :
449 : ulong next_leader_slot;
450 : long next_leader_tickcount;
451 : double tick_per_ns;
452 : ulong highwater_leader_slot;
453 : ulong reset_slot;
454 :
455 : /* Caught up to the cluster: replay has completed a slot within a few
456 : slots of the highest FEC set slot seen from repair (which tracks
457 : the turbine tip). */
458 : int caught_up;
459 : ulong catch_up_max_fec_slot;
460 : ulong catch_up_tip_advance_cnt;
461 : long boot_timestamp_nanos;
462 : fd_hash_t reset_cmr; /* chained merkle root of the reset block */
463 : fd_hash_t reset_dmr; /* ALPENGLOW-ONLY double merkle root of the reset block */
464 : long reset_timestamp_nanos;
465 : fd_bank_t * leader_bank;
466 :
467 : struct {
468 : ulong slot;
469 : long became_leader_nanos;
470 : long leader_slot_start_nanos;
471 : long first_fec_returned_nanos;
472 : ulong microblock_count;
473 : ulong pack_block_cost;
474 : ulong pack_vote_cost;
475 : ulong pack_data_bytes;
476 : ulong bundle_txn_count;
477 : int pack_end_reason;
478 : long pack_start_nanos;
479 : long pack_end_nanos;
480 : ulong timing_table_idx;
481 : } leader_stats;
482 :
483 : fd_pubkey_t identity_pubkey[1];
484 : ulong identity_idx;
485 : int identity_dirty;
486 :
487 : int has_vote_account;
488 : fd_pubkey_t vote_account[ 1 ];
489 :
490 : fd_node_info_box_t * node_info; /* shared */
491 :
492 : fd_keyswitch_t * keyswitch;
493 : int halt_leader;
494 :
495 : ulong resolv_tile_cnt;
496 :
497 : int in_kind[ 128 ];
498 : fd_replay_in_link_t in[ 128 ];
499 :
500 : fd_replay_out_link_t exec_out[ 1 ];
501 :
502 : fd_replay_out_link_t replay_out[1];
503 : fd_replay_out_link_t snapmk_out[1];
504 : ulong admin_out_idx;
505 :
506 : fd_replay_out_link_t epoch_out[1];
507 :
508 : /* The rpc tile needs to occasionally own a reference to a live bank.
509 : Replay needs to know if the rpc as a consumer is enabled so it can
510 : increment the bank's refcnt before publishing bank_idx. */
511 : int rpc_enabled;
512 :
513 : /* For dumping blocks to protobuf. For backtest only. */
514 : fd_block_dump_ctx_t * block_dump_ctx;
515 :
516 : /* We need a few pieces of information to compute the right addresses
517 : for bundle crank information that we need to send to pack. */
518 : struct {
519 : int enabled;
520 : fd_pubkey_t vote_account;
521 : fd_bundle_crank_gen_t gen[1];
522 : } bundle;
523 :
524 : /* snapshot producer */
525 : struct {
526 : uint supported:1;
527 : uint active:1;
528 : uint incremental:1;
529 : ulong bank_idx;
530 : ulong scheduled_at_slot;
531 : ulong full_interval_blocks;
532 : ulong next_full_block_height;
533 : ulong incremental_interval_blocks;
534 : ulong next_incremental_block_height;
535 : ulong base_slot;
536 : } snapmk;
537 :
538 : struct {
539 : fd_histf_t store_query_work[1];
540 : ulong store_query_cnt;
541 : ulong store_query_missing_cnt;
542 : ulong store_query_mr;
543 : ulong store_query_missing_mr;
544 :
545 : ulong slots_total;
546 : ulong transactions_total;
547 :
548 : ulong reasm_latest_slot;
549 : ulong reasm_latest_fec_idx;
550 :
551 : ulong sched_full;
552 : ulong reasm_empty;
553 : ulong leader_bid_wait;
554 : ulong banks_full;
555 : ulong storage_root_behind;
556 :
557 : ulong voted_slot; /* monotone, ULONG_MAX if none */
558 : } metrics;
559 :
560 : uchar __attribute__((aligned(FD_MULTI_EPOCH_LEADERS_ALIGN))) mleaders_mem[ FD_MULTI_EPOCH_LEADERS_FOOTPRINT ];
561 :
562 : ulong runtime_stack_seed;
563 : fd_runtime_stack_t * runtime_stack;
564 :
565 : fd_event_block_completed_t * block_completed_event;
566 :
567 : fd_leader_txn_timing_table_t const * leader_txn_timing;
568 :
569 : /* If non-zero, emit the runtime events during replay. */
570 : int report_runtime_diffs;
571 : };
572 :
573 : typedef struct fd_replay_tile fd_replay_tile_t;
574 :
575 : #endif /* HEADER_fd_src_discof_replay_fd_replay_tile_private_h */
|