LCOV - code coverage report
Current view: top level - disco/gui - fd_gui_peers.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 723 0.0 %
Date: 2025-10-13 04:42:14 Functions: 0 20 0.0 %

          Line data    Source code
       1             : #include "fd_gui_peers.h"
       2             : #include "fd_gui_printf.h"
       3             : 
       4             : #include "../../ballet/json/cJSON.h"
       5             : #include "../../flamenco/gossip/fd_gossip_private.h"
       6             : #include "../../flamenco/gossip/fd_gossip_types.h"
       7             : 
       8             : #define LOGGING 0
       9             : 
      10             : FD_FN_CONST ulong
      11           0 : fd_gui_peers_align( void ) {
      12           0 :   ulong a = 128UL;
      13           0 :   a = fd_ulong_max( a, alignof(fd_gui_peers_ctx_t)              );
      14           0 :   a = fd_ulong_max( a, fd_gui_peers_live_table_align()          );
      15           0 :   a = fd_ulong_max( a, fd_gui_peers_bandwidth_tracking_align()  );
      16           0 :   a = fd_ulong_max( a, fd_gui_peers_node_pubkey_map_align()     );
      17           0 :   a = fd_ulong_max( a, fd_gui_peers_node_sock_map_align()       );
      18           0 :   a = fd_ulong_max( a, alignof(fd_gui_peers_ws_conn_t)          );
      19           0 :   FD_TEST( fd_ulong_pow2_up( a )==a );
      20           0 :   return a;
      21           0 : }
      22             : 
      23             : FD_FN_CONST ulong
      24           0 : fd_gui_peers_footprint( ulong max_ws_conn_cnt ) {
      25           0 :   ulong pubkey_chain_cnt = fd_gui_peers_node_pubkey_map_chain_cnt_est( FD_CONTACT_INFO_TABLE_SIZE );
      26           0 :   ulong sock_chain_cnt   = fd_gui_peers_node_sock_map_chain_cnt_est  ( FD_CONTACT_INFO_TABLE_SIZE );
      27             : 
      28           0 :   ulong l = FD_LAYOUT_INIT;
      29           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_gui_peers_ctx_t),             sizeof(fd_gui_peers_ctx_t)                                              );
      30           0 :   l = FD_LAYOUT_APPEND( l, fd_gui_peers_live_table_align(),         fd_gui_peers_live_table_footprint        ( FD_CONTACT_INFO_TABLE_SIZE ) );
      31           0 :   l = FD_LAYOUT_APPEND( l, fd_gui_peers_bandwidth_tracking_align(), fd_gui_peers_bandwidth_tracking_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
      32           0 :   l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_pubkey_map_align(),    fd_gui_peers_node_pubkey_map_footprint   ( pubkey_chain_cnt )           );
      33           0 :   l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_sock_map_align(),      fd_gui_peers_node_sock_map_footprint     ( sock_chain_cnt )             );
      34           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_gui_peers_ws_conn_t),         max_ws_conn_cnt*sizeof(fd_gui_peers_ws_conn_t)                          );
      35             : 
      36           0 :   return FD_LAYOUT_FINI( l, fd_gui_peers_align() );
      37           0 : }
      38             : 
      39             : void *
      40             : fd_gui_peers_new( void *             shmem,
      41             :                   fd_http_server_t * http,
      42             :                   fd_topo_t *        topo,
      43             :                   ulong              max_ws_conn_cnt,
      44           0 :                   long               now ) {
      45           0 :     if( FD_UNLIKELY( !shmem ) ) {
      46           0 :       FD_LOG_WARNING(( "NULL shmem" ));
      47           0 :       return NULL;
      48           0 :     }
      49             : 
      50           0 :     if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_gui_peers_align() ) ) ) {
      51           0 :       FD_LOG_WARNING(( "misaligned shmem" ));
      52           0 :       return NULL;
      53           0 :     }
      54             : 
      55           0 :     ulong pubkey_chain_cnt = fd_gui_peers_node_pubkey_map_chain_cnt_est( FD_CONTACT_INFO_TABLE_SIZE );
      56           0 :     ulong sock_chain_cnt   = fd_gui_peers_node_sock_map_chain_cnt_est  ( FD_CONTACT_INFO_TABLE_SIZE );
      57             : 
      58           0 :     FD_SCRATCH_ALLOC_INIT( l, shmem );
      59           0 :     fd_gui_peers_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_gui_peers_ctx_t),             sizeof(fd_gui_peers_ctx_t)                                              );
      60           0 :     void * _live_table       = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_live_table_align(),         fd_gui_peers_live_table_footprint        ( FD_CONTACT_INFO_TABLE_SIZE ) );
      61           0 :     void * _bw_tracking      = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_bandwidth_tracking_align(), fd_gui_peers_bandwidth_tracking_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
      62           0 :     void * _pubkey_map       = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_pubkey_map_align(),    fd_gui_peers_node_pubkey_map_footprint   ( pubkey_chain_cnt )           );
      63           0 :     void * _sock_map         = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_sock_map_align(),      fd_gui_peers_node_sock_map_footprint     ( sock_chain_cnt )             );
      64           0 :     ctx->client_viewports    = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_gui_peers_ws_conn_t),         max_ws_conn_cnt*sizeof(fd_gui_peers_ws_conn_t)                          );
      65             : 
      66           0 :     for( ulong i = 0UL; i<max_ws_conn_cnt; i++ ) ctx->client_viewports[ i ].connected = 0;
      67             : 
      68           0 :     ctx->http = http;
      69           0 :     ctx->topo = topo;
      70             : 
      71           0 :     ctx->max_ws_conn_cnt   = max_ws_conn_cnt;
      72           0 :     ctx->open_ws_conn_cnt  = 0UL;
      73           0 :     ctx->active_ws_conn_id = ULONG_MAX;
      74             : 
      75           0 :     ctx->next_client_nanos              = now;
      76           0 :     ctx->next_metric_rate_update_nanos  = now;
      77           0 :     ctx->next_gossip_stats_update_nanos = now;
      78           0 :     memset( &ctx->gossip_stats, 0, sizeof(ctx->gossip_stats) );
      79             : 
      80           0 :     for( ulong i = 0; i<FD_CONTACT_INFO_TABLE_SIZE; i++) ctx->contact_info_table[ i ].valid = 0;
      81             : 
      82           0 :     ctx->live_table      = fd_gui_peers_live_table_join( fd_gui_peers_live_table_new( _live_table, FD_CONTACT_INFO_TABLE_SIZE ) );
      83           0 :     fd_gui_peers_live_table_seed( ctx->contact_info_table, FD_CONTACT_INFO_TABLE_SIZE, 42UL );
      84             : 
      85           0 :     ctx->bw_tracking     = fd_gui_peers_bandwidth_tracking_join( fd_gui_peers_bandwidth_tracking_new( _bw_tracking, FD_CONTACT_INFO_TABLE_SIZE ) );
      86           0 :     fd_gui_peers_bandwidth_tracking_seed( ctx->contact_info_table, FD_CONTACT_INFO_TABLE_SIZE, 42UL );
      87             : 
      88           0 :     ctx->node_pubkey_map = fd_gui_peers_node_pubkey_map_join( fd_gui_peers_node_pubkey_map_new( _pubkey_map, fd_gui_peers_node_pubkey_map_chain_cnt_est( FD_CONTACT_INFO_TABLE_SIZE ), 42UL ) );
      89           0 :     ctx->node_sock_map   = fd_gui_peers_node_sock_map_join  ( fd_gui_peers_node_sock_map_new  ( _sock_map,   fd_gui_peers_node_sock_map_chain_cnt_est  ( FD_CONTACT_INFO_TABLE_SIZE ), 42UL ) );
      90             : 
      91           0 :     return shmem;
      92           0 : }
      93             : 
      94             : fd_gui_peers_ctx_t *
      95           0 : fd_gui_peers_join( void * shmem ) {
      96           0 :   if( FD_UNLIKELY( !shmem ) ) {
      97           0 :     FD_LOG_WARNING(( "NULL shmem" ));
      98           0 :     return NULL;
      99           0 :   }
     100             : 
     101           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_gui_peers_align() ) ) ) {
     102           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     103           0 :     return NULL;
     104           0 :   }
     105             : 
     106           0 :   fd_gui_peers_ctx_t * ctx = (fd_gui_peers_ctx_t *)shmem;
     107             : 
     108           0 :   return ctx;
     109           0 : }
     110             : 
     111             : static ulong
     112             : fd_gui_sum_tiles_counter( fd_gui_peers_ctx_t * peers,
     113             :                           char const *         name,
     114             :                           ulong                tile_cnt,
     115           0 :                           ulong                metric_idx ) {
     116           0 :   ulong total = 0UL;
     117           0 :   for( ulong i = 0UL; i < peers->topo->tile_cnt; i++ ) {
     118           0 :     if( FD_UNLIKELY( !strcmp( peers->topo->tiles[ i ].name, name ) ) ) {
     119           0 :       FD_TEST( peers->topo->tiles[ i ].kind_id < tile_cnt );
     120           0 :       fd_topo_tile_t const * tile = &peers->topo->tiles[ i ];
     121           0 :       volatile ulong const * tile_metrics = fd_metrics_tile( tile->metrics );
     122           0 :       total += tile_metrics[ metric_idx ];
     123           0 :     }
     124           0 :   }
     125           0 :   return total;
     126           0 : }
     127             : 
     128             : static void
     129             : fd_gui_peers_gossip_stats_snap( fd_gui_peers_ctx_t *          peers,
     130             :                                 fd_gui_peers_gossip_stats_t * gossip_stats,
     131           0 :                                 long                          now ) {
     132           0 :   gossip_stats->sample_time = now;
     133           0 :   ulong gossvf_tile_cnt = fd_topo_tile_name_cnt( peers->topo, "gossvf"  );
     134           0 :   ulong gossip_tile_cnt = 1UL;
     135             : 
     136           0 :   gossip_stats->network_health_pull_response_msg_rx_success =
     137           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PULL_RESPONSE ) );
     138           0 :   gossip_stats->network_health_pull_response_msg_rx_failure =
     139           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_RESPONSE_NO_VALID_CRDS ) );
     140           0 :   gossip_stats->network_health_push_msg_rx_success =
     141           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PUSH ) );
     142           0 :   gossip_stats->network_health_push_msg_rx_failure =
     143           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PUSH_NO_VALID_CRDS ) );
     144           0 :   gossip_stats->network_health_push_crds_rx_success =
     145           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_UPSERTED_PUSH ) );
     146           0 :   gossip_stats->network_health_push_crds_rx_failure =
     147           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PUSH_STALE ) )
     148           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PUSH_DUPLICATE ) )
     149           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_SIGNATURE ) )
     150           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_RELAYER_NO_CONTACT_INFO ) )
     151           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_RELAYER_SHRED_VERSION ) )
     152           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_ORIGIN_NO_CONTACT_INFO ) )
     153           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_ORIGIN_SHRED_VERSION ) )
     154           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_INACTIVE ) )
     155           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_WALLCLOCK ) );
     156           0 :   gossip_stats->network_health_pull_response_crds_rx_success =
     157           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_UPSERTED_PULL_RESPONSE ) );
     158           0 :   gossip_stats->network_health_pull_response_crds_rx_failure =
     159           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_STALE ) )
     160           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_WALLCLOCK ) )
     161           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_DUPLICATE ) )
     162           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_DUPLICATE ) )
     163           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_SIGNATURE ) )
     164           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_RELAYER_SHRED_VERSION ) )
     165           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_ORIGIN_NO_CONTACT_INFO ) )
     166           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_ORIGIN_SHRED_VERSION ) )
     167           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_INACTIVE ) );
     168           0 :   gossip_stats->network_health_push_crds_rx_duplicate =
     169           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PUSH_DUPLICATE ) );
     170           0 :   gossip_stats->network_health_pull_response_crds_rx_duplicate =
     171           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_DUPLICATE ) )
     172           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_DUPLICATE ) );
     173             : 
     174           0 :   gossip_stats->network_health_total_stake = 0UL; /* todo ... fetch from RPC */
     175           0 :   gossip_stats->network_health_total_peers = 0UL; /* todo ... fetch from RPC */
     176             : 
     177           0 :   gossip_stats->network_health_connected_stake          = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_PEER_TOTAL_STAKE ) );
     178           0 :   gossip_stats->network_health_connected_staked_peers   = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_PEER_STAKED_COUNT ) );
     179           0 :   gossip_stats->network_health_connected_unstaked_peers = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_PEER_UNSTAKED_COUNT ) );
     180             : 
     181           0 :   gossip_stats->network_ingress_peer_sz = fd_ulong_min( fd_gui_peers_bandwidth_tracking_ele_cnt( peers->bw_tracking ), FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT );
     182           0 :   gossip_stats->network_ingress_total_bytes_per_sec = 0UL;
     183             : 
     184           0 :   for( fd_gui_peers_bandwidth_tracking_fwd_iter_t iter = fd_gui_peers_bandwidth_tracking_fwd_iter_init( peers->bw_tracking, &FD_GUI_PEERS_BW_TRACKING_INGRESS_SORT_KEY, peers->contact_info_table ), j = 0UL;
     185           0 :        !fd_gui_peers_bandwidth_tracking_fwd_iter_done( iter );
     186           0 :        iter = fd_gui_peers_bandwidth_tracking_fwd_iter_next( iter, peers->contact_info_table ), j++ ) {
     187           0 :     fd_gui_peers_node_t * cur = fd_gui_peers_bandwidth_tracking_fwd_iter_ele( iter, peers->contact_info_table );
     188             : 
     189           0 :     if( FD_UNLIKELY( j<gossip_stats->network_ingress_peer_sz ) ) {
     190           0 :       if( FD_LIKELY( cur->has_node_info ) ) FD_TEST( fd_cstr_printf_check( gossip_stats->network_ingress_peer_names[ j ], sizeof(gossip_stats->network_ingress_peer_names[ j ]), NULL, "%s", cur->name ) );
     191           0 :       else                                  gossip_stats->network_ingress_peer_names[ j ][ 0 ] = '\0';
     192           0 :       gossip_stats->network_ingress_peer_bytes_per_sec[ j ] = cur->gossvf_rx_sum.rate;
     193           0 :       fd_memcpy( &gossip_stats->network_ingress_peer_identities[ j ], cur->contact_info.pubkey.uc, 32UL );
     194           0 :     }
     195             : 
     196           0 :     gossip_stats->network_ingress_total_bytes_per_sec += cur->gossvf_rx_sum.rate;
     197           0 :   }
     198             : 
     199           0 :   gossip_stats->network_ingress_total_bytes =
     200           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PULL_REQUEST) )
     201           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PULL_RESPONSE) )
     202           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PUSH) )
     203           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PRUNE) )
     204           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PING) )
     205           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PONG) )
     206           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_UNPARSEABLE) )
     207           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PULL_REQUEST_NOT_CONTACT_INFO) )
     208           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PULL_REQUEST_LOOPBACK) )
     209           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PULL_REQUEST_INACTIVE) )
     210           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PULL_REQUEST_WALLCLOCK) )
     211           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PULL_REQUEST_SIGNATURE) )
     212           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PULL_REQUEST_SHRED_VERSION) )
     213           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PRUNE_DESTINATION) )
     214           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PRUNE_WALLCLOCK) )
     215           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PRUNE_SIGNATURE) )
     216           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PUSH_NO_VALID_CRDS) )
     217           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PULL_RESPONSE_NO_VALID_CRDS) )
     218           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PING_SIGNATURE) )
     219           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_DROPPED_PONG_SIGNATURE) );
     220             : 
     221           0 :   gossip_stats->network_egress_peer_sz = fd_ulong_min( fd_gui_peers_bandwidth_tracking_ele_cnt( peers->bw_tracking ), FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT );
     222             : 
     223           0 :   FD_TEST( gossip_stats->network_egress_peer_sz==gossip_stats->network_ingress_peer_sz );
     224             : 
     225           0 :   gossip_stats->network_egress_peer_sz = fd_ulong_min( fd_gui_peers_bandwidth_tracking_ele_cnt( peers->bw_tracking ), FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT );
     226           0 :   gossip_stats->network_egress_total_bytes_per_sec = 0UL;
     227             : 
     228           0 :   for( fd_gui_peers_bandwidth_tracking_fwd_iter_t iter = fd_gui_peers_bandwidth_tracking_fwd_iter_init( peers->bw_tracking, &FD_GUI_PEERS_BW_TRACKING_EGRESS_SORT_KEY, peers->contact_info_table ), j = 0UL;
     229           0 :        !fd_gui_peers_bandwidth_tracking_fwd_iter_done( iter );
     230           0 :        iter = fd_gui_peers_bandwidth_tracking_fwd_iter_next( iter, peers->contact_info_table ), j++ ) {
     231           0 :     fd_gui_peers_node_t * cur = fd_gui_peers_bandwidth_tracking_fwd_iter_ele( iter, peers->contact_info_table );
     232             : 
     233           0 :     if( FD_UNLIKELY( j<gossip_stats->network_egress_peer_sz ) ) {
     234           0 :       if( FD_LIKELY( cur->has_node_info ) ) FD_TEST( fd_cstr_printf_check( gossip_stats->network_egress_peer_names[ j ], sizeof(gossip_stats->network_egress_peer_names[ j ]), NULL, "%s", cur->name ) );
     235           0 :       else                                  gossip_stats->network_egress_peer_names[ j ][ 0 ] = '\0';
     236           0 :       gossip_stats->network_egress_peer_bytes_per_sec[ j ] = cur->gossip_tx_sum.rate;
     237           0 :       fd_memcpy( &gossip_stats->network_egress_peer_identities[ j ], cur->contact_info.pubkey.uc, 32UL );
     238           0 :     }
     239             : 
     240           0 :     gossip_stats->network_egress_total_bytes_per_sec += cur->gossip_tx_sum.rate;
     241           0 :   }
     242             : 
     243           0 :   gossip_stats->network_egress_total_bytes =
     244           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PING ) )
     245           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PONG ) )
     246           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PRUNE ) )
     247           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PULL_REQUEST ) )
     248           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PULL_RESPONSE ) )
     249           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PUSH ) );
     250             : 
     251           0 :   gossip_stats->storage_capacity = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_CAPACITY ) );
     252           0 :   gossip_stats->storage_expired_cnt = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_EXPIRED_COUNT ) );
     253           0 :   gossip_stats->storage_evicted_cnt = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_EVICTED_COUNT ) );
     254             : 
     255           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V1_IDX               ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_CONTACT_INFO_V1 )               );
     256           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_VOTE_IDX                          ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_VOTE )                          );
     257           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_LOWEST_SLOT_IDX                   ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_LOWEST_SLOT )                   );
     258           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_SNAPSHOT_HASHES_IDX               ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_SNAPSHOT_HASHES )               );
     259           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_ACCOUNTS_HASHES_IDX               ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_ACCOUNTS_HASHES )               );
     260           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_EPOCH_SLOTS_IDX                   ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_EPOCH_SLOTS )                   );
     261           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V1_IDX                    ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_VERSION_V1 )                    );
     262           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V2_IDX                    ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_VERSION_V2 )                    );
     263           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_NODE_INSTANCE_IDX                 ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_NODE_INSTANCE )                 );
     264           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_DUPLICATE_SHRED_IDX               ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_DUPLICATE_SHRED )               );
     265           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_INCREMENTAL_SNAPSHOT_HASHES_IDX   ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_INCREMENTAL_SNAPSHOT_HASHES )   );
     266           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V2_IDX               ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_CONTACT_INFO_V2 )               );
     267           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_LAST_VOTED_FORK_SLOTS_IDX ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_RESTART_LAST_VOTED_FORK_SLOTS ) );
     268           0 :   gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_HEAVIEST_FORK_IDX         ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_RESTART_HEAVIEST_FORK )         );
     269             : 
     270           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V1_IDX ] =
     271           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_CONTACT_INFO_V1 ) )
     272           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_CONTACT_INFO_V1 ) );
     273           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VOTE_IDX ] =
     274           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_VOTE ) )
     275           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_VOTE ) );
     276           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_LOWEST_SLOT_IDX ] =
     277           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_LOWEST_SLOT ) )
     278           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_LOWEST_SLOT ) );
     279           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_SNAPSHOT_HASHES_IDX ] =
     280           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_SNAPSHOT_HASHES ) )
     281           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_SNAPSHOT_HASHES ) );
     282           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_ACCOUNTS_HASHES_IDX ] =
     283           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_ACCOUNTS_HASHES ) )
     284           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_ACCOUNTS_HASHES ) );
     285           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_EPOCH_SLOTS_IDX ] =
     286           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_EPOCH_SLOTS ) )
     287           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_EPOCH_SLOTS ) );
     288           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V1_IDX ] =
     289           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_VERSION_V1 ) )
     290           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_VERSION_V1 ) );
     291           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V2_IDX ] =
     292           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_VERSION_V2 ) )
     293           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_VERSION_V2 ) );
     294           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_NODE_INSTANCE_IDX ] =
     295           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_NODE_INSTANCE ) )
     296           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_NODE_INSTANCE ) );
     297           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_DUPLICATE_SHRED_IDX ] =
     298           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_DUPLICATE_SHRED ) )
     299           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_DUPLICATE_SHRED ) );
     300           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_INCREMENTAL_SNAPSHOT_HASHES_IDX ] =
     301           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_INCREMENTAL_SNAPSHOT_HASHES ) )
     302           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_INCREMENTAL_SNAPSHOT_HASHES ) );
     303           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V2_IDX ] =
     304           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_CONTACT_INFO_V2 ) )
     305           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_CONTACT_INFO_V2 ) );
     306           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_LAST_VOTED_FORK_SLOTS_IDX ] =
     307           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_RESTART_LAST_VOTED_FORK_SLOTS ) )
     308           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_RESTART_LAST_VOTED_FORK_SLOTS ) );
     309           0 :   gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_HEAVIEST_FORK_IDX ] =
     310           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_RESTART_HEAVIEST_FORK ) )
     311           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_RESTART_HEAVIEST_FORK ) );
     312             : 
     313           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V1_IDX ] =
     314           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_CONTACT_INFO_V1 ) )
     315           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_CONTACT_INFO_V1 ) );
     316           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VOTE_IDX ] =
     317           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_VOTE ) )
     318           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_VOTE ) );
     319           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_LOWEST_SLOT_IDX ] =
     320           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_LOWEST_SLOT ) )
     321           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_LOWEST_SLOT ) );
     322           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_SNAPSHOT_HASHES_IDX ] =
     323           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_SNAPSHOT_HASHES ) )
     324           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_SNAPSHOT_HASHES ) );
     325           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_ACCOUNTS_HASHES_IDX ] =
     326           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_ACCOUNTS_HASHES ) )
     327           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_ACCOUNTS_HASHES ) );
     328           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_EPOCH_SLOTS_IDX ] =
     329           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_EPOCH_SLOTS ) )
     330           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_EPOCH_SLOTS ) );
     331           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V1_IDX ] =
     332           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_VERSION_V1 ) )
     333           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_VERSION_V1 ) );
     334           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V2_IDX ] =
     335           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_VERSION_V2 ) )
     336           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_VERSION_V2 ) );
     337           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_NODE_INSTANCE_IDX ] =
     338           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_NODE_INSTANCE ) )
     339           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_NODE_INSTANCE ) );
     340           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_DUPLICATE_SHRED_IDX ] =
     341           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_DUPLICATE_SHRED ) )
     342           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_DUPLICATE_SHRED ) );
     343           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_INCREMENTAL_SNAPSHOT_HASHES_IDX ] =
     344           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_INCREMENTAL_SNAPSHOT_HASHES ) )
     345           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_INCREMENTAL_SNAPSHOT_HASHES ) );
     346           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V2_IDX ] =
     347           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_CONTACT_INFO_V2 ) )
     348           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_CONTACT_INFO_V2 ) );
     349           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_LAST_VOTED_FORK_SLOTS_IDX ] =
     350           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_RESTART_LAST_VOTED_FORK_SLOTS ) )
     351           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_RESTART_LAST_VOTED_FORK_SLOTS ) );
     352           0 :   gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_HEAVIEST_FORK_IDX ] =
     353           0 :       fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_RESTART_HEAVIEST_FORK ) )
     354           0 :     + fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_RESTART_HEAVIEST_FORK ) );
     355             : 
     356           0 :   gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_REQUEST_IDX  ] = fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PULL_REQUEST ) );
     357           0 :   gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ] = fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PULL_RESPONSE ) );
     358           0 :   gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PUSH ) );
     359           0 :   gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PING_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PING ) );
     360           0 :   gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PONG_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PONG ) );
     361           0 :   gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PRUNE_IDX         ] = fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PRUNE ) );
     362             : 
     363           0 :   gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_REQUEST_IDX  ] =
     364           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PULL_REQUEST ) )
     365           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_NOT_CONTACT_INFO ) )
     366           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_LOOPBACK ) )
     367           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_INACTIVE ) )
     368           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_WALLCLOCK ) )
     369           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_SIGNATURE ) )
     370           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_SHRED_VERSION ) );
     371           0 :   gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ] =
     372           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PULL_RESPONSE ) )
     373           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_RESPONSE_NO_VALID_CRDS ) );
     374           0 :   gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX          ] =
     375           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PUSH ) )
     376           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PUSH_NO_VALID_CRDS ) );
     377           0 :   gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PING_IDX          ] =
     378           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PING ) )
     379           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PING_SIGNATURE ) );
     380           0 :   gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PONG_IDX          ] =
     381           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PONG ) )
     382           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PONG_SIGNATURE ) );
     383           0 :   gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PRUNE_IDX         ] =
     384           0 :       fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PRUNE ) )
     385           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PRUNE_DESTINATION ) )
     386           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PRUNE_WALLCLOCK ) )
     387           0 :     + fd_gui_sum_tiles_counter( peers, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PRUNE_SIGNATURE ) );
     388             : 
     389           0 :   gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_REQUEST_IDX  ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PULL_REQUEST ) );
     390           0 :   gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PULL_RESPONSE ) );
     391           0 :   gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PUSH ) );
     392           0 :   gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PING_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PING ) );
     393           0 :   gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PONG_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PONG ) );
     394           0 :   gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PRUNE_IDX         ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PRUNE ) );
     395             : 
     396           0 :   gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_REQUEST_IDX  ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PULL_REQUEST ) );
     397           0 :   gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PULL_RESPONSE ) );
     398           0 :   gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PUSH ) );
     399           0 :   gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PING_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PING ) );
     400           0 :   gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PONG_IDX          ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PONG ) );
     401           0 :   gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PRUNE_IDX         ] = fd_gui_sum_tiles_counter( peers, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PRUNE ) );
     402           0 : }
     403             : 
     404             : static int
     405             : fd_gui_peers_contact_info_eq( fd_contact_info_t const * ci1,
     406           0 :                               fd_contact_info_t const * ci2 ) {
     407           0 :   int ci_eq =
     408           0 :        ci1->shred_version                    == ci2->shred_version
     409           0 :     && ci1->instance_creation_wallclock_nanos== ci2->instance_creation_wallclock_nanos
     410             :  // && ci1->wallclock_nanos                  == ci2->wallclock_nanos
     411           0 :     && ci1->version.client                   == ci2->version.client
     412           0 :     && ci1->version.major                    == ci2->version.major
     413           0 :     && ci1->version.minor                    == ci2->version.minor
     414           0 :     && ci1->version.patch                    == ci2->version.patch
     415           0 :     && ci1->version.commit                   == ci2->version.commit
     416           0 :     && ci1->version.feature_set              == ci2->version.feature_set;
     417             : 
     418           0 :     if( FD_LIKELY( !ci_eq ) ) return 0;
     419           0 :     for( ulong j=0UL; j<(FD_CONTACT_INFO_SOCKET_CNT); j++ ) {
     420           0 :       if( FD_LIKELY( !(ci1->sockets[ j ].addr==ci2->sockets[ j ].addr && ci1->sockets[ j ].port==ci2->sockets[ j ].port) ) ) return 0;
     421           0 :     }
     422           0 :     return 1;
     423           0 : }
     424             : 
     425             : void
     426             : fd_gui_peers_handle_gossip_message( fd_gui_peers_ctx_t *  peers,
     427             :                                     uchar const *         payload,
     428             :                                     ulong                 payload_sz,
     429             :                                     fd_ip4_port_t const * peer_sock,
     430           0 :                                     int                   is_rx ) {
     431           0 :   fd_gui_peers_node_t * peer = fd_gui_peers_node_sock_map_ele_query( peers->node_sock_map, peer_sock, NULL, peers->contact_info_table );
     432             : 
     433             :   /* We set MAP_MULTI=1 since there are not guarantees that duplicates
     434             :      sockets wont exist. In cases where we see multiple sockets the
     435             :      update timestamp in fd_gui_peers_node_t is the tiebreaker */
     436           0 :   for( fd_gui_peers_node_t * p = peer; p!=NULL; p=(fd_gui_peers_node_t *)fd_gui_peers_node_sock_map_ele_next_const( p, NULL, peers->contact_info_table ) ) {
     437           0 :     if( peer->update_time_nanos>p->update_time_nanos ) peer = p;
     438           0 :   }
     439             : 
     440           0 :   if( FD_UNLIKELY( !peer ) ) return; /* NOP, peer not known yet */
     441             : 
     442           0 :   fd_gossip_view_t view[ 1 ];
     443           0 :   ulong decode_sz = fd_gossip_msg_parse( view, payload, payload_sz );
     444           0 :   if( FD_UNLIKELY( !decode_sz ) ) return; /* NOP, msg unparsable */
     445             : 
     446           0 :   FD_TEST( view->tag < FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT );
     447           0 :   fd_ptr_if( is_rx, &peer->gossvf_rx[ view->tag ], &peer->gossip_tx[ view->tag ] )->cur += payload_sz;
     448           0 :   fd_ptr_if( is_rx, (fd_gui_peers_metric_rate_t *)&peer->gossvf_rx_sum, (fd_gui_peers_metric_rate_t *)&peer->gossip_tx_sum )->cur += payload_sz;
     449             : #if LOGGING
     450             :   if( is_rx ) FD_LOG_WARNING(("payload rx=%lu", payload_sz ));
     451             :   else FD_LOG_WARNING(("payload tx=%lu", payload_sz ));
     452             : #endif
     453           0 : }
     454             : 
     455             : int
     456             : fd_gui_peers_handle_gossip_update( fd_gui_peers_ctx_t *               peers,
     457             :                                    fd_gossip_update_message_t const * update,
     458           0 :                                    long                               now ) {
     459           0 :     switch( update->tag ) {
     460           0 :       case FD_GOSSIP_UPDATE_TAG_CONTACT_INFO: {
     461             :         /* origin_pubkey should be the same as the contact info pubkey */
     462           0 :         if( FD_UNLIKELY( memcmp( update->contact_info.contact_info->pubkey.uc, update->origin_pubkey, 32UL ) ) ) {
     463           0 :           char ci_pk[ FD_BASE58_ENCODED_32_SZ ];
     464           0 :           char og_pk[ FD_BASE58_ENCODED_32_SZ ];
     465           0 :           fd_base58_encode_32( update->contact_info.contact_info->pubkey.uc, NULL, ci_pk );
     466           0 :           fd_base58_encode_32( update->origin_pubkey, NULL, og_pk );
     467             : 
     468           0 :           FD_LOG_ERR(( "invariant violation: update->contact_info.contact_info->pubkey.uc=%s != update->origin_pubkey=%s ", ci_pk, og_pk ));
     469           0 :         }
     470           0 :         if( FD_UNLIKELY( update->contact_info.idx>=FD_CONTACT_INFO_TABLE_SIZE ) ) FD_LOG_ERR(( "unexpected contact_info_idx %lu >= %lu", update->contact_info.idx, FD_CONTACT_INFO_TABLE_SIZE ));
     471           0 :         fd_gui_peers_node_t * peer = &peers->contact_info_table[ update->contact_info.idx ];
     472             : 
     473           0 :         if( FD_LIKELY( peer->valid ) ) {
     474             : #if LOGGING
     475             :           char _pk[ FD_BASE58_ENCODED_32_SZ ];
     476             :           fd_base58_encode_32( update->origin_pubkey, NULL, _pk );
     477             :           FD_LOG_WARNING(("UPDATE %lu pk=%s", update->contact_info.idx, _pk ));
     478             : #endif
     479             :           /* invariant checks */
     480           0 :           if( FD_UNLIKELY( memcmp( peer->contact_info.pubkey.uc, update->origin_pubkey, 32UL ) ) ) {
     481           0 :             char ci_pk[ FD_BASE58_ENCODED_32_SZ ];
     482           0 :             char og_pk[ FD_BASE58_ENCODED_32_SZ ];
     483           0 :             fd_base58_encode_32( peer->contact_info.pubkey.uc, NULL, ci_pk );
     484           0 :             fd_base58_encode_32( update->origin_pubkey, NULL, og_pk );
     485             : 
     486             :             /* A new pubkey is not allowed to overwrite an existing valid index */
     487           0 :             FD_LOG_ERR(( "invariant violation: peer->contact_info.pubkey.uc=%s != update->origin_pubkey=%s ", ci_pk, og_pk ));
     488           0 :           }
     489           0 :           FD_TEST( peer==fd_gui_peers_node_pubkey_map_ele_query_const( peers->node_pubkey_map, (fd_pubkey_t * )update->origin_pubkey, NULL, peers->contact_info_table ) );
     490           0 :           fd_gui_peers_node_t * peer_sock = fd_gui_peers_node_sock_map_ele_query( peers->node_sock_map, &peer->contact_info.sockets[ FD_CONTACT_INFO_SOCKET_GOSSIP ], NULL, peers->contact_info_table );
     491           0 :           int found = 0;
     492           0 :           for( fd_gui_peers_node_t * p = peer_sock; !!p; p=(fd_gui_peers_node_t *)fd_gui_peers_node_sock_map_ele_next_const( p, NULL, peers->contact_info_table ) ) {
     493           0 :             if( peer==p ) {
     494           0 :               found = 1;
     495           0 :               break;
     496           0 :             }
     497           0 :           }
     498           0 :           FD_TEST( found );
     499             : 
     500             :           /* update does nothing */
     501           0 :           if( FD_UNLIKELY( fd_gui_peers_contact_info_eq( &peer->contact_info, update->contact_info.contact_info ) ) ) {
     502           0 :             peer->contact_info.wallclock_nanos = update->contact_info.contact_info->wallclock_nanos;
     503           0 :             return FD_GUI_PEERS_NODE_NOP;
     504           0 :           }
     505             : 
     506           0 :           fd_gui_peers_node_sock_map_idx_remove_fast( peers->node_sock_map, update->contact_info.idx, peers->contact_info_table );
     507           0 :           fd_gui_peers_live_table_idx_remove        ( peers->live_table,    update->contact_info.idx, peers->contact_info_table );
     508           0 :           fd_gui_peers_bandwidth_tracking_idx_remove( peers->bw_tracking,   update->contact_info.idx, peers->contact_info_table );
     509             : 
     510           0 :           fd_memcpy( &peer->contact_info, update->contact_info.contact_info, sizeof(peer->contact_info) );
     511           0 :           peer->update_time_nanos = now;
     512             : 
     513           0 :           fd_gui_peers_bandwidth_tracking_idx_insert( peers->bw_tracking,   update->contact_info.idx, peers->contact_info_table );
     514           0 :           fd_gui_peers_live_table_idx_insert        ( peers->live_table,    update->contact_info.idx, peers->contact_info_table );
     515           0 :           fd_gui_peers_node_sock_map_idx_insert     ( peers->node_sock_map, update->contact_info.idx, peers->contact_info_table );
     516             : 
     517           0 :           return FD_GUI_PEERS_NODE_UPDATE;
     518           0 :         } else {
     519           0 :           FD_TEST( !fd_gui_peers_node_pubkey_map_ele_query_const( peers->node_pubkey_map, &update->contact_info.contact_info->pubkey, NULL, peers->contact_info_table ) );
     520             : #if LOGGING
     521             :           char _pk[ FD_BASE58_ENCODED_32_SZ ];
     522             :           fd_base58_encode_32( update->origin_pubkey, NULL, _pk );
     523             :           FD_LOG_WARNING(( "ADD %lu pk=%s", update->contact_info.idx, _pk ));
     524             : #endif
     525           0 :           memset( &peer->gossvf_rx,     0, sizeof(peer->gossvf_rx) );
     526           0 :           memset( &peer->gossip_tx,     0, sizeof(peer->gossip_tx) );
     527           0 :           memset( &peer->gossvf_rx_sum, 0, sizeof(peer->gossvf_rx_sum) );
     528           0 :           memset( &peer->gossip_tx_sum, 0, sizeof(peer->gossip_tx_sum) );
     529           0 :           peer->has_node_info = 0;
     530           0 :           peer->valid = 1;
     531           0 :           peer->update_time_nanos = now;
     532           0 :           fd_memcpy( &peer->contact_info, update->contact_info.contact_info, sizeof(peer->contact_info) );
     533             : 
     534             :           /* update pubkey_map, sock_map */
     535           0 :           fd_gui_peers_node_sock_map_idx_insert  ( peers->node_sock_map,   update->contact_info.idx, peers->contact_info_table );
     536           0 :           fd_gui_peers_node_pubkey_map_idx_insert( peers->node_pubkey_map, update->contact_info.idx, peers->contact_info_table );
     537             : 
     538             :           /* update live tables */
     539           0 :           fd_gui_peers_live_table_idx_insert        ( peers->live_table,  update->contact_info.idx, peers->contact_info_table );
     540           0 :           fd_gui_peers_bandwidth_tracking_idx_insert( peers->bw_tracking, update->contact_info.idx, peers->contact_info_table );
     541             : 
     542           0 :           fd_gui_printf_peers_view_resize( peers, fd_gui_peers_live_table_ele_cnt( peers->live_table ) );
     543           0 :           FD_TEST( !fd_http_server_ws_broadcast( peers->http ) );
     544             : 
     545           0 :           return FD_GUI_PEERS_NODE_ADD;
     546           0 :         }
     547             : 
     548           0 :         break;
     549           0 :       }
     550           0 :       case FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE: {
     551           0 :         if( FD_UNLIKELY( update->contact_info_remove.idx>=FD_CONTACT_INFO_TABLE_SIZE ) ) FD_LOG_ERR(( "unexpected remove_contact_info_idx %lu >= %lu", update->contact_info_remove.idx, FD_CONTACT_INFO_TABLE_SIZE ));
     552             : #if LOGGING
     553             :         char _pk[ FD_BASE58_ENCODED_32_SZ ];
     554             :         fd_base58_encode_32( update->origin_pubkey, NULL, _pk );
     555             :         FD_LOG_WARNING(( "REMOVE %lu pk=%s",update->contact_info.idx, _pk ));
     556             : #endif
     557             : 
     558           0 :         fd_gui_peers_node_t * peer = &peers->contact_info_table[ update->contact_info_remove.idx ];
     559             : 
     560             :         /* invariant checks */
     561           0 :         FD_TEST( peer->valid ); /* Should have already been in the table */
     562           0 :         FD_TEST( peer==fd_gui_peers_node_pubkey_map_ele_query_const( peers->node_pubkey_map, (fd_pubkey_t * )update->origin_pubkey, NULL, peers->contact_info_table ) );
     563           0 :         fd_gui_peers_node_t * peer_sock = fd_gui_peers_node_sock_map_ele_query( peers->node_sock_map, &peer->contact_info.sockets[ FD_CONTACT_INFO_SOCKET_GOSSIP ], NULL, peers->contact_info_table );
     564           0 :         int found = 0;
     565           0 :         for( fd_gui_peers_node_t const * p = peer_sock; !!p; p=(fd_gui_peers_node_t const *)fd_gui_peers_node_sock_map_ele_next_const( p, NULL, peers->contact_info_table ) ) {
     566           0 :           if( peer==p ) {
     567           0 :             found = 1;
     568           0 :             break;
     569           0 :           }
     570           0 :         }
     571           0 :         FD_TEST( found );
     572             : 
     573           0 :         fd_gui_peers_live_table_idx_remove          ( peers->live_table,      update->contact_info_remove.idx, peers->contact_info_table );
     574           0 :         fd_gui_peers_bandwidth_tracking_idx_remove  ( peers->bw_tracking,     update->contact_info_remove.idx, peers->contact_info_table );
     575           0 :         fd_gui_peers_node_sock_map_idx_remove_fast  ( peers->node_sock_map,   update->contact_info_remove.idx, peers->contact_info_table );
     576           0 :         fd_gui_peers_node_pubkey_map_idx_remove_fast( peers->node_pubkey_map, update->contact_info_remove.idx, peers->contact_info_table );
     577           0 :         peer->valid = 0;
     578             : 
     579           0 :         fd_gui_printf_peers_view_resize( peers, fd_gui_peers_live_table_ele_cnt( peers->live_table ) );
     580           0 :         FD_TEST( !fd_http_server_ws_broadcast( peers->http ) );
     581           0 :         break;
     582           0 :       }
     583           0 :       default: break;
     584           0 :     }
     585             : 
     586           0 :     return FD_GUI_PEERS_NODE_NOP;
     587           0 : }
     588             : 
     589             : static void
     590           0 : fd_gui_peers_viewport_snap( fd_gui_peers_ctx_t * peers, ulong ws_conn_id ) {
     591           0 :   FD_TEST( peers->client_viewports[ ws_conn_id ].connected );
     592           0 :   if( !(peers->client_viewports[ ws_conn_id ].row_cnt && peers->client_viewports[ ws_conn_id ].row_cnt<FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ) ) FD_LOG_ERR(("row_cnt=%lu", peers->client_viewports[ ws_conn_id ].row_cnt ));
     593             : 
     594           0 :   if( FD_UNLIKELY( fd_gui_peers_live_table_active_sort_key_cnt( peers->live_table )==FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT ) ) {
     595             :     /* we're out of cached sort keys. disconnect the oldest client */
     596           0 :     ulong oldest_ws_conn_id    = ULONG_MAX;
     597           0 :     long oldest_connected_time = LONG_MAX;
     598           0 :     for( ulong i=0UL; i<peers->max_ws_conn_cnt; i++ ) {
     599           0 :       if( FD_UNLIKELY( peers->client_viewports[ i ].connected && peers->client_viewports[ i ].connected_time < oldest_connected_time ) ) {
     600           0 :         oldest_ws_conn_id = i;
     601           0 :         oldest_connected_time = peers->client_viewports[ i ].connected_time;
     602           0 :       }
     603           0 :     }
     604           0 :     FD_TEST( oldest_ws_conn_id!=ULONG_MAX );
     605           0 :     fd_gui_peers_live_table_sort_key_remove( peers->live_table, peers->client_viewports[ oldest_ws_conn_id ].sort_key );
     606           0 :     FD_TEST( fd_gui_peers_live_table_active_sort_key_cnt( peers->live_table )==FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT-1UL );
     607           0 :   }
     608             : 
     609           0 :   for( fd_gui_peers_live_table_fwd_iter_t iter = fd_gui_peers_live_table_fwd_iter_init( peers->live_table, peers->client_viewports[ ws_conn_id ].sort_key, peers->contact_info_table ), j = 0;
     610           0 :        !fd_gui_peers_live_table_fwd_iter_done( iter ) && j<peers->client_viewports[ ws_conn_id ].start_row+peers->client_viewports[ ws_conn_id ].row_cnt;
     611           0 :        iter = fd_gui_peers_live_table_fwd_iter_next( iter, peers->contact_info_table ), j++ ) {
     612           0 :     if( FD_LIKELY( j<peers->client_viewports[ ws_conn_id ].start_row ) ) continue;
     613           0 :     fd_gui_peers_node_t const * cur = fd_gui_peers_live_table_fwd_iter_ele_const( iter, peers->contact_info_table );
     614           0 :     fd_gui_peers_node_t * ref = &peers->client_viewports[ ws_conn_id ].viewport[ j ];
     615             : 
     616           0 :     fd_memcpy( ref, cur, sizeof(fd_gui_peers_node_t) );
     617           0 :   }
     618           0 : }
     619             : 
     620             : static int
     621             : fd_gui_peers_request_scroll( fd_gui_peers_ctx_t * peers,
     622             :                              ulong                ws_conn_id,
     623             :                              ulong                request_id,
     624           0 :                              cJSON const *        params ) {
     625           0 :   if( FD_UNLIKELY( !peers->client_viewports[ ws_conn_id ].connected ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     626             : 
     627           0 :   const cJSON * start_row_param = cJSON_GetObjectItemCaseSensitive( params, "start_row" );
     628           0 :   if( FD_UNLIKELY( !cJSON_IsNumber( start_row_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     629           0 :   ulong _start_row = start_row_param->valueulong;
     630             : 
     631           0 :   const cJSON * row_cnt_param = cJSON_GetObjectItemCaseSensitive( params, "row_cnt" );
     632           0 :   if( FD_UNLIKELY( !cJSON_IsNumber( row_cnt_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     633           0 :   ulong _row_cnt = row_cnt_param->valueulong;
     634             : 
     635           0 :   if( FD_UNLIKELY( _row_cnt==0 || _row_cnt > FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ ) ) {
     636           0 :     fd_gui_printf_null_query_response( peers->http, "gossip", "query_scroll", request_id );
     637           0 :     FD_TEST( !fd_http_server_ws_send( peers->http, ws_conn_id ) );
     638           0 :     return 0;
     639           0 :   }
     640             : 
     641           0 :   if( FD_UNLIKELY( peers->client_viewports[ ws_conn_id ].start_row==_start_row && peers->client_viewports[ ws_conn_id ].row_cnt==_row_cnt ) ) {
     642           0 :     return 0; /* NOP, scroll window hasn't changed */
     643           0 :   }
     644             : 
     645             :   /* update the client's viewport */
     646           0 :   peers->client_viewports[ ws_conn_id ].start_row = _start_row;
     647           0 :   peers->client_viewports[ ws_conn_id ].row_cnt   = _row_cnt;
     648             : 
     649           0 :   fd_gui_printf_peers_viewport_request( peers, "query_scroll", ws_conn_id, request_id );
     650           0 :   FD_TEST( !fd_http_server_ws_send( peers->http, ws_conn_id ) );
     651           0 :   return 0;
     652           0 : }
     653             : 
     654             : static int
     655             : fd_gui_peers_request_sort( fd_gui_peers_ctx_t * peers,
     656             :                            ulong                ws_conn_id,
     657             :                            ulong                request_id,
     658           0 :                            cJSON const *        params ) {
     659           0 :   if( FD_UNLIKELY( !peers->client_viewports[ ws_conn_id ].connected ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     660             : 
     661           0 :   const cJSON * col_id_param = cJSON_GetObjectItemCaseSensitive( params, "col_id" );
     662           0 :   if( FD_UNLIKELY( !cJSON_IsString( col_id_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     663           0 :   char * _col_name = col_id_param->valuestring;
     664             : 
     665           0 :   ulong _col_idx = fd_gui_peers_live_table_col_name_to_idx( peers->live_table, _col_name );
     666           0 :   if( FD_UNLIKELY( _col_idx==ULONG_MAX) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     667           0 :   ulong sort_idx = ULONG_MAX;
     668           0 :   for( ulong i=0UL; i<FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT; i++ ) {
     669           0 :     if( FD_UNLIKELY( _col_idx==peers->client_viewports[ ws_conn_id ].sort_key->col[ i ] ) ) {
     670           0 :       sort_idx = i;
     671           0 :       break;
     672           0 :     }
     673           0 :   }
     674           0 :   FD_TEST( sort_idx!=ULONG_MAX );
     675             : 
     676           0 :   const cJSON * dir_param = cJSON_GetObjectItemCaseSensitive( params, "dir" );
     677           0 :   if( FD_UNLIKELY( !cJSON_IsNumber( dir_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     678           0 :   int _dir = dir_param->valueint;
     679             : 
     680           0 :   if( FD_UNLIKELY( _dir > 1 || _dir < -1 ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     681             : 
     682           0 :   if( FD_UNLIKELY( peers->client_viewports[ ws_conn_id ].sort_key->dir[ sort_idx ]==_dir ) ) return 0; /* NOP, sort_key hasn't changed */
     683             : 
     684             :   /* shift the column to the front of the sort key */
     685           0 :   for( ulong i=sort_idx; i>0; i-- ) {
     686           0 :     peers->client_viewports[ ws_conn_id ].sort_key->col[ i ] = peers->client_viewports[ ws_conn_id ].sort_key->col[ i-1UL ];
     687           0 :     peers->client_viewports[ ws_conn_id ].sort_key->dir[ i ] = peers->client_viewports[ ws_conn_id ].sort_key->dir[ i-1UL ];
     688           0 :   }
     689           0 :   peers->client_viewports[ ws_conn_id ].sort_key->col[ 0 ] = _col_idx;
     690           0 :   peers->client_viewports[ ws_conn_id ].sort_key->dir[ 0 ] = _dir;
     691             : 
     692           0 :   if( FD_UNLIKELY( peers->client_viewports[ ws_conn_id ].row_cnt==0 )) return 0; /* NOP */
     693             : 
     694           0 :   fd_gui_printf_peers_viewport_request( peers, "query_sort_col", ws_conn_id, request_id );
     695           0 :   FD_TEST( !fd_http_server_ws_send( peers->http, ws_conn_id ) );
     696           0 :   return 0;
     697           0 : }
     698             : 
     699             : int
     700             : fd_gui_peers_ws_message( fd_gui_peers_ctx_t * peers,
     701             :                          ulong                ws_conn_id,
     702             :                          uchar const *        data,
     703           0 :                          ulong                data_len ) {
     704             :   /* TODO: cJSON allocates, might fail SIGSYS due to brk(2)...
     705             :      switch off this (or use wksp allocator) */
     706           0 :   const char * parse_end;
     707           0 :   cJSON * json = cJSON_ParseWithLengthOpts( (char *)data, data_len, &parse_end, 0 );
     708           0 :   if( FD_UNLIKELY( !json ) ) {
     709           0 :     return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     710           0 :   }
     711             : 
     712           0 :   const cJSON * node = cJSON_GetObjectItemCaseSensitive( json, "id" );
     713           0 :   if( FD_UNLIKELY( !cJSON_IsNumber( node ) ) ) {
     714           0 :     cJSON_Delete( json );
     715           0 :     return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     716           0 :   }
     717           0 :   ulong id = node->valueulong;
     718             : 
     719           0 :   const cJSON * topic = cJSON_GetObjectItemCaseSensitive( json, "topic" );
     720           0 :   if( FD_UNLIKELY( !cJSON_IsString( topic ) || topic->valuestring==NULL ) ) {
     721           0 :     cJSON_Delete( json );
     722           0 :     return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     723           0 :   }
     724             : 
     725           0 :   const cJSON * key = cJSON_GetObjectItemCaseSensitive( json, "key" );
     726           0 :   if( FD_UNLIKELY( !cJSON_IsString( key ) || key->valuestring==NULL ) ) {
     727           0 :     cJSON_Delete( json );
     728           0 :     return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     729           0 :   }
     730             : 
     731           0 :   if( FD_LIKELY( !strcmp( topic->valuestring, "gossip" ) && !strcmp( key->valuestring, "query_sort_col" ) ) ) {
     732           0 :     const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
     733           0 :     if( FD_UNLIKELY( !cJSON_IsObject( params ) ) ) {
     734           0 :       cJSON_Delete( json );
     735           0 :       return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     736           0 :     }
     737             : 
     738           0 :     int result = fd_gui_peers_request_sort( peers, ws_conn_id, id, params );
     739           0 :     cJSON_Delete( json );
     740           0 :     return result;
     741           0 :   } else if( FD_LIKELY( !strcmp( topic->valuestring, "gossip" ) && !strcmp( key->valuestring, "query_scroll" ) ) ) {
     742           0 :     const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
     743           0 :     if( FD_UNLIKELY( !cJSON_IsObject( params ) ) ) {
     744           0 :       cJSON_Delete( json );
     745           0 :       return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
     746           0 :     }
     747             : 
     748           0 :     int result = fd_gui_peers_request_scroll( peers, ws_conn_id, id, params );
     749           0 :     cJSON_Delete( json );
     750           0 :     return result;
     751           0 :   }
     752             : 
     753           0 :   cJSON_Delete( json );
     754           0 :   return FD_HTTP_SERVER_CONNECTION_CLOSE_UNKNOWN_METHOD;
     755           0 : }
     756             : 
     757             : static void
     758             : fd_gui_peers_viewport_log( fd_gui_peers_ctx_t *  peers,
     759           0 :                            ulong                 ws_conn_id) {
     760             : 
     761           0 :   FD_TEST( peers->client_viewports[ ws_conn_id ].row_cnt && peers->client_viewports[ ws_conn_id ].row_cnt < FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ );
     762             : 
     763           0 :   char out[ 1<<14 ];
     764           0 :   char * p = fd_cstr_init( out );
     765             : 
     766           0 :   p = fd_cstr_append_printf( p,
     767           0 :     "\n[Viewport] table_size=%lu max_viewport_size=%lu\n"
     768           0 :     "+-------+----------------+----------------+----------------+----------------+----------------------------------------------------+-----------------+\n"
     769           0 :     "| Row # | RX Push (bps)  | RX Pull (bps)  | TX Push (bps)  | TX Pull (bps)  | Pubkey                                             | IP Address      |\n"
     770           0 :     "+-------+----------------+----------------+----------------+----------------+----------------------------------------------------+-----------------+\n",
     771           0 :     fd_gui_peers_live_table_ele_cnt( peers->live_table ), peers->client_viewports[ ws_conn_id ].row_cnt );
     772             : 
     773           0 :   FD_TEST( peers->client_viewports[ ws_conn_id ].connected );
     774           0 :   for( fd_gui_peers_live_table_fwd_iter_t iter = fd_gui_peers_live_table_fwd_iter_init( peers->live_table, peers->client_viewports[ ws_conn_id ].sort_key, peers->contact_info_table ), j = 0UL;
     775           0 :        !fd_gui_peers_live_table_fwd_iter_done(iter) && j < peers->client_viewports[ ws_conn_id ].start_row + peers->client_viewports[ ws_conn_id ].row_cnt;
     776           0 :        iter = fd_gui_peers_live_table_fwd_iter_next(iter, peers->contact_info_table), j++ ) {
     777           0 :     if( FD_LIKELY( j < peers->client_viewports[ ws_conn_id ].start_row ) ) continue;
     778             : 
     779           0 :     fd_gui_peers_node_t const * cur = fd_gui_peers_live_table_fwd_iter_ele_const( iter, peers->contact_info_table );
     780             : 
     781           0 :     char pubkey_base58[ FD_BASE58_ENCODED_32_SZ ];
     782           0 :     fd_base58_encode_32( cur->contact_info.pubkey.uc, NULL, pubkey_base58 );
     783             : 
     784           0 :     char peer_addr[ 16 ]; /* 255.255.255.255 + '\0' */
     785           0 :     FD_TEST(fd_cstr_printf_check( peer_addr, sizeof(peer_addr), NULL, FD_IP4_ADDR_FMT,
     786           0 :                                   FD_IP4_ADDR_FMT_ARGS( cur->contact_info.sockets[FD_CONTACT_INFO_SOCKET_GOSSIP].addr ) ) );
     787             : 
     788           0 :     long cur_egress_push_bps           = cur->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ].rate;
     789           0 :     long cur_ingress_push_bps          = cur->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ].rate;
     790           0 :     long cur_egress_pull_response_bps  = cur->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ].rate;
     791           0 :     long cur_ingress_pull_response_bps = cur->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ].rate;
     792             : 
     793           0 :     p = fd_cstr_append_printf( p,
     794           0 :                                "| %5lu | %14ld | %14ld | %14ld | %14ld | %-50s | %-15s |\n",
     795           0 :                                peers->client_viewports[ ws_conn_id ].start_row + j,
     796           0 :                                cur_ingress_push_bps,
     797           0 :                                cur_ingress_pull_response_bps,
     798           0 :                                cur_egress_push_bps,
     799           0 :                                cur_egress_pull_response_bps,
     800           0 :                                pubkey_base58,
     801           0 :                                peer_addr );
     802           0 :   }
     803           0 :   p = fd_cstr_append_printf(p, "+-------+----------------+----------------+----------------+----------------+----------------------------------------------------+-----------------+" );
     804           0 :   fd_cstr_fini( p );
     805           0 :   FD_LOG_NOTICE(( "%s", out ));
     806           0 : }
     807             : 
     808             : static void
     809           0 : fd_gui_peers_ws_conn_rr_grow( fd_gui_peers_ctx_t * peers, ulong ws_conn_id ) {
     810           0 :   if( FD_UNLIKELY( !peers->open_ws_conn_cnt ) ) peers->active_ws_conn_id = ws_conn_id;
     811           0 :   peers->open_ws_conn_cnt++;
     812           0 : }
     813             : 
     814             : static void
     815           0 : fd_gui_peers_ws_conn_rr_shrink( fd_gui_peers_ctx_t * peers, ulong ws_conn_id ) {
     816           0 :   peers->open_ws_conn_cnt--;
     817             : 
     818           0 :   if( FD_UNLIKELY( peers->open_ws_conn_cnt && peers->active_ws_conn_id==ws_conn_id ) ) {
     819           0 :     for( ulong i=0UL; i<peers->max_ws_conn_cnt; i++ ) {
     820           0 :       ulong next_ws_conn_id = (ws_conn_id + i) % peers->max_ws_conn_cnt;
     821           0 :       if( FD_UNLIKELY( peers->client_viewports[ next_ws_conn_id ].connected ) ) {
     822           0 :         peers->active_ws_conn_id = next_ws_conn_id;
     823           0 :         break;
     824           0 :       }
     825           0 :     }
     826           0 :   }
     827           0 : }
     828             : 
     829             : static int
     830           0 : fd_gui_peers_ws_conn_rr_advance( fd_gui_peers_ctx_t * peers, long now ) {
     831           0 :   if( FD_LIKELY( !peers->open_ws_conn_cnt || now <= peers->next_client_nanos ) ) return 0;
     832             : 
     833           0 :   for( ulong i=1UL; i<peers->max_ws_conn_cnt; i++ ) {
     834           0 :     ulong next_ws_conn_id = (peers->active_ws_conn_id + i) % peers->max_ws_conn_cnt;
     835           0 :     if( FD_UNLIKELY( peers->client_viewports[ next_ws_conn_id ].connected ) ) {
     836           0 :       peers->active_ws_conn_id = next_ws_conn_id;
     837           0 :       break;
     838           0 :     }
     839           0 :   }
     840           0 :   return 1;
     841           0 : }
     842             : 
     843             : int
     844           0 : fd_gui_peers_poll( fd_gui_peers_ctx_t * peers, long now ) {
     845           0 :   int did_work = 0;
     846             : 
     847             :   /* update client viewports in a round-robin */
     848           0 :   if( FD_UNLIKELY( fd_gui_peers_ws_conn_rr_advance( peers, now ) ) ) {
     849           0 :     FD_TEST( peers->client_viewports[ peers->active_ws_conn_id ].connected );
     850           0 :     if( FD_LIKELY( peers->client_viewports[ peers->active_ws_conn_id ].row_cnt ) ) {
     851             :       /* broadcast the diff as cell updates */
     852           0 :       fd_gui_printf_peers_viewport_update( peers, peers->active_ws_conn_id );
     853           0 :       FD_TEST( !fd_http_server_ws_send( peers->http, peers->active_ws_conn_id ) );
     854             : 
     855             :       /* log the diff */
     856           0 :       fd_gui_peers_viewport_log( peers, peers->active_ws_conn_id );
     857           0 :       (void)fd_gui_peers_viewport_log;
     858             : 
     859             :       /* update client state to the latest viewport */
     860           0 :       fd_gui_peers_viewport_snap( peers, peers->active_ws_conn_id );
     861           0 :     }
     862             : 
     863           0 :     peers->next_client_nanos = now + ((FD_GUI_PEERS_WS_VIEWPORT_UPDATE_INTERVAL_MILLIS * 1000000L) / (long)peers->open_ws_conn_cnt);
     864           0 :     did_work = 1;
     865           0 :   }
     866             : 
     867           0 :   if( FD_UNLIKELY( now >= peers->next_metric_rate_update_nanos ) ) {
     868           0 :     for( fd_gui_peers_node_pubkey_map_iter_t iter = fd_gui_peers_node_pubkey_map_iter_init( peers->node_pubkey_map, peers->contact_info_table );
     869           0 :          !fd_gui_peers_node_pubkey_map_iter_done( iter, peers->node_pubkey_map, peers->contact_info_table );
     870           0 :          iter = fd_gui_peers_node_pubkey_map_iter_next( iter, peers->node_pubkey_map, peers->contact_info_table ) ) {
     871           0 :       fd_gui_peers_node_t * peer = fd_gui_peers_node_pubkey_map_iter_ele( iter, peers->node_pubkey_map, peers->contact_info_table );
     872             : 
     873           0 :       double window = (double)(now - (peers->next_metric_rate_update_nanos - (FD_GUI_PEERS_METRIC_RATE_UPDATE_INTERVAL_MILLIS * 1000000L)));
     874             : 
     875             :       /* optimization: no need to remove / re-insert if the rates haven't changed */
     876           0 :       int change = 0;
     877           0 :       for( ulong i=0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT; i++ ) {
     878           0 :         fd_gui_peers_metric_rate_t * metric = &peer->gossvf_rx[ i ];
     879           0 :         long new_rate = (long)(((double)((long)metric->cur - (long)metric->ref) * 1e9 / window));
     880           0 :         if( FD_LIKELY( metric->rate == new_rate ) ) continue;
     881           0 :         change = 1;
     882           0 :         break;
     883           0 :       }
     884             : 
     885           0 :       if( !change) {
     886           0 :         for( ulong i=0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT; i++ ) {
     887           0 :           fd_gui_peers_metric_rate_t * metric = &peer->gossip_tx[ i ];
     888           0 :           long new_rate = (long)(((double)((long)metric->cur - (long)metric->ref) * 1e9 / window));
     889           0 :           if( FD_LIKELY( metric->rate == new_rate ) ) continue;
     890           0 :           change = 1;
     891           0 :           break;
     892           0 :         }
     893           0 :       }
     894             : 
     895           0 :       if( !change ) continue;
     896             : 
     897             :       /* live_table */
     898           0 :       fd_gui_peers_live_table_ele_remove( peers->live_table, peer, peers->contact_info_table );
     899           0 :       for( ulong i=0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT; i++ ) {
     900           0 :         fd_gui_peers_metric_rate_t * metric = &peer->gossvf_rx[ i ];
     901           0 :         metric->rate = (long)(((double)((long)metric->cur - (long)metric->ref) * 1e9 / window));
     902           0 :         metric->ref  = metric->cur;
     903           0 :       }
     904             : 
     905           0 :       for( ulong i=0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT; i++ ) {
     906           0 :         fd_gui_peers_metric_rate_t * metric = &peer->gossip_tx[ i ];
     907           0 :         metric->rate = (long)(((double)((long)metric->cur - (long)metric->ref) * 1e9 / window));
     908           0 :         metric->ref  = metric->cur;
     909           0 :       }
     910           0 :       fd_gui_peers_live_table_ele_insert( peers->live_table, peer, peers->contact_info_table );
     911             : 
     912             :       /* bandwidth_tracking */
     913           0 :       fd_gui_peers_bandwidth_tracking_ele_remove( peers->bw_tracking, peer, peers->contact_info_table );
     914           0 :       peer->gossvf_rx_sum.rate = (long)(((double)((long)peer->gossvf_rx_sum.cur - (long)peer->gossvf_rx_sum.ref) * 1e9 / window));
     915           0 :       peer->gossvf_rx_sum.ref  = peer->gossvf_rx_sum.cur;
     916             : 
     917           0 :       peer->gossip_tx_sum.rate = (long)(((double)((long)peer->gossip_tx_sum.cur - (long)peer->gossip_tx_sum.ref) * 1e9 / window));
     918           0 :       peer->gossip_tx_sum.ref  = peer->gossip_tx_sum.cur;
     919           0 :       fd_gui_peers_bandwidth_tracking_ele_insert( peers->bw_tracking, peer, peers->contact_info_table );
     920           0 :     }
     921             : 
     922           0 :     peers->next_metric_rate_update_nanos = now + (FD_GUI_PEERS_METRIC_RATE_UPDATE_INTERVAL_MILLIS * 1000000L);
     923           0 :     did_work = 1;
     924           0 :     fd_gui_peers_live_table_verify( peers->live_table, peers->contact_info_table ); /* todo ... remove */
     925           0 :   }
     926             : 
     927           0 :   if( FD_LIKELY( now >= peers->next_gossip_stats_update_nanos ) ) {
     928           0 :     fd_gui_peers_gossip_stats_snap( peers, peers->gossip_stats, now );
     929           0 :     fd_gui_peers_printf_gossip_stats( peers );
     930           0 :     fd_http_server_ws_broadcast( peers->http );
     931             : 
     932           0 :     peers->next_gossip_stats_update_nanos = now + (FD_GUI_PEERS_GOSSIP_STATS_UPDATE_INTERVAL_MILLIS * 1000000L);
     933           0 :     did_work = 1;
     934           0 :   }
     935             : 
     936           0 :   return did_work;
     937           0 : }
     938             : 
     939             : void
     940           0 : fd_gui_peers_ws_open( fd_gui_peers_ctx_t * peers, ulong ws_conn_id, long now ) {
     941           0 :   fd_gui_peers_ws_conn_rr_grow( peers, ws_conn_id );
     942           0 :   peers->client_viewports[ ws_conn_id ].connected = 1;
     943           0 :   peers->client_viewports[ ws_conn_id ].connected_time = now;
     944           0 :   peers->client_viewports[ ws_conn_id ].start_row = 0;
     945           0 :   peers->client_viewports[ ws_conn_id ].row_cnt = 0;
     946           0 :   fd_memcpy( peers->client_viewports[ ws_conn_id ].sort_key, &FD_GUI_PEERS_LIVE_TABLE_DEFAULT_SORT_KEY, sizeof(fd_gui_peers_live_table_sort_key_t) );
     947           0 : }
     948             : 
     949             : void
     950           0 : fd_gui_peers_ws_close( fd_gui_peers_ctx_t * peers, ulong ws_conn_id ) {
     951           0 :   fd_gui_peers_live_table_sort_key_remove( peers->live_table, peers->client_viewports[ ws_conn_id ].sort_key );
     952           0 :   peers->client_viewports[ ws_conn_id ].connected = 0;
     953           0 :   fd_gui_peers_ws_conn_rr_shrink( peers, ws_conn_id );
     954           0 : }
     955             : 
     956             : #undef LOGGING

Generated by: LCOV version 1.14