Skip to content

Commit edcb8e0

Browse files
committed
gui: leader schedule + peer vote info
1 parent b96b0bc commit edcb8e0

File tree

13 files changed

+482
-104
lines changed

13 files changed

+482
-104
lines changed

book/api/websocket.md

Lines changed: 35 additions & 33 deletions
Large diffs are not rendered by default.

src/app/firedancer/topology.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ fd_topo_initialize( config_t * config ) {
257257

258258
fd_topob_wksp( topo, "shred_out" );
259259
fd_topob_wksp( topo, "replay_stake" );
260+
fd_topob_wksp( topo, "replay_votes" );
260261
fd_topob_wksp( topo, "replay_exec" );
261262
fd_topob_wksp( topo, "replay_out" );
262263
fd_topob_wksp( topo, "tower_out" );
@@ -344,6 +345,9 @@ fd_topo_initialize( config_t * config ) {
344345
/**/ fd_topob_link( topo, "dedup_resolv", "dedup_resolv", 65536UL, FD_TPU_PARSED_MTU, 1UL );
345346
FOR(resolv_tile_cnt) fd_topob_link( topo, "resolv_pack", "resolv_pack", 65536UL, FD_TPU_RESOLVED_MTU, 1UL );
346347
/**/ fd_topob_link( topo, "replay_stake", "replay_stake", 128UL, FD_STAKE_OUT_MTU, 1UL ); /* TODO: This should be 2 but requires fixing STEM_BURST */
348+
if( FD_LIKELY( config->tiles.gui.enabled ) ) { /* the gui, which is optional, is the only consumer of replay_votes */
349+
fd_topob_link( topo, "replay_votes", "replay_votes", 128UL, sizeof(fd_replay_votes_t), 1UL );
350+
}
347351
/**/ fd_topob_link( topo, "replay_out", "replay_out", 8192UL, sizeof(fd_replay_message_t), 1UL );
348352
/**/ fd_topob_link( topo, "pack_poh", "pack_poh", 128UL, sizeof(fd_done_packing_t), 1UL );
349353
/* pack_bank is shared across all banks, so if one bank stalls due to complex transactions, the buffer neeeds to be large so that
@@ -503,6 +507,9 @@ fd_topo_initialize( config_t * config ) {
503507
/**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
504508
/**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_out", 0UL );
505509
/**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_stake", 0UL );
510+
if( FD_LIKELY( config->tiles.gui.enabled ) ) { /* the gui, which is optional, is the only consumer of replay_votes */
511+
fd_topob_tile_out( topo, "replay", 0UL, "replay_votes", 0UL );
512+
}
506513
/**/ fd_topob_tile_out( topo, "replay", 0UL, "executed_txn", 0UL );
507514
/**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_exec", 0UL );
508515
/**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "tower_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
@@ -668,6 +675,9 @@ fd_topo_initialize( config_t * config ) {
668675
/**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "gossip_net", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
669676
/**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "gossip_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
670677
/**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
678+
/**/ fd_topob_tile_in ( topo, "gui", 0UL, "metric_in", "replay_stake", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
679+
/**/ fd_topob_tile_in ( topo, "gui", 0UL, "metric_in", "replay_votes", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
680+
/**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "tower_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
671681

672682
if( FD_LIKELY( snapshots_enabled ) )
673683
fd_topob_tile_in ( topo, "gui", 0UL, "metric_in", "snaprd_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );

src/disco/gui/fd_gui.c

Lines changed: 103 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,16 @@ fd_gui_ws_open( fd_gui_t * gui,
221221
FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
222222
}
223223

224+
/* todo .. temporary workaround to skip the blur until frontend boot
225+
screen lands */
226+
if( FD_UNLIKELY( gui->summary.is_full_client ) ) {
227+
uchar prev = gui->summary.startup_progress.phase;
228+
gui->summary.startup_progress.phase = FD_GUI_START_PROGRESS_TYPE_RUNNING;
229+
fd_gui_printf_startup_progress( gui );
230+
FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
231+
gui->summary.startup_progress.phase = prev;
232+
}
233+
224234
if( FD_LIKELY( gui->block_engine.has_block_engine ) ) {
225235
fd_gui_printf_block_engine( gui );
226236
FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
@@ -632,8 +642,7 @@ fd_gui_run_boot_progress( fd_gui_t * gui, long now ) {
632642
gui->summary.boot_progress.phase = FD_GUI_BOOT_PROGRESS_TYPE_LOADING_INCREMENTAL_SNAPSHOT;
633643
gui->summary.boot_progress.loading_snapshot[ FD_GUI_BOOT_PROGRESS_INCREMENTAL_SNAPSHOT_IDX ].sample_time_nanos = now;
634644
}
635-
int slot_turbine_hist_full = gui->summary.slots_max_turbine[ FD_GUI_TURBINE_SLOT_HISTORY_SZ-1 ].slot!=ULONG_MAX;
636-
if( FD_UNLIKELY( snapshot_phase == 12UL && slot_turbine_hist_full && gui->summary.slot_completed!=ULONG_MAX ) ) {
645+
if( FD_UNLIKELY( snapshot_phase == 12UL && gui->summary.slots_max_turbine[ 0 ].slot!=ULONG_MAX && gui->summary.slot_completed!=ULONG_MAX ) ) {
637646
gui->summary.boot_progress.phase = FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP;
638647
gui->summary.boot_progress.catching_up_time_nanos = now;
639648
}
@@ -1344,56 +1353,49 @@ fd_gui_clear_slot( fd_gui_t * gui,
13441353
}
13451354
}
13461355

1347-
static void
1348-
fd_gui_handle_leader_schedule( fd_gui_t * gui,
1349-
ulong const * msg,
1350-
long now ) {
1351-
ulong epoch = msg[ 0 ];
1352-
ulong staked_cnt = msg[ 1 ];
1353-
ulong start_slot = msg[ 2 ];
1354-
ulong slot_cnt = msg[ 3 ];
1355-
ulong excluded_stake = msg[ 4 ];
1356-
ulong vote_keyed_lsched = msg[ 5 ];
1357-
1358-
FD_TEST( staked_cnt<=MAX_STAKED_LEADERS );
1359-
FD_TEST( slot_cnt<=MAX_SLOTS_PER_EPOCH );
1360-
1361-
ulong idx = epoch % 2UL;
1356+
void
1357+
fd_gui_handle_leader_schedule( fd_gui_t * gui,
1358+
fd_stake_weight_msg_t const * leader_schedule,
1359+
long now ) {
1360+
FD_TEST( leader_schedule->staked_cnt<=MAX_STAKED_LEADERS );
1361+
FD_TEST( leader_schedule->slot_cnt<=MAX_SLOTS_PER_EPOCH );
1362+
1363+
ulong idx = leader_schedule->epoch % 2UL;
13621364
gui->epoch.has_epoch[ idx ] = 1;
13631365

1364-
gui->epoch.epochs[ idx ].epoch = epoch;
1365-
gui->epoch.epochs[ idx ].start_slot = start_slot;
1366-
gui->epoch.epochs[ idx ].end_slot = start_slot + slot_cnt - 1; // end_slot is inclusive.
1367-
gui->epoch.epochs[ idx ].excluded_stake = excluded_stake;
1366+
gui->epoch.epochs[ idx ].epoch = leader_schedule->epoch;
1367+
gui->epoch.epochs[ idx ].start_slot = leader_schedule->start_slot;
1368+
gui->epoch.epochs[ idx ].end_slot = leader_schedule->start_slot + leader_schedule->slot_cnt - 1; // end_slot is inclusive.
1369+
gui->epoch.epochs[ idx ].excluded_stake = leader_schedule->excluded_stake;
13681370
gui->epoch.epochs[ idx ].my_total_slots = 0UL;
13691371
gui->epoch.epochs[ idx ].my_skipped_slots = 0UL;
13701372

13711373
memset( gui->epoch.epochs[ idx ].rankings, (int)(UINT_MAX), sizeof(gui->epoch.epochs[ idx ].rankings) );
13721374
memset( gui->epoch.epochs[ idx ].my_rankings, (int)(UINT_MAX), sizeof(gui->epoch.epochs[ idx ].my_rankings) );
13731375

1374-
gui->epoch.epochs[ idx ].rankings_slot = start_slot;
1376+
gui->epoch.epochs[ idx ].rankings_slot = leader_schedule->start_slot;
13751377

1376-
fd_vote_stake_weight_t const * stake_weights = fd_type_pun_const( msg+6UL );
1377-
memcpy( gui->epoch.epochs[ idx ].stakes, stake_weights, staked_cnt*sizeof(fd_vote_stake_weight_t) );
1378+
fd_vote_stake_weight_t const * stake_weights = leader_schedule->weights;
1379+
fd_memcpy( gui->epoch.epochs[ idx ].stakes, stake_weights, leader_schedule->staked_cnt*sizeof(fd_vote_stake_weight_t) );
13781380

13791381
fd_epoch_leaders_delete( fd_epoch_leaders_leave( gui->epoch.epochs[ idx ].lsched ) );
13801382
gui->epoch.epochs[idx].lsched = fd_epoch_leaders_join( fd_epoch_leaders_new( gui->epoch.epochs[ idx ]._lsched,
1381-
epoch,
1383+
leader_schedule->epoch,
13821384
gui->epoch.epochs[ idx ].start_slot,
1383-
slot_cnt,
1384-
staked_cnt,
1385+
leader_schedule->slot_cnt,
1386+
leader_schedule->staked_cnt,
13851387
gui->epoch.epochs[ idx ].stakes,
1386-
excluded_stake,
1387-
vote_keyed_lsched ) );
1388+
leader_schedule->excluded_stake,
1389+
leader_schedule->vote_keyed_lsched ) );
13881390

1389-
if( FD_UNLIKELY( start_slot==0UL ) ) {
1391+
if( FD_UNLIKELY( leader_schedule->start_slot==0UL ) ) {
13901392
gui->epoch.epochs[ 0 ].start_time = now;
13911393
} else {
13921394
gui->epoch.epochs[ idx ].start_time = LONG_MAX;
13931395

1394-
for( ulong i=0UL; i<fd_ulong_min( start_slot-1UL, FD_GUI_SLOTS_CNT ); i++ ) {
1395-
fd_gui_slot_t * slot = gui->slots[ (start_slot-i) % FD_GUI_SLOTS_CNT ];
1396-
if( FD_UNLIKELY( slot->slot!=(start_slot-i) ) ) break;
1396+
for( ulong i=0UL; i<fd_ulong_min( leader_schedule->start_slot-1UL, FD_GUI_SLOTS_CNT ); i++ ) {
1397+
fd_gui_slot_t * slot = gui->slots[ (leader_schedule->start_slot-i) % FD_GUI_SLOTS_CNT ];
1398+
if( FD_UNLIKELY( slot->slot!=(leader_schedule->start_slot-i) ) ) break;
13971399
else if( FD_UNLIKELY( slot->skipped ) ) continue;
13981400

13991401
gui->epoch.epochs[ idx ].start_time = slot->completed_time;
@@ -1773,8 +1775,7 @@ fd_gui_handle_rooted_slot( fd_gui_t * gui,
17731775
if( FD_UNLIKELY( slot->slot==ULONG_MAX) ) break;
17741776

17751777
if( FD_UNLIKELY( slot->slot!=parent_slot ) ) {
1776-
FD_LOG_ERR(( "_slot %lu i %lu we expect parent_slot %lu got slot->slot %lu", _slot, i, parent_slot, slot->slot ));
1777-
}
1778+
FD_LOG_ERR(( "_slot %lu i %lu we expect parent_slot %lu got slot->slot %lu", _slot, i, parent_slot, slot->slot )); }
17781779
if( FD_UNLIKELY( slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) break;
17791780

17801781
slot->level = FD_GUI_SLOT_LEVEL_ROOTED;
@@ -2069,7 +2070,8 @@ fd_gui_handle_tower_update( fd_gui_t * gui,
20692070
long now ) {
20702071
(void)now;
20712072

2072-
if( FD_LIKELY( gui->summary.slot_rooted!=tower->root_slot ) ) {
2073+
/* rooted slot */
2074+
if( FD_LIKELY( tower->root_slot!=ULONG_MAX && gui->summary.slot_rooted!=tower->root_slot ) ) {
20732075
/* Move any shred data up to and including _slot from staging array
20742076
to history array */
20752077
ulong start_slot = fd_ulong_if( gui->shreds.history_slot!=ULONG_MAX, gui->shreds.history_slot+1UL, tower->root_slot );
@@ -2112,11 +2114,71 @@ fd_gui_handle_tower_update( fd_gui_t * gui,
21122114
gui->shreds.staged_next_broadcast to keep it in the bounds of the
21132115
ring buffer */
21142116
gui->shreds.staged_next_broadcast = fd_ulong_max( gui->shreds.staged_head, gui->shreds.staged_next_broadcast );
2117+
2118+
gui->summary.slot_rooted = tower->root_slot;
2119+
fd_gui_printf_root_slot( gui );
2120+
fd_http_server_ws_broadcast( gui->http );
2121+
2122+
fd_gui_printf_slot( gui, gui->summary.slot_rooted );
2123+
fd_http_server_ws_broadcast( gui->http );
21152124
}
21162125

2117-
gui->summary.slot_rooted = tower->root_slot;
2118-
fd_gui_printf_root_slot( gui );
2119-
fd_http_server_ws_broadcast( gui->http );
2126+
/* optimisitically confirmed slot */
2127+
if( FD_UNLIKELY( tower->opt_confirmed_slot!=ULONG_MAX ) ) {
2128+
/* Slot 0 is always rooted. No need to iterate all the way back to
2129+
i==_slot */
2130+
for( ulong i=0UL; i<fd_ulong_min( tower->opt_confirmed_slot, FD_GUI_SLOTS_CNT ); i++ ) {
2131+
ulong parent_slot = tower->opt_confirmed_slot - i;
2132+
ulong parent_idx = parent_slot % FD_GUI_SLOTS_CNT;
2133+
2134+
fd_gui_slot_t * slot = gui->slots[ parent_idx ];
2135+
if( FD_UNLIKELY( slot->slot==ULONG_MAX) ) break;
2136+
2137+
if( FD_UNLIKELY( slot->slot>parent_slot ) ) {
2138+
FD_LOG_ERR(( "_slot %lu i %lu we expect parent_slot %lu got slot->slot %lu", tower->opt_confirmed_slot, i, parent_slot, slot->slot ));
2139+
} else if( FD_UNLIKELY( slot->slot<parent_slot ) ) {
2140+
/* Slot not even replayed yet ... will come out as optimistically confirmed */
2141+
continue;
2142+
}
2143+
if( FD_UNLIKELY( slot->level>=FD_GUI_SLOT_LEVEL_ROOTED ) ) break;
2144+
2145+
if( FD_LIKELY( slot->level<FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED ) ) {
2146+
slot->level = FD_GUI_SLOT_LEVEL_OPTIMISTICALLY_CONFIRMED;
2147+
fd_gui_printf_slot( gui, parent_slot );
2148+
fd_http_server_ws_broadcast( gui->http );
2149+
}
2150+
}
2151+
2152+
if( FD_UNLIKELY( gui->summary.slot_optimistically_confirmed!=ULONG_MAX && tower->opt_confirmed_slot<gui->summary.slot_optimistically_confirmed ) ) {
2153+
/* Optimistically confirmed slot went backwards ... mark some slots as no
2154+
longer optimistically confirmed. */
2155+
for( ulong i=gui->summary.slot_optimistically_confirmed; i>=tower->opt_confirmed_slot; i-- ) {
2156+
fd_gui_slot_t * slot = gui->slots[ i % FD_GUI_SLOTS_CNT ];
2157+
if( FD_UNLIKELY( slot->slot==ULONG_MAX ) ) break;
2158+
if( FD_LIKELY( slot->slot==i ) ) {
2159+
/* It's possible for the optimistically confirmed slot to skip
2160+
backwards between two slots that we haven't yet replayed. In
2161+
that case we don't need to change anything, since they will
2162+
get marked properly when they get completed. */
2163+
slot->level = FD_GUI_SLOT_LEVEL_COMPLETED;
2164+
fd_gui_printf_slot( gui, i );
2165+
fd_http_server_ws_broadcast( gui->http );
2166+
}
2167+
}
2168+
}
2169+
2170+
int slot_turbine_hist_full = gui->summary.slots_max_turbine[ FD_GUI_TURBINE_SLOT_HISTORY_SZ-1UL ].slot!=ULONG_MAX;
2171+
if( FD_UNLIKELY( gui->summary.slot_caught_up==ULONG_MAX && slot_turbine_hist_full && gui->summary.slots_max_turbine[ 0 ].slot < (tower->opt_confirmed_slot + 3UL) ) ) {
2172+
gui->summary.slot_caught_up = tower->opt_confirmed_slot + 4UL;
2173+
2174+
fd_gui_printf_slot_caught_up( gui );
2175+
fd_http_server_ws_broadcast( gui->http );
2176+
}
2177+
2178+
gui->summary.slot_optimistically_confirmed = tower->opt_confirmed_slot;
2179+
fd_gui_printf_optimistically_confirmed_slot( gui );
2180+
fd_http_server_ws_broadcast( gui->http );
2181+
}
21202182
}
21212183

21222184
void
@@ -2161,7 +2223,8 @@ fd_gui_plugin_message( fd_gui_t * gui,
21612223
break;
21622224
}
21632225
case FD_PLUGIN_MSG_LEADER_SCHEDULE: {
2164-
fd_gui_handle_leader_schedule( gui, (ulong const *)msg, now );
2226+
FD_STATIC_ASSERT( sizeof(fd_stake_weight_msg_t)==6*sizeof(ulong), "sanity check" );
2227+
fd_gui_handle_leader_schedule( gui, (fd_stake_weight_msg_t *)msg, now );
21652228
break;
21662229
}
21672230
case FD_PLUGIN_MSG_SLOT_START: {

src/disco/gui/fd_gui.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "../../discof/restore/fd_snaprd_tile.h"
1313
#include "../../discof/replay/fd_replay_tile.h"
1414
#include "../../discof/tower/fd_tower_tile.h"
15+
#include "../../choreo/fd_choreo_base.h"
1516

1617
#include "../topo/fd_topo.h"
1718

@@ -739,9 +740,19 @@ void
739740
fd_gui_handle_repair_slot( fd_gui_t * gui, ulong slot, long now );
740741

741742
void
742-
fd_gui_handle_snapshot_update( fd_gui_t * gui,
743+
fd_gui_handle_snapshot_update( fd_gui_t * gui,
743744
fd_snaprd_update_t * msg );
744745

746+
void
747+
fd_gui_handle_leader_schedule( fd_gui_t * gui,
748+
fd_stake_weight_msg_t const * leader_schedule,
749+
long now );
750+
751+
void
752+
fd_gui_handle_tower_update( fd_gui_t * gui,
753+
fd_tower_slot_done_t const * tower,
754+
long now );
755+
745756
void
746757
fd_gui_handle_replay_update( fd_gui_t * gui,
747758
fd_replay_slot_completed_t const * replay,

0 commit comments

Comments
 (0)