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/topo/fd_wksp_mon.h"
6 : #include "../../disco/store/fd_store.h"
7 : #include "../../disco/bundle/fd_bundle_crank.h"
8 : #include "../../disco/keyguard/fd_keyswitch.h"
9 : #include "../../disco/node_info/fd_node_info.h"
10 : #include "../../discof/reasm/fd_reasm.h"
11 : #include "../../discof/replay/fd_sched.h"
12 : #include "../../flamenco/capture/fd_capture_ctx.h"
13 : #include "../../flamenco/genesis/fd_genesis_parse.h"
14 : #include "../../flamenco/leaders/fd_multi_epoch_leaders.h"
15 : #include "../../flamenco/progcache/fd_progcache.h"
16 : #include "../../flamenco/runtime/fd_bank.h"
17 : #include "../../flamenco/runtime/fd_txncache.h"
18 : #include "../../flamenco/runtime/tests/fd_dump_pb.h"
19 : #include <stdio.h>
20 :
21 : struct fd_replay_in_link {
22 : fd_wksp_t * mem;
23 : ulong chunk0;
24 : ulong wmark;
25 : ulong mtu;
26 : };
27 :
28 : typedef struct fd_replay_in_link fd_replay_in_link_t;
29 :
30 : struct fd_replay_out_link {
31 : ulong idx;
32 : fd_wksp_t * mem;
33 : ulong chunk0;
34 : ulong wmark;
35 : ulong chunk;
36 : };
37 :
38 : typedef struct fd_replay_out_link fd_replay_out_link_t;
39 :
40 : /* fd_block_id_map is a simple map of block-ids to bank indices. The
41 : map sits on top of an array of fd_block_id_ele_t. This serves as a
42 : translation layer between block ids to bank indices. The data
43 : array is indexed by bank index and the latest observed merkle root
44 : for the bank index is stored in the array. Once the block id has
45 : been observed, the entry is keyed by the latest merkle root (aka the
46 : block id). */
47 :
48 : struct fd_block_id_ele {
49 : fd_hash_t latest_mr;
50 : uint latest_fec_idx;
51 : int block_id_seen;
52 : ulong slot;
53 : ulong bank_seq;
54 : ulong next_;
55 : };
56 : typedef struct fd_block_id_ele fd_block_id_ele_t;
57 :
58 : #define MAP_NAME fd_block_id_map
59 : #define MAP_ELE_T fd_block_id_ele_t
60 : #define MAP_KEY_T fd_hash_t
61 0 : #define MAP_KEY latest_mr
62 0 : #define MAP_NEXT next_
63 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0),(k1), sizeof(fd_hash_t)))
64 0 : #define MAP_KEY_HASH(key,seed) (fd_hash((seed),(key),sizeof(fd_hash_t)))
65 : #include "../../util/tmpl/fd_map_chain.c"
66 :
67 : struct fd_replay_tile {
68 : fd_wksp_t * wksp;
69 :
70 : uint rng_seed;
71 : fd_rng_t rng[ 1 ];
72 :
73 : fd_progcache_join_t progcache[1];
74 : fd_wksp_mon_t progcache_wksp_mon[1];
75 :
76 : fd_accdb_t * accdb;
77 : fd_txncache_t * txncache;
78 : fd_store_t * store;
79 : fd_banks_t * banks;
80 :
81 : /* This flag is 1 If we have seen a vote signature that our node has
82 : sent out get rooted at least one time. The value is 0 otherwise.
83 : We can't become leader and pack blocks until this flag has been
84 : set. This parallels the Agave 'has_new_vote_been_rooted'. */
85 : int identity_vote_rooted;
86 : int wait_for_vote_to_start_leader;
87 :
88 : /* wfs_enabled is 1 if the validator is booted in
89 : wait_for_supermajority mode. In this mode replay (and, by extension,
90 : downstream consumers) is not allowed to make progress until 80% of
91 : the cluster has published their ContactInfo in Gossip with a
92 : shred version matching expected_shred_version. When this happens,
93 : wfs_complete will be set to 1. */
94 : int wfs_enabled;
95 : int wfs_complete;
96 :
97 : fd_hash_t expected_bank_hash;
98 :
99 : ulong blockhash_seed;
100 : ulong reasm_seed;
101 : fd_reasm_t * reasm;
102 : fd_reasm_fec_t * reasm_evicted; /* evicted FEC by reasm_insert must be stored in returnable_frag, and then drained in after_credit */
103 :
104 : fd_sched_t * sched;
105 : ulong in_cnt;
106 : ulong execrp_idle_cnt;
107 :
108 : ulong vote_tracker_seed;
109 : fd_vote_tracker_t * vote_tracker;
110 :
111 : uint has_genesis_hash:1;
112 : uint has_cluster_type:1;
113 : uint has_genesis_timestamp:1;
114 : uint has_expected_genesis_timestamp:1;
115 :
116 : char genesis_path[ PATH_MAX ];
117 : fd_hash_t genesis_hash[1];
118 : fd_genesis_t genesis[1];
119 : ulong cluster_type;
120 : ulong genesis_timestamp;
121 : ulong expected_genesis_timestamp;
122 :
123 : ulong hard_fork_cnt;
124 : fd_hard_fork_t hard_forks[ FD_HARD_FORKS_MAX ];
125 :
126 : ushort expected_shred_version;
127 : ushort ipecho_shred_version;
128 :
129 : ulong enable_features_cnt;
130 : char enable_features[ 16 ][ FD_BASE58_ENCODED_32_SZ ];
131 :
132 : /* A note on publishing ...
133 :
134 : The watermarks are used to publish our fork-aware structures. For
135 : example, store, banks, and txncache need to be published to release
136 : resources occupied by rooted or dead blocks. In general,
137 : publishing has the effect of pruning forks in those structures,
138 : indicating that it is ok to release the memory being occupied by
139 : the blocks on said forks. Tower is responsible for informing us of
140 : the latest block on the consensus rooted fork. As soon as we can,
141 : we should move the published root as close as possible to the
142 : latest consensus root, publishing/pruning everything on the fork
143 : tree along the way. That is, all the blocks that directly descend
144 : from the current published root (inclusive) to the new published
145 : root (exclusive) on the rooted fork, as well as all the minority
146 : forks that branch from said blocks.
147 :
148 : Ideally, we'd move the published root to the consensus root
149 : immediately upon receiving a new consensus root. However, that's
150 : not always safe to do. One thing we need to be careful about is
151 : making sure that there are no more users/consumers of
152 : soon-to-be-pruned blocks, lest a use-after-free occurs. This can
153 : be done by using a reference counter for each block. Any
154 : concurrent activity, such as transaction execution in the exec
155 : tiles, should retain a refcnt on the block for as
156 : long as it needs access to the shared fork-aware structures related
157 : to that block. Eventually, refcnt on a given block will drop down
158 : to 0 as the block either finishes replaying or gets marked as dead,
159 : and any other tile that has retained a refcnt on the block releases
160 : it. At that point, it becomes a candidate for pruning. The key to
161 : safe publishing then becomes figuring out how far we could advance
162 : the published root, such that every minority fork branching off of
163 : blocks in between the current published root (inclusive) and the
164 : new published root (exclusive) is safe to be pruned. This is a
165 : straightforward tree traversal, where if a block B on the rooted
166 : fork has refcnt 0, and all minority forks branching off of B also
167 : have refcnt 0, then B is safe to be pruned. We advance the
168 : published root to the farthest consecutively prunable block on the
169 : rooted fork. Note that reasm presents the replay tile with a clean
170 : view of the world where every block is chained off of a parent
171 : block. So there are no orpahned/dangling tree nodes to worry
172 : about. The world is a nice single tree as far as replay is
173 : concerned.
174 :
175 : In the following fork tree, every node is a block and the number in
176 : parentheses is the refcnt on the block. The chain marked with
177 : double slashes is the rooted fork. Suppose the published root is
178 : at block P, and consensus root is at block T. We can't publish
179 : past block P because Q has refcnt 1.
180 :
181 :
182 : P(0)
183 : / \\
184 : Q(1) A(0)
185 : / || \
186 : X(0) B(0) C(0)
187 : / || \
188 : Y(0) M(0) R(0)
189 : / || / \
190 : D(2) T(0) J(0) L(0)
191 : ||
192 : ..
193 : ..
194 : ..
195 : ||
196 : blocks we might be actively replaying
197 :
198 :
199 : When refcnt on Q drops to 0, we would be able to advance the
200 : published root to block M, because blocks P, A, and B, as well as
201 : all subtrees branching off of them, have refcnt 0, and therefore
202 : can be pruned. Block M itself cannot be pruned yet because its
203 : child block D has refcnt 2. After publishing/pruning, the fork
204 : tree would be:
205 :
206 :
207 : M(0)
208 : / ||
209 : D(2) T(0)
210 : ||
211 : ..
212 : ..
213 : ..
214 : ||
215 : blocks we might be actively replaying
216 :
217 :
218 : As a result, the shared fork-aware structures can free resources
219 : for blocks P, A, B, and all subtrees branching off of them.
220 :
221 : For the reference counting part, the replay tile is the sole entity
222 : that can update the refcnt. This ensures that all refcnt increment
223 : and decrement attempts are serialized at the replay tile, and that
224 : there are no racy resurrection of a soon-to-be-pruned block. If a
225 : refcnt increment request arrives after a block has been pruned,
226 : replay simply rejects the request.
227 :
228 : A note on the implementation of the above ...
229 :
230 : Upon receiving a new consensus root, we descend down the rooted
231 : fork from the current published root to the new consensus root. On
232 : each node/block of the rooted fork, we do a summation of the refcnt
233 : on the block and all the minority fork blocks branching from the
234 : block. If the summation is 0, the block is safe for pruning. We
235 : advance the published root to the far end of the consecutive run of
236 : 0 refcnt sums originating from the current published root. On our
237 : descent down the minority forks, we also mark any block that hasn't
238 : finished replaying as dead, so we don't waste time executing them.
239 : No more transactions shall be dispatched for execution from dead
240 : blocks.
241 :
242 : Blocks start out with a refcnt of 0. Other tiles may send a
243 : request to the replay tile for a reference on a block. The
244 : transaction dispatcher is another source of refcnt updates. On
245 : every dispatch of a transaction for block B, we increment the
246 : refcnt for B. And on every transaction finalization, we decrement
247 : the refcnt for B. This means that whenever the refcnt on a block
248 : is 0, there is no more reference on that block from the execution
249 : pipeline. While it might be tempting to simply increment the
250 : refcnt once when we start replaying a block, and decrement the
251 : refcnt once when we finish a block, this more fine-grained refcnt
252 : update strategy allows for aborting and potentially immediate
253 : pruning of blocks under interleaved block replay. Upon receiving a
254 : new consensus root, we can simply look at the refcnt on minority
255 : fork blocks, and a refcnt of 0 would imply that the block is safe
256 : for pruning, even if we haven't finished replaying it. Without the
257 : fine-grained refcnt, we would need to first stop dispatching from
258 : the aborted block, and then wait for a full drain of the execution
259 : pipeline to know for sure that there are no more in-flight
260 : transactions executing on the aborted block. Note that this will
261 : allow the refcnt on any block to transiently drop down to 0. We
262 : will not mistakenly prune an actively replaying block, aka a leaf
263 : node, that is chaining off of the rooted fork, because the
264 : consensus root is always an ancestor of the actively replaying tip.
265 : */
266 : fd_hash_t consensus_root; /* The most recent block to have reached max lockout in the tower. */
267 : ulong consensus_root_slot; /* slot number of the above. */
268 : fd_hash_t notified_root; /* The most recent consensus root sent to sched, RPC, and resolv. */
269 : ulong notified_root_slot; /* slot number of the above. */
270 : fd_bank_t * notified_root_bank; /* bank held by sched, RPC, and resolv for the notified root. */
271 : ulong published_root_slot; /* slot number of the published root. */
272 : ulong published_root_bank_idx; /* bank index of the published root. */
273 :
274 : /* Randomly generated block id for the initial genesis/snapshot slot.
275 : Used as a fallback when the snapshot manifest does not contain a
276 : block_id (block_id will be populated in Agave 4.1 snapshots and
277 : will be required in Agave 4.2). */
278 :
279 : fd_hash_t initial_block_id;
280 :
281 : /* The merkle root of the snapshot slot's block.
282 : Only present in snapshots generated by Agave >=4.1. */
283 :
284 : int has_manifest_block_id;
285 : fd_hash_t manifest_block_id;
286 :
287 : /* We need to maintain a tile-local mapping of block-ids to bank index
288 : and vice versa. This translation layer is needed for conversion
289 : since tower operates on block-ids and downstream consumers of FEC
290 : sets operate on bank indices. This mapping must happen both ways:
291 : 1. tower sends us block ids and we must map them to bank indices.
292 : 2. when a block is completed, we must map the bank index to a block
293 : id to send a slot complete message to tower. */
294 : ulong block_id_len;
295 : fd_block_id_ele_t * block_id_arr;
296 : ulong block_id_map_seed;
297 : fd_block_id_map_t * block_id_map;
298 :
299 : /* Capture-related configs */
300 : fd_capture_ctx_t * capture_ctx;
301 : FILE * capture_file;
302 : fd_capture_link_buf_t cap_repl_out[1];
303 :
304 : /* Protobuf dumping context for debugging runtime execution and
305 : collecting seed corpora. */
306 : fd_dump_proto_ctx_t * dump_proto_ctx;
307 :
308 : /* Whether the runtime has been booted either from snapshot loading
309 : or from genesis. */
310 : int is_booted;
311 :
312 : /* Buffer to store vote towers that need to be published to the Tower
313 : tile. */
314 :
315 : fd_multi_epoch_leaders_t * mleaders;
316 :
317 : int larger_max_cost_per_block;
318 :
319 : /* When we transition to becoming leader, we can only unbecome leader
320 : if we have received a block id from the FEC reassembler, and a
321 : message from PoH that the leader slot has ended. After both of
322 : these conditions are met, then we are free to unbecome leader. */
323 : uint is_leader : 1;
324 : uint supports_leader : 1;
325 : int recv_poh;
326 : ulong next_leader_slot;
327 : long next_leader_tickcount;
328 : double tick_per_ns;
329 : ulong highwater_leader_slot;
330 : ulong reset_slot;
331 :
332 : /* Caught up to the cluster: replay has completed a slot within a few
333 : slots of the highest FEC set slot seen from repair (which tracks
334 : the turbine tip). */
335 : int caught_up;
336 : ulong catch_up_max_fec_slot;
337 : ulong catch_up_tip_advance_cnt;
338 : long boot_timestamp_nanos;
339 : fd_hash_t reset_block_id;
340 : long reset_timestamp_nanos;
341 : fd_bank_t * leader_bank;
342 :
343 : fd_pubkey_t identity_pubkey[1];
344 : ulong identity_idx;
345 : int identity_dirty;
346 :
347 : fd_node_info_box_t * node_info; /* shared */
348 :
349 : fd_keyswitch_t * keyswitch;
350 : int halt_leader;
351 :
352 : ulong resolv_tile_cnt;
353 :
354 : int in_kind[ 128 ];
355 : fd_replay_in_link_t in[ 128 ];
356 :
357 : fd_replay_out_link_t exec_out[ 1 ];
358 :
359 : fd_replay_out_link_t replay_out[1];
360 : fd_replay_out_link_t snapmk_out[1];
361 : ulong admin_out_idx;
362 :
363 : fd_replay_out_link_t epoch_out[1];
364 :
365 : /* The rpc tile needs to occasionally own a reference to a live bank.
366 : Replay needs to know if the rpc as a consumer is enabled so it can
367 : increment the bank's refcnt before publishing bank_idx. */
368 : int rpc_enabled;
369 :
370 : /* For dumping blocks to protobuf. For backtest only. */
371 : fd_block_dump_ctx_t * block_dump_ctx;
372 :
373 : /* We need a few pieces of information to compute the right addresses
374 : for bundle crank information that we need to send to pack. */
375 : struct {
376 : int enabled;
377 : fd_pubkey_t vote_account;
378 : fd_bundle_crank_gen_t gen[1];
379 : } bundle;
380 :
381 : /* snapshot producer */
382 : struct {
383 : uint supported:1;
384 : uint active:1;
385 : uint incremental:1;
386 : ulong bank_idx;
387 : ulong scheduled_at;
388 : ulong full_interval;
389 : ulong next_full_slot;
390 : ulong incremental_interval;
391 : ulong next_incremental_slot;
392 : ulong base_slot;
393 : } snapmk;
394 :
395 : struct {
396 : ulong store_query_acquire;
397 : ulong store_query_release;
398 : fd_histf_t store_query_wait[1];
399 : fd_histf_t store_query_work[1];
400 : ulong store_query_cnt;
401 : ulong store_query_missing_cnt;
402 : ulong store_query_mr;
403 : ulong store_query_missing_mr;
404 :
405 : ulong slots_total;
406 : ulong transactions_total;
407 :
408 : ulong reasm_latest_slot;
409 : ulong reasm_latest_fec_idx;
410 :
411 : ulong sched_full;
412 : ulong reasm_empty;
413 : ulong leader_bid_wait;
414 : ulong banks_full;
415 : ulong storage_root_behind;
416 : } metrics;
417 :
418 : uchar __attribute__((aligned(FD_MULTI_EPOCH_LEADERS_ALIGN))) mleaders_mem[ FD_MULTI_EPOCH_LEADERS_FOOTPRINT ];
419 :
420 : ulong runtime_stack_seed;
421 : fd_runtime_stack_t * runtime_stack;
422 : };
423 :
424 : typedef struct fd_replay_tile fd_replay_tile_t;
425 :
426 : #endif /* HEADER_fd_src_discof_replay_fd_replay_tile_private_h */
|