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