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