Line data Source code
1 : #include "fd_gui.h"
2 : #include "fd_gui_printf.h"
3 :
4 : #include "../metrics/fd_metrics.h"
5 : #include "../plugin/fd_plugin.h"
6 :
7 : #include "../../ballet/base58/fd_base58.h"
8 : #include "../../ballet/json/cJSON.h"
9 : #include "../../disco/genesis/fd_genesis_cluster.h"
10 : #include "../../disco/pack/fd_pack.h"
11 : #include "../../disco/pack/fd_pack_cost.h"
12 : #include "../../disco/shred/fd_stake_ci.h"
13 :
14 : FD_FN_CONST ulong
15 0 : fd_gui_align( void ) {
16 0 : return 128UL;
17 0 : }
18 :
19 : FD_FN_CONST ulong
20 0 : fd_gui_footprint( void ) {
21 0 : return sizeof(fd_gui_t);
22 0 : }
23 :
24 : void *
25 : fd_gui_new( void * shmem,
26 : fd_http_server_t * http,
27 : char const * version,
28 : char const * cluster,
29 : uchar const * identity_key,
30 : int has_vote_key,
31 : uchar const * vote_key,
32 : int is_voting,
33 : int schedule_strategy,
34 0 : fd_topo_t * topo ) {
35 :
36 0 : if( FD_UNLIKELY( !shmem ) ) {
37 0 : FD_LOG_WARNING(( "NULL shmem" ));
38 0 : return NULL;
39 0 : }
40 :
41 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_gui_align() ) ) ) {
42 0 : FD_LOG_WARNING(( "misaligned shmem" ));
43 0 : return NULL;
44 0 : }
45 :
46 0 : if( FD_UNLIKELY( topo->tile_cnt>FD_GUI_TILE_TIMER_TILE_CNT ) ) {
47 0 : FD_LOG_WARNING(( "too many tiles" ));
48 0 : return NULL;
49 0 : }
50 :
51 0 : fd_gui_t * gui = (fd_gui_t *)shmem;
52 :
53 0 : gui->http = http;
54 0 : gui->topo = topo;
55 :
56 0 : gui->debug_in_leader_slot = ULONG_MAX;
57 0 : gui->summary.schedule_strategy = schedule_strategy;
58 :
59 :
60 0 : gui->next_sample_400millis = fd_log_wallclock();
61 0 : gui->next_sample_100millis = gui->next_sample_400millis;
62 0 : gui->next_sample_10millis = gui->next_sample_400millis;
63 :
64 0 : memcpy( gui->summary.identity_key->uc, identity_key, 32UL );
65 0 : fd_base58_encode_32( identity_key, NULL, gui->summary.identity_key_base58 );
66 0 : gui->summary.identity_key_base58[ FD_BASE58_ENCODED_32_SZ-1UL ] = '\0';
67 :
68 0 : if( FD_LIKELY( has_vote_key ) ) {
69 0 : gui->summary.has_vote_key = 1;
70 0 : memcpy( gui->summary.vote_key->uc, vote_key, 32UL );
71 0 : fd_base58_encode_32( vote_key, NULL, gui->summary.vote_key_base58 );
72 0 : gui->summary.vote_key_base58[ FD_BASE58_ENCODED_32_SZ-1UL ] = '\0';
73 0 : } else {
74 0 : gui->summary.has_vote_key = 0;
75 0 : memset( gui->summary.vote_key_base58, 0, sizeof(gui->summary.vote_key_base58) );
76 0 : }
77 :
78 0 : gui->summary.version = version;
79 0 : gui->summary.cluster = cluster;
80 0 : gui->summary.startup_time_nanos = gui->next_sample_400millis;
81 :
82 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_INITIALIZING;
83 0 : gui->summary.startup_got_full_snapshot = 0;
84 0 : gui->summary.startup_full_snapshot_slot = 0;
85 0 : gui->summary.startup_incremental_snapshot_slot = 0;
86 0 : gui->summary.startup_waiting_for_supermajority_slot = ULONG_MAX;
87 :
88 0 : gui->summary.identity_account_balance = 0UL;
89 0 : gui->summary.vote_account_balance = 0UL;
90 0 : gui->summary.estimated_slot_duration_nanos = 0UL;
91 :
92 0 : gui->summary.vote_distance = 0UL;
93 0 : gui->summary.vote_state = is_voting ? FD_GUI_VOTE_STATE_VOTING : FD_GUI_VOTE_STATE_NON_VOTING;
94 :
95 0 : gui->summary.sock_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "sock" );
96 0 : gui->summary.net_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "net" );
97 0 : gui->summary.quic_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "quic" );
98 0 : gui->summary.verify_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "verify" );
99 0 : gui->summary.resolv_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "resolv" );
100 0 : gui->summary.bank_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "bank" );
101 0 : gui->summary.shred_tile_cnt = fd_topo_tile_name_cnt( gui->topo, "shred" );
102 :
103 0 : gui->summary.slot_rooted = 0UL;
104 0 : gui->summary.slot_optimistically_confirmed = 0UL;
105 0 : gui->summary.slot_completed = 0UL;
106 0 : gui->summary.slot_estimated = 0UL;
107 :
108 0 : gui->summary.estimated_tps_history_idx = 0UL;
109 0 : memset( gui->summary.estimated_tps_history, 0, sizeof(gui->summary.estimated_tps_history) );
110 :
111 0 : memset( gui->summary.txn_waterfall_reference, 0, sizeof(gui->summary.txn_waterfall_reference) );
112 0 : memset( gui->summary.txn_waterfall_current, 0, sizeof(gui->summary.txn_waterfall_current) );
113 :
114 0 : memset( gui->summary.tile_stats_reference, 0, sizeof(gui->summary.tile_stats_reference) );
115 0 : memset( gui->summary.tile_stats_current, 0, sizeof(gui->summary.tile_stats_current) );
116 :
117 0 : memset( gui->summary.tile_timers_snap[ 0 ], 0, sizeof(gui->summary.tile_timers_snap[ 0 ]) );
118 0 : memset( gui->summary.tile_timers_snap[ 1 ], 0, sizeof(gui->summary.tile_timers_snap[ 1 ]) );
119 0 : gui->summary.tile_timers_snap_idx = 2UL;
120 0 : gui->summary.tile_timers_history_idx = 0UL;
121 0 : for( ulong i=0UL; i<FD_GUI_TILE_TIMER_LEADER_CNT; i++ ) gui->summary.tile_timers_leader_history_slot[ i ] = ULONG_MAX;
122 :
123 0 : gui->block_engine.has_block_engine = 0;
124 :
125 0 : gui->epoch.has_epoch[ 0 ] = 0;
126 0 : gui->epoch.has_epoch[ 1 ] = 0;
127 :
128 0 : gui->gossip.peer_cnt = 0UL;
129 0 : gui->vote_account.vote_account_cnt = 0UL;
130 0 : gui->validator_info.info_cnt = 0UL;
131 :
132 0 : for( ulong i=0UL; i<FD_GUI_SLOTS_CNT; i++ ) gui->slots[ i ]->slot = ULONG_MAX;
133 0 : gui->pack_txn_idx = 0UL;
134 :
135 0 : fd_histf_new( gui->bundle_rx_delay_hist_current, FD_MHIST_MIN( BUNDLE, MESSAGE_RX_DELAY_NANOS ), FD_MHIST_MAX( BUNDLE, MESSAGE_RX_DELAY_NANOS ) );
136 0 : fd_histf_new( gui->bundle_rx_delay_hist_reference, FD_MHIST_MIN( BUNDLE, MESSAGE_RX_DELAY_NANOS ), FD_MHIST_MAX( BUNDLE, MESSAGE_RX_DELAY_NANOS ) );
137 :
138 0 : return gui;
139 0 : }
140 :
141 : fd_gui_t *
142 0 : fd_gui_join( void * shmem ) {
143 0 : return (fd_gui_t *)shmem;
144 0 : }
145 :
146 : void
147 : fd_gui_set_identity( fd_gui_t * gui,
148 0 : uchar const * identity_pubkey ) {
149 0 : memcpy( gui->summary.identity_key->uc, identity_pubkey, 32UL );
150 0 : fd_base58_encode_32( identity_pubkey, NULL, gui->summary.identity_key_base58 );
151 0 : gui->summary.identity_key_base58[ FD_BASE58_ENCODED_32_SZ-1UL ] = '\0';
152 :
153 0 : fd_gui_printf_identity_key( gui );
154 0 : fd_http_server_ws_broadcast( gui->http );
155 :
156 0 : fd_gui_printf_identity_balance( gui );
157 0 : fd_http_server_ws_broadcast( gui->http );
158 0 : }
159 :
160 : void
161 : fd_gui_ws_open( fd_gui_t * gui,
162 0 : ulong ws_conn_id ) {
163 0 : void (* printers[] )( fd_gui_t * gui ) = {
164 0 : fd_gui_printf_startup_progress,
165 0 : fd_gui_printf_version,
166 0 : fd_gui_printf_cluster,
167 0 : fd_gui_printf_commit_hash,
168 0 : fd_gui_printf_identity_key,
169 0 : fd_gui_printf_vote_key,
170 0 : fd_gui_printf_startup_time_nanos,
171 0 : fd_gui_printf_vote_state,
172 0 : fd_gui_printf_vote_distance,
173 0 : fd_gui_printf_skipped_history,
174 0 : fd_gui_printf_tps_history,
175 0 : fd_gui_printf_tiles,
176 0 : fd_gui_printf_schedule_strategy,
177 0 : fd_gui_printf_identity_balance,
178 0 : fd_gui_printf_vote_balance,
179 0 : fd_gui_printf_estimated_slot_duration_nanos,
180 0 : fd_gui_printf_root_slot,
181 0 : fd_gui_printf_optimistically_confirmed_slot,
182 0 : fd_gui_printf_completed_slot,
183 0 : fd_gui_printf_estimated_slot,
184 0 : fd_gui_printf_live_tile_timers,
185 0 : };
186 :
187 0 : ulong printers_len = sizeof(printers) / sizeof(printers[0]);
188 0 : for( ulong i=0UL; i<printers_len; i++ ) {
189 0 : printers[ i ]( gui );
190 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
191 0 : }
192 :
193 0 : if( FD_LIKELY( gui->block_engine.has_block_engine ) ) {
194 0 : fd_gui_printf_block_engine( gui );
195 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
196 0 : }
197 :
198 0 : for( ulong i=0UL; i<2UL; i++ ) {
199 0 : if( FD_LIKELY( gui->epoch.has_epoch[ i ] ) ) {
200 0 : fd_gui_printf_skip_rate( gui, i );
201 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
202 0 : fd_gui_printf_epoch( gui, i );
203 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
204 0 : }
205 0 : }
206 :
207 : /* Print peers last because it's the largest message and would
208 : block other information. */
209 0 : fd_gui_printf_peers_all( gui );
210 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
211 0 : }
212 :
213 : static void
214 0 : fd_gui_tile_timers_snap( fd_gui_t * gui ) {
215 0 : fd_gui_tile_timers_t * cur = gui->summary.tile_timers_snap[ gui->summary.tile_timers_snap_idx ];
216 0 : gui->summary.tile_timers_snap_idx = (gui->summary.tile_timers_snap_idx+1UL)%FD_GUI_TILE_TIMER_SNAP_CNT;
217 0 : for( ulong i=0UL; i<gui->topo->tile_cnt; i++ ) {
218 0 : fd_topo_tile_t * tile = &gui->topo->tiles[ i ];
219 0 : if ( FD_UNLIKELY( !tile->metrics ) ) {
220 : /* bench tiles might not have been booted initially.
221 : This check shouldn't be necessary if all tiles barrier after boot. */
222 : // TODO(FIXME) this probably isn't the right fix but it makes fddev bench work for now
223 0 : return;
224 0 : }
225 0 : volatile ulong const * tile_metrics = fd_metrics_tile( tile->metrics );
226 :
227 0 : cur[ i ].caughtup_housekeeping_ticks = tile_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_HOUSEKEEPING ) ];
228 0 : cur[ i ].processing_housekeeping_ticks = tile_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_HOUSEKEEPING ) ];
229 0 : cur[ i ].backpressure_housekeeping_ticks = tile_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_HOUSEKEEPING ) ];
230 0 : cur[ i ].caughtup_prefrag_ticks = tile_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_PREFRAG ) ];
231 0 : cur[ i ].processing_prefrag_ticks = tile_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_PREFRAG ) ];
232 0 : cur[ i ].backpressure_prefrag_ticks = tile_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
233 0 : cur[ i ].caughtup_postfrag_ticks = tile_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ];
234 0 : cur[ i ].processing_postfrag_ticks = tile_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_POSTFRAG ) ];
235 0 : }
236 0 : }
237 :
238 : static void
239 0 : fd_gui_estimated_tps_snap( fd_gui_t * gui ) {
240 0 : ulong total_txn_cnt = 0UL;
241 0 : ulong vote_txn_cnt = 0UL;
242 0 : ulong nonvote_failed_txn_cnt = 0UL;
243 :
244 0 : for( ulong i=0UL; i<fd_ulong_min( gui->summary.slot_completed+1UL, FD_GUI_SLOTS_CNT ); i++ ) {
245 0 : ulong _slot = gui->summary.slot_completed-i;
246 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
247 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX || slot->slot!=_slot ) ) break; /* Slot no longer exists, no TPS. */
248 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. */
249 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. */
250 0 : if( FD_UNLIKELY( slot->skipped ) ) continue; /* Skipped slots don't count to TPS. */
251 :
252 0 : total_txn_cnt += slot->total_txn_cnt;
253 0 : vote_txn_cnt += slot->vote_txn_cnt;
254 0 : nonvote_failed_txn_cnt += slot->nonvote_failed_txn_cnt;
255 0 : }
256 :
257 0 : gui->summary.estimated_tps_history[ gui->summary.estimated_tps_history_idx ][ 0 ] = total_txn_cnt;
258 0 : gui->summary.estimated_tps_history[ gui->summary.estimated_tps_history_idx ][ 1 ] = vote_txn_cnt;
259 0 : gui->summary.estimated_tps_history[ gui->summary.estimated_tps_history_idx ][ 2 ] = nonvote_failed_txn_cnt;
260 0 : gui->summary.estimated_tps_history_idx = (gui->summary.estimated_tps_history_idx+1UL) % FD_GUI_TPS_HISTORY_SAMPLE_CNT;
261 0 : }
262 :
263 : /* Snapshot all of the data from metrics to construct a view of the
264 : transaction waterfall.
265 :
266 : Tiles are sampled in reverse pipeline order: this helps prevent data
267 : discrepancies where a later tile has "seen" more transactions than an
268 : earlier tile, which shouldn't typically happen. */
269 :
270 : static void
271 : fd_gui_txn_waterfall_snap( fd_gui_t * gui,
272 0 : fd_gui_txn_waterfall_t * cur ) {
273 0 : fd_topo_t * topo = gui->topo;
274 :
275 0 : cur->out.block_success = 0UL;
276 0 : cur->out.block_fail = 0UL;
277 :
278 0 : cur->out.bank_invalid = 0UL;
279 0 : for( ulong i=0UL; i<gui->summary.bank_tile_cnt; i++ ) {
280 0 : fd_topo_tile_t const * bank = &topo->tiles[ fd_topo_find_tile( topo, "bank", i ) ];
281 :
282 0 : volatile ulong const * bank_metrics = fd_metrics_tile( bank->metrics );
283 :
284 0 : cur->out.block_success += bank_metrics[ MIDX( COUNTER, BANK, SUCCESSFUL_TRANSACTIONS ) ];
285 :
286 0 : cur->out.block_fail +=
287 0 : bank_metrics[ MIDX( COUNTER, BANK, EXECUTED_FAILED_TRANSACTIONS ) ]
288 0 : + bank_metrics[ MIDX( COUNTER, BANK, FEE_ONLY_TRANSACTIONS ) ];
289 :
290 0 : cur->out.bank_invalid +=
291 0 : bank_metrics[ MIDX( COUNTER, BANK, TRANSACTION_LOAD_ADDRESS_TABLES_SLOT_HASHES_SYSVAR_NOT_FOUND ) ]
292 0 : + bank_metrics[ MIDX( COUNTER, BANK, TRANSACTION_LOAD_ADDRESS_TABLES_ACCOUNT_NOT_FOUND ) ]
293 0 : + bank_metrics[ MIDX( COUNTER, BANK, TRANSACTION_LOAD_ADDRESS_TABLES_INVALID_ACCOUNT_OWNER ) ]
294 0 : + bank_metrics[ MIDX( COUNTER, BANK, TRANSACTION_LOAD_ADDRESS_TABLES_INVALID_ACCOUNT_DATA ) ]
295 0 : + bank_metrics[ MIDX( COUNTER, BANK, TRANSACTION_LOAD_ADDRESS_TABLES_INVALID_INDEX ) ];
296 :
297 0 : cur->out.bank_invalid +=
298 0 : bank_metrics[ MIDX( COUNTER, BANK, PROCESSING_FAILED ) ];
299 0 : }
300 :
301 :
302 0 : fd_topo_tile_t const * pack = &topo->tiles[ fd_topo_find_tile( topo, "pack", 0UL ) ];
303 0 : volatile ulong const * pack_metrics = fd_metrics_tile( pack->metrics );
304 :
305 0 : cur->out.pack_invalid_bundle =
306 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_DROPPED_PARTIAL_BUNDLE ) ]
307 0 : + pack_metrics[ MIDX( COUNTER, PACK, BUNDLE_CRANK_STATUS_INSERTION_FAILED ) ]
308 0 : + pack_metrics[ MIDX( COUNTER, PACK, BUNDLE_CRANK_STATUS_CREATION_FAILED ) ];
309 :
310 0 : cur->out.pack_invalid =
311 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_NONCE_CONFLICT ) ]
312 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_BUNDLE_BLACKLIST ) ]
313 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_INVALID_NONCE ) ]
314 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_WRITE_SYSVAR ) ]
315 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_ESTIMATION_FAIL ) ]
316 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_DUPLICATE_ACCOUNT ) ]
317 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_TOO_MANY_ACCOUNTS ) ]
318 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_TOO_LARGE ) ]
319 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_ADDR_LUT ) ]
320 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_UNAFFORDABLE ) ]
321 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_DUPLICATE ) ]
322 0 : - pack_metrics[ MIDX( COUNTER, PACK, BUNDLE_CRANK_STATUS_INSERTION_FAILED ) ]; /* so we don't double count this, since its already accounted for in invalid_bundle */
323 :
324 0 : cur->out.pack_expired = pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_EXPIRED ) ] +
325 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_EXPIRED ) ] +
326 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_DELETED ) ] +
327 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_NONCE_PRIORITY ) ];
328 :
329 0 : cur->out.pack_leader_slow = pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_PRIORITY ) ];
330 :
331 0 : cur->out.pack_wait_full =
332 0 : pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_DROPPED_FROM_EXTRA ) ];
333 :
334 0 : cur->out.pack_retained = pack_metrics[ MIDX( GAUGE, PACK, AVAILABLE_TRANSACTIONS ) ];
335 :
336 0 : ulong inserted_to_extra = pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_TO_EXTRA ) ];
337 0 : ulong inserted_from_extra = pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_INSERTED_FROM_EXTRA ) ]
338 0 : + pack_metrics[ MIDX( COUNTER, PACK, TRANSACTION_DROPPED_FROM_EXTRA ) ];
339 0 : cur->out.pack_retained += fd_ulong_if( inserted_to_extra>=inserted_from_extra, inserted_to_extra-inserted_from_extra, 0UL );
340 :
341 0 : cur->out.resolv_lut_failed = 0UL;
342 0 : cur->out.resolv_expired = 0UL;
343 0 : cur->out.resolv_ancient = 0UL;
344 0 : cur->out.resolv_no_ledger = 0UL;
345 0 : cur->out.resolv_retained = 0UL;
346 0 : for( ulong i=0UL; i<gui->summary.resolv_tile_cnt; i++ ) {
347 0 : fd_topo_tile_t const * resolv = &topo->tiles[ fd_topo_find_tile( topo, "resolv", i ) ];
348 0 : volatile ulong const * resolv_metrics = fd_metrics_tile( resolv->metrics );
349 :
350 0 : cur->out.resolv_no_ledger += resolv_metrics[ MIDX( COUNTER, RESOLV, NO_BANK_DROP ) ];
351 0 : cur->out.resolv_expired += resolv_metrics[ MIDX( COUNTER, RESOLV, BLOCKHASH_EXPIRED ) ]
352 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, TRANSACTION_BUNDLE_PEER_FAILURE ) ];
353 0 : cur->out.resolv_lut_failed += resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_ACCOUNT_NOT_FOUND ) ]
354 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_INVALID_ACCOUNT_OWNER ) ]
355 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_INVALID_ACCOUNT_DATA ) ]
356 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_ACCOUNT_UNINITIALIZED ) ]
357 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, LUT_RESOLVED_INVALID_LOOKUP_INDEX ) ];
358 0 : cur->out.resolv_ancient += resolv_metrics[ MIDX( COUNTER, RESOLV, STASH_OPERATION_OVERRUN ) ];
359 :
360 0 : ulong inserted_to_resolv = resolv_metrics[ MIDX( COUNTER, RESOLV, STASH_OPERATION_INSERTED ) ];
361 0 : ulong removed_from_resolv = resolv_metrics[ MIDX( COUNTER, RESOLV, STASH_OPERATION_OVERRUN ) ]
362 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, STASH_OPERATION_PUBLISHED ) ]
363 0 : + resolv_metrics[ MIDX( COUNTER, RESOLV, STASH_OPERATION_REMOVED ) ];
364 0 : cur->out.resolv_retained += fd_ulong_if( inserted_to_resolv>=removed_from_resolv, inserted_to_resolv-removed_from_resolv, 0UL );
365 0 : }
366 :
367 :
368 0 : fd_topo_tile_t const * dedup = &topo->tiles[ fd_topo_find_tile( topo, "dedup", 0UL ) ];
369 0 : volatile ulong const * dedup_metrics = fd_metrics_tile( dedup->metrics );
370 :
371 0 : cur->out.dedup_duplicate = dedup_metrics[ MIDX( COUNTER, DEDUP, TRANSACTION_DEDUP_FAILURE ) ]
372 0 : + dedup_metrics[ MIDX( COUNTER, DEDUP, TRANSACTION_BUNDLE_PEER_FAILURE ) ];
373 :
374 :
375 0 : cur->out.verify_overrun = 0UL;
376 0 : cur->out.verify_duplicate = 0UL;
377 0 : cur->out.verify_parse = 0UL;
378 0 : cur->out.verify_failed = 0UL;
379 :
380 0 : for( ulong i=0UL; i<gui->summary.verify_tile_cnt; i++ ) {
381 0 : fd_topo_tile_t const * verify = &topo->tiles[ fd_topo_find_tile( topo, "verify", i ) ];
382 0 : volatile ulong const * verify_metrics = fd_metrics_tile( verify->metrics );
383 :
384 0 : for( ulong j=0UL; j<gui->summary.quic_tile_cnt; j++ ) {
385 : /* TODO: Not precise... even if 1 frag gets skipped, it could have been for this verify tile. */
386 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;
387 0 : cur->out.verify_overrun += fd_metrics_link_in( verify->metrics, j )[ FD_METRICS_COUNTER_LINK_OVERRUN_READING_FRAG_COUNT_OFF ];
388 0 : }
389 :
390 0 : cur->out.verify_failed += verify_metrics[ MIDX( COUNTER, VERIFY, TRANSACTION_VERIFY_FAILURE ) ] +
391 0 : verify_metrics[ MIDX( COUNTER, VERIFY, TRANSACTION_BUNDLE_PEER_FAILURE ) ];
392 0 : cur->out.verify_parse += verify_metrics[ MIDX( COUNTER, VERIFY, TRANSACTION_PARSE_FAILURE ) ];
393 0 : cur->out.verify_duplicate += verify_metrics[ MIDX( COUNTER, VERIFY, TRANSACTION_DEDUP_FAILURE ) ];
394 0 : }
395 :
396 :
397 0 : cur->out.quic_overrun = 0UL;
398 0 : cur->out.quic_frag_drop = 0UL;
399 0 : cur->out.quic_abandoned = 0UL;
400 0 : cur->out.tpu_quic_invalid = 0UL;
401 0 : cur->out.tpu_udp_invalid = 0UL;
402 0 : for( ulong i=0UL; i<gui->summary.quic_tile_cnt; i++ ) {
403 0 : fd_topo_tile_t const * quic = &topo->tiles[ fd_topo_find_tile( topo, "quic", i ) ];
404 0 : volatile ulong * quic_metrics = fd_metrics_tile( quic->metrics );
405 :
406 0 : cur->out.tpu_udp_invalid += quic_metrics[ MIDX( COUNTER, QUIC, LEGACY_TXN_UNDERSZ ) ];
407 0 : cur->out.tpu_udp_invalid += quic_metrics[ MIDX( COUNTER, QUIC, LEGACY_TXN_OVERSZ ) ];
408 0 : cur->out.tpu_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC, PKT_UNDERSZ ) ];
409 0 : cur->out.tpu_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC, PKT_OVERSZ ) ];
410 0 : cur->out.tpu_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC, TXN_OVERSZ ) ];
411 0 : cur->out.tpu_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC, PKT_CRYPTO_FAILED ) ];
412 0 : cur->out.tpu_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC, PKT_NO_CONN ) ];
413 0 : cur->out.tpu_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC, PKT_NET_HEADER_INVALID ) ];
414 0 : cur->out.tpu_quic_invalid += quic_metrics[ MIDX( COUNTER, QUIC, PKT_QUIC_HEADER_INVALID ) ];
415 0 : cur->out.quic_abandoned += quic_metrics[ MIDX( COUNTER, QUIC, TXNS_ABANDONED ) ];
416 0 : cur->out.quic_frag_drop += quic_metrics[ MIDX( COUNTER, QUIC, TXNS_OVERRUN ) ];
417 :
418 0 : for( ulong j=0UL; j<gui->summary.net_tile_cnt; j++ ) {
419 : /* TODO: Not precise... net frags that were skipped might not have been destined for QUIC tile */
420 : /* TODO: Not precise... even if 1 frag gets skipped, it could have been for this QUIC tile */
421 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;
422 0 : cur->out.quic_overrun += fd_metrics_link_in( quic->metrics, j )[ FD_METRICS_COUNTER_LINK_OVERRUN_READING_FRAG_COUNT_OFF ];
423 0 : }
424 0 : }
425 :
426 0 : cur->out.net_overrun = 0UL;
427 0 : for( ulong i=0UL; i<gui->summary.net_tile_cnt; i++ ) {
428 0 : fd_topo_tile_t const * net = &topo->tiles[ fd_topo_find_tile( topo, "net", i ) ];
429 0 : volatile ulong * net_metrics = fd_metrics_tile( net->metrics );
430 :
431 0 : cur->out.net_overrun += net_metrics[ MIDX( COUNTER, NET, XDP_RX_RING_FULL ) ];
432 0 : cur->out.net_overrun += net_metrics[ MIDX( COUNTER, NET, XDP_RX_DROPPED_OTHER ) ];
433 0 : cur->out.net_overrun += net_metrics[ MIDX( COUNTER, NET, XDP_RX_FILL_RING_EMPTY_DESCS ) ];
434 0 : }
435 :
436 0 : ulong bundle_txns_received = 0UL;
437 0 : ulong bundle_tile_idx = fd_topo_find_tile( topo, "bundle", 0UL );
438 0 : if( FD_LIKELY( bundle_tile_idx!=ULONG_MAX ) ) {
439 0 : fd_topo_tile_t const * bundle = &topo->tiles[ bundle_tile_idx ];
440 0 : volatile ulong const * bundle_metrics = fd_metrics_tile( bundle->metrics );
441 :
442 0 : bundle_txns_received = bundle_metrics[ MIDX( COUNTER, BUNDLE, TRANSACTION_RECEIVED ) ];
443 0 : }
444 :
445 0 : cur->in.pack_cranked =
446 0 : pack_metrics[ MIDX( COUNTER, PACK, BUNDLE_CRANK_STATUS_INSERTED ) ]
447 0 : + pack_metrics[ MIDX( COUNTER, PACK, BUNDLE_CRANK_STATUS_INSERTION_FAILED ) ]
448 0 : + pack_metrics[ MIDX( COUNTER, PACK, BUNDLE_CRANK_STATUS_CREATION_FAILED ) ];
449 :
450 0 : cur->in.gossip = dedup_metrics[ MIDX( COUNTER, DEDUP, GOSSIPED_VOTES_RECEIVED ) ];
451 0 : cur->in.quic = cur->out.tpu_quic_invalid +
452 0 : cur->out.quic_overrun +
453 0 : cur->out.quic_frag_drop +
454 0 : cur->out.quic_abandoned +
455 0 : cur->out.net_overrun;
456 0 : cur->in.udp = cur->out.tpu_udp_invalid;
457 0 : cur->in.block_engine = bundle_txns_received;
458 0 : for( ulong i=0UL; i<gui->summary.quic_tile_cnt; i++ ) {
459 0 : fd_topo_tile_t const * quic = &topo->tiles[ fd_topo_find_tile( topo, "quic", i ) ];
460 0 : volatile ulong * quic_metrics = fd_metrics_tile( quic->metrics );
461 :
462 0 : cur->in.quic += quic_metrics[ MIDX( COUNTER, QUIC, TXNS_RECEIVED_QUIC_FAST ) ];
463 0 : cur->in.quic += quic_metrics[ MIDX( COUNTER, QUIC, TXNS_RECEIVED_QUIC_FRAG ) ];
464 0 : cur->in.udp += quic_metrics[ MIDX( COUNTER, QUIC, TXNS_RECEIVED_UDP ) ];
465 0 : }
466 0 : }
467 :
468 : static void
469 : fd_gui_tile_stats_snap( fd_gui_t * gui,
470 : fd_gui_txn_waterfall_t const * waterfall,
471 0 : fd_gui_tile_stats_t * stats ) {
472 0 : fd_topo_t const * topo = gui->topo;
473 :
474 0 : stats->sample_time_nanos = fd_log_wallclock();
475 :
476 0 : stats->net_in_rx_bytes = 0UL;
477 0 : stats->net_out_tx_bytes = 0UL;
478 0 : for( ulong i=0UL; i<gui->summary.net_tile_cnt; i++ ) {
479 0 : fd_topo_tile_t const * net = &topo->tiles[ fd_topo_find_tile( topo, "net", i ) ];
480 0 : volatile ulong * net_metrics = fd_metrics_tile( net->metrics );
481 :
482 0 : stats->net_in_rx_bytes += net_metrics[ MIDX( COUNTER, NET, RX_BYTES_TOTAL ) ];
483 0 : stats->net_out_tx_bytes += net_metrics[ MIDX( COUNTER, NET, TX_BYTES_TOTAL ) ];
484 0 : }
485 :
486 0 : for( ulong i=0UL; i<gui->summary.sock_tile_cnt; i++ ) {
487 0 : fd_topo_tile_t const * sock = &topo->tiles[ fd_topo_find_tile( topo, "sock", i ) ];
488 0 : volatile ulong * sock_metrics = fd_metrics_tile( sock->metrics );
489 :
490 0 : stats->net_in_rx_bytes += sock_metrics[ MIDX( COUNTER, SOCK, RX_BYTES_TOTAL ) ];
491 0 : stats->net_out_tx_bytes += sock_metrics[ MIDX( COUNTER, SOCK, TX_BYTES_TOTAL ) ];
492 0 : }
493 :
494 0 : stats->quic_conn_cnt = 0UL;
495 0 : for( ulong i=0UL; i<gui->summary.quic_tile_cnt; i++ ) {
496 0 : fd_topo_tile_t const * quic = &topo->tiles[ fd_topo_find_tile( topo, "quic", i ) ];
497 0 : volatile ulong * quic_metrics = fd_metrics_tile( quic->metrics );
498 :
499 0 : stats->quic_conn_cnt += quic_metrics[ MIDX( GAUGE, QUIC, CONNECTIONS_ALLOC ) ];
500 0 : }
501 :
502 0 : ulong bundle_tile_idx = fd_topo_find_tile( topo, "bundle", 0UL );
503 0 : if( FD_LIKELY( bundle_tile_idx!=ULONG_MAX ) ) {
504 0 : fd_topo_tile_t const * bundle = &topo->tiles[ bundle_tile_idx ];
505 0 : volatile ulong * bundle_metrics = fd_metrics_tile( bundle->metrics );
506 0 : stats->bundle_rtt_smoothed_nanos = bundle_metrics[ MIDX( GAUGE, BUNDLE, RTT_SMOOTHED ) ];
507 :
508 0 : gui->bundle_rx_delay_hist_current->sum = bundle_metrics[ MIDX( HISTOGRAM, BUNDLE, MESSAGE_RX_DELAY_NANOS ) + FD_HISTF_BUCKET_CNT ];
509 0 : for( ulong b=0; b<FD_HISTF_BUCKET_CNT; b++ ) gui->bundle_rx_delay_hist_current->counts[ b ] = bundle_metrics[ MIDX( HISTOGRAM, BUNDLE, MESSAGE_RX_DELAY_NANOS ) + b ];
510 0 : }
511 :
512 0 : stats->verify_drop_cnt = waterfall->out.verify_duplicate +
513 0 : waterfall->out.verify_parse +
514 0 : waterfall->out.verify_failed;
515 0 : stats->verify_total_cnt = waterfall->in.gossip +
516 0 : waterfall->in.quic +
517 0 : waterfall->in.udp -
518 0 : waterfall->out.net_overrun -
519 0 : waterfall->out.tpu_quic_invalid -
520 0 : waterfall->out.tpu_udp_invalid -
521 0 : waterfall->out.quic_abandoned -
522 0 : waterfall->out.quic_frag_drop -
523 0 : waterfall->out.quic_overrun -
524 0 : waterfall->out.verify_overrun;
525 0 : stats->dedup_drop_cnt = waterfall->out.dedup_duplicate;
526 0 : stats->dedup_total_cnt = stats->verify_total_cnt -
527 0 : waterfall->out.verify_duplicate -
528 0 : waterfall->out.verify_parse -
529 0 : waterfall->out.verify_failed;
530 :
531 0 : fd_topo_tile_t const * pack = &topo->tiles[ fd_topo_find_tile( topo, "pack", 0UL ) ];
532 0 : volatile ulong const * pack_metrics = fd_metrics_tile( pack->metrics );
533 0 : stats->pack_buffer_cnt = pack_metrics[ MIDX( GAUGE, PACK, AVAILABLE_TRANSACTIONS ) ];
534 0 : stats->pack_buffer_capacity = pack->pack.max_pending_transactions;
535 :
536 0 : stats->bank_txn_exec_cnt = waterfall->out.block_fail + waterfall->out.block_success;
537 0 : }
538 :
539 : int
540 0 : fd_gui_poll( fd_gui_t * gui ) {
541 0 : long now = fd_log_wallclock();
542 :
543 0 : int did_work = 0;
544 :
545 0 : if( FD_LIKELY( now>gui->next_sample_400millis ) ) {
546 0 : fd_gui_estimated_tps_snap( gui );
547 0 : fd_gui_printf_estimated_tps( gui );
548 0 : fd_http_server_ws_broadcast( gui->http );
549 :
550 0 : gui->next_sample_400millis += 400L*1000L*1000L;
551 0 : did_work = 1;
552 0 : }
553 :
554 0 : if( FD_LIKELY( now>gui->next_sample_100millis ) ) {
555 0 : fd_gui_txn_waterfall_snap( gui, gui->summary.txn_waterfall_current );
556 0 : fd_gui_printf_live_txn_waterfall( gui, gui->summary.txn_waterfall_reference, gui->summary.txn_waterfall_current, 0UL /* TODO: REAL NEXT LEADER SLOT */ );
557 0 : fd_http_server_ws_broadcast( gui->http );
558 :
559 0 : *gui->summary.tile_stats_reference = *gui->summary.tile_stats_current;
560 0 : fd_gui_tile_stats_snap( gui, gui->summary.txn_waterfall_current, gui->summary.tile_stats_current );
561 0 : fd_gui_printf_live_tile_stats( gui, gui->summary.tile_stats_reference, gui->summary.tile_stats_current );
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 : if( FD_UNLIKELY( gui->gossip.peer_cnt == FD_GUI_MAX_PEER_CNT ) ) {
585 0 : FD_LOG_DEBUG(("gossip peer cnt exceeds 40200 %lu, ignoring additional entries", gui->gossip.peer_cnt ));
586 0 : return;
587 0 : }
588 0 : ulong const * header = (ulong const *)fd_type_pun_const( msg );
589 0 : ulong peer_cnt = header[ 0 ];
590 :
591 0 : FD_TEST( peer_cnt<=FD_GUI_MAX_PEER_CNT );
592 :
593 0 : ulong added_cnt = 0UL;
594 0 : ulong added[ FD_GUI_MAX_PEER_CNT ] = {0};
595 :
596 0 : ulong update_cnt = 0UL;
597 0 : ulong updated[ FD_GUI_MAX_PEER_CNT ] = {0};
598 :
599 0 : ulong removed_cnt = 0UL;
600 0 : fd_pubkey_t removed[ FD_GUI_MAX_PEER_CNT ] = {0};
601 :
602 0 : uchar const * data = (uchar const *)(header+1UL);
603 0 : for( ulong i=0UL; i<gui->gossip.peer_cnt; i++ ) {
604 0 : int found = 0;
605 0 : for( ulong j=0UL; j<peer_cnt; j++ ) {
606 0 : if( FD_UNLIKELY( !memcmp( gui->gossip.peers[ i ].pubkey, data+j*(58UL+12UL*6UL), 32UL ) ) ) {
607 0 : found = 1;
608 0 : break;
609 0 : }
610 0 : }
611 :
612 0 : if( FD_UNLIKELY( !found ) ) {
613 0 : fd_memcpy( removed[ removed_cnt++ ].uc, gui->gossip.peers[ i ].pubkey->uc, 32UL );
614 0 : if( FD_LIKELY( i+1UL!=gui->gossip.peer_cnt ) ) {
615 0 : gui->gossip.peers[ i ] = gui->gossip.peers[ gui->gossip.peer_cnt-1UL ];
616 0 : gui->gossip.peer_cnt--;
617 0 : i--;
618 0 : }
619 0 : }
620 0 : }
621 :
622 0 : ulong before_peer_cnt = gui->gossip.peer_cnt;
623 0 : for( ulong i=0UL; i<peer_cnt; i++ ) {
624 0 : int found = 0;
625 0 : ulong found_idx = 0;
626 0 : for( ulong j=0UL; j<gui->gossip.peer_cnt; j++ ) {
627 0 : if( FD_UNLIKELY( !memcmp( gui->gossip.peers[ j ].pubkey, data+i*(58UL+12UL*6UL), 32UL ) ) ) {
628 0 : found_idx = j;
629 0 : found = 1;
630 0 : break;
631 0 : }
632 0 : }
633 :
634 0 : if( FD_UNLIKELY( !found ) ) {
635 0 : fd_memcpy( gui->gossip.peers[ gui->gossip.peer_cnt ].pubkey->uc, data+i*(58UL+12UL*6UL), 32UL );
636 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].wallclock = *(ulong const *)(data+i*(58UL+12UL*6UL)+32UL);
637 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].shred_version = *(ushort const *)(data+i*(58UL+12UL*6UL)+40UL);
638 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].has_version = *(data+i*(58UL+12UL*6UL)+42UL);
639 0 : if( FD_LIKELY( gui->gossip.peers[ gui->gossip.peer_cnt ].has_version ) ) {
640 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.major = *(ushort const *)(data+i*(58UL+12UL*6UL)+43UL);
641 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.minor = *(ushort const *)(data+i*(58UL+12UL*6UL)+45UL);
642 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.patch = *(ushort const *)(data+i*(58UL+12UL*6UL)+47UL);
643 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.has_commit = *(data+i*(58UL+12UL*6UL)+49UL);
644 0 : if( FD_LIKELY( gui->gossip.peers[ gui->gossip.peer_cnt ].version.has_commit ) ) {
645 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.commit = *(uint const *)(data+i*(58UL+12UL*6UL)+50UL);
646 0 : }
647 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].version.feature_set = *(uint const *)(data+i*(58UL+12UL*6UL)+54UL);
648 0 : }
649 :
650 0 : for( ulong j=0UL; j<12UL; j++ ) {
651 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].sockets[ j ].ipv4 = *(uint const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL);
652 0 : gui->gossip.peers[ gui->gossip.peer_cnt ].sockets[ j ].port = *(ushort const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL+4UL);
653 0 : }
654 :
655 0 : gui->gossip.peer_cnt++;
656 0 : } else {
657 0 : int peer_updated = gui->gossip.peers[ found_idx ].shred_version!=*(ushort const *)(data+i*(58UL+12UL*6UL)+40UL) ||
658 : // gui->gossip.peers[ found_idx ].wallclock!=*(ulong const *)(data+i*(58UL+12UL*6UL)+32UL) ||
659 0 : gui->gossip.peers[ found_idx ].has_version!=*(data+i*(58UL+12UL*6UL)+42UL);
660 :
661 0 : if( FD_LIKELY( !peer_updated && gui->gossip.peers[ found_idx ].has_version ) ) {
662 0 : peer_updated = gui->gossip.peers[ found_idx ].version.major!=*(ushort const *)(data+i*(58UL+12UL*6UL)+43UL) ||
663 0 : gui->gossip.peers[ found_idx ].version.minor!=*(ushort const *)(data+i*(58UL+12UL*6UL)+45UL) ||
664 0 : gui->gossip.peers[ found_idx ].version.patch!=*(ushort const *)(data+i*(58UL+12UL*6UL)+47UL) ||
665 0 : gui->gossip.peers[ found_idx ].version.has_commit!=*(data+i*(58UL+12UL*6UL)+49UL) ||
666 0 : (gui->gossip.peers[ found_idx ].version.has_commit && gui->gossip.peers[ found_idx ].version.commit!=*(uint const *)(data+i*(58UL+12UL*6UL)+50UL)) ||
667 0 : gui->gossip.peers[ found_idx ].version.feature_set!=*(uint const *)(data+i*(58UL+12UL*6UL)+54UL);
668 0 : }
669 :
670 0 : if( FD_LIKELY( !peer_updated ) ) {
671 0 : for( ulong j=0UL; j<12UL; j++ ) {
672 0 : peer_updated = gui->gossip.peers[ found_idx ].sockets[ j ].ipv4!=*(uint const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL) ||
673 0 : gui->gossip.peers[ found_idx ].sockets[ j ].port!=*(ushort const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL+4UL);
674 0 : if( FD_LIKELY( peer_updated ) ) break;
675 0 : }
676 0 : }
677 :
678 0 : if( FD_UNLIKELY( peer_updated ) ) {
679 0 : updated[ update_cnt++ ] = found_idx;
680 0 : gui->gossip.peers[ found_idx ].shred_version = *(ushort const *)(data+i*(58UL+12UL*6UL)+40UL);
681 0 : gui->gossip.peers[ found_idx ].wallclock = *(ulong const *)(data+i*(58UL+12UL*6UL)+32UL);
682 0 : gui->gossip.peers[ found_idx ].has_version = *(data+i*(58UL+12UL*6UL)+42UL);
683 0 : if( FD_LIKELY( gui->gossip.peers[ found_idx ].has_version ) ) {
684 0 : gui->gossip.peers[ found_idx ].version.major = *(ushort const *)(data+i*(58UL+12UL*6UL)+43UL);
685 0 : gui->gossip.peers[ found_idx ].version.minor = *(ushort const *)(data+i*(58UL+12UL*6UL)+45UL);
686 0 : gui->gossip.peers[ found_idx ].version.patch = *(ushort const *)(data+i*(58UL+12UL*6UL)+47UL);
687 0 : gui->gossip.peers[ found_idx ].version.has_commit = *(data+i*(58UL+12UL*6UL)+49UL);
688 0 : if( FD_LIKELY( gui->gossip.peers[ found_idx ].version.has_commit ) ) {
689 0 : gui->gossip.peers[ found_idx ].version.commit = *(uint const *)(data+i*(58UL+12UL*6UL)+50UL);
690 0 : }
691 0 : gui->gossip.peers[ found_idx ].version.feature_set = *(uint const *)(data+i*(58UL+12UL*6UL)+54UL);
692 0 : }
693 :
694 0 : for( ulong j=0UL; j<12UL; j++ ) {
695 0 : gui->gossip.peers[ found_idx ].sockets[ j ].ipv4 = *(uint const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL);
696 0 : gui->gossip.peers[ found_idx ].sockets[ j ].port = *(ushort const *)(data+i*(58UL+12UL*6UL)+58UL+j*6UL+4UL);
697 0 : }
698 0 : }
699 0 : }
700 0 : }
701 :
702 0 : added_cnt = gui->gossip.peer_cnt - before_peer_cnt;
703 0 : for( ulong i=before_peer_cnt; i<gui->gossip.peer_cnt; i++ ) added[ i-before_peer_cnt ] = i;
704 :
705 0 : fd_gui_printf_peers_gossip_update( gui, updated, update_cnt, removed, removed_cnt, added, added_cnt );
706 0 : fd_http_server_ws_broadcast( gui->http );
707 0 : }
708 :
709 : static void
710 : fd_gui_handle_vote_account_update( fd_gui_t * gui,
711 0 : uchar const * msg ) {
712 0 : if( FD_UNLIKELY( gui->vote_account.vote_account_cnt==FD_GUI_MAX_PEER_CNT ) ) {
713 0 : FD_LOG_DEBUG(("vote account cnt exceeds 40200 %lu, ignoring additional entries", gui->vote_account.vote_account_cnt ));
714 0 : return;
715 0 : }
716 0 : ulong const * header = (ulong const *)fd_type_pun_const( msg );
717 0 : ulong peer_cnt = header[ 0 ];
718 :
719 0 : FD_TEST( peer_cnt<=FD_GUI_MAX_PEER_CNT );
720 :
721 0 : ulong added_cnt = 0UL;
722 0 : ulong added[ FD_GUI_MAX_PEER_CNT ] = {0};
723 :
724 0 : ulong update_cnt = 0UL;
725 0 : ulong updated[ FD_GUI_MAX_PEER_CNT ] = {0};
726 :
727 0 : ulong removed_cnt = 0UL;
728 0 : fd_pubkey_t removed[ FD_GUI_MAX_PEER_CNT ] = {0};
729 :
730 0 : uchar const * data = (uchar const *)(header+1UL);
731 0 : for( ulong i=0UL; i<gui->vote_account.vote_account_cnt; i++ ) {
732 0 : int found = 0;
733 0 : for( ulong j=0UL; j<peer_cnt; j++ ) {
734 0 : if( FD_UNLIKELY( !memcmp( gui->vote_account.vote_accounts[ i ].vote_account, data+j*112UL, 32UL ) ) ) {
735 0 : found = 1;
736 0 : break;
737 0 : }
738 0 : }
739 :
740 0 : if( FD_UNLIKELY( !found ) ) {
741 0 : fd_memcpy( removed[ removed_cnt++ ].uc, gui->vote_account.vote_accounts[ i ].vote_account->uc, 32UL );
742 0 : if( FD_LIKELY( i+1UL!=gui->vote_account.vote_account_cnt ) ) {
743 0 : gui->vote_account.vote_accounts[ i ] = gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt-1UL ];
744 0 : gui->vote_account.vote_account_cnt--;
745 0 : i--;
746 0 : }
747 0 : }
748 0 : }
749 :
750 0 : ulong before_peer_cnt = gui->vote_account.vote_account_cnt;
751 0 : for( ulong i=0UL; i<peer_cnt; i++ ) {
752 0 : int found = 0;
753 0 : ulong found_idx;
754 0 : for( ulong j=0UL; j<gui->vote_account.vote_account_cnt; j++ ) {
755 0 : if( FD_UNLIKELY( !memcmp( gui->vote_account.vote_accounts[ j ].vote_account, data+i*112UL, 32UL ) ) ) {
756 0 : found_idx = j;
757 0 : found = 1;
758 0 : break;
759 0 : }
760 0 : }
761 :
762 0 : if( FD_UNLIKELY( !found ) ) {
763 0 : fd_memcpy( gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].vote_account->uc, data+i*112UL, 32UL );
764 0 : fd_memcpy( gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].pubkey->uc, data+i*112UL+32UL, 32UL );
765 :
766 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].activated_stake = *(ulong const *)(data+i*112UL+64UL);
767 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].last_vote = *(ulong const *)(data+i*112UL+72UL);
768 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].root_slot = *(ulong const *)(data+i*112UL+80UL);
769 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].epoch_credits = *(ulong const *)(data+i*112UL+88UL);
770 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].commission = *(data+i*112UL+96UL);
771 0 : gui->vote_account.vote_accounts[ gui->vote_account.vote_account_cnt ].delinquent = *(data+i*112UL+97UL);
772 :
773 0 : gui->vote_account.vote_account_cnt++;
774 0 : } else {
775 0 : int peer_updated =
776 0 : memcmp( gui->vote_account.vote_accounts[ found_idx ].pubkey->uc, data+i*112UL+32UL, 32UL ) ||
777 0 : gui->vote_account.vote_accounts[ found_idx ].activated_stake != *(ulong const *)(data+i*112UL+64UL) ||
778 : // gui->vote_account.vote_accounts[ found_idx ].last_vote != *(ulong const *)(data+i*112UL+72UL) ||
779 : // gui->vote_account.vote_accounts[ found_idx ].root_slot != *(ulong const *)(data+i*112UL+80UL) ||
780 : // gui->vote_account.vote_accounts[ found_idx ].epoch_credits != *(ulong const *)(data+i*112UL+88UL) ||
781 0 : gui->vote_account.vote_accounts[ found_idx ].commission != *(data+i*112UL+96UL) ||
782 0 : gui->vote_account.vote_accounts[ found_idx ].delinquent != *(data+i*112UL+97UL);
783 :
784 0 : if( FD_UNLIKELY( peer_updated ) ) {
785 0 : updated[ update_cnt++ ] = found_idx;
786 :
787 0 : fd_memcpy( gui->vote_account.vote_accounts[ found_idx ].pubkey->uc, data+i*112UL+32UL, 32UL );
788 0 : gui->vote_account.vote_accounts[ found_idx ].activated_stake = *(ulong const *)(data+i*112UL+64UL);
789 0 : gui->vote_account.vote_accounts[ found_idx ].last_vote = *(ulong const *)(data+i*112UL+72UL);
790 0 : gui->vote_account.vote_accounts[ found_idx ].root_slot = *(ulong const *)(data+i*112UL+80UL);
791 0 : gui->vote_account.vote_accounts[ found_idx ].epoch_credits = *(ulong const *)(data+i*112UL+88UL);
792 0 : gui->vote_account.vote_accounts[ found_idx ].commission = *(data+i*112UL+96UL);
793 0 : gui->vote_account.vote_accounts[ found_idx ].delinquent = *(data+i*112UL+97UL);
794 0 : }
795 0 : }
796 0 : }
797 :
798 0 : added_cnt = gui->vote_account.vote_account_cnt - before_peer_cnt;
799 0 : for( ulong i=before_peer_cnt; i<gui->vote_account.vote_account_cnt; i++ ) added[ i-before_peer_cnt ] = i;
800 :
801 0 : fd_gui_printf_peers_vote_account_update( gui, updated, update_cnt, removed, removed_cnt, added, added_cnt );
802 0 : fd_http_server_ws_broadcast( gui->http );
803 0 : }
804 :
805 : static void
806 : fd_gui_handle_validator_info_update( fd_gui_t * gui,
807 0 : uchar const * msg ) {
808 0 : if( FD_UNLIKELY( gui->validator_info.info_cnt == FD_GUI_MAX_PEER_CNT ) ) {
809 0 : FD_LOG_DEBUG(("validator info cnt exceeds 40200 %lu, ignoring additional entries", gui->validator_info.info_cnt ));
810 0 : return;
811 0 : }
812 0 : uchar const * data = (uchar const *)fd_type_pun_const( msg );
813 :
814 0 : ulong added_cnt = 0UL;
815 0 : ulong added[ 1 ] = {0};
816 :
817 0 : ulong update_cnt = 0UL;
818 0 : ulong updated[ 1 ] = {0};
819 :
820 0 : ulong removed_cnt = 0UL;
821 : /* Unlike gossip or vote account updates, validator info messages come
822 : in as info is discovered, and may contain as little as 1 validator
823 : per message. Therefore, it doesn't make sense to use the remove
824 : mechanism. */
825 :
826 0 : ulong before_peer_cnt = gui->validator_info.info_cnt;
827 0 : int found = 0;
828 0 : ulong found_idx;
829 0 : for( ulong j=0UL; j<gui->validator_info.info_cnt; j++ ) {
830 0 : if( FD_UNLIKELY( !memcmp( gui->validator_info.info[ j ].pubkey, data, 32UL ) ) ) {
831 0 : found_idx = j;
832 0 : found = 1;
833 0 : break;
834 0 : }
835 0 : }
836 :
837 0 : if( FD_UNLIKELY( !found ) ) {
838 0 : fd_memcpy( gui->validator_info.info[ gui->validator_info.info_cnt ].pubkey->uc, data, 32UL );
839 :
840 0 : strncpy( gui->validator_info.info[ gui->validator_info.info_cnt ].name, (char const *)(data+32UL), 64 );
841 0 : gui->validator_info.info[ gui->validator_info.info_cnt ].name[ 63 ] = '\0';
842 :
843 0 : strncpy( gui->validator_info.info[ gui->validator_info.info_cnt ].website, (char const *)(data+96UL), 128 );
844 0 : gui->validator_info.info[ gui->validator_info.info_cnt ].website[ 127 ] = '\0';
845 :
846 0 : strncpy( gui->validator_info.info[ gui->validator_info.info_cnt ].details, (char const *)(data+224UL), 256 );
847 0 : gui->validator_info.info[ gui->validator_info.info_cnt ].details[ 255 ] = '\0';
848 :
849 0 : strncpy( gui->validator_info.info[ gui->validator_info.info_cnt ].icon_uri, (char const *)(data+480UL), 128 );
850 0 : gui->validator_info.info[ gui->validator_info.info_cnt ].icon_uri[ 127 ] = '\0';
851 :
852 0 : gui->validator_info.info_cnt++;
853 0 : } else {
854 0 : int peer_updated =
855 0 : memcmp( gui->validator_info.info[ found_idx ].pubkey->uc, data, 32UL ) ||
856 0 : strncmp( gui->validator_info.info[ found_idx ].name, (char const *)(data+32UL), 64 ) ||
857 0 : strncmp( gui->validator_info.info[ found_idx ].website, (char const *)(data+96UL), 128 ) ||
858 0 : strncmp( gui->validator_info.info[ found_idx ].details, (char const *)(data+224UL), 256 ) ||
859 0 : strncmp( gui->validator_info.info[ found_idx ].icon_uri, (char const *)(data+480UL), 128 );
860 :
861 0 : if( FD_UNLIKELY( peer_updated ) ) {
862 0 : updated[ update_cnt++ ] = found_idx;
863 :
864 0 : fd_memcpy( gui->validator_info.info[ found_idx ].pubkey->uc, data, 32UL );
865 :
866 0 : strncpy( gui->validator_info.info[ found_idx ].name, (char const *)(data+32UL), 64 );
867 0 : gui->validator_info.info[ found_idx ].name[ 63 ] = '\0';
868 :
869 0 : strncpy( gui->validator_info.info[ found_idx ].website, (char const *)(data+96UL), 128 );
870 0 : gui->validator_info.info[ found_idx ].website[ 127 ] = '\0';
871 :
872 0 : strncpy( gui->validator_info.info[ found_idx ].details, (char const *)(data+224UL), 256 );
873 0 : gui->validator_info.info[ found_idx ].details[ 255 ] = '\0';
874 :
875 0 : strncpy( gui->validator_info.info[ found_idx ].icon_uri, (char const *)(data+480UL), 128 );
876 0 : gui->validator_info.info[ found_idx ].icon_uri[ 127 ] = '\0';
877 0 : }
878 0 : }
879 :
880 0 : added_cnt = gui->validator_info.info_cnt - before_peer_cnt;
881 0 : for( ulong i=before_peer_cnt; i<gui->validator_info.info_cnt; i++ ) added[ i-before_peer_cnt ] = i;
882 :
883 0 : fd_gui_printf_peers_validator_info_update( gui, updated, update_cnt, NULL, removed_cnt, added, added_cnt );
884 0 : fd_http_server_ws_broadcast( gui->http );
885 0 : }
886 :
887 : int
888 : fd_gui_request_slot( fd_gui_t * gui,
889 : ulong ws_conn_id,
890 : ulong request_id,
891 0 : cJSON const * params ) {
892 0 : const cJSON * slot_param = cJSON_GetObjectItemCaseSensitive( params, "slot" );
893 0 : if( FD_UNLIKELY( !cJSON_IsNumber( slot_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
894 :
895 0 : ulong _slot = slot_param->valueulong;
896 0 : fd_gui_slot_t const * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
897 0 : if( FD_UNLIKELY( slot->slot!=_slot || slot->slot==ULONG_MAX ) ) {
898 0 : fd_gui_printf_null_query_response( gui, "slot", "query", request_id );
899 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
900 0 : return 0;
901 0 : }
902 :
903 0 : fd_gui_printf_slot_request( gui, _slot, request_id );
904 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
905 0 : return 0;
906 0 : }
907 :
908 : int
909 : fd_gui_request_slot_transactions( fd_gui_t * gui,
910 : ulong ws_conn_id,
911 : ulong request_id,
912 0 : cJSON const * params ) {
913 0 : const cJSON * slot_param = cJSON_GetObjectItemCaseSensitive( params, "slot" );
914 0 : if( FD_UNLIKELY( !cJSON_IsNumber( slot_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
915 :
916 0 : ulong _slot = slot_param->valueulong;
917 0 : fd_gui_slot_t const * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
918 0 : if( FD_UNLIKELY( slot->slot!=_slot || slot->slot==ULONG_MAX ) ) {
919 0 : fd_gui_printf_null_query_response( gui, "slot", "query", request_id );
920 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
921 0 : return 0;
922 0 : }
923 :
924 0 : fd_gui_printf_slot_transactions_request( gui, _slot, request_id );
925 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
926 0 : return 0;
927 0 : }
928 :
929 : int
930 : fd_gui_request_slot_detailed( fd_gui_t * gui,
931 : ulong ws_conn_id,
932 : ulong request_id,
933 0 : cJSON const * params ) {
934 0 : const cJSON * slot_param = cJSON_GetObjectItemCaseSensitive( params, "slot" );
935 0 : if( FD_UNLIKELY( !cJSON_IsNumber( slot_param ) ) ) return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
936 :
937 0 : ulong _slot = slot_param->valueulong;
938 0 : fd_gui_slot_t const * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
939 0 : if( FD_UNLIKELY( slot->slot!=_slot || slot->slot==ULONG_MAX ) ) {
940 0 : fd_gui_printf_null_query_response( gui, "slot", "query", request_id );
941 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
942 0 : return 0;
943 0 : }
944 :
945 0 : fd_gui_printf_slot_request_detailed( gui, _slot, request_id );
946 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
947 0 : return 0;
948 0 : }
949 :
950 : int
951 : fd_gui_ws_message( fd_gui_t * gui,
952 : ulong ws_conn_id,
953 : uchar const * data,
954 0 : ulong data_len ) {
955 : /* TODO: cJSON allocates, might fail SIGSYS due to brk(2)...
956 : switch off this (or use wksp allocator) */
957 0 : const char * parse_end;
958 0 : cJSON * json = cJSON_ParseWithLengthOpts( (char *)data, data_len, &parse_end, 0 );
959 0 : if( FD_UNLIKELY( !json ) ) {
960 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
961 0 : }
962 :
963 0 : const cJSON * node = cJSON_GetObjectItemCaseSensitive( json, "id" );
964 0 : if( FD_UNLIKELY( !cJSON_IsNumber( node ) ) ) {
965 0 : cJSON_Delete( json );
966 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
967 0 : }
968 0 : ulong id = node->valueulong;
969 :
970 0 : const cJSON * topic = cJSON_GetObjectItemCaseSensitive( json, "topic" );
971 0 : if( FD_UNLIKELY( !cJSON_IsString( topic ) || topic->valuestring==NULL ) ) {
972 0 : cJSON_Delete( json );
973 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
974 0 : }
975 :
976 0 : const cJSON * key = cJSON_GetObjectItemCaseSensitive( json, "key" );
977 0 : if( FD_UNLIKELY( !cJSON_IsString( key ) || key->valuestring==NULL ) ) {
978 0 : cJSON_Delete( json );
979 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
980 0 : }
981 :
982 0 : if( FD_LIKELY( !strcmp( topic->valuestring, "slot" ) && !strcmp( key->valuestring, "query" ) ) ) {
983 0 : const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
984 0 : if( FD_UNLIKELY( !cJSON_IsObject( params ) ) ) {
985 0 : cJSON_Delete( json );
986 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
987 0 : }
988 :
989 0 : int result = fd_gui_request_slot( gui, ws_conn_id, id, params );
990 0 : cJSON_Delete( json );
991 0 : return result;
992 0 : } else if( FD_LIKELY( !strcmp( topic->valuestring, "slot" ) && !strcmp( key->valuestring, "query_detailed" ) ) ) {
993 0 : const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
994 0 : if( FD_UNLIKELY( !cJSON_IsObject( params ) ) ) {
995 0 : cJSON_Delete( json );
996 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
997 0 : }
998 :
999 0 : int result = fd_gui_request_slot_detailed( gui, ws_conn_id, id, params );
1000 0 : cJSON_Delete( json );
1001 0 : return result;
1002 0 : } else if( FD_LIKELY( !strcmp( topic->valuestring, "slot" ) && !strcmp( key->valuestring, "query_transactions" ) ) ) {
1003 0 : const cJSON * params = cJSON_GetObjectItemCaseSensitive( json, "params" );
1004 0 : if( FD_UNLIKELY( !cJSON_IsObject( params ) ) ) {
1005 0 : cJSON_Delete( json );
1006 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST;
1007 0 : }
1008 :
1009 0 : int result = fd_gui_request_slot_transactions( gui, ws_conn_id, id, params );
1010 0 : cJSON_Delete( json );
1011 0 : return result;
1012 0 : } else if( FD_LIKELY( !strcmp( topic->valuestring, "summary" ) && !strcmp( key->valuestring, "ping" ) ) ) {
1013 0 : fd_gui_printf_summary_ping( gui, id );
1014 0 : FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
1015 :
1016 0 : cJSON_Delete( json );
1017 0 : return 0;
1018 0 : }
1019 :
1020 0 : cJSON_Delete( json );
1021 0 : return FD_HTTP_SERVER_CONNECTION_CLOSE_UNKNOWN_METHOD;
1022 0 : }
1023 :
1024 : static void
1025 : fd_gui_clear_slot( fd_gui_t * gui,
1026 : ulong _slot,
1027 0 : ulong _parent_slot ) {
1028 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1029 :
1030 0 : int mine = 0;
1031 0 : ulong epoch_idx = 0UL;
1032 0 : for( ulong i=0UL; i<2UL; i++) {
1033 0 : if( FD_UNLIKELY( !gui->epoch.has_epoch[ i ] ) ) continue;
1034 0 : if( FD_LIKELY( _slot>=gui->epoch.epochs[ i ].start_slot && _slot<=gui->epoch.epochs[ i ].end_slot ) ) {
1035 0 : fd_pubkey_t const * slot_leader = fd_epoch_leaders_get( gui->epoch.epochs[ i ].lsched, _slot );
1036 0 : mine = !memcmp( slot_leader->uc, gui->summary.identity_key->uc, 32UL );
1037 0 : epoch_idx = i;
1038 0 : break;
1039 0 : }
1040 0 : }
1041 :
1042 0 : slot->slot = _slot;
1043 0 : slot->parent_slot = _parent_slot;
1044 0 : slot->max_compute_units = UINT_MAX;
1045 0 : slot->mine = mine;
1046 0 : slot->skipped = 0;
1047 0 : slot->must_republish = 1;
1048 0 : slot->level = FD_GUI_SLOT_LEVEL_INCOMPLETE;
1049 0 : slot->total_txn_cnt = UINT_MAX;
1050 0 : slot->vote_txn_cnt = UINT_MAX;
1051 0 : slot->failed_txn_cnt = UINT_MAX;
1052 0 : slot->nonvote_failed_txn_cnt = UINT_MAX;
1053 0 : slot->compute_units = UINT_MAX;
1054 0 : slot->transaction_fee = ULONG_MAX;
1055 0 : slot->priority_fee = ULONG_MAX;
1056 0 : slot->tips = ULONG_MAX;
1057 0 : slot->leader_state = FD_GUI_SLOT_LEADER_UNSTARTED;
1058 0 : slot->completed_time = LONG_MAX;
1059 :
1060 0 : slot->txs.leader_start_time = LONG_MAX;
1061 0 : slot->txs.leader_end_time = LONG_MAX;
1062 0 : slot->txs.microblocks_upper_bound = USHORT_MAX;
1063 0 : slot->txs.begin_microblocks = 0U;
1064 0 : slot->txs.end_microblocks = 0U;
1065 0 : slot->txs.reference_ticks = LONG_MAX;
1066 0 : slot->txs.reference_nanos = LONG_MAX;
1067 0 : slot->txs.start_offset = ULONG_MAX;
1068 0 : slot->txs.end_offset = ULONG_MAX;
1069 :
1070 0 : if( FD_LIKELY( slot->mine ) ) {
1071 : /* All slots start off not skipped, until we see it get off the reset
1072 : chain. */
1073 0 : gui->epoch.epochs[ epoch_idx ].my_total_slots++;
1074 0 : }
1075 :
1076 0 : if( FD_UNLIKELY( !_slot ) ) {
1077 : /* Slot 0 is always rooted */
1078 0 : slot->level = FD_GUI_SLOT_LEVEL_ROOTED;
1079 0 : }
1080 0 : }
1081 :
1082 : static void
1083 : fd_gui_handle_leader_schedule( fd_gui_t * gui,
1084 0 : ulong const * msg ) {
1085 0 : ulong epoch = msg[ 0 ];
1086 0 : ulong staked_cnt = msg[ 1 ];
1087 0 : ulong start_slot = msg[ 2 ];
1088 0 : ulong slot_cnt = msg[ 3 ];
1089 0 : ulong excluded_stake = msg[ 4 ];
1090 0 : ulong vote_keyed_lsched = msg[ 5 ];
1091 :
1092 0 : FD_TEST( staked_cnt<=MAX_STAKED_LEADERS );
1093 0 : FD_TEST( slot_cnt<=MAX_SLOTS_PER_EPOCH );
1094 :
1095 0 : ulong idx = epoch % 2UL;
1096 0 : gui->epoch.has_epoch[ idx ] = 1;
1097 :
1098 0 : gui->epoch.epochs[ idx ].epoch = epoch;
1099 0 : gui->epoch.epochs[ idx ].start_slot = start_slot;
1100 0 : gui->epoch.epochs[ idx ].end_slot = start_slot + slot_cnt - 1; // end_slot is inclusive.
1101 0 : gui->epoch.epochs[ idx ].excluded_stake = excluded_stake;
1102 0 : gui->epoch.epochs[ idx ].my_total_slots = 0UL;
1103 0 : gui->epoch.epochs[ idx ].my_skipped_slots = 0UL;
1104 :
1105 0 : fd_vote_stake_weight_t const * stake_weights = fd_type_pun_const( msg+6UL );
1106 0 : memcpy( gui->epoch.epochs[ idx ].stakes, stake_weights, staked_cnt*sizeof(fd_vote_stake_weight_t) );
1107 :
1108 0 : fd_epoch_leaders_delete( fd_epoch_leaders_leave( gui->epoch.epochs[ idx ].lsched ) );
1109 0 : gui->epoch.epochs[idx].lsched = fd_epoch_leaders_join( fd_epoch_leaders_new( gui->epoch.epochs[ idx ]._lsched,
1110 0 : epoch,
1111 0 : gui->epoch.epochs[ idx ].start_slot,
1112 0 : slot_cnt,
1113 0 : staked_cnt,
1114 0 : gui->epoch.epochs[ idx ].stakes,
1115 0 : excluded_stake,
1116 0 : vote_keyed_lsched ) );
1117 :
1118 0 : if( FD_UNLIKELY( start_slot==0UL ) ) {
1119 0 : gui->epoch.epochs[ 0 ].start_time = fd_log_wallclock();
1120 0 : } else {
1121 0 : gui->epoch.epochs[ idx ].start_time = LONG_MAX;
1122 :
1123 0 : for( ulong i=0UL; i<fd_ulong_min( start_slot-1UL, FD_GUI_SLOTS_CNT ); i++ ) {
1124 0 : fd_gui_slot_t * slot = gui->slots[ (start_slot-i) % FD_GUI_SLOTS_CNT ];
1125 0 : if( FD_UNLIKELY( slot->slot!=(start_slot-i) ) ) break;
1126 0 : else if( FD_UNLIKELY( slot->skipped ) ) continue;
1127 :
1128 0 : gui->epoch.epochs[ idx ].start_time = slot->completed_time;
1129 0 : break;
1130 0 : }
1131 0 : }
1132 :
1133 0 : fd_gui_printf_epoch( gui, idx );
1134 0 : fd_http_server_ws_broadcast( gui->http );
1135 0 : }
1136 :
1137 : static void
1138 : fd_gui_handle_slot_start( fd_gui_t * gui,
1139 0 : ulong * msg ) {
1140 0 : ulong _slot = msg[ 0 ];
1141 0 : ulong _parent_slot = msg[ 1 ];
1142 : // FD_LOG_WARNING(( "Got start slot %lu parent_slot %lu", _slot, _parent_slot ));
1143 0 : FD_TEST( gui->debug_in_leader_slot==ULONG_MAX );
1144 0 : gui->debug_in_leader_slot = _slot;
1145 :
1146 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1147 :
1148 0 : if( FD_UNLIKELY( slot->slot!=_slot ) ) fd_gui_clear_slot( gui, _slot, _parent_slot );
1149 0 : slot->leader_state = FD_GUI_SLOT_LEADER_STARTED;
1150 :
1151 0 : fd_gui_tile_timers_snap( gui );
1152 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;
1153 :
1154 0 : fd_gui_txn_waterfall_t waterfall[ 1 ];
1155 0 : fd_gui_txn_waterfall_snap( gui, waterfall );
1156 0 : fd_gui_tile_stats_snap( gui, waterfall, slot->tile_stats_begin );
1157 0 : }
1158 :
1159 : static void
1160 : fd_gui_handle_slot_end( fd_gui_t * gui,
1161 0 : ulong * msg ) {
1162 0 : ulong _slot = msg[ 0 ];
1163 0 : ulong _cus_used = msg[ 1 ];
1164 0 : if( FD_UNLIKELY( gui->debug_in_leader_slot!=_slot ) ) {
1165 0 : FD_LOG_ERR(( "gui->debug_in_leader_slot %lu _slot %lu", gui->debug_in_leader_slot, _slot ));
1166 0 : }
1167 0 : gui->debug_in_leader_slot = ULONG_MAX;
1168 :
1169 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1170 0 : FD_TEST( slot->slot==_slot );
1171 :
1172 0 : slot->leader_state = FD_GUI_SLOT_LEADER_ENDED;
1173 0 : slot->compute_units = (uint)_cus_used;
1174 :
1175 0 : fd_gui_tile_timers_snap( gui );
1176 : /* Record slot number so we can detect overwrite. */
1177 0 : gui->summary.tile_timers_leader_history_slot[ gui->summary.tile_timers_history_idx ] = _slot;
1178 : /* Point into per-leader-slot storage. */
1179 0 : slot->tile_timers_history_idx = gui->summary.tile_timers_history_idx;
1180 : /* Downsample tile timers into per-leader-slot storage. */
1181 0 : ulong end = gui->summary.tile_timers_snap_idx;
1182 0 : end = fd_ulong_if( end<gui->summary.tile_timers_snap_idx_slot_start, end+FD_GUI_TILE_TIMER_SNAP_CNT, end );
1183 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;
1184 0 : ulong stride = fd_ulong_max( 1UL, (end-gui->summary.tile_timers_snap_idx_slot_start) / FD_GUI_TILE_TIMER_LEADER_DOWNSAMPLE_CNT );
1185 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++ ) {
1186 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 ]) );
1187 0 : }
1188 0 : gui->summary.tile_timers_history_idx = (gui->summary.tile_timers_history_idx+1UL)%FD_GUI_TILE_TIMER_LEADER_CNT;
1189 :
1190 : /* When a slot ends, snap the state of the waterfall and save it into
1191 : that slot, and also reset the reference counters to the end of the
1192 : slot. */
1193 :
1194 0 : fd_gui_txn_waterfall_snap( gui, slot->waterfall_end );
1195 0 : memcpy( slot->waterfall_begin, gui->summary.txn_waterfall_reference, sizeof(slot->waterfall_begin) );
1196 0 : memcpy( gui->summary.txn_waterfall_reference, slot->waterfall_end, sizeof(gui->summary.txn_waterfall_reference) );
1197 :
1198 0 : fd_gui_tile_stats_snap( gui, slot->waterfall_end, slot->tile_stats_end );
1199 0 : }
1200 :
1201 : static void
1202 : fd_gui_handle_reset_slot( fd_gui_t * gui,
1203 0 : ulong * msg ) {
1204 0 : ulong last_landed_vote = msg[ 0 ];
1205 :
1206 0 : ulong parent_cnt = msg[ 1 ];
1207 0 : FD_TEST( parent_cnt<4096UL );
1208 :
1209 0 : ulong _slot = msg[ 2 ];
1210 :
1211 0 : for( ulong i=0UL; i<parent_cnt; i++ ) {
1212 0 : ulong parent_slot = msg[2UL+i];
1213 0 : fd_gui_slot_t * slot = gui->slots[ parent_slot % FD_GUI_SLOTS_CNT ];
1214 0 : if( FD_UNLIKELY( slot->slot!=parent_slot ) ) {
1215 0 : ulong parent_parent_slot = ULONG_MAX;
1216 0 : if( FD_UNLIKELY( i!=parent_cnt-1UL) ) parent_parent_slot = msg[ 3UL+i ];
1217 0 : fd_gui_clear_slot( gui, parent_slot, parent_parent_slot );
1218 0 : }
1219 0 : }
1220 :
1221 0 : if( FD_UNLIKELY( gui->summary.vote_distance!=_slot-last_landed_vote ) ) {
1222 0 : gui->summary.vote_distance = _slot-last_landed_vote;
1223 0 : fd_gui_printf_vote_distance( gui );
1224 0 : fd_http_server_ws_broadcast( gui->http );
1225 0 : }
1226 :
1227 0 : if( FD_LIKELY( gui->summary.vote_state!=FD_GUI_VOTE_STATE_NON_VOTING ) ) {
1228 0 : if( FD_UNLIKELY( last_landed_vote==ULONG_MAX || (last_landed_vote+150UL)<_slot ) ) {
1229 0 : if( FD_UNLIKELY( gui->summary.vote_state!=FD_GUI_VOTE_STATE_DELINQUENT ) ) {
1230 0 : gui->summary.vote_state = FD_GUI_VOTE_STATE_DELINQUENT;
1231 0 : fd_gui_printf_vote_state( gui );
1232 0 : fd_http_server_ws_broadcast( gui->http );
1233 0 : }
1234 0 : } else {
1235 0 : if( FD_UNLIKELY( gui->summary.vote_state!=FD_GUI_VOTE_STATE_VOTING ) ) {
1236 0 : gui->summary.vote_state = FD_GUI_VOTE_STATE_VOTING;
1237 0 : fd_gui_printf_vote_state( gui );
1238 0 : fd_http_server_ws_broadcast( gui->http );
1239 0 : }
1240 0 : }
1241 0 : }
1242 :
1243 0 : ulong parent_slot_idx = 0UL;
1244 :
1245 0 : int republish_skip_rate[ 2 ] = {0};
1246 :
1247 0 : for( ulong i=0UL; i<fd_ulong_min( _slot+1, FD_GUI_SLOTS_CNT ); i++ ) {
1248 0 : ulong parent_slot = _slot - i;
1249 0 : ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
1250 :
1251 0 : fd_gui_slot_t * slot = gui->slots[ parent_idx ];
1252 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX || slot->slot!=parent_slot ) ) fd_gui_clear_slot( gui, parent_slot, ULONG_MAX );
1253 :
1254 : /* The chain of parents may stretch into already rooted slots if
1255 : they haven't been squashed yet, if we reach one of them we can
1256 : just exit, all the information prior to the root is already
1257 : correct. */
1258 :
1259 0 : if( FD_LIKELY( slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) break;
1260 :
1261 0 : int should_republish = slot->must_republish;
1262 0 : slot->must_republish = 0;
1263 :
1264 0 : if( FD_UNLIKELY( parent_slot!=msg[2UL+parent_slot_idx] ) ) {
1265 : /* We are between two parents in the rooted chain, which means
1266 : we were skipped. */
1267 0 : if( FD_UNLIKELY( !slot->skipped ) ) {
1268 0 : slot->skipped = 1;
1269 0 : should_republish = 1;
1270 0 : if( FD_LIKELY( slot->mine ) ) {
1271 0 : for( ulong i=0UL; i<2UL; i++ ) {
1272 0 : if( FD_LIKELY( parent_slot>=gui->epoch.epochs[ i ].start_slot && parent_slot<=gui->epoch.epochs[ i ].end_slot ) ) {
1273 0 : gui->epoch.epochs[ i ].my_skipped_slots++;
1274 0 : republish_skip_rate[ i ] = 1;
1275 0 : break;
1276 0 : }
1277 0 : }
1278 0 : }
1279 0 : }
1280 0 : } else {
1281 : /* Reached the next parent... */
1282 0 : if( FD_UNLIKELY( slot->skipped ) ) {
1283 0 : slot->skipped = 0;
1284 0 : should_republish = 1;
1285 0 : if( FD_LIKELY( slot->mine ) ) {
1286 0 : for( ulong i=0UL; i<2UL; i++ ) {
1287 0 : if( FD_LIKELY( parent_slot>=gui->epoch.epochs[ i ].start_slot && parent_slot<=gui->epoch.epochs[ i ].end_slot ) ) {
1288 0 : gui->epoch.epochs[ i ].my_skipped_slots--;
1289 0 : republish_skip_rate[ i ] = 1;
1290 0 : break;
1291 0 : }
1292 0 : }
1293 0 : }
1294 0 : }
1295 0 : parent_slot_idx++;
1296 0 : }
1297 :
1298 0 : if( FD_LIKELY( should_republish ) ) {
1299 0 : fd_gui_printf_slot( gui, parent_slot );
1300 0 : fd_http_server_ws_broadcast( gui->http );
1301 0 : }
1302 :
1303 : /* We reached the last parent in the chain, everything above this
1304 : must have already been rooted, so we can exit. */
1305 :
1306 0 : if( FD_UNLIKELY( parent_slot_idx>=parent_cnt ) ) break;
1307 0 : }
1308 :
1309 0 : ulong last_slot = _slot;
1310 0 : long last_published = gui->slots[ _slot % FD_GUI_SLOTS_CNT ]->completed_time;
1311 :
1312 0 : for( ulong i=0UL; i<fd_ulong_min( _slot+1, 750UL ); i++ ) {
1313 0 : ulong parent_slot = _slot - i;
1314 0 : ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
1315 :
1316 0 : fd_gui_slot_t * slot = gui->slots[ parent_idx ];
1317 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX) ) break;
1318 0 : if( FD_UNLIKELY( slot->slot!=parent_slot ) ) {
1319 0 : FD_LOG_ERR(( "_slot %lu i %lu we expect _slot-i %lu got slot->slot %lu", _slot, i, _slot-i, slot->slot ));
1320 0 : }
1321 :
1322 0 : if( FD_LIKELY( !slot->skipped ) ) {
1323 0 : last_slot = parent_slot;
1324 0 : last_published = slot->completed_time;
1325 0 : }
1326 0 : }
1327 :
1328 0 : if( FD_LIKELY( _slot!=last_slot )) {
1329 0 : gui->summary.estimated_slot_duration_nanos = (ulong)(fd_log_wallclock()-last_published)/(_slot-last_slot);
1330 0 : fd_gui_printf_estimated_slot_duration_nanos( gui );
1331 0 : fd_http_server_ws_broadcast( gui->http );
1332 0 : }
1333 :
1334 0 : if( FD_LIKELY( _slot!=gui->summary.slot_completed ) ) {
1335 0 : gui->summary.slot_completed = _slot;
1336 0 : fd_gui_printf_completed_slot( gui );
1337 0 : fd_http_server_ws_broadcast( gui->http );
1338 0 : }
1339 :
1340 0 : for( ulong i=0UL; i<2UL; i++ ) {
1341 0 : if( FD_LIKELY( republish_skip_rate[ i ] ) ) {
1342 0 : fd_gui_printf_skip_rate( gui, i );
1343 0 : fd_http_server_ws_broadcast( gui->http );
1344 0 : }
1345 0 : }
1346 0 : }
1347 :
1348 : static void
1349 : fd_gui_handle_completed_slot( fd_gui_t * gui,
1350 0 : ulong * msg ) {
1351 0 : ulong _slot = msg[ 0 ];
1352 0 : uint total_txn_count = (uint)msg[ 1 ];
1353 0 : uint nonvote_txn_count = (uint)msg[ 2 ];
1354 0 : uint failed_txn_count = (uint)msg[ 3 ];
1355 0 : uint nonvote_failed_txn_count = (uint)msg[ 4 ];
1356 0 : uint compute_units = (uint)msg[ 5 ];
1357 0 : ulong transaction_fee = msg[ 6 ];
1358 0 : ulong priority_fee = msg[ 7 ];
1359 0 : ulong tips = msg[ 8 ];
1360 0 : ulong _parent_slot = msg[ 9 ];
1361 0 : ulong max_compute_units = msg[ 10 ];
1362 :
1363 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1364 0 : if( FD_UNLIKELY( slot->slot!=_slot ) ) fd_gui_clear_slot( gui, _slot, _parent_slot );
1365 :
1366 0 : slot->completed_time = fd_log_wallclock();
1367 0 : slot->parent_slot = _parent_slot;
1368 0 : slot->max_compute_units = (uint)max_compute_units;
1369 0 : if( FD_LIKELY( slot->level<FD_GUI_SLOT_LEVEL_COMPLETED ) ) {
1370 : /* Typically a slot goes from INCOMPLETE to COMPLETED but it can
1371 : happen that it starts higher. One such case is when we
1372 : optimistically confirm a higher slot that skips this one, but
1373 : then later we replay this one anyway to track the bank fork. */
1374 :
1375 0 : if( FD_LIKELY( _slot<gui->summary.slot_optimistically_confirmed ) ) {
1376 : /* Cluster might have already optimistically confirmed by the time
1377 : we finish replaying it. */
1378 0 : slot->level = FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED;
1379 0 : } else {
1380 0 : slot->level = FD_GUI_SLOT_LEVEL_COMPLETED;
1381 0 : }
1382 0 : }
1383 0 : slot->total_txn_cnt = total_txn_count;
1384 0 : slot->vote_txn_cnt = total_txn_count - nonvote_txn_count;
1385 0 : slot->failed_txn_cnt = failed_txn_count;
1386 0 : slot->nonvote_failed_txn_cnt = nonvote_failed_txn_count;
1387 0 : slot->transaction_fee = transaction_fee;
1388 0 : slot->priority_fee = priority_fee;
1389 0 : slot->tips = tips;
1390 0 : if( FD_LIKELY( slot->leader_state==FD_GUI_SLOT_LEADER_UNSTARTED ) ) {
1391 : /* If we were already leader for this slot, then the poh component
1392 : calculated the CUs used and sent them there, rather than the
1393 : replay component which is sending this completed slot. */
1394 0 : slot->compute_units = compute_units;
1395 0 : }
1396 :
1397 0 : if( FD_UNLIKELY( gui->epoch.has_epoch[ 0 ] && _slot==gui->epoch.epochs[ 0 ].end_slot ) ) {
1398 0 : gui->epoch.epochs[ 0 ].end_time = slot->completed_time;
1399 0 : } else if( FD_UNLIKELY( gui->epoch.has_epoch[ 1 ] && _slot==gui->epoch.epochs[ 1 ].end_slot ) ) {
1400 0 : gui->epoch.epochs[ 1 ].end_time = slot->completed_time;
1401 0 : }
1402 :
1403 : /* Broadcast new skip rate if one of our slots got completed. */
1404 0 : if( FD_LIKELY( slot->mine ) ) {
1405 0 : for( ulong i=0UL; i<2UL; i++ ) {
1406 0 : if( FD_LIKELY( _slot>=gui->epoch.epochs[ i ].start_slot && _slot<=gui->epoch.epochs[ i ].end_slot ) ) {
1407 0 : fd_gui_printf_skip_rate( gui, i );
1408 0 : fd_http_server_ws_broadcast( gui->http );
1409 0 : break;
1410 0 : }
1411 0 : }
1412 0 : }
1413 0 : }
1414 :
1415 : static void
1416 : fd_gui_handle_rooted_slot( fd_gui_t * gui,
1417 0 : ulong * msg ) {
1418 0 : ulong _slot = msg[ 0 ];
1419 :
1420 : // FD_LOG_WARNING(( "Got rooted slot %lu", _slot ));
1421 :
1422 : /* Slot 0 is always rooted. No need to iterate all the way back to
1423 : i==_slot */
1424 0 : for( ulong i=0UL; i<fd_ulong_min( _slot, FD_GUI_SLOTS_CNT ); i++ ) {
1425 0 : ulong parent_slot = _slot - i;
1426 0 : ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
1427 :
1428 0 : fd_gui_slot_t * slot = gui->slots[ parent_idx ];
1429 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX) ) break;
1430 :
1431 0 : if( FD_UNLIKELY( slot->slot!=parent_slot ) ) {
1432 0 : FD_LOG_ERR(( "_slot %lu i %lu we expect parent_slot %lu got slot->slot %lu", _slot, i, parent_slot, slot->slot ));
1433 0 : }
1434 0 : if( FD_UNLIKELY( slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) break;
1435 :
1436 0 : slot->level = FD_GUI_SLOT_LEVEL_ROOTED;
1437 0 : fd_gui_printf_slot( gui, parent_slot );
1438 0 : fd_http_server_ws_broadcast( gui->http );
1439 0 : }
1440 :
1441 0 : gui->summary.slot_rooted = _slot;
1442 0 : fd_gui_printf_root_slot( gui );
1443 0 : fd_http_server_ws_broadcast( gui->http );
1444 0 : }
1445 :
1446 : static void
1447 : fd_gui_handle_optimistically_confirmed_slot( fd_gui_t * gui,
1448 0 : ulong * msg ) {
1449 0 : ulong _slot = msg[ 0 ];
1450 :
1451 : /* Slot 0 is always rooted. No need to iterate all the way back to
1452 : i==_slot */
1453 0 : for( ulong i=0UL; i<fd_ulong_min( _slot, FD_GUI_SLOTS_CNT ); i++ ) {
1454 0 : ulong parent_slot = _slot - i;
1455 0 : ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
1456 :
1457 0 : fd_gui_slot_t * slot = gui->slots[ parent_idx ];
1458 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX) ) break;
1459 :
1460 0 : if( FD_UNLIKELY( slot->slot>parent_slot ) ) {
1461 0 : FD_LOG_ERR(( "_slot %lu i %lu we expect parent_slot %lu got slot->slot %lu", _slot, i, parent_slot, slot->slot ));
1462 0 : } else if( FD_UNLIKELY( slot->slot<parent_slot ) ) {
1463 : /* Slot not even replayed yet ... will come out as optimistically confirmed */
1464 0 : continue;
1465 0 : }
1466 0 : if( FD_UNLIKELY( slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) break;
1467 :
1468 0 : if( FD_LIKELY( slot->level<FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED ) ) {
1469 0 : slot->level = FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED;
1470 0 : fd_gui_printf_slot( gui, parent_slot );
1471 0 : fd_http_server_ws_broadcast( gui->http );
1472 0 : }
1473 0 : }
1474 :
1475 0 : if( FD_UNLIKELY( _slot<gui->summary.slot_optimistically_confirmed ) ) {
1476 : /* Optimistically confirmed slot went backwards ... mark some slots as no
1477 : longer optimistically confirmed. */
1478 0 : for( ulong i=gui->summary.slot_optimistically_confirmed; i>=_slot; i-- ) {
1479 0 : fd_gui_slot_t * slot = gui->slots[ i % FD_GUI_SLOTS_CNT ];
1480 0 : if( FD_UNLIKELY( slot->slot==ULONG_MAX ) ) break;
1481 0 : if( FD_LIKELY( slot->slot==i ) ) {
1482 : /* It's possible for the optimistically confirmed slot to skip
1483 : backwards between two slots that we haven't yet replayed. In
1484 : that case we don't need to change anything, since they will
1485 : get marked properly when they get completed. */
1486 0 : slot->level = FD_GUI_SLOT_LEVEL_COMPLETED;
1487 0 : fd_gui_printf_slot( gui, i );
1488 0 : fd_http_server_ws_broadcast( gui->http );
1489 0 : }
1490 0 : }
1491 0 : }
1492 :
1493 0 : gui->summary.slot_optimistically_confirmed = _slot;
1494 0 : fd_gui_printf_optimistically_confirmed_slot( gui );
1495 0 : fd_http_server_ws_broadcast( gui->http );
1496 0 : }
1497 :
1498 : static void
1499 : fd_gui_handle_balance_update( fd_gui_t * gui,
1500 0 : ulong const * msg ) {
1501 0 : switch( msg[ 0 ] ) {
1502 0 : case 0UL:
1503 0 : gui->summary.identity_account_balance = msg[ 1 ];
1504 0 : fd_gui_printf_identity_balance( gui );
1505 0 : fd_http_server_ws_broadcast( gui->http );
1506 0 : break;
1507 0 : case 1UL:
1508 0 : gui->summary.vote_account_balance = msg[ 1 ];
1509 0 : fd_gui_printf_vote_balance( gui );
1510 0 : fd_http_server_ws_broadcast( gui->http );
1511 0 : break;
1512 0 : default:
1513 0 : FD_LOG_ERR(( "balance: unknown account type: %lu", msg[ 0 ] ));
1514 0 : }
1515 0 : }
1516 :
1517 : static void
1518 : fd_gui_handle_start_progress( fd_gui_t * gui,
1519 0 : uchar const * msg ) {
1520 0 : uchar type = msg[ 0 ];
1521 :
1522 0 : switch (type) {
1523 0 : case 0:
1524 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_INITIALIZING;
1525 0 : FD_LOG_INFO(( "progress: initializing" ));
1526 0 : break;
1527 0 : case 1: {
1528 0 : char const * snapshot_type;
1529 0 : if( FD_UNLIKELY( gui->summary.startup_got_full_snapshot ) ) {
1530 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_SEARCHING_FOR_INCREMENTAL_SNAPSHOT;
1531 0 : snapshot_type = "incremental";
1532 0 : } else {
1533 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_SEARCHING_FOR_FULL_SNAPSHOT;
1534 0 : snapshot_type = "full";
1535 0 : }
1536 0 : FD_LOG_INFO(( "progress: searching for %s snapshot", snapshot_type ));
1537 0 : break;
1538 0 : }
1539 0 : case 2: {
1540 0 : uchar is_full_snapshot = msg[ 1 ];
1541 0 : if( FD_LIKELY( is_full_snapshot ) ) {
1542 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_DOWNLOADING_FULL_SNAPSHOT;
1543 0 : gui->summary.startup_full_snapshot_slot = *((ulong *)(msg + 2));
1544 0 : gui->summary.startup_full_snapshot_peer_ip_addr = *((uint *)(msg + 10));
1545 0 : gui->summary.startup_full_snapshot_peer_port = *((ushort *)(msg + 14));
1546 0 : gui->summary.startup_full_snapshot_total_bytes = *((ulong *)(msg + 16));
1547 0 : gui->summary.startup_full_snapshot_current_bytes = *((ulong *)(msg + 24));
1548 0 : gui->summary.startup_full_snapshot_elapsed_secs = *((double *)(msg + 32));
1549 0 : gui->summary.startup_full_snapshot_remaining_secs = *((double *)(msg + 40));
1550 0 : gui->summary.startup_full_snapshot_throughput = *((double *)(msg + 48));
1551 0 : FD_LOG_INFO(( "progress: downloading full snapshot: slot=%lu", gui->summary.startup_full_snapshot_slot ));
1552 0 : } else {
1553 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_DOWNLOADING_INCREMENTAL_SNAPSHOT;
1554 0 : gui->summary.startup_incremental_snapshot_slot = *((ulong *)(msg + 2));
1555 0 : gui->summary.startup_incremental_snapshot_peer_ip_addr = *((uint *)(msg + 10));
1556 0 : gui->summary.startup_incremental_snapshot_peer_port = *((ushort *)(msg + 14));
1557 0 : gui->summary.startup_incremental_snapshot_total_bytes = *((ulong *)(msg + 16));
1558 0 : gui->summary.startup_incremental_snapshot_current_bytes = *((ulong *)(msg + 24));
1559 0 : gui->summary.startup_incremental_snapshot_elapsed_secs = *((double *)(msg + 32));
1560 0 : gui->summary.startup_incremental_snapshot_remaining_secs = *((double *)(msg + 40));
1561 0 : gui->summary.startup_incremental_snapshot_throughput = *((double *)(msg + 48));
1562 0 : FD_LOG_INFO(( "progress: downloading incremental snapshot: slot=%lu", gui->summary.startup_incremental_snapshot_slot ));
1563 0 : }
1564 0 : break;
1565 0 : }
1566 0 : case 3: {
1567 0 : gui->summary.startup_got_full_snapshot = 1;
1568 0 : break;
1569 0 : }
1570 0 : case 4:
1571 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_CLEANING_BLOCK_STORE;
1572 0 : FD_LOG_INFO(( "progress: cleaning block store" ));
1573 0 : break;
1574 0 : case 5:
1575 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_CLEANING_ACCOUNTS;
1576 0 : FD_LOG_INFO(( "progress: cleaning accounts" ));
1577 0 : break;
1578 0 : case 6:
1579 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_LOADING_LEDGER;
1580 0 : FD_LOG_INFO(( "progress: loading ledger" ));
1581 0 : break;
1582 0 : case 7: {
1583 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_PROCESSING_LEDGER;
1584 0 : gui->summary.startup_ledger_slot = fd_ulong_load_8( msg + 1 );
1585 0 : gui->summary.startup_ledger_max_slot = fd_ulong_load_8( msg + 9 );
1586 0 : FD_LOG_INFO(( "progress: processing ledger: slot=%lu, max_slot=%lu", gui->summary.startup_ledger_slot, gui->summary.startup_ledger_max_slot ));
1587 0 : break;
1588 0 : }
1589 0 : case 8:
1590 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_STARTING_SERVICES;
1591 0 : FD_LOG_INFO(( "progress: starting services" ));
1592 0 : break;
1593 0 : case 9:
1594 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_HALTED;
1595 0 : FD_LOG_INFO(( "progress: halted" ));
1596 0 : break;
1597 0 : case 10: {
1598 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_WAITING_FOR_SUPERMAJORITY;
1599 0 : gui->summary.startup_waiting_for_supermajority_slot = fd_ulong_load_8( msg + 1 );
1600 0 : gui->summary.startup_waiting_for_supermajority_stake_pct = fd_ulong_load_8( msg + 9 );
1601 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 ));
1602 0 : break;
1603 0 : }
1604 0 : case 11:
1605 0 : gui->summary.startup_progress = FD_GUI_START_PROGRESS_TYPE_RUNNING;
1606 0 : FD_LOG_INFO(( "progress: running" ));
1607 0 : break;
1608 0 : default:
1609 0 : FD_LOG_ERR(( "progress: unknown type: %u", type ));
1610 0 : }
1611 :
1612 0 : fd_gui_printf_startup_progress( gui );
1613 0 : fd_http_server_ws_broadcast( gui->http );
1614 0 : }
1615 :
1616 : static void
1617 : fd_gui_handle_genesis_hash( fd_gui_t * gui,
1618 0 : uchar const * msg ) {
1619 0 : FD_BASE58_ENCODE_32_BYTES(msg, hash_cstr);
1620 0 : ulong cluster = fd_genesis_cluster_identify(hash_cstr);
1621 0 : char const * cluster_name = fd_genesis_cluster_name(cluster);
1622 :
1623 0 : if( FD_LIKELY( strcmp( gui->summary.cluster, cluster_name ) ) ) {
1624 0 : gui->summary.cluster = fd_genesis_cluster_name(cluster);
1625 0 : fd_gui_printf_cluster( gui );
1626 0 : fd_http_server_ws_broadcast( gui->http );
1627 0 : }
1628 0 : }
1629 :
1630 : static void
1631 : fd_gui_handle_block_engine_update( fd_gui_t * gui,
1632 0 : uchar const * msg ) {
1633 0 : fd_plugin_msg_block_engine_update_t const * update = (fd_plugin_msg_block_engine_update_t const *)msg;
1634 :
1635 0 : gui->block_engine.has_block_engine = 1;
1636 0 : memcpy( gui->block_engine.name, update->name, sizeof(gui->block_engine.name )-1 );
1637 0 : memcpy( gui->block_engine.url, update->url, sizeof(gui->block_engine.url )-1 );
1638 0 : memcpy( gui->block_engine.ip_cstr, update->ip_cstr, sizeof(gui->block_engine.ip_cstr)-1 );
1639 0 : gui->block_engine.status = update->status;
1640 :
1641 0 : fd_gui_printf_block_engine( gui );
1642 0 : fd_http_server_ws_broadcast( gui->http );
1643 0 : }
1644 :
1645 : void
1646 : fd_gui_plugin_message( fd_gui_t * gui,
1647 : ulong plugin_msg,
1648 0 : uchar const * msg ) {
1649 :
1650 0 : switch( plugin_msg ) {
1651 0 : case FD_PLUGIN_MSG_SLOT_ROOTED:
1652 0 : fd_gui_handle_rooted_slot( gui, (ulong *)msg );
1653 0 : break;
1654 0 : case FD_PLUGIN_MSG_SLOT_OPTIMISTICALLY_CONFIRMED:
1655 0 : fd_gui_handle_optimistically_confirmed_slot( gui, (ulong *)msg );
1656 0 : break;
1657 0 : case FD_PLUGIN_MSG_SLOT_COMPLETED:
1658 0 : fd_gui_handle_completed_slot( gui, (ulong *)msg );
1659 0 : break;
1660 0 : case FD_PLUGIN_MSG_SLOT_ESTIMATED:
1661 0 : gui->summary.slot_estimated = *(ulong const *)msg;
1662 0 : fd_gui_printf_estimated_slot( gui );
1663 0 : fd_http_server_ws_broadcast( gui->http );
1664 0 : break;
1665 0 : case FD_PLUGIN_MSG_LEADER_SCHEDULE: {
1666 0 : fd_gui_handle_leader_schedule( gui, (ulong const *)msg );
1667 0 : break;
1668 0 : }
1669 0 : case FD_PLUGIN_MSG_SLOT_START: {
1670 0 : fd_gui_handle_slot_start( gui, (ulong *)msg );
1671 0 : break;
1672 0 : }
1673 0 : case FD_PLUGIN_MSG_SLOT_END: {
1674 0 : fd_gui_handle_slot_end( gui, (ulong *)msg );
1675 0 : break;
1676 0 : }
1677 0 : case FD_PLUGIN_MSG_GOSSIP_UPDATE: {
1678 0 : fd_gui_handle_gossip_update( gui, msg );
1679 0 : break;
1680 0 : }
1681 0 : case FD_PLUGIN_MSG_VOTE_ACCOUNT_UPDATE: {
1682 0 : fd_gui_handle_vote_account_update( gui, msg );
1683 0 : break;
1684 0 : }
1685 0 : case FD_PLUGIN_MSG_VALIDATOR_INFO: {
1686 0 : fd_gui_handle_validator_info_update( gui, msg );
1687 0 : break;
1688 0 : }
1689 0 : case FD_PLUGIN_MSG_SLOT_RESET: {
1690 0 : fd_gui_handle_reset_slot( gui, (ulong *)msg );
1691 0 : break;
1692 0 : }
1693 0 : case FD_PLUGIN_MSG_BALANCE: {
1694 0 : fd_gui_handle_balance_update( gui, (ulong *)msg );
1695 0 : break;
1696 0 : }
1697 0 : case FD_PLUGIN_MSG_START_PROGRESS: {
1698 0 : fd_gui_handle_start_progress( gui, msg );
1699 0 : break;
1700 0 : }
1701 0 : case FD_PLUGIN_MSG_GENESIS_HASH_KNOWN: {
1702 0 : fd_gui_handle_genesis_hash( gui, msg );
1703 0 : break;
1704 0 : }
1705 0 : case FD_PLUGIN_MSG_BLOCK_ENGINE_UPDATE: {
1706 0 : fd_gui_handle_block_engine_update( gui, msg );
1707 0 : break;
1708 0 : }
1709 0 : default:
1710 0 : FD_LOG_ERR(( "Unhandled plugin msg: 0x%lx", plugin_msg ));
1711 0 : break;
1712 0 : }
1713 0 : }
1714 :
1715 : static void
1716 : fd_gui_init_slot_txns( fd_gui_t * gui,
1717 : long tickcount,
1718 0 : ulong _slot ) {
1719 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1720 0 : if( FD_UNLIKELY( slot->slot!=_slot ) ) fd_gui_clear_slot( gui, _slot, ULONG_MAX );
1721 :
1722 : /* initialize reference timestamp */
1723 0 : if ( FD_UNLIKELY( LONG_MAX==slot->txs.reference_ticks ) ) {
1724 0 : slot->txs.reference_ticks = tickcount;
1725 0 : slot->txs.reference_nanos = fd_log_wallclock() - (long)((double)(fd_tickcount() - slot->txs.reference_ticks) / fd_tempo_tick_per_ns( NULL ));
1726 0 : }
1727 0 : }
1728 :
1729 : void
1730 : fd_gui_became_leader( fd_gui_t * gui,
1731 : long tickcount,
1732 : ulong _slot,
1733 : long start_time_nanos,
1734 : long end_time_nanos,
1735 : ulong max_compute_units,
1736 0 : ulong max_microblocks ) {
1737 0 : fd_gui_init_slot_txns( gui, tickcount, _slot );
1738 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1739 0 : slot->max_compute_units = (uint)max_compute_units;
1740 :
1741 0 : slot->txs.leader_start_time = start_time_nanos;
1742 0 : slot->txs.leader_end_time = end_time_nanos;
1743 0 : if( FD_LIKELY( slot->txs.microblocks_upper_bound==USHORT_MAX ) ) slot->txs.microblocks_upper_bound = (ushort)max_microblocks;
1744 :
1745 : // snapshot of bundle rx histogram at leader rotation start
1746 0 : ulong bundle_tile_idx = fd_topo_find_tile( gui->topo, "bundle", 0UL );
1747 0 : if( FD_UNLIKELY( bundle_tile_idx!=ULONG_MAX && _slot % 4 == 0 ) ) {
1748 0 : fd_topo_tile_t const * bundle = &gui->topo->tiles[ bundle_tile_idx ];
1749 0 : volatile ulong * bundle_metrics = fd_metrics_tile( bundle->metrics );
1750 0 : (void)bundle_metrics;
1751 :
1752 0 : gui->bundle_rx_delay_hist_current->sum = bundle_metrics[ MIDX( HISTOGRAM, BUNDLE, MESSAGE_RX_DELAY_NANOS ) + FD_HISTF_BUCKET_CNT ];
1753 0 : for( ulong b=0; b<FD_HISTF_BUCKET_CNT; b++ ) gui->bundle_rx_delay_hist_current->counts[ b ] = bundle_metrics[ MIDX( HISTOGRAM, BUNDLE, MESSAGE_RX_DELAY_NANOS ) + b ];
1754 :
1755 0 : gui->bundle_rx_delay_hist_reference->sum = bundle_metrics[ MIDX( HISTOGRAM, BUNDLE, MESSAGE_RX_DELAY_NANOS ) + FD_HISTF_BUCKET_CNT ];
1756 0 : for( ulong b=0; b<FD_HISTF_BUCKET_CNT; b++ ) gui->bundle_rx_delay_hist_reference->counts[ b ] = bundle_metrics[ MIDX( HISTOGRAM, BUNDLE, MESSAGE_RX_DELAY_NANOS ) + b ];
1757 0 : }
1758 0 : }
1759 :
1760 : void
1761 : fd_gui_unbecame_leader( fd_gui_t * gui,
1762 : long tickcount,
1763 : ulong _slot,
1764 0 : ulong microblocks_in_slot ) {
1765 0 : fd_gui_init_slot_txns( gui, tickcount, _slot );
1766 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1767 :
1768 0 : slot->txs.microblocks_upper_bound = (ushort)microblocks_in_slot;
1769 0 : }
1770 :
1771 : void
1772 : fd_gui_microblock_execution_begin( fd_gui_t * gui,
1773 : long tickcount,
1774 : ulong _slot,
1775 : fd_txn_p_t * txns,
1776 : ulong txn_cnt,
1777 : uint microblock_idx,
1778 0 : ulong pack_txn_idx ) {
1779 0 : fd_gui_init_slot_txns( gui, tickcount, _slot );
1780 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1781 :
1782 0 : if( FD_UNLIKELY( slot->txs.start_offset==ULONG_MAX ) ) slot->txs.start_offset = pack_txn_idx;
1783 0 : else slot->txs.start_offset = fd_ulong_min( slot->txs.start_offset, pack_txn_idx );
1784 :
1785 0 : gui->pack_txn_idx = fd_ulong_max( gui->pack_txn_idx, pack_txn_idx+txn_cnt-1UL );
1786 :
1787 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
1788 0 : fd_txn_p_t * txn_payload = &txns[ i ];
1789 0 : fd_txn_t * txn = TXN( txn_payload );
1790 :
1791 0 : ulong sig_rewards = FD_PACK_FEE_PER_SIGNATURE * txn->signature_cnt;
1792 0 : ulong priority_rewards = ULONG_MAX;
1793 0 : ulong requested_execution_cus = ULONG_MAX;
1794 0 : ulong precompile_sigs = ULONG_MAX;
1795 0 : ulong requested_loaded_accounts_data_cost = ULONG_MAX;
1796 0 : uint _flags;
1797 0 : ulong cost_estimate = fd_pack_compute_cost( txn, txn_payload->payload, &_flags, &requested_execution_cus, &priority_rewards, &precompile_sigs, &requested_loaded_accounts_data_cost );
1798 0 : sig_rewards += FD_PACK_FEE_PER_SIGNATURE * precompile_sigs;
1799 0 : sig_rewards = sig_rewards * FD_PACK_TXN_FEE_BURN_PCT / 100UL;
1800 :
1801 0 : fd_gui_txn_t * txn_entry = gui->txs[ (pack_txn_idx + i)%FD_GUI_TXN_HISTORY_SZ ];
1802 0 : fd_memcpy(txn_entry->signature, txn_payload->payload + txn->signature_off, FD_SHA512_HASH_SZ);
1803 0 : txn_entry->timestamp_arrival_nanos = txn_payload->scheduler_arrival_time_nanos;
1804 0 : txn_entry->compute_units_requested = cost_estimate & 0x1FFFFFU;
1805 0 : txn_entry->priority_fee = priority_rewards;
1806 0 : txn_entry->transaction_fee = sig_rewards;
1807 0 : txn_entry->timestamp_delta_start_nanos = (int)((double)(tickcount - slot->txs.reference_ticks) / fd_tempo_tick_per_ns( NULL ));
1808 0 : txn_entry->microblock_idx = microblock_idx;
1809 0 : txn_entry->flags |= (uchar)FD_GUI_TXN_FLAGS_STARTED;
1810 0 : txn_entry->flags &= (uchar)(~(uchar)(FD_GUI_TXN_FLAGS_IS_SIMPLE_VOTE | FD_GUI_TXN_FLAGS_FROM_BUNDLE));
1811 0 : txn_entry->flags |= (uchar)fd_uint_if(txn_payload->flags & FD_TXN_P_FLAGS_IS_SIMPLE_VOTE, FD_GUI_TXN_FLAGS_IS_SIMPLE_VOTE, 0U);
1812 0 : txn_entry->flags |= (uchar)fd_uint_if((txn_payload->flags & FD_TXN_P_FLAGS_BUNDLE) || (txn_payload->flags & FD_TXN_P_FLAGS_INITIALIZER_BUNDLE), FD_GUI_TXN_FLAGS_FROM_BUNDLE, 0U);
1813 0 : }
1814 :
1815 : /* At the moment, bank publishes at most 1 transaction per microblock,
1816 : even if it received microblocks with multiple transactions
1817 : (i.e. a bundle). This means that we need to calculate microblock
1818 : count here based on the transaction count. */
1819 0 : slot->txs.begin_microblocks = (ushort)(slot->txs.begin_microblocks + txn_cnt);
1820 0 : }
1821 :
1822 : void
1823 : fd_gui_microblock_execution_end( fd_gui_t * gui,
1824 : long tickcount,
1825 : ulong bank_idx,
1826 : ulong _slot,
1827 : ulong txn_cnt,
1828 : fd_txn_p_t * txns,
1829 : ulong pack_txn_idx,
1830 : uchar txn_start_pct,
1831 : uchar txn_load_end_pct,
1832 : uchar txn_end_pct,
1833 : uchar txn_preload_end_pct,
1834 0 : ulong tips ) {
1835 0 : if( FD_UNLIKELY( 1UL!=txn_cnt ) ) FD_LOG_ERR(( "gui expects 1 txn per microblock from bank, found %lu", txn_cnt ));
1836 :
1837 0 : fd_gui_init_slot_txns( gui, tickcount, _slot );
1838 0 : fd_gui_slot_t * slot = gui->slots[ _slot % FD_GUI_SLOTS_CNT ];
1839 :
1840 0 : if( FD_UNLIKELY( slot->txs.end_offset==ULONG_MAX ) ) slot->txs.end_offset = pack_txn_idx + txn_cnt;
1841 0 : else slot->txs.end_offset = fd_ulong_max( slot->txs.end_offset, pack_txn_idx+txn_cnt );
1842 :
1843 0 : gui->pack_txn_idx = fd_ulong_max( gui->pack_txn_idx, pack_txn_idx+txn_cnt-1UL );
1844 :
1845 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
1846 0 : fd_txn_p_t * txn_p = &txns[ i ];
1847 :
1848 0 : fd_gui_txn_t * txn_entry = gui->txs[ (pack_txn_idx + i)%FD_GUI_TXN_HISTORY_SZ ];
1849 0 : txn_entry->bank_idx = bank_idx & 0x3FU;
1850 0 : txn_entry->compute_units_consumed = txn_p->bank_cu.actual_consumed_cus & 0x1FFFFFU;
1851 0 : txn_entry->error_code = (txn_p->flags >> 24) & 0x3FU;
1852 0 : txn_entry->timestamp_delta_end_nanos = (int)((double)(tickcount - slot->txs.reference_ticks) / fd_tempo_tick_per_ns( NULL ));
1853 0 : txn_entry->txn_start_pct = txn_start_pct;
1854 0 : txn_entry->txn_load_end_pct = txn_load_end_pct;
1855 0 : txn_entry->txn_end_pct = txn_end_pct;
1856 0 : txn_entry->txn_preload_end_pct = txn_preload_end_pct;
1857 0 : txn_entry->tips = tips;
1858 0 : txn_entry->flags |= (uchar)FD_GUI_TXN_FLAGS_ENDED;
1859 0 : txn_entry->flags &= (uchar)(~(uchar)FD_GUI_TXN_FLAGS_LANDED_IN_BLOCK);
1860 0 : txn_entry->flags |= (uchar)fd_uint_if(txn_p->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS, FD_GUI_TXN_FLAGS_LANDED_IN_BLOCK, 0U);
1861 0 : }
1862 :
1863 0 : slot->txs.end_microblocks = slot->txs.end_microblocks + (uint)txn_cnt;
1864 0 : }
|