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