LCOV - code coverage report
Current view: top level - disco/gui - fd_gui.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 236 0.0 %
Date: 2026-08-13 04:56:22 Functions: 0 102 0.0 %

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

Generated by: LCOV version 1.14