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 "../../util/net/fd_net_headers.h"
20 : #include "../../disco/metrics/generated/fd_metrics_enums.h"
21 : #include "../../flamenco/gossip/fd_gossip_types.h"
22 : #include "../../flamenco/runtime/fd_runtime_const.h"
23 :
24 : #include "../../waltz/http/fd_http_server.h"
25 : #include "../topo/fd_topo.h"
26 :
27 : #include <math.h>
28 :
29 : /* Node in an IP geolocation binary trie. The trie is built from a
30 : compressed binary file with the following format:
31 :
32 : num_country_codes: ulong count of country codes (little-endian)
33 : country_codes: Array of 2-byte ASCII country codes
34 : nodes: Series of 6-byte records containing:
35 : uint network address (big-endian)
36 : uchar prefix length (0-32), which defines the applicable network
37 : mask
38 : uchar country code index, into country_codes
39 :
40 : In the trie, each node represents one bit position in an IP address.
41 : Paths follow 0 (left child) and 1 (right child) bits from the IP's MSB
42 : to LSB. Nodes with has_prefix=1 indicate a match at that prefix
43 : lengt country_code_idx references the 2-letter country code in the
44 : table. Maximum depth is 32 levels (for IPv4). */
45 :
46 : struct fd_gui_ipinfo_node {
47 : uchar has_prefix;
48 : uchar country_code_idx;
49 :
50 : struct fd_gui_ipinfo_node * left;
51 : struct fd_gui_ipinfo_node * right;
52 : };
53 :
54 : typedef struct fd_gui_ipinfo_node fd_gui_ipinfo_node_t;
55 :
56 : struct fd_gui_country_code {
57 : char cc[ 3 ];
58 : };
59 :
60 : typedef struct fd_gui_country_code fd_gui_country_code_t;
61 :
62 : #define FD_GUI_PEERS_NODE_NOP (0)
63 0 : #define FD_GUI_PEERS_NODE_ADD (1)
64 0 : #define FD_GUI_PEERS_NODE_UPDATE (2)
65 0 : #define FD_GUI_PEERS_NODE_DELETE (3)
66 :
67 0 : #define FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT (256UL) /* maximum number of maintained active sort keys */
68 : #define FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ (200UL) /* the maximum number of rows a client can request for a table viewport */
69 0 : #define FD_GUI_PEERS_WS_VIEWPORT_UPDATE_INTERVAL_MILLIS ( 150L)
70 0 : #define FD_GUI_PEERS_METRIC_RATE_UPDATE_INTERVAL_MILLIS ( 150L)
71 0 : #define FD_GUI_PEERS_GOSSIP_STATS_UPDATE_INTERVAL_MILLIS ( 300L)
72 :
73 0 : #define FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT (64UL)
74 :
75 : /* Some table columns are rates of change, which require keeping a
76 : historical value / timestamp. */
77 : struct fd_gui_peers_metric_rate {
78 : ulong cur;
79 : ulong ref;
80 : long rate_ema; /* units per sec. live_table treaps use this field to sort table entries */
81 : long update_timestamp_ns; /* time when cur was last copied over to ref */
82 : };
83 : typedef struct fd_gui_peers_metric_rate fd_gui_peers_metric_rate_t;
84 :
85 0 : #define FD_GUI_PEERS_EMA_HALF_LIFE_NS (3000000000UL)
86 :
87 : static inline long
88 : fd_gui_peers_adaptive_ema( long last_update_time,
89 : long current_time,
90 : long current_value,
91 0 : long value_at_last_update ) {
92 0 : if( FD_UNLIKELY( last_update_time==0) ) return current_value;
93 :
94 0 : long elapsed_time = current_time - last_update_time;
95 0 : if( FD_UNLIKELY( elapsed_time<=0 ) ) return value_at_last_update;
96 :
97 : // Calculate alpha using half-life formula
98 : // alpha = 1 - exp(-ln(2) * elapsed_time / half_life)
99 0 : double decay_factor = 0.69314718055994 * ((double)elapsed_time / (double)FD_GUI_PEERS_EMA_HALF_LIFE_NS);
100 0 : double alpha = 1.0 - exp(-decay_factor);
101 :
102 0 : if( FD_UNLIKELY( alpha>1.0 ) ) alpha = 1.0;
103 0 : if( FD_UNLIKELY( alpha<0.0 ) ) alpha = 0.0;
104 :
105 0 : return (long)(alpha * (double)current_value + (1.0 - alpha) * (double)value_at_last_update);
106 0 : }
107 :
108 : struct fd_gui_peers_vote {
109 : fd_pubkey_t node_account;
110 : fd_pubkey_t vote_account;
111 : ulong stake;
112 : ulong last_vote_slot;
113 : long last_vote_timestamp;
114 : uchar commission;
115 : ulong epoch;
116 : ulong epoch_credits;
117 : };
118 :
119 : typedef struct fd_gui_peers_vote fd_gui_peers_vote_t;
120 :
121 : struct fd_gui_peers_node {
122 : int valid;
123 : long update_time_nanos;
124 : fd_contact_info_t contact_info;
125 : char name[ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
126 :
127 : fd_gui_peers_metric_rate_t gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
128 : fd_gui_peers_metric_rate_t gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
129 : fd_gui_peers_metric_rate_t gossvf_rx_sum; /* sum of gossvf_rx */
130 : fd_gui_peers_metric_rate_t gossip_tx_sum; /* sum of gossip_tx */
131 :
132 : int has_vote_info;
133 : fd_pubkey_t vote_account;
134 : ulong stake; /* if has_vote_info==0 then stake==ULONG_MAX */
135 : ulong last_vote_slot;
136 : long last_vote_timestamp;
137 : uchar commission;
138 : ulong epoch;
139 : ulong epoch_credits;
140 : uchar country_code_idx;
141 : int delinquent;
142 :
143 : struct {
144 : ulong next;
145 : ulong prev;
146 : } pubkey_map;
147 :
148 : struct {
149 : ulong next;
150 : ulong prev;
151 : } sock_map;
152 :
153 : struct {
154 : ulong parent;
155 : ulong left;
156 : ulong right;
157 : ulong prio;
158 : ulong next;
159 : ulong prev;
160 : } treaps_live_table[ FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT ];
161 : struct {
162 : ulong next;
163 : ulong prev;
164 : } dlist_live_table;
165 : ulong sort_keys_live_table;
166 :
167 : struct {
168 : ulong parent;
169 : ulong left;
170 : ulong right;
171 : ulong prio;
172 : ulong next;
173 : ulong prev;
174 : } treaps_bandwidth_tracking[ 2UL ];
175 : struct {
176 : ulong next;
177 : ulong prev;
178 : } dlist_bandwidth_tracking;
179 : ulong sort_keys_bandwidth_tracking;
180 : };
181 : typedef struct fd_gui_peers_node fd_gui_peers_node_t;
182 :
183 : struct fd_gui_peers_gossip_stats {
184 : long sample_time;
185 : ulong network_health_pull_response_msg_rx_success;
186 : ulong network_health_pull_response_msg_rx_failure;
187 : ulong network_health_push_msg_rx_success;
188 : ulong network_health_push_msg_rx_failure;
189 : ulong network_health_push_crds_rx_duplicate;
190 : ulong network_health_pull_response_crds_rx_duplicate;
191 : ulong network_health_push_crds_rx_success;
192 : ulong network_health_push_crds_rx_failure;
193 : ulong network_health_pull_response_crds_rx_success;
194 : ulong network_health_pull_response_crds_rx_failure;
195 : ulong network_health_push_msg_tx;
196 : ulong network_health_pull_response_msg_tx;
197 : ulong network_health_total_stake; /* lamports */
198 : ulong network_health_total_peers;
199 : ulong network_health_connected_stake; /* lamports */
200 : ulong network_health_connected_staked_peers;
201 : ulong network_health_connected_unstaked_peers;
202 : ulong network_ingress_total_bytes;
203 : ulong network_ingress_peer_sz;
204 : long network_ingress_peer_bytes_per_sec [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
205 : char network_ingress_peer_names [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ][ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
206 : fd_pubkey_t network_ingress_peer_identities[ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
207 : long network_ingress_total_bytes_per_sec;
208 : ulong network_egress_total_bytes;
209 : ulong network_egress_peer_sz;
210 : long network_egress_peer_bytes_per_sec [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
211 : char network_egress_peer_names [ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ][ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ];
212 : fd_pubkey_t network_egress_peer_identities[ FD_GUI_PEERS_GOSSIP_TOP_PEERS_CNT ];
213 : long network_egress_total_bytes_per_sec;
214 : ulong storage_capacity;
215 : ulong storage_expired_cnt;
216 : ulong storage_evicted_cnt;
217 : ulong storage_active_cnt[ FD_METRICS_ENUM_CRDS_VALUE_CNT ];
218 : ulong storage_cnt_tx [ FD_METRICS_ENUM_CRDS_VALUE_CNT ];
219 : ulong storage_bytes_tx [ FD_METRICS_ENUM_CRDS_VALUE_CNT ];
220 : ulong messages_push_rx_cnt;
221 : ulong messages_push_tx_cnt;
222 : ulong messages_pull_response_rx_cnt;
223 : ulong messages_pull_response_tx_cnt;
224 : ulong messages_bytes_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
225 : ulong messages_count_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
226 : ulong messages_bytes_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
227 : ulong messages_count_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_CNT ];
228 : };
229 : typedef struct fd_gui_peers_gossip_stats fd_gui_peers_gossip_stats_t;
230 :
231 : #define POOL_NAME fd_gui_peers_node_info_pool
232 0 : #define POOL_T fd_gui_config_parse_info_t
233 0 : #define POOL_NEXT pool.next
234 : #include "../../util/tmpl/fd_pool.c"
235 :
236 : #define MAP_NAME fd_gui_peers_node_info_map
237 : #define MAP_ELE_T fd_gui_config_parse_info_t
238 : #define MAP_KEY_T fd_pubkey_t
239 0 : #define MAP_KEY pubkey
240 0 : #define MAP_IDX_T ulong
241 0 : #define MAP_NEXT map.next
242 0 : #define MAP_PREV map.prev
243 0 : #define MAP_KEY_HASH(k,s) (fd_hash( (s), (k)->uc, sizeof(fd_pubkey_t) ))
244 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->uc, (k1)->uc, 32UL))
245 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
246 : #include "../../util/tmpl/fd_map_chain.c"
247 :
248 : #define MAP_NAME fd_gui_peers_node_pubkey_map
249 0 : #define MAP_ELE_T fd_gui_peers_node_t
250 : #define MAP_KEY_T fd_pubkey_t
251 0 : #define MAP_KEY contact_info.pubkey
252 0 : #define MAP_IDX_T ulong
253 0 : #define MAP_NEXT pubkey_map.next
254 0 : #define MAP_PREV pubkey_map.prev
255 0 : #define MAP_KEY_HASH(k,s) (fd_hash( (s), (k)->uc, sizeof(fd_pubkey_t) ))
256 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->uc, (k1)->uc, 32UL))
257 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
258 : #include "../../util/tmpl/fd_map_chain.c"
259 :
260 : #define MAP_NAME fd_gui_peers_node_sock_map
261 0 : #define MAP_ELE_T fd_gui_peers_node_t
262 : #define MAP_KEY_T fd_ip4_port_t
263 0 : #define MAP_KEY contact_info.sockets[ FD_CONTACT_INFO_SOCKET_GOSSIP ]
264 0 : #define MAP_IDX_T ulong
265 0 : #define MAP_NEXT sock_map.next
266 0 : #define MAP_PREV sock_map.prev
267 0 : #define MAP_KEY_HASH(k,s) ( fd_hash( (s), (k), sizeof(uint) + sizeof(ushort) ) )
268 0 : #define MAP_KEY_EQ(k0,k1) ((k0)->l==(k1)->l )
269 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
270 : #define MAP_MULTI 1
271 : #include "../../util/tmpl/fd_map_chain.c"
272 :
273 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; }
274 0 : static int live_table_col_long_lt ( void const * a, void const * b ) { return *(long *)a < *(long *)b; }
275 0 : static int live_table_col_uchar_lt ( void const * a, void const * b ) { return *(uchar *)a < *(uchar *)b; }
276 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); }
277 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; }
278 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 ); }
279 :
280 : #define LIVE_TABLE_NAME fd_gui_peers_live_table
281 0 : #define LIVE_TABLE_TREAP treaps_live_table
282 0 : #define LIVE_TABLE_SORT_KEYS sort_keys_live_table
283 0 : #define LIVE_TABLE_DLIST dlist_live_table
284 0 : #define LIVE_TABLE_COLUMN_CNT (9UL)
285 0 : #define LIVE_TABLE_MAX_SORT_KEY_CNT FD_GUI_PEERS_CI_TABLE_SORT_KEY_CNT
286 : #define LIVE_TABLE_ROW_T fd_gui_peers_node_t
287 0 : #define LIVE_TABLE_COLUMNS LIVE_TABLE_COL_ARRAY( \
288 0 : LIVE_TABLE_COL_ENTRY( "Stake", stake, live_table_col_stake_lt ), \
289 0 : LIVE_TABLE_COL_ENTRY( "Pubkey", contact_info.pubkey, live_table_col_pubkey_lt ), \
290 0 : LIVE_TABLE_COL_ENTRY( "Name", name, live_table_col_name_lt ), \
291 0 : LIVE_TABLE_COL_ENTRY( "Country", country_code_idx, live_table_col_uchar_lt ), \
292 0 : LIVE_TABLE_COL_ENTRY( "IP Addr", contact_info.sockets[ FD_CONTACT_INFO_SOCKET_GOSSIP ].addr, live_table_col_ipv4_lt ), \
293 0 : LIVE_TABLE_COL_ENTRY( "Ingress Push", gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ].rate_ema, live_table_col_long_lt ), \
294 0 : LIVE_TABLE_COL_ENTRY( "Ingress Pull", gossvf_rx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ].rate_ema, live_table_col_long_lt ), \
295 0 : LIVE_TABLE_COL_ENTRY( "Egress Push", gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PUSH_IDX ].rate_ema, live_table_col_long_lt ), \
296 0 : LIVE_TABLE_COL_ENTRY( "Egress Pull", gossip_tx[ FD_METRICS_ENUM_GOSSIP_MESSAGE_V_PULL_RESPONSE_IDX ].rate_ema, live_table_col_long_lt ), )
297 : #include "fd_gui_live_table_tmpl.c"
298 :
299 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 } })
300 :
301 : #define LIVE_TABLE_NAME fd_gui_peers_bandwidth_tracking
302 0 : #define LIVE_TABLE_TREAP treaps_bandwidth_tracking
303 0 : #define LIVE_TABLE_SORT_KEYS sort_keys_bandwidth_tracking
304 0 : #define LIVE_TABLE_DLIST dlist_bandwidth_tracking
305 0 : #define LIVE_TABLE_COLUMN_CNT (2UL)
306 0 : #define LIVE_TABLE_MAX_SORT_KEY_CNT (2UL)
307 : #define LIVE_TABLE_ROW_T fd_gui_peers_node_t
308 0 : #define LIVE_TABLE_COLUMNS LIVE_TABLE_COL_ARRAY( \
309 0 : LIVE_TABLE_COL_ENTRY( "Ingress Total", gossvf_rx_sum.rate_ema, live_table_col_long_lt ), \
310 0 : LIVE_TABLE_COL_ENTRY( "Egress Total", gossip_tx_sum.rate_ema, live_table_col_long_lt ) )
311 : #include "fd_gui_live_table_tmpl.c"
312 :
313 0 : #define FD_GUI_PEERS_BW_TRACKING_INGRESS_SORT_KEY ((fd_gui_peers_bandwidth_tracking_sort_key_t){ .col = { 0, 1 }, .dir = { -1, 0 } })
314 0 : #define FD_GUI_PEERS_BW_TRACKING_EGRESS_SORT_KEY ((fd_gui_peers_bandwidth_tracking_sort_key_t){ .col = { 0, 1 }, .dir = { 0, -1 } })
315 :
316 : struct fd_gui_peers_ws_conn {
317 : int connected;
318 : long connected_time;
319 :
320 : ulong start_row;
321 : ulong row_cnt;
322 : fd_gui_peers_node_t viewport[ FD_GUI_PEERS_WS_VIEWPORT_MAX_SZ ];
323 : fd_gui_peers_live_table_sort_key_t sort_key;
324 : };
325 :
326 : typedef struct fd_gui_peers_ws_conn fd_gui_peers_ws_conn_t;
327 :
328 : struct fd_gui_peers_ctx {
329 : long next_client_nanos; /* ns timestamp when we'll service the next ws client */
330 : long next_metric_rate_update_nanos; /* ns timestamp when we'll next update rate-of-change metrics */
331 : long next_gossip_stats_update_nanos; /* ns timestamp when we'll next broadcast out gossip stats message */
332 :
333 : fd_gui_config_parse_info_t * node_info_pool;
334 : fd_gui_peers_node_info_map_t * node_info_map;
335 : fd_gui_peers_node_pubkey_map_t * node_pubkey_map;
336 : fd_gui_peers_node_sock_map_t * node_sock_map;
337 : fd_gui_peers_live_table_t * live_table;
338 : fd_gui_peers_bandwidth_tracking_t * bw_tracking;
339 :
340 : fd_http_server_t * http;
341 : fd_topo_t * topo;
342 :
343 : ulong max_ws_conn_cnt;
344 : ulong open_ws_conn_cnt;
345 : ulong active_ws_conn_id;
346 : 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 */
347 :
348 : fd_gui_peers_gossip_stats_t gossip_stats [ 1 ];
349 : fd_gui_peers_node_t contact_info_table[ FD_CONTACT_INFO_TABLE_SIZE ];
350 :
351 : ulong slot_voted; /* last vote slot for this validator */
352 :
353 : fd_gui_peers_vote_t votes [ FD_RUNTIME_MAX_VOTE_ACCOUNTS ];
354 : fd_gui_peers_vote_t votes_scratch[ FD_RUNTIME_MAX_VOTE_ACCOUNTS ]; /* for fast stable sort */
355 :
356 : struct {
357 : fd_gui_ipinfo_node_t * nodes;
358 : fd_gui_country_code_t country_code[ 512 ]; /* ISO 3166-1 alpha-2 country codes */
359 : } ipinfo;
360 : };
361 :
362 : typedef struct fd_gui_peers_ctx fd_gui_peers_ctx_t;
363 :
364 : FD_PROTOTYPES_BEGIN
365 :
366 : FD_FN_CONST ulong
367 : fd_gui_peers_align( void );
368 :
369 : FD_FN_CONST ulong
370 : fd_gui_peers_footprint( ulong max_ws_conn_cnt );
371 :
372 : void *
373 : fd_gui_peers_new( void * shmem,
374 : fd_http_server_t * http,
375 : fd_topo_t * topo,
376 : ulong max_ws_conn_cnt,
377 : long now );
378 :
379 : fd_gui_peers_ctx_t *
380 : fd_gui_peers_join( void * shmem );
381 :
382 : /* fd_gui_peers_handle_gossip_message_rx parses gossip messages from the
383 : net_gossvf link for ingress messages and the gossip_net link for
384 : egress messages and tracks per-peer, per-message bytes. payload and
385 : payload_sz corresponds to the frag data after the network headers
386 : have been stripped. is_rx is true if the frag is an incoming message
387 : from the net_gossvf link. Otherwise, the frag is assumed to be an
388 : outgoing message from the gossip_net link. peer_sock is the ipv4
389 : address and port from the stripped net headers, which identifies the
390 : peers that sent or will receive the message.
391 :
392 : Note that gossip_net frags are unverified gossip messages from the
393 : network. Messages that cannot be parsed are ignored. */
394 : void
395 : fd_gui_peers_handle_gossip_message( fd_gui_peers_ctx_t * peers,
396 : uchar const * payload,
397 : ulong payload_sz,
398 : fd_ip4_port_t const * peer_sock,
399 : int is_rx );
400 :
401 : /* fd_gui_peers_handle_gossip_message_tx parses frags on the gossip_out
402 : link and uses the contact info update to build up the peer table. */
403 :
404 : void
405 : fd_gui_peers_handle_gossip_update( fd_gui_peers_ctx_t * peers,
406 : fd_gossip_update_message_t const * update,
407 : long now );
408 :
409 : void
410 : fd_gui_peers_handle_vote_update( fd_gui_peers_ctx_t * peers,
411 : fd_gui_peers_vote_t * votes,
412 : ulong vote_cnt,
413 : long now,
414 : fd_pubkey_t * identity );
415 :
416 : void
417 : fd_gui_peers_handle_config_account( fd_gui_peers_ctx_t * peers,
418 : uchar const * data,
419 : ulong sz );
420 :
421 : /* fd_gui_peers_ws_message handles incoming websocket request payloads
422 : requesting peer-related responses. ws_conn_id is the connection id
423 : of the requester. data is a pointer to the start of the
424 : json-formatted request payload. data_len is the length of the
425 : request payload. */
426 : int
427 : fd_gui_peers_ws_message( fd_gui_peers_ctx_t * peers,
428 : ulong ws_conn_id,
429 : uchar const * data,
430 : ulong data_len );
431 :
432 : /* fd_gui_peers_ws_open is a callback which should be triggered when a
433 : new client opens a WebSocket connection. ws_conn_id is the
434 : connection id of the new client. now is a UNIX nanosecond timestamp
435 : for the current time. */
436 : void
437 : fd_gui_peers_ws_open( fd_gui_peers_ctx_t * peers,
438 : ulong ws_conn_id,
439 : long now );
440 :
441 : /* fd_gui_peers_ws_close is a callback which should be triggered when an
442 : existing client closes their WebSocket connection. ws_conn_id is the
443 : connection id of the client.*/
444 : void
445 : fd_gui_peers_ws_close( fd_gui_peers_ctx_t * peers,
446 : ulong ws_conn_id );
447 :
448 : /* fd_gui_peers_poll should be called in a the tile's main spin loop to
449 : periodically update peers internal state as well as publish new
450 : Websocket messages to clients. now is a UNIX nanosecond timestamp for
451 : the current time. */
452 : int
453 : fd_gui_peers_poll( fd_gui_peers_ctx_t * peers,
454 : long now );
455 :
456 : FD_PROTOTYPES_END
457 :
458 : #endif /* HEADER_fd_src_disco_gui_fd_gui_peers_h */
|