LCOV - code coverage report
Current view: top level - disco/gui - fd_gui.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 40 278 14.4 %
Date: 2026-09-17 04:28:31 Functions: 7 154 4.5 %

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

Generated by: LCOV version 1.14