Line data Source code
1 : #include "fd_gui.h"
2 : #include "fd_gui_printf.h"
3 :
4 : #include "../fd_disco.h"
5 : #include "../plugin/fd_plugin.h"
6 :
7 : #include "../../ballet/base58/fd_base58.h"
8 : #include "../../ballet/json/cJSON.h"
9 :
10 : #include "../../app/fdctl/config.h"
11 :
12 : FD_FN_CONST ulong
13 0 : fd_gui_align( void ) {
14 0 : return 128UL;
15 0 : }
16 :
17 : FD_FN_CONST ulong
18 0 : fd_gui_footprint( void ) {
19 0 : return sizeof(fd_gui_t);
20 0 : }
21 :
22 : void *
23 : fd_gui_new( void * shmem,
24 : fd_http_server_t * http,
25 : char const * version,
26 : char const * cluster,
27 : uchar const * identity_key,
28 : int is_voting,
29 0 : fd_topo_t * topo ) {
30 :
31 0 : if( FD_UNLIKELY( !shmem ) ) {
32 0 : FD_LOG_WARNING(( "NULL shmem" ));
33 0 : return NULL;
34 0 : }
35 :
36 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_gui_align() ) ) ) {
37 0 : FD_LOG_WARNING(( "misaligned shmem" ));
38 0 : return NULL;
39 0 : }
40 :
41 0 : if( FD_UNLIKELY( topo->tile_cnt>FD_GUI_TILE_TIMER_TILE_CNT ) ) {
42 0 : FD_LOG_WARNING(( "too many tiles" ));
43 0 : return NULL;
44 0 : }
45 :
46 0 : fd_gui_t * gui = (fd_gui_t *)shmem;
47 :
48 0 : gui->http = http;
49 0 : gui->topo = topo;
50 :
51 0 : gui->debug_in_leader_slot = ULONG_MAX;
52 :
53 :
54 0 : gui->next_sample_400millis = fd_log_wallclock();
55 0 : gui->next_sample_100millis = gui->next_sample_400millis;
56 0 : gui->next_sample_10millis = gui->next_sample_400millis;
57 :
58 0 : memcpy( gui->summary.identity_key->uc, identity_key, 32UL );
59 0 : fd_base58_encode_32( identity_key, NULL, gui->summary.identity_key_base58 );
60 0 : gui->summary.identity_key_base58[ FD_BASE58_ENCODED_32_SZ-1UL ] = '\0';
61 :
62 0 : gui->summary.version = version;
63 0 : gui->summary.cluster = cluster;
64 0 : gui->summary.startup_time_nanos = gui->next_sample_400millis;
65 :
66 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_INITIALIZING;
67 0 : gui->summary.startup_got_full_snapshot = 0;
68 0 : gui->summary.startup_full_snapshot_slot = 0;
69 0 : gui->summary.startup_incremental_snapshot_slot = 0;
70 0 : gui->summary.startup_waiting_for_supermajority_slot = ULONG_MAX;
71 :
72 0 : gui->summary.balance = 0UL;
73 0 : gui->summary.estimated_slot_duration_nanos = 0UL;
74 :
75 0 : gui->summary.vote_distance = 0UL;
76 0 : gui->summary.vote_state = is_voting ? FD_GUI_VOTE_STATE_VOTING : FD_GUI_VOTE_STATE_NON_VOTING;
77 :
78 0 : gui->summary.net_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "net" );
79 0 : gui->summary.quic_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "quic" );
80 0 : gui->summary.verify_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "verify" );
81 0 : gui->summary.resolv_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "resolv" );
82 0 : gui->summary.bank_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "bank" );
83 0 : gui->summary.shred_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "shred" );
84 :
85 0 : gui->summary.slot_rooted = 0UL;
86 0 : gui->summary.slot_optimistically_confirmed = 0UL;
87 0 : gui->summary.slot_completed = 0UL;
88 0 : gui->summary.slot_estimated = 0UL;
89 :
90 0 : gui->summary.estimated_tps_history_idx = 0UL;
91 0 : memset( gui->summary.estimated_tps_history, 0, sizeof(gui->summary.estimated_tps_history) );
92 :
93 0 : memset( gui->summary.txn_waterfall_reference, 0, sizeof(gui->summary.txn_waterfall_reference) );
94 0 : memset( gui->summary.txn_waterfall_current, 0, sizeof(gui->summary.txn_waterfall_current) );
95 :
96 0 : memset( gui->summary.tile_prime_metric_ref, 0, sizeof(gui->summary.tile_prime_metric_ref) );
97 0 : memset( gui->summary.tile_prime_metric_cur, 0, sizeof(gui->summary.tile_prime_metric_cur) );
98 0 : gui->summary.tile_prime_metric_ref[ 0 ].ts_nanos = fd_log_wallclock();
99 :
100 0 : memset( gui->summary.tile_timers_snap[ 0 ], 0, sizeof(gui->summary.tile_timers_snap[ 0 ]) );
101 0 : memset( gui->summary.tile_timers_snap[ 1 ], 0, sizeof(gui->summary.tile_timers_snap[ 1 ]) );
102 0 : gui->summary.tile_timers_snap_idx = 2UL;
103 0 : gui->summary.tile_timers_history_idx = 0UL;
104 0 : for( ulong i=0UL; i<FD_GUI_TILE_TIMER_LEADER_CNT; i++ ) gui->summary.tile_timers_leader_history_slot[ i ] = ULONG_MAX;
105 :
106 0 : gui->epoch.has_epoch[ 0 ] = 0;
107 0 : gui->epoch.has_epoch[ 1 ] = 0;
108 :
109 0 : gui->gossip.peer_cnt = 0UL;
110 0 : gui->vote_account.vote_account_cnt = 0UL;
111 0 : gui->validator_info.info_cnt = 0UL;
112 :
113 0 : for( ulong i=0UL; i<FD_GUI_SLOTS_CNT; i++ ) gui->slots[ i ]->slot = ULONG_MAX;
114 :
115 0 : return gui;
116 0 : }
117 :
118 : fd_gui_t *
119 0 : fd_gui_join( void * shmem ) {
120 0 : return (fd_gui_t *)shmem;
121 0 : }
122 :
123 : void
124 : fd_gui_ws_open( fd_gui_t * gui,
125 0 : ulong ws_conn_id ) {
126 0 : void (* printers[] )( fd_gui_t * gui ) = {
127 0 : fd_gui_printf_startup_progress,
128 0 : fd_gui_printf_version,
129 0 : fd_gui_printf_cluster,
130 0 : fd_gui_printf_identity_key,
131 0 : fd_gui_printf_uptime_nanos,
132 0 : fd_gui_printf_vote_state,
133 0 : fd_gui_printf_vote_distance,
134 0 : fd_gui_printf_skipped_history,
135 0 : fd_gui_printf_tps_history,
136 0 : fd_gui_printf_tiles,
137 0 : fd_gui_printf_balance,
138 0 : fd_gui_printf_estimated_slot_duration_nanos,
139 0 : fd_gui_printf_root_slot,
140 0 : fd_gui_printf_optimistically_confirmed_slot,
141 0 : fd_gui_printf_completed_slot,
142 0 : fd_gui_printf_estimated_slot,
143 0 : fd_gui_printf_live_tile_timers,
144 0 : };
145 :
146 0 : ulong printers_len = sizeof(printers) / sizeof(printers[0]);
147 0 : for( ulong i=0UL; i<printers_len; i++ ) {
148 0 : printers[ i ]( gui );
149 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
150 0 : }
151 :
152 0 : for( ulong i=0UL; i<2UL; i++ ) {
153 0 : if( FD_LIKELY( gui->epoch.has_epoch[ i ] ) ) {
154 0 : fd_gui_printf_skip_rate( gui, i );
155 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
156 0 : fd_gui_printf_epoch( gui, i );
157 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
158 0 : }
159 0 : }
160 :
161 : /* Print peers last because it's the largest message and would
162 : block other information. */
163 0 : fd_gui_printf_peers_all( gui );
164 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
165 0 : }
166 :
167 : static void
168 0 : fd_gui_tile_timers_snap( fd_gui_t * gui ) {
169 0 : fd_gui_tile_timers_t * cur = gui->summary.tile_timers_snap[ gui->summary.tile_timers_snap_idx ];
170 0 : gui->summary.tile_timers_snap_idx = (gui->summary.tile_timers_snap_idx+1UL)%FD_GUI_TILE_TIMER_SNAP_CNT;
171 0 : for( ulong i=0UL; i<gui->topo->tile_cnt; i++ ) {
172 0 : fd_topo_tile_t * tile = &gui->topo->tiles[ i ];
173 0 : if ( FD_UNLIKELY( !tile->metrics ) ) {
174 : /* bench tiles might not have been booted initially.
175 : This check shouldn't be necessary if all tiles barrier after boot. */
176 : // TODO(FIXME) this probably isn't the right fix but it makes fddev bench work for now
177 0 : return;
178 0 : }
179 0 : volatile ulong const * tile_metrics = fd_metrics_tile( tile->metrics );
180 :
181 0 : cur[ i ].caughtup_housekeeping_ticks = tile_metrics[ MIDX( COUNTER, STEM, REGIME_DURATION_NANOS_CAUGHT_UP_HOUSEKEEPING ) ];
182 0 : cur[ i ].processing_housekeeping_ticks = tile_metrics[ MIDX( COUNTER, STEM, REGIME_DURATION_NANOS_PROCESSING_HOUSEKEEPING ) ];
183 0 : cur[ i ].backpressure_housekeeping_ticks = tile_metrics[ MIDX( COUNTER, STEM, REGIME_DURATION_NANOS_BACKPRESSURE_HOUSEKEEPING ) ];
184 0 : cur[ i ].caughtup_prefrag_ticks = tile_metrics[ MIDX( COUNTER, STEM, REGIME_DURATION_NANOS_CAUGHT_UP_PREFRAG ) ];
185 0 : cur[ i ].processing_prefrag_ticks = tile_metrics[ MIDX( COUNTER, STEM, REGIME_DURATION_NANOS_PROCESSING_PREFRAG ) ];
186 0 : cur[ i ].backpressure_prefrag_ticks = tile_metrics[ MIDX( COUNTER, STEM, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
187 0 : cur[ i ].caughtup_postfrag_ticks = tile_metrics[ MIDX( COUNTER, STEM, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ];
188 0 : cur[ i ].processing_postfrag_ticks = tile_metrics[ MIDX( COUNTER, STEM, REGIME_DURATION_NANOS_PROCESSING_POSTFRAG ) ];
189 0 : }
190 0 : }
191 :
192 : static void
193 0 : fd_gui_estimated_tps_snap( fd_gui_t * gui ) {
194 0 : ulong total_txn_cnt = 0UL;
195 0 : ulong vote_txn_cnt = 0UL;
196 0 : ulong nonvote_failed_txn_cnt = 0UL;
197 :
198 0 : for( ulong i=0UL; i<fd_ulong_min( gui->summary.slot_completed+1UL, FD_GUI_SLOTS_CNT ); i++ ) {
199 0 : ulong _slot = gui->summary.slot_completed-i;
200 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
201 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX || slot->slot!=_slot ) ) break; /* Slot no longer exists, no TPS. */
202 0 : if( FD_UNLIKELY( slot->completed_time==LONG_MAX ) ) continue; /* Slot is on this fork but was never completed, must have been in root path on boot. */
203 0 : if( FD_UNLIKELY( slot->completed_time+FD_GUI_TPS_HISTORY_WINDOW_DURATION_SECONDS*1000L*1000L*1000L<gui->next_sample_400millis ) ) break; /* Slot too old. */
204 0 : if( FD_UNLIKELY( slot->skipped ) ) continue; /* Skipped slots don't count to TPS. */
205 :
206 0 : total_txn_cnt += slot->total_txn_cnt;
207 0 : vote_txn_cnt += slot->vote_txn_cnt;
208 0 : nonvote_failed_txn_cnt += slot->nonvote_failed_txn_cnt;
209 0 : }
210 :
211 0 : gui->summary.estimated_tps_history[ gui->summary.estimated_tps_history_idx ][ 0 ] = total_txn_cnt;
212 0 : gui->summary.estimated_tps_history[ gui->summary.estimated_tps_history_idx ][ 1 ] = vote_txn_cnt;
213 0 : gui->summary.estimated_tps_history[ gui->summary.estimated_tps_history_idx ][ 2 ] = nonvote_failed_txn_cnt;
214 0 : gui->summary.estimated_tps_history_idx = (gui->summary.estimated_tps_history_idx+1UL) % FD_GUI_TPS_HISTORY_SAMPLE_CNT;
215 0 : }
216 :
217 : /* Snapshot all of the data from metrics to construct a view of the
218 : transaction waterfall.
219 :
220 : Tiles are sampled in reverse pipeline order: this helps prevent data
221 : discrepancies where a later tile has "seen" more transactions than an
222 : earlier tile, which shouldn't typically happen. */
223 :
224 : static void
225 : fd_gui_txn_waterfall_snap( fd_gui_t * gui,
226 0 : fd_gui_txn_waterfall_t * cur ) {
227 0 : fd_topo_t * topo = gui->topo;
228 :
229 0 : cur->out.block_success = 0UL;
230 0 : cur->out.block_fail = 0UL;
231 :
232 0 : cur->out.bank_invalid = 0UL;
233 0 : for( ulong i=0UL; i<gui->summary.bank_tile_cnt; i++ ) {
234 0 : fd_topo_tile_t const * bank = &topo->tiles[ fd_topo_find_tile( topo, "bank", i ) ];
235 :
236 0 : volatile ulong const * bank_metrics = fd_metrics_tile( bank->metrics );
237 :
238 0 : cur->out.block_success += bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_SUCCESS ) ];
239 :
240 0 : cur->out.block_fail +=
241 0 : bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_ACCOUNT_IN_USE ) ]
242 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_ACCOUNT_LOADED_TWICE ) ]
243 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_ACCOUNT_NOT_FOUND ) ]
244 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_PROGRAM_ACCOUNT_NOT_FOUND ) ]
245 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INSUFFICIENT_FUNDS_FOR_FEE ) ]
246 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_ACCOUNT_FOR_FEE ) ]
247 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_ALREADY_PROCESSED ) ]
248 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_BLOCKHASH_NOT_FOUND ) ]
249 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INSTRUCTION_ERROR ) ]
250 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_CALL_CHAIN_TOO_DEEP ) ]
251 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_MISSING_SIGNATURE_FOR_FEE ) ]
252 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_ACCOUNT_INDEX ) ]
253 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_SIGNATURE_FAILURE ) ]
254 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_PROGRAM_FOR_EXECUTION ) ]
255 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_SANITIZE_FAILURE ) ]
256 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_CLUSTER_MAINTENANCE ) ]
257 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_ACCOUNT_BORROW_OUTSTANDING ) ]
258 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_WOULD_EXCEED_MAX_BLOCK_COST_LIMIT ) ]
259 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_UNSUPPORTED_VERSION ) ]
260 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_WRITABLE_ACCOUNT ) ]
261 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_WOULD_EXCEED_MAX_ACCOUNT_COST_LIMIT ) ]
262 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT ) ]
263 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_TOO_MANY_ACCOUNT_LOCKS ) ]
264 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_ADDRESS_LOOKUP_TABLE_NOT_FOUND ) ]
265 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_ADDRESS_LOOKUP_TABLE_OWNER ) ]
266 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_ADDRESS_LOOKUP_TABLE_DATA ) ]
267 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_ADDRESS_LOOKUP_TABLE_INDEX ) ]
268 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_RENT_PAYING_ACCOUNT ) ]
269 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_WOULD_EXCEED_MAX_VOTE_COST_LIMIT ) ]
270 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT ) ]
271 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_DUPLICATE_INSTRUCTION ) ]
272 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INSUFFICIENT_FUNDS_FOR_RENT ) ]
273 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_MAX_LOADED_ACCOUNTS_DATA_SIZE_EXCEEDED ) ]
274 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_INVALID_LOADED_ACCOUNTS_DATA_SIZE_LIMIT ) ]
275 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_RESANITIZATION_NEEDED ) ]
276 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED ) ]
277 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_UNBALANCED_TRANSACTION ) ]
278 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTED_PROGRAM_CACHE_HIT_MAX_LIMIT ) ];
279 :
280 0 : cur->out.bank_invalid +=
281 0 : bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ADDRESS_TABLES_SLOT_HASHES_SYSVAR_NOT_FOUND ) ]
282 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ADDRESS_TABLES_ACCOUNT_NOT_FOUND ) ]
283 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ADDRESS_TABLES_INVALID_ACCOUNT_OWNER ) ]
284 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ADDRESS_TABLES_INVALID_ACCOUNT_DATA ) ]
285 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ADDRESS_TABLES_INVALID_INDEX ) ];
286 :
287 0 : cur->out.bank_invalid +=
288 0 : bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ACCOUNT_IN_USE ) ]
289 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ACCOUNT_LOADED_TWICE ) ]
290 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ACCOUNT_NOT_FOUND ) ]
291 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_PROGRAM_ACCOUNT_NOT_FOUND ) ]
292 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INSUFFICIENT_FUNDS_FOR_FEE ) ]
293 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_ACCOUNT_FOR_FEE ) ]
294 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ALREADY_PROCESSED ) ]
295 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_BLOCKHASH_NOT_FOUND ) ]
296 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INSTRUCTION_ERROR ) ]
297 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_CALL_CHAIN_TOO_DEEP ) ]
298 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_MISSING_SIGNATURE_FOR_FEE ) ]
299 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_ACCOUNT_INDEX ) ]
300 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_SIGNATURE_FAILURE ) ]
301 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_PROGRAM_FOR_EXECUTION ) ]
302 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_SANITIZE_FAILURE ) ]
303 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_CLUSTER_MAINTENANCE ) ]
304 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ACCOUNT_BORROW_OUTSTANDING ) ]
305 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_WOULD_EXCEED_MAX_BLOCK_COST_LIMIT ) ]
306 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_UNSUPPORTED_VERSION ) ]
307 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_WRITABLE_ACCOUNT ) ]
308 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_WOULD_EXCEED_MAX_ACCOUNT_COST_LIMIT ) ]
309 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT ) ]
310 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_TOO_MANY_ACCOUNT_LOCKS ) ]
311 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_ADDRESS_LOOKUP_TABLE_NOT_FOUND ) ]
312 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_ADDRESS_LOOKUP_TABLE_OWNER ) ]
313 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_ADDRESS_LOOKUP_TABLE_DATA ) ]
314 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_ADDRESS_LOOKUP_TABLE_INDEX ) ]
315 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_RENT_PAYING_ACCOUNT ) ]
316 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_WOULD_EXCEED_MAX_VOTE_COST_LIMIT ) ]
317 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT ) ]
318 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_DUPLICATE_INSTRUCTION ) ]
319 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INSUFFICIENT_FUNDS_FOR_RENT ) ]
320 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_MAX_LOADED_ACCOUNTS_DATA_SIZE_EXCEEDED ) ]
321 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_INVALID_LOADED_ACCOUNTS_DATA_SIZE_LIMIT ) ]
322 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_RESANITIZATION_NEEDED ) ]
323 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED ) ]
324 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_UNBALANCED_TRANSACTION ) ]
325 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_LOAD_PROGRAM_CACHE_HIT_MAX_LIMIT ) ];
326 :
327 0 : cur->out.bank_invalid +=
328 0 : bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_ACCOUNT_IN_USE ) ]
329 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_ACCOUNT_LOADED_TWICE ) ]
330 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_ACCOUNT_NOT_FOUND ) ]
331 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_PROGRAM_ACCOUNT_NOT_FOUND ) ]
332 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INSUFFICIENT_FUNDS_FOR_FEE ) ]
333 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_ACCOUNT_FOR_FEE ) ]
334 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_ALREADY_PROCESSED ) ]
335 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_BLOCKHASH_NOT_FOUND ) ]
336 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INSTRUCTION_ERROR ) ]
337 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_CALL_CHAIN_TOO_DEEP ) ]
338 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_MISSING_SIGNATURE_FOR_FEE ) ]
339 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_ACCOUNT_INDEX ) ]
340 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_SIGNATURE_FAILURE ) ]
341 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_PROGRAM_FOR_EXECUTION ) ]
342 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_SANITIZE_FAILURE ) ]
343 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_CLUSTER_MAINTENANCE ) ]
344 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_ACCOUNT_BORROW_OUTSTANDING ) ]
345 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_WOULD_EXCEED_MAX_BLOCK_COST_LIMIT ) ]
346 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_UNSUPPORTED_VERSION ) ]
347 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_WRITABLE_ACCOUNT ) ]
348 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_WOULD_EXCEED_MAX_ACCOUNT_COST_LIMIT ) ]
349 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT ) ]
350 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_TOO_MANY_ACCOUNT_LOCKS ) ]
351 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_ADDRESS_LOOKUP_TABLE_NOT_FOUND ) ]
352 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_ADDRESS_LOOKUP_TABLE_OWNER ) ]
353 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_ADDRESS_LOOKUP_TABLE_DATA ) ]
354 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_ADDRESS_LOOKUP_TABLE_INDEX ) ]
355 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_RENT_PAYING_ACCOUNT ) ]
356 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_WOULD_EXCEED_MAX_VOTE_COST_LIMIT ) ]
357 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT ) ]
358 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_DUPLICATE_INSTRUCTION ) ]
359 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INSUFFICIENT_FUNDS_FOR_RENT ) ]
360 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_MAX_LOADED_ACCOUNTS_DATA_SIZE_EXCEEDED ) ]
361 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_INVALID_LOADED_ACCOUNTS_DATA_SIZE_LIMIT ) ]
362 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_RESANITIZATION_NEEDED ) ]
363 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED ) ]
364 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_UNBALANCED_TRANSACTION ) ]
365 0 : + bank_metrics[ MIDX( COUNTER, BANK_TILE, TRANSACTION_EXECUTING_PROGRAM_CACHE_HIT_MAX_LIMIT ) ];
366 0 : }
367 :
368 :
369 0 : fd_topo_tile_t const * pack = &topo->tiles[ fd_topo_find_tile( topo, "pack", 0UL ) ];
370 0 : volatile ulong const * pack_metrics = fd_metrics_tile( pack->metrics );
371 :
372 0 : cur->out.pack_invalid =
373 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_WRITE_SYSVAR ) ] +
374 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_ESTIMATION_FAIL ) ] +
375 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_DUPLICATE_ACCOUNT ) ] +
376 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_TOO_MANY_ACCOUNTS ) ] +
377 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_TOO_LARGE ) ] +
378 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_ADDR_LUT ) ] +
379 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_UNAFFORDABLE ) ] +
380 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_DUPLICATE ) ];
381 :
382 0 : cur->out.pack_expired = pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_EXPIRED ) ] +
383 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_EXPIRED ) ];
384 :
385 0 : cur->out.pack_leader_slow =
386 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_PRIORITY ) ] +
387 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_NONVOTE_REPLACE ) ] +
388 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_VOTE_REPLACE ) ];
389 :
390 0 : cur->out.pack_wait_full =
391 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_DROPPED_FROM_EXTRA ) ];
392 :
393 0 : cur->out.pack_retained = pack_metrics[ MIDX( GAUGE, PACK, AVAILABLE_TRANSACTIONS ) ];
394 :
395 0 : ulong inserted_to_extra = pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_TO_EXTRA ) ];
396 0 : ulong inserted_from_extra = pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_FROM_EXTRA ) ]
397 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_DROPPED_FROM_EXTRA ) ];
398 0 : cur->out.pack_retained += fd_ulong_if( inserted_to_extra>=inserted_from_extra, inserted_to_extra-inserted_from_extra, 0UL );
399 :
400 0 : cur->out.resolv_failed = 0UL;
401 0 : for( ulong i=0UL; i<gui->summary.resolv_tile_cnt; i++ ) {
402 0 : fd_topo_tile_t const * resolv = &topo->tiles[ fd_topo_find_tile( topo, "resolv", i ) ];
403 0 : volatile ulong const * resolv_metrics = fd_metrics_tile( resolv->metrics );
404 :
405 0 : cur->out.resolv_failed += resolv_metrics[ MIDX( COUNTER, RESOLV, NO_BANK_DROP ) ] +
406 0 : resolv_metrics[ MIDX( COUNTER, RESOLV, BLOCKHASH_EXPIRED ) ];
407 0 : cur->out.resolv_failed += resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_ACCOUNT_NOT_FOUND ) ]
408 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_INVALID_ACCOUNT_OWNER ) ]
409 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_INVALID_ACCOUNT_DATA ) ]
410 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_ACCOUNT_UNINITIALIZED ) ]
411 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_INVALID_LOOKUP_INDEX ) ];
412 0 : }
413 :
414 :
415 0 : fd_topo_tile_t const * dedup = &topo->tiles[ fd_topo_find_tile( topo, "dedup", 0UL ) ];
416 0 : volatile ulong const * dedup_metrics = fd_metrics_tile( dedup->metrics );
417 :
418 0 : cur->out.dedup_duplicate = dedup_metrics[ MIDX( COUNTER, DEDUP, TRANSACTION_DEDUP_FAILURE ) ];
419 :
420 :
421 0 : cur->out.verify_overrun = 0UL;
422 0 : cur->out.verify_duplicate = 0UL;
423 0 : cur->out.verify_parse = 0UL;
424 0 : cur->out.verify_failed = 0UL;
425 :
426 0 : for( ulong i=0UL; i<gui->summary.verify_tile_cnt; i++ ) {
427 0 : fd_topo_tile_t const * verify = &topo->tiles[ fd_topo_find_tile( topo, "verify", i ) ];
428 0 : volatile ulong const * verify_metrics = fd_metrics_tile( verify->metrics );
429 :
430 0 : for( ulong j=0UL; j<gui->summary.quic_tile_cnt; j++ ) {
431 : /* TODO: Not precise... even if 1 frag gets skipped, it could have been for this verify tile. */
432 0 : cur->out.verify_overrun += fd_metrics_link_in( verify->metrics, j )[ FD_METRICS_COUNTER_LINK_OVERRUN_POLLING_FRAG_COUNT_OFF ] / gui->summary.verify_tile_cnt;
433 0 : cur->out.verify_overrun += fd_metrics_link_in( verify->metrics, j )[ FD_METRICS_COUNTER_LINK_OVERRUN_READING_FRAG_COUNT_OFF ];
434 0 : }
435 :
436 0 : cur->out.verify_failed += verify_metrics[ MIDX( COUNTER, VERIFY, TRANSACTION_VERIFY_FAILURE ) ];
437 0 : cur->out.verify_parse += verify_metrics[ MIDX( COUNTER, VERIFY, TRANSACTION_PARSE_FAILURE ) ];
438 0 : cur->out.verify_duplicate += verify_metrics[ MIDX( COUNTER, VERIFY, TRANSACTION_DEDUP_FAILURE ) ];
439 0 : }
440 :
441 :
442 0 : cur->out.quic_overrun = 0UL;
443 0 : cur->out.quic_quic_invalid = 0UL;
444 0 : cur->out.quic_udp_invalid = 0UL;
445 0 : for( ulong i=0UL; i<gui->summary.quic_tile_cnt; i++ ) {
446 0 : fd_topo_tile_t const * quic = &topo->tiles[ fd_topo_find_tile( topo, "quic", i ) ];
447 0 : volatile ulong * quic_metrics = fd_metrics_tile( quic->metrics );
448 :
449 0 : cur->out.quic_udp_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, NON_QUIC_PACKET_TOO_SMALL ) ];
450 0 : cur->out.quic_udp_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, NON_QUIC_PACKET_TOO_LARGE ) ];
451 0 : cur->out.quic_udp_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, NON_QUIC_REASSEMBLY_PUBLISH_ERROR_OVERSIZE ) ];
452 0 : cur->out.quic_udp_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, NON_QUIC_REASSEMBLY_PUBLISH_ERROR_SKIP ) ];
453 0 : cur->out.quic_udp_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, NON_QUIC_REASSEMBLY_PUBLISH_ERROR_STATE ) ];
454 :
455 0 : cur->out.quic_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, QUIC_PACKET_TOO_SMALL ) ];
456 0 : cur->out.quic_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, REASSEMBLY_PUBLISH_ERROR_OVERSIZE ) ];
457 0 : cur->out.quic_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, REASSEMBLY_PUBLISH_ERROR_SKIP ) ];
458 0 : cur->out.quic_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, REASSEMBLY_PUBLISH_ERROR_STATE ) ];
459 0 : cur->out.quic_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC_TILE, REASSEMBLY_NOTIFY_ABORTED ) ];
460 :
461 0 : cur->out.quic_overrun += quic_metrics[ MIDX( COUNTER, QUIC_TILE, REASSEMBLY_NOTIFY_CLOBBERED ) ];
462 :
463 0 : for( ulong j=0UL; j<gui->summary.net_tile_cnt; j++ ) {
464 : /* TODO: Not precise... net frags that were skipped might not have been destined for QUIC tile */
465 : /* TODO: Not precise... even if 1 frag gets skipped, it could have been for this QUIC tile */
466 0 : cur->out.quic_overrun += fd_metrics_link_in( quic->metrics, j )[ FD_METRICS_COUNTER_LINK_OVERRUN_POLLING_FRAG_COUNT_OFF ] / gui->summary.quic_tile_cnt;
467 0 : cur->out.quic_overrun += fd_metrics_link_in( quic->metrics, j )[ FD_METRICS_COUNTER_LINK_OVERRUN_READING_FRAG_COUNT_OFF ];
468 0 : }
469 0 : }
470 :
471 0 : cur->out.net_overrun = 0UL;
472 0 : for( ulong i=0UL; i<gui->summary.net_tile_cnt; i++ ) {
473 0 : fd_topo_tile_t const * net = &topo->tiles[ fd_topo_find_tile( topo, "net", i ) ];
474 0 : volatile ulong * net_metrics = fd_metrics_tile( net->metrics );
475 :
476 0 : cur->out.net_overrun += net_metrics[ MIDX( COUNTER, NET_TILE, XDP_RX_DROPPED_RING_FULL ) ];
477 0 : cur->out.net_overrun += net_metrics[ MIDX( COUNTER, NET_TILE, XDP_RX_DROPPED_OTHER ) ];
478 0 : }
479 :
480 0 : cur->in.gossip = dedup_metrics[ MIDX( COUNTER, DEDUP, GOSSIPED_VOTES_RECEIVED ) ];
481 0 : cur->in.quic = cur->out.quic_quic_invalid+cur->out.quic_overrun+cur->out.net_overrun;
482 0 : cur->in.udp = cur->out.quic_udp_invalid;
483 0 : for( ulong i=0UL; i<gui->summary.quic_tile_cnt; i++ ) {
484 0 : fd_topo_tile_t const * quic = &topo->tiles[ fd_topo_find_tile( topo, "quic", i ) ];
485 0 : volatile ulong * quic_metrics = fd_metrics_tile( quic->metrics );
486 :
487 0 : cur->in.quic += quic_metrics[ MIDX( COUNTER, QUIC_TILE, REASSEMBLY_PUBLISH_SUCCESS ) ];
488 0 : cur->in.udp += quic_metrics[ MIDX( COUNTER, QUIC_TILE, NON_QUIC_REASSEMBLY_PUBLISH_SUCCESS ) ];
489 0 : }
490 :
491 : /* TODO: We can get network packet drops between the device and the
492 : kernel ring buffer by querying some network device stats... */
493 0 : }
494 :
495 : static void
496 : fd_gui_tile_prime_metric_snap( fd_gui_t * gui,
497 : fd_gui_txn_waterfall_t * w_cur,
498 0 : fd_gui_tile_prime_metric_t * m_cur ) {
499 0 : fd_topo_t * topo = gui->topo;
500 :
501 0 : m_cur->ts_nanos = fd_log_wallclock();
502 :
503 0 : m_cur->net_in_bytes = 0UL;
504 0 : m_cur->net_out_bytes = 0UL;
505 0 : for( ulong i=0UL; i<gui->summary.net_tile_cnt; i++ ) {
506 0 : fd_topo_tile_t const * net = &topo->tiles[ fd_topo_find_tile( topo, "net", i ) ];
507 0 : volatile ulong * net_metrics = fd_metrics_tile( net->metrics );
508 :
509 0 : m_cur->net_in_bytes += net_metrics[ MIDX( COUNTER, NET_TILE, RECEIVED_BYTES ) ];
510 0 : m_cur->net_out_bytes += net_metrics[ MIDX( COUNTER, NET_TILE, SENT_BYTES ) ];
511 0 : }
512 :
513 0 : m_cur->quic_conns = 0UL;
514 0 : for( ulong i=0UL; i<gui->summary.quic_tile_cnt; i++ ) {
515 0 : fd_topo_tile_t const * quic = &topo->tiles[ fd_topo_find_tile( topo, "quic", i ) ];
516 0 : volatile ulong * quic_metrics = fd_metrics_tile( quic->metrics );
517 :
518 0 : m_cur->quic_conns += quic_metrics[ MIDX( GAUGE, QUIC, CONNECTIONS_ACTIVE ) ];
519 0 : }
520 :
521 0 : m_cur->verify_drop_numerator = w_cur->out.verify_duplicate +
522 0 : w_cur->out.verify_parse +
523 0 : w_cur->out.verify_failed;
524 0 : m_cur->verify_drop_denominator = w_cur->in.gossip +
525 0 : w_cur->in.quic +
526 0 : w_cur->in.udp -
527 0 : w_cur->out.verify_overrun;
528 0 : m_cur->dedup_drop_numerator = w_cur->out.dedup_duplicate;
529 0 : m_cur->dedup_drop_denominator = m_cur->verify_drop_denominator -
530 0 : m_cur->verify_drop_numerator;
531 :
532 0 : fd_topo_tile_t const * pack = &topo->tiles[ fd_topo_find_tile( topo, "pack", 0UL ) ];
533 0 : volatile ulong const * pack_metrics = fd_metrics_tile( pack->metrics );
534 0 : m_cur->pack_fill_numerator = pack_metrics[ MIDX( GAUGE, PACK, AVAILABLE_TRANSACTIONS ) ];
535 0 : m_cur->pack_fill_denominator = pack->pack.max_pending_transactions;
536 :
537 0 : m_cur->bank_txn = w_cur->out.block_fail + w_cur->out.block_success;
538 0 : }
539 :
540 : int
541 0 : fd_gui_poll( fd_gui_t * gui ) {
542 0 : long now = fd_log_wallclock();
543 :
544 0 : int did_work = 0;
545 :
546 0 : if( FD_LIKELY( now>gui->next_sample_400millis ) ) {
547 0 : fd_gui_estimated_tps_snap( gui );
548 0 : fd_gui_printf_estimated_tps( gui );
549 0 : fd_http_server_ws_broadcast( gui->http );
550 :
551 0 : gui->next_sample_400millis += 400L*1000L*1000L;
552 0 : did_work = 1;
553 0 : }
554 :
555 0 : if( FD_LIKELY( now>gui->next_sample_100millis ) ) {
556 0 : fd_gui_txn_waterfall_snap( gui, gui->summary.txn_waterfall_current );
557 0 : fd_gui_printf_live_txn_waterfall( gui, gui->summary.txn_waterfall_reference, gui->summary.txn_waterfall_current, 0UL /* TODO: REAL NEXT LEADER SLOT */ );
558 0 : fd_http_server_ws_broadcast( gui->http );
559 :
560 0 : fd_gui_tile_prime_metric_snap( gui, gui->summary.txn_waterfall_current, gui->summary.tile_prime_metric_cur );
561 0 : fd_gui_printf_live_tile_prime_metric( gui, gui->summary.tile_prime_metric_ref, gui->summary.tile_prime_metric_cur, 0UL ); // TODO: REAL NEXT LEADER SLOT
562 0 : fd_http_server_ws_broadcast( gui->http );
563 :
564 0 : gui->next_sample_100millis += 100L*1000L*1000L;
565 0 : did_work = 1;
566 0 : }
567 :
568 0 : if( FD_LIKELY( now>gui->next_sample_10millis ) ) {
569 0 : fd_gui_tile_timers_snap( gui );
570 :
571 0 : fd_gui_printf_live_tile_timers( gui );
572 0 : fd_http_server_ws_broadcast( gui->http );
573 :
574 0 : gui->next_sample_10millis += 10L*1000L*1000L;
575 0 : did_work = 1;
576 0 : }
577 :
578 0 : return did_work;
579 0 : }
580 :
581 : static void
582 : fd_gui_handle_gossip_update( fd_gui_t * gui,
583 0 : uchar const * msg ) {
584 0 : ulong const * header = (ulong const *)fd_type_pun_const( msg );
585 0 : ulong peer_cnt = header[ 0 ];
586 :
587 0 : FD_TEST( peer_cnt<=40200UL );
588 :
589 0 : ulong added_cnt = 0UL;
590 0 : ulong added[ 40200 ] = {0};
591 :
592 0 : ulong update_cnt = 0UL;
593 0 : ulong updated[ 40200 ] = {0};
594 :
595 0 : ulong removed_cnt = 0UL;
596 0 : fd_pubkey_t removed[ 40200 ] = {0};
597 :
598 0 : uchar const * data = (uchar const *)(header+1UL);
599 0 : for( ulong i=0UL; i<gui->gossip.peer_cnt; i++ ) {
600 0 : int found = 0;
601 0 : for( ulong j=0UL; j<peer_cnt; j++ ) {
602 0 : if( FD_UNLIKELY( !memcmp( gui->gossip.peers[ i ].pubkey, data+j*(58UL+12UL*6UL), 32UL ) ) ) {
603 0 : found = 1;
604 0 : break;
605 0 : }
606 0 : }
607 :
608 0 : if( FD_UNLIKELY( !found ) ) {
609 0 : fd_memcpy( removed[ removed_cnt++ ].uc, gui->gossip.peers[ i ].pubkey->uc, 32UL );
610 0 : if( FD_LIKELY( i+1UL!=gui->gossip.peer_cnt ) ) {
611 0 : fd_memcpy( &gui->gossip.peers[ i ], &gui->gossip.peers[ gui->gossip.peer_cnt-1UL ], sizeof(struct fd_gui_gossip_peer) );
612 0 : gui->gossip.peer_cnt--;
613 0 : i--;
614 0 : }
615 0 : }
616 0 : }
617 :
618 0 : ulong before_peer_cnt = gui->gossip.peer_cnt;
619 0 : for( ulong i=0UL; i<peer_cnt; i++ ) {
620 0 : int found = 0;
621 0 : ulong found_idx;
622 0 : for( ulong j=0UL; j<gui->gossip.peer_cnt; j++ ) {
623 0 : if( FD_UNLIKELY( !memcmp( gui->gossip.peers[ j ].pubkey, data+i*(58UL+12UL*6UL), 32UL ) ) ) {
624 0 : found_idx = j;
625 0 : found = 1;
626 0 : break;
627 0 : }
628 0 : }
629 :
630 0 : if( FD_UNLIKELY( !found ) ) {
631 0 : fd_memcpy( gui->gossip.peers[ gui->gossip.peer_cnt ].pubkey->uc, data+i*(58UL+12UL*6UL), 32UL );
632 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].wallclock = *(ulong const *)(data+i*(58UL+12UL*6UL)+32UL);
633 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].shred_version = *(ushort const *)(data+i*(58UL+12UL*6UL)+40UL);
634 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].has_version = *(data+i*(58UL+12UL*6UL)+42UL);
635 0 : if( FD_LIKELY( gui->gossip.peers[ gui->gossip.peer_cnt ].has_version ) ) {
636 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.major = *(ushort const *)(data+i*(58UL+12UL*6UL)+43UL);
637 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.minor = *(ushort const *)(data+i*(58UL+12UL*6UL)+45UL);
638 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.patch = *(ushort const *)(data+i*(58UL+12UL*6UL)+47UL);
639 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.has_commit = *(data+i*(58UL+12UL*6UL)+49UL);
640 0 : if( FD_LIKELY( gui->gossip.peers[ gui->gossip.peer_cnt ].version.has_commit ) ) {
641 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.commit = *(uint const *)(data+i*(58UL+12UL*6UL)+50UL);
642 0 : }
643 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.feature_set = *(uint const *)(data+i*(58UL+12UL*6UL)+54UL);
644 0 : }
645 :
646 0 : for( ulong j=0UL; j<12UL; j++ ) {
647 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].sockets[ j ].ipv4 = *(uint const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL);
648 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].sockets[ j ].port = *(ushort const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL+4UL);
649 0 : }
650 :
651 0 : gui->gossip.peer_cnt++;
652 0 : } else {
653 0 : int peer_updated = gui->gossip.peers[ found_idx ].shred_version!=*(ushort const *)(data+i*(58UL+12UL*6UL)+40UL) ||
654 : // gui->gossip.peers[ found_idx ].wallclock!=*(ulong const *)(data+i*(58UL+12UL*6UL)+32UL) ||
655 0 : gui->gossip.peers[ found_idx ].has_version!=*(data+i*(58UL+12UL*6UL)+42UL);
656 :
657 0 : if( FD_LIKELY( !peer_updated && gui->gossip.peers[ found_idx ].has_version ) ) {
658 0 : peer_updated = gui->gossip.peers[ found_idx ].version.major!=*(ushort const *)(data+i*(58UL+12UL*6UL)+43UL) ||
659 0 : gui->gossip.peers[ found_idx ].version.minor!=*(ushort const *)(data+i*(58UL+12UL*6UL)+45UL) ||
660 0 : gui->gossip.peers[ found_idx ].version.patch!=*(ushort const *)(data+i*(58UL+12UL*6UL)+47UL) ||
661 0 : gui->gossip.peers[ found_idx ].version.has_commit!=*(data+i*(58UL+12UL*6UL)+49UL) ||
662 0 : (gui->gossip.peers[ found_idx ].version.has_commit && gui->gossip.peers[ found_idx ].version.commit!=*(uint const *)(data+i*(58UL+12UL*6UL)+50UL)) ||
663 0 : gui->gossip.peers[ found_idx ].version.feature_set!=*(uint const *)(data+i*(58UL+12UL*6UL)+54UL);
664 0 : }
665 :
666 0 : if( FD_LIKELY( !peer_updated ) ) {
667 0 : for( ulong j=0UL; j<12UL; j++ ) {
668 0 : peer_updated = gui->gossip.peers[ found_idx ].sockets[ j ].ipv4!=*(uint const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL) ||
669 0 : gui->gossip.peers[ found_idx ].sockets[ j ].port!=*(ushort const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL+4UL);
670 0 : if( FD_LIKELY( peer_updated ) ) break;
671 0 : }
672 0 : }
673 :
674 0 : if( FD_UNLIKELY( peer_updated ) ) {
675 0 : updated[ update_cnt++ ] = found_idx;
676 0 : gui->gossip.peers[ found_idx ].shred_version = *(ushort const *)(data+i*(58UL+12UL*6UL)+40UL);
677 0 : gui->gossip.peers[ found_idx ].wallclock = *(ulong const *)(data+i*(58UL+12UL*6UL)+32UL);
678 0 : gui->gossip.peers[ found_idx ].has_version = *(data+i*(58UL+12UL*6UL)+42UL);
679 0 : if( FD_LIKELY( gui->gossip.peers[ found_idx ].has_version ) ) {
680 0 : gui->gossip.peers[ found_idx ].version.major = *(ushort const *)(data+i*(58UL+12UL*6UL)+43UL);
681 0 : gui->gossip.peers[ found_idx ].version.minor = *(ushort const *)(data+i*(58UL+12UL*6UL)+45UL);
682 0 : gui->gossip.peers[ found_idx ].version.patch = *(ushort const *)(data+i*(58UL+12UL*6UL)+47UL);
683 0 : gui->gossip.peers[ found_idx ].version.has_commit = *(data+i*(58UL+12UL*6UL)+49UL);
684 0 : if( FD_LIKELY( gui->gossip.peers[ found_idx ].version.has_commit ) ) {
685 0 : gui->gossip.peers[ found_idx ].version.commit = *(uint const *)(data+i*(58UL+12UL*6UL)+50UL);
686 0 : }
687 0 : gui->gossip.peers[ found_idx ].version.feature_set = *(uint const *)(data+i*(58UL+12UL*6UL)+54UL);
688 0 : }
689 :
690 0 : for( ulong j=0UL; j<12UL; j++ ) {
691 0 : gui->gossip.peers[ found_idx ].sockets[ j ].ipv4 = *(uint const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL);
692 0 : gui->gossip.peers[ found_idx ].sockets[ j ].port = *(ushort const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL+4UL);
693 0 : }
694 0 : }
695 0 : }
696 0 : }
697 :
698 0 : added_cnt = gui->gossip.peer_cnt - before_peer_cnt;
699 0 : for( ulong i=before_peer_cnt; i<gui->gossip.peer_cnt; i++ ) added[ i-before_peer_cnt ] = i;
700 :
701 0 : fd_gui_printf_peers_gossip_update( gui, updated, update_cnt, removed, removed_cnt, added, added_cnt );
702 0 : fd_http_server_ws_broadcast( gui->http );
703 0 : }
704 :
705 : static void
706 : fd_gui_handle_vote_account_update( fd_gui_t * gui,
707 0 : uchar const * msg ) {
708 0 : ulong const * header = (ulong const *)fd_type_pun_const( msg );
709 0 : ulong peer_cnt = header[ 0 ];
710 :
711 0 : FD_TEST( peer_cnt<=40200UL );
712 :
713 0 : ulong added_cnt = 0UL;
714 0 : ulong added[ 40200 ] = {0};
715 :
716 0 : ulong update_cnt = 0UL;
717 0 : ulong updated[ 40200 ] = {0};
718 :
719 0 : ulong removed_cnt = 0UL;
720 0 : fd_pubkey_t removed[ 40200 ] = {0};
721 :
722 0 : uchar const * data = (uchar const *)(header+1UL);
723 0 : for( ulong i=0UL; i<gui->vote_account.vote_account_cnt; i++ ) {
724 0 : int found = 0;
725 0 : for( ulong j=0UL; j<peer_cnt; j++ ) {
726 0 : if( FD_UNLIKELY( !memcmp( gui->vote_account.vote_accounts[ i ].vote_account, data+j*112UL, 32UL ) ) ) {
727 0 : found = 1;
728 0 : break;
729 0 : }
730 0 : }
731 :
732 0 : if( FD_UNLIKELY( !found ) ) {
733 0 : fd_memcpy( removed[ removed_cnt++ ].uc, gui->vote_account.vote_accounts[ i ].vote_account->uc, 32UL );
734 0 : if( FD_LIKELY( i+1UL!=gui->vote_account.vote_account_cnt ) ) {
735 0 : fd_memcpy( &gui->vote_account.vote_accounts[ i ], &gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt-1UL ], sizeof(struct fd_gui_vote_account) );
736 0 : gui->vote_account.vote_account_cnt--;
737 0 : i--;
738 0 : }
739 0 : }
740 0 : }
741 :
742 0 : ulong before_peer_cnt = gui->vote_account.vote_account_cnt;
743 0 : for( ulong i=0UL; i<peer_cnt; i++ ) {
744 0 : int found = 0;
745 0 : ulong found_idx;
746 0 : for( ulong j=0UL; j<gui->vote_account.vote_account_cnt; j++ ) {
747 0 : if( FD_UNLIKELY( !memcmp( gui->vote_account.vote_accounts[ j ].vote_account, data+i*112UL, 32UL ) ) ) {
748 0 : found_idx = j;
749 0 : found = 1;
750 0 : break;
751 0 : }
752 0 : }
753 :
754 0 : if( FD_UNLIKELY( !found ) ) {
755 0 : fd_memcpy( gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].vote_account->uc, data+i*112UL, 32UL );
756 0 : fd_memcpy( gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].pubkey->uc, data+i*112UL+32UL, 32UL );
757 :
758 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].activated_stake = *(ulong const *)(data+i*112UL+64UL);
759 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].last_vote = *(ulong const *)(data+i*112UL+72UL);
760 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].root_slot = *(ulong const *)(data+i*112UL+80UL);
761 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].epoch_credits = *(ulong const *)(data+i*112UL+88UL);
762 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].commission = *(data+i*112UL+96UL);
763 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].delinquent = *(data+i*112UL+97UL);
764 :
765 0 : gui->vote_account.vote_account_cnt++;
766 0 : } else {
767 0 : int peer_updated =
768 0 : memcmp( gui->vote_account.vote_accounts[ found_idx ].pubkey->uc, data+i*112UL+32UL, 32UL ) ||
769 0 : gui->vote_account.vote_accounts[ found_idx ].activated_stake != *(ulong const *)(data+i*112UL+64UL) ||
770 : // gui->vote_account.vote_accounts[ found_idx ].last_vote != *(ulong const *)(data+i*112UL+72UL) ||
771 : // gui->vote_account.vote_accounts[ found_idx ].root_slot != *(ulong const *)(data+i*112UL+80UL) ||
772 : // gui->vote_account.vote_accounts[ found_idx ].epoch_credits != *(ulong const *)(data+i*112UL+88UL) ||
773 0 : gui->vote_account.vote_accounts[ found_idx ].commission != *(data+i*112UL+96UL) ||
774 0 : gui->vote_account.vote_accounts[ found_idx ].delinquent != *(data+i*112UL+97UL);
775 :
776 0 : if( FD_UNLIKELY( peer_updated ) ) {
777 0 : updated[ update_cnt++ ] = found_idx;
778 :
779 0 : fd_memcpy( gui->vote_account.vote_accounts[ found_idx ].pubkey->uc, data+i*112UL+32UL, 32UL );
780 0 : gui->vote_account.vote_accounts[ found_idx ].activated_stake = *(ulong const *)(data+i*112UL+64UL);
781 0 : gui->vote_account.vote_accounts[ found_idx ].last_vote = *(ulong const *)(data+i*112UL+72UL);
782 0 : gui->vote_account.vote_accounts[ found_idx ].root_slot = *(ulong const *)(data+i*112UL+80UL);
783 0 : gui->vote_account.vote_accounts[ found_idx ].epoch_credits = *(ulong const *)(data+i*112UL+88UL);
784 0 : gui->vote_account.vote_accounts[ found_idx ].commission = *(data+i*112UL+96UL);
785 0 : gui->vote_account.vote_accounts[ found_idx ].delinquent = *(data+i*112UL+97UL);
786 0 : }
787 0 : }
788 0 : }
789 :
790 0 : added_cnt = gui->vote_account.vote_account_cnt - before_peer_cnt;
791 0 : for( ulong i=before_peer_cnt; i<gui->vote_account.vote_account_cnt; i++ ) added[ i-before_peer_cnt ] = i;
792 :
793 0 : fd_gui_printf_peers_vote_account_update( gui, updated, update_cnt, removed, removed_cnt, added, added_cnt );
794 0 : fd_http_server_ws_broadcast( gui->http );
795 0 : }
796 :
797 : static void
798 : fd_gui_handle_validator_info_update( fd_gui_t * gui,
799 0 : uchar const * msg ) {
800 0 : ulong const * header = (ulong const *)fd_type_pun_const( msg );
801 0 : ulong peer_cnt = header[ 0 ];
802 :
803 0 : FD_TEST( peer_cnt<=40200UL );
804 :
805 0 : ulong added_cnt = 0UL;
806 0 : ulong added[ 40200 ] = {0};
807 :
808 0 : ulong update_cnt = 0UL;
809 0 : ulong updated[ 40200 ] = {0};
810 :
811 0 : ulong removed_cnt = 0UL;
812 0 : fd_pubkey_t removed[ 40200 ] = {0};
813 :
814 0 : uchar const * data = (uchar const *)(header+1UL);
815 0 : for( ulong i=0UL; i<gui->validator_info.info_cnt; i++ ) {
816 0 : int found = 0;
817 0 : for( ulong j=0UL; j<peer_cnt; j++ ) {
818 0 : if( FD_UNLIKELY( !memcmp( gui->validator_info.info[ i ].pubkey, data+j*608UL, 32UL ) ) ) {
819 0 : found = 1;
820 0 : break;
821 0 : }
822 0 : }
823 :
824 0 : if( FD_UNLIKELY( !found ) ) {
825 0 : fd_memcpy( removed[ removed_cnt++ ].uc, gui->validator_info.info[ i ].pubkey->uc, 32UL );
826 0 : if( FD_LIKELY( i+1UL!=gui->validator_info.info_cnt ) ) {
827 0 : fd_memcpy( &gui->validator_info.info[ i ], &gui->validator_info.info[ gui->validator_info.info_cnt-1UL ], sizeof(struct fd_gui_validator_info) );
828 0 : gui->validator_info.info_cnt--;
829 0 : i--;
830 0 : }
831 0 : }
832 0 : }
833 :
834 0 : ulong before_peer_cnt = gui->validator_info.info_cnt;
835 0 : for( ulong i=0UL; i<peer_cnt; i++ ) {
836 0 : int found = 0;
837 0 : ulong found_idx;
838 0 : for( ulong j=0UL; j<gui->validator_info.info_cnt; j++ ) {
839 0 : if( FD_UNLIKELY( !memcmp( gui->validator_info.info[ j ].pubkey, data+i*608UL, 32UL ) ) ) {
840 0 : found_idx = j;
841 0 : found = 1;
842 0 : break;
843 0 : }
844 0 : }
845 :
846 0 : if( FD_UNLIKELY( !found ) ) {
847 0 : fd_memcpy( gui->validator_info.info[ gui->validator_info.info_cnt ].pubkey->uc, data+i*608UL, 32UL );
848 :
849 0 : strncpy( gui->validator_info.info[ gui->validator_info.info_cnt ].name, (char const *)(data+i*608UL+32UL), 64 );
850 0 : gui->validator_info.info[ gui->validator_info.info_cnt ].name[ 63 ] = '\0';
851 :
852 0 : strncpy( gui->validator_info.info[ gui->validator_info.info_cnt ].website, (char const *)(data+i*608UL+96UL), 128 );
853 0 : gui->validator_info.info[ gui->validator_info.info_cnt ].website[ 127 ] = '\0';
854 :
855 0 : strncpy( gui->validator_info.info[ gui->validator_info.info_cnt ].details, (char const *)(data+i*608UL+224UL), 256 );
856 0 : gui->validator_info.info[ gui->validator_info.info_cnt ].details[ 255 ] = '\0';
857 :
858 0 : strncpy( gui->validator_info.info[ gui->validator_info.info_cnt ].icon_uri, (char const *)(data+i*608UL+480UL), 128 );
859 0 : gui->validator_info.info[ gui->validator_info.info_cnt ].icon_uri[ 127 ] = '\0';
860 :
861 0 : gui->validator_info.info_cnt++;
862 0 : } else {
863 0 : int peer_updated =
864 0 : memcmp( gui->validator_info.info[ found_idx ].pubkey->uc, data+i*608UL, 32UL ) ||
865 0 : strncmp( gui->validator_info.info[ found_idx ].name, (char const *)(data+i*608UL+32UL), 64 ) ||
866 0 : strncmp( gui->validator_info.info[ found_idx ].website, (char const *)(data+i*608UL+96UL), 128 ) ||
867 0 : strncmp( gui->validator_info.info[ found_idx ].details, (char const *)(data+i*608UL+224UL), 256 ) ||
868 0 : strncmp( gui->validator_info.info[ found_idx ].icon_uri, (char const *)(data+i*608UL+480UL), 128 );
869 :
870 0 : if( FD_UNLIKELY( peer_updated ) ) {
871 0 : updated[ update_cnt++ ] = found_idx;
872 :
873 0 : fd_memcpy( gui->validator_info.info[ found_idx ].pubkey->uc, data+i*608UL, 32UL );
874 :
875 0 : strncpy( gui->validator_info.info[ found_idx ].name, (char const *)(data+i*608UL+32UL), 64 );
876 0 : gui->validator_info.info[ found_idx ].name[ 63 ] = '\0';
877 :
878 0 : strncpy( gui->validator_info.info[ found_idx ].website, (char const *)(data+i*608UL+96UL), 128 );
879 0 : gui->validator_info.info[ found_idx ].website[ 127 ] = '\0';
880 :
881 0 : strncpy( gui->validator_info.info[ found_idx ].details, (char const *)(data+i*608UL+224UL), 256 );
882 0 : gui->validator_info.info[ found_idx ].details[ 255 ] = '\0';
883 :
884 0 : strncpy( gui->validator_info.info[ found_idx ].icon_uri, (char const *)(data+i*608UL+480UL), 128 );
885 0 : gui->validator_info.info[ found_idx ].icon_uri[ 127 ] = '\0';
886 0 : }
887 0 : }
888 0 : }
889 :
890 0 : added_cnt = gui->validator_info.info_cnt - before_peer_cnt;
891 0 : for( ulong i=before_peer_cnt; i<gui->validator_info.info_cnt; i++ ) added[ i-before_peer_cnt ] = i;
892 :
893 0 : fd_gui_printf_peers_validator_info_update( gui, updated, update_cnt, removed, removed_cnt, added, added_cnt );
894 0 : fd_http_server_ws_broadcast( gui->http );
895 0 : }
896 :
897 : int
898 : fd_gui_request_slot( fd_gui_t * gui,
899 : ulong ws_conn_id,
900 : ulong request_id,
901 0 : cJSON const * params ) {
902 0 : const cJSON * slot_param = cJSON_GetObjectItemCaseSensitive( params, "slot" );
903 0 : if( FD_UNLIKELY( !cJSON_IsNumber( slot_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
904 :
905 0 : ulong _slot = slot_param->valueulong;
906 0 : fd_gui_slot_t const * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
907 0 : if( FD_UNLIKELY( slot->slot!=_slot || slot->slot==ULONG_MAX ) ) {
908 0 : fd_gui_printf_null_query_response( gui, "slot", "query", request_id );
909 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
910 0 : return 0;
911 0 : }
912 :
913 0 : fd_gui_printf_slot_request( gui, _slot, request_id );
914 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
915 0 : return 0;
916 0 : }
917 :
918 : int
919 : fd_gui_ws_message( fd_gui_t * gui,
920 : ulong ws_conn_id,
921 : uchar const * data,
922 0 : ulong data_len ) {
923 : /* TODO: cJSON allocates, might fail SIGSYS due to brk(2)...
924 : switch off this (or use wksp allocator) */
925 0 : const char * parse_end;
926 0 : cJSON * json = cJSON_ParseWithLengthOpts( (char *)data, data_len, &parse_end, 0 );
927 0 : if( FD_UNLIKELY( !json ) ) {
928 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
929 0 : }
930 :
931 0 : const cJSON * node = cJSON_GetObjectItemCaseSensitive( json, "id" );
932 0 : if( FD_UNLIKELY( !cJSON_IsNumber( node ) ) ) {
933 0 : cJSON_Delete( json );
934 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
935 0 : }
936 0 : ulong id = node->valueulong;
937 :
938 0 : const cJSON * topic = cJSON_GetObjectItemCaseSensitive( json, "topic" );
939 0 : if( FD_UNLIKELY( !cJSON_IsString( topic ) || topic->valuestring==NULL ) ) {
940 0 : cJSON_Delete( json );
941 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
942 0 : }
943 :
944 0 : const cJSON * key = cJSON_GetObjectItemCaseSensitive( json, "key" );
945 0 : if( FD_UNLIKELY( !cJSON_IsString( key ) || key->valuestring==NULL ) ) {
946 0 : cJSON_Delete( json );
947 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
948 0 : }
949 :
950 0 : if( FD_LIKELY( !strcmp( topic->valuestring, "slot" ) && !strcmp( key->valuestring, "query" ) ) ) {
951 0 : const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
952 0 : if( FD_UNLIKELY( !cJSON_IsObject( params ) ) ) {
953 0 : cJSON_Delete( json );
954 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
955 0 : }
956 :
957 0 : int result = fd_gui_request_slot( gui, ws_conn_id, id, params );
958 0 : cJSON_Delete( json );
959 0 : return result;
960 0 : } else if( FD_LIKELY( !strcmp( topic->valuestring, "summary" ) && !strcmp( key->valuestring, "ping" ) ) ) {
961 0 : fd_gui_printf_summary_ping( gui, id );
962 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
963 :
964 0 : cJSON_Delete( json );
965 0 : return 0;
966 0 : }
967 :
968 0 : cJSON_Delete( json );
969 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_UNKNOWN_METHOD;
970 0 : }
971 :
972 : static void
973 : fd_gui_clear_slot( fd_gui_t * gui,
974 : ulong _slot,
975 0 : ulong _parent_slot ) {
976 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
977 :
978 0 : int mine = 0;
979 0 : ulong epoch_idx = 0UL;
980 0 : for( ulong i=0UL; i<2UL; i++) {
981 0 : if( FD_LIKELY( _slot>=gui->epoch.epochs[ i ].start_slot && _slot<=gui->epoch.epochs[ i ].end_slot ) ) {
982 0 : fd_pubkey_t const * slot_leader = fd_epoch_leaders_get( gui->epoch.epochs[ i ].lsched, _slot );
983 0 : mine = !memcmp( slot_leader->uc, gui->summary.identity_key->uc, 32UL );
984 0 : epoch_idx = i;
985 0 : break;
986 0 : }
987 0 : }
988 :
989 0 : slot->slot = _slot;
990 0 : slot->parent_slot = _parent_slot;
991 0 : slot->mine = mine;
992 0 : slot->skipped = 0;
993 0 : slot->must_republish = 1;
994 0 : slot->level = FD_GUI_SLOT_LEVEL_INCOMPLETE;
995 0 : slot->total_txn_cnt = ULONG_MAX;
996 0 : slot->vote_txn_cnt = ULONG_MAX;
997 0 : slot->failed_txn_cnt = ULONG_MAX;
998 0 : slot->nonvote_failed_txn_cnt = ULONG_MAX;
999 0 : slot->compute_units = ULONG_MAX;
1000 0 : slot->transaction_fee = ULONG_MAX;
1001 0 : slot->priority_fee = ULONG_MAX;
1002 0 : slot->leader_state = FD_GUI_SLOT_LEADER_UNSTARTED;
1003 0 : slot->completed_time = LONG_MAX;
1004 :
1005 0 : if( FD_LIKELY( slot->mine ) ) {
1006 : /* All slots start off not skipped, until we see it get off the reset
1007 : chain. */
1008 0 : gui->epoch.epochs[ epoch_idx ].my_total_slots++;
1009 0 : }
1010 :
1011 0 : if( FD_UNLIKELY( !_slot ) ) {
1012 : /* Slot 0 is always rooted */
1013 0 : slot->level = FD_GUI_SLOT_LEVEL_ROOTED;
1014 0 : }
1015 0 : }
1016 :
1017 : static void
1018 : fd_gui_handle_leader_schedule( fd_gui_t * gui,
1019 0 : ulong const * msg ) {
1020 0 : ulong epoch = msg[ 0 ];
1021 0 : ulong staked_cnt = msg[ 1 ];
1022 0 : ulong start_slot = msg[ 2 ];
1023 0 : ulong slot_cnt = msg[ 3 ];
1024 0 : ulong excluded_stake = msg[ 4 ];
1025 :
1026 0 : FD_TEST( staked_cnt<=50000UL );
1027 0 : FD_TEST( slot_cnt<=432000UL );
1028 :
1029 0 : ulong idx = epoch % 2UL;
1030 0 : gui->epoch.has_epoch[ idx ] = 1;
1031 :
1032 :
1033 0 : gui->epoch.epochs[ idx ].epoch = epoch;
1034 0 : gui->epoch.epochs[ idx ].start_slot = start_slot;
1035 0 : gui->epoch.epochs[ idx ].end_slot = start_slot + slot_cnt - 1; // end_slot is inclusive.
1036 0 : gui->epoch.epochs[ idx ].excluded_stake = excluded_stake;
1037 0 : gui->epoch.epochs[ idx ].my_total_slots = 0UL;
1038 0 : gui->epoch.epochs[ idx ].my_skipped_slots = 0UL;
1039 0 : fd_epoch_leaders_delete( fd_epoch_leaders_leave( gui->epoch.epochs[ idx ].lsched ) );
1040 0 : gui->epoch.epochs[idx].lsched = fd_epoch_leaders_join( fd_epoch_leaders_new( gui->epoch.epochs[ idx ]._lsched,
1041 0 : epoch,
1042 0 : gui->epoch.epochs[ idx ].start_slot,
1043 0 : slot_cnt,
1044 0 : staked_cnt,
1045 0 : fd_type_pun_const( msg+5UL ),
1046 0 : excluded_stake ) );
1047 0 : fd_memcpy( gui->epoch.epochs[ idx ].stakes, fd_type_pun_const( msg+5UL ), staked_cnt*sizeof(gui->epoch.epochs[ idx ].stakes[ 0 ]) );
1048 :
1049 0 : if( FD_UNLIKELY( start_slot==0UL ) ) {
1050 0 : gui->epoch.epochs[ 0 ].start_time = fd_log_wallclock();
1051 0 : } else {
1052 0 : gui->epoch.epochs[ idx ].start_time = LONG_MAX;
1053 :
1054 0 : for( ulong i=0UL; i<fd_ulong_min( start_slot-1UL, FD_GUI_SLOTS_CNT ); i++ ) {
1055 0 : fd_gui_slot_t * slot = gui->slots[ (start_slot-i) % FD_GUI_SLOTS_CNT ];
1056 0 : if( FD_UNLIKELY( slot->slot!=(start_slot-i) ) ) break;
1057 0 : else if( FD_UNLIKELY( slot->skipped ) ) continue;
1058 :
1059 0 : gui->epoch.epochs[ idx ].start_time = slot->completed_time;
1060 0 : break;
1061 0 : }
1062 0 : }
1063 :
1064 0 : fd_gui_printf_epoch( gui, idx );
1065 0 : fd_http_server_ws_broadcast( gui->http );
1066 0 : }
1067 :
1068 : static void
1069 : fd_gui_handle_slot_start( fd_gui_t * gui,
1070 0 : ulong * msg ) {
1071 0 : ulong _slot = msg[ 0 ];
1072 0 : ulong _parent_slot = msg[ 1 ];
1073 : // FD_LOG_WARNING(( "Got start slot %lu parent_slot %lu", _slot, _parent_slot ));
1074 0 : FD_TEST( gui->debug_in_leader_slot==ULONG_MAX );
1075 0 : gui->debug_in_leader_slot = _slot;
1076 :
1077 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1078 :
1079 0 : if( FD_UNLIKELY( slot->slot!=_slot ) ) fd_gui_clear_slot( gui, _slot, _parent_slot );
1080 0 : slot->leader_state = FD_GUI_SLOT_LEADER_STARTED;
1081 :
1082 0 : fd_gui_tile_timers_snap( gui );
1083 0 : gui->summary.tile_timers_snap_idx_slot_start = (gui->summary.tile_timers_snap_idx+(FD_GUI_TILE_TIMER_SNAP_CNT-1UL))%FD_GUI_TILE_TIMER_SNAP_CNT;
1084 0 : }
1085 :
1086 : static void
1087 : fd_gui_handle_slot_end( fd_gui_t * gui,
1088 0 : ulong * msg ) {
1089 0 : ulong _slot = msg[ 0 ];
1090 0 : ulong _cus_used = msg[ 1 ];
1091 0 : if( FD_UNLIKELY( gui->debug_in_leader_slot!=_slot ) ) {
1092 0 : FD_LOG_ERR(( "gui->debug_in_leader_slot %lu _slot %lu", gui->debug_in_leader_slot, _slot ));
1093 0 : }
1094 0 : gui->debug_in_leader_slot = ULONG_MAX;
1095 :
1096 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1097 0 : FD_TEST( slot->slot==_slot );
1098 :
1099 0 : slot->leader_state = FD_GUI_SLOT_LEADER_ENDED;
1100 0 : slot->compute_units = _cus_used;
1101 :
1102 0 : fd_gui_tile_timers_snap( gui );
1103 : /* Record slot number so we can detect overwrite. */
1104 0 : gui->summary.tile_timers_leader_history_slot[ gui->summary.tile_timers_history_idx ] = _slot;
1105 : /* Point into per-leader-slot storage. */
1106 0 : slot->tile_timers_history_idx = gui->summary.tile_timers_history_idx;
1107 : /* Downsample tile timers into per-leader-slot storage. */
1108 0 : ulong end = gui->summary.tile_timers_snap_idx;
1109 0 : end = fd_ulong_if( end<gui->summary.tile_timers_snap_idx_slot_start, end+FD_GUI_TILE_TIMER_SNAP_CNT, end );
1110 0 : gui->summary.tile_timers_leader_history_slot_sample_cnt[ gui->summary.tile_timers_history_idx ] = end-gui->summary.tile_timers_snap_idx_slot_start;
1111 0 : ulong stride = fd_ulong_max( 1UL, (end-gui->summary.tile_timers_snap_idx_slot_start) / FD_GUI_TILE_TIMER_LEADER_DOWNSAMPLE_CNT );
1112 0 : for( ulong sample_snap_idx=gui->summary.tile_timers_snap_idx_slot_start, i=0UL; sample_snap_idx<end; sample_snap_idx+=stride, i++ ) {
1113 0 : memcpy( gui->summary.tile_timers_leader_history[ gui->summary.tile_timers_history_idx ][ i ], gui->summary.tile_timers_snap[ sample_snap_idx%FD_GUI_TILE_TIMER_SNAP_CNT ], sizeof(gui->summary.tile_timers_leader_history[ gui->summary.tile_timers_history_idx ][ i ]) );
1114 0 : }
1115 0 : gui->summary.tile_timers_history_idx = (gui->summary.tile_timers_history_idx+1UL)%FD_GUI_TILE_TIMER_LEADER_CNT;
1116 :
1117 : /* When a slot ends, snap the state of the waterfall and save it into
1118 : that slot, and also reset the reference counters to the end of the
1119 : slot. */
1120 :
1121 0 : fd_gui_txn_waterfall_snap( gui, slot->waterfall_end );
1122 0 : fd_gui_tile_prime_metric_snap( gui, slot->waterfall_end, slot->tile_prime_metric_end );
1123 0 : memcpy( slot->waterfall_begin, gui->summary.txn_waterfall_reference, sizeof(slot->waterfall_begin) );
1124 0 : memcpy( gui->summary.txn_waterfall_reference, slot->waterfall_end, sizeof(gui->summary.txn_waterfall_reference) );
1125 0 : memcpy( slot->tile_prime_metric_begin, gui->summary.tile_prime_metric_ref, sizeof(slot->tile_prime_metric_begin) );
1126 0 : memcpy( gui->summary.tile_prime_metric_ref, slot->tile_prime_metric_end, sizeof(gui->summary.tile_prime_metric_ref) );
1127 0 : }
1128 :
1129 : static void
1130 : fd_gui_handle_reset_slot( fd_gui_t * gui,
1131 0 : ulong * msg ) {
1132 0 : ulong last_landed_vote = msg[ 0 ];
1133 :
1134 0 : ulong parent_cnt = msg[ 1 ];
1135 0 : FD_TEST( parent_cnt<4096UL );
1136 :
1137 0 : ulong _slot = msg[ 2 ];
1138 :
1139 0 : for( ulong i=0UL; i<parent_cnt; i++ ) {
1140 0 : ulong parent_slot = msg[2UL+i];
1141 0 : fd_gui_slot_t * slot = gui->slots[ parent_slot % FD_GUI_SLOTS_CNT ];
1142 0 : if( FD_UNLIKELY( slot->slot!=parent_slot ) ) {
1143 0 : ulong parent_parent_slot = ULONG_MAX;
1144 0 : if( FD_UNLIKELY( i!=parent_cnt-1UL) ) parent_parent_slot = msg[ 3UL+i ];
1145 0 : fd_gui_clear_slot( gui, parent_slot, parent_parent_slot );
1146 0 : }
1147 0 : }
1148 :
1149 0 : if( FD_UNLIKELY( gui->summary.vote_distance!=_slot-last_landed_vote ) ) {
1150 0 : gui->summary.vote_distance = _slot-last_landed_vote;
1151 0 : fd_gui_printf_vote_distance( gui );
1152 0 : fd_http_server_ws_broadcast( gui->http );
1153 0 : }
1154 :
1155 0 : if( FD_LIKELY( gui->summary.vote_state!=FD_GUI_VOTE_STATE_NON_VOTING ) ) {
1156 0 : if( FD_UNLIKELY( last_landed_vote==ULONG_MAX || (last_landed_vote+150UL)<_slot ) ) {
1157 0 : if( FD_UNLIKELY( gui->summary.vote_state!=FD_GUI_VOTE_STATE_DELINQUENT ) ) {
1158 0 : gui->summary.vote_state = FD_GUI_VOTE_STATE_DELINQUENT;
1159 0 : fd_gui_printf_vote_state( gui );
1160 0 : fd_http_server_ws_broadcast( gui->http );
1161 0 : }
1162 0 : } else {
1163 0 : if( FD_UNLIKELY( gui->summary.vote_state!=FD_GUI_VOTE_STATE_VOTING ) ) {
1164 0 : gui->summary.vote_state = FD_GUI_VOTE_STATE_VOTING;
1165 0 : fd_gui_printf_vote_state( gui );
1166 0 : fd_http_server_ws_broadcast( gui->http );
1167 0 : }
1168 0 : }
1169 0 : }
1170 :
1171 0 : ulong parent_slot_idx = 0UL;
1172 :
1173 0 : int republish_skip_rate[ 2 ] = {0};
1174 :
1175 0 : for( ulong i=0UL; i<fd_ulong_min( _slot+1, FD_GUI_SLOTS_CNT ); i++ ) {
1176 0 : ulong parent_slot = _slot - i;
1177 0 : ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
1178 :
1179 0 : fd_gui_slot_t * slot = gui->slots[ parent_idx ];
1180 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX || slot->slot!=parent_slot ) ) fd_gui_clear_slot( gui, parent_slot, ULONG_MAX );
1181 :
1182 : /* The chain of parents may stretch into already rooted slots if
1183 : they haven't been squashed yet, if we reach one of them we can
1184 : just exit, all the information prior to the root is already
1185 : correct. */
1186 :
1187 0 : if( FD_LIKELY( slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) break;
1188 :
1189 0 : int should_republish = slot->must_republish;
1190 0 : slot->must_republish = 0;
1191 :
1192 0 : if( FD_UNLIKELY( parent_slot!=msg[2UL+parent_slot_idx] ) ) {
1193 : /* We are between two parents in the rooted chain, which means
1194 : we were skipped. */
1195 0 : if( FD_UNLIKELY( !slot->skipped ) ) {
1196 0 : slot->skipped = 1;
1197 0 : should_republish = 1;
1198 0 : if( FD_LIKELY( slot->mine ) ) {
1199 0 : for( ulong i=0UL; i<2UL; i++ ) {
1200 0 : if( FD_LIKELY( parent_slot>=gui->epoch.epochs[ i ].start_slot && parent_slot<=gui->epoch.epochs[ i ].end_slot ) ) {
1201 0 : gui->epoch.epochs[ i ].my_skipped_slots++;
1202 0 : republish_skip_rate[ i ] = 1;
1203 0 : break;
1204 0 : }
1205 0 : }
1206 0 : }
1207 0 : }
1208 0 : } else {
1209 : /* Reached the next parent... */
1210 0 : if( FD_UNLIKELY( slot->skipped ) ) {
1211 0 : slot->skipped = 0;
1212 0 : should_republish = 1;
1213 0 : if( FD_LIKELY( slot->mine ) ) {
1214 0 : for( ulong i=0UL; i<2UL; i++ ) {
1215 0 : if( FD_LIKELY( parent_slot>=gui->epoch.epochs[ i ].start_slot && parent_slot<=gui->epoch.epochs[ i ].end_slot ) ) {
1216 0 : gui->epoch.epochs[ i ].my_skipped_slots--;
1217 0 : republish_skip_rate[ i ] = 1;
1218 0 : break;
1219 0 : }
1220 0 : }
1221 0 : }
1222 0 : }
1223 0 : parent_slot_idx++;
1224 0 : }
1225 :
1226 0 : if( FD_LIKELY( should_republish ) ) {
1227 0 : fd_gui_printf_slot( gui, parent_slot );
1228 0 : fd_http_server_ws_broadcast( gui->http );
1229 0 : }
1230 :
1231 : /* We reached the last parent in the chain, everything above this
1232 : must have already been rooted, so we can exit. */
1233 :
1234 0 : if( FD_UNLIKELY( parent_slot_idx>=parent_cnt ) ) break;
1235 0 : }
1236 :
1237 0 : ulong last_slot = _slot;
1238 0 : long last_published = gui->slots[ _slot % FD_GUI_SLOTS_CNT ]->completed_time;
1239 :
1240 0 : for( ulong i=0UL; i<fd_ulong_min( _slot+1, 750UL ); i++ ) {
1241 0 : ulong parent_slot = _slot - i;
1242 0 : ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
1243 :
1244 0 : fd_gui_slot_t * slot = gui->slots[ parent_idx ];
1245 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX) ) break;
1246 0 : if( FD_UNLIKELY( slot->slot!=parent_slot ) ) {
1247 0 : FD_LOG_ERR(( "_slot %lu i %lu we expect _slot-i %lu got slot->slot %lu", _slot, i, _slot-i, slot->slot ));
1248 0 : }
1249 :
1250 0 : if( FD_LIKELY( !slot->skipped ) ) {
1251 0 : last_slot = parent_slot;
1252 0 : last_published = slot->completed_time;
1253 0 : }
1254 0 : }
1255 :
1256 0 : if( FD_LIKELY( _slot!=last_slot )) {
1257 0 : gui->summary.estimated_slot_duration_nanos = (ulong)(fd_log_wallclock()-last_published)/(_slot-last_slot);
1258 0 : fd_gui_printf_estimated_slot_duration_nanos( gui );
1259 0 : fd_http_server_ws_broadcast( gui->http );
1260 0 : }
1261 :
1262 0 : if( FD_LIKELY( _slot!=gui->summary.slot_completed ) ) {
1263 0 : gui->summary.slot_completed = _slot;
1264 0 : fd_gui_printf_completed_slot( gui );
1265 0 : fd_http_server_ws_broadcast( gui->http );
1266 0 : }
1267 :
1268 0 : for( ulong i=0UL; i<2UL; i++ ) {
1269 0 : if( FD_LIKELY( republish_skip_rate[ i ] ) ) {
1270 0 : fd_gui_printf_skip_rate( gui, i );
1271 0 : fd_http_server_ws_broadcast( gui->http );
1272 0 : }
1273 0 : }
1274 0 : }
1275 :
1276 : static void
1277 : fd_gui_handle_completed_slot( fd_gui_t * gui,
1278 0 : ulong * msg ) {
1279 0 : ulong _slot = msg[ 0 ];
1280 0 : ulong total_txn_count = msg[ 1 ];
1281 0 : ulong nonvote_txn_count = msg[ 2 ];
1282 0 : ulong failed_txn_count = msg[ 3 ];
1283 0 : ulong nonvote_failed_txn_count = msg[ 4 ];
1284 0 : ulong compute_units = msg[ 5 ];
1285 0 : ulong transaction_fee = msg[ 6 ];
1286 0 : ulong priority_fee = msg[ 7 ];
1287 0 : ulong _parent_slot = msg[ 8 ];
1288 :
1289 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1290 0 : if( FD_UNLIKELY( slot->slot!=_slot ) ) fd_gui_clear_slot( gui, _slot, _parent_slot );
1291 :
1292 0 : slot->completed_time = fd_log_wallclock();
1293 0 : slot->parent_slot = _parent_slot;
1294 0 : if( FD_LIKELY( slot->level<FD_GUI_SLOT_LEVEL_COMPLETED ) ) {
1295 : /* Typically a slot goes from INCOMPLETE to COMPLETED but it can
1296 : happen that it starts higher. One such case is when we
1297 : optimistically confirm a higher slot that skips this one, but
1298 : then later we replay this one anyway to track the bank fork. */
1299 :
1300 0 : if( FD_LIKELY( _slot<gui->summary.slot_optimistically_confirmed ) ) {
1301 : /* Cluster might have already optimistically confirmed by the time
1302 : we finish replaying it. */
1303 0 : slot->level = FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED;
1304 0 : } else {
1305 0 : slot->level = FD_GUI_SLOT_LEVEL_COMPLETED;
1306 0 : }
1307 0 : }
1308 0 : slot->total_txn_cnt = total_txn_count;
1309 0 : slot->vote_txn_cnt = total_txn_count - nonvote_txn_count;
1310 0 : slot->failed_txn_cnt = failed_txn_count;
1311 0 : slot->nonvote_failed_txn_cnt = nonvote_failed_txn_count;
1312 0 : slot->transaction_fee = transaction_fee;
1313 0 : slot->priority_fee = priority_fee;
1314 0 : if( FD_LIKELY( slot->leader_state==FD_GUI_SLOT_LEADER_UNSTARTED ) ) {
1315 : /* If we were already leader for this slot, then the poh component
1316 : calculated the CUs used and sent them there, rather than the
1317 : replay component which is sending this completed slot. */
1318 0 : slot->compute_units = compute_units;
1319 0 : }
1320 :
1321 0 : if( FD_UNLIKELY( gui->epoch.has_epoch[ 0 ] && _slot==gui->epoch.epochs[ 0 ].end_slot ) ) {
1322 0 : gui->epoch.epochs[ 0 ].end_time = slot->completed_time;
1323 0 : } else if( FD_UNLIKELY( gui->epoch.has_epoch[ 1 ] && _slot==gui->epoch.epochs[ 1 ].end_slot ) ) {
1324 0 : gui->epoch.epochs[ 1 ].end_time = slot->completed_time;
1325 0 : }
1326 :
1327 : /* Broadcast new skip rate if one of our slots got completed. */
1328 0 : if( FD_LIKELY( slot->mine ) ) {
1329 0 : for( ulong i=0UL; i<2UL; i++ ) {
1330 0 : if( FD_LIKELY( _slot>=gui->epoch.epochs[ i ].start_slot && _slot<=gui->epoch.epochs[ i ].end_slot ) ) {
1331 0 : fd_gui_printf_skip_rate( gui, i );
1332 0 : fd_http_server_ws_broadcast( gui->http );
1333 0 : break;
1334 0 : }
1335 0 : }
1336 0 : }
1337 0 : }
1338 :
1339 : static void
1340 : fd_gui_handle_rooted_slot( fd_gui_t * gui,
1341 0 : ulong * msg ) {
1342 0 : ulong _slot = msg[ 0 ];
1343 :
1344 : // FD_LOG_WARNING(( "Got rooted slot %lu", _slot ));
1345 :
1346 : /* Slot 0 is always rooted. No need to iterate all the way back to
1347 : i==_slot */
1348 0 : for( ulong i=0UL; i<fd_ulong_min( _slot, FD_GUI_SLOTS_CNT ); i++ ) {
1349 0 : ulong parent_slot = _slot - i;
1350 0 : ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
1351 :
1352 0 : fd_gui_slot_t * slot = gui->slots[ parent_idx ];
1353 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX) ) break;
1354 :
1355 0 : if( FD_UNLIKELY( slot->slot!=parent_slot ) ) {
1356 0 : FD_LOG_ERR(( "_slot %lu i %lu we expect parent_slot %lu got slot->slot %lu", _slot, i, parent_slot, slot->slot ));
1357 0 : }
1358 0 : if( FD_UNLIKELY( slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) break;
1359 :
1360 0 : slot->level = FD_GUI_SLOT_LEVEL_ROOTED;
1361 0 : fd_gui_printf_slot( gui, parent_slot );
1362 0 : fd_http_server_ws_broadcast( gui->http );
1363 0 : }
1364 :
1365 0 : gui->summary.slot_rooted = _slot;
1366 0 : fd_gui_printf_root_slot( gui );
1367 0 : fd_http_server_ws_broadcast( gui->http );
1368 0 : }
1369 :
1370 : static void
1371 : fd_gui_handle_optimistically_confirmed_slot( fd_gui_t * gui,
1372 0 : ulong * msg ) {
1373 0 : ulong _slot = msg[ 0 ];
1374 :
1375 : /* Slot 0 is always rooted. No need to iterate all the way back to
1376 : i==_slot */
1377 0 : for( ulong i=0UL; i<fd_ulong_min( _slot, FD_GUI_SLOTS_CNT ); i++ ) {
1378 0 : ulong parent_slot = _slot - i;
1379 0 : ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
1380 :
1381 0 : fd_gui_slot_t * slot = gui->slots[ parent_idx ];
1382 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX) ) break;
1383 :
1384 0 : if( FD_UNLIKELY( slot->slot>parent_slot ) ) {
1385 0 : FD_LOG_ERR(( "_slot %lu i %lu we expect parent_slot %lu got slot->slot %lu", _slot, i, parent_slot, slot->slot ));
1386 0 : } else if( FD_UNLIKELY( slot->slot<parent_slot ) ) {
1387 : /* Slot not even replayed yet ... will come out as optmistically confirmed */
1388 0 : continue;
1389 0 : }
1390 0 : if( FD_UNLIKELY( slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) break;
1391 :
1392 0 : if( FD_LIKELY( slot->level<FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED ) ) {
1393 0 : slot->level = FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED;
1394 0 : fd_gui_printf_slot( gui, parent_slot );
1395 0 : fd_http_server_ws_broadcast( gui->http );
1396 0 : }
1397 0 : }
1398 :
1399 0 : if( FD_UNLIKELY( _slot<gui->summary.slot_optimistically_confirmed ) ) {
1400 : /* Optimistically confirmed slot went backwards ... mark some slots as no
1401 : longer optimistically confirmed. */
1402 0 : for( ulong i=gui->summary.slot_optimistically_confirmed; i>=_slot; i-- ) {
1403 0 : fd_gui_slot_t * slot = gui->slots[ i % FD_GUI_SLOTS_CNT ];
1404 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX ) ) break;
1405 0 : if( FD_LIKELY( slot->slot==i ) ) {
1406 : /* It's possible for the optimistically confirmed slot to skip
1407 : backwards between two slots that we haven't yet replayed. In
1408 : that case we don't need to change anything, since they will
1409 : get marked properly when they get completed. */
1410 0 : slot->level = FD_GUI_SLOT_LEVEL_COMPLETED;
1411 0 : fd_gui_printf_slot( gui, i );
1412 0 : fd_http_server_ws_broadcast( gui->http );
1413 0 : }
1414 0 : }
1415 0 : }
1416 :
1417 0 : gui->summary.slot_optimistically_confirmed = _slot;
1418 0 : fd_gui_printf_optimistically_confirmed_slot( gui );
1419 0 : fd_http_server_ws_broadcast( gui->http );
1420 0 : }
1421 :
1422 : static void
1423 : fd_gui_handle_balance_update( fd_gui_t * gui,
1424 0 : ulong balance ) {
1425 0 : gui->summary.balance = balance;
1426 0 : fd_gui_printf_balance( gui );
1427 0 : fd_http_server_ws_broadcast( gui->http );
1428 0 : }
1429 :
1430 : static void
1431 : fd_gui_handle_start_progress( fd_gui_t * gui,
1432 0 : uchar const * msg ) {
1433 0 : (void)gui;
1434 :
1435 0 : uchar type = msg[ 0 ];
1436 :
1437 0 : switch (type) {
1438 0 : case 0:
1439 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_INITIALIZING;
1440 0 : FD_LOG_INFO(( "progress: initializing" ));
1441 0 : break;
1442 0 : case 1: {
1443 0 : char const * snapshot_type;
1444 0 : if( FD_UNLIKELY( gui->summary.startup_got_full_snapshot ) ) {
1445 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_SEARCHING_FOR_INCREMENTAL_SNAPSHOT;
1446 0 : snapshot_type = "incremental";
1447 0 : } else {
1448 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_SEARCHING_FOR_FULL_SNAPSHOT;
1449 0 : snapshot_type = "full";
1450 0 : }
1451 0 : FD_LOG_INFO(( "progress: searching for %s snapshot", snapshot_type ));
1452 0 : break;
1453 0 : }
1454 0 : case 2: {
1455 0 : uchar is_full_snapshot = msg[ 1 ];
1456 0 : if( FD_LIKELY( is_full_snapshot ) ) {
1457 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_DOWNLOADING_FULL_SNAPSHOT;
1458 0 : gui->summary.startup_full_snapshot_slot = *((ulong *)(msg + 2));
1459 0 : gui->summary.startup_full_snapshot_peer_ip_addr = *((uint *)(msg + 10));
1460 0 : gui->summary.startup_full_snapshot_peer_port = *((ushort *)(msg + 14));
1461 0 : gui->summary.startup_full_snapshot_total_bytes = *((ulong *)(msg + 16));
1462 0 : gui->summary.startup_full_snapshot_current_bytes = *((ulong *)(msg + 24));
1463 0 : gui->summary.startup_full_snapshot_elapsed_secs = *((double *)(msg + 32));
1464 0 : gui->summary.startup_full_snapshot_remaining_secs = *((double *)(msg + 40));
1465 0 : gui->summary.startup_full_snapshot_throughput = *((double *)(msg + 48));
1466 0 : FD_LOG_INFO(( "progress: downloading full snapshot: slot=%lu", gui->summary.startup_full_snapshot_slot ));
1467 0 : } else {
1468 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_DOWNLOADING_INCREMENTAL_SNAPSHOT;
1469 0 : gui->summary.startup_incremental_snapshot_slot = *((ulong *)(msg + 2));
1470 0 : gui->summary.startup_incremental_snapshot_peer_ip_addr = *((uint *)(msg + 10));
1471 0 : gui->summary.startup_incremental_snapshot_peer_port = *((ushort *)(msg + 14));
1472 0 : gui->summary.startup_incremental_snapshot_total_bytes = *((ulong *)(msg + 16));
1473 0 : gui->summary.startup_incremental_snapshot_current_bytes = *((ulong *)(msg + 24));
1474 0 : gui->summary.startup_incremental_snapshot_elapsed_secs = *((double *)(msg + 32));
1475 0 : gui->summary.startup_incremental_snapshot_remaining_secs = *((double *)(msg + 40));
1476 0 : gui->summary.startup_incremental_snapshot_throughput = *((double *)(msg + 48));
1477 0 : FD_LOG_INFO(( "progress: downloading incremental snapshot: slot=%lu", gui->summary.startup_incremental_snapshot_slot ));
1478 0 : }
1479 0 : break;
1480 0 : }
1481 0 : case 3: {
1482 0 : gui->summary.startup_got_full_snapshot = 1;
1483 0 : break;
1484 0 : }
1485 0 : case 4:
1486 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_CLEANING_BLOCK_STORE;
1487 0 : FD_LOG_INFO(( "progress: cleaning block store" ));
1488 0 : break;
1489 0 : case 5:
1490 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_CLEANING_ACCOUNTS;
1491 0 : FD_LOG_INFO(( "progress: cleaning accounts" ));
1492 0 : break;
1493 0 : case 6:
1494 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_LOADING_LEDGER;
1495 0 : FD_LOG_INFO(( "progress: loading ledger" ));
1496 0 : break;
1497 0 : case 7: {
1498 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_PROCESSING_LEDGER;
1499 0 : gui->summary.startup_ledger_slot = fd_ulong_load_8( msg + 1 );
1500 0 : gui->summary.startup_ledger_max_slot = fd_ulong_load_8( msg + 9 );
1501 0 : FD_LOG_INFO(( "progress: processing ledger: slot=%lu, max_slot=%lu", gui->summary.startup_ledger_slot, gui->summary.startup_ledger_max_slot ));
1502 0 : break;
1503 0 : }
1504 0 : case 8:
1505 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_STARTING_SERVICES;
1506 0 : FD_LOG_INFO(( "progress: starting services" ));
1507 0 : break;
1508 0 : case 9:
1509 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_HALTED;
1510 0 : FD_LOG_INFO(( "progress: halted" ));
1511 0 : break;
1512 0 : case 10: {
1513 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY;
1514 0 : gui->summary.startup_waiting_for_supermajority_slot = fd_ulong_load_8( msg + 1 );
1515 0 : gui->summary.startup_waiting_for_supermajority_stake_pct = fd_ulong_load_8( msg + 9 );
1516 0 : FD_LOG_INFO(( "progress: waiting for supermajority: slot=%lu, gossip_stake_percent=%lu", gui->summary.startup_waiting_for_supermajority_slot, gui->summary.startup_waiting_for_supermajority_stake_pct ));
1517 0 : break;
1518 0 : }
1519 0 : case 11:
1520 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_RUNNING;
1521 0 : FD_LOG_INFO(( "progress: running" ));
1522 0 : break;
1523 0 : default:
1524 0 : FD_LOG_ERR(( "progress: unknown type: %u", type ));
1525 0 : }
1526 :
1527 0 : fd_gui_printf_startup_progress( gui );
1528 0 : fd_http_server_ws_broadcast( gui->http );
1529 0 : }
1530 :
1531 : void
1532 : fd_gui_plugin_message( fd_gui_t * gui,
1533 : ulong plugin_msg,
1534 0 : uchar const * msg ) {
1535 :
1536 0 : switch( plugin_msg ) {
1537 0 : case FD_PLUGIN_MSG_SLOT_ROOTED:
1538 0 : fd_gui_handle_rooted_slot( gui, (ulong *)msg );
1539 0 : break;
1540 0 : case FD_PLUGIN_MSG_SLOT_OPTIMISTICALLY_CONFIRMED:
1541 0 : fd_gui_handle_optimistically_confirmed_slot( gui, (ulong *)msg );
1542 0 : break;
1543 0 : case FD_PLUGIN_MSG_SLOT_COMPLETED:
1544 0 : fd_gui_handle_completed_slot( gui, (ulong *)msg );
1545 0 : break;
1546 0 : case FD_PLUGIN_MSG_SLOT_ESTIMATED:
1547 0 : gui->summary.slot_estimated = *(ulong const *)msg;
1548 0 : fd_gui_printf_estimated_slot( gui );
1549 0 : fd_http_server_ws_broadcast( gui->http );
1550 0 : break;
1551 0 : case FD_PLUGIN_MSG_LEADER_SCHEDULE: {
1552 0 : fd_gui_handle_leader_schedule( gui, (ulong const *)msg );
1553 0 : break;
1554 0 : }
1555 0 : case FD_PLUGIN_MSG_SLOT_START: {
1556 0 : fd_gui_handle_slot_start( gui, (ulong *)msg );
1557 0 : break;
1558 0 : }
1559 0 : case FD_PLUGIN_MSG_SLOT_END: {
1560 0 : fd_gui_handle_slot_end( gui, (ulong *)msg );
1561 0 : break;
1562 0 : }
1563 0 : case FD_PLUGIN_MSG_GOSSIP_UPDATE: {
1564 0 : fd_gui_handle_gossip_update( gui, msg );
1565 0 : break;
1566 0 : }
1567 0 : case FD_PLUGIN_MSG_VOTE_ACCOUNT_UPDATE: {
1568 0 : fd_gui_handle_vote_account_update( gui, msg );
1569 0 : break;
1570 0 : }
1571 0 : case FD_PLUGIN_MSG_VALIDATOR_INFO: {
1572 0 : fd_gui_handle_validator_info_update( gui, msg );
1573 0 : break;
1574 0 : }
1575 0 : case FD_PLUGIN_MSG_SLOT_RESET: {
1576 0 : fd_gui_handle_reset_slot( gui, (ulong *)msg );
1577 0 : break;
1578 0 : }
1579 0 : case FD_PLUGIN_MSG_BALANCE: {
1580 0 : fd_gui_handle_balance_update( gui, *(ulong *)msg );
1581 0 : break;
1582 0 : }
1583 0 : case FD_PLUGIN_MSG_START_PROGRESS: {
1584 0 : fd_gui_handle_start_progress( gui, msg );
1585 0 : break;
1586 0 : }
1587 0 : default:
1588 0 : FD_LOG_ERR(( "Unhandled plugin msg: 0x%lx", plugin_msg ));
1589 0 : break;
1590 0 : }
1591 0 : }
|