Line data Source code
1 : #ifndef HEADER_fd_src_disco_gui_fd_gui_peers_h
2 : #define HEADER_fd_src_disco_gui_fd_gui_peers_h
3 :
4 : /* fd_gui_peers defines methods that maintain metrics and metadata about
5 : Solana cluster peers that are active on the Gossip network.
6 :
7 : Peer identifiers are added and removed by incoming update messages
8 : from the gossip tile. Additional information about the peer is
9 : obtained from other places and merged into the a large peers table
10 : with live updates.
11 :
12 : fd_gui_peers also defines methods for handling messages from a
13 : WebSocket client. These messages contain peer related information,
14 : including a live view of the peer table with the option to order the
15 : table a custom sort key. */
16 :
17 : #include "fd_gui_config_parse.h"
18 : #include "fd_gui_ema.h"
19 : #include "fd_gui_gossip_bw.h"
20 :
21 : #include "../../disco/metrics/generated/fd_metrics_enums.h"
22 : #include "../../flamenco/gossip/fd_gossip_message.h"
23 : #include "../../flamenco/leaders/fd_leaders_base.h"
24 : #include "../../flamenco/alpenglow/fd_block_marker.h"
25 :
26 : #include "../../waltz/http/fd_http_server.h"
27 : #include "../topo/fd_topo.h"
28 : #include "../../util/fd_hash32.h"
29 :
30 : #define FD_GUI_GEOIP_ZSTD_COMPRESSION_LEVEL 19
31 0 : #define FD_GUI_GEOIP_ZSTD_WINDOW_LOG 23
32 : #define ZSTD_STATIC_LINKING_ONLY
33 : #include <zstd.h>
34 :
35 : #include <math.h>
36 :
37 : /* Node in an IP geolocation binary trie. The trie is built from a
38 : compressed binary file with the following format:
39 :
40 : GeoIp Binary layout
41 : | country_code_cnt (8 bytes) |
42 : | country_codes (2*country_code_cnt bytes) |
43 : | city_names_cnt (8 bytes) |
44 : | city_names (see below) |
45 : | zero pad to 4-byte alignment |
46 : | seg_cnt (8 bytes) |
47 : | seg_start (4*seg_cnt bytes) |
48 : | seg_country (1*seg_cnt bytes) |
49 : | zero pad to 4-byte alignment |
50 : | seg_city (4*seg_cnt bytes) |
51 :
52 : country_code_cnt: ulong count of country codes (little-endian)
53 : country_codes: Array of 2-byte ASCII country codes, not null-terminated.
54 : city_names_cnt: ulong count of city names (little-endian).
55 : city_names: Concatenation of variable-length, null-terminated city names
56 : seg_cnt: ulong count of segments
57 :
58 : The segments partition the entire IPv4 space into disjoint,
59 : contiguous ranges: segment i covers [seg_start[i], seg_start[i+1])
60 : (host byte order, little-endian on disk; seg_start[0]==0 and the
61 : last segment extends to 2^32). The generator flattens the source
62 : database's (possibly nested) CIDR records into this form with
63 : longest-prefix-match semantics, so lookup is a binary search over
64 : seg_start rather than a bit trie walk: the columnar arrays are used
65 : in place directly out of the decompressed image, making startup a
66 : single streaming decompress. seg_country[i] is an index into
67 : country_codes, or 255 for unmapped space; seg_city[i] is an index
68 : into city_names, or UINT_MAX. */
69 :
70 0 : #define FD_GUI_GEOIP_BIN_MAX (128UL<<20) /* decompressed image cap */
71 0 : #define FD_GUI_GEOIP_MAX_CITY_NAME_SZ (80UL)
72 : #define FD_GUI_GEOIP_MAX_CITY_CNT (160000UL)
73 : #define FD_GUI_GEOIP_MAX_COUNTRY_CNT (254UL)
74 :
75 : struct fd_gui_wfs_peer {
76 : fd_pubkey_t identity_key;
77 : ulong stake;
78 : int is_online;
79 : long update_time_nanos;
80 :
81 : ulong fresh_prev;
82 : ulong fresh_next;
83 : };
84 : typedef struct fd_gui_wfs_peer fd_gui_wfs_peer_t;
85 :
86 : #define DLIST_NAME wfs_fresh_dlist
87 : #define DLIST_ELE_T fd_gui_wfs_peer_t
88 0 : #define DLIST_PREV fresh_prev
89 0 : #define DLIST_NEXT fresh_next
90 : #include "../../util/tmpl/fd_dlist.c"
91 :
92 0 : #define FD_GUI_PEERS_NODE_ADD (1)
93 0 : #define FD_GUI_PEERS_NODE_UPDATE (2)
94 0 : #define FD_GUI_PEERS_NODE_DELETE (3)
95 :
96 0 : #define FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT (256UL) /* maximum number of maintained active sort keys */
97 : #define FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ (200UL) /* the maximum number of rows a client can request for a table viewport */
98 0 : #define FD_GUI_PEERS_WS_VIEWPORT_UPDATE_INTERVAL_MILLIS ( 150L)
99 0 : #define FD_GUI_PEERS_METRIC_RATE_UPDATE_INTERVAL_MILLIS ( 150L)
100 0 : #define FD_GUI_PEERS_GOSSIP_STATS_UPDATE_INTERVAL_MILLIS ( 300L)
101 :
102 : FD_STATIC_ASSERT( FD_CONTACT_INFO_TABLE_SIZE < UINT_MAX, gui_peer_idx_fits_uint );
103 :
104 0 : #define FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT (64UL)
105 :
106 : static inline void
107 : fd_gui_peers_top_insert( ulong * top,
108 : double * val,
109 : ulong * cnt,
110 : ulong max,
111 : ulong idx,
112 0 : double v ) {
113 0 : ulong n = *cnt;
114 0 : if( FD_LIKELY( n==max ) ) {
115 0 : if( FD_LIKELY( !max || !(v>val[ n-1UL ]) ) ) return;
116 0 : n--; /* drop the smallest */
117 0 : }
118 0 : ulong i = n;
119 0 : while( i && v>val[ i-1UL ] ) { top[ i ] = top[ i-1UL ]; val[ i ] = val[ i-1UL ]; i--; }
120 0 : top[ i ] = idx;
121 0 : val[ i ] = v;
122 0 : *cnt = n+1UL;
123 0 : }
124 :
125 : /* Some table columns are rates of change, which require keeping a
126 : historical value / timestamp. */
127 : struct fd_gui_peers_metric_rate {
128 : ulong cur;
129 : ulong ref;
130 : fd_gui_ema_t rate_ema; /* units per sec. live_table treaps use this field to sort table entries */
131 : long update_timestamp_ns; /* time when cur was last copied over to ref */
132 : };
133 : typedef struct fd_gui_peers_metric_rate fd_gui_peers_metric_rate_t;
134 :
135 0 : #define FD_GUI_PEERS_EMA_HALF_LIFE_NS (3000000000L)
136 :
137 : struct fd_gui_peers_voter {
138 : fd_vote_stake_weight_t weight;
139 : };
140 : typedef struct fd_gui_peers_voter fd_gui_peers_voter_t;
141 :
142 : /* fd_gui_peers_row_t holds all of the per-peer state that is read when
143 : formatting websocket viewport messages snapshotted into a client's
144 : viewport. */
145 : struct fd_gui_peers_row {
146 : int valid;
147 : long update_time_nanos;
148 : fd_pubkey_t pubkey;
149 : long wallclock_nanos;
150 : fd_gossip_contact_info_t contact_info;
151 : char name[ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
152 :
153 : fd_gui_peers_metric_rate_t gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT ];
154 : fd_gui_peers_metric_rate_t gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT ];
155 : fd_gui_peers_metric_rate_t gossvf_rx_sum; /* sum of gossvf_rx */
156 : fd_gui_peers_metric_rate_t gossip_tx_sum; /* sum of gossip_tx */
157 :
158 : int has_vote_info;
159 : fd_pubkey_t vote_account;
160 : ulong stake;
161 : int delinquent;
162 :
163 : uchar country_code_idx;
164 : uint city_name_idx;
165 : };
166 : typedef struct fd_gui_peers_row fd_gui_peers_row_t;
167 :
168 : struct fd_gui_peers_node {
169 : fd_gui_peers_row_t row;
170 :
171 : struct {
172 : ulong next;
173 : ulong prev;
174 : } pubkey_map;
175 :
176 : struct {
177 : ulong next;
178 : ulong prev;
179 : } sock_map;
180 :
181 : struct {
182 : uint parent;
183 : uint left;
184 : uint right;
185 : uint prio;
186 : uint next;
187 : uint prev;
188 : } treaps_live_table[ FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT ];
189 : struct {
190 : ulong next;
191 : ulong prev;
192 : } dlist_live_table;
193 : ulong sort_keys_live_table;
194 :
195 : /* Peers that received or sent bytes since their rates last settled
196 : to zero. Only these need their rate EMAs advanced. */
197 : int bw_dirty;
198 : struct {
199 : ulong next;
200 : ulong prev;
201 : } bw_dirty_dlist;
202 : };
203 : typedef struct fd_gui_peers_node fd_gui_peers_node_t;
204 :
205 : #define DLIST_NAME fd_gui_peers_bw_dirty_dlist
206 : #define DLIST_ELE_T fd_gui_peers_node_t
207 0 : #define DLIST_PREV bw_dirty_dlist.prev
208 0 : #define DLIST_NEXT bw_dirty_dlist.next
209 : #include "../../util/tmpl/fd_dlist.c"
210 :
211 : struct fd_gui_peers_gossip_stats {
212 : long sample_time;
213 : ulong network_health_pull_response_msg_rx_success;
214 : ulong network_health_pull_response_msg_rx_failure;
215 : ulong network_health_push_msg_rx_success;
216 : ulong network_health_push_msg_rx_failure;
217 : ulong network_health_push_crds_rx_duplicate;
218 : ulong network_health_pull_response_crds_rx_duplicate;
219 : ulong network_health_push_crds_rx_success;
220 : ulong network_health_push_crds_rx_failure;
221 : ulong network_health_pull_response_crds_rx_success;
222 : ulong network_health_pull_response_crds_rx_failure;
223 : ulong network_health_push_msg_tx;
224 : ulong network_health_pull_response_msg_tx;
225 : ulong network_health_total_stake; /* lamports */
226 : ulong network_health_total_peers;
227 : ulong network_health_connected_stake; /* lamports */
228 : ulong network_health_connected_staked_peers;
229 : ulong network_health_connected_unstaked_peers;
230 : ulong network_ingress_total_bytes;
231 : ulong network_ingress_peer_sz;
232 : long network_ingress_peer_bytes_per_sec [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
233 : char network_ingress_peer_names [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ][ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
234 : fd_pubkey_t network_ingress_peer_identities[ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
235 : long network_ingress_total_bytes_per_sec;
236 : ulong network_egress_total_bytes;
237 : ulong network_egress_peer_sz;
238 : long network_egress_peer_bytes_per_sec [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
239 : char network_egress_peer_names [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ][ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
240 : fd_pubkey_t network_egress_peer_identities[ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
241 : long network_egress_total_bytes_per_sec;
242 : ulong storage_capacity;
243 : ulong storage_expired_cnt;
244 : ulong storage_evicted_cnt;
245 : ulong storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_TYPE_CNT ];
246 : ulong storage_cnt_tx [ FD_METRICS_ENUM_CRDS_VALUE_TYPE_CNT ];
247 : ulong storage_bytes_tx [ FD_METRICS_ENUM_CRDS_VALUE_TYPE_CNT ];
248 : ulong messages_push_rx_cnt;
249 : ulong messages_push_tx_cnt;
250 : ulong messages_pull_response_rx_cnt;
251 : ulong messages_pull_response_tx_cnt;
252 : ulong messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT ];
253 : ulong messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT ];
254 : ulong messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT ];
255 : ulong messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_CNT ];
256 : };
257 : typedef struct fd_gui_peers_gossip_stats fd_gui_peers_gossip_stats_t;
258 :
259 : #define POOL_NAME fd_gui_peers_node_info_pool
260 0 : #define POOL_T fd_gui_config_parse_info_t
261 0 : #define POOL_NEXT pool.next
262 : #include "../../util/tmpl/fd_pool.c"
263 :
264 : #define MAP_NAME fd_gui_peers_node_info_map
265 : #define MAP_ELE_T fd_gui_config_parse_info_t
266 : #define MAP_KEY_T fd_pubkey_t
267 0 : #define MAP_KEY pubkey
268 0 : #define MAP_IDX_T ulong
269 0 : #define MAP_NEXT map.next
270 0 : #define MAP_PREV map.prev
271 0 : #define MAP_KEY_HASH(k,s) (fd_hash32( (k)->uc, (s) ))
272 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->uc, (k1)->uc, 32UL))
273 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
274 : #include "../../util/tmpl/fd_map_chain.c"
275 :
276 : #define MAP_NAME fd_gui_peers_node_pubkey_map
277 0 : #define MAP_ELE_T fd_gui_peers_node_t
278 : #define MAP_KEY_T fd_pubkey_t
279 0 : #define MAP_KEY row.pubkey
280 0 : #define MAP_IDX_T ulong
281 0 : #define MAP_NEXT pubkey_map.next
282 0 : #define MAP_PREV pubkey_map.prev
283 0 : #define MAP_KEY_HASH(k,s) (fd_hash32( (k)->uc, (s) ))
284 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->uc, (k1)->uc, 32UL))
285 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
286 : #include "../../util/tmpl/fd_map_chain.c"
287 :
288 : static inline ulong
289 : fd_gui_peers_sock_key_hash( fd_gossip_socket_t const * key,
290 0 : ulong seed ) {
291 0 : if( FD_LIKELY( !key->is_ipv6 ) ) return fd_ulong_hash( seed ^ ((ulong)key->ip4<<16) ^ (ulong)key->port );
292 0 : else return fd_ulong_hash( fd_hash( seed, key->ip6, 16UL ) ^ (ulong)key->port );
293 0 : }
294 :
295 : #define MAP_NAME fd_gui_peers_node_sock_map
296 0 : #define MAP_ELE_T fd_gui_peers_node_t
297 : #define MAP_KEY_T fd_gossip_socket_t
298 0 : #define MAP_KEY row.contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ]
299 0 : #define MAP_IDX_T ulong
300 0 : #define MAP_NEXT sock_map.next
301 0 : #define MAP_PREV sock_map.prev
302 0 : #define MAP_KEY_HASH(k,s) (fd_gui_peers_sock_key_hash( (k), (s) ))
303 0 : #define MAP_KEY_EQ(k0,k1) ((k0)->is_ipv6==(k1)->is_ipv6 && (k0)->port==(k1)->port && (!((k0)->is_ipv6) ? (k0)->ip4==(k1)->ip4 : !memcmp((k0)->ip6,(k1)->ip6,16UL)) )
304 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
305 : #define MAP_MULTI 1
306 : #include "../../util/tmpl/fd_map_chain.c"
307 :
308 0 : static int live_table_col_pubkey_lt( void const * a, void const * b ) { return memcmp( ((fd_pubkey_t *)a)->uc, ((fd_pubkey_t *)b)->uc, 32UL ) < 0; }
309 :
310 0 : static int live_table_col_double_lt( void const * a, void const * b ) { return *(double *)a < *(double *)b; }
311 0 : static int live_table_col_uchar_lt ( void const * a, void const * b ) { return *(uchar *)a < *(uchar *)b; }
312 0 : static int live_table_col_ipv4_lt ( void const * a, void const * b ) { return fd_uint_bswap(*(uint *)a) < fd_uint_bswap(*(uint *)b); }
313 0 : static int live_table_col_name_lt ( void const * a, void const * b ) { return memcmp( (char *)a, (char *)b, FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ) < 0; }
314 0 : static int live_table_col_stake_lt ( void const * a, void const * b ) { return fd_long_if( *(ulong *)a>LONG_MAX, -1L, (long)*(ulong *)a ) < fd_long_if( *(ulong *)b>LONG_MAX, -1L, (long)*(ulong *)b ); }
315 :
316 : #define LIVE_TABLE_NAME fd_gui_peers_live_table
317 0 : #define LIVE_TABLE_TREAP treaps_live_table
318 0 : #define LIVE_TABLE_SORT_KEYS sort_keys_live_table
319 0 : #define LIVE_TABLE_DLIST dlist_live_table
320 0 : #define LIVE_TABLE_COLUMN_CNT (9UL)
321 0 : #define LIVE_TABLE_IDX_T uint
322 0 : #define LIVE_TABLE_MAX_SORT_KEY_CNT FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT
323 : #define LIVE_TABLE_ROW_T fd_gui_peers_node_t
324 0 : #define LIVE_TABLE_COLUMNS LIVE_TABLE_COL_ARRAY( \
325 0 : LIVE_TABLE_COL_ENTRY( "Stake", row.stake, live_table_col_stake_lt ), \
326 0 : LIVE_TABLE_COL_ENTRY( "Pubkey", row.pubkey, live_table_col_pubkey_lt ), \
327 0 : LIVE_TABLE_COL_ENTRY( "Name", row.name, live_table_col_name_lt ), \
328 0 : LIVE_TABLE_COL_ENTRY( "Country", row.country_code_idx, live_table_col_uchar_lt ), \
329 0 : LIVE_TABLE_COL_ENTRY( "IP Addr", row.contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip4, live_table_col_ipv4_lt ), \
330 0 : LIVE_TABLE_COL_ENTRY( "Ingress Push", row.gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PUSH_IDX ].rate_ema.value, live_table_col_double_lt ), \
331 0 : LIVE_TABLE_COL_ENTRY( "Ingress Pull", row.gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PULL_RESPONSE_IDX ].rate_ema.value, live_table_col_double_lt ), \
332 0 : LIVE_TABLE_COL_ENTRY( "Egress Push", row.gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PUSH_IDX ].rate_ema.value, live_table_col_double_lt ), \
333 0 : LIVE_TABLE_COL_ENTRY( "Egress Pull", row.gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_TYPE_V_PULL_RESPONSE_IDX ].rate_ema.value, live_table_col_double_lt ), )
334 : #include "fd_gui_live_table_tmpl.c"
335 :
336 0 : #define FD_GUI_PEERS_LIVE_TABLE_DEFAULT_SORT_KEY ((fd_gui_peers_live_table_sort_key_t){ .col = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, .dir = { -1, -1, -1, -1, -1, -1, -1, -1, -1 } })
337 :
338 : struct fd_gui_peers_ws_conn {
339 : int connected;
340 : long connected_time;
341 :
342 : ulong start_row;
343 : ulong row_cnt;
344 : fd_gui_peers_row_t viewport[ FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ ];
345 : fd_gui_peers_live_table_sort_key_t sort_key;
346 : };
347 :
348 : typedef struct fd_gui_peers_ws_conn fd_gui_peers_ws_conn_t;
349 :
350 : struct fd_gui_ip_db {
351 : /* Columnar disjoint-segment table, pointing into the decompressed
352 : database image (see format comment above). */
353 : ulong seg_cnt;
354 : uint const * seg_start; /* [seg_cnt] sorted, host byte order */
355 : uchar const * seg_country; /* [seg_cnt] index into country_code, 255=unknown */
356 : uint const * seg_city; /* [seg_cnt] index into city_name, UINT_MAX=unknown */
357 : char country_code[ FD_GUI_GEOIP_MAX_COUNTRY_CNT ][ 3 ]; /* ISO 3166-1 alpha-2 country codes as cstrings */
358 : char city_name[ FD_GUI_GEOIP_MAX_CITY_CNT ][ FD_GUI_GEOIP_MAX_CITY_NAME_SZ ]; /* city_names as cstrings */
359 : };
360 :
361 : typedef struct fd_gui_ip_db fd_gui_ip_db_t;
362 :
363 : struct fd_gui_peers_ctx {
364 : long next_client_nanos; /* ns timestamp when we'll service the next ws client */
365 : long next_metric_rate_update_nanos; /* ns timestamp when we'll next update rate-of-change metrics */
366 : long next_gossip_stats_update_nanos; /* ns timestamp when we'll next broadcast out gossip stats message */
367 :
368 : fd_gui_config_parse_info_t * node_info_pool;
369 : fd_gui_peers_node_info_map_t * node_info_map;
370 : fd_gui_peers_node_pubkey_map_t * node_pubkey_map;
371 : fd_gui_peers_node_sock_map_t * node_sock_map;
372 : fd_gui_peers_live_table_t * live_table;
373 : fd_gui_peers_bw_dirty_dlist_t bw_dirty_dlist[ 1 ];
374 :
375 : fd_http_server_t * http;
376 : fd_topo_t const * topo;
377 :
378 : ulong max_ws_conn_cnt;
379 : ulong open_ws_conn_cnt;
380 : ulong active_ws_conn_id;
381 : fd_gui_peers_ws_conn_t * client_viewports; /* points to 2D array with max_ws_conn_cnt rows and FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ columns */
382 :
383 : fd_gui_peers_gossip_stats_t gossip_stats [ 1 ];
384 : fd_gui_peers_node_t contact_info_table[ FD_CONTACT_INFO_TABLE_SIZE ];
385 :
386 : /* stakes is sorted descending by identity pubkey. */
387 : struct {
388 : ulong epoch;
389 : ulong start_slot;
390 :
391 : ulong stakes_cnt;
392 : fd_gui_peers_voter_t stakes [ MAX_STAKE_WEIGHTS ];
393 : } epochs[ 2 ];
394 :
395 : struct {
396 : ulong epoch; /* ULONG_MAX if none */
397 : ulong last_settled; /* highest reward slot seen */
398 : ulong stamp_since; /* first stamped reward slot of epoch, ULONG_MAX if none */
399 : int reconcile; /* peers may hold vote info absent from epoch's stakes */
400 : ulong last_voted[ FD_BLS_SET_MAX ];
401 : } delinquency;
402 :
403 : union {
404 : struct {
405 : int actions[ FD_CONTACT_INFO_TABLE_SIZE ];
406 : ulong idxs [ FD_CONTACT_INFO_TABLE_SIZE ];
407 : };
408 : struct {
409 : ulong wfs_peers[ MAX_STAKE_WEIGHTS ];
410 : };
411 : struct {
412 : fd_gui_peers_row_t viewport [ FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ ]; /* new rows snapshotted from live_table */
413 : fd_gui_peers_row_t viewport_ref[ FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ ]; /* old baseline, diff reference */
414 : ulong viewport_cnt;
415 : };
416 : } scratch;
417 :
418 : uchar * dbip_image;
419 : fd_gui_ip_db_t dbip;
420 :
421 : int wfs_enabled;
422 : fd_gui_wfs_peer_t wfs_peers[ MAX_STAKE_WEIGHTS ];
423 : ulong wfs_peers_cnt;
424 : int wfs_peers_valid;
425 : int wfs_stakes_sent;
426 : wfs_fresh_dlist_t wfs_fresh_dlist[ 1 ];
427 : };
428 :
429 : typedef struct fd_gui_peers_ctx fd_gui_peers_ctx_t;
430 :
431 : FD_PROTOTYPES_BEGIN
432 :
433 : FD_FN_CONST ulong
434 : fd_gui_peers_align( void );
435 :
436 : FD_FN_CONST ulong
437 : fd_gui_peers_footprint( ulong max_ws_conn_cnt );
438 :
439 : void *
440 : fd_gui_peers_new( void * shmem,
441 : fd_http_server_t * http,
442 : fd_topo_t const * topo,
443 : ulong max_ws_conn_cnt,
444 : char const * wfs_expected_bank_hash_cstr,
445 : ulong seed,
446 : long now );
447 :
448 : fd_gui_peers_ctx_t *
449 : fd_gui_peers_join( void * shmem );
450 :
451 : /* fd_gui_peers_handle_gossip_bw adds rec_cnt per-socket gossip byte
452 : counts to the peers owning those gossip sockets. is_rx is true for
453 : ingress records from the gossvf tiles and false for egress records
454 : from the gossip tile. Records for sockets with no known peer are
455 : ignored. */
456 :
457 : void
458 : fd_gui_peers_handle_gossip_bw( fd_gui_peers_ctx_t * peers,
459 : fd_gui_gossip_bw_rec_t const * rec,
460 : ulong rec_cnt,
461 : int is_rx );
462 :
463 : /* fd_gui_peers_handle_gossip_update parses frags on the gossip_ciseen
464 : link and uses the contact info update to build up the peer table. */
465 :
466 : void
467 : fd_gui_peers_handle_gossip_update( fd_gui_peers_ctx_t * peers,
468 : fd_gossip_update_message_t const * update,
469 : long now );
470 :
471 : /* fd_gui_peers_handle_epoch_info is called at the epoch boundary and
472 : publishes updates for peer stake information. */
473 : void
474 : fd_gui_peers_handle_epoch_info( fd_gui_peers_ctx_t * peers,
475 : fd_epoch_info_msg_t const * epoch_info,
476 : long now );
477 :
478 : /* fd_gui_peers_handle_slot_completed stamps the reward cert signers in
479 : footer (slot's block footer) and publishes peers whose delinquency
480 : changed. epoch is slot's epoch. */
481 : void
482 : fd_gui_peers_handle_slot_completed( fd_gui_peers_ctx_t * peers,
483 : ulong slot,
484 : ulong epoch,
485 : fd_block_footer_t const * footer );
486 :
487 : void
488 : fd_gui_peers_handle_config_account( fd_gui_peers_ctx_t * peers,
489 : uchar const * data,
490 : ulong sz,
491 : ulong slot );
492 :
493 : void
494 : fd_gui_peers_start_wfs( fd_gui_peers_ctx_t * peers,
495 : fd_epoch_info_msg_t const * epoch_info,
496 : long now );
497 :
498 : /* fd_gui_peers_ws_message handles incoming websocket request payloads
499 : requesting peer-related responses. ws_conn_id is the connection id
500 : of the requester. data is a pointer to the start of the
501 : json-formatted request payload. data_len is the length of the
502 : request payload. */
503 : int
504 : fd_gui_peers_ws_message( fd_gui_peers_ctx_t * peers,
505 : ulong ws_conn_id,
506 : uchar const * data,
507 : ulong data_len );
508 :
509 : /* fd_gui_peers_ws_open is a callback which should be triggered when a
510 : new client opens a WebSocket connection. ws_conn_id is the
511 : connection id of the new client. now is a UNIX nanosecond timestamp
512 : for the current time. */
513 : void
514 : fd_gui_peers_ws_open( fd_gui_peers_ctx_t * peers,
515 : ulong ws_conn_id,
516 : long now );
517 :
518 : /* fd_gui_peers_ws_close is a callback which should be triggered when an
519 : existing client closes their WebSocket connection. ws_conn_id is the
520 : connection id of the client.*/
521 : void
522 : fd_gui_peers_ws_close( fd_gui_peers_ctx_t * peers,
523 : ulong ws_conn_id );
524 :
525 : /* fd_gui_peers_poll should be called in a the tile's main spin loop to
526 : periodically update peers internal state as well as publish new
527 : Websocket messages to clients. now is a UNIX nanosecond timestamp for
528 : the current time. */
529 : int
530 : fd_gui_peers_poll( fd_gui_peers_ctx_t * peers,
531 : long now );
532 :
533 : /* fd_gui_peers_next_deadline returns the wallclock nanos at which the
534 : earliest of the periodic timers run by fd_gui_peers_poll (client
535 : viewport tick, rate update, gossip stats) is next due. */
536 :
537 : long
538 : fd_gui_peers_next_deadline( fd_gui_peers_ctx_t const * peers );
539 :
540 : /* fd_gui_peers_rate_update advances the per-peer gossip bandwidth
541 : rate filters to now and re-keys the peers whose rates changed in the
542 : live table. Called by fd_gui_peers_poll once per
543 : FD_GUI_PEERS_METRIC_RATE_UPDATE_INTERVAL_MILLIS; exposed for tests. */
544 : void
545 : fd_gui_peers_rate_update( fd_gui_peers_ctx_t * peers,
546 : long now );
547 :
548 : FD_PROTOTYPES_END
549 :
550 : #endif /* HEADER_fd_src_disco_gui_fd_gui_peers_h */
|