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 :
19 : #include "../../disco/metrics/generated/fd_metrics_enums.h"
20 : #include "../../flamenco/gossip/fd_gossip_message.h"
21 : #include "../../flamenco/leaders/fd_leaders_base.h"
22 :
23 : #include "../../waltz/http/fd_http_server.h"
24 : #include "../../discof/restore/utils/fd_ssmsg.h"
25 : #include "../topo/fd_topo.h"
26 :
27 : #if FD_HAS_ZSTD
28 : #define FD_GUI_GEOIP_ZSTD_COMPRESSION_LEVEL 19
29 0 : #define FD_GUI_GEOIP_ZSTD_WINDOW_LOG 23
30 : #define ZSTD_STATIC_LINKING_ONLY
31 : #include <zstd.h>
32 : #endif
33 :
34 : #include <math.h>
35 :
36 : /* Node in an IP geolocation binary trie. The trie is built from a
37 : compressed binary file with the following format:
38 :
39 : GeoIp Binary layout
40 : | country_code_cnt (8 bytes) |
41 : | country_codes (2*country_code_cnt bytes) |
42 : | city_names_cnt (8 bytes) |
43 : | city_names (see below) |
44 : | record_cnt (8 bytes) |
45 : | records (record_cnt*10UL) |
46 :
47 : Record binary layout
48 : | ip (4 bytes) |
49 : | prefix_sz (1 byte) |
50 : | country_code_idx (1 byte) |
51 : | city_name_idx (4 bytes) |
52 :
53 : country_code_cnt: ulong count of country codes (little-endian)
54 : country_codes: Array of 2-byte ASCII country codes, not null-terminated.
55 : city_names_cnt: ulong count of city names (little-endian).
56 : city_names: Concatenation of variable-length, null-terminated city names
57 : record_cnt: ulong count of CIDR IP ranges in records
58 : records: Series of 10-byte records containing:
59 : ip: network ipv4 address (big-endian)
60 : prefix_sz: uchar count of 1 bits (up to 32) in the netmask of the CIDR address
61 : country_code_idx: uchar country code index, into country_codes
62 : city_name_idx: uint city name index, into city_names.
63 :
64 : In the trie, each node represents one bit position in an IP address.
65 : Paths follow 0 (left child) and 1 (right child) bits from the IP's MSB
66 : to LSB. Nodes with has_prefix=1 indicate a match at that prefix
67 : length country_code_idx references the 2-letter country code in the
68 : table. Maximum depth is 32 levels (for IPv4).
69 :
70 : FD_GUI_GEOIP_*_MAX_NODES should be larger than 2*num_records (since
71 : records are stored in the leaves of a binary tree). */
72 :
73 0 : #define FD_GUI_GEOIP_DBIP_MAX_NODES (1UL<<24UL) /* 16M nodes */
74 0 : #define FD_GUI_GEOIP_MAX_CITY_NAME_SZ (80UL)
75 : #define FD_GUI_GEOIP_MAX_CITY_CNT (160000UL)
76 : #define FD_GUI_GEOIP_MAX_COUNTRY_CNT (254UL)
77 :
78 : struct fd_gui_geoip_node {
79 : uchar has_prefix;
80 : uchar country_code_idx;
81 : uint city_name_idx;
82 :
83 : struct fd_gui_geoip_node * left;
84 : struct fd_gui_geoip_node * right;
85 : };
86 :
87 : typedef struct fd_gui_geoip_node fd_gui_geoip_node_t;
88 :
89 : struct fd_gui_wfs_peer {
90 : fd_pubkey_t identity_key;
91 : ulong stake;
92 : int is_online;
93 : long update_time_nanos;
94 :
95 : ulong fresh_prev;
96 : ulong fresh_next;
97 : };
98 : typedef struct fd_gui_wfs_peer fd_gui_wfs_peer_t;
99 :
100 : #define DLIST_NAME wfs_fresh_dlist
101 : #define DLIST_ELE_T fd_gui_wfs_peer_t
102 0 : #define DLIST_PREV fresh_prev
103 0 : #define DLIST_NEXT fresh_next
104 : #include "../../util/tmpl/fd_dlist.c"
105 :
106 : #define FD_GUI_PEERS_NODE_NOP (0)
107 0 : #define FD_GUI_PEERS_NODE_ADD (1)
108 0 : #define FD_GUI_PEERS_NODE_UPDATE (2)
109 0 : #define FD_GUI_PEERS_NODE_DELETE (3)
110 :
111 0 : #define FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT (256UL) /* maximum number of maintained active sort keys */
112 : #define FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ (200UL) /* the maximum number of rows a client can request for a table viewport */
113 0 : #define FD_GUI_PEERS_WS_VIEWPORT_UPDATE_INTERVAL_MILLIS ( 150L)
114 0 : #define FD_GUI_PEERS_METRIC_RATE_UPDATE_INTERVAL_MILLIS ( 150L)
115 0 : #define FD_GUI_PEERS_GOSSIP_STATS_UPDATE_INTERVAL_MILLIS ( 300L)
116 :
117 0 : #define FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT (64UL)
118 :
119 : /* Some table columns are rates of change, which require keeping a
120 : historical value / timestamp. */
121 : struct fd_gui_peers_metric_rate {
122 : ulong cur;
123 : ulong ref;
124 : long rate_ema; /* units per sec. live_table treaps use this field to sort table entries */
125 : long update_timestamp_ns; /* time when cur was last copied over to ref */
126 : };
127 : typedef struct fd_gui_peers_metric_rate fd_gui_peers_metric_rate_t;
128 :
129 0 : #define FD_GUI_PEERS_EMA_HALF_LIFE_NS (3000000000L)
130 :
131 : /* fd_gui_ema computes an adaptive exponential moving average tick.
132 : Given the previous EMA value, a new sample, timestamps, and a
133 : half-life (all in nanoseconds), returns the updated EMA. On the
134 : first call (last_update_nanos==0) the new sample is returned as-is
135 : to seed the series. */
136 :
137 : static inline double
138 : fd_gui_ema( long last_update_nanos,
139 : long now_nanos,
140 : double new_sample,
141 : double prev_ema,
142 0 : long half_life_ns ) {
143 0 : if( FD_UNLIKELY( last_update_nanos==0L ) ) return new_sample;
144 :
145 0 : long dt = now_nanos - last_update_nanos;
146 0 : if( FD_UNLIKELY( dt<=0L ) ) return prev_ema;
147 :
148 0 : double alpha = 1.0 - exp( -0.69314718055994 * (double)dt / (double)half_life_ns );
149 0 : return alpha * new_sample + (1.0 - alpha) * prev_ema;
150 0 : }
151 :
152 : struct fd_gui_peers_voter {
153 : fd_vote_stake_weight_t weight;
154 : ulong vote_slot;
155 : };
156 : typedef struct fd_gui_peers_voter fd_gui_peers_voter_t;
157 :
158 : struct fd_gui_peers_voter_idx {
159 : fd_pubkey_t key;
160 : ulong idx;
161 : };
162 : typedef struct fd_gui_peers_voter_idx fd_gui_peers_voter_idx_t;
163 :
164 : /* fd_gui_peers_row_t holds all of the per-peer state that is read when
165 : formatting websocket viewport messages snapshotted into a client's
166 : viewport. */
167 : struct fd_gui_peers_row {
168 : int valid;
169 : long update_time_nanos;
170 : fd_pubkey_t pubkey;
171 : long wallclock_nanos;
172 : fd_gossip_contact_info_t contact_info;
173 : char name[ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
174 :
175 : fd_gui_peers_metric_rate_t gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
176 : fd_gui_peers_metric_rate_t gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
177 : fd_gui_peers_metric_rate_t gossvf_rx_sum; /* sum of gossvf_rx */
178 : fd_gui_peers_metric_rate_t gossip_tx_sum; /* sum of gossip_tx */
179 :
180 : int has_vote_info;
181 : fd_pubkey_t vote_account;
182 : int delinquent;
183 : ulong stake;
184 :
185 : uchar country_code_idx;
186 : uint city_name_idx;
187 : };
188 : typedef struct fd_gui_peers_row fd_gui_peers_row_t;
189 :
190 : struct fd_gui_peers_node {
191 : fd_gui_peers_row_t row;
192 :
193 : struct {
194 : ulong next;
195 : ulong prev;
196 : } pubkey_map;
197 :
198 : struct {
199 : ulong next;
200 : ulong prev;
201 : } sock_map;
202 :
203 : struct {
204 : ulong parent;
205 : ulong left;
206 : ulong right;
207 : ulong prio;
208 : ulong next;
209 : ulong prev;
210 : } treaps_live_table[ FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT ];
211 : struct {
212 : ulong next;
213 : ulong prev;
214 : } dlist_live_table;
215 : ulong sort_keys_live_table;
216 :
217 : struct {
218 : ulong parent;
219 : ulong left;
220 : ulong right;
221 : ulong prio;
222 : ulong next;
223 : ulong prev;
224 : } treaps_bandwidth_tracking[ 2UL ];
225 : struct {
226 : ulong next;
227 : ulong prev;
228 : } dlist_bandwidth_tracking;
229 : ulong sort_keys_bandwidth_tracking;
230 : };
231 : typedef struct fd_gui_peers_node fd_gui_peers_node_t;
232 :
233 : struct fd_gui_peers_gossip_stats {
234 : long sample_time;
235 : ulong network_health_pull_response_msg_rx_success;
236 : ulong network_health_pull_response_msg_rx_failure;
237 : ulong network_health_push_msg_rx_success;
238 : ulong network_health_push_msg_rx_failure;
239 : ulong network_health_push_crds_rx_duplicate;
240 : ulong network_health_pull_response_crds_rx_duplicate;
241 : ulong network_health_push_crds_rx_success;
242 : ulong network_health_push_crds_rx_failure;
243 : ulong network_health_pull_response_crds_rx_success;
244 : ulong network_health_pull_response_crds_rx_failure;
245 : ulong network_health_push_msg_tx;
246 : ulong network_health_pull_response_msg_tx;
247 : ulong network_health_total_stake; /* lamports */
248 : ulong network_health_total_peers;
249 : ulong network_health_connected_stake; /* lamports */
250 : ulong network_health_connected_staked_peers;
251 : ulong network_health_connected_unstaked_peers;
252 : ulong network_ingress_total_bytes;
253 : ulong network_ingress_peer_sz;
254 : long network_ingress_peer_bytes_per_sec [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
255 : char network_ingress_peer_names [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ][ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
256 : fd_pubkey_t network_ingress_peer_identities[ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
257 : long network_ingress_total_bytes_per_sec;
258 : ulong network_egress_total_bytes;
259 : ulong network_egress_peer_sz;
260 : long network_egress_peer_bytes_per_sec [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
261 : char network_egress_peer_names [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ][ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
262 : fd_pubkey_t network_egress_peer_identities[ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
263 : long network_egress_total_bytes_per_sec;
264 : ulong storage_capacity;
265 : ulong storage_expired_cnt;
266 : ulong storage_evicted_cnt;
267 : ulong storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_CNT ];
268 : ulong storage_cnt_tx [ FD_METRICS_ENUM_CRDS_VALUE_CNT ];
269 : ulong storage_bytes_tx [ FD_METRICS_ENUM_CRDS_VALUE_CNT ];
270 : ulong messages_push_rx_cnt;
271 : ulong messages_push_tx_cnt;
272 : ulong messages_pull_response_rx_cnt;
273 : ulong messages_pull_response_tx_cnt;
274 : ulong messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
275 : ulong messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
276 : ulong messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
277 : ulong messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
278 : };
279 : typedef struct fd_gui_peers_gossip_stats fd_gui_peers_gossip_stats_t;
280 :
281 : #define POOL_NAME fd_gui_peers_node_info_pool
282 0 : #define POOL_T fd_gui_config_parse_info_t
283 0 : #define POOL_NEXT pool.next
284 : #include "../../util/tmpl/fd_pool.c"
285 :
286 : #define MAP_NAME fd_gui_peers_node_info_map
287 : #define MAP_ELE_T fd_gui_config_parse_info_t
288 : #define MAP_KEY_T fd_pubkey_t
289 0 : #define MAP_KEY pubkey
290 0 : #define MAP_IDX_T ulong
291 0 : #define MAP_NEXT map.next
292 0 : #define MAP_PREV map.prev
293 0 : #define MAP_KEY_HASH(k,s) (fd_hash( (s), (k)->uc, sizeof(fd_pubkey_t) ))
294 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->uc, (k1)->uc, 32UL))
295 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
296 : #include "../../util/tmpl/fd_map_chain.c"
297 :
298 : #define MAP_NAME fd_gui_peers_node_pubkey_map
299 0 : #define MAP_ELE_T fd_gui_peers_node_t
300 : #define MAP_KEY_T fd_pubkey_t
301 0 : #define MAP_KEY row.pubkey
302 0 : #define MAP_IDX_T ulong
303 0 : #define MAP_NEXT pubkey_map.next
304 0 : #define MAP_PREV pubkey_map.prev
305 0 : #define MAP_KEY_HASH(k,s) (fd_hash( (s), (k)->uc, sizeof(fd_pubkey_t) ))
306 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->uc, (k1)->uc, 32UL))
307 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
308 : #include "../../util/tmpl/fd_map_chain.c"
309 :
310 : #define MAP_NAME fd_gui_peers_node_sock_map
311 0 : #define MAP_ELE_T fd_gui_peers_node_t
312 : #define MAP_KEY_T fd_gossip_socket_t
313 0 : #define MAP_KEY row.contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ]
314 0 : #define MAP_IDX_T ulong
315 0 : #define MAP_NEXT sock_map.next
316 0 : #define MAP_PREV sock_map.prev
317 0 : #define MAP_KEY_HASH(k,s) ( fd_hash( (s), (k), sizeof(uint) + sizeof(ushort) ) )
318 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)) )
319 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
320 : #define MAP_MULTI 1
321 : #include "../../util/tmpl/fd_map_chain.c"
322 :
323 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; }
324 0 : static int live_table_col_long_lt ( void const * a, void const * b ) { return *(long *)a < *(long *)b; }
325 0 : static int live_table_col_uchar_lt ( void const * a, void const * b ) { return *(uchar *)a < *(uchar *)b; }
326 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); }
327 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; }
328 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 ); }
329 :
330 : #define LIVE_TABLE_NAME fd_gui_peers_live_table
331 0 : #define LIVE_TABLE_TREAP treaps_live_table
332 0 : #define LIVE_TABLE_SORT_KEYS sort_keys_live_table
333 0 : #define LIVE_TABLE_DLIST dlist_live_table
334 0 : #define LIVE_TABLE_COLUMN_CNT (9UL)
335 0 : #define LIVE_TABLE_MAX_SORT_KEY_CNT FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT
336 : #define LIVE_TABLE_ROW_T fd_gui_peers_node_t
337 0 : #define LIVE_TABLE_COLUMNS LIVE_TABLE_COL_ARRAY( \
338 0 : LIVE_TABLE_COL_ENTRY( "Stake", row.stake, live_table_col_stake_lt ), \
339 0 : LIVE_TABLE_COL_ENTRY( "Pubkey", row.pubkey, live_table_col_pubkey_lt ), \
340 0 : LIVE_TABLE_COL_ENTRY( "Name", row.name, live_table_col_name_lt ), \
341 0 : LIVE_TABLE_COL_ENTRY( "Country", row.country_code_idx, live_table_col_uchar_lt ), \
342 0 : LIVE_TABLE_COL_ENTRY( "IP Addr", row.contact_info.sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip4, live_table_col_ipv4_lt ), \
343 0 : LIVE_TABLE_COL_ENTRY( "Ingress Push", row.gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ].rate_ema, live_table_col_long_lt ), \
344 0 : LIVE_TABLE_COL_ENTRY( "Ingress Pull", row.gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ].rate_ema, live_table_col_long_lt ), \
345 0 : LIVE_TABLE_COL_ENTRY( "Egress Push", row.gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ].rate_ema, live_table_col_long_lt ), \
346 0 : LIVE_TABLE_COL_ENTRY( "Egress Pull", row.gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ].rate_ema, live_table_col_long_lt ), )
347 : #include "fd_gui_live_table_tmpl.c"
348 :
349 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 } })
350 :
351 : #define LIVE_TABLE_NAME fd_gui_peers_bandwidth_tracking
352 0 : #define LIVE_TABLE_TREAP treaps_bandwidth_tracking
353 0 : #define LIVE_TABLE_SORT_KEYS sort_keys_bandwidth_tracking
354 0 : #define LIVE_TABLE_DLIST dlist_bandwidth_tracking
355 0 : #define LIVE_TABLE_COLUMN_CNT (2UL)
356 0 : #define LIVE_TABLE_MAX_SORT_KEY_CNT (2UL)
357 : #define LIVE_TABLE_ROW_T fd_gui_peers_node_t
358 0 : #define LIVE_TABLE_COLUMNS LIVE_TABLE_COL_ARRAY( \
359 0 : LIVE_TABLE_COL_ENTRY( "Ingress Total", row.gossvf_rx_sum.rate_ema, live_table_col_long_lt ), \
360 0 : LIVE_TABLE_COL_ENTRY( "Egress Total", row.gossip_tx_sum.rate_ema, live_table_col_long_lt ) )
361 : #include "fd_gui_live_table_tmpl.c"
362 :
363 0 : #define FD_GUI_PEERS_BW_TRACKING_INGRESS_SORT_KEY ((fd_gui_peers_bandwidth_tracking_sort_key_t){ .col = { 0, 1 }, .dir = { -1, 0 } })
364 0 : #define FD_GUI_PEERS_BW_TRACKING_EGRESS_SORT_KEY ((fd_gui_peers_bandwidth_tracking_sort_key_t){ .col = { 0, 1 }, .dir = { 0, -1 } })
365 :
366 : struct fd_gui_peers_ws_conn {
367 : int connected;
368 : long connected_time;
369 :
370 : ulong start_row;
371 : ulong row_cnt;
372 : fd_gui_peers_row_t viewport[ FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ ];
373 : fd_gui_peers_live_table_sort_key_t sort_key;
374 : };
375 :
376 : typedef struct fd_gui_peers_ws_conn fd_gui_peers_ws_conn_t;
377 :
378 : struct fd_gui_ip_db {
379 : fd_gui_geoip_node_t * nodes;
380 : char country_code[ FD_GUI_GEOIP_MAX_COUNTRY_CNT ][ 3 ]; /* ISO 3166-1 alpha-2 country codes as cstrings */
381 : char city_name[ FD_GUI_GEOIP_MAX_CITY_CNT ][ FD_GUI_GEOIP_MAX_CITY_NAME_SZ ]; /* city_names as cstrings */
382 : };
383 :
384 : typedef struct fd_gui_ip_db fd_gui_ip_db_t;
385 :
386 : struct fd_gui_peers_ctx {
387 : long next_client_nanos; /* ns timestamp when we'll service the next ws client */
388 : long next_metric_rate_update_nanos; /* ns timestamp when we'll next update rate-of-change metrics */
389 : long next_gossip_stats_update_nanos; /* ns timestamp when we'll next broadcast out gossip stats message */
390 :
391 : fd_gui_config_parse_info_t * node_info_pool;
392 : fd_gui_peers_node_info_map_t * node_info_map;
393 : fd_gui_peers_node_pubkey_map_t * node_pubkey_map;
394 : fd_gui_peers_node_sock_map_t * node_sock_map;
395 : fd_gui_peers_live_table_t * live_table;
396 : fd_gui_peers_bandwidth_tracking_t * bw_tracking;
397 :
398 : fd_http_server_t * http;
399 : fd_topo_t const * topo;
400 :
401 : ulong max_ws_conn_cnt;
402 : ulong open_ws_conn_cnt;
403 : ulong active_ws_conn_id;
404 : 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 */
405 :
406 : fd_gui_peers_gossip_stats_t gossip_stats [ 1 ];
407 : fd_gui_peers_node_t contact_info_table[ FD_CONTACT_INFO_TABLE_SIZE ];
408 :
409 : ulong slot_voted; /* last vote slot for this validator */
410 :
411 : /* stakes is sorted descending by identity pubkey. vote_idx is sorted
412 : descending by vote account pubkey, each entry pointing back into
413 : the stakes array. */
414 : struct {
415 : ulong epoch;
416 :
417 : ulong stakes_cnt;
418 : fd_gui_peers_voter_t stakes [ MAX_COMPRESSED_STAKE_WEIGHTS ];
419 : fd_gui_peers_voter_idx_t vote_idx[ MAX_COMPRESSED_STAKE_WEIGHTS ];
420 : } epochs[ 2 ];
421 :
422 : union {
423 : struct {
424 : int actions[ FD_CONTACT_INFO_TABLE_SIZE ];
425 : ulong idxs [ FD_CONTACT_INFO_TABLE_SIZE ];
426 : };
427 : struct {
428 : ulong wfs_peers[ FD_VOTE_ACCOUNTS_MAX ];
429 : };
430 : struct {
431 : fd_stake_weight_t manifest_id_weights [ FD_VOTE_ACCOUNTS_MAX ];
432 : fd_vote_stake_weight_t manifest_vote_weights[ FD_VOTE_ACCOUNTS_MAX ];
433 : };
434 : fd_gui_peers_voter_t voters_scratch[ MAX_COMPRESSED_STAKE_WEIGHTS ];
435 : struct {
436 : fd_gui_peers_row_t viewport [ FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ ]; /* new rows snapshotted from live_table */
437 : fd_gui_peers_row_t viewport_ref[ FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ ]; /* old baseline, diff reference */
438 : ulong viewport_cnt;
439 : };
440 : } scratch;
441 :
442 : #if FD_HAS_ZSTD
443 : ZSTD_DCtx * zstd_dctx;
444 : #endif
445 :
446 : fd_gui_ip_db_t dbip;
447 :
448 : int wfs_enabled;
449 : fd_gui_wfs_peer_t wfs_peers[ FD_VOTE_ACCOUNTS_MAX ];
450 : ulong wfs_peers_cnt;
451 : int wfs_peers_valid;
452 : int wfs_stakes_sent;
453 : wfs_fresh_dlist_t wfs_fresh_dlist[ 1 ];
454 : };
455 :
456 : typedef struct fd_gui_peers_ctx fd_gui_peers_ctx_t;
457 :
458 : /* FIXME: see src/discof/restore/utils/fd_ssmsg.h */
459 : FD_STATIC_ASSERT( sizeof(((fd_gui_peers_ctx_t *)NULL)->wfs_peers)/sizeof(((fd_gui_peers_ctx_t *)NULL)->wfs_peers[0])==
460 : sizeof(((struct fd_snapshot_manifest *)NULL)->vote_accounts)/sizeof(((struct fd_snapshot_manifest *)NULL)->vote_accounts[0]),
461 : wfs_peers_vote_accounts );
462 :
463 : FD_PROTOTYPES_BEGIN
464 :
465 : FD_FN_CONST ulong
466 : fd_gui_peers_align( void );
467 :
468 : FD_FN_CONST ulong
469 : fd_gui_peers_footprint( ulong max_ws_conn_cnt );
470 :
471 : void *
472 : fd_gui_peers_new( void * shmem,
473 : fd_http_server_t * http,
474 : fd_topo_t const * topo,
475 : ulong max_ws_conn_cnt,
476 : char const * wfs_expected_bank_hash_cstr,
477 : long now );
478 :
479 : fd_gui_peers_ctx_t *
480 : fd_gui_peers_join( void * shmem );
481 :
482 : /* fd_gui_peers_handle_gossip_message_rx parses gossip messages from the
483 : net_gossvf link for ingress messages and the gossip_net link for
484 : egress messages and tracks per-peer, per-message bytes. payload and
485 : payload_sz corresponds to the frag data after the network headers
486 : have been stripped. is_rx is true if the frag is an incoming message
487 : from the net_gossvf link. Otherwise, the frag is assumed to be an
488 : outgoing message from the gossip_net link. peer_sock is the ipv4
489 : address and port from the stripped net headers, which identifies the
490 : peers that sent or will receive the message.
491 :
492 : Note that gossip_net frags are unverified gossip messages from the
493 : network. Messages that cannot be parsed are ignored. */
494 : void
495 : fd_gui_peers_handle_gossip_message( fd_gui_peers_ctx_t * peers,
496 : uchar const * payload,
497 : ulong payload_sz,
498 : fd_gossip_socket_t const * peer_sock,
499 : int is_rx );
500 :
501 : /* fd_gui_peers_handle_gossip_message_tx parses frags on the gossip_out
502 : link and uses the contact info update to build up the peer table. */
503 :
504 : void
505 : fd_gui_peers_handle_gossip_update( fd_gui_peers_ctx_t * peers,
506 : fd_gossip_update_message_t const * update,
507 : long now );
508 :
509 : void
510 : fd_gui_peers_handle_vote( fd_gui_peers_ctx_t * peers,
511 : fd_pubkey_t const * vote_account,
512 : ulong vote_slot,
513 : int is_us );
514 :
515 : /* fd_gui_peers_update_delinquency is called infrequently (currently,
516 : once per slot) and scans the cluster for any nodes that are
517 : delinquent, publishing delinquency updates to the frontend. */
518 : void
519 : fd_gui_peers_update_delinquency( fd_gui_peers_ctx_t * peers,
520 : long now );
521 :
522 : /* fd_gui_peers_handle_epoch_info is called at the epoch boundary and
523 : publishes updates for peer stake information. */
524 : void
525 : fd_gui_peers_handle_epoch_info( fd_gui_peers_ctx_t * peers,
526 : fd_epoch_info_msg_t const * epoch_info,
527 : long now );
528 :
529 : void
530 : fd_gui_peers_handle_config_account( fd_gui_peers_ctx_t * peers,
531 : uchar const * data,
532 : ulong sz );
533 :
534 : void
535 : fd_gui_peers_stage_snapshot_manifest( fd_gui_peers_ctx_t * peers,
536 : fd_snapshot_manifest_t const * manifest,
537 : long now );
538 :
539 : void
540 : fd_gui_peers_commit_snapshot_manifest( fd_gui_peers_ctx_t * peers );
541 :
542 : /* fd_gui_peers_ws_message handles incoming websocket request payloads
543 : requesting peer-related responses. ws_conn_id is the connection id
544 : of the requester. data is a pointer to the start of the
545 : json-formatted request payload. data_len is the length of the
546 : request payload. */
547 : int
548 : fd_gui_peers_ws_message( fd_gui_peers_ctx_t * peers,
549 : ulong ws_conn_id,
550 : uchar const * data,
551 : ulong data_len );
552 :
553 : /* fd_gui_peers_ws_open is a callback which should be triggered when a
554 : new client opens a WebSocket connection. ws_conn_id is the
555 : connection id of the new client. now is a UNIX nanosecond timestamp
556 : for the current time. */
557 : void
558 : fd_gui_peers_ws_open( fd_gui_peers_ctx_t * peers,
559 : ulong ws_conn_id,
560 : long now );
561 :
562 : /* fd_gui_peers_ws_close is a callback which should be triggered when an
563 : existing client closes their WebSocket connection. ws_conn_id is the
564 : connection id of the client.*/
565 : void
566 : fd_gui_peers_ws_close( fd_gui_peers_ctx_t * peers,
567 : ulong ws_conn_id );
568 :
569 : /* fd_gui_peers_poll should be called in a the tile's main spin loop to
570 : periodically update peers internal state as well as publish new
571 : Websocket messages to clients. now is a UNIX nanosecond timestamp for
572 : the current time. */
573 : int
574 : fd_gui_peers_poll( fd_gui_peers_ctx_t * peers,
575 : long now );
576 :
577 : FD_PROTOTYPES_END
578 :
579 : #endif /* HEADER_fd_src_disco_gui_fd_gui_peers_h */
|