Line data Source code
1 : #ifndef HEADER_fd_src_disco_gui_fd_gui_h
2 : #define HEADER_fd_src_disco_gui_fd_gui_h
3 :
4 : #include "fd_gui_peers.h"
5 :
6 : #include "../topo/fd_topo.h"
7 :
8 : #include "../../ballet/txn/fd_txn.h"
9 : #include "../../disco/tiles.h"
10 : #include "../../disco/fd_txn_p.h"
11 : #include "../../disco/bundle/fd_bundle_tile.h"
12 : #include "../../discof/restore/fd_snapct_tile.h"
13 : #include "../../discof/restore/utils/fd_ssmsg.h"
14 : #include "../../discof/tower/fd_tower_tile.h"
15 : #include "../../discof/replay/fd_replay_tile.h"
16 : #include "../../choreo/tower/fd_tower.h"
17 : #include "../../choreo/tower/fd_tower_serdes.h"
18 : #include "../../flamenco/leaders/fd_leaders.h"
19 : #include "../../flamenco/types/fd_types_custom.h"
20 : #include "../../util/fd_util_base.h"
21 : #include "../../util/hist/fd_histf.h"
22 : #include "../../waltz/http/fd_http_server.h"
23 :
24 : /* frankendancer only */
25 : #define FD_GUI_MAX_PEER_CNT (108000UL)
26 :
27 : /* frankendancer only */
28 0 : #define FD_GUI_START_PROGRESS_TYPE_INITIALIZING ( 0)
29 0 : #define FD_GUI_START_PROGRESS_TYPE_SEARCHING_FOR_FULL_SNAPSHOT ( 1)
30 0 : #define FD_GUI_START_PROGRESS_TYPE_DOWNLOADING_FULL_SNAPSHOT ( 2)
31 0 : #define FD_GUI_START_PROGRESS_TYPE_SEARCHING_FOR_INCREMENTAL_SNAPSHOT ( 3)
32 0 : #define FD_GUI_START_PROGRESS_TYPE_DOWNLOADING_INCREMENTAL_SNAPSHOT ( 4)
33 0 : #define FD_GUI_START_PROGRESS_TYPE_CLEANING_BLOCK_STORE ( 5)
34 0 : #define FD_GUI_START_PROGRESS_TYPE_CLEANING_ACCOUNTS ( 6)
35 0 : #define FD_GUI_START_PROGRESS_TYPE_LOADING_LEDGER ( 7)
36 0 : #define FD_GUI_START_PROGRESS_TYPE_PROCESSING_LEDGER ( 8)
37 0 : #define FD_GUI_START_PROGRESS_TYPE_STARTING_SERVICES ( 9)
38 0 : #define FD_GUI_START_PROGRESS_TYPE_HALTED (10)
39 0 : #define FD_GUI_START_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY (11)
40 0 : #define FD_GUI_START_PROGRESS_TYPE_RUNNING (12)
41 :
42 : /* frankendancer only */
43 : struct fd_gui_gossip_peer {
44 : fd_pubkey_t pubkey[ 1 ];
45 : ulong wallclock;
46 : ushort shred_version;
47 :
48 : int has_version;
49 : struct {
50 : ushort major;
51 : ushort minor;
52 : ushort patch;
53 :
54 : int has_commit;
55 : uint commit;
56 :
57 : uint feature_set;
58 : } version;
59 :
60 : struct {
61 : uint ipv4;
62 : ushort port;
63 : } sockets[ 12 ];
64 : };
65 :
66 : /* frankendancer only */
67 : struct fd_gui_vote_account {
68 : fd_pubkey_t pubkey[ 1 ];
69 : fd_pubkey_t vote_account[ 1 ];
70 :
71 : ulong activated_stake;
72 : ulong last_vote;
73 : ulong root_slot;
74 : ulong epoch_credits;
75 : uchar commission;
76 : int delinquent;
77 : };
78 :
79 : /* frankendancer only */
80 : struct fd_gui_validator_info {
81 : fd_pubkey_t pubkey[ 1 ];
82 :
83 : char name[ 64 ];
84 : char website[ 128 ];
85 : char details[ 256 ];
86 : char icon_uri[ 128 ];
87 : };
88 :
89 : /* frankendancer only */
90 : #define FD_GUI_SLOT_LEADER_UNSTARTED (0UL)
91 : #define FD_GUI_SLOT_LEADER_STARTED (1UL)
92 : #define FD_GUI_SLOT_LEADER_ENDED (2UL)
93 :
94 0 : #define FD_GUI_SLOTS_CNT (864000UL) /* 2x 432000 */
95 0 : #define FD_GUI_LEADER_CNT (4096UL)
96 :
97 0 : #define FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS (10L)
98 0 : #define FD_GUI_TPS_HISTORY_SAMPLE_CNT (150UL)
99 :
100 0 : #define FD_GUI_TILE_TIMER_SNAP_CNT (512UL)
101 0 : #define FD_GUI_TILE_TIMER_LEADER_DOWNSAMPLE_CNT (50UL) /* 500ms / 10ms */
102 0 : #define FD_GUI_SCHEDULER_COUNT_SNAP_CNT (512UL)
103 : #define FD_GUI_SCHEDULER_COUNT_LEADER_DOWNSAMPLE_CNT (50UL) /* 500ms / 10ms */
104 :
105 0 : #define FD_GUI_VOTE_STATE_NON_VOTING (0)
106 0 : #define FD_GUI_VOTE_STATE_VOTING (1)
107 0 : #define FD_GUI_VOTE_STATE_DELINQUENT (2)
108 :
109 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_JOINING_GOSSIP (1)
110 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_FULL_SNAPSHOT (2)
111 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_INCREMENTAL_SNAPSHOT (3)
112 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY (4)
113 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP (5)
114 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_RUNNING (6)
115 :
116 0 : #define FD_GUI_BOOT_PROGRESS_FULL_SNAPSHOT_IDX (0UL)
117 0 : #define FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX (1UL)
118 0 : #define FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT (2UL)
119 :
120 0 : #define FD_GUI_SLOT_LEVEL_INCOMPLETE (0)
121 0 : #define FD_GUI_SLOT_LEVEL_COMPLETED (1)
122 0 : #define FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED (2)
123 0 : #define FD_GUI_SLOT_LEVEL_ROOTED (3)
124 0 : #define FD_GUI_SLOT_LEVEL_FINALIZED (4)
125 :
126 : /* Ideally, we would store an entire epoch's worth of transactions. If
127 : we assume any given validator will have at most 5% stake, and average
128 : transactions per slot is around 10_000, then an epoch will have about
129 : 432_000*10_000*0.05 transactions (~2^28).
130 :
131 : Unfortunately, the transaction struct is 100+ bytes. If we sized the
132 : array to 2^28 entries then the memory required would be ~26GB. In
133 : order to keep memory usage to a more reasonable level, we'll
134 : arbitrarily use a fourth of that size. */
135 0 : #define FD_GUI_TXN_HISTORY_SZ (1UL<<26UL)
136 :
137 0 : #define FD_GUI_TXN_FLAGS_STARTED ( 1U)
138 0 : #define FD_GUI_TXN_FLAGS_ENDED ( 2U)
139 0 : #define FD_GUI_TXN_FLAGS_IS_SIMPLE_VOTE ( 4U)
140 0 : #define FD_GUI_TXN_FLAGS_FROM_BUNDLE ( 8U)
141 0 : #define FD_GUI_TXN_FLAGS_LANDED_IN_BLOCK (16U)
142 :
143 0 : #define FD_GUI_TURBINE_RECV_TIMESTAMPS (750UL)
144 :
145 : /* One use case for tracking ingress shred slot is to estimate when we
146 : have caught up to the tip of the blockchain. A naive approach would
147 : be to track the maximum seen slot.
148 :
149 : maximum_seen_slot = fd_ulong_max( maximum_seen_slot, new_slot_from_shred_tile );
150 :
151 : Unfortunately, this doesn't always work because a validator can send
152 : a slot number that is arbitrarily large on a false fork. Also, these
153 : shreds can be for a repair response, which can be arbitrarily small.
154 :
155 : The prospects here seem bleak, but not all hope is lost! We know
156 : that for a sufficiently large historical time window there is a high
157 : probability that at least some of the slots we observe will be valid
158 : recent turbine slots. For a sufficiently small window there is a high
159 : probability that all the observed shred slots are non-malicious (i.e.
160 : not arbitrarily large).
161 :
162 : In practice shred slots are almost always non-malicious. We can keep
163 : a history of the 12 largest slots we've seen in the past 4.8 seconds.
164 : We'll consider the "tip" of the blockchain to be the maximum slot in
165 : our history. This way, if we receive maliciously large slot number,
166 : it will be evicted after 4.8 seconds. If we receive a small slot from
167 : a repair response it will be ignored because we've seen other larger
168 : slots, meaning that our estimate is eventually consistent. For
169 : monitoring purposes this is sufficient.
170 :
171 : The worst case scenario is that this validator receives an incorrect
172 : shred slot slot more than once every 3 leader rotations. Before the
173 : previous incorrect slot is evicted from the history, a new one takes
174 : it's place and we wouldn't never get a correct estimate of the tip of
175 : the chain. We also would indefinitely think that that we haven't
176 : caught up. This would require the chain having perpetually malicious
177 : leaders with adjacent rotations. If this happens, Solana has bigger
178 : problems. */
179 0 : #define FD_GUI_TURBINE_SLOT_HISTORY_SZ ( 12UL )
180 :
181 : /* Like the turbine slot, the latest repair slot can also swing to
182 : arbitrarily large values due to a malicious fork switch. The gui
183 : provides the same guarantees for freshness and accuracy. This
184 : history is somewhat larger to handle the increased repair bandwidth
185 : during catch up. */
186 0 : #define FD_GUI_REPAIR_SLOT_HISTORY_SZ ( 512UL )
187 :
188 : /* FD_GUI_*_CATCH_UP_HISTORY_SZ is the capacity of the record of slots
189 : seen from repair or turbine during the catch up stage at startup.
190 : These buffers are run-length encoded, so they will typically be very
191 : small. The worst-case scenario is unbounded, so bounds here are
192 : determined heuristically. */
193 0 : #define FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ (4096UL)
194 0 : #define FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ (4096UL)
195 :
196 : /* FD_GUI_SHREDS_STAGING_SZ is number of shred events we'll retain in
197 : in a small staging area. The lifecycle of a shred looks something
198 : like the following
199 :
200 : states] turbine -> repairing (optional) -> processing -> waiting_for_siblings -> slot_complete
201 : events] ^-repair_requested ^-shred_received/shred_repaired ^-shred_replayed ^-max(shred_replayed)
202 :
203 : We're interested in recording timestamps for state transitions (which
204 : these docs call "shred events"). Unfortunately, due to forking,
205 : duplicate packets, etc we can't make any guarantees about ordering or
206 : uniqueness for these event timestamps. Instead the GUI just records
207 : timestamps for all events as they occur and put them into an array.
208 : Newly recorded event timestamps are also broadcast live to WebSocket
209 : consumers.
210 :
211 : The amount of shred events for non-finalized blocks can't really be
212 : bounded, so we use generous estimates here to set a memory bound. */
213 0 : #define FD_GUI_MAX_SHREDS_PER_BLOCK (32UL*1024UL)
214 0 : #define FD_GUI_MAX_EVENTS_PER_SHRED ( 32UL)
215 0 : #define FD_GUI_SHREDS_STAGING_SZ (32UL * FD_GUI_MAX_SHREDS_PER_BLOCK * FD_GUI_MAX_EVENTS_PER_SHRED)
216 :
217 : /* FD_GUI_SHREDS_HISTORY_SZ the number of shred events in our historical
218 : shred store. Shred events here belong to finalized slots which means
219 : we won't record any additional shred updates for these slots.
220 :
221 : All shred events for a given slot will be places in a contiguous
222 : chunk in the array, and the bounding indicies are stored in the
223 : fd_gui_slot_t slot history. Within a slot chunk, shred events are
224 : ordered in the ordered they were recorded by the gui tile.
225 :
226 : Ideally, we have enough space to store an epoch's worth of events,
227 : but we are limited by realistic memory consumption. Instead, we pick
228 : bound heuristically. */
229 0 : #define FD_GUI_SHREDS_HISTORY_SZ (432000UL*2000UL*4UL / 12UL)
230 :
231 0 : #define FD_GUI_SLOT_SHRED_REPAIR_REQUEST (0UL)
232 0 : #define FD_GUI_SLOT_SHRED_SHRED_RECEIVED_TURBINE (1UL)
233 0 : #define FD_GUI_SLOT_SHRED_SHRED_RECEIVED_REPAIR (2UL)
234 0 : #define FD_GUI_SLOT_SHRED_SHRED_REPLAY_EXEC_DONE (3UL)
235 0 : #define FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE (4UL)
236 : /* #define FD_GUI_SLOT_SHRED_SHRED_REPLAY_EXEC_START (5UL) // UNUSED */
237 0 : #define FD_GUI_SLOT_SHRED_SHRED_PUBLISHED (6UL)
238 :
239 0 : #define FD_GUI_SLOT_RANKINGS_SZ (100UL)
240 0 : #define FD_GUI_SLOT_RANKING_TYPE_ASC (0)
241 0 : #define FD_GUI_SLOT_RANKING_TYPE_DESC (1)
242 :
243 : struct fd_gui_tile_timers {
244 : ulong timers[ FD_METRICS_ENUM_TILE_REGIME_CNT ];
245 : ulong sched_timers[ FD_METRICS_ENUM_CPU_REGIME_CNT ];
246 :
247 : int in_backp;
248 : ushort last_cpu;
249 : uchar status;
250 : ulong heartbeat;
251 : ulong backp_cnt;
252 : ulong nvcsw;
253 : ulong nivcsw;
254 : ulong minflt;
255 : ulong majflt;
256 : };
257 :
258 : typedef struct fd_gui_tile_timers fd_gui_tile_timers_t;
259 :
260 : struct fd_gui_scheduler_counts {
261 : long sample_time_ns;
262 : ulong regular;
263 : ulong votes;
264 : ulong conflicting;
265 : ulong bundles;
266 : };
267 :
268 : typedef struct fd_gui_scheduler_counts fd_gui_scheduler_counts_t;
269 :
270 : struct fd_gui_network_stats {
271 : /* total bytes accumulated */
272 : struct {
273 : ulong turbine;
274 : ulong gossip;
275 : ulong tpu;
276 : ulong repair;
277 : ulong metric;
278 : } in, out;
279 : };
280 :
281 : typedef struct fd_gui_network_stats fd_gui_network_stats_t;
282 :
283 : struct fd_gui_leader_slot {
284 : ulong slot;
285 : fd_hash_t block_hash;
286 : long leader_start_time; /* UNIX timestamp of when we first became leader in this slot */
287 : long leader_end_time; /* UNIX timestamp of when we stopped being leader in this slot */
288 :
289 : /* Stem tiles can exist in one of 8 distinct activity regimes at any
290 : given moment. One of these regimes, caughtup_postfrag, is the
291 : only regime where a tile is in a spin loop without doing any
292 : useful work. This info is useful from a monitoring perspective
293 : because it lets us estimate CPU utilization on a pinned core.
294 :
295 : Every 10ms, the gui tile samples the amount of time tiles spent
296 : in each regime in the past 10ms. This sample is used to infer
297 : the CPU utilization in the past 10ms. This utilization is
298 : streamed live to WebSocket clients.
299 :
300 : In additional to live utilization, we are interested in recording
301 : utilization during one of this validator's leader slots. The gui
302 : tile is continuously recording samples to storage with capacity
303 : FD_GUI_TILE_TIMER_SNAP_CNT. The sample index is recorded at the
304 : start and end of a leader slot, and the number of samples is
305 : downsampled to be at most FD_GUI_TILE_TIMER_LEADER_DOWNSAMPLE_CNT
306 : samples (e.g. if there was an unusually long leader slot) and
307 : inserted into historical storage with capacity FD_GUI_LEADER_CNT.
308 :
309 : The tile_timers pointer references trailing storage allocated
310 : in fd_gui_footprint, with gui->tile_cnt elements per sample.
311 : Sized as tile_timers[ FD_GUI_TILE_TIMER_LEADER_DOWNSAMPLE_CNT ][ tile_cnt ]. */
312 : fd_gui_tile_timers_t * tile_timers;
313 : ulong tile_timers_sample_cnt;
314 :
315 : fd_gui_scheduler_counts_t scheduler_counts[ FD_GUI_SCHEDULER_COUNT_LEADER_DOWNSAMPLE_CNT ][ 1 ];
316 : ulong scheduler_counts_sample_cnt;
317 :
318 : struct {
319 : uint microblocks_upper_bound; /* An upper bound on the number of microblocks in the slot. If the number of
320 : microblocks observed is equal to this, the slot can be considered over.
321 : Generally, the bound is set to a "final" state by a done packing message,
322 : which sets it to the exact number of microblocks, but sometimes this message
323 : is not sent, if the max upper bound published by poh was already correct. */
324 : uint begin_microblocks; /* The number of microblocks we have seen be started (sent) from pack to banks. */
325 : uint end_microblocks; /* The number of microblocks we have seen be ended (sent) from banks to poh. The
326 : slot is only considered over if the begin and end microblocks seen are both equal
327 : to the microblock upper bound. */
328 :
329 : ulong start_offset; /* The smallest pack transaction index for this slot. The first transaction for this slot will
330 : be written to gui->txs[ start_offset%FD_GUI_TXN_HISTORY_SZ ]. */
331 : ulong end_offset; /* The largest pack transaction index for this slot, plus 1. The last transaction for this
332 : slot will be written to gui->txs[ (end_offset-1)%FD_GUI_TXN_HISTORY_SZ ]. */
333 : } txs;
334 :
335 : fd_done_packing_t scheduler_stats[ 1 ];
336 :
337 : uchar unbecame_leader: 1;
338 : };
339 :
340 : typedef struct fd_gui_leader_slot fd_gui_leader_slot_t;
341 :
342 : struct fd_gui_turbine_slot {
343 : ulong slot;
344 : long timestamp;
345 : };
346 :
347 : typedef struct fd_gui_turbine_slot fd_gui_turbine_slot_t;
348 :
349 : struct fd_gui_slot_staged_shred_event {
350 : long timestamp;
351 : ulong slot;
352 : ushort shred_idx;
353 : uchar event;
354 : };
355 :
356 : typedef struct fd_gui_slot_staged_shred_event fd_gui_slot_staged_shred_event_t;
357 :
358 : struct __attribute__((packed)) fd_gui_slot_history_shred_event {
359 : long timestamp;
360 : ushort shred_idx;
361 : uchar event;
362 : };
363 :
364 : typedef struct fd_gui_slot_history_shred_event fd_gui_slot_history_shred_event_t;
365 :
366 : struct fd_gui_slot_ranking {
367 : ulong slot;
368 : ulong value;
369 : int type;
370 : };
371 : typedef struct fd_gui_slot_ranking fd_gui_slot_ranking_t;
372 :
373 : struct fd_gui_slot_rankings {
374 : fd_gui_slot_ranking_t largest_tips [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
375 : fd_gui_slot_ranking_t largest_fees [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
376 : fd_gui_slot_ranking_t largest_rewards [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
377 : fd_gui_slot_ranking_t largest_duration [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
378 : fd_gui_slot_ranking_t largest_compute_units [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
379 : fd_gui_slot_ranking_t largest_skipped [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
380 : fd_gui_slot_ranking_t largest_rewards_per_cu [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
381 : fd_gui_slot_ranking_t smallest_tips [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
382 : fd_gui_slot_ranking_t smallest_fees [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
383 : fd_gui_slot_ranking_t smallest_rewards [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
384 : fd_gui_slot_ranking_t smallest_rewards_per_cu[ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
385 : fd_gui_slot_ranking_t smallest_duration [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
386 : fd_gui_slot_ranking_t smallest_compute_units [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
387 : fd_gui_slot_ranking_t smallest_skipped [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
388 : };
389 :
390 : typedef struct fd_gui_slot_rankings fd_gui_slot_rankings_t;
391 :
392 : struct fd_gui_ephemeral_slot {
393 : ulong slot; /* ULONG_MAX indicates invalid/evicted */
394 : long timestamp_arrival_nanos;
395 : };
396 : typedef struct fd_gui_ephemeral_slot fd_gui_ephemeral_slot_t;
397 :
398 : struct __attribute__((packed)) fd_gui_txn {
399 : uchar signature[ FD_TXN_SIGNATURE_SZ ];
400 : ulong transaction_fee;
401 : ulong priority_fee;
402 : ulong tips;
403 : long timestamp_arrival_nanos;
404 :
405 : /* compute_units_requested has both execution and non-execution cus */
406 : uint compute_units_requested : 21; /* <= 1.4M */
407 : uint compute_units_consumed : 21; /* <= 1.4M */
408 : uint bank_idx : 6; /* in [0, 64) */
409 : uint error_code : 6; /* in [0, 64) */
410 : int timestamp_delta_start_nanos;
411 : int timestamp_delta_end_nanos;
412 :
413 : /* txn_{}_pct is used as a fraction of the total microblock
414 : duration. For example, txn_load_end_pct can be used to find the
415 : time when this transaction started executing:
416 :
417 : timestamp_delta_start_exec_nanos = (
418 : (timestamp_delta_end_nanos-timestamp_delta_start_nanos) *
419 : ((double)txn_{}_pct/UCHAR_MAX)
420 : ) */
421 : uchar txn_start_pct;
422 : uchar txn_load_end_pct;
423 : uchar txn_end_pct;
424 : uchar txn_preload_end_pct;
425 : uchar flags; /* assigned with the FD_GUI_TXN_FLAGS_* macros */
426 : uchar source_tpu; /* FD_TXN_M_TPU_SOURCE_* */
427 : uint source_ipv4;
428 : uint microblock_idx;
429 : };
430 :
431 : typedef struct fd_gui_txn fd_gui_txn_t;
432 :
433 : struct fd_gui_txn_waterfall {
434 : struct {
435 : ulong quic;
436 : ulong udp;
437 : ulong gossip;
438 : ulong block_engine;
439 : ulong pack_cranked;
440 : } in;
441 :
442 : struct {
443 : ulong net_overrun;
444 : ulong quic_overrun;
445 : ulong quic_frag_drop;
446 : ulong quic_abandoned;
447 : ulong tpu_quic_invalid;
448 : ulong tpu_udp_invalid;
449 : ulong verify_overrun;
450 : ulong verify_parse;
451 : ulong verify_failed;
452 : ulong verify_duplicate;
453 : ulong dedup_duplicate;
454 : ulong resolv_lut_failed;
455 : ulong resolv_expired;
456 : ulong resolv_ancient;
457 : ulong resolv_no_ledger;
458 : ulong resolv_retained;
459 : ulong pack_invalid;
460 : ulong pack_invalid_bundle;
461 : ulong pack_expired;
462 : ulong pack_already_executed;
463 : ulong pack_retained;
464 : ulong pack_wait_full;
465 : ulong pack_leader_slow;
466 : ulong bank_invalid;
467 : ulong bank_nonce_already_advanced;
468 : ulong bank_nonce_advance_failed;
469 : ulong bank_nonce_wrong_blockhash;
470 : ulong block_success;
471 : ulong block_fail;
472 : } out;
473 : };
474 :
475 : typedef struct fd_gui_txn_waterfall fd_gui_txn_waterfall_t;
476 :
477 : struct fd_gui_tile_stats {
478 : long sample_time_nanos;
479 :
480 : ulong net_in_rx_bytes; /* Number of bytes received by the net or sock tile*/
481 : ulong quic_conn_cnt; /* Number of active QUIC connections */
482 : fd_histf_t bundle_rx_delay_hist; /* Histogram of bundle rx delay */
483 : ulong bundle_rtt_smoothed_nanos; /* RTT (nanoseconds) moving average */
484 : ulong verify_drop_cnt; /* Number of transactions dropped by verify tiles */
485 : ulong verify_total_cnt; /* Number of transactions received by verify tiles */
486 : ulong dedup_drop_cnt; /* Number of transactions dropped by dedup tile */
487 : ulong dedup_total_cnt; /* Number of transactions received by dedup tile */
488 : ulong pack_buffer_cnt; /* Number of buffered transactions in the pack tile */
489 : ulong pack_buffer_capacity; /* Total size of the pack transaction buffer */
490 : ulong bank_txn_exec_cnt; /* Number of transactions processed by the bank tile */
491 : ulong net_out_tx_bytes; /* Number of bytes sent by the net or sock tile */
492 : };
493 :
494 : typedef struct fd_gui_tile_stats fd_gui_tile_stats_t;
495 :
496 : struct fd_gui_slot {
497 : ulong slot;
498 : ulong parent_slot;
499 : ulong vote_slot;
500 : ulong reset_slot;
501 : long completed_time;
502 : uint max_compute_units;
503 : int mine;
504 : int skipped;
505 : int must_republish;
506 : int level;
507 : uint compute_units;
508 : ulong transaction_fee;
509 : ulong priority_fee;
510 : ulong tips;
511 : uint shred_cnt;
512 : uchar vote_latency;
513 :
514 : uint vote_success;
515 : uint vote_failed;
516 : uint nonvote_success;
517 : uint nonvote_failed;
518 :
519 : /* Some slot info is only tracked for our own leader slots. These
520 : slots are kept in a separate buffer. */
521 : ulong leader_history_idx;
522 :
523 : fd_gui_txn_waterfall_t waterfall_begin[ 1 ];
524 : fd_gui_txn_waterfall_t waterfall_end[ 1 ];
525 :
526 : fd_gui_tile_stats_t tile_stats_begin[ 1 ];
527 : fd_gui_tile_stats_t tile_stats_end[ 1 ];
528 :
529 : struct {
530 : ulong start_offset; /* gui->shreds.history[ start_offset % FD_GUI_SHREDS_HISTORY_SZ ] is the first shred event in
531 : contiguous chunk of events in the shred history corresponding to this slot. */
532 : ulong end_offset; /* One past the last shred event in the contiguous chunk of events in the shred history
533 : corresponding to this slot. */
534 : } shreds;
535 : };
536 :
537 : typedef struct fd_gui_slot fd_gui_slot_t;
538 :
539 : struct fd_gui {
540 : fd_http_server_t * http;
541 : fd_topo_t * topo;
542 :
543 : ulong tile_cnt;
544 :
545 : long next_sample_400millis;
546 : long next_sample_100millis;
547 : long next_sample_50millis;
548 : long next_sample_25millis;
549 : long next_sample_10millis;
550 :
551 : ulong leader_slot;
552 :
553 : struct {
554 : fd_pubkey_t identity_key[ 1 ];
555 : int has_vote_key;
556 : fd_pubkey_t vote_key[ 1 ];
557 : char vote_key_base58[ FD_BASE58_ENCODED_32_SZ ];
558 : char identity_key_base58[ FD_BASE58_ENCODED_32_SZ ];
559 :
560 : int is_full_client;
561 : char const * version;
562 : char const * cluster;
563 :
564 : char wfs_bank_hash[ FD_BASE58_ENCODED_32_SZ ];
565 : ushort expected_shred_version;
566 : int wfs_enabled;
567 :
568 : ulong vote_distance;
569 : int vote_state;
570 :
571 : long startup_time_nanos;
572 :
573 : union {
574 : struct { /* frankendancer only */
575 : uchar phase;
576 : int startup_got_full_snapshot;
577 :
578 : ulong startup_incremental_snapshot_slot;
579 : uint startup_incremental_snapshot_peer_ip_addr;
580 : ushort startup_incremental_snapshot_peer_port;
581 : double startup_incremental_snapshot_elapsed_secs;
582 : double startup_incremental_snapshot_remaining_secs;
583 : double startup_incremental_snapshot_throughput;
584 : ulong startup_incremental_snapshot_total_bytes;
585 : ulong startup_incremental_snapshot_current_bytes;
586 :
587 : ulong startup_full_snapshot_slot;
588 : uint startup_full_snapshot_peer_ip_addr;
589 : ushort startup_full_snapshot_peer_port;
590 : double startup_full_snapshot_elapsed_secs;
591 : double startup_full_snapshot_remaining_secs;
592 : double startup_full_snapshot_throughput;
593 : ulong startup_full_snapshot_total_bytes;
594 : ulong startup_full_snapshot_current_bytes;
595 :
596 : ulong startup_ledger_slot;
597 : ulong startup_ledger_max_slot;
598 :
599 : ulong startup_waiting_for_supermajority_slot;
600 : ulong startup_waiting_for_supermajority_stake_pct;
601 : } startup_progress;
602 : struct { /* used in the full client */
603 : uchar phase;
604 : long joining_gossip_time_nanos;
605 : struct {
606 : ulong slot;
607 : uint peer_addr;
608 : ushort peer_port;
609 : ulong total_bytes_compressed;
610 : long reset_time_nanos; /* UNIX nanosecond timestamp */
611 : long sample_time_nanos;
612 : ulong reset_cnt;
613 :
614 : ulong read_bytes_compressed;
615 : char read_path[ PATH_MAX+30UL ]; /* URL or filesystem path. 30 is fd_cstr_nlen( "https://255.255.255.255:12345/", ULONG_MAX ) */
616 :
617 : ulong decompress_bytes_decompressed;
618 : ulong decompress_bytes_compressed;
619 :
620 : ulong insert_bytes_decompressed;
621 : char insert_path[ PATH_MAX ];
622 : ulong insert_accounts_current;
623 : } loading_snapshot[ FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT ];
624 :
625 : ulong wfs_total_stake;
626 : ulong wfs_connected_stake;
627 : ulong wfs_total_peers;
628 : ulong wfs_connected_peers;
629 : ulong wfs_attempt;
630 :
631 : long catching_up_time_nanos;
632 : ulong catching_up_first_replay_slot;
633 : } boot_progress;
634 : };
635 :
636 : int schedule_strategy;
637 :
638 : ulong identity_account_balance;
639 : ulong vote_account_balance;
640 : ulong estimated_slot_duration_nanos;
641 :
642 : ulong sock_tile_cnt;
643 : ulong net_tile_cnt;
644 : ulong quic_tile_cnt;
645 : ulong verify_tile_cnt;
646 : ulong resolh_tile_cnt;
647 : ulong resolv_tile_cnt;
648 : ulong bank_tile_cnt;
649 : ulong execle_tile_cnt;
650 : ulong execrp_tile_cnt;
651 : ulong shred_tile_cnt;
652 :
653 : ulong slot_rooted;
654 : ulong slot_optimistically_confirmed;
655 : ulong slot_completed;
656 : ulong slot_estimated;
657 : ulong slot_caught_up;
658 : ulong slot_repair;
659 : ulong slot_turbine;
660 : ulong slot_reset;
661 : ulong slot_storage;
662 : ulong active_fork_cnt;
663 :
664 : fd_gui_ephemeral_slot_t slots_max_turbine[ FD_GUI_TURBINE_SLOT_HISTORY_SZ+1UL ];
665 : fd_gui_ephemeral_slot_t slots_max_repair [ FD_GUI_REPAIR_SLOT_HISTORY_SZ +1UL ];
666 :
667 : /* catchup_* and late_votes are run-length encoded. i.e. adjacent
668 : pairs represent contiguous runs */
669 : ulong catch_up_turbine[ FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ ];
670 : ulong catch_up_turbine_sz;
671 :
672 : ulong catch_up_repair[ FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ ];
673 : ulong catch_up_repair_sz;
674 :
675 : ulong late_votes[ MAX_SLOTS_PER_EPOCH ];
676 : ulong late_votes_sz;
677 :
678 : ulong estimated_tps_history_idx;
679 : struct {
680 : ulong vote_failed;
681 : ulong vote_success;
682 : ulong nonvote_success;
683 : ulong nonvote_failed;
684 : } estimated_tps_history[ FD_GUI_TPS_HISTORY_SAMPLE_CNT ];
685 :
686 : fd_gui_network_stats_t network_stats_current[ 1 ];
687 :
688 : fd_gui_txn_waterfall_t txn_waterfall_reference[ 1 ];
689 : fd_gui_txn_waterfall_t txn_waterfall_current[ 1 ];
690 :
691 : fd_gui_tile_stats_t tile_stats_reference[ 1 ];
692 : fd_gui_tile_stats_t tile_stats_current[ 1 ];
693 :
694 : ulong tile_timers_snap_idx;
695 : ulong tile_timers_snap_idx_slot_start;
696 : /* Temporary storage for samples. Will be downsampled into
697 : leader history on slot end. Sized as
698 : tile_timers_snap[ FD_GUI_TILE_TIMER_SNAP_CNT ][ tile_cnt ] */
699 : fd_gui_tile_timers_t * tile_timers_snap;
700 :
701 : ulong scheduler_counts_snap_idx;
702 : ulong scheduler_counts_snap_idx_slot_start;
703 : /* Temporary storage for samples. Will be downsampled into leader history on slot end. */
704 : fd_gui_scheduler_counts_t scheduler_counts_snap[ FD_GUI_SCHEDULER_COUNT_SNAP_CNT ][ 1 ];
705 : } summary;
706 :
707 : fd_gui_slot_t slots[ FD_GUI_SLOTS_CNT ][ 1 ];
708 :
709 : /* used for estimating slot duration */
710 : fd_gui_turbine_slot_t turbine_slots[ FD_GUI_TURBINE_RECV_TIMESTAMPS ];
711 :
712 : fd_gui_leader_slot_t leader_slots[ FD_GUI_LEADER_CNT ][ 1 ];
713 : ulong leader_slots_cnt;
714 :
715 : fd_gui_txn_t txs[ FD_GUI_TXN_HISTORY_SZ ][ 1 ];
716 : ulong pack_txn_idx; /* The pack index of the most recently received transaction */
717 :
718 : ulong tower_cnt;
719 : fd_vote_acc_vote_t tower[ FD_TOWER_VOTE_MAX ];
720 :
721 : struct {
722 : int has_block_engine;
723 : char name[ 16 ];
724 : char url[ 256 ];
725 : char ip_cstr[ 40 ]; /* IPv4 or IPv6 cstr */
726 : int status;
727 : } block_engine;
728 :
729 : struct {
730 : int has_epoch[ 2 ];
731 :
732 : struct {
733 : ulong epoch;
734 : long start_time;
735 : long end_time;
736 :
737 : ulong my_total_slots;
738 : ulong my_skipped_slots;
739 :
740 : ulong start_slot;
741 : ulong end_slot;
742 : fd_epoch_leaders_t * lsched;
743 : uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) _lsched[ FD_EPOCH_LEADERS_FOOTPRINT(MAX_STAKED_LEADERS, MAX_SLOTS_PER_EPOCH) ];
744 : fd_vote_stake_weight_t stakes[ MAX_STAKED_LEADERS ];
745 :
746 : ulong rankings_slot; /* One more than the largest slot we've processed into our rankings */
747 : fd_gui_slot_rankings_t rankings[ 1 ]; /* global slot rankings */
748 : fd_gui_slot_rankings_t my_rankings[ 1 ]; /* my slots only */
749 : } epochs[ 2 ];
750 : } epoch;
751 :
752 : struct { /* frankendancer only */
753 : ulong peer_cnt;
754 : struct fd_gui_gossip_peer peers[ FD_GUI_MAX_PEER_CNT ];
755 : } gossip;
756 :
757 : struct { /* frankendancer only */
758 : ulong vote_account_cnt;
759 : struct fd_gui_vote_account vote_accounts[ FD_GUI_MAX_PEER_CNT ];
760 : } vote_account;
761 :
762 : struct { /* frankendancer only */
763 : ulong info_cnt;
764 : struct fd_gui_validator_info info[ FD_GUI_MAX_PEER_CNT ];
765 : } validator_info;
766 :
767 : fd_gui_peers_ctx_t * peers; /* full-client */
768 :
769 : struct {
770 : ulong leader_shred_cnt; /* A gauge counting the number of leader shreds seen on the SHRED_OUT link. Resets at
771 : the end of a leader slot. This works because leader fecs are published in order. */
772 : ulong staged_next_broadcast; /* staged[ staged_next_broadcast % FD_GUI_SHREDS_STAGING_SZ ] is the first shred event
773 : that hasn't yet been broadcast to WebSocket clients */
774 : ulong staged_head; /* staged_head % FD_GUI_SHREDS_STAGING_SZ is the first valid event in staged */
775 : ulong staged_tail; /* staged_tail % FD_GUI_SHREDS_STAGING_SZ is one past the last valid event in staged */
776 : fd_gui_slot_staged_shred_event_t staged [ FD_GUI_SHREDS_STAGING_SZ ];
777 :
778 : ulong history_slot; /* the largest slot store in history */
779 : ulong history_tail; /* history_tail % FD_GUI_SHREDS_HISTORY_SZ is one past the last valid event in history */
780 : fd_gui_slot_history_shred_event_t history[ FD_GUI_SHREDS_HISTORY_SZ ];
781 :
782 : /* scratch space for archiving staged events */
783 : fd_gui_slot_staged_shred_event_t _staged_scratch [ FD_GUI_SHREDS_STAGING_SZ ];
784 : } shreds; /* full client */
785 : };
786 :
787 : typedef struct fd_gui fd_gui_t;
788 :
789 : FD_PROTOTYPES_BEGIN
790 :
791 : FD_FN_CONST ulong
792 : fd_gui_align( void );
793 :
794 : FD_FN_CONST ulong
795 : fd_gui_footprint( ulong tile_cnt );
796 :
797 : void *
798 : fd_gui_new( void * shmem,
799 : fd_http_server_t * http,
800 : char const * version,
801 : char const * cluster,
802 : uchar const * identity_key,
803 : int has_vote_key,
804 : uchar const * vote_key,
805 : int is_full_client,
806 : int snapshots_enabled,
807 : int is_voting,
808 : int schedule_strategy,
809 : char const * wfs_expected_bank_hash_cstr,
810 : ushort expected_shred_version,
811 : fd_topo_t * topo,
812 : long now );
813 :
814 : fd_gui_t *
815 : fd_gui_join( void * shmem );
816 :
817 : void
818 : fd_gui_set_identity( fd_gui_t * gui,
819 : uchar const * identity_pubkey );
820 :
821 : void
822 : fd_gui_ws_open( fd_gui_t * gui,
823 : ulong conn_id,
824 : long now );
825 :
826 : int
827 : fd_gui_ws_message( fd_gui_t * gui,
828 : ulong ws_conn_id,
829 : uchar const * data,
830 : ulong data_len );
831 :
832 : void
833 : fd_gui_plugin_message( fd_gui_t * gui,
834 : ulong plugin_msg,
835 : void const * msg,
836 : long now );
837 :
838 : void
839 : fd_gui_became_leader( fd_gui_t * gui,
840 : ulong slot,
841 : long start_time_nanos,
842 : long end_time_nanos,
843 : ulong max_compute_units,
844 : ulong max_microblocks );
845 :
846 : void
847 : fd_gui_unbecame_leader( fd_gui_t * gui,
848 : ulong _slot,
849 : fd_done_packing_t const * done_packing,
850 : long now );
851 :
852 : void
853 : fd_gui_microblock_execution_begin( fd_gui_t * gui,
854 : long now,
855 : ulong _slot,
856 : fd_txn_e_t * txns,
857 : ulong txn_cnt,
858 : uint microblock_idx,
859 : ulong pack_txn_idx );
860 :
861 : void
862 : fd_gui_microblock_execution_end( fd_gui_t * gui,
863 : long now,
864 : ulong bank_idx,
865 : ulong _slot,
866 : ulong txn_cnt,
867 : fd_txn_p_t * txns,
868 : ulong pack_txn_idx,
869 : uchar txn_start_pct,
870 : uchar txn_load_end_pct,
871 : uchar txn_end_pct,
872 : uchar txn_preload_end_pct,
873 : ulong tips );
874 :
875 : int
876 : fd_gui_poll( fd_gui_t * gui, long now );
877 :
878 : void
879 : fd_gui_handle_block_engine_update( fd_gui_t * gui,
880 : fd_bundle_block_engine_update_t const * update );
881 :
882 : void
883 : fd_gui_handle_shred( fd_gui_t * gui,
884 : ulong slot,
885 : ulong shred_idx,
886 : int is_turbine,
887 : long tsorig );
888 :
889 : void
890 : fd_gui_handle_leader_fec( fd_gui_t * gui,
891 : ulong slot,
892 : ulong fec_shred_cnt,
893 : int is_end_of_slot,
894 : long tsorig );
895 :
896 : void
897 : fd_gui_handle_exec_txn_done( fd_gui_t * gui,
898 : ulong slot,
899 : ulong start_shred_idx,
900 : ulong end_shred_idx,
901 : long tsorig_ns,
902 : long tspub_ns );
903 :
904 : void
905 : fd_gui_handle_repair_slot( fd_gui_t * gui, ulong slot, long now );
906 :
907 : void
908 : fd_gui_handle_repair_request( fd_gui_t * gui, ulong slot, ulong shred_idx, long now );
909 :
910 : void
911 : fd_gui_handle_snapshot_update( fd_gui_t * gui,
912 : fd_snapct_update_t const * msg );
913 :
914 : void
915 : fd_gui_stage_snapshot_manifest( fd_gui_t * gui,
916 : fd_snapshot_manifest_t const * manifest );
917 :
918 : void
919 : fd_gui_handle_leader_schedule( fd_gui_t * gui,
920 : fd_stake_weight_msg_t const * leader_schedule,
921 : long now );
922 :
923 : void
924 : fd_gui_handle_epoch_info( fd_gui_t * gui,
925 : fd_epoch_info_msg_t const * epoch_info,
926 : long now );
927 :
928 : void
929 : fd_gui_handle_votes_update( fd_gui_t * gui,
930 : fd_tower_slot_confirmed_t const * votes );
931 :
932 : void
933 : fd_gui_handle_tower_update( fd_gui_t * gui,
934 : fd_tower_slot_done_t const * msg,
935 : long now );
936 :
937 : void
938 : fd_gui_handle_replay_update( fd_gui_t * gui,
939 : fd_replay_slot_completed_t const * slot_completed,
940 : ulong vote_slot,
941 : long now );
942 :
943 : void
944 : fd_gui_handle_genesis_hash( fd_gui_t * gui,
945 : fd_hash_t const * msg );
946 :
947 : static inline ulong
948 0 : fd_gui_current_epoch_idx( fd_gui_t * gui ) {
949 0 : ulong epoch_idx = ULONG_MAX;
950 0 : ulong epoch = ULONG_MAX;
951 0 : for( ulong i = 0UL; i<2UL; i++ ) {
952 0 : if( FD_LIKELY( gui->epoch.has_epoch[ i ] ) ) {
953 : /* the "current" epoch is the smaller one */
954 0 : if( FD_LIKELY( gui->epoch.epochs[ i ].epoch<epoch ) ) {
955 0 : epoch = gui->epoch.epochs[ i ].epoch;
956 0 : epoch_idx = i;
957 0 : }
958 0 : }
959 0 : }
960 0 : return epoch_idx;
961 0 : }
962 :
963 : static inline fd_gui_slot_t *
964 0 : fd_gui_get_slot( fd_gui_t const * gui, ulong _slot ) {
965 0 : fd_gui_slot_t const * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
966 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX || _slot==ULONG_MAX || slot->slot!=_slot ) ) return NULL;
967 0 : return (fd_gui_slot_t *)slot;
968 0 : }
969 :
970 : static inline fd_gui_slot_t const *
971 0 : fd_gui_get_slot_const( fd_gui_t const * gui, ulong _slot ) {
972 0 : return fd_gui_get_slot( gui, _slot );
973 0 : }
974 :
975 : static inline fd_gui_leader_slot_t *
976 0 : fd_gui_get_leader_slot( fd_gui_t const * gui, ulong _slot ) {
977 0 : fd_gui_slot_t const * slot = fd_gui_get_slot( gui, _slot );
978 0 : if( FD_UNLIKELY( !slot
979 0 : || !slot->mine
980 0 : || slot->leader_history_idx==ULONG_MAX
981 0 : || slot->leader_history_idx + FD_GUI_LEADER_CNT < gui->leader_slots_cnt
982 0 : || gui->leader_slots[ slot->leader_history_idx % FD_GUI_LEADER_CNT ]->slot!=_slot ) ) return NULL;
983 0 : return (fd_gui_leader_slot_t *)gui->leader_slots[ slot->leader_history_idx % FD_GUI_LEADER_CNT ];
984 0 : }
985 :
986 : static inline fd_gui_leader_slot_t const *
987 0 : fd_gui_get_leader_slot_const( fd_gui_t const * gui, ulong _slot ) {
988 0 : return fd_gui_get_leader_slot( gui, _slot );
989 0 : }
990 :
991 : /* fd_gui_get_root_slot returns a handle to the closest ancestor of slot
992 : that is a root, if available, otherwise NULL. */
993 : static inline fd_gui_slot_t *
994 : fd_gui_get_root_slot( fd_gui_t const * gui,
995 0 : ulong slot ) {
996 0 : fd_gui_slot_t * c = fd_gui_get_slot( gui, slot );
997 0 : while( c ) {
998 0 : if( FD_UNLIKELY( c->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) return c;
999 0 : c = fd_gui_get_slot( gui, c->parent_slot );
1000 0 : }
1001 0 : return NULL;
1002 0 : }
1003 :
1004 : /* fd_gui_slot_is_ancestor returns 1 if anc is known to be an ancestor
1005 : of slot (on the same fork), 0 otherwise. */
1006 : static inline int
1007 : fd_gui_slot_is_ancestor( fd_gui_t const * gui,
1008 : ulong anc,
1009 0 : ulong slot ) {
1010 0 : fd_gui_slot_t * c = fd_gui_get_slot( gui, slot );
1011 0 : while( c ) {
1012 0 : if( FD_UNLIKELY( c->slot==anc ) ) return 1;
1013 0 : c = fd_gui_get_slot( gui, c->parent_slot );
1014 0 : }
1015 0 : return 0;
1016 0 : }
1017 :
1018 : /* fd_gui_get_parent_slot_on_fork returns a handle to the parent of slot
1019 : on the fork ending on frontier_slot. If slot is unknown or skipped,
1020 : the closest (by slot number) valid parent on the fork is returned.
1021 :
1022 : NULL if slot is not an ancestor of frontier slot or if the parent is
1023 : unknown. */
1024 : static inline fd_gui_slot_t *
1025 : fd_gui_get_parent_slot_on_fork( fd_gui_t const * gui,
1026 : ulong frontier_slot,
1027 0 : ulong slot ) {
1028 0 : fd_gui_slot_t * c = fd_gui_get_slot( gui, frontier_slot );
1029 0 : while( c ) {
1030 0 : if( FD_UNLIKELY( c->slot<=slot ) ) return NULL;
1031 0 : fd_gui_slot_t * p = fd_gui_get_slot( gui, c->parent_slot );
1032 0 : if( FD_UNLIKELY( p && p->slot<=slot-1UL ) ) return p;
1033 0 : c = p;
1034 0 : }
1035 0 : return NULL;
1036 0 : }
1037 :
1038 : /* fd_gui_is_skipped_on_fork returns 1 if slot is skipped on the fork
1039 : starting at anc and ending at des, 0 otherwise. */
1040 : static inline int
1041 : fd_gui_is_skipped_on_fork( fd_gui_t const * gui,
1042 : ulong anc,
1043 : ulong des,
1044 0 : ulong slot ) {
1045 0 : fd_gui_slot_t const * c = fd_gui_get_slot( gui, des );
1046 0 : while( c ) {
1047 0 : if( FD_UNLIKELY( anc==c->slot ) ) return 0; /* on the fork, not skipped */
1048 0 : fd_gui_slot_t const * p = fd_gui_get_slot( gui, c->parent_slot );
1049 0 : if( FD_UNLIKELY( p && p->slot<slot && c->slot>slot ) ) return 1; /* in-between two nodes, skipped */
1050 0 : c = p;
1051 0 : }
1052 :
1053 0 : return 0; /* slot not between anc and des, or is unknown */
1054 0 : }
1055 :
1056 : FD_PROTOTYPES_END
1057 :
1058 : #endif /* HEADER_fd_src_disco_gui_fd_gui_h */
|