LCOV - code coverage report
Current view: top level - disco/gui - fd_gui.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 383 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 270 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_disco_gui_fd_gui_h
       2             : #define HEADER_fd_src_disco_gui_fd_gui_h
       3             : 
       4             : #include "fd_gui_peers.h"
       5             : #include "fd_gui_shred.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/restore/utils/fd_wfs.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           0 : #define FD_GUI_SLOT_RANKINGS_SZ (100UL)
     112           0 : #define FD_GUI_SLOT_RANKING_TYPE_ASC  (0)
     113           0 : #define FD_GUI_SLOT_RANKING_TYPE_DESC (1)
     114             : 
     115             : /* ---- Boot ----------------------------------------------------------- */
     116             : 
     117           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_JOINING_GOSSIP               (1)
     118           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_FULL_SNAPSHOT        (2)
     119           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_LOADING_INCREMENTAL_SNAPSHOT (3)
     120           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY    (4)
     121           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP                  (5)
     122           0 : #define FD_GUI_BOOT_PROGRESS_TYPE_RUNNING                      (6)
     123             : 
     124           0 : #define FD_GUI_BOOT_PROGRESS_FULL_SNAPSHOT_IDX        (0UL)
     125           0 : #define FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX (1UL)
     126           0 : #define FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT             (2UL)
     127             : 
     128           0 : #define FD_GUI_SLOT_LEVEL_INCOMPLETE               (0)
     129           0 : #define FD_GUI_SLOT_LEVEL_COMPLETED                (1)
     130           0 : #define FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED (2) /* "notarized" under Alpenglow */
     131           0 : #define FD_GUI_SLOT_LEVEL_ROOTED                   (3)
     132           0 : #define FD_GUI_SLOT_LEVEL_FINALIZED                (4)
     133             : 
     134             : /* Strength of the notarization and finalization proofs known for a
     135             :    block.*/
     136             : 
     137           0 : #define FD_GUI_AG_NOTAR_NONE     ((uchar)0)
     138           0 : #define FD_GUI_AG_NOTAR_FALLBACK ((uchar)1)
     139           0 : #define FD_GUI_AG_NOTAR_REGULAR  ((uchar)2) /* supersedes fallback */
     140             : 
     141           0 : #define FD_GUI_AG_FINAL_NONE     ((uchar)0)
     142           0 : #define FD_GUI_AG_FINAL_IMPLICIT ((uchar)1) /* an ancestor of a finalized block */
     143           0 : #define FD_GUI_AG_FINAL_SLOW     ((uchar)2) /* supersedes implicit */
     144           0 : #define FD_GUI_AG_FINAL_FAST     ((uchar)3) /* supersedes slow */
     145             : 
     146             : /* Whether our ordinary notarize or skip vote for a slot made it into
     147             :    the reward certificate. */
     148             : 
     149           0 : #define FD_GUI_VOTE_REWARDED_UNKNOWN ((uchar)0)
     150           0 : #define FD_GUI_VOTE_REWARDED_YES     ((uchar)1)
     151           0 : #define FD_GUI_VOTE_REWARDED_NO      ((uchar)2)
     152             : 
     153           0 : #define FD_GUI_IS_VOTER_UNKNOWN ((uchar)0)
     154           0 : #define FD_GUI_IS_VOTER_YES     ((uchar)1)
     155           0 : #define FD_GUI_IS_VOTER_NO      ((uchar)2)
     156             : 
     157             : /* Under Tower, NOTARIZED isn't used and FINALIZED is a catch-all for
     158             :    skipped.  UNKNOWN is used while fork choice has not resolved a
     159             :    replayed slot. */
     160           0 : #define FD_GUI_SKIP_STATUS_UNKNOWN     ((uchar)0)
     161           0 : #define FD_GUI_SKIP_STATUS_NOT_SKIPPED ((uchar)1)
     162           0 : #define FD_GUI_SKIP_STATUS_NOTARIZED   ((uchar)2)
     163           0 : #define FD_GUI_SKIP_STATUS_FINALIZED   ((uchar)3)
     164             : 
     165           0 : #define FD_GUI_TXN_FLAGS_STARTED         ( 1U)
     166           0 : #define FD_GUI_TXN_FLAGS_ENDED           ( 2U)
     167           0 : #define FD_GUI_TXN_FLAGS_IS_SIMPLE_VOTE  ( 4U)
     168           0 : #define FD_GUI_TXN_FLAGS_FROM_BUNDLE     ( 8U)
     169           0 : #define FD_GUI_TXN_FLAGS_LANDED_IN_BLOCK (16U)
     170             : 
     171           0 : #define FD_GUI_VOTE_STATE_NON_VOTING (0)
     172           0 : #define FD_GUI_VOTE_STATE_VOTING     (1)
     173           0 : #define FD_GUI_VOTE_STATE_DELINQUENT (2)
     174             : 
     175             : #define FD_GUI_MAX_VOTE_DISTANCE     (512UL)
     176             : 
     177             : #define FD_GUI_LANDED_VOTE_MAX      (4096UL)
     178             : 
     179           0 : #define FD_GUI_HTTP_MIN_SEND_BUFFER_SZ        (256UL<<20)
     180             : 
     181           0 : #define FD_GUI_TIMELINE_STORED_GRANULARITY_CNT (7UL)
     182           0 : #define FD_GUI_TIMELINE_GRANULARITY_CNT        (20UL)
     183             : #define FD_GUI_TIMELINE_QUERY_SHRED_MAX        (262144UL)
     184             : /* At least one bucket per pixel on a 4K display, and enough to cover
     185             :    the widest raw txn_batch view (1.5h) at 2s in a single request. */
     186             : #define FD_GUI_TIMELINE_QUERY_MAX_BUCKETS      (4096UL)
     187             : 
     188             : struct fd_gui_timeline_granularity {
     189             :   char const * name;
     190             :   ulong        stored_idx; /* stored granularity the buckets are built from */
     191             :   ulong        merge_cnt;  /* stored buckets merged into one bucket */
     192             : };
     193             : typedef struct fd_gui_timeline_granularity fd_gui_timeline_granularity_t;
     194             : extern fd_gui_timeline_granularity_t const fd_gui_timeline_granularities[ FD_GUI_TIMELINE_GRANULARITY_CNT ];
     195             : extern ulong const fd_gui_timeline_stored_granularity_ns[ FD_GUI_TIMELINE_STORED_GRANULARITY_CNT ];
     196             : 
     197             : /* fd_gui_timeline_granularity_ns returns the bucket duration of
     198             :    granularity g in nanoseconds. */
     199             : 
     200             : static inline ulong
     201           0 : fd_gui_timeline_granularity_ns( ulong g ) {
     202           0 :   fd_gui_timeline_granularity_t const * desc = &fd_gui_timeline_granularities[ g ];
     203           0 :   return fd_gui_timeline_stored_granularity_ns[ desc->stored_idx ]*desc->merge_cnt;
     204           0 : }
     205             : 
     206             : /* Stored timeline-day bucket layout. */
     207           0 : #define FD_GUI_TIMELINE_DAY_NS                 (86400000000000L)
     208             : 
     209           0 : #define FD_GUI_TIMELINE_GRANULARITY_250MS (0)
     210           0 : #define FD_GUI_TIMELINE_GRANULARITY_2S    (1)
     211           0 : #define FD_GUI_TIMELINE_GRANULARITY_15S   (2)
     212           0 : #define FD_GUI_TIMELINE_GRANULARITY_2M    (3)
     213           0 : #define FD_GUI_TIMELINE_GRANULARITY_15M   (4)
     214           0 : #define FD_GUI_TIMELINE_GRANULARITY_2H    (5)
     215           0 : #define FD_GUI_TIMELINE_GRANULARITY_12H   (6)
     216             : 
     217           0 : #define FD_GUI_TIMELINE_250MS_CNT (FD_GUI_TIMELINE_DAY_NS / (             250L*1000L*1000L))
     218           0 : #define FD_GUI_TIMELINE_2S_CNT    (FD_GUI_TIMELINE_DAY_NS / (         2L*1000L*1000L*1000L))
     219           0 : #define FD_GUI_TIMELINE_15S_CNT   (FD_GUI_TIMELINE_DAY_NS / (        15L*1000L*1000L*1000L))
     220           0 : #define FD_GUI_TIMELINE_2M_CNT    (FD_GUI_TIMELINE_DAY_NS / (     2L*60L*1000L*1000L*1000L))
     221           0 : #define FD_GUI_TIMELINE_15M_CNT   (FD_GUI_TIMELINE_DAY_NS / (    15L*60L*1000L*1000L*1000L))
     222           0 : #define FD_GUI_TIMELINE_2H_CNT    (FD_GUI_TIMELINE_DAY_NS / ( 2L*60L*60L*1000L*1000L*1000L))
     223           0 : #define FD_GUI_TIMELINE_12H_CNT   (FD_GUI_TIMELINE_DAY_NS / (12L*60L*60L*1000L*1000L*1000L))
     224             : 
     225             : #define FD_GUI_TIMELINE_FIELD_MAP( X )                                                           \
     226             :   /* field                              250ms   2s      15s     2m      15m     2h      12h   */ \
     227           0 :   X( START_SLOT,      start_slot,       ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     228           0 :   X( END_SLOT,        end_slot,         ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     229           0 :   X( TURBINE,         turbine,          uint,   uint,   uint,   uint,   ulong,  ulong,  ulong  ) \
     230           0 :   X( REPAIR,          repair,           uint,   uint,   uint,   uint,   ulong,  ulong,  ulong  ) \
     231           0 :   X( RECONSTRUCTED,   reconstructed,    uint,   uint,   uint,   uint,   ulong,  ulong,  ulong  ) \
     232           0 :   X( PUBLISHED,       published,        uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     233           0 :   X( COMPUTE_UNITS,   compute_units,    uint,   uint,   ulong,  ulong,  ulong,  ulong,  ulong  ) \
     234           0 :   X( MAX_COMPUTE,     max_compute,      uint,   uint,   uint,   uint,   uint,   uint,   uint   ) \
     235           0 :   X( TXN_FEES,        txn_fees,         ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     236           0 :   X( PRIO_FEES,       prio_fees,        ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     237           0 :   X( TIPS,            tips,             ulong,  ulong,  ulong,  ulong,  ulong,  ulong,  ulong  ) \
     238           0 :   X( NONVOTE_SUCCESS, nonvote_success,  uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     239           0 :   X( NONVOTE_FAILED,  nonvote_failed,   uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     240           0 :   X( VOTE_SUCCESS,    vote_success,     uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     241           0 :   X( VOTE_FAILED,     vote_failed,      uint,   uint,   uint,   uint,   uint,   ulong,  ulong  ) \
     242           0 :   X( SKIPPED,         skipped,          ushort, ushort, ushort, ushort, ushort, ushort, uint   )
     243             : 
     244             : #define FD_GUI_TIMELINE_FIELD_ENUM( id, name, t0, t1, t2, t3, t4, t5, t6 ) FD_GUI_TIMELINE_FIELD_##id,
     245             : enum fd_gui_timeline_field {
     246             :   FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_FIELD_ENUM )
     247             :   FD_GUI_TIMELINE_FIELD_CNT
     248             : };
     249             : #undef FD_GUI_TIMELINE_FIELD_ENUM
     250             : 
     251             : #define FD_GUI_TIMELINE_DECL_250MS(id, name, t0, t1, t2, t3, t4, t5, t6) t0 name[ FD_GUI_TIMELINE_250MS_CNT ];
     252             : #define FD_GUI_TIMELINE_DECL_2S(id, name, t0, t1, t2, t3, t4, t5, t6)    t1 name[ FD_GUI_TIMELINE_2S_CNT    ];
     253             : #define FD_GUI_TIMELINE_DECL_15S(id, name, t0, t1, t2, t3, t4, t5, t6)   t2 name[ FD_GUI_TIMELINE_15S_CNT   ];
     254             : #define FD_GUI_TIMELINE_DECL_2M(id, name, t0, t1, t2, t3, t4, t5, t6)    t3 name[ FD_GUI_TIMELINE_2M_CNT    ];
     255             : #define FD_GUI_TIMELINE_DECL_15M(id, name, t0, t1, t2, t3, t4, t5, t6)   t4 name[ FD_GUI_TIMELINE_15M_CNT   ];
     256             : #define FD_GUI_TIMELINE_DECL_2H(id, name, t0, t1, t2, t3, t4, t5, t6)    t5 name[ FD_GUI_TIMELINE_2H_CNT    ];
     257             : #define FD_GUI_TIMELINE_DECL_12H(id, name, t0, t1, t2, t3, t4, t5, t6)   t6 name[ FD_GUI_TIMELINE_12H_CNT   ];
     258             : 
     259             : typedef struct fd_gui_timeline_250ms { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_250MS ) } fd_gui_timeline_250ms_t;
     260             : typedef struct fd_gui_timeline_2s    { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_2S    ) } fd_gui_timeline_2s_t;
     261             : typedef struct fd_gui_timeline_15s   { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_15S   ) } fd_gui_timeline_15s_t;
     262             : typedef struct fd_gui_timeline_2m    { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_2M    ) } fd_gui_timeline_2m_t;
     263             : typedef struct fd_gui_timeline_15m   { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_15M   ) } fd_gui_timeline_15m_t;
     264             : typedef struct fd_gui_timeline_2h    { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_2H    ) } fd_gui_timeline_2h_t;
     265             : typedef struct fd_gui_timeline_12h   { FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_DECL_12H   ) } fd_gui_timeline_12h_t;
     266             : 
     267             : #undef FD_GUI_TIMELINE_DECL_250MS
     268             : #undef FD_GUI_TIMELINE_DECL_2S
     269             : #undef FD_GUI_TIMELINE_DECL_15S
     270             : #undef FD_GUI_TIMELINE_DECL_2M
     271             : #undef FD_GUI_TIMELINE_DECL_15M
     272             : #undef FD_GUI_TIMELINE_DECL_2H
     273             : #undef FD_GUI_TIMELINE_DECL_12H
     274             : 
     275             : struct fd_gui_timeline_day {
     276             :   long insert_time_ns;
     277             :   long end_time_ns; /* exclusive UTC day end (the following midnight) */
     278             :   fd_gui_timeline_250ms_t bucket_250ms;
     279             :   fd_gui_timeline_2s_t    bucket_2s;
     280             :   fd_gui_timeline_15s_t   bucket_15s;
     281             :   fd_gui_timeline_2m_t    bucket_2m;
     282             :   fd_gui_timeline_15m_t   bucket_15m;
     283             :   fd_gui_timeline_2h_t    bucket_2h;
     284             :   fd_gui_timeline_12h_t   bucket_12h;
     285             : };
     286             : typedef struct fd_gui_timeline_day fd_gui_timeline_day_t;
     287             : 
     288             : static inline ulong
     289           0 : fd_gui_timeline_bucket_cnt( int granularity ) {
     290           0 :   switch( granularity ) {
     291           0 :     case FD_GUI_TIMELINE_GRANULARITY_250MS: return FD_GUI_TIMELINE_250MS_CNT;
     292           0 :     case FD_GUI_TIMELINE_GRANULARITY_2S:    return FD_GUI_TIMELINE_2S_CNT;
     293           0 :     case FD_GUI_TIMELINE_GRANULARITY_15S:   return FD_GUI_TIMELINE_15S_CNT;
     294           0 :     case FD_GUI_TIMELINE_GRANULARITY_2M:    return FD_GUI_TIMELINE_2M_CNT;
     295           0 :     case FD_GUI_TIMELINE_GRANULARITY_15M:   return FD_GUI_TIMELINE_15M_CNT;
     296           0 :     case FD_GUI_TIMELINE_GRANULARITY_2H:    return FD_GUI_TIMELINE_2H_CNT;
     297           0 :     case FD_GUI_TIMELINE_GRANULARITY_12H:   return FD_GUI_TIMELINE_12H_CNT;
     298           0 :     default: FD_LOG_ERR(( "invalid timeline granularity %d", granularity ));
     299           0 :   }
     300           0 : }
     301             : 
     302             : static inline void *
     303             : fd_gui_timeline_field_ptr( fd_gui_timeline_day_t * day,
     304             :                            int                     granularity,
     305             :                            int                     field,
     306             :                            ulong                   idx,
     307           0 :                            ulong *                 sz ) {
     308           0 :   if( FD_UNLIKELY( !day ) ) FD_LOG_ERR(( "NULL timeline day" ));
     309           0 :   ulong bucket_cnt = fd_gui_timeline_bucket_cnt( granularity );
     310           0 :   if( FD_UNLIKELY( idx>=bucket_cnt ) ) FD_LOG_ERR(( "timeline bucket index %lu out of range [0,%lu)", idx, bucket_cnt ));
     311           0 : #define FD_GUI_TIMELINE_FIELD_CASE( id, name, t0, t1, t2, t3, t4, t5, t6 ) \
     312           0 :   case FD_GUI_TIMELINE_FIELD_##id: \
     313           0 :     switch( granularity ) { \
     314           0 :       case FD_GUI_TIMELINE_GRANULARITY_250MS: *sz=sizeof(t0); return &day->bucket_250ms.name[ idx ]; \
     315           0 :       case FD_GUI_TIMELINE_GRANULARITY_2S:    *sz=sizeof(t1); return &day->bucket_2s.name   [ idx ]; \
     316           0 :       case FD_GUI_TIMELINE_GRANULARITY_15S:   *sz=sizeof(t2); return &day->bucket_15s.name  [ idx ]; \
     317           0 :       case FD_GUI_TIMELINE_GRANULARITY_2M:    *sz=sizeof(t3); return &day->bucket_2m.name   [ idx ]; \
     318           0 :       case FD_GUI_TIMELINE_GRANULARITY_15M:   *sz=sizeof(t4); return &day->bucket_15m.name  [ idx ]; \
     319           0 :       case FD_GUI_TIMELINE_GRANULARITY_2H:    *sz=sizeof(t5); return &day->bucket_2h.name   [ idx ]; \
     320           0 :       case FD_GUI_TIMELINE_GRANULARITY_12H:   *sz=sizeof(t6); return &day->bucket_12h.name  [ idx ]; \
     321           0 :       default: FD_LOG_ERR(( "invalid timeline granularity %d", granularity )); \
     322           0 :     }
     323           0 :   switch( field ) {
     324           0 :     FD_GUI_TIMELINE_FIELD_MAP( FD_GUI_TIMELINE_FIELD_CASE )
     325           0 :     default: FD_LOG_ERR(( "invalid timeline field %d", field ));
     326           0 :   }
     327           0 : #undef FD_GUI_TIMELINE_FIELD_CASE
     328           0 : }
     329             : 
     330             : static inline ulong
     331             : fd_gui_timeline_field_get( fd_gui_timeline_day_t const * day,
     332             :                            int                           granularity,
     333             :                            int                           field,
     334           0 :                            ulong                         idx ) {
     335           0 :   ulong sz = 0UL;
     336           0 :   void const * ptr = fd_gui_timeline_field_ptr( (fd_gui_timeline_day_t *)day, granularity, field, idx, &sz );
     337           0 :   if( sz==sizeof(ushort) ) { ushort v = *(ushort const *)ptr; return v==USHORT_MAX ? ULONG_MAX : (ulong)v; }
     338           0 :   if( sz==sizeof(uint  ) ) { uint   v = *(uint   const *)ptr; return v==UINT_MAX   ? ULONG_MAX : (ulong)v; }
     339           0 :   ulong v = *(ulong const *)ptr;
     340           0 :   return v;
     341           0 : }
     342             : 
     343             : static inline void
     344             : fd_gui_timeline_field_set( fd_gui_timeline_day_t * day,
     345             :                            int                     granularity,
     346             :                            int                     field,
     347             :                            ulong                   idx,
     348           0 :                            ulong                   value ) {
     349           0 :   ulong sz = 0UL;
     350           0 :   void * ptr = fd_gui_timeline_field_ptr( day, granularity, field, idx, &sz );
     351           0 :   if( sz==sizeof(ushort) ) { *(ushort *)ptr = value==ULONG_MAX ? USHORT_MAX : (ushort)fd_ulong_min( value, (ulong)USHORT_MAX-1UL ); return; }
     352           0 :   if( sz==sizeof(uint  ) ) { *(uint   *)ptr = value==ULONG_MAX ? UINT_MAX   : (uint  )fd_ulong_min( value, (ulong)UINT_MAX-1UL   ); return; }
     353           0 :   *(ulong *)ptr = value;
     354           0 : }
     355             : 
     356             : /* fd_gui_timeline_combine returns the result of folding value into the
     357             :    accumulator acc for field. */
     358             : 
     359             : static inline ulong
     360             : fd_gui_timeline_combine( ulong acc,
     361             :                          ulong value,
     362           0 :                          int   field ) {
     363           0 :   if( value==ULONG_MAX ) return acc;
     364           0 :   if( acc==ULONG_MAX )   return value;
     365           0 :   switch( field ) {
     366           0 :     case FD_GUI_TIMELINE_FIELD_START_SLOT:  return fd_ulong_min( acc, value );
     367           0 :     case FD_GUI_TIMELINE_FIELD_END_SLOT:
     368           0 :     case FD_GUI_TIMELINE_FIELD_MAX_COMPUTE: return fd_ulong_max( acc, value );
     369           0 :     default:                                return fd_ulong_min( ULONG_MAX-1UL, fd_ulong_sat_add( acc, value ) );
     370           0 :   }
     371           0 : }
     372             : 
     373             : /* fd_gui_timeline_field_accum folds value into a stored bucket. */
     374             : 
     375             : static inline void
     376             : fd_gui_timeline_field_accum( fd_gui_timeline_day_t * day,
     377             :                              int                     granularity,
     378             :                              int                     field,
     379             :                              ulong                   idx,
     380           0 :                              ulong                   value ) {
     381           0 :   ulong acc = fd_gui_timeline_field_get( day, granularity, field, idx );
     382           0 :   fd_gui_timeline_field_set( day, granularity, field, idx, fd_gui_timeline_combine( acc, value, field ) );
     383           0 : }
     384             : 
     385             : struct fd_gui_tile_timers {
     386             :   long   sample_time_nanos; /* wallclock ns this sample was taken; identical across the per-tile records. */
     387             :   ulong  tile_idx;          /* global tile index into topo->tiles. */
     388             :   ulong timers[ FD_METRICS_ENUM_TILE_REGIME_CNT ];
     389             :   ulong sched_timers[ FD_METRICS_ENUM_THREAD_REGIME_CNT ];
     390             : 
     391             :   int    in_backp;
     392             :   ushort last_cpu;
     393             :   uchar  status;
     394             :   ulong  heartbeat;
     395             :   ulong  backp_cnt;
     396             :   ulong  nvcsw;
     397             :   ulong  nivcsw;
     398             :   ulong  minflt;
     399             :   ulong  majflt;
     400             :   ulong  interrupts;
     401             :   ulong  tlb_shootdowns;
     402             :   ulong  timer_ticks;
     403             : };
     404             : 
     405             : typedef struct fd_gui_tile_timers fd_gui_tile_timers_t;
     406             : 
     407           0 : #define FD_GUI_EXEC_DONE_SLOT_CNT (16UL)
     408             : 
     409             : struct fd_gui_tile_timers_hist {
     410             :   long   sample_time_nanos;
     411             :   ushort tile_idx;
     412             :   uchar  alive;
     413             :   uchar  in_backp;
     414             :   ushort timers[ FD_METRICS_ENUM_TILE_REGIME_CNT ];
     415             :   ushort sched_timers[ FD_METRICS_ENUM_THREAD_REGIME_CNT ];
     416             :   ushort idle_ratio;
     417             :   ushort last_cpu;
     418             :   ulong  backp_msgs;
     419             :   ulong  nvcsw;
     420             :   ulong  nivcsw;
     421             :   ulong  minflt;
     422             :   ulong  majflt;
     423             :   ulong  interrupts;
     424             :   ulong  tlb_shootdowns;
     425             :   ulong  timer_ticks;
     426             : };
     427             : 
     428             : typedef struct fd_gui_tile_timers_hist fd_gui_tile_timers_hist_t;
     429             : 
     430             : struct fd_gui_scheduler_counts {
     431             :   long  sample_time_ns;
     432             :   ulong regular;
     433             :   ulong votes;
     434             :   ulong conflicting;
     435             :   ulong bundles;
     436             : };
     437             : 
     438             : typedef struct fd_gui_scheduler_counts fd_gui_scheduler_counts_t;
     439             : 
     440             : struct fd_gui_network_stats {
     441             :   /* total bytes accumulated */
     442             :   struct {
     443             :     ulong turbine;
     444             :     ulong gossip;
     445             :     ulong tpu;
     446             :     ulong repair;
     447             :     ulong rserve;
     448             :     ulong metric;
     449             :     ulong votor;
     450             :   } in, out;
     451             : };
     452             : 
     453             : typedef struct fd_gui_network_stats fd_gui_network_stats_t;
     454             : 
     455             : struct fd_gui_accounts_stats {
     456             :   /* Raw counters/gauges read from tile metric pages.  Stored so we can
     457             :      compute deltas (and per-second rates) against a previous snapshot. */
     458             :   long  sample_time_nanos;
     459             : 
     460             :   /* Disk (gauges from accdb tile). */
     461             :   ulong accounts_total;
     462             :   ulong accounts_capacity;
     463             :   ulong disk_allocated_bytes;
     464             :   ulong disk_current_bytes;
     465             :   ulong disk_used_bytes;
     466             : 
     467             :   /* Compaction (gauges + counters from accdb tile). */
     468             :   ulong in_compaction;
     469             :   ulong compactions_requested;
     470             :   ulong compactions_completed;
     471             :   ulong accounts_relocated_bytes;
     472             : 
     473             :   /* Cache occupancy (gauges from accdb tile, per class). */
     474             :   ulong cache_class_used           [ FD_ACCDB_CACHE_CLASS_CNT ];
     475             :   ulong cache_class_max            [ FD_ACCDB_CACHE_CLASS_CNT ];
     476             :   ulong cache_class_reserved       [ FD_ACCDB_CACHE_CLASS_CNT ];
     477             :   /* Preeviction thresholds, expressed as used-slot counts directly
     478             :      comparable to cache_class_used / cache_class_max. */
     479             :   ulong cache_class_target_used    [ FD_ACCDB_CACHE_CLASS_CNT ];
     480             :   ulong cache_class_low_water_used [ FD_ACCDB_CACHE_CLASS_CNT ];
     481             : 
     482             :   /* Aggregate counters summed across all accdb consumer tiles. */
     483             :   ulong acquired;                   /* total acquires */
     484             :   ulong acquired_writable;          /* writable subset of acquires (RW tiles only) */
     485             :   ulong acquired_per_class           [ FD_ACCDB_CACHE_CLASS_CNT ]; /* acquires attributed to a class (RW tiles only) */
     486             :   ulong acquired_writable_per_class  [ FD_ACCDB_CACHE_CLASS_CNT ]; /* writable acquires attributed to a class (RW tiles only) */
     487             :   ulong not_found_per_class          [ FD_ACCDB_CACHE_CLASS_CNT ]; /* misses, per class */
     488             :   ulong evicted_per_class            [ FD_ACCDB_CACHE_CLASS_CNT ];
     489             :   ulong preevicted_per_class         [ FD_ACCDB_CACHE_CLASS_CNT ];
     490             :   ulong committed_new_per_class      [ FD_ACCDB_CACHE_CLASS_CNT ];
     491             :   ulong committed_overwrite_per_class[ FD_ACCDB_CACHE_CLASS_CNT ];
     492             : 
     493             :   /* IO counters.  bytes_written/read_ops/write_ops are summed across
     494             :      all consumer tiles; bytes_written_accdb is the bytes written by
     495             :      the accdb tile itself (background preevict + compaction), used to
     496             :      compute the prewrite ratio. */
     497             :   ulong bytes_read;
     498             :   ulong bytes_copied;
     499             :   ulong bytes_written;
     500             :   ulong bytes_written_accdb;
     501             :   ulong read_ops;
     502             :   ulong write_ops;
     503             : };
     504             : 
     505             : typedef struct fd_gui_accounts_stats fd_gui_accounts_stats_t;
     506             : 
     507             : struct fd_gui_turbine_slot {
     508             :  ulong slot;
     509             :  long timestamp;
     510             : };
     511             : 
     512             : typedef struct fd_gui_turbine_slot fd_gui_turbine_slot_t;
     513             : 
     514             : struct __attribute__((packed)) fd_gui_slot {
     515             :   ulong     slot;             /* this record's slot number. */
     516             :   ulong     bank_seq;         /* this block's fork discriminator (the record's own key bank_seq).*/
     517             :   ulong     parent_bank_seq;  /* parent block's fork discriminator (ULONG_MAX if unknown) */
     518             :   long      completed_time;   /* slot completion wallclock ns (LONG_MAX if unknown) */
     519             :   uint      shred_cnt;        /* slot->shred_cnt at completion */
     520             :   fd_hash_t block_hash;       /* block hash of the slot */
     521             :   uchar     mine:1;           /* 1 if this was our leader slot */
     522             :   uchar     is_voter:2;       /* one of FD_GUI_IS_VOTER_* */
     523             :   uint      vote_success;     /* successful vote txn count     (UINT_MAX if unknown) */
     524             :   uint      vote_failed;      /* failed vote txn count         (UINT_MAX if unknown) */
     525             :   uint      nonvote_success;  /* successful nonvote txn count  (UINT_MAX if unknown) */
     526             :   uint      nonvote_failed;   /* failed nonvote txn count      (UINT_MAX if unknown) */
     527             :   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. */
     528             :   uint      max_compute_units;/* block compute unit limit      (UINT_MAX if unknown) */
     529             :   uint      compute_units;    /* block compute units consumed  (UINT_MAX if unknown) */
     530             :   ulong     transaction_fee;  /* total transaction fee         (ULONG_MAX if unknown) */
     531             :   ulong     priority_fee;     /* total priority fee            (ULONG_MAX if unknown) */
     532             :   ulong     tips;             /* total tips                    (ULONG_MAX if unknown) */
     533             :   ulong     parent_slot;      /* parent (on-fork) slot number  (ULONG_MAX if unknown) */
     534             :   long      parent_completed_time; /* parent block completion wallclock ns (LONG_MAX if unknown) */
     535             :   ulong     vote_slot;        /* most recent slot we had landed a vote for as of this slot's replay (ULONG_MAX if unknown) */
     536             :   uchar     skip;             /* one of FD_GUI_SKIP_STATUS_* */
     537             :   uchar     level;            /* one of FD_GUI_SLOT_LEVEL_* */
     538             :   uchar     notarization_kind; /* Alpenglow: one of FD_GUI_AG_NOTAR_* */
     539             :   uchar     finalization_kind; /* Alpenglow: one of FD_GUI_AG_FINAL_* */
     540             :   uchar     vote_rewarded;     /* Alpenglow: one of FD_GUI_VOTE_REWARDED_* */
     541             :   ushort    vote_count;        /* Alpenglow: distinct signers of the reward certs carried by this (cert) slot, USHORT_MAX if unknown */
     542             : };
     543             : 
     544             : typedef struct fd_gui_slot fd_gui_slot_t;
     545             : 
     546             : static inline void
     547             : fd_gui_slot_set_voter_state( fd_gui_slot_t * slot,
     548           0 :                              uchar           state ) {
     549           0 :   slot->is_voter = FD_GUI_IS_VOTER_UNKNOWN;
     550           0 :   if(      state==FD_GUI_IS_VOTER_YES ) slot->is_voter = FD_GUI_IS_VOTER_YES;
     551           0 :   else if( state==FD_GUI_IS_VOTER_NO  ) slot->is_voter = FD_GUI_IS_VOTER_NO;
     552           0 : }
     553             : 
     554             : /* Alpenglow certificates name a block by (slot, block_id), while slot
     555             :    records are keyed by (slot, bank_seq).  Certificates are buffered as
     556             :    they arrive before replay completes their block. They are then
     557             :    eagerly joined by their block_id.  The buffer covers max_live_slots
     558             :    so replay can consume any block retained by the execution pipeline. */
     559             : 
     560             : struct fd_gui_ag_slot {
     561             :   ulong     slot;              /* ULONG_MAX when this entry is empty */
     562             :   fd_hash_t block_id;          /* Double merkle root of the block data. { 0 } by default. */
     563             :   ulong     bank_seq;          /* ULONG_MAX until this block completes replay */
     564             :   uchar     notarization_kind; /* one of FD_GUI_AG_NOTAR_* */
     565             :   uchar     finalization_kind; /* one of FD_GUI_AG_FINAL_* */
     566             :   uchar     skip;              /* one of FD_GUI_SKIP_STATUS_* */
     567             : };
     568             : 
     569             : typedef struct fd_gui_ag_slot fd_gui_ag_slot_t;
     570             : 
     571             : struct fd_gui_slot_ranking {
     572             :   ulong slot;
     573             :   ulong value;
     574             :   int   type;
     575             : };
     576             : typedef struct fd_gui_slot_ranking fd_gui_slot_ranking_t;
     577             : 
     578             : struct fd_gui_slot_rankings {
     579             :   fd_gui_slot_ranking_t largest_tips           [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     580             :   fd_gui_slot_ranking_t largest_fees           [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     581             :   fd_gui_slot_ranking_t largest_rewards        [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     582             :   fd_gui_slot_ranking_t largest_duration       [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     583             :   fd_gui_slot_ranking_t largest_compute_units  [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     584             :   fd_gui_slot_ranking_t largest_skipped        [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     585             :   fd_gui_slot_ranking_t largest_rewards_per_cu [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     586             :   fd_gui_slot_ranking_t smallest_tips          [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     587             :   fd_gui_slot_ranking_t smallest_fees          [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     588             :   fd_gui_slot_ranking_t smallest_rewards       [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     589             :   fd_gui_slot_ranking_t smallest_rewards_per_cu[ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     590             :   fd_gui_slot_ranking_t smallest_duration      [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     591             :   fd_gui_slot_ranking_t smallest_compute_units [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     592             :   fd_gui_slot_ranking_t smallest_skipped       [ FD_GUI_SLOT_RANKINGS_SZ+1UL ];
     593             : };
     594             : 
     595             : typedef struct fd_gui_slot_rankings fd_gui_slot_rankings_t;
     596             : 
     597           0 : #define FD_GUI_EPOCH_SCHED_CNT ((MAX_SLOTS_PER_EPOCH+FD_EPOCH_SLOTS_PER_ROTATION-1UL)/FD_EPOCH_SLOTS_PER_ROTATION)
     598             : #define FD_GUI_EPOCH_PUB_CNT   (MAX_STAKE_WEIGHTS)
     599             : 
     600           0 : #define FD_GUI_VOTE_LATENCY_NOT_VOTED ((uchar)(UCHAR_MAX))     /* vote missing */
     601           0 : #define FD_GUI_VOTE_LATENCY_MAX       ((uchar)(UCHAR_MAX-1UL)) /* largest observable vote latency */
     602             : 
     603             : struct fd_gui_epoch {
     604             :   ulong epoch;
     605             :   ulong start_slot;
     606             :   ulong slot_cnt;                        /* end_slot = start_slot + slot_cnt - 1 */
     607             :   long  start_time;                      /* epoch start wallclock ns (LONG_MAX if unknown) */
     608             :   long  end_time;                        /* epoch end wallclock ns   (LONG_MAX if unknown) */
     609             :   long  target_slot_duration_ns;         /* protocol target slot duration */
     610             :   ulong my_total_slots;                  /* our leader slots seen this epoch */
     611             :   ulong my_skipped_slots;                /* our skipped leader slots this epoch */
     612             :   ulong rankings_slot;                   /* one more than the largest slot processed into rankings */
     613             :   fd_gui_slot_rankings_t rankings   [ 1 ]; /* global slot rankings */
     614             :   fd_gui_slot_rankings_t my_rankings[ 1 ]; /* my slots only */
     615             : 
     616             :   uchar  latency_exact[ MAX_SLOTS_PER_EPOCH ]; /* skip-discounted latency or FD_GUI_VOTE_LATENCY_* */
     617             :   uchar  is_voter     [ MAX_SLOTS_PER_EPOCH ]; /* Alpenglow: one of FD_GUI_IS_VOTER_* */
     618             :   uchar  skipped      [ MAX_SLOTS_PER_EPOCH ]; /* 1 if the slot was skipped on the rooted fork */
     619             :   uchar  vote_rewarded[ MAX_SLOTS_PER_EPOCH ]; /* Alpenglow: one of FD_GUI_VOTE_REWARDED_* */
     620             :   ushort vote_count   [ MAX_SLOTS_PER_EPOCH ]; /* Alpenglow: distinct signers of the reward certs carried by this (cert) slot, USHORT_MAX if unknown */
     621             : 
     622             :   fd_epoch_schedule_t epoch_schedule;    /* slot<->epoch conversion (fd_slot_to_epoch) */
     623             :   ulong               pub_cnt;           /* number of deduped leader pubkeys in pub[] */
     624             :   ulong               stakes_cnt;        /* number of entries in stakes[] */
     625             :   uint                sched[ FD_GUI_EPOCH_SCHED_CNT ]; /* rotation -> index into pub[] */
     626             :   fd_pubkey_t         pub[ FD_GUI_EPOCH_PUB_CNT ];     /* deduped leaders */
     627             :   fd_vote_stake_weight_t stakes[ MAX_STAKE_WEIGHTS ];
     628             : };
     629             : 
     630             : typedef struct fd_gui_epoch fd_gui_epoch_t;
     631             : 
     632             : struct fd_gui_ephemeral_slot {
     633             :   ulong slot; /* ULONG_MAX indicates invalid/evicted */
     634             :   long timestamp_arrival_nanos;
     635             : };
     636             : typedef struct fd_gui_ephemeral_slot fd_gui_ephemeral_slot_t;
     637             : 
     638             : struct __attribute__((packed)) fd_gui_store_txn_start {
     639             :   ulong slot;
     640             :   ulong bank_seq;               /* per-fork bank sequence */
     641             :   long  insert_time_ns;
     642             :   ulong txn_idx;                /* per-(bank_seq) transaction index */
     643             :   uchar signature[ FD_TXN_SIGNATURE_SZ ];
     644             :   ulong transaction_fee;
     645             :   ulong priority_fee;
     646             :   long  timestamp_arrival_nanos;
     647             :   long  microblock_start_ns;    /* absolute ns (begin tspub) */
     648             :   uint  compute_units_requested;
     649             :   uint  microblock_idx;
     650             :   uint  source_ipv4;
     651             :   uchar source_tpu;
     652             :   uchar flags;                  /* STARTED | IS_SIMPLE_VOTE | FROM_BUNDLE */
     653             : };
     654             : typedef struct fd_gui_store_txn_start fd_gui_store_txn_start_t;
     655             : 
     656             : struct __attribute__((packed)) fd_gui_store_txn_end {
     657             :   ulong slot;
     658             :   ulong bank_seq;               /* per-fork bank sequence */
     659             :   long  insert_time_ns;
     660             :   ulong txn_idx;
     661             :   long  timestamp_arrival_nanos;
     662             :   long  microblock_end_ns;      /* absolute ns (end tspub) */
     663             :   fd_txn_ns_dt_t txn_ns_dt;     /* relative to microblock_start */
     664             :   ulong tips;
     665             :   uint  compute_units_consumed;
     666             :   uint  bank_idx;
     667             :   uint  error_code;
     668             :   uchar flags;                  /* ENDED | LANDED_IN_BLOCK */
     669             : };
     670             : typedef struct fd_gui_store_txn_end fd_gui_store_txn_end_t;
     671             : 
     672             : struct fd_gui_store_replay_txn {
     673             :   long  insert_time_ns;
     674             :   long  completion_time_ns;
     675             :   ulong block_compute_unit_limit;
     676             :   ulong slot;
     677             :   ulong txn_idx;
     678             :   ulong txn_exec_idx;
     679             :   ulong txn_sigverify_exec_idx;
     680             :   uint  txn_start_shred_idx; /* UINT_MAX if unknown; USHORT_MAX is a valid saturated index */
     681             :   uint  txn_end_shred_idx;   /* Exclusive end, capped at the block's last shred; UINT_MAX if unknown */
     682             :   uchar signature[ FD_TXN_SIGNATURE_SZ ];
     683             : 
     684             :   long sigverify_start_ns;
     685             :   long sigverify_end_ns;
     686             :   long load_start_ns;
     687             :   long check_start_ns;
     688             :   long exec_start_ns;
     689             :   long commit_start_ns;
     690             :   long commit_end_ns;
     691             : 
     692             :   ulong transaction_fee;
     693             :   ulong priority_fee;
     694             :   ulong tips;
     695             :   uint  compute_units_requested; /* 0 means unavailable */
     696             :   uint  compute_units_consumed;
     697             :   uint  error_code;
     698             :   uchar is_committable;
     699             :   uchar is_fees_only;
     700             :   uchar is_simple_vote;
     701             : };
     702             : typedef struct fd_gui_store_replay_txn fd_gui_store_replay_txn_t;
     703             : 
     704             : struct fd_gui_slot_txn_join {
     705             :   fd_gui_store_txn_start_t const * start;
     706             :   fd_gui_store_txn_end_t   const * end;
     707             : };
     708             : 
     709             : typedef struct fd_gui_slot_txn_join fd_gui_slot_txn_join_t;
     710             : 
     711             : struct fd_gui_txn_waterfall {
     712             :   long sample_time_nanos; /* wallclock ns this sample was taken. */
     713             :   struct {
     714             :     ulong quic;
     715             :     ulong udp;
     716             :     ulong gossip;
     717             :     ulong block_engine;
     718             :     ulong pack_cranked;
     719             :   } in;
     720             : 
     721             :   struct {
     722             :     ulong net_overrun;
     723             :     ulong quic_overrun;
     724             :     ulong quic_frag_drop;
     725             :     ulong quic_abandoned;
     726             :     ulong tpu_quic_invalid;
     727             :     ulong tpu_udp_invalid;
     728             :     ulong verify_overrun;
     729             :     ulong verify_parse;
     730             :     ulong verify_failed;
     731             :     ulong verify_duplicate;
     732             :     ulong dedup_duplicate;
     733             :     ulong resolv_lut_failed;
     734             :     ulong resolv_expired;
     735             :     ulong resolv_ancient;
     736             :     ulong resolv_no_ledger;
     737             :     ulong resolv_retained;
     738             :     ulong pack_invalid;
     739             :     ulong pack_invalid_bundle;
     740             :     ulong pack_expired;
     741             :     ulong pack_already_executed;
     742             :     ulong pack_retained;
     743             :     ulong pack_leader_slow;
     744             :     ulong bank_invalid;
     745             :     ulong bank_nonce_already_advanced;
     746             :     ulong bank_nonce_advance_failed;
     747             :     ulong bank_nonce_wrong_blockhash;
     748             :     ulong block_success;
     749             :     ulong block_fail;
     750             :   } out;
     751             : };
     752             : 
     753             : typedef struct fd_gui_txn_waterfall fd_gui_txn_waterfall_t;
     754             : 
     755             : struct __attribute__((packed)) fd_gui_leader_slot {
     756             :   ulong     slot;                         /* this record's slot number (the record's own key). */
     757             :   ulong     bank_seq;                     /* this block's fork discriminator (the record's own key bank_seq). */
     758             :   long      leader_start_time;            /* wallclock ns we became leader */
     759             :   long      leader_end_time;              /* wallclock ns we stopped being leader */
     760             :   long      txn_insert_time_min_ns;
     761             :   long      txn_insert_time_max_ns;
     762             :   fd_hash_t block_hash;                   /* block hash of the produced block */
     763             :   ulong     max_microblocks;              /* initial max microblocks packable into the slot */
     764             :   ulong     max_compute_units;            /* block CU limit announced on becoming leader */
     765             :   uint      microblocks_upper_bound;      /* final/exact microblock upper bound */
     766             :   uint      begin_microblocks;            /* microblocks started (pack -> bank) */
     767             :   uint      end_microblocks;              /* microblocks ended (bank -> poh) */
     768             :   fd_done_packing_t scheduler_stats[ 1 ]; /* pack "done packing" record (limits, usage, results) */
     769             :   fd_gui_txn_waterfall_t waterfall_reference[ 1 ];
     770             :   fd_gui_txn_waterfall_t waterfall[ 1 ];
     771             :   uchar     has_waterfall;                /* 1 once waterfall has been finalized */
     772             :   uchar     unbecame_leader;              /* 1 if we relinquished leadership for this slot */
     773             : };
     774             : 
     775             : typedef struct fd_gui_leader_slot fd_gui_leader_slot_t;
     776             : 
     777             : struct fd_gui_tile_stats {
     778             :   long  sample_time_nanos;
     779             : 
     780             :   ulong net_in_rx_bytes;           /* Number of bytes received by the net or sock tile*/
     781             :   ulong quic_conn_cnt;             /* Number of active QUIC connections */
     782             :   fd_histf_t bundle_rx_delay_hist; /* Histogram of bundle rx delay */
     783             :   ulong bundle_rtt_smoothed_nanos; /* RTT (nanoseconds) moving average */
     784             :   ulong verify_drop_cnt;           /* Number of transactions dropped by verify tiles */
     785             :   ulong verify_total_cnt;          /* Number of transactions received by verify tiles */
     786             :   ulong dedup_drop_cnt;            /* Number of transactions dropped by dedup tile */
     787             :   ulong dedup_total_cnt;           /* Number of transactions received by dedup tile */
     788             :   ulong pack_buffer_cnt;           /* Number of buffered transactions in the pack tile */
     789             :   ulong pack_buffer_capacity;      /* Total size of the pack transaction buffer */
     790             :   ulong bank_txn_exec_cnt;         /* Number of transactions processed by the bank tile */
     791             :   ulong net_out_tx_bytes;          /* Number of bytes sent by the net or sock tile */
     792             : };
     793             : 
     794             : typedef struct fd_gui_tile_stats fd_gui_tile_stats_t;
     795             : 
     796             : 
     797             : struct fd_gui_boot_progress {
     798             :   uchar phase;
     799             :   ulong boot_target_slot_duration_nanos;
     800             :   long joining_gossip_time_nanos;
     801             :   struct {
     802             :     ulong  slot;
     803             :     uint   peer_addr;
     804             :     ushort peer_port;
     805             :     ulong  total_bytes_compressed;
     806             :     long   reset_time_nanos;          /* UNIX nanosecond timestamp */
     807             :     long   sample_time_nanos;
     808             :     ulong  reset_cnt;
     809             : 
     810             :     ulong read_bytes_compressed;
     811             :     char  read_path[ PATH_MAX+30UL ]; /* URL or filesystem path.  30 is fd_cstr_nlen( "https://255.255.255.255:12345/", ULONG_MAX ) */
     812             : 
     813             :     ulong decompress_bytes_decompressed;
     814             :     ulong decompress_bytes_compressed;
     815             : 
     816             :     ulong insert_bytes_decompressed;
     817             :     char  insert_path[ PATH_MAX ];
     818             :     ulong insert_accounts_current;
     819             :   } loading_snapshot[ FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT ];
     820             : 
     821             :   ulong wfs_total_stake;
     822             :   ulong wfs_connected_stake;
     823             :   ulong wfs_total_peers;
     824             :   ulong wfs_connected_peers;
     825             :   ulong wfs_attempt;
     826             : 
     827             :   long  catching_up_time_nanos;
     828             :   ulong catching_up_first_replay_slot;
     829             : };
     830             : 
     831             : typedef struct fd_gui_boot_progress fd_gui_boot_progress_t;
     832             : 
     833             : #define FD_GUI_SNAPSV_PENDING_MAX (16384UL)
     834             : #define FD_GUI_SNAPSV_PUBLISH_INTERVAL_NANOS ( 50L*1000L*1000L)
     835             : 
     836             : struct fd_gui_snapsv_pending {
     837             :   fd_snapsv_msg_snap_t msg;
     838             :   long sample_time_nanos;
     839             :   int  closed;
     840             : };
     841             : 
     842             : typedef struct fd_gui_snapsv_pending fd_gui_snapsv_pending_t;
     843             : 
     844             : /* Triangular-weighted moving window over per-snap deltas.  Snap
     845             :    cadence is ~100ms, window is FD_GUI_ACCDB_WIN_SAMPLES samples
     846             :    (~5s).  The output rate for any metric is:
     847             : 
     848             :      rate = sum( w[i] * delta[i] ) / sum( w[i] * dt[i] )
     849             : 
     850             :    where w[i] is the triangular weight (newest sample heaviest,
     851             :    oldest weight=1).  This is fully caught up to a new rate after
     852             :    the window length but smoother than a boxcar (no cliff edge as
     853             :    samples age out). */
     854           0 : #define FD_GUI_ACCDB_WIN_SAMPLES 50UL
     855             : 
     856             : /* Per-partition triangular-weighted windows for read/write rates.
     857             :    Sized to match the accdb partition pool ceiling (8192).  Rates
     858             :    are derived in fd_gui_printf via fd_gui_accdb_weighted_rate. */
     859           0 : #define FD_GUI_MAX_PARTITIONS 8192UL
     860             : 
     861           0 : #define FD_GUI_ACCDB_EMA_HALF_LIFE_NS (600L*1000L*1000L*1000L) /* 10 minutes */
     862             : 
     863             : /* Tile kinds.  Determines which subset of metrics to read. */
     864           0 : #define FD_GUI_ACCDB_TILE_KIND_RW     0  /* execle, execrp, replay, tower */
     865           0 : #define FD_GUI_ACCDB_TILE_KIND_RO     1  /* rpc, resolv */
     866           0 : #define FD_GUI_ACCDB_TILE_KIND_SNAPIN 2  /* snapin (direct snapshot disk writer) */
     867           0 : #define FD_GUI_ACCDB_TILE_KIND_ACCDB  3  /* accdb tile itself (prewrite + compaction writes) */
     868             : 
     869             : /* 60s-history rings for the per-tile sparkline.  Each bucket is the
     870             :    sum of per-snap deltas that fell into that bucket window.  When a
     871             :    bucket interval elapses the per-second rate is formatted once, as
     872             :    the "%.2f," JSON array element it is emitted as, and appended to a
     873             :    packed text ring (oldest first, the oldest dropping off the front
     874             :    once full) so each emit is one copy rather than a printf per sample.
     875             :    240 buckets x 250ms = 60 second window.  The longest element is the
     876             :    rate of a wrapped counter, 2^64/0.25 s: 20 digits, ".00" and the
     877             :    comma. */
     878           0 : #define FD_GUI_ACCDB_SPARKLINE_SAMPLES   240UL
     879           0 : #define FD_GUI_ACCDB_SPARKLINE_BUCKET_NS 250000000L
     880             : #define FD_GUI_ACCDB_SPARKLINE_TEXT_MAX  24UL
     881             : 
     882             : struct fd_gui_accdb_stats {
     883             :   ulong accdb_win_idx;        /* next write index */
     884             :   ulong accdb_win_count;      /* samples filled, capped at FD_GUI_ACCDB_WIN_SAMPLES */
     885             :   long  accdb_win_dt_nanos [ FD_GUI_ACCDB_WIN_SAMPLES ];
     886             : 
     887             :   /* Aggregate delta rings (units per snap). */
     888             :   ulong agg_acquired_win          [ FD_GUI_ACCDB_WIN_SAMPLES ];
     889             :   ulong agg_acquired_writable_win [ FD_GUI_ACCDB_WIN_SAMPLES ];
     890             :   ulong agg_bytes_read_win        [ FD_GUI_ACCDB_WIN_SAMPLES ];
     891             :   ulong agg_bytes_copied_win      [ FD_GUI_ACCDB_WIN_SAMPLES ];
     892             :   ulong agg_bytes_written_win     [ FD_GUI_ACCDB_WIN_SAMPLES ];
     893             :   ulong agg_bytes_written_accdb_win[FD_GUI_ACCDB_WIN_SAMPLES ];
     894             :   ulong agg_read_ops_win          [ FD_GUI_ACCDB_WIN_SAMPLES ];
     895             :   ulong agg_write_ops_win         [ FD_GUI_ACCDB_WIN_SAMPLES ];
     896             :   ulong agg_relocated_bytes_win   [ FD_GUI_ACCDB_WIN_SAMPLES ];
     897             :   ulong agg_misses_win            [ FD_GUI_ACCDB_WIN_SAMPLES ];
     898             : 
     899             :   /* Per-class delta rings (units per snap). */
     900             :   ulong class_acq_win         [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     901             :   ulong class_acq_wr_win      [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     902             :   ulong class_not_found_win   [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     903             :   ulong class_evicted_win     [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     904             :   ulong class_preevicted_win  [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     905             :   ulong class_commit_new_win  [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     906             :   ulong class_commit_over_win [ FD_ACCDB_CACHE_CLASS_CNT ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     907             : 
     908             :   ulong partition_cnt;            /* live count from accdb_shmem; <= FD_GUI_MAX_PARTITIONS */
     909             :   ulong partition_read_ops_win    [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     910             :   ulong partition_bytes_read_win  [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     911             :   ulong partition_write_ops_win   [ FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     912             :   ulong partition_bytes_written_win[FD_GUI_MAX_PARTITIONS ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     913             : 
     914             :   /* Per-partition snapshots (most recent values, for non-rate fields:
     915             :      offset, layer, write_offset, bytes_freed, ticks, compaction state). */
     916             :   fd_accdb_shmem_partition_info_t partitions[ FD_GUI_MAX_PARTITIONS ];
     917             : 
     918             :   /* Cumulative counters from the previous snap for delta computation. */
     919             :   ulong partition_prev_read_ops    [ FD_GUI_MAX_PARTITIONS ];
     920             :   ulong partition_prev_bytes_read  [ FD_GUI_MAX_PARTITIONS ];
     921             :   ulong partition_prev_write_ops   [ FD_GUI_MAX_PARTITIONS ];
     922             :   ulong partition_prev_bytes_written[FD_GUI_MAX_PARTITIONS ];
     923             : 
     924             :   /* Per-tier (i.e. hot, warm, cold) EMAs (10 min half-life) used to project when the next
     925             :      compaction will trigger. */
     926             :   long         tier_sample_nanos;
     927             :   fd_gui_ema_t tier_fill_bps_ema[ FD_ACCDB_COMPACTION_LAYER_CNT ];
     928             :   fd_gui_ema_t tier_free_bps_ema[ FD_ACCDB_COMPACTION_LAYER_CNT ];
     929             :   double next_compaction_remaining_secs;
     930             :   ulong  next_compaction_partition_idx;
     931             : 
     932             :   /* Per-tile accdb stats.  At init we walk the topology and assign a
     933             :      slot to each tile that uses the account database (execle, execrp,
     934             :      replay, tower, rpc, resolv, accdb).  Each slot keeps cumulative
     935             :      previous values for delta computation and a triangular-weighted
     936             :      delta ring (same cadence / weighting as the aggregate rings). */
     937             :   ulong  accdb_tile_cnt;
     938             :   ushort accdb_tile_topo_idx [ FD_TOPO_MAX_TILES ]; /* index into topo->tiles */
     939             :   uchar  accdb_tile_kind     [ FD_TOPO_MAX_TILES ];
     940             : 
     941             :   /* Most-recent cumulative values per tile, plus the snapshot from
     942             :      the previous snap for delta computation. */
     943             :   ulong tile_cur_acquired          [ FD_TOPO_MAX_TILES ];
     944             :   ulong tile_cur_acquired_writable [ FD_TOPO_MAX_TILES ];
     945             :   ulong tile_cur_bytes_read        [ FD_TOPO_MAX_TILES ];
     946             :   ulong tile_cur_bytes_copied      [ FD_TOPO_MAX_TILES ];
     947             :   ulong tile_cur_bytes_written     [ FD_TOPO_MAX_TILES ];
     948             :   ulong tile_cur_read_ops          [ FD_TOPO_MAX_TILES ];
     949             :   ulong tile_cur_write_ops         [ FD_TOPO_MAX_TILES ];
     950             :   ulong tile_cur_misses            [ FD_TOPO_MAX_TILES ];
     951             :   ulong tile_cur_evicted           [ FD_TOPO_MAX_TILES ];
     952             :   ulong tile_cur_committed         [ FD_TOPO_MAX_TILES ];
     953             :   ulong tile_cur_acquire_calls     [ FD_TOPO_MAX_TILES ];
     954             :   uchar tile_cur_status            [ FD_TOPO_MAX_TILES ]; /* 1=running, 2=shutdown */
     955             : 
     956             :   ulong tile_prev_acquired         [ FD_TOPO_MAX_TILES ];
     957             :   ulong tile_prev_acquired_writable[ FD_TOPO_MAX_TILES ];
     958             :   ulong tile_prev_bytes_read       [ FD_TOPO_MAX_TILES ];
     959             :   ulong tile_prev_bytes_copied     [ FD_TOPO_MAX_TILES ];
     960             :   ulong tile_prev_bytes_written    [ FD_TOPO_MAX_TILES ];
     961             :   ulong tile_prev_read_ops         [ FD_TOPO_MAX_TILES ];
     962             :   ulong tile_prev_write_ops        [ FD_TOPO_MAX_TILES ];
     963             :   ulong tile_prev_misses           [ FD_TOPO_MAX_TILES ];
     964             :   ulong tile_prev_evicted          [ FD_TOPO_MAX_TILES ];
     965             :   ulong tile_prev_committed        [ FD_TOPO_MAX_TILES ];
     966             :   ulong tile_prev_acquire_calls    [ FD_TOPO_MAX_TILES ];
     967             : 
     968             :   /* Per-tile delta rings. */
     969             :   ulong tile_acquired_win         [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     970             :   ulong tile_acquired_writable_win[ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     971             :   ulong tile_bytes_read_win       [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     972             :   ulong tile_bytes_copied_win     [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     973             :   ulong tile_bytes_written_win    [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     974             :   ulong tile_read_ops_win         [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     975             :   ulong tile_write_ops_win        [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     976             :   ulong tile_misses_win           [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     977             :   ulong tile_evicted_win          [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     978             :   ulong tile_committed_win        [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     979             :   ulong tile_acquire_calls_win    [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_WIN_SAMPLES ];
     980             : 
     981             :   long  tile_sparkline_bucket_start_nanos [ FD_TOPO_MAX_TILES ];
     982             :   ulong tile_sparkline_acq_bucket         [ FD_TOPO_MAX_TILES ];
     983             :   ulong tile_sparkline_acq_wr_bucket      [ FD_TOPO_MAX_TILES ];
     984             :   /* Per-second rates of the last N completed buckets as the JSON array
     985             :      body they are emitted as: the "%.2f," elements concatenated oldest
     986             :      first in text[ 0, text_len ).  Elements contain no comma but the
     987             :      one that ends them, so the boundaries are the commas. */
     988             :   char  tile_sparkline_acq_text        [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_SPARKLINE_SAMPLES*FD_GUI_ACCDB_SPARKLINE_TEXT_MAX ];
     989             :   char  tile_sparkline_acq_wr_text     [ FD_TOPO_MAX_TILES ][ FD_GUI_ACCDB_SPARKLINE_SAMPLES*FD_GUI_ACCDB_SPARKLINE_TEXT_MAX ];
     990             :   ulong tile_sparkline_acq_text_len    [ FD_TOPO_MAX_TILES ];
     991             :   ulong tile_sparkline_acq_wr_text_len [ FD_TOPO_MAX_TILES ];
     992             :   ulong tile_sparkline_count           [ FD_TOPO_MAX_TILES ];  /* completed buckets, capped at FD_GUI_ACCDB_SPARKLINE_SAMPLES */
     993             : };
     994             : 
     995             : typedef struct fd_gui_accdb_stats fd_gui_accdb_stats_t;
     996             : 
     997             : /* fd_gui_summary_t holds the aggregate node-level state rendered by the
     998             :    "summary" websocket topic and the terminal dashboard. */
     999             : 
    1000             : struct fd_gui_summary {
    1001             :   fd_pubkey_t identity_key[ 1 ];
    1002             :   int         has_vote_key;
    1003             :   fd_pubkey_t vote_key[ 1 ];
    1004             :   char vote_key_base58[ FD_BASE58_ENCODED_32_SZ ];
    1005             :   char identity_key_base58[ FD_BASE58_ENCODED_32_SZ ];
    1006             : 
    1007             :   int          is_alpenglow;
    1008             :   int          is_voting;      /* configured to vote (vote account set) */
    1009             :   char const * version;
    1010             :   char const * cluster;
    1011             :   char         accounts_database_path[ PATH_MAX ];
    1012             :   char         gui_database_path     [ PATH_MAX ];
    1013             : 
    1014             :   char   wfs_bank_hash[ FD_BASE58_ENCODED_32_SZ ];
    1015             :   ulong  wfs_slot;
    1016             :   ushort expected_shred_version;
    1017             :   int    wfs_enabled;
    1018             : 
    1019             :   ulong vote_distance;
    1020             :   int   vote_state;
    1021             : 
    1022             :   long  startup_time_nanos;
    1023             : 
    1024             :   fd_gui_boot_progress_t boot_progress;
    1025             :   fd_gui_boot_progress_t prev_boot_progress;
    1026             : 
    1027             :   int schedule_strategy;
    1028             : 
    1029             :   ulong  identity_account_balance;
    1030             :   ulong  vote_account_balance;
    1031             :   ushort vote_commission;
    1032             :   ulong  estimated_slot_duration_nanos;
    1033             : 
    1034             :   ulong sock_tile_cnt;
    1035             :   ulong mlx5_tile_cnt;
    1036             :   ulong net_tile_cnt;
    1037             :   ulong quic_tile_cnt;
    1038             :   ulong verify_tile_cnt;
    1039             :   ulong resolv_tile_cnt;
    1040             :   ulong execle_tile_cnt;
    1041             :   ulong execrp_tile_cnt;
    1042             :   ulong shred_tile_cnt;
    1043             : 
    1044             :   ulong slot_rooted;
    1045             :   ulong slot_finalized;
    1046             :   ulong slot_notarized;
    1047             :   ulong slot_optimistically_confirmed;
    1048             :   ulong slot_estimated;
    1049             :   ulong slot_caught_up;
    1050             :   ulong slot_repair;
    1051             :   ulong slot_turbine;
    1052             :   ulong slot_reset;
    1053             :   ulong slot_storage;
    1054             :   ulong slot_tower;
    1055             :   ulong slot_tower_bank_seq; /* tracks canonical fork frontier */
    1056             :   ulong slot_voted;
    1057             :   ulong active_fork_cnt;
    1058             : 
    1059             :   struct {
    1060             :     ulong epoch;
    1061             :     ulong skipped;
    1062             :     ulong total;
    1063             :   } skip_rate[ 2 ];
    1064             : 
    1065             :   fd_gui_ephemeral_slot_t slots_max_turbine[ FD_GUI_TURBINE_SLOT_HISTORY_SZ+1UL ];
    1066             :   fd_gui_ephemeral_slot_t slots_max_repair [ FD_GUI_REPAIR_SLOT_HISTORY_SZ +1UL ];
    1067             : 
    1068             :   /* catchup_* is run-length encoded. i.e. adjacent pairs represent
    1069             :      contiguous runs */
    1070             :   ulong catch_up_turbine[ FD_GUI_TURBINE_CATCH_UP_HISTORY_SZ ];
    1071             :   ulong catch_up_turbine_sz;
    1072             : 
    1073             :   ulong catch_up_repair[ FD_GUI_REPAIR_CATCH_UP_HISTORY_SZ ];
    1074             :   ulong catch_up_repair_sz;
    1075             : 
    1076             :   ulong estimated_tps_history_idx;
    1077             :   struct {
    1078             :    ulong vote_failed;
    1079             :    ulong vote_success;
    1080             :    ulong nonvote_success;
    1081             :    ulong nonvote_failed;
    1082             :   } estimated_tps_history[ FD_GUI_TPS_HISTORY_SAMPLE_CNT ];
    1083             : 
    1084             :   fd_gui_network_stats_t network_stats_current[ 1 ];
    1085             :   fd_gui_network_stats_t network_stats_prev   [ 1 ];
    1086             :   int                    network_stats_has_prev;
    1087             : 
    1088             :   /* EMA-smoothed network throughput (bytes/sec) with a 1-second
    1089             :      half-life. */
    1090             :   fd_gui_ema_t             ingress_ema[ FD_GUI_NET_PROTO_CNT ];
    1091             :   fd_gui_ema_t             egress_ema[ FD_GUI_NET_PROTO_CNT ];
    1092             :   long                     net_rate_prev_ts;
    1093             :   fd_gui_rate_entry_t *    ingress_maxq;
    1094             :   fd_gui_rate_entry_t *    egress_maxq;
    1095             : 
    1096             :   fd_gui_accdb_stats_t    accdb[ 1 ];
    1097             : 
    1098             :   fd_gui_accounts_stats_t accounts_stats_reference[ 1 ];
    1099             :   fd_gui_accounts_stats_t accounts_stats_current  [ 1 ];
    1100             :   int                     accounts_stats_have_reference;
    1101             : 
    1102             :   fd_gui_txn_waterfall_t txn_waterfall_reference[ 1 ];
    1103             :   fd_gui_txn_waterfall_t txn_waterfall_current  [ 1 ];
    1104             : 
    1105             :   fd_gui_tile_stats_t tile_stats_reference[ 1 ];
    1106             :   fd_gui_tile_stats_t tile_stats_current  [ 1 ];
    1107             : 
    1108             :   ulong progcache_history_idx;
    1109             :   ulong progcache_hits_history   [ FD_GUI_PROGCACHE_HISTORY_CNT ];
    1110             :   ulong progcache_lookups_history[ FD_GUI_PROGCACHE_HISTORY_CNT ];
    1111             :   ulong progcache_hits_1min;
    1112             :   ulong progcache_lookups_1min;
    1113             : 
    1114             :   fd_gui_tile_timers_t      tile_timers_reference[ FD_TOPO_MAX_TILES ];
    1115             :   fd_gui_tile_timers_t      tile_timers_current  [ FD_TOPO_MAX_TILES ];
    1116             :   fd_gui_tile_timers_hist_t tile_timers_packed   [ FD_TOPO_MAX_TILES ];
    1117             : 
    1118             :   /* Topo tile indices in display order, built once on init. */
    1119             :   ulong tile[ FD_TOPO_MAX_TILES ];
    1120             :   ulong tile_cnt;
    1121             : };
    1122             : 
    1123             : typedef struct fd_gui_summary fd_gui_summary_t;
    1124             : 
    1125             : struct fd_gui_timeline_query_bucket {
    1126             :   ulong fields[ FD_GUI_TIMELINE_FIELD_CNT ];
    1127             : };
    1128             : typedef struct fd_gui_timeline_query_bucket fd_gui_timeline_query_bucket_t;
    1129             : 
    1130             : /* Workspace for timeline queries.  The GUI tile is single threaded and
    1131             :    each user runs to completion, so the members alias. */
    1132             : 
    1133             : union fd_gui_timeline_scratch {
    1134             :   fd_gui_shred_event_t           events[ FD_GUI_TIMELINE_QUERY_SHRED_MAX ];
    1135             :   fd_gui_timeline_query_bucket_t buckets[ FD_GUI_TIMELINE_QUERY_MAX_BUCKETS ];
    1136             : };
    1137             : typedef union fd_gui_timeline_scratch fd_gui_timeline_scratch_t;
    1138             : 
    1139             : struct fd_gui {
    1140             :   fd_gui_timeline_scratch_t timeline_scratch;
    1141             : 
    1142             :   fd_http_server_t * http;
    1143             :   fd_topo_t const * topo;
    1144             :   fd_accdb_shmem_t const * accdb_shmem;
    1145             : 
    1146             :   void * db; /* GUI database */
    1147             :   void * hist;
    1148             : 
    1149             :   double tick_per_ns;
    1150             :   ulong  tile_cnt;
    1151             : 
    1152             :   long next_sample_1sec;
    1153             :   long next_sample_200millis;
    1154             :   long next_sample_100millis;
    1155             :   long next_sample_50millis;
    1156             :   long next_sample_40millis;
    1157             :   long next_sample_25millis;
    1158             :   long next_sample_10millis;
    1159             : 
    1160             :   ulong leader_begin_slot;
    1161             :   ulong leader_end_slot;
    1162             :   ulong leader_slot_pending;
    1163             :   ulong leader_bank_seq_pending;
    1164             : 
    1165             :   fd_gui_summary_t summary;
    1166             : 
    1167             :   struct {
    1168             :     int                        valid;
    1169             :     fd_diag_system_resources_t resources;
    1170             :   } system;
    1171             : 
    1172             :   /* Scratch record for the synthesized skipped slots returned by
    1173             :      fd_gui_slot_get_canon_safe. */
    1174             :   fd_gui_slot_t skipped_scratch[ 1 ];
    1175             : 
    1176             :   struct {
    1177             :     ulong              slot_cnt;
    1178             :     fd_gui_ag_slot_t * slot; /* [slot_cnt] */
    1179             :   } ag;
    1180             : 
    1181             :   /* used for estimating slot duration */
    1182             :   fd_gui_turbine_slot_t turbine_slots[ FD_GUI_TURBINE_RECV_TIMESTAMPS ];
    1183             : 
    1184             :   /* Reusable scratch for reassembling a single slot's transactions at
    1185             :      query time (fd_gui_printf_slot_transactions_request). */
    1186             :   struct {
    1187             :     ulong                      max;
    1188             :     fd_gui_store_txn_start_t * starts; /* [max] */
    1189             :     fd_gui_store_txn_end_t *   ends;   /* [max] */
    1190             :     fd_gui_slot_txn_join_t *   joined; /* [max] */
    1191             :   } slot_txn_scratch;
    1192             : 
    1193             :   struct {
    1194             :     ulong landed_slot;
    1195             :     ulong landed_bank_seq;
    1196             :     ulong voted_slot;
    1197             :   } landed_votes[ FD_GUI_LANDED_VOTE_MAX ];
    1198             :   ulong landed_vote_cnt;
    1199             : 
    1200             :   struct {
    1201             :     int has_block_engine;
    1202             :     char name[ 16 ];
    1203             :     char url[ FD_URL_MAX ];
    1204             :     char ip_cstr[ 40 ]; /* IPv4 or IPv6 cstr */
    1205             :     int status;
    1206             :   } block_engine;
    1207             : 
    1208             :   struct {
    1209             :     /* The epoch we are currently in, advanced at epoch_info ingest. */
    1210             :     ulong current_epoch;
    1211             : 
    1212             :     int                 has_epoch_schedule;
    1213             :     fd_epoch_schedule_t epoch_schedule;
    1214             : 
    1215             :     ulong missed_vote_history_dirty; /* epoch whose missed vote history changed since the last broadcast, ULONG_MAX if none */
    1216             : 
    1217             :     uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) lsched_scratch[ FD_EPOCH_LEADERS_FOOTPRINT(MAX_STAKE_WEIGHTS, MAX_SLOTS_PER_EPOCH) ];
    1218             :     fd_vote_stake_weight_t stakes_scratch[ MAX_STAKE_WEIGHTS ];
    1219             :   } epoch;
    1220             : 
    1221             :   fd_gui_peers_ctx_t * peers; /* full-client */
    1222             : 
    1223             :   /* One decoded shred event for fd_gui_printf_shreds_window, which
    1224             :      prints a window's events column by column and would otherwise
    1225             :      decode the batches once per column. */
    1226             :   struct {
    1227             :     fd_gui_shred_scratch_t * ev;  /* [max] */
    1228             :     ulong                    max;
    1229             :   } shred_scratch;
    1230             : 
    1231             :   /* Earliest REPLAY_EXEC_DONE timestamp per shred of the
    1232             :      FD_GUI_EXEC_DONE_SLOT_CNT most recent slots, LONG_MAX if none */
    1233             :   struct {
    1234             :     ulong  slot[ FD_GUI_EXEC_DONE_SLOT_CNT ];
    1235             :     long * ts; /* [FD_GUI_EXEC_DONE_SLOT_CNT][FD_SHRED_BLK_MAX] */
    1236             :   } exec_done;
    1237             : 
    1238             :   struct {
    1239             :     ulong leader_shred_cnt;      /* A gauge counting the number of leader shreds seen on the SHRED_OUT link.  Resets at
    1240             :                                     the end of a leader slot.  This works because leader fecs are published in order. */
    1241             :     ulong leader_shred_slot;     /* The slot leader_shred_cnt is currently counting, ULONG_MAX if none. Used to reset
    1242             :                                     counter after an abandoned/ended slot. This works because leader fecs are published
    1243             :                                     in order. */
    1244             : 
    1245             :     fd_gui_shred_builder_t builder;
    1246             :     ulong                  dropped_event_cnt;
    1247             : 
    1248             :     /* Events with insert_time_ns below this watermark have already
    1249             :        been pushed to clients. */
    1250             :     long broadcast_watermark_ns;
    1251             :   } shreds;
    1252             : 
    1253             :   struct {
    1254             :     ulong                   pending_cnt;
    1255             :     fd_gui_snapsv_pending_t pending[ FD_GUI_SNAPSV_PENDING_MAX ];
    1256             :   } snapsv;
    1257             : };
    1258             : 
    1259             : typedef struct fd_gui fd_gui_t;
    1260             : 
    1261             : FD_PROTOTYPES_BEGIN
    1262             : 
    1263             : /* fd_gui_timeline_day_get returns the retained calendar day for day_idx, or NULL if not found. */
    1264             : 
    1265             : fd_gui_timeline_day_t *
    1266             : fd_gui_timeline_day_get( fd_gui_t * gui,
    1267             :                          ulong      day_idx );
    1268             : 
    1269             : /* fd_gui_handle_replay_txn records replay transaction metadata. */
    1270             : 
    1271             : void
    1272             : fd_gui_handle_replay_txn( fd_gui_t *                       gui,
    1273             :                           fd_replay_txn_executed_t const * txn,
    1274             :                           long                             now );
    1275             : 
    1276             : /* fd_gui_tile_timers_diff computes the compact, display-ready diff of a
    1277             :    single tile's timers between two raw cumulative samples `prev` and
    1278             :    `cur`, writing it into `out`. */
    1279             : 
    1280             : void
    1281             : fd_gui_tile_timers_diff( fd_gui_tile_timers_hist_t *  out,
    1282             :                          fd_gui_tile_timers_t const * prev,
    1283             :                          fd_gui_tile_timers_t const * cur,
    1284             :                          ulong                        tile_idx,
    1285             :                          long                         sample_time_nanos );
    1286             : 
    1287             : /* fd_gui_boot_snapshot_slot returns the boot slot: the incremental's
    1288             :    if one loaded, else the full's, ULONG_MAX before any load.  Parsed
    1289             :    from archive filenames, so best-effort. */
    1290             : 
    1291             : FD_FN_PURE static inline ulong
    1292           0 : fd_gui_boot_snapshot_slot( fd_gui_t const * gui ) {
    1293           0 :   for( ulong i=FD_GUI_BOOT_PROGRESS_SNAPSHOT_CNT; i>0UL; i-- ) {
    1294           0 :     ulong slot = gui->summary.boot_progress.loading_snapshot[ i-1UL ].slot;
    1295           0 :     if( slot!=ULONG_MAX ) return slot;
    1296           0 :   }
    1297           0 :   return ULONG_MAX;
    1298           0 : }
    1299             : 
    1300             : /* fd_gui_wfs_mode recomputes the WFS verdict (see fd_wfs.h); gossip's
    1301             :    WAIT_FOR_SUPERMAJORITY_STATE gauge cannot tell MATCH from NOOP, as
    1302             :    both end in STATE_DONE. */
    1303             : 
    1304             : FD_FN_PURE static inline int
    1305           0 : fd_gui_wfs_mode( fd_gui_t const * gui ) {
    1306           0 :   return fd_wfs_mode( gui->summary.wfs_slot,
    1307           0 :                       gui->summary.wfs_bank_hash[ 0 ]=='\0',
    1308           0 :                       (ulong)gui->summary.expected_shred_version,
    1309           0 :                       fd_gui_boot_snapshot_slot( gui ) );
    1310           0 : }
    1311             : 
    1312             : FD_FN_CONST ulong
    1313             : fd_gui_align( void );
    1314             : 
    1315             : ulong
    1316             : fd_gui_footprint( ulong tile_cnt,
    1317             :                   ulong max_live_slots,
    1318             :                   ulong max_txn_per_slot );
    1319             : 
    1320             : void *
    1321             : fd_gui_new( void *                   shmem,
    1322             :             fd_http_server_t *       http,
    1323             :             char const *             version,
    1324             :             char const *             cluster,
    1325             :             uchar const *            identity_key,
    1326             :             int                      has_vote_key,
    1327             :             uchar const *            vote_key,
    1328             :             int                      is_alpenglow,
    1329             :             ulong                    max_live_slots,
    1330             :             ulong                    max_txn_per_slot,
    1331             :             int                      snapshots_enabled,
    1332             :             int                      is_voting,
    1333             :             int                      schedule_strategy,
    1334             :             char const *             wfs_expected_bank_hash_cstr,
    1335             :             ushort                   expected_shred_version,
    1336             :             ulong                    wfs_slot,
    1337             :             char const *             accounts_database_path,
    1338             :             char const *             gui_database_path,
    1339             :             void *                   db,
    1340             :             fd_topo_t const *        topo,
    1341             :             fd_accdb_shmem_t const * accdb_shmem,
    1342             :             long                     now );
    1343             : 
    1344             : fd_gui_t *
    1345             : fd_gui_join( void * shmem );
    1346             : 
    1347             : void
    1348             : fd_gui_set_identity( fd_gui_t *    gui,
    1349             :                      uchar const * identity_pubkey );
    1350             : 
    1351             : void
    1352             : fd_gui_handle_diag_snapshot( fd_gui_t *    gui,
    1353             :                              void const *  data,
    1354             :                              ulong         data_sz );
    1355             : 
    1356             : void
    1357             : fd_gui_ws_open( fd_gui_t *  gui,
    1358             :                 ulong       conn_id,
    1359             :                 long now );
    1360             : 
    1361             : int
    1362             : fd_gui_ws_message( fd_gui_t *    gui,
    1363             :                    ulong         ws_conn_id,
    1364             :                    uchar const * data,
    1365             :                    ulong         data_len );
    1366             : 
    1367             : void
    1368             : fd_gui_became_leader( fd_gui_t * gui,
    1369             :                       ulong      slot,
    1370             :                       long       start_time_nanos,
    1371             :                       long       end_time_nanos,
    1372             :                       ulong      max_compute_units,
    1373             :                       ulong      max_microblocks,
    1374             :                       ulong      bank_seq );
    1375             : 
    1376             : void
    1377             : fd_gui_unbecame_leader( fd_gui_t *                gui,
    1378             :                         ulong                     _slot,
    1379             :                         fd_done_packing_t const * done_packing );
    1380             : 
    1381             : void
    1382             : fd_gui_done_draining( fd_gui_t * gui,
    1383             :                       long       now );
    1384             : 
    1385             : void
    1386             : fd_gui_microblock_execution_begin( fd_gui_t *   gui,
    1387             :                                    long         tspub_ns,
    1388             :                                    ulong        _slot,
    1389             :                                    fd_txn_e_t * txns,
    1390             :                                    ulong        txn_cnt,
    1391             :                                    uint         microblock_idx,
    1392             :                                    ulong        pack_txn_idx,
    1393             :                                    ulong        bank_seq,
    1394             :                                    long         now );
    1395             : 
    1396             : void
    1397             : fd_gui_microblock_execution_end( fd_gui_t *     gui,
    1398             :                                  long           tspub_ns,
    1399             :                                  ulong          bank_idx,
    1400             :                                  ulong          _slot,
    1401             :                                  ulong          txn_cnt,
    1402             :                                  fd_txn_p_t *   txns,
    1403             :                                  ulong          pack_txn_idx,
    1404             :                                  fd_txn_ns_dt_t txn_ns_dt,
    1405             :                                  long           exec_end_ticks,
    1406             :                                  ulong          tips,
    1407             :                                  ulong          bank_seq,
    1408             :                                  long           now );
    1409             : 
    1410             : int
    1411             : fd_gui_poll( fd_gui_t * gui, long now );
    1412             : 
    1413             : long
    1414             : fd_gui_next_deadline( fd_gui_t const * gui );
    1415             : 
    1416             : void
    1417             : fd_gui_handle_block_engine_update( fd_gui_t *                              gui,
    1418             :                                    fd_bundle_block_engine_update_t const * update );
    1419             : 
    1420             : void
    1421             : fd_gui_handle_shred( fd_gui_t * gui,
    1422             :                      ulong      slot,
    1423             :                      ulong      shred_idx,
    1424             :                      int        is_turbine,
    1425             :                      long       tsorig,
    1426             :                      long       now );
    1427             : 
    1428             : void
    1429             : fd_gui_handle_leader_fec( fd_gui_t * gui,
    1430             :                           ulong      slot,
    1431             :                           ulong      fec_shred_cnt,
    1432             :                           int        is_end_of_slot,
    1433             :                           long       tsorig,
    1434             :                           long       now );
    1435             : 
    1436             : void
    1437             : fd_gui_handle_exec_txn_done( fd_gui_t * gui,
    1438             :                              ulong      slot,
    1439             :                              ulong      start_shred_idx,
    1440             :                              ulong      end_shred_idx,
    1441             :                              long       tsorig_ns,
    1442             :                              long       tspub_ns,
    1443             :                              long       now );
    1444             : 
    1445             : void
    1446             : fd_gui_handle_repair_slot( fd_gui_t * gui, ulong slot, long now );
    1447             : 
    1448             : void
    1449             : fd_gui_handle_repair_request( fd_gui_t * gui, ulong slot, ulong shred_idx, long tsorig, long now );
    1450             : 
    1451             : void
    1452             : fd_gui_handle_snapshot_update( fd_gui_t *                 gui,
    1453             :                                fd_snapct_update_t const * msg );
    1454             : 
    1455             : void
    1456             : fd_gui_handle_snapsv_update( fd_gui_t *              gui,
    1457             :                              ulong                   sig,
    1458             :                              fd_snapsv_msg_t const * msg,
    1459             :                              ulong                   sz,
    1460             :                              int                     eom,
    1461             :                              long                    now );
    1462             : 
    1463             : void
    1464             : fd_gui_stage_snapshot_manifest( fd_gui_t *                       gui,
    1465             :                                 fd_snapshot_manifest_t const *   manifest );
    1466             : 
    1467             : void
    1468             : fd_gui_handle_epoch_info( fd_gui_t *                  gui,
    1469             :                           fd_epoch_info_msg_t const * epoch_info,
    1470             :                           long                        now );
    1471             : 
    1472             : void
    1473             : fd_gui_handle_replay_update( fd_gui_t *                         gui,
    1474             :                              fd_replay_slot_completed_t const * slot_completed,
    1475             :                              ulong                              vote_slot,
    1476             :                              long                               now );
    1477             : 
    1478             : void
    1479             : fd_gui_stage_landed_vote( fd_gui_t * gui,
    1480             :                           ulong      landed_slot,
    1481             :                           ulong      landed_bank_seq,
    1482             :                           ulong      voted_slot );
    1483             : 
    1484             : void
    1485             : fd_gui_handle_genesis_hash( fd_gui_t *        gui,
    1486             :                             fd_hash_t const * msg );
    1487             : 
    1488             : /* fd_gui_handle_root_advanced is invoked on REPLAY_SIG_ROOT_ADVANCED.
    1489             :    It roots the (slot, bank_seq) fork and updates the dependent state. */
    1490             : void
    1491             : fd_gui_handle_root_advanced( fd_gui_t * gui,
    1492             :                              ulong      slot,
    1493             :                              ulong      bank_seq,
    1494             :                              long       now );
    1495             : 
    1496             : /* fd_gui_handle_oc_advanced is invoked on REPLAY_SIG_OC_ADVANCED.  It
    1497             :    marks the (slot, bank_seq) fork optimistically confirmed. */
    1498             : void
    1499             : fd_gui_handle_oc_advanced( fd_gui_t * gui,
    1500             :                            ulong      slot,
    1501             :                            ulong      bank_seq,
    1502             :                            long       now );
    1503             : 
    1504             : void
    1505             : fd_gui_handle_ag_notarized( fd_gui_t *        gui,
    1506             :                             ulong             slot,
    1507             :                             fd_hash_t const * block_id,
    1508             :                             uchar             notarization_kind );
    1509             : 
    1510             : void
    1511             : fd_gui_handle_ag_finalized( fd_gui_t *        gui,
    1512             :                             ulong             slot,
    1513             :                             fd_hash_t const * block_id,
    1514             :                             uchar             finalization_kind );
    1515             : 
    1516             : void
    1517             : fd_gui_handle_ag_skip_cert( fd_gui_t * gui,
    1518             :                             ulong      slot );
    1519             : 
    1520             : void
    1521             : fd_gui_handle_ag_leader( fd_gui_t * gui,
    1522             :                          ulong      parent_slot );
    1523             : 
    1524             : void
    1525             : fd_gui_ag_register_block( fd_gui_t *        gui,
    1526             :                           ulong             slot,
    1527             :                           fd_hash_t const * block_id,
    1528             :                           ulong             bank_seq );
    1529             : 
    1530             : int
    1531             : fd_gui_ag_slot_is_skip_notarized( fd_gui_t const * gui,
    1532             :                                   ulong            slot );
    1533             : 
    1534             : /* fd_gui_slot_get_canon_safe resolves slot number `_slot` on the
    1535             :    canonical fork and returns a renderable record. */
    1536             : fd_gui_slot_t *
    1537             : fd_gui_slot_get_canon_safe( fd_gui_t * gui, ulong _slot );
    1538             : 
    1539             : 
    1540             : /* fd_gui_epoch returns the DB EPOCH record for `epoch`, or NULL if no
    1541             :    record for that epoch is durable in the store.  The returned pointer
    1542             :    may be written in place. */
    1543             : static inline fd_gui_epoch_t *
    1544           0 : fd_gui_epoch( fd_gui_t * gui, ulong epoch ) {
    1545           0 :   fd_gui_hist_epoch_key_t key[ 1 ]; key->epoch = epoch;
    1546           0 :   return (fd_gui_epoch_t *)fd_gui_hist_kv_get( gui, FD_GUI_HIST_EPOCH, key );
    1547           0 : }
    1548             : 
    1549             : /* fd_gui_current_epoch returns the record for the current epoch,
    1550             :    or NULL if no epoch has been ingested yet. */
    1551             : static inline fd_gui_epoch_t *
    1552           0 : fd_gui_current_epoch( fd_gui_t * gui ) {
    1553           0 :   if( FD_UNLIKELY( gui->epoch.current_epoch==ULONG_MAX ) ) return NULL;
    1554           0 :   return fd_gui_epoch( gui, gui->epoch.current_epoch );
    1555           0 : }
    1556             : 
    1557             : /* fd_gui_epoch_get_or_create returns the mutable DB EPOCH record
    1558             :    for `epoch`, creating it if none exists yet. */
    1559             : static inline fd_gui_epoch_t *
    1560             : fd_gui_epoch_get_or_create( fd_gui_t * gui,
    1561             :                             ulong      epoch,
    1562           0 :                             int *      created_out ) {
    1563           0 :   fd_gui_epoch_t * rec = fd_gui_epoch( gui, epoch );
    1564           0 :   if( FD_LIKELY( rec ) ) { if( created_out ) *created_out = 0; return rec; }
    1565             : 
    1566           0 :   fd_gui_hist_epoch_key_t key[ 1 ]; key->epoch = epoch;
    1567           0 :   rec = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_EPOCH, key );
    1568           0 :   if( FD_UNLIKELY( !rec ) ) return NULL;
    1569           0 :   if( created_out ) *created_out = 1;
    1570           0 :   return rec;
    1571           0 : }
    1572             : 
    1573             : /* fd_gui_first_replay_slot returns the lowest slot number the validator
    1574             :    has authoritative knowledge of during this run. */
    1575             : static inline ulong
    1576           0 : fd_gui_first_replay_slot( fd_gui_t const * gui ) {
    1577           0 :   ulong slot_incremental = gui->summary.boot_progress.loading_snapshot[ FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX ].slot;
    1578           0 :   ulong slot_full        = gui->summary.boot_progress.loading_snapshot[ FD_GUI_BOOT_PROGRESS_FULL_SNAPSHOT_IDX ].slot;
    1579           0 :   return fd_ulong_if( slot_incremental!=ULONG_MAX, slot_incremental+1UL,
    1580           0 :                       fd_ulong_if( slot_full!=ULONG_MAX, slot_full+1UL, ULONG_MAX ) );
    1581           0 : }
    1582             : 
    1583             : static inline fd_gui_leader_slot_t *
    1584             : fd_gui_slot_leader_get( fd_gui_t * gui,
    1585             :                         ulong      slot,
    1586           0 :                         ulong      bank_seq ) {
    1587           0 :   if( FD_UNLIKELY( !gui->db || slot==ULONG_MAX || bank_seq==ULONG_MAX ) ) return NULL;
    1588           0 :   fd_gui_hist_leader_slot_key_t key = { .slot = slot, .bank_seq = bank_seq };
    1589           0 :   return fd_gui_hist_kv_get( gui, FD_GUI_HIST_LEADER_SLOT, &key );
    1590           0 : }
    1591             : 
    1592             : /* fd_gui_slot_leader_get_or_create returns the mutable returns the
    1593             :    DB LEADER_SLOT record for (slot, bank_seq), creating it if none
    1594             :    exists yet. */
    1595             : 
    1596             : static inline fd_gui_leader_slot_t *
    1597             : fd_gui_slot_leader_get_or_create( fd_gui_t * gui,
    1598             :                                   ulong      slot,
    1599           0 :                                   ulong      bank_seq ) {
    1600           0 :   if( FD_UNLIKELY( !gui->db || slot==ULONG_MAX ) ) return NULL;
    1601             : 
    1602           0 :   fd_gui_leader_slot_t * rec = fd_gui_slot_leader_get( gui, slot, bank_seq );
    1603           0 :   if( FD_LIKELY( rec ) ) return rec;
    1604             : 
    1605           0 :   fd_gui_hist_leader_slot_key_t key = { .slot = slot, .bank_seq = bank_seq };
    1606           0 :   fd_gui_leader_slot_t * seed = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_LEADER_SLOT, &key );
    1607           0 :   if( FD_UNLIKELY( !seed ) ) return NULL;
    1608             : 
    1609           0 :   *seed = (fd_gui_leader_slot_t){
    1610           0 :     .slot                    = slot,
    1611           0 :     .bank_seq                = bank_seq,
    1612           0 :     .leader_start_time       = LONG_MAX,
    1613           0 :     .leader_end_time         = LONG_MAX,
    1614           0 :     .txn_insert_time_min_ns  = LONG_MAX,
    1615           0 :     .txn_insert_time_max_ns  = LONG_MIN,
    1616           0 :     .max_microblocks         = ULONG_MAX,
    1617           0 :     .max_compute_units       = ULONG_MAX,
    1618           0 :     .microblocks_upper_bound = UINT_MAX,
    1619           0 :     .begin_microblocks       = 0U,
    1620           0 :     .end_microblocks         = 0U,
    1621           0 :     .has_waterfall           = 0,
    1622           0 :     .unbecame_leader         = 0
    1623           0 :   };
    1624           0 :   return seed;
    1625           0 : }
    1626             : 
    1627             : /* fd_gui_slot_leader_get_any returns the DB record for key matching
    1628             :    (lslot, *), regardless of fork, or NULL if no record exists for lslot. */
    1629             : 
    1630             : static inline fd_gui_leader_slot_t *
    1631           0 : fd_gui_slot_leader_get_any( fd_gui_t * gui, ulong _lslot ) {
    1632           0 :   if( FD_UNLIKELY( !gui->db || _lslot==ULONG_MAX ) ) return NULL;
    1633           0 :   return fd_gui_hist_kv_get_slot_any( gui, FD_GUI_HIST_LEADER_SLOT, _lslot );
    1634           0 : }
    1635             : 
    1636             : /* fd_gui_slot_get returns the DB record for the exact key
    1637             :    (slot, bank_seq), or NULL if none exists. */
    1638             : 
    1639             : static inline fd_gui_slot_t *
    1640           0 : fd_gui_slot_get( fd_gui_t * gui, ulong slot, ulong bank_seq ) {
    1641           0 :   if( FD_UNLIKELY( slot==ULONG_MAX || bank_seq==ULONG_MAX ) ) return NULL;
    1642           0 :   fd_gui_hist_slot_key_t key;
    1643           0 :   key.slot = slot; key.bank_seq = bank_seq;
    1644           0 :   return (fd_gui_slot_t *)fd_gui_hist_kv_get( gui, FD_GUI_HIST_SLOT, &key );
    1645           0 : }
    1646             : 
    1647             : /* fd_gui_slot_parent_get returns the DB record for the parent of `c` on
    1648             :    c's own fork or NULL if the parent is unknown or has no record. */
    1649             : 
    1650             : static inline fd_gui_slot_t *
    1651           0 : fd_gui_slot_parent_get( fd_gui_t * gui, fd_gui_slot_t const * c ) {
    1652           0 :   if( FD_UNLIKELY( !c ) ) return NULL;
    1653           0 :   return fd_gui_slot_get( gui, c->parent_slot, c->parent_bank_seq );
    1654           0 : }
    1655             : 
    1656             : /* fd_gui_slot_get_canon returns the DB record for slot number `_slot`
    1657             :    on the canonical fork, or NULL if `_slot` is skipped, above the
    1658             :    canonical tip, or the tip is not yet resolved.  The canonical fork is
    1659             :    the consensus fork chosen by tower fork choice. */
    1660             : 
    1661             : static inline fd_gui_slot_t *
    1662           0 : fd_gui_slot_get_canon( fd_gui_t * gui, ulong _slot ) {
    1663           0 :   if( FD_UNLIKELY( _slot==ULONG_MAX ) ) return NULL;
    1664             : 
    1665             :   /* At or below the root the canonical fork has settled, meaning there
    1666             :      should be exactly one NOT_SKIPPED entry per slot. */
    1667           0 :   if( FD_LIKELY( gui->summary.slot_rooted!=ULONG_MAX && _slot<=gui->summary.slot_rooted ) ) {
    1668           0 :     fd_gui_hist_kv_slot_iter_t it[ 1 ];
    1669           0 :     for( fd_gui_hist_kv_iter_begin( gui, it, FD_GUI_HIST_SLOT, _slot ); it->rec; fd_gui_hist_kv_iter_next( it ) ) {
    1670           0 :       fd_gui_slot_t const * rec = (fd_gui_slot_t const *)it->rec;
    1671           0 :       if( FD_LIKELY( rec->skip==FD_GUI_SKIP_STATUS_NOT_SKIPPED ) ) return fd_gui_slot_get( gui, _slot, it->bank_seq );
    1672           0 :     }
    1673           0 :     return NULL; /* skipped on the canonical fork, or no record */
    1674           0 :   }
    1675             : 
    1676             :   /* Otherwise do a fork-walk from the canonical frontier. */
    1677           0 :   ulong slot     = gui->summary.slot_tower;
    1678           0 :   ulong bank_seq = gui->summary.slot_tower_bank_seq;
    1679           0 :   for(;;) {
    1680           0 :     if( FD_UNLIKELY( slot==ULONG_MAX || bank_seq==ULONG_MAX ) ) return NULL;
    1681           0 :     if( FD_UNLIKELY( slot<_slot ) ) return NULL; /* walked past _slot: skipped or above-tip */
    1682           0 :     fd_gui_slot_t * n = fd_gui_slot_get( gui, slot, bank_seq );
    1683           0 :     if( FD_UNLIKELY( !n ) ) return NULL; /* evicted / no record */
    1684           0 :     if( FD_LIKELY( slot==_slot ) ) return n;
    1685           0 :     slot     = n->parent_slot;
    1686           0 :     bank_seq = n->parent_bank_seq;
    1687           0 :   }
    1688           0 : }
    1689             : 
    1690             : /* fd_gui_slot_get_any returns the DB record for slot number `_slot`
    1691             :    without any fork awareness: it is a simple store lookup that returns
    1692             :    the first record that exists for `_slot`. */
    1693             : 
    1694             : static inline fd_gui_slot_t *
    1695           0 : fd_gui_slot_get_any( fd_gui_t * gui, ulong _slot ) {
    1696           0 :   if( FD_UNLIKELY( _slot==ULONG_MAX ) ) return NULL;
    1697             : 
    1698           0 :   fd_gui_hist_kv_slot_iter_t it[ 1 ];
    1699           0 :   fd_gui_hist_kv_iter_begin( gui, it, FD_GUI_HIST_SLOT, _slot );
    1700           0 :   if( FD_UNLIKELY( !it->rec ) ) return NULL;
    1701           0 :   return fd_gui_slot_get( gui, _slot, it->bank_seq );
    1702           0 : }
    1703             : 
    1704             : /* fd_gui_slot_is_ancestor returns 1 if (ancestor_slot,
    1705             :    ancestor_bank_seq) is known to be an ancestor of (slot, bank_seq), 0
    1706             :    otherwise. */
    1707             : 
    1708             : static inline int
    1709             : fd_gui_slot_is_ancestor( fd_gui_t * gui,
    1710             :                          ulong      ancestor_slot,
    1711             :                          ulong      ancestor_bank_seq,
    1712             :                          ulong      slot,
    1713           0 :                          ulong      bank_seq ) {
    1714           0 :   for(;;) {
    1715           0 :     if( FD_UNLIKELY( slot==ancestor_slot ) ) return bank_seq==ancestor_bank_seq;
    1716           0 :     if( FD_UNLIKELY( slot<ancestor_slot ) ) return 0;
    1717             : 
    1718           0 :     fd_gui_slot_t const * rec = fd_gui_slot_get( gui, slot, bank_seq );
    1719           0 :     if( FD_UNLIKELY( !rec ) ) return 0;
    1720           0 :     slot     = rec->parent_slot;
    1721           0 :     bank_seq = rec->parent_bank_seq;
    1722           0 :   }
    1723           0 : }
    1724             : 
    1725             : /* fd_gui_slot_is_skipped returns 1 if `slot` is skipped on the fork
    1726             :    whose tip is (des, des_bank_seq) and whose root is `root`, 0
    1727             :    otherwise. */
    1728             : 
    1729             : static inline int
    1730           0 : fd_gui_slot_is_skipped( fd_gui_t * gui, ulong root, ulong des, ulong des_bank_seq, ulong slot ) {
    1731           0 :   fd_gui_slot_t * c = fd_gui_slot_get( gui, des, des_bank_seq );
    1732           0 :   ulong cslot = des;
    1733           0 :   while( c ) {
    1734           0 :     if( FD_UNLIKELY( root==cslot ) ) return 0; /* on the fork, not skipped */
    1735           0 :     ulong pslot = c->parent_slot;
    1736           0 :     fd_gui_slot_t * p = fd_gui_slot_parent_get( gui, c );
    1737           0 :     if( FD_UNLIKELY( p && pslot<slot && cslot>slot ) ) return 1; /* in-between two on-fork records, skipped */
    1738           0 :     c     = p;
    1739           0 :     cslot = pslot;
    1740           0 :   }
    1741           0 :   return 0; /* slot not between root and des, or is unknown */
    1742           0 : }
    1743             : 
    1744             : /* fd_gui_get_epoch_by_slot returns the mutable DB EPOCH record covering
    1745             :    `_slot` (resolved via the epoch schedule), or NULL if no epoch
    1746             :    schedule is known yet or no record for that epoch is present. */
    1747             : 
    1748             : static inline fd_gui_epoch_t *
    1749           0 : fd_gui_get_epoch_by_slot( fd_gui_t * gui, ulong _slot ) {
    1750           0 :   if( FD_UNLIKELY( !gui->epoch.has_epoch_schedule ) ) return NULL;
    1751           0 :   ulong epoch = fd_slot_to_epoch( &gui->epoch.epoch_schedule, _slot, NULL );
    1752           0 :   return fd_gui_epoch( gui, epoch );
    1753           0 : }
    1754             : 
    1755             : /* fd_gui_slot_voter_state returns one of FD_GUI_IS_VOTER_* for
    1756             :    `_slot`. */
    1757             : 
    1758             : static inline uchar
    1759           0 : fd_gui_slot_voter_state( fd_gui_t * gui, ulong _slot ) {
    1760           0 :   fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    1761           0 :   if( FD_UNLIKELY( !epoch || _slot<epoch->start_slot ) ) return FD_GUI_IS_VOTER_UNKNOWN;
    1762           0 :   ulong idx = _slot - epoch->start_slot;
    1763           0 :   return idx<epoch->slot_cnt ? epoch->is_voter[ idx ] : FD_GUI_IS_VOTER_UNKNOWN;
    1764           0 : }
    1765             : 
    1766             : static inline uchar
    1767           0 : fd_gui_slot_vote_rewarded_state( fd_gui_t * gui, ulong _slot ) {
    1768           0 :   fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    1769           0 :   if( FD_UNLIKELY( !epoch || _slot<epoch->start_slot ) ) return FD_GUI_VOTE_REWARDED_UNKNOWN;
    1770           0 :   ulong idx = _slot - epoch->start_slot;
    1771           0 :   return idx<epoch->slot_cnt ? epoch->vote_rewarded[ idx ] : FD_GUI_VOTE_REWARDED_UNKNOWN;
    1772           0 : }
    1773             : 
    1774             : static inline ushort
    1775           0 : fd_gui_slot_vote_count( fd_gui_t * gui, ulong _slot ) {
    1776           0 :   fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    1777           0 :   if( FD_UNLIKELY( !epoch || _slot<epoch->start_slot ) ) return USHORT_MAX;
    1778           0 :   ulong idx = _slot-epoch->start_slot;
    1779           0 :   return idx<epoch->slot_cnt ? epoch->vote_count[ idx ] : USHORT_MAX;
    1780           0 : }
    1781             : 
    1782             : /* fd_gui_get_slot_leader returns the leader pubkey scheduled for
    1783             :    `_slot`, or NULL if `epoch` is NULL, `_slot` is outside the epoch,
    1784             :    or the schedule index is indeterminate. */
    1785             : 
    1786             : static inline fd_pubkey_t const *
    1787           0 : fd_gui_get_slot_leader( fd_gui_epoch_t const * epoch, ulong _slot ) {
    1788           0 :   if( FD_UNLIKELY( !epoch ) ) return NULL;
    1789           0 :   if( FD_UNLIKELY( _slot<epoch->start_slot || _slot>=epoch->start_slot+epoch->slot_cnt ) ) return NULL;
    1790           0 :   ulong rot = (_slot - epoch->start_slot)/FD_EPOCH_SLOTS_PER_ROTATION;
    1791           0 :   uint  idx = epoch->sched[ rot ];
    1792           0 :   if( FD_UNLIKELY( idx>=epoch->pub_cnt ) ) return NULL; /* indeterminate */
    1793           0 :   return &epoch->pub[ idx ];
    1794           0 : }
    1795             : 
    1796             : static inline int
    1797           0 : fd_gui_slot_is_mine( fd_gui_t * gui, ulong _slot ) {
    1798           0 :   fd_gui_epoch_t const * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    1799           0 :   fd_pubkey_t const * slot_leader = fd_gui_get_slot_leader( epoch, _slot );
    1800           0 :   if( FD_UNLIKELY( !slot_leader ) ) return 0;
    1801           0 :   return !memcmp( slot_leader->uc, gui->summary.identity_key->uc, 32UL );
    1802           0 : }
    1803             : 
    1804             : /* fd_gui_slot_get_or_create returns the mutable returns the mutable DB
    1805             :    SLOT record for (_slot, bank_seq), creating it if none exists yet. */
    1806             : 
    1807             : static inline fd_gui_slot_t *
    1808             : fd_gui_slot_get_or_create( fd_gui_t * gui,
    1809             :                            ulong      _slot,
    1810             :                            ulong      _parent_slot,
    1811             :                            ulong      bank_seq,
    1812           0 :                            ulong      parent_bank_seq ) {
    1813           0 :   fd_gui_slot_t * rec = fd_gui_slot_get( gui, _slot, bank_seq );
    1814           0 :   if( FD_LIKELY( rec ) ) return rec;
    1815             : 
    1816           0 :   fd_gui_epoch_t * epoch = fd_gui_get_epoch_by_slot( gui, _slot );
    1817           0 :   fd_pubkey_t const * slot_leader = fd_gui_get_slot_leader( epoch, _slot );
    1818           0 :   int mine = fd_int_if( !!slot_leader, !memcmp( slot_leader->uc, gui->summary.identity_key->uc, 32UL ), 0 );
    1819             : 
    1820           0 :   fd_gui_hist_slot_key_t key;
    1821           0 :   key.slot = _slot; key.bank_seq = bank_seq;
    1822           0 :   fd_gui_slot_t * meta = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_SLOT, &key );
    1823           0 :   if( FD_UNLIKELY( !meta ) ) return NULL;
    1824             : 
    1825           0 :   meta->slot              = _slot;
    1826           0 :   meta->bank_seq          = bank_seq;
    1827           0 :   meta->parent_bank_seq   = parent_bank_seq;
    1828           0 :   meta->parent_slot       = _parent_slot;
    1829           0 :   meta->vote_slot          = ULONG_MAX;
    1830           0 :   meta->vote_latency_exact = FD_GUI_VOTE_LATENCY_NOT_VOTED;
    1831           0 :   meta->max_compute_units = UINT_MAX;
    1832           0 :   meta->completed_time    = LONG_MAX;
    1833           0 :   meta->parent_completed_time = LONG_MAX;
    1834           0 :   meta->mine              = (uchar)(mine & 1);
    1835           0 :   meta->is_voter          = FD_GUI_IS_VOTER_NO;
    1836           0 :   if( FD_UNLIKELY( gui->summary.is_alpenglow ) ) meta->is_voter = FD_GUI_IS_VOTER_UNKNOWN;
    1837           0 :   meta->skip              = FD_GUI_SKIP_STATUS_UNKNOWN;
    1838           0 :   meta->level             = (uchar)( _slot ? FD_GUI_SLOT_LEVEL_INCOMPLETE : FD_GUI_SLOT_LEVEL_ROOTED ); /* slot 0 always rooted */
    1839           0 :   meta->notarization_kind = FD_GUI_AG_NOTAR_NONE;
    1840           0 :   meta->finalization_kind = FD_GUI_AG_FINAL_NONE;
    1841           0 :   meta->vote_rewarded     = FD_GUI_VOTE_REWARDED_UNKNOWN;
    1842           0 :   meta->vote_count        = USHORT_MAX;
    1843           0 :   meta->vote_failed       = UINT_MAX;
    1844           0 :   meta->vote_success      = UINT_MAX;
    1845           0 :   meta->nonvote_success   = UINT_MAX;
    1846           0 :   meta->nonvote_failed    = UINT_MAX;
    1847           0 :   meta->compute_units     = UINT_MAX;
    1848           0 :   meta->transaction_fee   = ULONG_MAX;
    1849           0 :   meta->priority_fee      = ULONG_MAX;
    1850           0 :   meta->tips              = ULONG_MAX;
    1851           0 :   meta->shred_cnt         = UINT_MAX;
    1852           0 :   memset( meta->block_hash.uc, 0, sizeof(fd_hash_t) );
    1853             : 
    1854           0 :   if( FD_UNLIKELY( mine && epoch ) ) epoch->my_total_slots++;
    1855             : 
    1856           0 :   return meta;
    1857           0 : }
    1858             : 
    1859             : FD_PROTOTYPES_END
    1860             : 
    1861             : #endif /* HEADER_fd_src_disco_gui_fd_gui_h */

Generated by: LCOV version 1.14