Line data Source code
1 : #include "fd_gui_peers.h"
2 : #include "fd_gui_printf.h"
3 : #include "fd_gui_config_parse.h"
4 : #include "fd_gui_metrics.h"
5 :
6 : #include "../../flamenco/gossip/fd_gossip_private.h"
7 : #include "../../disco/metrics/fd_metrics_base.h"
8 :
9 : FD_IMPORT_BINARY( ipinfo, "src/disco/gui/ipinfo.bin" );
10 : #define IPINFO_MAX_NODES (1UL<<22UL) /* 4M nodes */
11 :
12 : #define LOGGING 0
13 :
14 : FD_FN_CONST ulong
15 0 : fd_gui_peers_align( void ) {
16 0 : ulong a = 128UL;
17 0 : a = fd_ulong_max( a, alignof(fd_gui_peers_ctx_t) );
18 0 : a = fd_ulong_max( a, fd_gui_peers_live_table_align() );
19 0 : a = fd_ulong_max( a, fd_gui_peers_bandwidth_tracking_align() );
20 0 : a = fd_ulong_max( a, fd_gui_peers_node_info_pool_align() );
21 0 : a = fd_ulong_max( a, fd_gui_peers_node_info_map_align() );
22 0 : a = fd_ulong_max( a, fd_gui_peers_node_pubkey_map_align() );
23 0 : a = fd_ulong_max( a, fd_gui_peers_node_sock_map_align() );
24 0 : a = fd_ulong_max( a, alignof(fd_gui_peers_ws_conn_t) );
25 0 : a = fd_ulong_max( a, alignof(fd_gui_ipinfo_node_t) );
26 0 : FD_TEST( fd_ulong_pow2_up( a )==a );
27 0 : return a;
28 0 : }
29 :
30 : FD_FN_CONST ulong
31 0 : fd_gui_peers_footprint( ulong max_ws_conn_cnt ) {
32 0 : ulong info_chain_cnt = fd_gui_peers_node_info_map_chain_cnt_est ( FD_CONTACT_INFO_TABLE_SIZE );
33 0 : ulong pubkey_chain_cnt = fd_gui_peers_node_pubkey_map_chain_cnt_est( FD_CONTACT_INFO_TABLE_SIZE );
34 0 : ulong sock_chain_cnt = fd_gui_peers_node_sock_map_chain_cnt_est ( FD_CONTACT_INFO_TABLE_SIZE );
35 :
36 0 : ulong l = FD_LAYOUT_INIT;
37 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_gui_peers_ctx_t), sizeof(fd_gui_peers_ctx_t) );
38 0 : l = FD_LAYOUT_APPEND( l, fd_gui_peers_live_table_align(), fd_gui_peers_live_table_footprint ( FD_CONTACT_INFO_TABLE_SIZE ) );
39 0 : l = FD_LAYOUT_APPEND( l, fd_gui_peers_bandwidth_tracking_align(), fd_gui_peers_bandwidth_tracking_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
40 0 : l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_info_pool_align(), fd_gui_peers_node_info_pool_footprint ( FD_CONTACT_INFO_TABLE_SIZE ) );
41 0 : l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_info_map_align(), fd_gui_peers_node_info_map_footprint ( info_chain_cnt ) );
42 0 : l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_pubkey_map_align(), fd_gui_peers_node_pubkey_map_footprint ( pubkey_chain_cnt ) );
43 0 : l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_sock_map_align(), fd_gui_peers_node_sock_map_footprint ( sock_chain_cnt ) );
44 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_gui_peers_ws_conn_t), max_ws_conn_cnt*sizeof(fd_gui_peers_ws_conn_t) );
45 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_gui_ipinfo_node_t), sizeof(fd_gui_ipinfo_node_t)*IPINFO_MAX_NODES );
46 :
47 0 : return FD_LAYOUT_FINI( l, fd_gui_peers_align() );
48 0 : }
49 :
50 : /* We sort the country codes so that fd_gui_peers_live_table can sort by
51 : table index instead of the codes themselves. */
52 : #define SORT_NAME fd_gui_country_code_sort
53 0 : #define SORT_KEY_T fd_gui_country_code_t
54 0 : #define SORT_BEFORE(a,b) (strcmp( (char *)&(a), (char *)&(b) ) < 0)
55 : #include "../../util/tmpl/fd_sort.c"
56 :
57 : static void
58 : build_ipinfo_trie( fd_gui_peers_ctx_t * peers,
59 0 : fd_gui_ipinfo_node_t * nodes ) {
60 0 : peers->ipinfo.nodes = nodes;
61 0 : ulong country_code_cnt = FD_LOAD( ulong, ipinfo );
62 0 : FD_TEST( country_code_cnt && country_code_cnt<256UL ); /* 256 reserved for unknown */
63 0 : FD_TEST( ipinfo_sz>=8UL+country_code_cnt*2UL );
64 :
65 0 : for( ulong i=0UL; i<country_code_cnt; i++ ) {
66 0 : fd_memcpy( peers->ipinfo.country_code[ i ].cc, ipinfo+8UL+i*2UL, 2UL );
67 0 : peers->ipinfo.country_code[ i ].cc[ 2 ] = '\0';
68 0 : }
69 :
70 0 : fd_gui_country_code_sort_insert( peers->ipinfo.country_code, country_code_cnt );
71 :
72 0 : ulong processed = 8UL+country_code_cnt*2UL;
73 0 : FD_TEST( !((ipinfo_sz-processed)%6UL) );
74 0 : FD_TEST( (ipinfo_sz-processed)/6UL<=IPINFO_MAX_NODES-1UL );
75 :
76 0 : fd_gui_ipinfo_node_t * root = &nodes[ 0 ];
77 0 : root->left = NULL;
78 0 : root->right = NULL;
79 0 : root->has_prefix = 0;
80 :
81 0 : ulong node_cnt = 1UL;
82 0 : while( processed<ipinfo_sz ) {
83 0 : uint ip_addr = fd_uint_bswap( FD_LOAD( uint, ipinfo+processed ) );
84 0 : uchar prefix_len = *( ipinfo+processed+4UL );
85 0 : FD_TEST( prefix_len<=32UL );
86 0 : uchar country_idx = *( ipinfo+processed+5UL );
87 0 : FD_TEST( country_idx<country_code_cnt );
88 :
89 0 : fd_gui_ipinfo_node_t * node = root;
90 0 : for( uchar bit_pos=0; bit_pos<prefix_len; bit_pos++ ) {
91 0 : uchar bit = (ip_addr >> (31 - bit_pos)) & 1;
92 :
93 0 : fd_gui_ipinfo_node_t * child;
94 0 : if( FD_LIKELY( !bit ) ) {
95 0 : child = node->left;
96 0 : if( FD_LIKELY( !child ) ) {
97 0 : FD_TEST( node_cnt<IPINFO_MAX_NODES );
98 0 : child = &nodes[ node_cnt++ ];
99 0 : child->left = NULL;
100 0 : child->right = NULL;
101 0 : child->has_prefix = 0;
102 0 : node->left = child;
103 0 : }
104 0 : } else {
105 0 : child = node->right;
106 0 : if( FD_LIKELY( !child ) ) {
107 0 : FD_TEST( node_cnt<IPINFO_MAX_NODES );
108 0 : child = &nodes[ node_cnt++ ];
109 0 : child->left = NULL;
110 0 : child->right = NULL;
111 0 : child->has_prefix = 0;
112 0 : node->right = child;
113 0 : }
114 0 : }
115 0 : node = child;
116 0 : }
117 :
118 0 : node->has_prefix = 1;
119 0 : node->country_code_idx = country_idx;
120 :
121 0 : processed += 6UL;
122 0 : }
123 0 : }
124 :
125 : void *
126 : fd_gui_peers_new( void * shmem,
127 : fd_http_server_t * http,
128 : fd_topo_t * topo,
129 : ulong max_ws_conn_cnt,
130 0 : long now ) {
131 0 : if( FD_UNLIKELY( !shmem ) ) {
132 0 : FD_LOG_WARNING(( "NULL shmem" ));
133 0 : return NULL;
134 0 : }
135 :
136 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_gui_peers_align() ) ) ) {
137 0 : FD_LOG_WARNING(( "misaligned shmem" ));
138 0 : return NULL;
139 0 : }
140 :
141 0 : ulong info_chain_cnt = fd_gui_peers_node_info_map_chain_cnt_est ( FD_CONTACT_INFO_TABLE_SIZE );
142 0 : ulong pubkey_chain_cnt = fd_gui_peers_node_pubkey_map_chain_cnt_est( FD_CONTACT_INFO_TABLE_SIZE );
143 0 : ulong sock_chain_cnt = fd_gui_peers_node_sock_map_chain_cnt_est ( FD_CONTACT_INFO_TABLE_SIZE );
144 :
145 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
146 0 : fd_gui_peers_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_gui_peers_ctx_t), sizeof(fd_gui_peers_ctx_t) );
147 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 ) );
148 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 ) );
149 0 : void * _info_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_info_pool_align(), fd_gui_peers_node_info_pool_footprint ( FD_CONTACT_INFO_TABLE_SIZE ) );
150 0 : void * _info_map = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_info_map_align(), fd_gui_peers_node_info_map_footprint ( info_chain_cnt ) );
151 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 ) );
152 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 ) );
153 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) );
154 0 : void * _nodes = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_gui_ipinfo_node_t), sizeof(fd_gui_ipinfo_node_t)*IPINFO_MAX_NODES );
155 :
156 0 : for( ulong i = 0UL; i<max_ws_conn_cnt; i++ ) ctx->client_viewports[ i ].connected = 0;
157 :
158 0 : ctx->http = http;
159 0 : ctx->topo = topo;
160 :
161 0 : ctx->max_ws_conn_cnt = max_ws_conn_cnt;
162 0 : ctx->open_ws_conn_cnt = 0UL;
163 0 : ctx->active_ws_conn_id = ULONG_MAX;
164 :
165 0 : ctx->slot_voted = ULONG_MAX;
166 :
167 0 : ctx->next_client_nanos = now;
168 0 : ctx->next_metric_rate_update_nanos = now;
169 0 : ctx->next_gossip_stats_update_nanos = now;
170 0 : memset( &ctx->gossip_stats, 0, sizeof(ctx->gossip_stats) );
171 :
172 0 : for( ulong i = 0; i<FD_CONTACT_INFO_TABLE_SIZE; i++) ctx->contact_info_table[ i ].valid = 0;
173 :
174 0 : ctx->live_table = fd_gui_peers_live_table_join( fd_gui_peers_live_table_new( _live_table, FD_CONTACT_INFO_TABLE_SIZE ) );
175 0 : fd_gui_peers_live_table_seed( ctx->contact_info_table, FD_CONTACT_INFO_TABLE_SIZE, 42UL );
176 :
177 0 : ctx->bw_tracking = fd_gui_peers_bandwidth_tracking_join( fd_gui_peers_bandwidth_tracking_new( _bw_tracking, FD_CONTACT_INFO_TABLE_SIZE ) );
178 0 : fd_gui_peers_bandwidth_tracking_seed( ctx->contact_info_table, FD_CONTACT_INFO_TABLE_SIZE, 42UL );
179 :
180 0 : ctx->node_info_pool = fd_gui_peers_node_info_pool_join ( fd_gui_peers_node_info_pool_new ( _info_pool, FD_CONTACT_INFO_TABLE_SIZE ) );
181 0 : ctx->node_info_map = fd_gui_peers_node_info_map_join ( fd_gui_peers_node_info_map_new ( _info_map, info_chain_cnt, 42UL ) );
182 0 : ctx->node_pubkey_map = fd_gui_peers_node_pubkey_map_join( fd_gui_peers_node_pubkey_map_new( _pubkey_map, pubkey_chain_cnt, 42UL ) );
183 0 : ctx->node_sock_map = fd_gui_peers_node_sock_map_join ( fd_gui_peers_node_sock_map_new ( _sock_map, sock_chain_cnt, 42UL ) );
184 :
185 0 : build_ipinfo_trie( ctx, _nodes );
186 :
187 0 : return shmem;
188 0 : }
189 :
190 : fd_gui_peers_ctx_t *
191 0 : fd_gui_peers_join( void * shmem ) {
192 0 : if( FD_UNLIKELY( !shmem ) ) {
193 0 : FD_LOG_WARNING(( "NULL shmem" ));
194 0 : return NULL;
195 0 : }
196 :
197 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_gui_peers_align() ) ) ) {
198 0 : FD_LOG_WARNING(( "misaligned shmem" ));
199 0 : return NULL;
200 0 : }
201 :
202 0 : fd_gui_peers_ctx_t * ctx = (fd_gui_peers_ctx_t *)shmem;
203 :
204 0 : return ctx;
205 0 : }
206 :
207 : static void
208 : fd_gui_peers_gossip_stats_snap( fd_gui_peers_ctx_t * peers,
209 : fd_gui_peers_gossip_stats_t * gossip_stats,
210 0 : long now ) {
211 0 : gossip_stats->sample_time = now;
212 0 : ulong gossvf_tile_cnt = fd_topo_tile_name_cnt( peers->topo, "gossvf" );
213 0 : ulong gossip_tile_cnt = 1UL;
214 :
215 0 : gossip_stats->network_health_pull_response_msg_rx_success =
216 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PULL_RESPONSE ) );
217 0 : gossip_stats->network_health_pull_response_msg_rx_failure =
218 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_RESPONSE_NO_VALID_CRDS ) );
219 0 : gossip_stats->network_health_push_msg_rx_success =
220 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PUSH ) );
221 0 : gossip_stats->network_health_push_msg_rx_failure =
222 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PUSH_NO_VALID_CRDS ) );
223 0 : gossip_stats->network_health_push_crds_rx_success =
224 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_UPSERTED_PUSH ) );
225 0 : gossip_stats->network_health_push_crds_rx_failure =
226 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PUSH_STALE ) )
227 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PUSH_DUPLICATE ) )
228 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_SIGNATURE ) )
229 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_RELAYER_NO_CONTACT_INFO ) )
230 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_RELAYER_SHRED_VERSION ) )
231 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_ORIGIN_NO_CONTACT_INFO ) )
232 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_ORIGIN_SHRED_VERSION ) )
233 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_INACTIVE ) )
234 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PUSH_WALLCLOCK ) );
235 0 : gossip_stats->network_health_pull_response_crds_rx_success =
236 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_UPSERTED_PULL_RESPONSE ) );
237 0 : gossip_stats->network_health_pull_response_crds_rx_failure =
238 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_STALE ) )
239 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_WALLCLOCK ) )
240 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_DUPLICATE ) )
241 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_DUPLICATE ) )
242 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_SIGNATURE ) )
243 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_RELAYER_SHRED_VERSION ) )
244 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_ORIGIN_NO_CONTACT_INFO ) )
245 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_ORIGIN_SHRED_VERSION ) )
246 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_INACTIVE ) );
247 0 : gossip_stats->network_health_push_crds_rx_duplicate =
248 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PUSH_DUPLICATE ) );
249 0 : gossip_stats->network_health_pull_response_crds_rx_duplicate =
250 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_DUPLICATE ) )
251 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, CRDS_RX_COUNT_DROPPED_PULL_RESPONSE_DUPLICATE ) );
252 :
253 0 : gossip_stats->network_health_total_stake = 0UL; /* todo ... fetch from RPC */
254 0 : gossip_stats->network_health_total_peers = 0UL; /* todo ... fetch from RPC */
255 :
256 0 : gossip_stats->network_health_connected_stake = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_PEER_TOTAL_STAKE ) );
257 0 : gossip_stats->network_health_connected_staked_peers = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_PEER_STAKED_COUNT ) );
258 0 : gossip_stats->network_health_connected_unstaked_peers = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_PEER_UNSTAKED_COUNT ) );
259 :
260 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 );
261 0 : gossip_stats->network_ingress_total_bytes_per_sec = 0UL;
262 :
263 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;
264 0 : !fd_gui_peers_bandwidth_tracking_fwd_iter_done( iter );
265 0 : iter = fd_gui_peers_bandwidth_tracking_fwd_iter_next( iter, peers->contact_info_table ), j++ ) {
266 0 : fd_gui_peers_node_t * cur = fd_gui_peers_bandwidth_tracking_fwd_iter_ele( iter, peers->contact_info_table );
267 :
268 0 : if( FD_UNLIKELY( j<gossip_stats->network_ingress_peer_sz ) ) {
269 0 : fd_gui_config_parse_info_t * node_info = fd_gui_peers_node_info_map_ele_query( peers->node_info_map, &cur->contact_info.pubkey, NULL, peers->node_info_pool );
270 0 : if( FD_LIKELY( node_info ) ) FD_TEST( fd_cstr_printf_check( gossip_stats->network_ingress_peer_names[ j ], FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ+1UL, NULL, "%s", node_info->name ) );
271 0 : else gossip_stats->network_ingress_peer_names[ j ][ 0 ] = '\0';
272 0 : gossip_stats->network_ingress_peer_bytes_per_sec[ j ] = cur->gossvf_rx_sum.rate_ema;
273 0 : fd_memcpy( &gossip_stats->network_ingress_peer_identities[ j ], cur->contact_info.pubkey.uc, 32UL );
274 0 : }
275 :
276 0 : gossip_stats->network_ingress_total_bytes_per_sec += cur->gossvf_rx_sum.rate_ema;
277 0 : }
278 :
279 0 : gossip_stats->network_ingress_total_bytes = fd_gui_metrics_gossip_total_ingress_bytes( peers->topo, gossvf_tile_cnt );
280 :
281 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 );
282 :
283 0 : FD_TEST( gossip_stats->network_egress_peer_sz==gossip_stats->network_ingress_peer_sz );
284 :
285 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 );
286 0 : gossip_stats->network_egress_total_bytes_per_sec = 0UL;
287 :
288 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;
289 0 : !fd_gui_peers_bandwidth_tracking_fwd_iter_done( iter );
290 0 : iter = fd_gui_peers_bandwidth_tracking_fwd_iter_next( iter, peers->contact_info_table ), j++ ) {
291 0 : fd_gui_peers_node_t * cur = fd_gui_peers_bandwidth_tracking_fwd_iter_ele( iter, peers->contact_info_table );
292 :
293 0 : if( FD_UNLIKELY( j<gossip_stats->network_egress_peer_sz ) ) {
294 0 : fd_gui_config_parse_info_t * node_info = fd_gui_peers_node_info_map_ele_query( peers->node_info_map, &cur->contact_info.pubkey, NULL, peers->node_info_pool );
295 0 : if( FD_LIKELY( node_info ) ) FD_TEST( fd_cstr_printf_check( gossip_stats->network_egress_peer_names[ j ], FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ+1UL, NULL, "%s", node_info->name ) );
296 0 : else gossip_stats->network_egress_peer_names[ j ][ 0 ] = '\0';
297 0 : gossip_stats->network_egress_peer_bytes_per_sec[ j ] = cur->gossip_tx_sum.rate_ema;
298 0 : fd_memcpy( &gossip_stats->network_egress_peer_identities[ j ], cur->contact_info.pubkey.uc, 32UL );
299 0 : }
300 :
301 0 : gossip_stats->network_egress_total_bytes_per_sec += cur->gossip_tx_sum.rate_ema;
302 0 : }
303 :
304 0 : gossip_stats->network_egress_total_bytes = fd_gui_metrics_gosip_total_egress_bytes( peers->topo, gossip_tile_cnt );
305 :
306 0 : gossip_stats->storage_capacity = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_CAPACITY ) );
307 0 : gossip_stats->storage_expired_cnt = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_EXPIRED_COUNT ) );
308 0 : gossip_stats->storage_evicted_cnt = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_EVICTED_COUNT ) );
309 :
310 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V1_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_CONTACT_INFO_V1 ) );
311 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_VOTE_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_VOTE ) );
312 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_LOWEST_SLOT_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_LOWEST_SLOT ) );
313 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_SNAPSHOT_HASHES_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_SNAPSHOT_HASHES ) );
314 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_ACCOUNTS_HASHES_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_ACCOUNTS_HASHES ) );
315 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_EPOCH_SLOTS_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_EPOCH_SLOTS ) );
316 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V1_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_VERSION_V1 ) );
317 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V2_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_VERSION_V2 ) );
318 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_NODE_INSTANCE_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_NODE_INSTANCE ) );
319 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_DUPLICATE_SHRED_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_DUPLICATE_SHRED ) );
320 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_INCREMENTAL_SNAPSHOT_HASHES_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_INCREMENTAL_SNAPSHOT_HASHES ) );
321 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V2_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_CONTACT_INFO_V2 ) );
322 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_LAST_VOTED_FORK_SLOTS_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_RESTART_LAST_VOTED_FORK_SLOTS ) );
323 0 : gossip_stats->storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_HEAVIEST_FORK_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( GAUGE, GOSSIP, CRDS_COUNT_RESTART_HEAVIEST_FORK ) );
324 :
325 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V1_IDX ] =
326 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_CONTACT_INFO_V1 ) )
327 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_CONTACT_INFO_V1 ) );
328 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VOTE_IDX ] =
329 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_VOTE ) )
330 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_VOTE ) );
331 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_LOWEST_SLOT_IDX ] =
332 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_LOWEST_SLOT ) )
333 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_LOWEST_SLOT ) );
334 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_SNAPSHOT_HASHES_IDX ] =
335 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_SNAPSHOT_HASHES ) )
336 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_SNAPSHOT_HASHES ) );
337 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_ACCOUNTS_HASHES_IDX ] =
338 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_ACCOUNTS_HASHES ) )
339 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_ACCOUNTS_HASHES ) );
340 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_EPOCH_SLOTS_IDX ] =
341 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_EPOCH_SLOTS ) )
342 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_EPOCH_SLOTS ) );
343 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V1_IDX ] =
344 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_VERSION_V1 ) )
345 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_VERSION_V1 ) );
346 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V2_IDX ] =
347 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_VERSION_V2 ) )
348 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_VERSION_V2 ) );
349 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_NODE_INSTANCE_IDX ] =
350 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_NODE_INSTANCE ) )
351 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_NODE_INSTANCE ) );
352 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_DUPLICATE_SHRED_IDX ] =
353 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_DUPLICATE_SHRED ) )
354 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_DUPLICATE_SHRED ) );
355 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_INCREMENTAL_SNAPSHOT_HASHES_IDX ] =
356 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_INCREMENTAL_SNAPSHOT_HASHES ) )
357 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_INCREMENTAL_SNAPSHOT_HASHES ) );
358 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V2_IDX ] =
359 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_CONTACT_INFO_V2 ) )
360 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_CONTACT_INFO_V2 ) );
361 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_LAST_VOTED_FORK_SLOTS_IDX ] =
362 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_RESTART_LAST_VOTED_FORK_SLOTS ) )
363 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_RESTART_LAST_VOTED_FORK_SLOTS ) );
364 0 : gossip_stats->storage_cnt_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_HEAVIEST_FORK_IDX ] =
365 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_COUNT_RESTART_HEAVIEST_FORK ) )
366 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_COUNT_RESTART_HEAVIEST_FORK ) );
367 :
368 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V1_IDX ] =
369 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_CONTACT_INFO_V1 ) )
370 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_CONTACT_INFO_V1 ) );
371 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VOTE_IDX ] =
372 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_VOTE ) )
373 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_VOTE ) );
374 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_LOWEST_SLOT_IDX ] =
375 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_LOWEST_SLOT ) )
376 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_LOWEST_SLOT ) );
377 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_SNAPSHOT_HASHES_IDX ] =
378 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_SNAPSHOT_HASHES ) )
379 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_SNAPSHOT_HASHES ) );
380 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_ACCOUNTS_HASHES_IDX ] =
381 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_ACCOUNTS_HASHES ) )
382 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_ACCOUNTS_HASHES ) );
383 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_EPOCH_SLOTS_IDX ] =
384 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_EPOCH_SLOTS ) )
385 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_EPOCH_SLOTS ) );
386 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V1_IDX ] =
387 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_VERSION_V1 ) )
388 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_VERSION_V1 ) );
389 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_VERSION_V2_IDX ] =
390 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_VERSION_V2 ) )
391 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_VERSION_V2 ) );
392 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_NODE_INSTANCE_IDX ] =
393 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_NODE_INSTANCE ) )
394 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_NODE_INSTANCE ) );
395 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_DUPLICATE_SHRED_IDX ] =
396 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_DUPLICATE_SHRED ) )
397 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_DUPLICATE_SHRED ) );
398 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_INCREMENTAL_SNAPSHOT_HASHES_IDX ] =
399 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_INCREMENTAL_SNAPSHOT_HASHES ) )
400 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_INCREMENTAL_SNAPSHOT_HASHES ) );
401 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_CONTACT_INFO_V2_IDX ] =
402 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_CONTACT_INFO_V2 ) )
403 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_CONTACT_INFO_V2 ) );
404 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_LAST_VOTED_FORK_SLOTS_IDX ] =
405 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_RESTART_LAST_VOTED_FORK_SLOTS ) )
406 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_RESTART_LAST_VOTED_FORK_SLOTS ) );
407 0 : gossip_stats->storage_bytes_tx[ FD_METRICS_ENUM_CRDS_VALUE_V_RESTART_HEAVIEST_FORK_IDX ] =
408 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PUSH_BYTES_RESTART_HEAVIEST_FORK ) )
409 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, CRDS_TX_PULL_RESPONSE_BYTES_RESTART_HEAVIEST_FORK ) );
410 :
411 0 : gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_REQUEST_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PULL_REQUEST ) );
412 0 : gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PULL_RESPONSE ) );
413 0 : gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PUSH ) );
414 0 : gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PING_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PING ) );
415 0 : gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PONG_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PONG ) );
416 0 : gossip_stats->messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PRUNE_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_BYTES_SUCCESS_PRUNE ) );
417 :
418 0 : gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_REQUEST_IDX ] =
419 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PULL_REQUEST ) )
420 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_NOT_CONTACT_INFO ) )
421 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_LOOPBACK ) )
422 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_INACTIVE ) )
423 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_WALLCLOCK ) )
424 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_SIGNATURE ) )
425 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_REQUEST_SHRED_VERSION ) );
426 0 : gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ] =
427 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PULL_RESPONSE ) )
428 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PULL_RESPONSE_NO_VALID_CRDS ) );
429 0 : gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ] =
430 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PUSH ) )
431 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PUSH_NO_VALID_CRDS ) );
432 0 : gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PING_IDX ] =
433 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PING ) )
434 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PING_SIGNATURE ) );
435 0 : gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PONG_IDX ] =
436 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PONG ) )
437 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PONG_SIGNATURE ) );
438 0 : gossip_stats->messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PRUNE_IDX ] =
439 0 : fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_SUCCESS_PRUNE ) )
440 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PRUNE_DESTINATION ) )
441 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PRUNE_WALLCLOCK ) )
442 0 : + fd_gui_metrics_sum_tiles_counter( peers->topo, "gossvf", gossvf_tile_cnt, MIDX( COUNTER, GOSSVF, MESSAGE_RX_COUNT_DROPPED_PRUNE_SIGNATURE ) );
443 :
444 0 : gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_REQUEST_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PULL_REQUEST ) );
445 0 : gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PULL_RESPONSE ) );
446 0 : gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PUSH ) );
447 0 : gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PING_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PING ) );
448 0 : gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PONG_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PONG ) );
449 0 : gossip_stats->messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PRUNE_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_BYTES_PRUNE ) );
450 :
451 0 : gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_REQUEST_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PULL_REQUEST ) );
452 0 : gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PULL_RESPONSE ) );
453 0 : gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PUSH ) );
454 0 : gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PING_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PING ) );
455 0 : gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PONG_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PONG ) );
456 0 : gossip_stats->messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PRUNE_IDX ] = fd_gui_metrics_sum_tiles_counter( peers->topo, "gossip", gossip_tile_cnt, MIDX( COUNTER, GOSSIP, MESSAGE_TX_COUNT_PRUNE ) );
457 0 : }
458 :
459 : static int
460 : fd_gui_peers_contact_info_eq( fd_contact_info_t const * ci1,
461 0 : fd_contact_info_t const * ci2 ) {
462 0 : int ci_eq =
463 0 : ci1->shred_version == ci2->shred_version
464 0 : && ci1->instance_creation_wallclock_nanos== ci2->instance_creation_wallclock_nanos
465 : // && ci1->wallclock_nanos == ci2->wallclock_nanos
466 0 : && ci1->version.client == ci2->version.client
467 0 : && ci1->version.major == ci2->version.major
468 0 : && ci1->version.minor == ci2->version.minor
469 0 : && ci1->version.patch == ci2->version.patch
470 0 : && ci1->version.commit == ci2->version.commit
471 0 : && ci1->version.feature_set == ci2->version.feature_set;
472 :
473 0 : if( FD_LIKELY( !ci_eq ) ) return 0;
474 0 : for( ulong j=0UL; j<(FD_CONTACT_INFO_SOCKET_CNT); j++ ) {
475 0 : if( FD_LIKELY( !(ci1->sockets[ j ].addr==ci2->sockets[ j ].addr && ci1->sockets[ j ].port==ci2->sockets[ j ].port) ) ) return 0;
476 0 : }
477 0 : return 1;
478 0 : }
479 :
480 : void
481 : fd_gui_peers_handle_gossip_message( fd_gui_peers_ctx_t * peers,
482 : uchar const * payload,
483 : ulong payload_sz,
484 : fd_ip4_port_t const * peer_sock,
485 0 : int is_rx ) {
486 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 );
487 :
488 : /* We set MAP_MULTI=1 since there are not guarantees that duplicates
489 : sockets wont exist. In cases where we see multiple sockets the
490 : update timestamp in fd_gui_peers_node_t is the tiebreaker */
491 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 ) ) {
492 0 : if( peer->update_time_nanos>p->update_time_nanos ) peer = p;
493 0 : }
494 :
495 0 : if( FD_UNLIKELY( !peer ) ) return; /* NOP, peer not known yet */
496 :
497 0 : fd_gossip_view_t view[ 1 ];
498 0 : ulong decode_sz = fd_gossip_msg_parse( view, payload, payload_sz );
499 0 : if( FD_UNLIKELY( !decode_sz ) ) return; /* NOP, msg unparsable */
500 :
501 0 : FD_TEST( view->tag < FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT );
502 0 : fd_ptr_if( is_rx, &peer->gossvf_rx[ view->tag ], &peer->gossip_tx[ view->tag ] )->cur += payload_sz;
503 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;
504 : #if LOGGING
505 : if( is_rx ) FD_LOG_WARNING(("payload rx=%lu", payload_sz ));
506 : else FD_LOG_WARNING(("payload tx=%lu", payload_sz ));
507 : #endif
508 0 : }
509 :
510 : #if FD_HAS_ZSTD
511 :
512 : static uchar
513 : ipinfo_lookup( fd_gui_ipinfo_node_t const * nodes,
514 0 : uint ip_addr ) {
515 0 : uchar best_country_idx = 0;
516 0 : uchar found_match = 0;
517 :
518 0 : uint ip_addr_host = fd_uint_bswap( ip_addr );
519 :
520 0 : fd_gui_ipinfo_node_t const * node = &nodes[0];
521 :
522 0 : for( uchar bit_pos=0; bit_pos<32; bit_pos++ ) {
523 0 : if( FD_UNLIKELY( node->has_prefix ) ) {
524 0 : best_country_idx = node->country_code_idx;
525 0 : found_match = 1;
526 0 : }
527 :
528 0 : uchar bit = (ip_addr_host >> (31 - bit_pos)) & 1;
529 0 : fd_gui_ipinfo_node_t const * child = bit ? node->right : node->left;
530 0 : if( FD_UNLIKELY( !child ) ) break;
531 :
532 0 : node = child;
533 0 : }
534 :
535 0 : if( FD_UNLIKELY( node->has_prefix ) ) {
536 0 : best_country_idx = node->country_code_idx;
537 0 : found_match = 1;
538 0 : }
539 :
540 0 : if( FD_LIKELY( found_match ) ) return best_country_idx;
541 0 : return UCHAR_MAX;
542 0 : }
543 :
544 : #endif
545 :
546 : void
547 : fd_gui_peers_handle_gossip_update( fd_gui_peers_ctx_t * peers,
548 : fd_gossip_update_message_t const * update,
549 0 : long now ) {
550 0 : switch( update->tag ) {
551 0 : case FD_GOSSIP_UPDATE_TAG_CONTACT_INFO: {
552 : #ifdef FD_GUI_USE_HANDHOLDING
553 : /* origin_pubkey should be the same as the contact info pubkey */
554 : if( FD_UNLIKELY( memcmp( update->contact_info.contact_info->pubkey.uc, update->origin_pubkey, 32UL ) ) ) {
555 : char ci_pk[ FD_BASE58_ENCODED_32_SZ ];
556 : char og_pk[ FD_BASE58_ENCODED_32_SZ ];
557 : fd_base58_encode_32( update->contact_info.contact_info->pubkey.uc, NULL, ci_pk );
558 : fd_base58_encode_32( update->origin_pubkey, NULL, og_pk );
559 :
560 : FD_LOG_ERR(( "invariant violation: update->contact_info.contact_info->pubkey.uc=%s != update->origin_pubkey=%s ", ci_pk, og_pk ));
561 : }
562 : 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 ));
563 : #endif
564 0 : fd_gui_peers_node_t * peer = &peers->contact_info_table[ update->contact_info.idx ];
565 0 : if( FD_LIKELY( peer->valid ) ) {
566 : #if LOGGING
567 : char _pk[ FD_BASE58_ENCODED_32_SZ ];
568 : fd_base58_encode_32( update->origin_pubkey, NULL, _pk );
569 : FD_LOG_WARNING(("UPDATE %lu pk=%s", update->contact_info.idx, _pk ));
570 : #endif
571 : #ifdef FD_GUI_USE_HANDHOLDING
572 : /* invariant checks */
573 : if( FD_UNLIKELY( memcmp( peer->contact_info.pubkey.uc, update->origin_pubkey, 32UL ) ) ) {
574 : char ci_pk[ FD_BASE58_ENCODED_32_SZ ];
575 : char og_pk[ FD_BASE58_ENCODED_32_SZ ];
576 : fd_base58_encode_32( peer->contact_info.pubkey.uc, NULL, ci_pk );
577 : fd_base58_encode_32( update->origin_pubkey, NULL, og_pk );
578 :
579 : /* A new pubkey is not allowed to overwrite an existing valid index */
580 : FD_LOG_ERR(( "invariant violation: peer->contact_info.pubkey.uc=%s != update->origin_pubkey=%s ", ci_pk, og_pk ));
581 : }
582 : 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 ) );
583 : 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 );
584 : int found = 0;
585 : 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 ) ) {
586 : if( peer==p ) {
587 : found = 1;
588 : break;
589 : }
590 : }
591 : FD_TEST( found );
592 : #endif
593 : /* update does nothing */
594 0 : if( FD_UNLIKELY( fd_gui_peers_contact_info_eq( &peer->contact_info, update->contact_info.contact_info ) ) ) {
595 0 : peer->contact_info.wallclock_nanos = update->contact_info.contact_info->wallclock_nanos;
596 0 : break;
597 0 : }
598 :
599 0 : fd_gui_peers_node_sock_map_idx_remove_fast( peers->node_sock_map, update->contact_info.idx, peers->contact_info_table );
600 0 : fd_gui_peers_live_table_idx_remove ( peers->live_table, update->contact_info.idx, peers->contact_info_table );
601 :
602 0 : fd_memcpy( &peer->contact_info, update->contact_info.contact_info, sizeof(peer->contact_info) );
603 :
604 0 : peer->update_time_nanos = now;
605 : /* fetch and set country code */
606 0 : #if FD_HAS_ZSTD
607 0 : peer->country_code_idx = ipinfo_lookup( peers->ipinfo.nodes, peer->contact_info.sockets[ FD_CONTACT_INFO_SOCKET_GOSSIP ].addr );
608 : #else
609 : peer->country_code_idx = UCHAR_MAX;
610 : #endif
611 :
612 0 : fd_gui_peers_live_table_idx_insert ( peers->live_table, update->contact_info.idx, peers->contact_info_table );
613 0 : fd_gui_peers_node_sock_map_idx_insert ( peers->node_sock_map, update->contact_info.idx, peers->contact_info_table );
614 :
615 : /* broadcast update to WebSocket clients */
616 0 : fd_gui_peers_printf_nodes( peers, (int[]){ FD_GUI_PEERS_NODE_UPDATE }, (ulong[]){ update->contact_info.idx }, 1UL );
617 0 : fd_http_server_ws_broadcast( peers->http );
618 0 : } else {
619 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 ) );
620 : #if LOGGING
621 : char _pk[ FD_BASE58_ENCODED_32_SZ ];
622 : fd_base58_encode_32( update->origin_pubkey, NULL, _pk );
623 : FD_LOG_WARNING(( "ADD %lu pk=%s", update->contact_info.idx, _pk ));
624 : #endif
625 0 : memset( &peer->gossvf_rx, 0, sizeof(peer->gossvf_rx) );
626 0 : memset( &peer->gossip_tx, 0, sizeof(peer->gossip_tx) );
627 0 : memset( &peer->gossvf_rx_sum, 0, sizeof(peer->gossvf_rx_sum) );
628 0 : memset( &peer->gossip_tx_sum, 0, sizeof(peer->gossip_tx_sum) );
629 0 : peer->has_vote_info = 0;
630 0 : peer->stake = ULONG_MAX;
631 :
632 0 : fd_gui_config_parse_info_t * info = fd_gui_peers_node_info_map_ele_query( peers->node_info_map, &update->contact_info.contact_info->pubkey, NULL, peers->node_info_pool );
633 0 : if( FD_LIKELY( info ) ) fd_memcpy( peer->name, info->name, sizeof(info->name) );
634 0 : else peer->name[ 0 ] = '\0';
635 :
636 0 : peer->update_time_nanos = now;
637 0 : fd_memcpy( &peer->contact_info, update->contact_info.contact_info, sizeof(peer->contact_info) );
638 :
639 : /* fetch and set country code */
640 0 : #if FD_HAS_ZSTD
641 0 : peer->country_code_idx = ipinfo_lookup( peers->ipinfo.nodes, peer->contact_info.sockets[ FD_CONTACT_INFO_SOCKET_GOSSIP ].addr );
642 : #else
643 : peer->country_code_idx = UCHAR_MAX;
644 : #endif
645 0 : peer->valid = 1;
646 :
647 : /* update pubkey_map, sock_map */
648 0 : fd_gui_peers_node_sock_map_idx_insert ( peers->node_sock_map, update->contact_info.idx, peers->contact_info_table );
649 0 : fd_gui_peers_node_pubkey_map_idx_insert( peers->node_pubkey_map, update->contact_info.idx, peers->contact_info_table );
650 :
651 : /* update live tables */
652 0 : fd_gui_peers_live_table_idx_insert ( peers->live_table, update->contact_info.idx, peers->contact_info_table );
653 0 : fd_gui_peers_bandwidth_tracking_idx_insert( peers->bw_tracking, update->contact_info.idx, peers->contact_info_table );
654 :
655 0 : fd_gui_printf_peers_view_resize( peers, fd_gui_peers_live_table_ele_cnt( peers->live_table ) );
656 0 : fd_http_server_ws_broadcast( peers->http );
657 :
658 : /* broadcast update to WebSocket clients */
659 0 : fd_gui_peers_printf_nodes( peers, (int[]){ FD_GUI_PEERS_NODE_ADD }, (ulong[]){ update->contact_info.idx }, 1UL );
660 0 : fd_http_server_ws_broadcast( peers->http );
661 0 : }
662 0 : break;
663 0 : }
664 0 : case FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE: {
665 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 ));
666 : #if LOGGING
667 : char _pk[ FD_BASE58_ENCODED_32_SZ ];
668 : fd_base58_encode_32( update->origin_pubkey, NULL, _pk );
669 : FD_LOG_WARNING(( "REMOVE %lu pk=%s",update->contact_info_remove.idx, _pk ));
670 : #endif
671 :
672 0 : fd_gui_peers_node_t * peer = &peers->contact_info_table[ update->contact_info_remove.idx ];
673 :
674 : #ifdef FD_GUI_USE_HANDHOLDING
675 : /* invariant checks */
676 : FD_TEST( peer->valid ); /* Should have already been in the table */
677 : 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 ) );
678 : 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 );
679 : int found = 0;
680 : 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 ) ) {
681 : if( peer==p ) {
682 : found = 1;
683 : break;
684 : }
685 : }
686 : FD_TEST( found );
687 : #endif
688 0 : fd_gui_peers_live_table_idx_remove ( peers->live_table, update->contact_info_remove.idx, peers->contact_info_table );
689 0 : fd_gui_peers_bandwidth_tracking_idx_remove ( peers->bw_tracking, update->contact_info_remove.idx, peers->contact_info_table );
690 0 : fd_gui_peers_node_sock_map_idx_remove_fast ( peers->node_sock_map, update->contact_info_remove.idx, peers->contact_info_table );
691 0 : fd_gui_peers_node_pubkey_map_idx_remove_fast( peers->node_pubkey_map, update->contact_info_remove.idx, peers->contact_info_table );
692 0 : peer->valid = 0;
693 :
694 0 : fd_gui_printf_peers_view_resize( peers, fd_gui_peers_live_table_ele_cnt( peers->live_table ) );
695 0 : fd_http_server_ws_broadcast( peers->http );
696 :
697 : /* broadcast update to WebSocket clients */
698 0 : fd_gui_peers_printf_nodes( peers, (int[]){ FD_GUI_PEERS_NODE_DELETE }, (ulong[]){ update->contact_info_remove.idx }, 1UL );
699 0 : fd_http_server_ws_broadcast( peers->http );
700 0 : break;
701 0 : }
702 0 : default: break;
703 0 : }
704 0 : }
705 :
706 : #define SORT_NAME fd_gui_peers_votes_slot_sort
707 0 : #define SORT_KEY_T fd_gui_peers_vote_t
708 0 : #define SORT_BEFORE(a,b) ((a).last_vote_slot<(b).last_vote_slot)
709 : #include "../../util/tmpl/fd_sort.c"
710 :
711 : #define SORT_NAME fd_gui_peers_votes_stake_sort
712 0 : #define SORT_KEY_T fd_gui_peers_vote_t
713 0 : #define SORT_BEFORE(a,b) ((a).stake>(b).stake)
714 : #include "../../util/tmpl/fd_sort.c"
715 :
716 : #define SORT_NAME fd_gui_peers_votes_pkey_sort
717 0 : #define SORT_KEY_T fd_gui_peers_vote_t
718 0 : #define SORT_BEFORE(a,b) ( memcmp((a).node_account.uc, (b).node_account.uc, sizeof(fd_pubkey_t) ) < 0 )
719 : #include "../../util/tmpl/fd_sort.c"
720 :
721 : void
722 : fd_gui_peers_handle_vote_update( fd_gui_peers_ctx_t * peers,
723 : fd_gui_peers_vote_t * votes,
724 : ulong vote_cnt,
725 : long now,
726 0 : fd_pubkey_t * identity ) {
727 0 : (void)now;
728 0 : fd_gui_peers_vote_t * votes_sorted = votes;
729 0 : fd_gui_peers_vote_t * votes_scratch = peers->votes_scratch;
730 0 : fd_memcpy( votes_sorted, votes, vote_cnt*sizeof(fd_gui_peers_vote_t) );
731 :
732 : /* deduplicate node accounts, keeping the vote accounts with largest stake */
733 0 : fd_gui_peers_votes_stake_sort_inplace( votes_sorted, vote_cnt );
734 0 : fd_gui_peers_votes_pkey_sort_stable( votes_sorted, vote_cnt, votes_scratch );
735 :
736 0 : ulong total_stake = 0UL;
737 0 : fd_pubkey_t prev_peer = { 0 };
738 0 : for( ulong i=0UL; i<vote_cnt; i++ ) {
739 0 : if( FD_UNLIKELY( !memcmp( prev_peer.uc, votes_sorted[ i ].node_account.uc, sizeof(fd_pubkey_t) ) ) ) {
740 0 : votes_sorted[ i ].stake = ULONG_MAX; /* flag as duplicate */
741 0 : } else {
742 0 : total_stake += votes_sorted[ i ].stake;
743 0 : }
744 0 : prev_peer = votes_sorted[ i ].node_account;
745 0 : }
746 :
747 : /* get stake-weighted 67th percentile last_vote_slot */
748 0 : fd_gui_peers_votes_slot_sort_inplace( votes_sorted, vote_cnt );
749 :
750 0 : ulong cumulative_stake = 0UL;
751 0 : ulong last_vote_slot_p67 = ULONG_MAX;
752 0 : for( ulong i=0UL; i<vote_cnt; i++ ) {
753 0 : if( FD_UNLIKELY( votes_sorted[ i ].stake==ULONG_MAX ) ) continue;
754 0 : cumulative_stake += votes_sorted[ i ].stake;
755 0 : if( FD_LIKELY( 3*cumulative_stake>2*total_stake ) ) {
756 0 : last_vote_slot_p67 = votes_sorted[ i ].last_vote_slot;
757 0 : }
758 0 : }
759 :
760 : /* resuse scratch to for publish state */
761 0 : int * actions = (void *)votes_scratch;
762 0 : ulong * idxs = (ulong *)((uchar *)votes_scratch + FD_RUNTIME_MAX_VOTE_ACCOUNTS*sizeof(int));
763 0 : FD_STATIC_ASSERT( sizeof(peers->votes_scratch)>=(FD_RUNTIME_MAX_VOTE_ACCOUNTS*(sizeof(int) + sizeof(ulong))), "scratch too small" );
764 :
765 0 : ulong count = 0UL;
766 0 : for( ulong i=0UL; i<vote_cnt; i++ ) {
767 0 : if( FD_UNLIKELY( votes_sorted[ i ].stake==ULONG_MAX ) ) continue;
768 :
769 : /* votes_sorted is a copy of the vote_states bank field that has
770 : been sorted by stake descending and deduplicated. Deduplicated
771 : here means if multiple vote accounts point to the same identity
772 : key, we go with the one with the most stake. TODO: This logic
773 : will need to change once SIMD-0180 hits mainnet.
774 :
775 : As long as the vote account exists, it will be in vote_states,
776 : which get initialized at snapshot load and gets updated by the
777 : runtime. So, on any given fork, `last_voted_slot` should reflect
778 : the last landed vote for ALL the vote accounts (including those
779 : referencing identity->uc) from the perspective of that fork's
780 : bank, even if that slot didn't have landed votes for some of
781 : those accounts. */
782 0 : if( FD_UNLIKELY( !memcmp( &votes_sorted[ i ].node_account, identity->uc, sizeof(fd_pubkey_t) ) && peers->slot_voted!=votes_sorted[ i ].last_vote_slot ) ) {
783 0 : peers->slot_voted = fd_ulong_if( votes_sorted[ i ].last_vote_slot==0UL, ULONG_MAX, votes_sorted[ i ].last_vote_slot );
784 0 : fd_gui_peers_printf_vote_slot( peers );
785 0 : fd_http_server_ws_broadcast( peers->http );
786 0 : }
787 :
788 0 : ulong peer_idx = fd_gui_peers_node_pubkey_map_idx_query( peers->node_pubkey_map, &votes_sorted[ i ].node_account, ULONG_MAX, peers->contact_info_table );
789 0 : if( FD_UNLIKELY( peer_idx==ULONG_MAX ) ) continue; /* peer not on gossip */
790 :
791 0 : fd_gui_peers_node_t * peer = peers->contact_info_table + peer_idx;
792 :
793 : /* TODO: we only publish updates when stake changes, otherwise we'd
794 : have to republish for every peer every slot, which ends up being
795 : too much bandwidth because we republish all the peer info.
796 : Ideally, we decouple the vote updates from the reset of the peer
797 : info which would let us make updates quickly. */
798 0 : int is_delinquent = ((long)last_vote_slot_p67 - (long)votes_sorted[ i ].last_vote_slot) > 150L;
799 0 : int vote_eq = peer->has_vote_info
800 0 : && !memcmp( peer->vote_account.uc, votes_sorted[ i ].vote_account.uc, sizeof(fd_pubkey_t) )
801 0 : && peer->stake ==votes_sorted[ i ].stake
802 : // && peer->last_vote_slot ==votes_sorted[ i ].last_vote_slot
803 : // && peer->last_vote_timestamp ==votes_sorted[ i ].last_vote_timestamp
804 : // && peer->epoch_credits ==votes_sorted[ i ].epoch_credits
805 0 : && peer->commission ==votes_sorted[ i ].commission
806 0 : && peer->epoch ==votes_sorted[ i ].epoch
807 0 : && peer->delinquent ==is_delinquent;
808 :
809 0 : if( FD_LIKELY( vote_eq ) ) continue; /* nop */
810 :
811 0 : peer->has_vote_info = 1;
812 0 : peer->vote_account = votes_sorted[ i ].vote_account;
813 0 : peer->last_vote_slot = votes_sorted[ i ].last_vote_slot;
814 0 : peer->last_vote_timestamp = votes_sorted[ i ].last_vote_timestamp;
815 0 : peer->epoch_credits = votes_sorted[ i ].epoch_credits;
816 0 : peer->commission = votes_sorted[ i ].commission;
817 0 : peer->epoch = votes_sorted[ i ].epoch;
818 0 : peer->delinquent = is_delinquent;
819 :
820 0 : if( FD_UNLIKELY( peer->stake!=votes_sorted[ i ].stake ) ) {
821 0 : fd_gui_peers_live_table_idx_remove( peers->live_table, peer_idx, peers->contact_info_table );
822 0 : peer->stake = votes_sorted[ i ].stake;
823 0 : fd_gui_peers_live_table_idx_insert( peers->live_table, peer_idx, peers->contact_info_table );
824 0 : }
825 :
826 0 : actions[ count ] = FD_GUI_PEERS_NODE_UPDATE;
827 0 : idxs [ count ] = peer_idx;
828 0 : count++;
829 0 : }
830 :
831 0 : if( FD_UNLIKELY( count ) ) {
832 0 : fd_gui_peers_printf_nodes( peers, actions, idxs, count );
833 0 : fd_http_server_ws_broadcast( peers->http );
834 0 : }
835 0 : }
836 :
837 : void
838 : fd_gui_peers_handle_config_account( fd_gui_peers_ctx_t * peers,
839 : uchar const * data,
840 0 : ulong sz ) {
841 : /* optimistically acquire node_info */
842 0 : if( FD_UNLIKELY( !fd_gui_peers_node_info_pool_free( peers->node_info_pool ) ) ) {
843 0 : FD_LOG_WARNING(( "On-chain ConfigProgram accounts count exceeded %lu", FD_CONTACT_INFO_TABLE_SIZE ));
844 0 : return;
845 0 : }
846 0 : fd_gui_config_parse_info_t * node_info = fd_gui_peers_node_info_pool_ele_acquire( peers->node_info_pool );
847 :
848 0 : cJSON * json;
849 0 : if( FD_UNLIKELY( !fd_gui_config_parse_validator_info_check( data, sz, &json, &node_info->pubkey ) ) ) {
850 0 : fd_gui_peers_node_info_pool_ele_release( peers->node_info_pool, node_info );
851 0 : return;
852 0 : }
853 :
854 0 : if( FD_UNLIKELY( fd_gui_peers_node_info_map_ele_query( peers->node_info_map, &node_info->pubkey, NULL, peers->node_info_pool ) ) ) {
855 0 : fd_gui_peers_node_info_pool_ele_release( peers->node_info_pool, node_info );
856 0 : cJSON_Delete( json );
857 0 : return; /* no duplicate entries */
858 0 : }
859 :
860 0 : fd_gui_config_parse_validator_info( json, node_info ); /* calls cJSON_delete( json ) */
861 0 : fd_gui_peers_node_info_map_ele_insert( peers->node_info_map, node_info, peers->node_info_pool );
862 0 : }
863 :
864 :
865 : static void
866 0 : fd_gui_peers_viewport_snap( fd_gui_peers_ctx_t * peers, ulong ws_conn_id ) {
867 0 : FD_TEST( peers->client_viewports[ ws_conn_id ].connected );
868 0 : if( FD_UNLIKELY( peers->client_viewports[ ws_conn_id ].row_cnt==0UL ) ) return; /* empty viewport */
869 0 : if( FD_UNLIKELY( 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 ));
870 :
871 0 : if( FD_UNLIKELY( fd_gui_peers_live_table_active_sort_key_cnt( peers->live_table )==FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT ) ) {
872 : /* we're out of cached sort keys. disconnect the oldest client */
873 0 : ulong oldest_ws_conn_id = ULONG_MAX;
874 0 : long oldest_connected_time = LONG_MAX;
875 0 : for( ulong i=0UL; i<peers->max_ws_conn_cnt; i++ ) {
876 0 : if( FD_UNLIKELY( peers->client_viewports[ i ].connected && peers->client_viewports[ i ].connected_time < oldest_connected_time ) ) {
877 0 : oldest_ws_conn_id = i;
878 0 : oldest_connected_time = peers->client_viewports[ i ].connected_time;
879 0 : }
880 0 : }
881 0 : FD_TEST( oldest_ws_conn_id!=ULONG_MAX );
882 0 : fd_gui_peers_live_table_sort_key_remove( peers->live_table, &peers->client_viewports[ oldest_ws_conn_id ].sort_key );
883 0 : FD_TEST( fd_gui_peers_live_table_active_sort_key_cnt( peers->live_table )==FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT-1UL );
884 0 : }
885 :
886 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;
887 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;
888 0 : iter = fd_gui_peers_live_table_fwd_iter_next( iter, peers->contact_info_table ), j++ ) {
889 0 : if( FD_LIKELY( j<peers->client_viewports[ ws_conn_id ].start_row ) ) continue;
890 0 : fd_gui_peers_node_t const * cur = fd_gui_peers_live_table_fwd_iter_ele_const( iter, peers->contact_info_table );
891 :
892 0 : ulong viewport_idx = j-peers->client_viewports[ ws_conn_id ].start_row;
893 0 : FD_TEST( viewport_idx<FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ );
894 0 : fd_gui_peers_node_t * ref = &peers->client_viewports[ ws_conn_id ].viewport[ viewport_idx ];
895 :
896 0 : fd_memcpy( ref, cur, sizeof(fd_gui_peers_node_t) );
897 0 : }
898 0 : }
899 :
900 : static int
901 : fd_gui_peers_request_scroll( fd_gui_peers_ctx_t * peers,
902 : ulong ws_conn_id,
903 : ulong request_id,
904 0 : cJSON const * params ) {
905 0 : if( FD_UNLIKELY( !peers->client_viewports[ ws_conn_id ].connected ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
906 :
907 0 : const cJSON * start_row_param = cJSON_GetObjectItemCaseSensitive( params, "start_row" );
908 0 : if( FD_UNLIKELY( !cJSON_IsNumber( start_row_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
909 0 : ulong _start_row = start_row_param->valueulong;
910 :
911 0 : const cJSON * row_cnt_param = cJSON_GetObjectItemCaseSensitive( params, "row_cnt" );
912 0 : if( FD_UNLIKELY( !cJSON_IsNumber( row_cnt_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
913 0 : ulong _row_cnt = row_cnt_param->valueulong;
914 :
915 0 : if( FD_UNLIKELY( _row_cnt > FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ || _start_row > fd_gui_peers_live_table_ele_cnt( peers->live_table )-_row_cnt ) ) {
916 0 : fd_gui_printf_null_query_response( peers->http, "gossip", "query_scroll", request_id );
917 0 : FD_TEST( !fd_http_server_ws_send( peers->http, ws_conn_id ) );
918 0 : return 0;
919 0 : }
920 :
921 0 : if( FD_UNLIKELY( (peers->client_viewports[ ws_conn_id ].start_row==_start_row || _row_cnt==0UL) && peers->client_viewports[ ws_conn_id ].row_cnt==_row_cnt ) ) {
922 0 : return 0; /* NOP, scroll window hasn't changed */
923 0 : }
924 :
925 : /* update the client's viewport */
926 0 : peers->client_viewports[ ws_conn_id ].start_row = _start_row;
927 0 : peers->client_viewports[ ws_conn_id ].row_cnt = _row_cnt;
928 :
929 0 : fd_gui_printf_peers_viewport_request( peers, "query_scroll", ws_conn_id, request_id );
930 0 : FD_TEST( !fd_http_server_ws_send( peers->http, ws_conn_id ) );
931 0 : return 0;
932 0 : }
933 :
934 : static int
935 : fd_gui_peers_request_sort( fd_gui_peers_ctx_t * peers,
936 : ulong ws_conn_id,
937 : ulong request_id,
938 0 : cJSON const * params ) {
939 0 : if( FD_UNLIKELY( !peers->client_viewports[ ws_conn_id ].connected ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
940 :
941 0 : const cJSON * _col = cJSON_GetObjectItemCaseSensitive( params, "col" );
942 0 : if( FD_UNLIKELY( !cJSON_IsArray( _col ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
943 :
944 0 : fd_gui_peers_live_table_sort_key_t sort_key;
945 :
946 0 : do {
947 0 : cJSON * c;
948 0 : ulong i;
949 0 : for( c = _col->child, i=0UL; c; c = c->next, i++ ) {
950 0 : if( FD_UNLIKELY( i >= fd_gui_peers_live_table_col_cnt() ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
951 0 : sort_key.col[ i ] = fd_gui_peers_live_table_col_name_to_idx( peers->live_table, c->valuestring );
952 0 : if( FD_UNLIKELY( sort_key.col[ i ]==ULONG_MAX ) ) {
953 0 : FD_LOG_WARNING(( "unexpected column name %s", c->valuestring ));
954 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
955 0 : }
956 0 : }
957 0 : } while( 0 );
958 :
959 0 : const cJSON * _dir = cJSON_GetObjectItemCaseSensitive( params, "dir" );
960 0 : if( FD_UNLIKELY( !cJSON_IsArray( _dir ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
961 :
962 0 : do {
963 0 : cJSON * c;
964 0 : ulong i;
965 0 : for( c = _dir->child, i=0UL; c; c = c->next, i++ ) {
966 0 : if( FD_UNLIKELY( i >= fd_gui_peers_live_table_col_cnt() ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
967 0 : sort_key.dir[ i ] = c->valueint;
968 0 : }
969 0 : } while( 0 );
970 :
971 0 : if( FD_UNLIKELY( !fd_gui_peers_live_table_verify_sort_key( &sort_key ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
972 :
973 0 : fd_memcpy( &peers->client_viewports[ ws_conn_id ].sort_key, &sort_key, sizeof(fd_gui_peers_live_table_sort_key_t) );
974 :
975 0 : fd_gui_printf_peers_viewport_request( peers, "query_sort", ws_conn_id, request_id );
976 0 : FD_TEST( !fd_http_server_ws_send( peers->http, ws_conn_id ) );
977 0 : return 0;
978 0 : }
979 :
980 : int
981 : fd_gui_peers_ws_message( fd_gui_peers_ctx_t * peers,
982 : ulong ws_conn_id,
983 : uchar const * data,
984 0 : ulong data_len ) {
985 : /* TODO: cJSON allocates, might fail SIGSYS due to brk(2)...
986 : switch off this (or use wksp allocator) */
987 0 : const char * parse_end;
988 0 : cJSON * json = cJSON_ParseWithLengthOpts( (char *)data, data_len, &parse_end, 0 );
989 0 : if( FD_UNLIKELY( !json ) ) {
990 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
991 0 : }
992 :
993 0 : const cJSON * node = cJSON_GetObjectItemCaseSensitive( json, "id" );
994 0 : if( FD_UNLIKELY( !cJSON_IsNumber( node ) ) ) {
995 0 : cJSON_Delete( json );
996 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
997 0 : }
998 0 : ulong id = node->valueulong;
999 :
1000 0 : const cJSON * topic = cJSON_GetObjectItemCaseSensitive( json, "topic" );
1001 0 : if( FD_UNLIKELY( !cJSON_IsString( topic ) || topic->valuestring==NULL ) ) {
1002 0 : cJSON_Delete( json );
1003 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
1004 0 : }
1005 :
1006 0 : const cJSON * key = cJSON_GetObjectItemCaseSensitive( json, "key" );
1007 0 : if( FD_UNLIKELY( !cJSON_IsString( key ) || key->valuestring==NULL ) ) {
1008 0 : cJSON_Delete( json );
1009 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
1010 0 : }
1011 :
1012 0 : if( FD_LIKELY( !strcmp( topic->valuestring, "gossip" ) && !strcmp( key->valuestring, "query_sort" ) ) ) {
1013 0 : const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
1014 0 : if( FD_UNLIKELY( !cJSON_IsObject( params ) ) ) {
1015 0 : cJSON_Delete( json );
1016 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
1017 0 : }
1018 :
1019 0 : int result = fd_gui_peers_request_sort( peers, ws_conn_id, id, params );
1020 0 : cJSON_Delete( json );
1021 0 : return result;
1022 0 : } else if( FD_LIKELY( !strcmp( topic->valuestring, "gossip" ) && !strcmp( key->valuestring, "query_scroll" ) ) ) {
1023 0 : const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
1024 0 : if( FD_UNLIKELY( !cJSON_IsObject( params ) ) ) {
1025 0 : cJSON_Delete( json );
1026 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
1027 0 : }
1028 :
1029 0 : int result = fd_gui_peers_request_scroll( peers, ws_conn_id, id, params );
1030 0 : cJSON_Delete( json );
1031 0 : return result;
1032 0 : }
1033 :
1034 0 : cJSON_Delete( json );
1035 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_UNKNOWN_METHOD;
1036 0 : }
1037 :
1038 : static void
1039 : fd_gui_peers_viewport_log( fd_gui_peers_ctx_t * peers,
1040 0 : ulong ws_conn_id) {
1041 :
1042 0 : FD_TEST( peers->client_viewports[ ws_conn_id ].row_cnt<=FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ );
1043 :
1044 0 : char out[ 1<<14 ];
1045 0 : char * p = fd_cstr_init( out );
1046 :
1047 0 : p = fd_cstr_append_printf( p,
1048 0 : "\n[Viewport] table_size=%lu max_viewport_size=%lu\n"
1049 0 : "+-------+----------------+----------------+----------------+----------------+----------------------------------------------------+-----------------+\n"
1050 0 : "| Row # | RX Push (bps) | RX Pull (bps) | TX Push (bps) | TX Pull (bps) | Pubkey | IP Address |\n"
1051 0 : "+-------+----------------+----------------+----------------+----------------+----------------------------------------------------+-----------------+\n",
1052 0 : fd_gui_peers_live_table_ele_cnt( peers->live_table ), peers->client_viewports[ ws_conn_id ].row_cnt );
1053 :
1054 0 : FD_TEST( peers->client_viewports[ ws_conn_id ].connected );
1055 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;
1056 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;
1057 0 : iter = fd_gui_peers_live_table_fwd_iter_next(iter, peers->contact_info_table), j++ ) {
1058 0 : if( FD_LIKELY( j < peers->client_viewports[ ws_conn_id ].start_row ) ) continue;
1059 :
1060 0 : fd_gui_peers_node_t const * cur = fd_gui_peers_live_table_fwd_iter_ele_const( iter, peers->contact_info_table );
1061 :
1062 0 : char pubkey_base58[ FD_BASE58_ENCODED_32_SZ ];
1063 0 : fd_base58_encode_32( cur->contact_info.pubkey.uc, NULL, pubkey_base58 );
1064 :
1065 0 : char peer_addr[ 16 ]; /* 255.255.255.255 + '\0' */
1066 0 : FD_TEST(fd_cstr_printf_check( peer_addr, sizeof(peer_addr), NULL, FD_IP4_ADDR_FMT,
1067 0 : FD_IP4_ADDR_FMT_ARGS( cur->contact_info.sockets[FD_CONTACT_INFO_SOCKET_GOSSIP].addr ) ) );
1068 :
1069 0 : long cur_egress_push_bps = cur->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ].rate_ema;
1070 0 : long cur_ingress_push_bps = cur->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ].rate_ema;
1071 0 : long cur_egress_pull_response_bps = cur->gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ].rate_ema;
1072 0 : long cur_ingress_pull_response_bps = cur->gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ].rate_ema;
1073 :
1074 0 : p = fd_cstr_append_printf( p,
1075 0 : "| %5lu | %14ld | %14ld | %14ld | %14ld | %-50s | %-15s |\n",
1076 0 : peers->client_viewports[ ws_conn_id ].start_row + j,
1077 0 : cur_ingress_push_bps,
1078 0 : cur_ingress_pull_response_bps,
1079 0 : cur_egress_push_bps,
1080 0 : cur_egress_pull_response_bps,
1081 0 : pubkey_base58,
1082 0 : peer_addr );
1083 0 : }
1084 0 : p = fd_cstr_append_printf(p, "+-------+----------------+----------------+----------------+----------------+----------------------------------------------------+-----------------+" );
1085 0 : fd_cstr_fini( p );
1086 0 : FD_LOG_NOTICE(( "%s", out ));
1087 0 : }
1088 :
1089 : static void
1090 0 : fd_gui_peers_ws_conn_rr_grow( fd_gui_peers_ctx_t * peers, ulong ws_conn_id ) {
1091 0 : if( FD_UNLIKELY( !peers->open_ws_conn_cnt ) ) peers->active_ws_conn_id = ws_conn_id;
1092 0 : peers->open_ws_conn_cnt++;
1093 0 : }
1094 :
1095 : static void
1096 0 : fd_gui_peers_ws_conn_rr_shrink( fd_gui_peers_ctx_t * peers, ulong ws_conn_id ) {
1097 0 : peers->open_ws_conn_cnt--;
1098 :
1099 0 : if( FD_UNLIKELY( peers->open_ws_conn_cnt && peers->active_ws_conn_id==ws_conn_id ) ) {
1100 0 : for( ulong i=1UL; i<peers->max_ws_conn_cnt+1UL; i++ ) {
1101 0 : ulong next_ws_conn_id = (ws_conn_id + i) % peers->max_ws_conn_cnt;
1102 0 : if( FD_UNLIKELY( peers->client_viewports[ next_ws_conn_id ].connected ) ) {
1103 0 : peers->active_ws_conn_id = next_ws_conn_id;
1104 0 : break;
1105 0 : }
1106 0 : }
1107 0 : }
1108 0 : }
1109 :
1110 : static int
1111 0 : fd_gui_peers_ws_conn_rr_advance( fd_gui_peers_ctx_t * peers, long now ) {
1112 0 : if( FD_LIKELY( !peers->open_ws_conn_cnt || now <= peers->next_client_nanos ) ) return 0;
1113 :
1114 0 : for( ulong i=1UL; i<peers->max_ws_conn_cnt+1UL; i++ ) {
1115 0 : ulong next_ws_conn_id = (peers->active_ws_conn_id + i) % peers->max_ws_conn_cnt;
1116 0 : if( FD_UNLIKELY( peers->client_viewports[ next_ws_conn_id ].connected ) ) {
1117 0 : peers->active_ws_conn_id = next_ws_conn_id;
1118 0 : break;
1119 0 : }
1120 0 : }
1121 0 : return 1;
1122 0 : }
1123 :
1124 : int
1125 0 : fd_gui_peers_poll( fd_gui_peers_ctx_t * peers, long now ) {
1126 0 : int did_work = 0;
1127 :
1128 : /* update client viewports in a round-robin */
1129 0 : if( FD_UNLIKELY( fd_gui_peers_ws_conn_rr_advance( peers, now ) ) ) {
1130 0 : FD_TEST( peers->client_viewports[ peers->active_ws_conn_id ].connected );
1131 0 : if( FD_LIKELY( peers->client_viewports[ peers->active_ws_conn_id ].row_cnt ) ) {
1132 : /* broadcast the diff as cell updates */
1133 0 : fd_gui_printf_peers_viewport_update( peers, peers->active_ws_conn_id );
1134 :
1135 : #if LOGGING
1136 : /* log the diff */
1137 : fd_gui_peers_viewport_log( peers, peers->active_ws_conn_id );
1138 : #endif
1139 0 : (void)fd_gui_peers_viewport_log;
1140 :
1141 : /* update client state to the latest viewport */
1142 0 : fd_gui_peers_viewport_snap( peers, peers->active_ws_conn_id );
1143 :
1144 : /* In rare cases, fd_http_server_ws_send can close the websocket
1145 : connection. Since fd_gui_peers_viewport_snap assumes the connected
1146 : peer has not disconnected, we call it before. */
1147 0 : FD_TEST( !fd_http_server_ws_send( peers->http, peers->active_ws_conn_id ) );
1148 0 : }
1149 :
1150 0 : peers->next_client_nanos = now + ((FD_GUI_PEERS_WS_VIEWPORT_UPDATE_INTERVAL_MILLIS * 1000000L) / (long)peers->open_ws_conn_cnt);
1151 0 : did_work = 1;
1152 0 : }
1153 :
1154 0 : if( FD_UNLIKELY( now >= peers->next_metric_rate_update_nanos ) ) {
1155 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 );
1156 0 : !fd_gui_peers_node_pubkey_map_iter_done( iter, peers->node_pubkey_map, peers->contact_info_table );
1157 0 : iter = fd_gui_peers_node_pubkey_map_iter_next( iter, peers->node_pubkey_map, peers->contact_info_table ) ) {
1158 0 : fd_gui_peers_node_t * peer = fd_gui_peers_node_pubkey_map_iter_ele( iter, peers->node_pubkey_map, peers->contact_info_table );
1159 :
1160 0 : double window = (double)(now - (peers->next_metric_rate_update_nanos - (FD_GUI_PEERS_METRIC_RATE_UPDATE_INTERVAL_MILLIS * 1000000L)));
1161 :
1162 : /* optimization: no need to remove / re-insert if the rates haven't changed */
1163 0 : int change = 0;
1164 0 : for( ulong i=0UL; !change && i<FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT; i++ ) {
1165 0 : fd_gui_peers_metric_rate_t * metric = &peer->gossvf_rx[ i ];
1166 0 : long new_rate = (long)(((double)((long)metric->cur - (long)metric->ref) * 1e9 / window));
1167 0 : long new_rate_ema = fd_gui_peers_adaptive_ema( metric->update_timestamp_ns, now, (long)new_rate, (long)metric->rate_ema );
1168 0 : if( FD_LIKELY( new_rate_ema==0L && metric->rate_ema==0L ) ) continue; /* don't update zero-bandwith peers */
1169 0 : change = 1;
1170 0 : }
1171 :
1172 0 : for( ulong i=0UL; !change && i<FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT; i++ ) {
1173 0 : fd_gui_peers_metric_rate_t * metric = &peer->gossip_tx[ i ];
1174 0 : long new_rate = (long)(((double)((long)metric->cur - (long)metric->ref) * 1e9 / window));
1175 0 : long new_rate_ema = fd_gui_peers_adaptive_ema( metric->update_timestamp_ns, now, (long)new_rate, (long)metric->rate_ema );
1176 0 : if( FD_LIKELY( new_rate_ema==0L && metric->rate_ema==0L ) ) continue; /* don't update zero-bandwith peers */
1177 0 : change = 1;
1178 0 : }
1179 :
1180 0 : if( !change ) continue;
1181 :
1182 : /* live_table */
1183 0 : fd_gui_peers_live_table_ele_remove( peers->live_table, peer, peers->contact_info_table );
1184 0 : for( ulong i=0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT; i++ ) {
1185 0 : fd_gui_peers_metric_rate_t * metric = &peer->gossvf_rx[ i ];
1186 0 : long new_rate = (long)(((double)((long)metric->cur - (long)metric->ref) * 1e9 / window));
1187 0 : long new_rate_ema = fd_gui_peers_adaptive_ema( metric->update_timestamp_ns, now, (long)new_rate, (long)metric->rate_ema );
1188 0 : metric->rate_ema = fd_long_if( new_rate_ema<100L, 0L, new_rate_ema ); /* snap near-zero ema to zero. 100 bytes/s threshold */
1189 0 : metric->ref = metric->cur;
1190 0 : metric->update_timestamp_ns = now;
1191 0 : }
1192 :
1193 0 : for( ulong i=0UL; i<FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT; i++ ) {
1194 0 : fd_gui_peers_metric_rate_t * metric = &peer->gossip_tx[ i ];
1195 0 : long new_rate = (long)(((double)((long)metric->cur - (long)metric->ref) * 1e9 / window));
1196 0 : long new_rate_ema = fd_gui_peers_adaptive_ema( metric->update_timestamp_ns, now, new_rate, metric->rate_ema );
1197 0 : metric->rate_ema = fd_long_if( new_rate_ema<100L, 0L, new_rate_ema ); /* snap near-zero ema to zero. 100 bytes/s threshold */
1198 0 : metric->ref = metric->cur;
1199 0 : metric->update_timestamp_ns = now;
1200 0 : }
1201 0 : fd_gui_peers_live_table_ele_insert( peers->live_table, peer, peers->contact_info_table );
1202 :
1203 : /* bandwidth_tracking */
1204 0 : fd_gui_peers_bandwidth_tracking_ele_remove( peers->bw_tracking, peer, peers->contact_info_table );
1205 0 : peer->gossvf_rx_sum.rate_ema = fd_gui_peers_adaptive_ema( peer->gossvf_rx_sum.update_timestamp_ns, now, (long)(((double)((long)peer->gossvf_rx_sum.cur - (long)peer->gossvf_rx_sum.ref) * 1e9 / window)), peer->gossvf_rx_sum.rate_ema );
1206 0 : peer->gossvf_rx_sum.ref = peer->gossvf_rx_sum.cur;
1207 0 : peer->gossvf_rx_sum.update_timestamp_ns = now;
1208 :
1209 0 : peer->gossip_tx_sum.rate_ema = fd_gui_peers_adaptive_ema( peer->gossip_tx_sum.update_timestamp_ns, now, (long)(((double)((long)peer->gossip_tx_sum.cur - (long)peer->gossip_tx_sum.ref) * 1e9 / window)), peer->gossip_tx_sum.rate_ema );
1210 0 : peer->gossip_tx_sum.ref = peer->gossip_tx_sum.cur;
1211 0 : peer->gossip_tx_sum.update_timestamp_ns = now;
1212 0 : fd_gui_peers_bandwidth_tracking_ele_insert( peers->bw_tracking, peer, peers->contact_info_table );
1213 0 : }
1214 :
1215 0 : peers->next_metric_rate_update_nanos = now + (FD_GUI_PEERS_METRIC_RATE_UPDATE_INTERVAL_MILLIS * 1000000L);
1216 0 : did_work = 1;
1217 : #ifdef FD_GUI_USE_HANDHOLDING
1218 : fd_gui_peers_live_table_verify( peers->live_table, peers->contact_info_table );
1219 : #endif
1220 0 : }
1221 :
1222 0 : if( FD_LIKELY( now >= peers->next_gossip_stats_update_nanos ) ) {
1223 0 : fd_gui_peers_gossip_stats_snap( peers, peers->gossip_stats, now );
1224 0 : fd_gui_peers_printf_gossip_stats( peers );
1225 0 : fd_http_server_ws_broadcast( peers->http );
1226 :
1227 0 : peers->next_gossip_stats_update_nanos = now + (FD_GUI_PEERS_GOSSIP_STATS_UPDATE_INTERVAL_MILLIS * 1000000L);
1228 0 : did_work = 1;
1229 0 : }
1230 :
1231 0 : return did_work;
1232 0 : }
1233 :
1234 : void
1235 : fd_gui_peers_ws_open( fd_gui_peers_ctx_t * peers,
1236 : ulong ws_conn_id,
1237 0 : long now ) {
1238 0 : peers->client_viewports[ ws_conn_id ].connected = 1;
1239 0 : peers->client_viewports[ ws_conn_id ].connected_time = now;
1240 0 : peers->client_viewports[ ws_conn_id ].start_row = 0;
1241 0 : peers->client_viewports[ ws_conn_id ].row_cnt = 0;
1242 0 : peers->client_viewports[ ws_conn_id ].sort_key = FD_GUI_PEERS_LIVE_TABLE_DEFAULT_SORT_KEY;
1243 0 : fd_gui_peers_ws_conn_rr_grow( peers, ws_conn_id );
1244 :
1245 0 : fd_gui_peers_printf_node_all( peers );
1246 0 : FD_TEST( !fd_http_server_ws_send( peers->http, ws_conn_id ) );
1247 0 : }
1248 :
1249 : void
1250 : fd_gui_peers_ws_close( fd_gui_peers_ctx_t * peers,
1251 0 : ulong ws_conn_id ) {
1252 0 : fd_gui_peers_live_table_sort_key_remove( peers->live_table, &peers->client_viewports[ ws_conn_id ].sort_key );
1253 0 : peers->client_viewports[ ws_conn_id ].connected = 0;
1254 0 : fd_gui_peers_ws_conn_rr_shrink( peers, ws_conn_id );
1255 0 : }
1256 :
1257 : #undef LOGGING
|