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