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 : #include "fd_gui_hist.h"
6 : #include "fd_gui_ema.h"
7 :
8 : #include "../topo/fd_topo.h"
9 :
10 : #include "../../ballet/txn/fd_txn.h"
11 : #include "../../disco/tiles.h"
12 : #include "../../disco/fd_txn_p.h"
13 : #include "../../disco/bundle/fd_bundle_tile.h"
14 : #include "../../discof/restore/fd_snapct_tile.h"
15 : #include "../../discof/restore/utils/fd_ssmsg.h"
16 : #include "../../discof/tower/fd_tower_tile.h"
17 : #include "../../discof/replay/fd_replay_tile.h"
18 : #include "../../flamenco/leaders/fd_leaders.h"
19 : #include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h" /* fd_slot_to_epoch */
20 : #include "../../util/fd_util_base.h"
21 : #include "../../util/hist/fd_histf.h"
22 : #include "../../waltz/http/fd_http_server.h"
23 : #include "../../flamenco/accdb/fd_accdb_cache.h"
24 : #include "../../flamenco/accdb/fd_accdb_shmem.h"
25 :
26 :
27 :
28 : /* ---- Network Bandwidth Monitoring ----------------------------------- */
29 :
30 0 : #define FD_GUI_NETWORK_EMA_HALF_LIFE_NS (1000000000L) /* 1 second in nanoseconds */
31 0 : #define FD_GUI_NET_PROTO_CNT (6UL) /* turbine, gossip, tpu, repair, rserve, metric */
32 0 : #define FD_GUI_NET_RATE_MAX_WINDOW_NS (300L*1000L*1000L*1000L) /* 5 minutes in nanoseconds */
33 :
34 : /* Monotonic deque element for sliding-window max tracking of
35 : EMA-smoothed network throughput. */
36 : struct fd_gui_rate_entry {
37 : long ts_nanos;
38 : double value;
39 : };
40 : typedef struct fd_gui_rate_entry fd_gui_rate_entry_t;
41 :
42 : /* At 100 ms sampling, 5 minutes = 3000 samples. */
43 : #define DEQUE_NAME fd_gui_rate_deque
44 0 : #define DEQUE_T fd_gui_rate_entry_t
45 0 : #define DEQUE_MAX 4096UL
46 : #include "../../util/tmpl/fd_deque.c"
47 :
48 : /* ---- History Buffers ------------------------------------------------ */
49 :
50 0 : #define FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS (10L)
51 0 : #define FD_GUI_TPS_HISTORY_SAMPLE_CNT (300UL)
52 :
53 0 : #define FD_GUI_PROGCACHE_HISTORY_CNT (600UL) /* 60s / 100ms */
54 :
55 0 : #define FD_GUI_TURBINE_RECV_TIMESTAMPS (750UL)
56 :
57 : /* One use case for tracking ingress shred slot is to estimate when we
58 : have caught up to the tip of the blockchain. A naive approach would
59 : be to track the maximum seen slot.
60 :
61 : maximum_seen_slot = fd_ulong_max( maximum_seen_slot, new_slot_from_shred_tile );
62 :
63 : Unfortunately, this doesn't always work because a validator can send
64 : a slot number that is arbitrarily large on a false fork. Also, these
65 : shreds can be for a repair response, which can be arbitrarily small.
66 :
67 : The prospects here seem bleak, but not all hope is lost! We know
68 : that for a sufficiently large historical time window there is a high
69 : probability that at least some of the slots we observe will be valid
70 : recent turbine slots. For a sufficiently small window there is a high
71 : probability that all the observed shred slots are non-malicious (i.e.
72 : not arbitrarily large).
73 :
74 : In practice shred slots are almost always non-malicious. We can keep
75 : a history of the 12 largest slots we've seen in the past 4.8 seconds.
76 : We'll consider the "tip" of the blockchain to be the maximum slot in
77 : our history. This way, if we receive maliciously large slot number,
78 : it will be evicted after 4.8 seconds. If we receive a small slot from
79 : a repair response it will be ignored because we've seen other larger
80 : slots, meaning that our estimate is eventually consistent. For
81 : monitoring purposes this is sufficient.
82 :
83 : The worst case scenario is that this validator receives an incorrect
84 : shred slot slot more than once every 3 leader rotations. Before the
85 : previous incorrect slot is evicted from the history, a new one takes
86 : it's place and we wouldn't never get a correct estimate of the tip of
87 : the chain. We also would indefinitely think that that we haven't
88 : caught up. This would require the chain having perpetually malicious
89 : leaders with adjacent rotations. If this happens, Solana has bigger
90 : problems. */
91 0 : #define FD_GUI_TURBINE_SLOT_HISTORY_SZ ( 12UL )
92 :
93 : /* Like the turbine slot, the latest repair slot can also swing to
94 : arbitrarily large values due to a malicious fork switch. The gui
95 : provides the same guarantees for freshness and accuracy. This
96 : history is somewhat larger to handle the increased repair bandwidth
97 : during catch up. */
98 0 : #define FD_GUI_REPAIR_SLOT_HISTORY_SZ ( 512UL )
99 :
100 : /* FD_GUI_*_CATCH_UP_HISTORY_SZ is the capacity of the record of slots
101 : seen from repair or turbine during the catch up stage at startup.
102 : These buffers are run-length encoded, so they will typically be very
103 : small. The worst-case scenario is unbounded, so bounds here are
104 : determined heuristically. */
105 0 : #define FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ (4096UL)
106 0 : #define FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ (4096UL)
107 :
108 : /* FD_GUI_SHREDS_HISTORY_SZ the number of shred events in our historical
109 : shred store. Shred events here belong to finalized slots which means
110 : we won't record any additional shred updates for these slots.
111 :
112 : All shred events for a given slot will be places in a contiguous
113 : chunk in the array, and the bounding indices are stored in the
114 : slot history. Within a slot chunk, shred events are ordered in the
115 : order they were recorded by the gui tile.
116 :
117 : Ideally, we have enough space to store an epoch's worth of events,
118 : but we are limited by realistic memory consumption. Instead, we pick
119 : bound heuristically. */
120 : #define FD_GUI_SHREDS_HISTORY_SZ (432000UL*2000UL*4UL / 12UL)
121 :
122 0 : #define FD_GUI_SLOT_RANKINGS_SZ (100UL)
123 0 : #define FD_GUI_SLOT_RANKING_TYPE_ASC (0)
124 0 : #define FD_GUI_SLOT_RANKING_TYPE_DESC (1)
125 :
126 : /* ---- Boot ----------------------------------------------------------- */
127 :
128 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_JOINING_GOSSIP (1)
129 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_FULL_SNAPSHOT (2)
130 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_INCREMENTAL_SNAPSHOT (3)
131 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY (4)
132 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP (5)
133 0 : #define FD_GUI_BOOT_PROGRESS_TYPE_RUNNING (6)
134 :
135 0 : #define FD_GUI_BOOT_PROGRESS_FULL_SNAPSHOT_IDX (0UL)
136 0 : #define FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX (1UL)
137 0 : #define FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT (2UL)
138 :
139 0 : #define FD_GUI_SLOT_LEVEL_INCOMPLETE (0)
140 0 : #define FD_GUI_SLOT_LEVEL_COMPLETED (1)
141 0 : #define FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED (2)
142 0 : #define FD_GUI_SLOT_LEVEL_ROOTED (3)
143 0 : #define FD_GUI_SLOT_LEVEL_FINALIZED (4)
144 :
145 : /* "Skipped" means not present on the canonical fork (i.e. tower_slot's
146 : fork). Since tower_slot may fall behind replay, we need a third state
147 : to assign slots which are ahead of it. */
148 0 : #define FD_GUI_SKIP_STATUS_UNKNOWN ((uchar)0)
149 0 : #define FD_GUI_SKIP_STATUS_NOT_SKIPPED ((uchar)1)
150 0 : #define FD_GUI_SKIP_STATUS_SKIPPED ((uchar)2)
151 :
152 0 : #define FD_GUI_TXN_FLAGS_STARTED ( 1U)
153 0 : #define FD_GUI_TXN_FLAGS_ENDED ( 2U)
154 0 : #define FD_GUI_TXN_FLAGS_IS_SIMPLE_VOTE ( 4U)
155 0 : #define FD_GUI_TXN_FLAGS_FROM_BUNDLE ( 8U)
156 0 : #define FD_GUI_TXN_FLAGS_LANDED_IN_BLOCK (16U)
157 :
158 0 : #define FD_GUI_VOTE_STATE_NON_VOTING (0)
159 0 : #define FD_GUI_VOTE_STATE_VOTING (1)
160 0 : #define FD_GUI_VOTE_STATE_DELINQUENT (2)
161 :
162 : #define FD_GUI_MAX_VOTE_DISTANCE (512UL)
163 :
164 : #define FD_GUI_LANDED_VOTE_MAX (4096UL)
165 :
166 0 : #define FD_GUI_SLOT_SHRED_REPAIR_REQUEST (0UL)
167 0 : #define FD_GUI_SLOT_SHRED_SHRED_RECEIVED_TURBINE (1UL)
168 0 : #define FD_GUI_SLOT_SHRED_SHRED_RECEIVED_REPAIR (2UL)
169 0 : #define FD_GUI_SLOT_SHRED_SHRED_REPLAY_EXEC_DONE (3UL)
170 0 : #define FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE (4UL)
171 : /* #define FD_GUI_SLOT_SHRED_SHRED_REPLAY_EXEC_START (5UL) // UNUSED */
172 0 : #define FD_GUI_SLOT_SHRED_SHRED_PUBLISHED (6UL)
173 :
174 : struct fd_gui_tile_timers {
175 : long sample_time_nanos; /* wallclock ns this sample was taken; identical across the per-tile records. */
176 : ulong tile_idx; /* global tile index into topo->tiles. */
177 : ulong timers[ FD_METRICS_ENUM_TILE_REGIME_CNT ];
178 : ulong sched_timers[ FD_METRICS_ENUM_CPU_REGIME_CNT ];
179 :
180 : int in_backp;
181 : ushort last_cpu;
182 : uchar status;
183 : ulong heartbeat;
184 : ulong backp_cnt;
185 : ulong nvcsw;
186 : ulong nivcsw;
187 : ulong minflt;
188 : ulong majflt;
189 : ulong interrupts;
190 : ulong tlb_shootdowns;
191 : ulong timer_ticks;
192 : };
193 :
194 : typedef struct fd_gui_tile_timers fd_gui_tile_timers_t;
195 :
196 : struct fd_gui_tile_timers_hist {
197 : long sample_time_nanos;
198 : ushort tile_idx;
199 : uchar alive;
200 : uchar in_backp;
201 : ushort timers[ FD_METRICS_ENUM_TILE_REGIME_CNT ];
202 : ushort sched_timers[ FD_METRICS_ENUM_CPU_REGIME_CNT ];
203 : ushort idle_ratio;
204 : ushort last_cpu;
205 : ulong backp_msgs;
206 : ulong nvcsw;
207 : ulong nivcsw;
208 : ulong minflt;
209 : ulong majflt;
210 : ulong interrupts;
211 : ulong tlb_shootdowns;
212 : ulong timer_ticks;
213 : };
214 :
215 : typedef struct fd_gui_tile_timers_hist fd_gui_tile_timers_hist_t;
216 :
217 : struct fd_gui_scheduler_counts {
218 : long sample_time_ns;
219 : ulong regular;
220 : ulong votes;
221 : ulong conflicting;
222 : ulong bundles;
223 : };
224 :
225 : typedef struct fd_gui_scheduler_counts fd_gui_scheduler_counts_t;
226 :
227 : struct fd_gui_network_stats {
228 : /* total bytes accumulated */
229 : struct {
230 : ulong turbine;
231 : ulong gossip;
232 : ulong tpu;
233 : ulong repair;
234 : ulong rserve;
235 : ulong metric;
236 : } in, out;
237 : };
238 :
239 : typedef struct fd_gui_network_stats fd_gui_network_stats_t;
240 :
241 : struct fd_gui_accounts_stats {
242 : /* Raw counters/gauges read from tile metric pages. Stored so we can
243 : compute deltas (and per-second rates) against a previous snapshot. */
244 : long sample_time_nanos;
245 :
246 : /* Disk (gauges from accdb tile). */
247 : ulong accounts_total;
248 : ulong accounts_capacity;
249 : ulong disk_allocated_bytes;
250 : ulong disk_current_bytes;
251 : ulong disk_used_bytes;
252 :
253 : /* Compaction (gauges + counters from accdb tile). */
254 : ulong in_compaction;
255 : ulong compactions_requested;
256 : ulong compactions_completed;
257 : ulong accounts_relocated_bytes;
258 :
259 : /* Cache occupancy (gauges from accdb tile, per class). */
260 : ulong cache_class_used [ FD_ACCDB_CACHE_CLASS_CNT ];
261 : ulong cache_class_max [ FD_ACCDB_CACHE_CLASS_CNT ];
262 : ulong cache_class_reserved [ FD_ACCDB_CACHE_CLASS_CNT ];
263 : /* Preeviction thresholds, expressed as used-slot counts directly
264 : comparable to cache_class_used / cache_class_max. */
265 : ulong cache_class_target_used [ FD_ACCDB_CACHE_CLASS_CNT ];
266 : ulong cache_class_low_water_used [ FD_ACCDB_CACHE_CLASS_CNT ];
267 :
268 : /* Aggregate counters summed across all accdb consumer tiles. */
269 : ulong acquired; /* total acquires */
270 : ulong acquired_writable; /* writable subset of acquires (RW tiles only) */
271 : ulong acquired_per_class [ FD_ACCDB_CACHE_CLASS_CNT ]; /* acquires attributed to a class (RW tiles only) */
272 : ulong acquired_writable_per_class [ FD_ACCDB_CACHE_CLASS_CNT ]; /* writable acquires attributed to a class (RW tiles only) */
273 : ulong not_found_per_class [ FD_ACCDB_CACHE_CLASS_CNT ]; /* misses, per class */
274 : ulong evicted_per_class [ FD_ACCDB_CACHE_CLASS_CNT ];
275 : ulong preevicted_per_class [ FD_ACCDB_CACHE_CLASS_CNT ];
276 : ulong committed_new_per_class [ FD_ACCDB_CACHE_CLASS_CNT ];
277 : ulong committed_overwrite_per_class[ FD_ACCDB_CACHE_CLASS_CNT ];
278 :
279 : /* IO counters. bytes_written/read_ops/write_ops are summed across
280 : all consumer tiles; bytes_written_accdb is the bytes written by
281 : the accdb tile itself (background preevict + compaction), used to
282 : compute the prewrite ratio. */
283 : ulong bytes_read;
284 : ulong bytes_copied;
285 : ulong bytes_written;
286 : ulong bytes_written_accdb;
287 : ulong read_ops;
288 : ulong write_ops;
289 : };
290 :
291 : typedef struct fd_gui_accounts_stats fd_gui_accounts_stats_t;
292 :
293 : struct fd_gui_turbine_slot {
294 : ulong slot;
295 : long timestamp;
296 : };
297 :
298 : typedef struct fd_gui_turbine_slot fd_gui_turbine_slot_t;
299 :
300 : struct __attribute__((packed)) fd_gui_slot_history_shred_event {
301 : long timestamp;
302 : uint slot;
303 : ushort shred_idx;
304 : uchar event;
305 : };
306 :
307 : typedef struct fd_gui_slot_history_shred_event fd_gui_slot_history_shred_event_t;
308 :
309 : struct __attribute__((packed)) fd_gui_slot {
310 : ulong slot; /* this record's slot number. */
311 : ulong bank_seq; /* this block's fork discriminator (the record's own key bank_seq).*/
312 : ulong parent_bank_seq; /* parent block's fork discriminator (ULONG_MAX if unknown) */
313 : long completed_time; /* slot completion wallclock ns (LONG_MAX if unknown) */
314 : uint shred_cnt; /* slot->shred_cnt at completion */
315 : fd_hash_t block_hash; /* block hash of the slot */
316 : uchar mine:1; /* 1 if this was our leader slot */
317 : uchar is_voter:1; /* 1 if we were structurally a voter when this slot was replayed */
318 : uint vote_success; /* successful vote txn count (UINT_MAX if unknown) */
319 : uint vote_failed; /* failed vote txn count (UINT_MAX if unknown) */
320 : uint nonvote_success; /* successful nonvote txn count (UINT_MAX if unknown) */
321 : uint nonvote_failed; /* failed nonvote txn count (UINT_MAX if unknown) */
322 : uchar vote_latency_exact; /* our vote latency minus skipped slots on the landed fork, or FD_GUI_VOTE_LATENCY_NOT_VOTED if no vote landed. */
323 : uint max_compute_units;/* block compute unit limit (UINT_MAX if unknown) */
324 : uint compute_units; /* block compute units consumed (UINT_MAX if unknown) */
325 : ulong transaction_fee; /* total transaction fee (ULONG_MAX if unknown) */
326 : ulong priority_fee; /* total priority fee (ULONG_MAX if unknown) */
327 : ulong tips; /* total tips (ULONG_MAX if unknown) */
328 : ulong parent_slot; /* parent (on-fork) slot number (ULONG_MAX if unknown) */
329 : long parent_completed_time; /* parent block completion wallclock ns (LONG_MAX if unknown) */
330 : ulong vote_slot; /* most recent slot we had landed a vote for as of this slot's replay (ULONG_MAX if unknown) */
331 : uchar skip; /* one of FD_GUI_SKIP_STATUS_* */
332 : uchar level; /* one of FD_GUI_SLOT_LEVEL_* */
333 : };
334 :
335 : typedef struct fd_gui_slot fd_gui_slot_t;
336 :
337 : struct __attribute__((packed)) fd_gui_leader_slot {
338 : ulong slot; /* this record's slot number (the record's own key). */
339 : ulong bank_seq; /* this block's fork discriminator (the record's own key bank_seq). */
340 : long leader_start_time; /* wallclock ns we became leader */
341 : long leader_end_time; /* wallclock ns we stopped being leader */
342 : fd_hash_t block_hash; /* block hash of the produced block */
343 : ulong max_microblocks; /* initial max microblocks packable into the slot */
344 : uint microblocks_upper_bound; /* final/exact microblock upper bound */
345 : uint begin_microblocks; /* microblocks started (pack -> bank) */
346 : uint end_microblocks; /* microblocks ended (bank -> poh) */
347 : fd_done_packing_t scheduler_stats[ 1 ]; /* pack "done packing" record (limits, usage, results) */
348 : uchar unbecame_leader; /* 1 if we relinquished leadership for this slot */
349 : };
350 :
351 : typedef struct fd_gui_leader_slot fd_gui_leader_slot_t;
352 :
353 : struct fd_gui_slot_ranking {
354 : ulong slot;
355 : ulong value;
356 : int type;
357 : };
358 : typedef struct fd_gui_slot_ranking fd_gui_slot_ranking_t;
359 :
360 : struct fd_gui_slot_rankings {
361 : fd_gui_slot_ranking_t largest_tips [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
362 : fd_gui_slot_ranking_t largest_fees [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
363 : fd_gui_slot_ranking_t largest_rewards [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
364 : fd_gui_slot_ranking_t largest_duration [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
365 : fd_gui_slot_ranking_t largest_compute_units [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
366 : fd_gui_slot_ranking_t largest_skipped [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
367 : fd_gui_slot_ranking_t largest_rewards_per_cu [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
368 : fd_gui_slot_ranking_t smallest_tips [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
369 : fd_gui_slot_ranking_t smallest_fees [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
370 : fd_gui_slot_ranking_t smallest_rewards [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
371 : fd_gui_slot_ranking_t smallest_rewards_per_cu[ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
372 : fd_gui_slot_ranking_t smallest_duration [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
373 : fd_gui_slot_ranking_t smallest_compute_units [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
374 : fd_gui_slot_ranking_t smallest_skipped [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
375 : };
376 :
377 : typedef struct fd_gui_slot_rankings fd_gui_slot_rankings_t;
378 :
379 0 : #define FD_GUI_EPOCH_SCHED_CNT ((MAX_SLOTS_PER_EPOCH+FD_EPOCH_SLOTS_PER_ROTATION-1UL)/FD_EPOCH_SLOTS_PER_ROTATION)
380 : #define FD_GUI_EPOCH_PUB_CNT (MAX_STAKE_WEIGHTS)
381 :
382 0 : #define FD_GUI_VOTE_LATENCY_NOT_VOTED ((uchar)(UCHAR_MAX)) /* vote missing */
383 0 : #define FD_GUI_VOTE_LATENCY_MAX ((uchar)(UCHAR_MAX-1UL)) /* largest observable vote latency */
384 :
385 : struct fd_gui_epoch {
386 : ulong epoch;
387 : ulong start_slot;
388 : ulong slot_cnt; /* end_slot = start_slot + slot_cnt - 1 */
389 : long start_time; /* epoch start wallclock ns (LONG_MAX if unknown) */
390 : long end_time; /* epoch end wallclock ns (LONG_MAX if unknown) */
391 : long target_slot_duration_ns; /* protocol target slot duration */
392 : ulong my_total_slots; /* our leader slots seen this epoch */
393 : ulong my_skipped_slots; /* our skipped leader slots this epoch */
394 : ulong rankings_slot; /* one more than the largest slot processed into rankings */
395 : fd_gui_slot_rankings_t rankings [ 1 ]; /* global slot rankings */
396 : fd_gui_slot_rankings_t my_rankings[ 1 ]; /* my slots only */
397 :
398 : uchar latency_exact[ MAX_SLOTS_PER_EPOCH ]; /* skip-discounted latency or FD_GUI_VOTE_LATENCY_* */
399 : uchar is_voter [ MAX_SLOTS_PER_EPOCH ]; /* 1 if we were structurally a voter when this slot was replayed */
400 : uchar skipped [ MAX_SLOTS_PER_EPOCH ]; /* 1 if the slot was skipped on the rooted fork */
401 :
402 : fd_epoch_schedule_t epoch_schedule; /* slot<->epoch conversion (fd_slot_to_epoch) */
403 : ulong pub_cnt; /* number of deduped leader pubkeys in pub[] */
404 : ulong stakes_cnt; /* number of entries in stakes[] */
405 : uint sched[ FD_GUI_EPOCH_SCHED_CNT ]; /* rotation -> index into pub[] */
406 : fd_pubkey_t pub[ FD_GUI_EPOCH_PUB_CNT ]; /* deduped leaders */
407 : fd_vote_stake_weight_t stakes[ MAX_STAKE_WEIGHTS ];
408 : };
409 :
410 : typedef struct fd_gui_epoch fd_gui_epoch_t;
411 :
412 : struct fd_gui_ephemeral_slot {
413 : ulong slot; /* ULONG_MAX indicates invalid/evicted */
414 : long timestamp_arrival_nanos;
415 : };
416 : typedef struct fd_gui_ephemeral_slot fd_gui_ephemeral_slot_t;
417 :
418 : struct __attribute__((packed)) fd_gui_store_txn_start {
419 : ulong slot;
420 : ulong bank_seq; /* per-fork bank sequence */
421 : ulong txn_idx; /* per-(bank_seq) transaction index */
422 : uchar signature[ FD_TXN_SIGNATURE_SZ ];
423 : ulong transaction_fee;
424 : ulong priority_fee;
425 : long timestamp_arrival_nanos;
426 : long microblock_start_ns; /* absolute ns (begin tspub) */
427 : uint compute_units_requested;
428 : uint microblock_idx;
429 : uint source_ipv4;
430 : uchar source_tpu;
431 : uchar flags; /* STARTED | IS_SIMPLE_VOTE | FROM_BUNDLE */
432 : };
433 : typedef struct fd_gui_store_txn_start fd_gui_store_txn_start_t;
434 :
435 : struct __attribute__((packed)) fd_gui_store_txn_end {
436 : ulong slot;
437 : ulong bank_seq; /* per-fork bank sequence */
438 : ulong txn_idx;
439 : long timestamp_arrival_nanos;
440 : long microblock_end_ns; /* absolute ns (end tspub) */
441 : fd_txn_ns_dt_t txn_ns_dt; /* relative to microblock_start */
442 : ulong tips;
443 : uint compute_units_consumed;
444 : uint bank_idx;
445 : uint error_code;
446 : uchar flags; /* ENDED | LANDED_IN_BLOCK */
447 : };
448 : typedef struct fd_gui_store_txn_end fd_gui_store_txn_end_t;
449 :
450 : struct fd_gui_slot_txn_join {
451 : fd_gui_store_txn_start_t const * start;
452 : fd_gui_store_txn_end_t const * end;
453 : };
454 :
455 : typedef struct fd_gui_slot_txn_join fd_gui_slot_txn_join_t;
456 :
457 : struct fd_gui_txn_waterfall {
458 : long sample_time_nanos; /* wallclock ns this sample was taken. */
459 : struct {
460 : ulong quic;
461 : ulong udp;
462 : ulong gossip;
463 : ulong block_engine;
464 : ulong pack_cranked;
465 : } in;
466 :
467 : struct {
468 : ulong net_overrun;
469 : ulong quic_overrun;
470 : ulong quic_frag_drop;
471 : ulong quic_abandoned;
472 : ulong tpu_quic_invalid;
473 : ulong tpu_udp_invalid;
474 : ulong verify_overrun;
475 : ulong verify_parse;
476 : ulong verify_failed;
477 : ulong verify_duplicate;
478 : ulong dedup_duplicate;
479 : ulong resolv_lut_failed;
480 : ulong resolv_expired;
481 : ulong resolv_ancient;
482 : ulong resolv_no_ledger;
483 : ulong resolv_retained;
484 : ulong pack_invalid;
485 : ulong pack_invalid_bundle;
486 : ulong pack_expired;
487 : ulong pack_already_executed;
488 : ulong pack_retained;
489 : ulong pack_wait_full;
490 : ulong pack_leader_slow;
491 : ulong bank_invalid;
492 : ulong bank_nonce_already_advanced;
493 : ulong bank_nonce_advance_failed;
494 : ulong bank_nonce_wrong_blockhash;
495 : ulong block_success;
496 : ulong block_fail;
497 : } out;
498 : };
499 :
500 : typedef struct fd_gui_txn_waterfall fd_gui_txn_waterfall_t;
501 :
502 : struct fd_gui_tile_stats {
503 : long sample_time_nanos;
504 :
505 : ulong net_in_rx_bytes; /* Number of bytes received by the net or sock tile*/
506 : ulong quic_conn_cnt; /* Number of active QUIC connections */
507 : fd_histf_t bundle_rx_delay_hist; /* Histogram of bundle rx delay */
508 : ulong bundle_rtt_smoothed_nanos; /* RTT (nanoseconds) moving average */
509 : ulong verify_drop_cnt; /* Number of transactions dropped by verify tiles */
510 : ulong verify_total_cnt; /* Number of transactions received by verify tiles */
511 : ulong dedup_drop_cnt; /* Number of transactions dropped by dedup tile */
512 : ulong dedup_total_cnt; /* Number of transactions received by dedup tile */
513 : ulong pack_buffer_cnt; /* Number of buffered transactions in the pack tile */
514 : ulong pack_buffer_capacity; /* Total size of the pack transaction buffer */
515 : ulong bank_txn_exec_cnt; /* Number of transactions processed by the bank tile */
516 : ulong net_out_tx_bytes; /* Number of bytes sent by the net or sock tile */
517 : };
518 :
519 : typedef struct fd_gui_tile_stats fd_gui_tile_stats_t;
520 :
521 :
522 : struct fd_gui_boot_progress {
523 : uchar phase;
524 : long joining_gossip_time_nanos;
525 : struct {
526 : ulong slot;
527 : uint peer_addr;
528 : ushort peer_port;
529 : ulong total_bytes_compressed;
530 : long reset_time_nanos; /* UNIX nanosecond timestamp */
531 : long sample_time_nanos;
532 : ulong reset_cnt;
533 :
534 : ulong read_bytes_compressed;
535 : char read_path[ PATH_MAX+30UL ]; /* URL or filesystem path. 30 is fd_cstr_nlen( "https://255.255.255.255:12345/", ULONG_MAX ) */
536 :
537 : ulong decompress_bytes_decompressed;
538 : ulong decompress_bytes_compressed;
539 :
540 : ulong insert_bytes_decompressed;
541 : char insert_path[ PATH_MAX ];
542 : ulong insert_accounts_current;
543 :
544 : ulong snapwr_in_bytes_decompressed;
545 : ulong snapwr_out_bytes_decompressed;
546 : ulong snapwr_accounts_current;
547 : } loading_snapshot[ FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT ];
548 :
549 : ulong wfs_total_stake;
550 : ulong wfs_connected_stake;
551 : ulong wfs_total_peers;
552 : ulong wfs_connected_peers;
553 : ulong wfs_attempt;
554 :
555 : long catching_up_time_nanos;
556 : ulong catching_up_first_replay_slot;
557 : };
558 :
559 : typedef struct fd_gui_boot_progress fd_gui_boot_progress_t;
560 :
561 : /* Triangular-weighted moving window over per-snap deltas. Snap
562 : cadence is ~100ms, window is FD_GUI_ACCDB_WIN_SAMPLES samples
563 : (~5s). The output rate for any metric is:
564 :
565 : rate = sum( w[i] * delta[i] ) / sum( w[i] * dt[i] )
566 :
567 : where w[i] is the triangular weight (newest sample heaviest,
568 : oldest weight=1). This is fully caught up to a new rate after
569 : the window length but smoother than a boxcar (no cliff edge as
570 : samples age out). */
571 0 : #define FD_GUI_ACCDB_WIN_SAMPLES 50UL
572 :
573 : /* Per-partition triangular-weighted windows for read/write rates.
574 : Sized to match the accdb partition pool ceiling (8192). Rates
575 : are derived in fd_gui_printf via fd_gui_accdb_weighted_rate. */
576 0 : #define FD_GUI_MAX_PARTITIONS 8192UL
577 :
578 0 : #define FD_GUI_ACCDB_EMA_HALF_LIFE_NS (600L*1000L*1000L*1000L) /* 10 minutes */
579 :
580 : /* Per-tile accdb stats. At init we walk the topology and assign a
581 : slot to each tile that uses the account database (execle, execrp,
582 : replay, tower, rpc, resolv, snapwr). Each slot keeps cumulative
583 : previous values for delta computation and a triangular-weighted
584 : delta ring (same cadence / weighting as the aggregate rings). */
585 : #define FD_GUI_MAX_ACCDB_TILES 64UL
586 :
587 : /* Tile kinds. Determines which subset of metrics to read. */
588 0 : #define FD_GUI_ACCDB_TILE_KIND_RW 0 /* execle, execrp, replay, tower */
589 0 : #define FD_GUI_ACCDB_TILE_KIND_RO 1 /* rpc, resolv */
590 0 : #define FD_GUI_ACCDB_TILE_KIND_SNAPWR 2 /* snapwr (direct disk writer during snapshot load) */
591 0 : #define FD_GUI_ACCDB_TILE_KIND_ACCDB 3 /* accdb tile itself (prewrite + compaction writes) */
592 :
593 : /* 60s-history rings for the per-tile sparkline. Each bucket is the
594 : sum of per-snap deltas that fell into that bucket window.
595 : index 0 = current bucket (in-flight), older buckets follow. When
596 : a bucket interval elapses we shift right (older buckets drop off
597 : the end) and start a new index-0 bucket. 240 buckets x 250ms =
598 : 60 second window. */
599 0 : #define FD_GUI_ACCDB_SPARKLINE_SAMPLES 240UL
600 0 : #define FD_GUI_ACCDB_SPARKLINE_BUCKET_NS 250000000L
601 :
602 : struct fd_gui_accdb_stats {
603 : ulong accdb_win_idx; /* next write index */
604 : ulong accdb_win_count; /* samples filled, capped at FD_GUI_ACCDB_WIN_SAMPLES */
605 : long accdb_win_dt_nanos [ FD_GUI_ACCDB_WIN_SAMPLES ];
606 :
607 : /* Aggregate delta rings (units per snap). */
608 : ulong agg_acquired_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
609 : ulong agg_acquired_writable_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
610 : ulong agg_bytes_read_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
611 : ulong agg_bytes_copied_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
612 : ulong agg_bytes_written_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
613 : ulong agg_bytes_written_accdb_win[FD_GUI_ACCDB_WIN_SAMPLES ];
614 : ulong agg_read_ops_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
615 : ulong agg_write_ops_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
616 : ulong agg_relocated_bytes_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
617 : ulong agg_misses_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
618 :
619 : /* Per-class delta rings (units per snap). */
620 : ulong class_acq_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
621 : ulong class_acq_wr_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
622 : ulong class_not_found_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
623 : ulong class_evicted_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
624 : ulong class_preevicted_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
625 : ulong class_commit_new_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
626 : ulong class_commit_over_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
627 :
628 : ulong partition_cnt; /* live count from accdb_shmem; <= FD_GUI_MAX_PARTITIONS */
629 : ulong partition_read_ops_win [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
630 : ulong partition_bytes_read_win [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
631 : ulong partition_write_ops_win [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
632 : ulong partition_bytes_written_win[FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
633 :
634 : /* Per-partition snapshots (most recent values, for non-rate fields:
635 : offset, layer, write_offset, bytes_freed, ticks, compaction state). */
636 : fd_accdb_shmem_partition_info_t partitions[ FD_GUI_MAX_PARTITIONS ];
637 :
638 : /* Cumulative counters from the previous snap for delta computation. */
639 : ulong partition_prev_read_ops [ FD_GUI_MAX_PARTITIONS ];
640 : ulong partition_prev_bytes_read [ FD_GUI_MAX_PARTITIONS ];
641 : ulong partition_prev_write_ops [ FD_GUI_MAX_PARTITIONS ];
642 : ulong partition_prev_bytes_written[FD_GUI_MAX_PARTITIONS ];
643 :
644 : /* Per-tier (i.e. hot, warm, cold) EMAs (10 min half-life) used to project when the next
645 : compaction will trigger. */
646 : long tier_sample_nanos;
647 : fd_gui_ema_t tier_fill_bps_ema[ FD_ACCDB_COMPACTION_LAYER_CNT ];
648 : fd_gui_ema_t tier_free_bps_ema[ FD_ACCDB_COMPACTION_LAYER_CNT ];
649 : double next_compaction_remaining_secs;
650 : ulong next_compaction_partition_idx;
651 :
652 : ulong accdb_tile_cnt;
653 : ushort accdb_tile_topo_idx [ FD_GUI_MAX_ACCDB_TILES ]; /* index into topo->tiles */
654 : uchar accdb_tile_kind [ FD_GUI_MAX_ACCDB_TILES ];
655 :
656 : /* Most-recent cumulative values per tile, plus the snapshot from
657 : the previous snap for delta computation. */
658 : ulong tile_cur_acquired [ FD_GUI_MAX_ACCDB_TILES ];
659 : ulong tile_cur_acquired_writable [ FD_GUI_MAX_ACCDB_TILES ];
660 : ulong tile_cur_bytes_read [ FD_GUI_MAX_ACCDB_TILES ];
661 : ulong tile_cur_bytes_copied [ FD_GUI_MAX_ACCDB_TILES ];
662 : ulong tile_cur_bytes_written [ FD_GUI_MAX_ACCDB_TILES ];
663 : ulong tile_cur_read_ops [ FD_GUI_MAX_ACCDB_TILES ];
664 : ulong tile_cur_write_ops [ FD_GUI_MAX_ACCDB_TILES ];
665 : ulong tile_cur_misses [ FD_GUI_MAX_ACCDB_TILES ];
666 : ulong tile_cur_evicted [ FD_GUI_MAX_ACCDB_TILES ];
667 : ulong tile_cur_committed [ FD_GUI_MAX_ACCDB_TILES ];
668 : ulong tile_cur_acquire_calls [ FD_GUI_MAX_ACCDB_TILES ];
669 : uchar tile_cur_status [ FD_GUI_MAX_ACCDB_TILES ]; /* 1=running, 2=shutdown */
670 :
671 : ulong tile_prev_acquired [ FD_GUI_MAX_ACCDB_TILES ];
672 : ulong tile_prev_acquired_writable[ FD_GUI_MAX_ACCDB_TILES ];
673 : ulong tile_prev_bytes_read [ FD_GUI_MAX_ACCDB_TILES ];
674 : ulong tile_prev_bytes_copied [ FD_GUI_MAX_ACCDB_TILES ];
675 : ulong tile_prev_bytes_written [ FD_GUI_MAX_ACCDB_TILES ];
676 : ulong tile_prev_read_ops [ FD_GUI_MAX_ACCDB_TILES ];
677 : ulong tile_prev_write_ops [ FD_GUI_MAX_ACCDB_TILES ];
678 : ulong tile_prev_misses [ FD_GUI_MAX_ACCDB_TILES ];
679 : ulong tile_prev_evicted [ FD_GUI_MAX_ACCDB_TILES ];
680 : ulong tile_prev_committed [ FD_GUI_MAX_ACCDB_TILES ];
681 : ulong tile_prev_acquire_calls [ FD_GUI_MAX_ACCDB_TILES ];
682 :
683 : /* Per-tile delta rings. */
684 : ulong tile_acquired_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
685 : ulong tile_acquired_writable_win[ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
686 : ulong tile_bytes_read_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
687 : ulong tile_bytes_copied_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
688 : ulong tile_bytes_written_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
689 : ulong tile_read_ops_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
690 : ulong tile_write_ops_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
691 : ulong tile_misses_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
692 : ulong tile_evicted_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
693 : ulong tile_committed_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
694 : ulong tile_acquire_calls_win [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
695 :
696 : long tile_sparkline_bucket_start_nanos [ FD_GUI_MAX_ACCDB_TILES ];
697 : ulong tile_sparkline_acq_bucket [ FD_GUI_MAX_ACCDB_TILES ];
698 : ulong tile_sparkline_acq_wr_bucket [ FD_GUI_MAX_ACCDB_TILES ];
699 : /* Per-second rates (units/second) for the last N completed buckets.
700 : Newest at index 0, oldest at the end. Filled lazily as snaps
701 : complete each bucket interval. */
702 : double tile_sparkline_acq_history [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_SPARKLINE_SAMPLES ];
703 : double tile_sparkline_acq_wr_history [ FD_GUI_MAX_ACCDB_TILES ][ FD_GUI_ACCDB_SPARKLINE_SAMPLES ];
704 : ulong tile_sparkline_count [ FD_GUI_MAX_ACCDB_TILES ]; /* completed buckets, capped at FD_GUI_ACCDB_SPARKLINE_SAMPLES */
705 : };
706 :
707 : typedef struct fd_gui_accdb_stats fd_gui_accdb_stats_t;
708 :
709 : /* fd_gui_summary_t holds the aggregate node-level state rendered by the
710 : "summary" websocket topic and the terminal dashboard. */
711 :
712 : struct fd_gui_summary {
713 : fd_pubkey_t identity_key[ 1 ];
714 : int has_vote_key;
715 : fd_pubkey_t vote_key[ 1 ];
716 : char vote_key_base58[ FD_BASE58_ENCODED_32_SZ ];
717 : char identity_key_base58[ FD_BASE58_ENCODED_32_SZ ];
718 :
719 : int is_full_client;
720 : char const * version;
721 : char const * cluster;
722 : char accounts_database_path[ PATH_MAX ];
723 : char gui_database_path [ PATH_MAX ];
724 :
725 : char wfs_bank_hash[ FD_BASE58_ENCODED_32_SZ ];
726 : ushort expected_shred_version;
727 : int wfs_enabled;
728 :
729 : ulong vote_distance;
730 : int vote_state;
731 :
732 : long startup_time_nanos;
733 :
734 : fd_gui_boot_progress_t boot_progress;
735 : fd_gui_boot_progress_t prev_boot_progress;
736 :
737 : int schedule_strategy;
738 :
739 : ulong identity_account_balance;
740 : ulong vote_account_balance;
741 : ushort vote_commission;
742 : ulong estimated_slot_duration_nanos;
743 :
744 : ulong sock_tile_cnt;
745 : ulong net_tile_cnt;
746 : ulong quic_tile_cnt;
747 : ulong verify_tile_cnt;
748 : ulong resolh_tile_cnt;
749 : ulong resolv_tile_cnt;
750 : ulong bank_tile_cnt;
751 : ulong execle_tile_cnt;
752 : ulong execrp_tile_cnt;
753 : ulong shred_tile_cnt;
754 :
755 : ulong slot_rooted;
756 : ulong slot_optimistically_confirmed;
757 : ulong slot_estimated;
758 : ulong slot_caught_up;
759 : ulong slot_repair;
760 : ulong slot_turbine;
761 : ulong slot_reset;
762 : ulong slot_storage;
763 : ulong slot_tower;
764 : ulong slot_tower_bank_seq; /* tracks canonical fork frontier */
765 : ulong active_fork_cnt;
766 :
767 : struct {
768 : ulong epoch;
769 : ulong skipped;
770 : ulong total;
771 : } skip_rate[ 2 ];
772 :
773 : fd_gui_ephemeral_slot_t slots_max_turbine[ FD_GUI_TURBINE_SLOT_HISTORY_SZ+1UL ];
774 : fd_gui_ephemeral_slot_t slots_max_repair [ FD_GUI_REPAIR_SLOT_HISTORY_SZ +1UL ];
775 :
776 : /* catchup_* is run-length encoded. i.e. adjacent pairs represent
777 : contiguous runs */
778 : ulong catch_up_turbine[ FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ ];
779 : ulong catch_up_turbine_sz;
780 :
781 : ulong catch_up_repair[ FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ ];
782 : ulong catch_up_repair_sz;
783 :
784 : ulong estimated_tps_history_idx;
785 : struct {
786 : ulong vote_failed;
787 : ulong vote_success;
788 : ulong nonvote_success;
789 : ulong nonvote_failed;
790 : } estimated_tps_history[ FD_GUI_TPS_HISTORY_SAMPLE_CNT ];
791 :
792 : fd_gui_network_stats_t network_stats_current[ 1 ];
793 : fd_gui_network_stats_t network_stats_prev [ 1 ];
794 : int network_stats_has_prev;
795 :
796 : /* EMA-smoothed network throughput (bytes/sec) with a 1-second
797 : half-life. */
798 : fd_gui_ema_t ingress_ema[ FD_GUI_NET_PROTO_CNT ];
799 : fd_gui_ema_t egress_ema[ FD_GUI_NET_PROTO_CNT ];
800 : long net_rate_prev_ts;
801 : fd_gui_rate_entry_t * ingress_maxq;
802 : fd_gui_rate_entry_t * egress_maxq;
803 :
804 : fd_gui_accdb_stats_t accdb[ 1 ];
805 :
806 : fd_gui_accounts_stats_t accounts_stats_reference[ 1 ];
807 : fd_gui_accounts_stats_t accounts_stats_current [ 1 ];
808 : int accounts_stats_have_reference;
809 :
810 : fd_gui_txn_waterfall_t txn_waterfall_reference[ 1 ];
811 : fd_gui_txn_waterfall_t txn_waterfall_current [ 1 ];
812 :
813 : fd_gui_tile_stats_t tile_stats_reference[ 1 ];
814 : fd_gui_tile_stats_t tile_stats_current [ 1 ];
815 :
816 : ulong progcache_history_idx;
817 : ulong progcache_hits_history [ FD_GUI_PROGCACHE_HISTORY_CNT ];
818 : ulong progcache_lookups_history[ FD_GUI_PROGCACHE_HISTORY_CNT ];
819 : ulong progcache_hits_1min;
820 : ulong progcache_lookups_1min;
821 :
822 : fd_gui_tile_timers_t tile_timers_reference[ FD_TOPO_MAX_TILES ];
823 : fd_gui_tile_timers_t tile_timers_current [ FD_TOPO_MAX_TILES ];
824 : fd_gui_tile_timers_hist_t tile_timers_packed [ FD_TOPO_MAX_TILES ];
825 :
826 : /* Topo tile indices in display order, built once on init. */
827 : ulong tile[ FD_TOPO_MAX_TILES ];
828 : ulong tile_cnt;
829 : };
830 :
831 : typedef struct fd_gui_summary fd_gui_summary_t;
832 :
833 : struct fd_gui {
834 : fd_http_server_t * http;
835 : fd_topo_t const * topo;
836 : fd_accdb_shmem_t const * accdb_shmem;
837 :
838 : void * db; /* GUI database */
839 : void * hist;
840 :
841 : double tick_per_ns;
842 : ulong tile_cnt;
843 :
844 : long next_sample_1sec;
845 : long next_sample_200millis;
846 : long next_sample_100millis;
847 : long next_sample_50millis;
848 : long next_sample_40millis;
849 : long next_sample_25millis;
850 : long next_sample_10millis;
851 :
852 : int leader_active;
853 :
854 : fd_gui_summary_t summary;
855 :
856 : /* Scratch record for the synthesized skipped slots returned by
857 : fd_gui_slot_get_canon_safe. */
858 : fd_gui_slot_t skipped_scratch[ 1 ];
859 :
860 : /* used for estimating slot duration */
861 : fd_gui_turbine_slot_t turbine_slots[ FD_GUI_TURBINE_RECV_TIMESTAMPS ];
862 :
863 : /* Reusable scratch for reassembling a single slot's transactions at
864 : query time (fd_gui_printf_slot_transactions_request). */
865 : struct {
866 : fd_gui_store_txn_start_t starts[ FD_MAX_TXN_PER_SLOT ];
867 : fd_gui_store_txn_end_t ends [ FD_MAX_TXN_PER_SLOT ];
868 : fd_gui_slot_txn_join_t joined[ FD_MAX_TXN_PER_SLOT ];
869 : } slot_txn_scratch;
870 :
871 : struct {
872 : ulong landed_slot;
873 : ulong landed_bank_seq;
874 : ulong voted_slot;
875 : } landed_votes[ FD_GUI_LANDED_VOTE_MAX ];
876 : ulong landed_vote_cnt;
877 :
878 : struct {
879 : int has_block_engine;
880 : char name[ 16 ];
881 : char url[ 256 ];
882 : char ip_cstr[ 40 ]; /* IPv4 or IPv6 cstr */
883 : int status;
884 : } block_engine;
885 :
886 : struct {
887 : /* The epoch we are currently in, advanced at epoch_info ingest. */
888 : ulong current_epoch;
889 : ulong stored_epoch_cnt;
890 :
891 : int has_epoch_schedule;
892 : fd_epoch_schedule_t epoch_schedule;
893 :
894 : uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) lsched_scratch[ FD_EPOCH_LEADERS_FOOTPRINT(MAX_STAKE_WEIGHTS, MAX_SLOTS_PER_EPOCH) ];
895 : fd_vote_stake_weight_t stakes_scratch[ MAX_STAKE_WEIGHTS ];
896 : } epoch;
897 :
898 : fd_gui_peers_ctx_t * peers; /* full-client */
899 :
900 : struct {
901 : ulong leader_shred_cnt; /* A gauge counting the number of leader shreds seen on the SHRED_OUT link. Resets at
902 : the end of a leader slot. This works because leader fecs are published in order. */
903 :
904 : /* The wallclock-ns timestamp up to which shred events have already
905 : been pushed to clients. */
906 : long broadcast_watermark_ns;
907 : } shreds;
908 : };
909 :
910 : typedef struct fd_gui fd_gui_t;
911 :
912 : FD_PROTOTYPES_BEGIN
913 :
914 : /* fd_gui_tile_timers_diff computes the compact, display-ready diff of a
915 : single tile's timers between two raw cumulative samples `prev` and
916 : `cur`, writing it into `out`. */
917 :
918 : void
919 : fd_gui_tile_timers_diff( fd_gui_tile_timers_hist_t * out,
920 : fd_gui_tile_timers_t const * prev,
921 : fd_gui_tile_timers_t const * cur,
922 : ulong tile_idx,
923 : long sample_time_nanos );
924 :
925 : FD_FN_CONST ulong
926 : fd_gui_align( void );
927 :
928 : ulong
929 : fd_gui_footprint( ulong tile_cnt );
930 :
931 : void *
932 : fd_gui_new( void * shmem,
933 : fd_http_server_t * http,
934 : char const * version,
935 : char const * cluster,
936 : uchar const * identity_key,
937 : int has_vote_key,
938 : uchar const * vote_key,
939 : int is_full_client,
940 : int snapshots_enabled,
941 : int is_voting,
942 : int schedule_strategy,
943 : char const * wfs_expected_bank_hash_cstr,
944 : ushort expected_shred_version,
945 : char const * accounts_database_path,
946 : char const * gui_database_path,
947 : void * db,
948 : fd_topo_t const * topo,
949 : fd_accdb_shmem_t const * accdb_shmem,
950 : long now );
951 :
952 : fd_gui_t *
953 : fd_gui_join( void * shmem );
954 :
955 : void
956 : fd_gui_set_identity( fd_gui_t * gui,
957 : uchar const * identity_pubkey );
958 :
959 : void
960 : fd_gui_ws_open( fd_gui_t * gui,
961 : ulong conn_id,
962 : long now );
963 :
964 : int
965 : fd_gui_ws_message( fd_gui_t * gui,
966 : ulong ws_conn_id,
967 : uchar const * data,
968 : ulong data_len );
969 :
970 : void
971 : fd_gui_became_leader( fd_gui_t * gui,
972 : ulong slot,
973 : long start_time_nanos,
974 : long end_time_nanos,
975 : ulong max_compute_units,
976 : ulong max_microblocks,
977 : ulong bank_seq );
978 :
979 : void
980 : fd_gui_unbecame_leader( fd_gui_t * gui,
981 : ulong _slot,
982 : fd_done_packing_t const * done_packing,
983 : long now );
984 :
985 : void
986 : fd_gui_microblock_execution_begin( fd_gui_t * gui,
987 : long tspub_ns,
988 : ulong _slot,
989 : fd_txn_e_t * txns,
990 : ulong txn_cnt,
991 : uint microblock_idx,
992 : ulong pack_txn_idx,
993 : ulong bank_seq,
994 : long now );
995 :
996 : void
997 : fd_gui_microblock_execution_end( fd_gui_t * gui,
998 : long tspub_ns,
999 : ulong bank_idx,
1000 : ulong _slot,
1001 : ulong txn_cnt,
1002 : fd_txn_p_t * txns,
1003 : ulong pack_txn_idx,
1004 : fd_txn_ns_dt_t txn_ns_dt,
1005 : ulong tips,
1006 : ulong bank_seq,
1007 : long now );
1008 :
1009 : int
1010 : fd_gui_poll( fd_gui_t * gui, long now );
1011 :
1012 : void
1013 : fd_gui_handle_block_engine_update( fd_gui_t * gui,
1014 : fd_bundle_block_engine_update_t const * update );
1015 :
1016 : void
1017 : fd_gui_handle_shred( fd_gui_t * gui,
1018 : ulong slot,
1019 : ulong shred_idx,
1020 : int is_turbine,
1021 : long tsorig,
1022 : long now );
1023 :
1024 : void
1025 : fd_gui_handle_leader_fec( fd_gui_t * gui,
1026 : ulong slot,
1027 : ulong fec_shred_cnt,
1028 : int is_end_of_slot,
1029 : long tsorig,
1030 : long now );
1031 :
1032 : void
1033 : fd_gui_handle_exec_txn_done( fd_gui_t * gui,
1034 : ulong slot,
1035 : ulong start_shred_idx,
1036 : ulong end_shred_idx,
1037 : long tsorig_ns,
1038 : long tspub_ns,
1039 : long now );
1040 :
1041 : void
1042 : fd_gui_handle_repair_slot( fd_gui_t * gui, ulong slot, long now );
1043 :
1044 : void
1045 : fd_gui_handle_repair_request( fd_gui_t * gui, ulong slot, ulong shred_idx, long now );
1046 :
1047 : void
1048 : fd_gui_handle_snapshot_update( fd_gui_t * gui,
1049 : fd_snapct_update_t const * msg );
1050 :
1051 : void
1052 : fd_gui_stage_snapshot_manifest( fd_gui_t * gui,
1053 : fd_snapshot_manifest_t const * manifest );
1054 :
1055 : void
1056 : fd_gui_handle_epoch_info( fd_gui_t * gui,
1057 : fd_epoch_info_msg_t const * epoch_info,
1058 : long now );
1059 :
1060 : void
1061 : fd_gui_handle_tower_update( fd_gui_t * gui,
1062 : fd_tower_slot_done_t const * msg,
1063 : long now );
1064 :
1065 : void
1066 : fd_gui_handle_replay_update( fd_gui_t * gui,
1067 : fd_replay_slot_completed_t const * slot_completed,
1068 : ulong vote_slot,
1069 : long now );
1070 :
1071 : void
1072 : fd_gui_stage_landed_vote( fd_gui_t * gui,
1073 : ulong landed_slot,
1074 : ulong landed_bank_seq,
1075 : ulong voted_slot );
1076 :
1077 : void
1078 : fd_gui_handle_genesis_hash( fd_gui_t * gui,
1079 : fd_hash_t const * msg );
1080 :
1081 : /* fd_gui_handle_root_advanced is invoked on REPLAY_SIG_ROOT_ADVANCED.
1082 : It roots the (slot, bank_seq) fork and updates the dependent state. */
1083 : void
1084 : fd_gui_handle_root_advanced( fd_gui_t * gui,
1085 : ulong slot,
1086 : ulong bank_seq,
1087 : long now );
1088 :
1089 : /* fd_gui_handle_oc_advanced is invoked on REPLAY_SIG_OC_ADVANCED. It
1090 : marks the (slot, bank_seq) fork optimistically confirmed. */
1091 : void
1092 : fd_gui_handle_oc_advanced( fd_gui_t * gui,
1093 : ulong slot,
1094 : ulong bank_seq,
1095 : long now );
1096 :
1097 : /* fd_gui_slot_get_canon_safe resolves slot number `_slot` on the
1098 : canonical fork and returns a renderable record. */
1099 : fd_gui_slot_t *
1100 : fd_gui_slot_get_canon_safe( fd_gui_t * gui, ulong _slot );
1101 :
1102 :
1103 : /* fd_gui_epoch returns the DB EPOCH record for `epoch`, or NULL if no
1104 : record for that epoch is durable in the store. The returned pointer
1105 : may be written in place. */
1106 : static inline fd_gui_epoch_t *
1107 0 : fd_gui_epoch( fd_gui_t * gui, ulong epoch ) {
1108 0 : fd_gui_hist_epoch_key_t key[ 1 ]; key->epoch = epoch;
1109 0 : return (fd_gui_epoch_t *)fd_gui_hist_kv_get( gui, FD_GUI_HIST_EPOCH, key );
1110 0 : }
1111 :
1112 : /* fd_gui_current_epoch returns the record for the current epoch,
1113 : or NULL if no epoch has been ingested yet. */
1114 : static inline fd_gui_epoch_t *
1115 0 : fd_gui_current_epoch( fd_gui_t * gui ) {
1116 0 : if( FD_UNLIKELY( gui->epoch.current_epoch==ULONG_MAX ) ) return NULL;
1117 0 : return fd_gui_epoch( gui, gui->epoch.current_epoch );
1118 0 : }
1119 :
1120 : /* fd_gui_epoch_get_or_create returns the mutable DB EPOCH record
1121 : for `epoch`, creating it if none exists yet. */
1122 : static inline fd_gui_epoch_t *
1123 : fd_gui_epoch_get_or_create( fd_gui_t * gui,
1124 : ulong epoch,
1125 0 : int * created_out ) {
1126 0 : fd_gui_epoch_t * rec = fd_gui_epoch( gui, epoch );
1127 0 : if( FD_LIKELY( rec ) ) { if( created_out ) *created_out = 0; return rec; }
1128 :
1129 0 : fd_gui_hist_epoch_key_t key[ 1 ]; key->epoch = epoch;
1130 0 : rec = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_EPOCH, key );
1131 0 : if( FD_UNLIKELY( !rec ) ) return NULL;
1132 0 : gui->epoch.stored_epoch_cnt++; /* account the new epoch on successful creation only */
1133 0 : if( created_out ) *created_out = 1;
1134 0 : return rec;
1135 0 : }
1136 :
1137 : /* fd_gui_first_replay_slot returns the lowest slot number the validator
1138 : has authoritative knowledge of during this run. */
1139 : static inline ulong
1140 0 : fd_gui_first_replay_slot( fd_gui_t const * gui ) {
1141 0 : ulong slot_incremental = gui->summary.boot_progress.loading_snapshot[ FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX ].slot;
1142 0 : ulong slot_full = gui->summary.boot_progress.loading_snapshot[ FD_GUI_BOOT_PROGRESS_FULL_SNAPSHOT_IDX ].slot;
1143 0 : return fd_ulong_if( slot_incremental!=ULONG_MAX, slot_incremental+1UL,
1144 0 : fd_ulong_if( slot_full!=ULONG_MAX, slot_full+1UL, ULONG_MAX ) );
1145 0 : }
1146 :
1147 : /* fd_gui_slot_leader_get_or_create returns the mutable returns the
1148 : DB LEADER_SLOT record for (slot, bank_seq), creating it if none
1149 : exists yet. */
1150 :
1151 : static inline fd_gui_leader_slot_t *
1152 : fd_gui_slot_leader_get_or_create( fd_gui_t * gui,
1153 : ulong slot,
1154 0 : ulong bank_seq ) {
1155 0 : if( FD_UNLIKELY( !gui->db || slot==ULONG_MAX ) ) return NULL;
1156 :
1157 0 : fd_gui_hist_leader_slot_key_t key = { .slot = slot, .bank_seq = bank_seq };
1158 :
1159 0 : fd_gui_leader_slot_t * rec = fd_gui_hist_kv_get( gui, FD_GUI_HIST_LEADER_SLOT, &key );
1160 0 : if( FD_LIKELY( rec ) ) return rec;
1161 :
1162 0 : fd_gui_leader_slot_t * seed = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_LEADER_SLOT, &key );
1163 0 : if( FD_UNLIKELY( !seed ) ) return NULL;
1164 :
1165 0 : *seed = (fd_gui_leader_slot_t){
1166 0 : .slot = slot,
1167 0 : .bank_seq = bank_seq,
1168 0 : .leader_start_time = LONG_MAX,
1169 0 : .leader_end_time = LONG_MAX,
1170 0 : .max_microblocks = ULONG_MAX,
1171 0 : .microblocks_upper_bound = UINT_MAX,
1172 0 : .begin_microblocks = 0U,
1173 0 : .end_microblocks = 0U,
1174 0 : .unbecame_leader = 0
1175 0 : };
1176 0 : return seed;
1177 0 : }
1178 :
1179 : /* fd_gui_slot_leader_get_any returns the DB record for key matching
1180 : (lslot, *), regardless of fork, or NULL if no record exists for lslot. */
1181 :
1182 : static inline fd_gui_leader_slot_t *
1183 0 : fd_gui_slot_leader_get_any( fd_gui_t * gui, ulong _lslot ) {
1184 0 : if( FD_UNLIKELY( !gui->db || _lslot==ULONG_MAX ) ) return NULL;
1185 0 : return fd_gui_hist_kv_get_slot_any( gui, FD_GUI_HIST_LEADER_SLOT, _lslot );
1186 0 : }
1187 :
1188 : /* fd_gui_slot_get returns the DB record for the exact key
1189 : (slot, bank_seq), or NULL if none exists. */
1190 :
1191 : static inline fd_gui_slot_t *
1192 0 : fd_gui_slot_get( fd_gui_t * gui, ulong slot, ulong bank_seq ) {
1193 0 : if( FD_UNLIKELY( slot==ULONG_MAX || bank_seq==ULONG_MAX ) ) return NULL;
1194 0 : fd_gui_hist_slot_key_t key;
1195 0 : key.slot = slot; key.bank_seq = bank_seq;
1196 0 : return (fd_gui_slot_t *)fd_gui_hist_kv_get( gui, FD_GUI_HIST_SLOT, &key );
1197 0 : }
1198 :
1199 : /* fd_gui_slot_parent_get returns the DB record for the parent of `c` on
1200 : c's own fork or NULL if the parent is unknown or has no record. */
1201 :
1202 : static inline fd_gui_slot_t *
1203 0 : fd_gui_slot_parent_get( fd_gui_t * gui, fd_gui_slot_t const * c ) {
1204 0 : if( FD_UNLIKELY( !c ) ) return NULL;
1205 0 : return fd_gui_slot_get( gui, c->parent_slot, c->parent_bank_seq );
1206 0 : }
1207 :
1208 : /* fd_gui_slot_get_canon returns the DB record for slot number `_slot`
1209 : on the canonical fork, or NULL if `_slot` is skipped, above the
1210 : canonical tip, or the tip is not yet resolved. The canonical fork is
1211 : the consensus fork chosen by tower fork choice. */
1212 :
1213 : static inline fd_gui_slot_t *
1214 0 : fd_gui_slot_get_canon( fd_gui_t * gui, ulong _slot ) {
1215 0 : if( FD_UNLIKELY( _slot==ULONG_MAX ) ) return NULL;
1216 :
1217 : /* At or below the root the canonical fork has settled, meaning there
1218 : should be exactly one NOT_SKIPPED entry per slot. */
1219 0 : if( FD_LIKELY( gui->summary.slot_rooted!=ULONG_MAX && _slot<=gui->summary.slot_rooted ) ) {
1220 0 : fd_gui_hist_kv_slot_iter_t it[ 1 ];
1221 0 : for( fd_gui_hist_kv_iter_begin( gui, it, FD_GUI_HIST_SLOT, _slot ); it->rec; fd_gui_hist_kv_iter_next( it ) ) {
1222 0 : fd_gui_slot_t const * rec = (fd_gui_slot_t const *)it->rec;
1223 0 : if( FD_LIKELY( rec->skip==FD_GUI_SKIP_STATUS_NOT_SKIPPED ) ) return fd_gui_slot_get( gui, _slot, it->bank_seq );
1224 0 : }
1225 0 : return NULL; /* skipped on the canonical fork, or no record */
1226 0 : }
1227 :
1228 : /* Otherwise do a fork-walk from the canonical frontier. */
1229 0 : ulong slot = gui->summary.slot_tower;
1230 0 : ulong bank_seq = gui->summary.slot_tower_bank_seq;
1231 0 : for(;;) {
1232 0 : if( FD_UNLIKELY( slot==ULONG_MAX || bank_seq==ULONG_MAX ) ) return NULL;
1233 0 : if( FD_UNLIKELY( slot<_slot ) ) return NULL; /* walked past _slot: skipped or above-tip */
1234 0 : fd_gui_slot_t * n = fd_gui_slot_get( gui, slot, bank_seq );
1235 0 : if( FD_UNLIKELY( !n ) ) return NULL; /* evicted / no record */
1236 0 : if( FD_LIKELY( slot==_slot ) ) return n;
1237 0 : slot = n->parent_slot;
1238 0 : bank_seq = n->parent_bank_seq;
1239 0 : }
1240 0 : }
1241 :
1242 : /* fd_gui_slot_get_any returns the DB record for slot number `_slot`
1243 : without any fork awareness: it is a simple store lookup that returns
1244 : the first record that exists for `_slot`. */
1245 :
1246 : static inline fd_gui_slot_t *
1247 0 : fd_gui_slot_get_any( fd_gui_t * gui, ulong _slot ) {
1248 0 : if( FD_UNLIKELY( _slot==ULONG_MAX ) ) return NULL;
1249 :
1250 0 : fd_gui_hist_kv_slot_iter_t it[ 1 ];
1251 0 : fd_gui_hist_kv_iter_begin( gui, it, FD_GUI_HIST_SLOT, _slot );
1252 0 : if( FD_UNLIKELY( !it->rec ) ) return NULL;
1253 0 : return fd_gui_slot_get( gui, _slot, it->bank_seq );
1254 0 : }
1255 :
1256 : /* fd_gui_slot_is_ancestor returns 1 if anc is known to be an ancestor
1257 : of slot and neither slot is skipped, 0 otherwise. */
1258 :
1259 : static inline int
1260 0 : fd_gui_slot_is_ancestor( fd_gui_t * gui, ulong anc, ulong slot ) {
1261 : /* Walk the canonical fork from `slot` down to the root by slot number,
1262 : hopping the (parent_slot, parent_bank_seq) edge. */
1263 0 : fd_gui_slot_t * c = fd_gui_slot_get_canon( gui, slot );
1264 0 : ulong cslot = slot;
1265 0 : while( c ) {
1266 0 : if( FD_UNLIKELY( cslot==anc ) ) return 1;
1267 0 : if( FD_UNLIKELY( cslot<anc ) ) return 0; /* walked past anc on this fork */
1268 0 : ulong pslot = c->parent_slot;
1269 0 : c = fd_gui_slot_parent_get( gui, c );
1270 0 : cslot = pslot;
1271 0 : }
1272 0 : return 0;
1273 0 : }
1274 :
1275 : /* fd_gui_slot_skipped_get_parent returns the largest slot number
1276 : smaller than slot that is not skipped. */
1277 :
1278 : static inline ulong
1279 0 : fd_gui_slot_skipped_get_parent( fd_gui_t * gui, ulong slot ) {
1280 0 : fd_gui_slot_t * c = fd_gui_slot_get_canon( gui, gui->summary.slot_tower );
1281 0 : while( c ) {
1282 0 : ulong pslot = c->parent_slot;
1283 0 : fd_gui_slot_t * p = fd_gui_slot_parent_get( gui, c );
1284 0 : if( FD_UNLIKELY( p && pslot<slot ) ) return pslot;
1285 0 : c = p;
1286 0 : }
1287 0 : return ULONG_MAX;
1288 0 : }
1289 :
1290 : /* fd_gui_slot_is_skipped returns 1 if `slot` is skipped on the fork
1291 : whose tip is (des, des_bank_seq) and whose root is `root`, 0
1292 : otherwise. */
1293 :
1294 : static inline int
1295 0 : fd_gui_slot_is_skipped( fd_gui_t * gui, ulong root, ulong des, ulong des_bank_seq, ulong slot ) {
1296 0 : fd_gui_slot_t * c = fd_gui_slot_get( gui, des, des_bank_seq );
1297 0 : ulong cslot = des;
1298 0 : while( c ) {
1299 0 : if( FD_UNLIKELY( root==cslot ) ) return 0; /* on the fork, not skipped */
1300 0 : ulong pslot = c->parent_slot;
1301 0 : fd_gui_slot_t * p = fd_gui_slot_parent_get( gui, c );
1302 0 : if( FD_UNLIKELY( p && pslot<slot && cslot>slot ) ) return 1; /* in-between two on-fork records, skipped */
1303 0 : c = p;
1304 0 : cslot = pslot;
1305 0 : }
1306 0 : return 0; /* slot not between root and des, or is unknown */
1307 0 : }
1308 :
1309 : /* fd_gui_get_epoch_by_slot returns the mutable DB EPOCH record covering
1310 : `_slot` (resolved via the epoch schedule), or NULL if no epoch
1311 : schedule is known yet or no record for that epoch is present. */
1312 :
1313 : static inline fd_gui_epoch_t *
1314 0 : fd_gui_get_epoch_by_slot( fd_gui_t * gui, ulong _slot ) {
1315 0 : if( FD_UNLIKELY( !gui->epoch.has_epoch_schedule ) ) return NULL;
1316 0 : ulong epoch = fd_slot_to_epoch( &gui->epoch.epoch_schedule, _slot, NULL );
1317 0 : return fd_gui_epoch( gui, epoch );
1318 0 : }
1319 :
1320 : /* fd_gui_get_slot_leader returns the leader pubkey scheduled for
1321 : `_slot`, or NULL if `epoch` is NULL, `_slot` is outside the epoch,
1322 : or the schedule index is indeterminate. */
1323 :
1324 : static inline fd_pubkey_t const *
1325 0 : fd_gui_get_slot_leader( fd_gui_epoch_t const * epoch, ulong _slot ) {
1326 0 : if( FD_UNLIKELY( !epoch ) ) return NULL;
1327 0 : if( FD_UNLIKELY( _slot<epoch->start_slot || _slot>=epoch->start_slot+epoch->slot_cnt ) ) return NULL;
1328 0 : ulong rot = (_slot - epoch->start_slot)/FD_EPOCH_SLOTS_PER_ROTATION;
1329 0 : uint idx = epoch->sched[ rot ];
1330 0 : if( FD_UNLIKELY( idx>=epoch->pub_cnt ) ) return NULL; /* indeterminate */
1331 0 : return &epoch->pub[ idx ];
1332 0 : }
1333 :
1334 : static inline int
1335 0 : fd_gui_slot_is_mine( fd_gui_t * gui, ulong _slot ) {
1336 0 : fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
1337 0 : fd_pubkey_t const * slot_leader = fd_gui_get_slot_leader( epoch, _slot );
1338 0 : if( FD_UNLIKELY( !slot_leader ) ) return 0;
1339 0 : return !memcmp( slot_leader->uc, gui->summary.identity_key->uc, 32UL );
1340 0 : }
1341 :
1342 : /* fd_gui_slot_get_or_create returns the mutable returns the mutable DB
1343 : SLOT record for (_slot, bank_seq), creating it if none exists yet. */
1344 :
1345 : static inline fd_gui_slot_t *
1346 : fd_gui_slot_get_or_create( fd_gui_t * gui,
1347 : ulong _slot,
1348 : ulong _parent_slot,
1349 : ulong bank_seq,
1350 0 : ulong parent_bank_seq ) {
1351 0 : fd_gui_slot_t * rec = fd_gui_slot_get( gui, _slot, bank_seq );
1352 0 : if( FD_LIKELY( rec ) ) return rec;
1353 :
1354 0 : fd_gui_epoch_t * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
1355 0 : fd_pubkey_t const * slot_leader = fd_gui_get_slot_leader( epoch, _slot );
1356 0 : int mine = fd_int_if( !!slot_leader, !memcmp( slot_leader->uc, gui->summary.identity_key->uc, 32UL ), 0 );
1357 :
1358 0 : fd_gui_hist_slot_key_t key;
1359 0 : key.slot = _slot; key.bank_seq = bank_seq;
1360 0 : fd_gui_slot_t * meta = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_SLOT, &key );
1361 0 : if( FD_UNLIKELY( !meta ) ) return NULL;
1362 :
1363 0 : meta->slot = _slot;
1364 0 : meta->bank_seq = bank_seq;
1365 0 : meta->parent_bank_seq = parent_bank_seq;
1366 0 : meta->parent_slot = _parent_slot;
1367 0 : meta->vote_slot = ULONG_MAX;
1368 0 : meta->vote_latency_exact = FD_GUI_VOTE_LATENCY_NOT_VOTED;
1369 0 : meta->max_compute_units = UINT_MAX;
1370 0 : meta->completed_time = LONG_MAX;
1371 0 : meta->parent_completed_time = LONG_MAX;
1372 0 : meta->mine = (uchar)(mine & 1);
1373 0 : meta->is_voter = 0;
1374 0 : meta->skip = FD_GUI_SKIP_STATUS_UNKNOWN;
1375 0 : meta->level = (uchar)( _slot ? FD_GUI_SLOT_LEVEL_INCOMPLETE : FD_GUI_SLOT_LEVEL_ROOTED ); /* slot 0 always rooted */
1376 0 : meta->vote_failed = UINT_MAX;
1377 0 : meta->vote_success = UINT_MAX;
1378 0 : meta->nonvote_success = UINT_MAX;
1379 0 : meta->nonvote_failed = UINT_MAX;
1380 0 : meta->compute_units = UINT_MAX;
1381 0 : meta->transaction_fee = ULONG_MAX;
1382 0 : meta->priority_fee = ULONG_MAX;
1383 0 : meta->tips = ULONG_MAX;
1384 0 : meta->shred_cnt = UINT_MAX;
1385 0 : memset( meta->block_hash.uc, 0, sizeof(fd_hash_t) );
1386 :
1387 0 : if( FD_UNLIKELY( mine && epoch ) ) epoch->my_total_slots++;
1388 :
1389 0 : return meta;
1390 0 : }
1391 :
1392 : FD_PROTOTYPES_END
1393 :
1394 : #endif /* HEADER_fd_src_disco_gui_fd_gui_h */
|