diff --git a/book/api/metrics-generated.md b/book/api/metrics-generated.md
index 304f5cde0c7..c8d9fb61f7a 100644
--- a/book/api/metrics-generated.md
+++ b/book/api/metrics-generated.md
@@ -1128,3 +1128,27 @@
| snapwh_state | gauge | State of the tile. 0=IDLE, 1=PROCESSING, 4=SHUTDOWN |
+
+## Snapla Tile
+
+
+
+| Metric | Type | Description |
+|--------|------|-------------|
+| snapla_state | gauge | State of the tile. 0=IDLE, 1=PROCESSING, 2=FINISHING, 3=ERROR, 4=SHUTDOWN |
+| snapla_full_accounts_hashed | gauge | Number of accounts hashed for the full snapshot during snapshot loading. Might decrease if snapshot load is aborted and restarted |
+| snapla_incremental_accounts_hashed | gauge | Number of accounts hashed for the incremental snapshot during snapshot loading. Might decrease if snapshot load is aborted and restarted |
+
+
+
+## Snapls Tile
+
+
+
+| Metric | Type | Description |
+|--------|------|-------------|
+| snapls_state | gauge | State of the tile. 0=IDLE, 1=PROCESSING, 2=FINISHING, 3=ERROR, 4=SHUTDOWN |
+| snapls_full_accounts_hashed | gauge | Number of accounts hashed for the full snapshot during snapshot loading. Might decrease if snapshot load is aborted and restarted |
+| snapls_incremental_accounts_hashed | gauge | Number of accounts hashed for the incremental snapshot during snapshot loading. Might decrease if snapshot load is aborted and restarted |
+
+
diff --git a/src/app/firedancer-dev/commands/backtest.c b/src/app/firedancer-dev/commands/backtest.c
index ae069650050..23129ac596f 100644
--- a/src/app/firedancer-dev/commands/backtest.c
+++ b/src/app/firedancer-dev/commands/backtest.c
@@ -47,10 +47,12 @@ backtest_topo( config_t * config ) {
config->development.no_clone = 1;
ulong exec_tile_cnt = config->firedancer.layout.exec_tile_count;
+ ulong lta_tile_cnt = config->firedancer.layout.snapla_tile_count;
- int disable_snap_loader = !config->gossip.entrypoints_cnt;
- int snap_vinyl = !!config->firedancer.vinyl.enabled;
- int solcap_enabled = strlen( config->capture.solcap_capture )>0;
+ int disable_snap_loader = !config->gossip.entrypoints_cnt;
+ int snap_vinyl = !!config->firedancer.vinyl.enabled;
+ int solcap_enabled = strlen( config->capture.solcap_capture )>0;
+ int snapshot_lthash_disabled = config->development.snapshots.disable_lthash_verification;
fd_topo_t * topo = { fd_topob_new( &config->topo, config->name ) };
topo->max_page_size = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
@@ -109,10 +111,20 @@ backtest_topo( config_t * config ) {
fd_topob_wksp( topo, "snapld" );
fd_topob_wksp( topo, "snapdc" );
fd_topob_wksp( topo, "snapin" );
+
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ fd_topob_wksp( topo, "snapla" );
+ fd_topob_wksp( topo, "snapls" );
+ }
+
fd_topo_tile_t * snapct_tile = fd_topob_tile( topo, "snapct", "snapct", "metric_in", cpu_idx++, 0, 0 );
fd_topo_tile_t * snapld_tile = fd_topob_tile( topo, "snapld", "snapld", "metric_in", cpu_idx++, 0, 0 );
fd_topo_tile_t * snapdc_tile = fd_topob_tile( topo, "snapdc", "snapdc", "metric_in", cpu_idx++, 0, 0 );
snapin_tile = fd_topob_tile( topo, "snapin", "snapin", "metric_in", cpu_idx++, 0, 0 );
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ FOR(lta_tile_cnt) fd_topob_tile( topo, "snapla", "snapla", "metric_in", cpu_idx++, 0, 0 )->allow_shutdown = 1;
+ /**/ fd_topob_tile( topo, "snapls", "snapls", "metric_in", cpu_idx++, 0, 0 )->allow_shutdown = 1;
+ }
snapct_tile->allow_shutdown = 1;
snapld_tile->allow_shutdown = 1;
snapdc_tile->allow_shutdown = 1;
@@ -143,18 +155,40 @@ backtest_topo( config_t * config ) {
fd_topob_wksp( topo, "snapct_ld" );
fd_topob_wksp( topo, "snapld_dc" );
fd_topob_wksp( topo, "snapdc_in" );
- fd_topob_wksp( topo, "snapin_ct" );
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_wksp( topo, "snapin_ct" );
+ } else {
+ fd_topob_wksp( topo, "snapls_ct" );
+ }
+
fd_topob_wksp( topo, "snapin_manif" );
fd_topob_wksp( topo, "snapct_repr" );
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ fd_topob_wksp( topo, "snapla_ls" );
+ fd_topob_wksp( topo, "snapin_ls" );
+ }
+
fd_topob_link( topo, "snapct_ld", "snapct_ld", 128UL, sizeof(fd_ssctrl_init_t), 1UL );
fd_topob_link( topo, "snapld_dc", "snapld_dc", 16384UL, USHORT_MAX, 1UL );
fd_topob_link( topo, "snapdc_in", "snapdc_in", 16384UL, USHORT_MAX, 1UL );
- fd_topob_link( topo, "snapin_ct", "snapin_ct", 128UL, 0UL, 1UL );
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_link( topo, "snapin_ct", "snapin_ct", 128UL, 0UL, 1UL );
+ }
fd_topob_link( topo, "snapin_manif", "snapin_manif", 4UL, sizeof(fd_snapshot_manifest_t), 1UL ); /* TODO: Should be depth 1 or 2 but replay backpressures */
fd_topob_link( topo, "snapct_repr", "snapct_repr", 128UL, 0UL, 1UL )->permit_no_consumers = 1;
- fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapin_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ FOR(lta_tile_cnt) fd_topob_link( topo, "snapla_ls", "snapla_ls", 128UL, sizeof(fd_lthash_value_t), 1UL );
+ /**/ fd_topob_link( topo, "snapin_ls", "snapin_ls", 256UL, sizeof(fd_snapshot_full_account_t), 1UL );
+ /**/ fd_topob_link( topo, "snapls_ct", "snapls_ct", 128UL, 0UL, 1UL );
+ }
+
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapin_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ } else {
+ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapls_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ }
fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
fd_topob_tile_out( topo, "snapct", 0UL, "snapct_ld", 0UL );
fd_topob_tile_out( topo, "snapct", 0UL, "snapct_repr", 0UL );
@@ -163,9 +197,22 @@ backtest_topo( config_t * config ) {
fd_topob_tile_in ( topo, "snapdc", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
fd_topob_tile_out( topo, "snapdc", 0UL, "snapdc_in", 0UL );
fd_topob_tile_in ( topo, "snapin", 0UL, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
- fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL );
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL );
+ } else {
+ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ls", 0UL );
+ }
+
fd_topob_tile_out( topo, "snapin", 0UL, "snapin_manif", 0UL );
fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ FOR(lta_tile_cnt) fd_topob_tile_in ( topo, "snapla", i, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ FOR(lta_tile_cnt) fd_topob_tile_out( topo, "snapla", i, "snapla_ls", i );
+ /**/ fd_topob_tile_in ( topo, "snapls", 0UL, "metric_in", "snapin_ls", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ FOR(lta_tile_cnt) fd_topob_tile_in ( topo, "snapls", 0UL, "metric_in", "snapla_ls", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ /**/ fd_topob_tile_out( topo, "snapls", 0UL, "snapls_ct", 0UL );
+ }
} else {
fd_topob_wksp( topo, "genesi_out" );
fd_topob_link( topo, "genesi_out", "genesi_out", 2UL, 10UL*1024UL*1024UL+32UL+sizeof(fd_lthash_value_t), 1UL );
diff --git a/src/app/firedancer-dev/commands/snapshot_load.c b/src/app/firedancer-dev/commands/snapshot_load.c
index 2e4b8e891f8..675a5657bf1 100644
--- a/src/app/firedancer-dev/commands/snapshot_load.c
+++ b/src/app/firedancer-dev/commands/snapshot_load.c
@@ -14,6 +14,7 @@
#include "../../../funk/fd_funk.h"
#include "../../../vinyl/fd_vinyl.h"
#include "../../../tango/cnc/fd_cnc.h"
+#include "../../../ballet/lthash/fd_lthash.h"
#include
#include /* open */
@@ -47,6 +48,9 @@ snapshot_load_topo( config_t * config ) {
config->firedancer.funk.max_database_transactions,
config->firedancer.funk.heap_size_gib );
+ int snapshot_lthash_disabled = config->development.snapshots.disable_lthash_verification;
+ ulong lta_tile_cnt = config->firedancer.layout.snapla_tile_count;
+
if( config->firedancer.vinyl.enabled ) {
setup_topo_vinyl_meta( topo, &config->firedancer );
}
@@ -92,17 +96,36 @@ snapshot_load_topo( config_t * config ) {
fd_topob_wksp( topo, "snapct_ld" );
fd_topob_wksp( topo, "snapld_dc" );
fd_topob_wksp( topo, "snapdc_in" );
- fd_topob_wksp( topo, "snapin_ct" );
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_wksp( topo, "snapin_ct" );
+ }
fd_topob_wksp( topo, "snapin_manif" );
fd_topob_wksp( topo, "snapct_repr" );
if( vinyl_enabled ) {
fd_topob_wksp( topo, "snapin_wr" );
}
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ fd_topob_wksp( topo, "snapla" );
+ fd_topob_wksp( topo, "snapls" );
+ fd_topob_wksp( topo, "snapla_ls" );
+ fd_topob_wksp( topo, "snapin_ls" );
+ fd_topob_wksp( topo, "snapls_ct" );
+ }
+
+#define FOR(cnt) for( ulong i=0UL; iallow_shutdown = 1;
+ /**/ fd_topob_tile( topo, "snapls", "snapls", "metric_in", ULONG_MAX, 0, 0 )->allow_shutdown = 1;
+ }
+
fd_topob_link( topo, "snapct_ld", "snapct_ld", 128UL, sizeof(fd_ssctrl_init_t), 1UL );
fd_topob_link( topo, "snapld_dc", "snapld_dc", 16384UL, USHORT_MAX, 1UL );
fd_topob_link( topo, "snapdc_in", "snapdc_in", 16384UL, USHORT_MAX, 1UL );
- fd_topob_link( topo, "snapin_ct", "snapin_ct", 128UL, 0UL, 1UL );
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_link( topo, "snapin_ct", "snapin_ct", 128UL, 0UL, 1UL );
+ }
fd_topob_link( topo, "snapin_manif", "snapin_manif", 2UL, sizeof(fd_snapshot_manifest_t), 1UL )->permit_no_consumers = 1;
fd_topob_link( topo, "snapct_repr", "snapct_repr", 128UL, 0UL, 1UL )->permit_no_consumers = 1;
if( vinyl_enabled ) {
@@ -111,7 +134,17 @@ snapshot_load_topo( config_t * config ) {
fd_pod_insertf_ulong( topo->props, 8UL, "obj.%lu.app_sz", snapin_wh->dcache_obj_id );
}
- fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapin_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ FOR(lta_tile_cnt) fd_topob_link( topo, "snapla_ls", "snapla_ls", 128UL, sizeof(fd_lthash_value_t), 1UL );
+ /**/ fd_topob_link( topo, "snapin_ls", "snapin_ls", 256UL, sizeof(fd_snapshot_full_account_t), 1UL );
+ /**/ fd_topob_link( topo, "snapls_ct", "snapls_ct", 128UL, 0UL, 1UL );
+ }
+
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapin_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ } else {
+ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapls_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ }
fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
fd_topob_tile_out( topo, "snapct", 0UL, "snapct_ld", 0UL );
fd_topob_tile_out( topo, "snapct", 0UL, "snapct_repr", 0UL );
@@ -120,7 +153,11 @@ snapshot_load_topo( config_t * config ) {
fd_topob_tile_in ( topo, "snapdc", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
fd_topob_tile_out( topo, "snapdc", 0UL, "snapdc_in", 0UL );
fd_topob_tile_in ( topo, "snapin", 0UL, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
- fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL );
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL );
+ } else {
+ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ls", 0UL );
+ }
fd_topob_tile_out( topo, "snapin", 0UL, "snapin_manif", 0UL );
if( vinyl_enabled ) {
fd_topob_tile_out( topo, "snapin", 0UL, "snapin_wh", 0UL );
@@ -129,6 +166,13 @@ snapshot_load_topo( config_t * config ) {
fd_topob_tile_in ( topo, "snapwr", 0UL, "metric_in", "snapwh_wr", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
fd_topob_tile_uses( topo, snapwr_tile, &topo->objs[ topo->links[ fd_topo_find_link( topo, "snapin_wh", 0UL ) ].dcache_obj_id ], FD_SHMEM_JOIN_MODE_READ_ONLY );
}
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ FOR(lta_tile_cnt) fd_topob_tile_in ( topo, "snapla", i, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ FOR(lta_tile_cnt) fd_topob_tile_out( topo, "snapla", i, "snapla_ls", i );
+ /**/ fd_topob_tile_in ( topo, "snapls", 0UL, "metric_in", "snapin_ls", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ FOR(lta_tile_cnt) fd_topob_tile_in ( topo, "snapls", 0UL, "metric_in", "snapla_ls", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ /**/ fd_topob_tile_out( topo, "snapls", 0UL, "snapls_ct", 0UL );
+ }
/* snapin funk / txncache access */
fd_topob_tile_uses( topo, snapin_tile, funk_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
@@ -574,6 +618,8 @@ snapshot_load_cmd_fn( args_t * args,
ulong snapwr_idx = fd_topo_find_tile( topo, "snapwr", 0UL );
fd_topo_tile_t * snapwh_tile = snapwh_idx!=ULONG_MAX ? &topo->tiles[ snapwh_idx ] : NULL;
fd_topo_tile_t * snapwr_tile = snapwr_idx!=ULONG_MAX ? &topo->tiles[ snapwr_idx ] : NULL;
+ ulong snapls_idx = fd_topo_find_tile( topo, "snapls", 0UL );
+ fd_topo_tile_t * snapls_tile = snapls_idx!=ULONG_MAX ? &topo->tiles[ snapls_idx ] : NULL;
double tick_per_ns = fd_tempo_tick_per_ns( NULL );
double ns_per_tick = 1.0/tick_per_ns;
@@ -587,6 +633,7 @@ snapshot_load_cmd_fn( args_t * args,
ulong volatile * const snapin_metrics = fd_metrics_tile( snapin_tile->metrics );
ulong volatile * const snapwh_metrics = snapwh_tile ? fd_metrics_tile( snapwh_tile->metrics ) : NULL;
ulong volatile * const snapwr_metrics = snapwr_tile ? fd_metrics_tile( snapwr_tile->metrics ) : NULL;
+ ulong volatile * const snapls_metrics = snapls_tile ? fd_metrics_tile( snapls_tile->metrics ) : NULL;
ulong total_off_old = 0UL;
ulong decomp_off_old = 0UL;
@@ -601,6 +648,8 @@ snapshot_load_cmd_fn( args_t * args,
ulong snapin_wait_old = 0UL;
ulong snapwh_wait_old = 0UL;
ulong snapwr_wait_old = 0UL;
+ ulong snapls_backp_old = 0UL;
+ ulong snapls_wait_old = 0UL;
ulong acc_cnt_old = 0UL;
sleep( 1 );
@@ -615,7 +664,8 @@ snapshot_load_cmd_fn( args_t * args,
puts( "" );
fputs( "--------------------------------------------", stdout );
if( snapwr_tile ) fputs( "--------------", stdout );
- fputs( "[ct],[ld],[dc],[in]--------[ct],[ld],[dc],[in]", stdout );
+ if( snapls_tile ) fputs( "[ct],[ld],[dc],[in],[lts]--------[ct],[ld],[dc],[in],[lts]", stdout );
+ else fputs( "[ct],[ld],[dc],[in]--------[ct],[ld],[dc],[in]", stdout );
if( snapwr_tile ) fputs( ",[wh],[wr]" , stdout );
puts( "--------------" );
}
@@ -626,8 +676,9 @@ snapshot_load_cmd_fn( args_t * args,
ulong snapld_status = FD_VOLATILE_CONST( snapld_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
ulong snapdc_status = FD_VOLATILE_CONST( snapdc_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
ulong snapin_status = FD_VOLATILE_CONST( snapin_metrics[ MIDX( GAUGE, TILE, STATUS ) ] );
+ ulong snapls_status = snapls_metrics ? FD_VOLATILE_CONST( snapls_metrics[ MIDX( GAUGE, TILE, STATUS ) ] ) : 2UL;
- if( FD_UNLIKELY( snapct_status==2UL && snapld_status==2UL && snapdc_status==2UL && snapin_status == 2UL ) ) break;
+ if( FD_UNLIKELY( snapct_status==2UL && snapld_status==2UL && snapdc_status==2UL && snapin_status==2UL && snapls_status==2UL ) ) break;
long cur = fd_log_wallclock();
if( FD_UNLIKELY( curfiredancer.layout.gossvf_tile_count;
ulong exec_tile_cnt = config->firedancer.layout.exec_tile_count;
ulong sign_tile_cnt = config->firedancer.layout.sign_tile_count;
+ ulong lta_tile_cnt = config->firedancer.layout.snapla_tile_count;
int snapshots_enabled = !!config->gossip.entrypoints_cnt;
int vinyl_enabled = !!config->firedancer.vinyl.enabled;
+ int snapshot_lthash_disabled = config->development.snapshots.disable_lthash_verification;
fd_topo_t * topo = fd_topob_new( &config->topo, config->name );
@@ -381,13 +383,25 @@ fd_topo_initialize( config_t * config ) {
fd_topob_wksp( topo, "snapct_ld" );
fd_topob_wksp( topo, "snapld_dc" );
fd_topob_wksp( topo, "snapdc_in" );
- fd_topob_wksp( topo, "snapin_ct" );
if( vinyl_enabled ) fd_topob_wksp( topo, "snapin_wr" );
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_wksp( topo, "snapin_ct" );
+ } else {
+ fd_topob_wksp( topo, "snapls_ct" );
+ }
+
if( FD_LIKELY( config->tiles.gui.enabled ) ) fd_topob_wksp( topo, "snapct_gui" );
if( FD_LIKELY( config->tiles.gui.enabled ) ) fd_topob_wksp( topo, "snapin_gui" );
fd_topob_wksp( topo, "snapin_manif" );
fd_topob_wksp( topo, "snapct_repr" );
+
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ fd_topob_wksp( topo, "snapla" );
+ fd_topob_wksp( topo, "snapls" );
+ fd_topob_wksp( topo, "snapla_ls" );
+ fd_topob_wksp( topo, "snapin_ls" );
+ }
}
#define FOR(cnt) for( ulong i=0UL; ipermit_no_consumers = 1; /* TODO: wire in repair later */
if( FD_LIKELY( config->tiles.gui.enabled ) ) {
/**/ fd_topob_link( topo, "snapct_gui", "snapct_gui", 128UL, sizeof(fd_snapct_update_t), 1UL );
@@ -421,6 +438,10 @@ fd_topo_initialize( config_t * config ) {
/**/ fd_topob_link( topo, "snapwh_wr", "snapin_wr", 4UL, 0UL, 1UL );
fd_pod_insertf_ulong( topo->props, 8UL, "obj.%lu.app_sz", snapin_wh->dcache_obj_id );
}
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ FOR(lta_tile_cnt) fd_topob_link( topo, "snapla_ls", "snapla_ls", 128UL, sizeof(fd_lthash_value_t), 1UL );
+ /**/ fd_topob_link( topo, "snapin_ls", "snapin_ls", 256UL, sizeof(fd_snapshot_full_account_t), 1UL );
+ }
}
/**/ fd_topob_link( topo, "genesi_out", "genesi_out", 2UL, 10UL*1024UL*1024UL+32UL+sizeof(fd_lthash_value_t), 1UL );
@@ -508,6 +529,11 @@ fd_topo_initialize( config_t * config ) {
/**/ fd_topob_tile( topo, "snapin", "snapin", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 )->allow_shutdown = 1;
if(vinyl_enabled) fd_topob_tile( topo, "snapwh", "snapwh", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 )->allow_shutdown = 1;
if(vinyl_enabled) fd_topob_tile( topo, "snapwr", "snapwr", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 )->allow_shutdown = 1;
+
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ FOR(lta_tile_cnt) fd_topob_tile( topo, "snapla", "snapla", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 )->allow_shutdown = 1;
+ /**/ fd_topob_tile( topo, "snapls", "snapls", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 )->allow_shutdown = 1;
+ }
}
/**/ fd_topob_tile( topo, "genesi", "genesi", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 )->allow_shutdown = 1;
@@ -572,10 +598,15 @@ fd_topo_initialize( config_t * config ) {
if( FD_LIKELY( snapshots_gossip_enabled ) ) {
/**/ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "gossip_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
}
- /**/ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapin_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
- /**/ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
- /**/ fd_topob_tile_out( topo, "snapct", 0UL, "snapct_ld", 0UL );
- /**/ fd_topob_tile_out( topo, "snapct", 0UL, "snapct_repr", 0UL );
+
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapin_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ } else {
+ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapls_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ }
+ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ fd_topob_tile_out( topo, "snapct", 0UL, "snapct_ld", 0UL );
+ fd_topob_tile_out( topo, "snapct", 0UL, "snapct_repr", 0UL );
if( FD_LIKELY( config->tiles.gui.enabled ) ) {
/**/ fd_topob_tile_out( topo, "snapct", 0UL, "snapct_gui", 0UL );
}
@@ -593,12 +624,23 @@ fd_topo_initialize( config_t * config ) {
/**/ fd_topob_tile_in ( topo, "snapdc", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
/**/ fd_topob_tile_out( topo, "snapdc", 0UL, "snapdc_in", 0UL );
- /**/ fd_topob_tile_in ( topo, "snapin", 0UL, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
- /**/ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL );
- /**/ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_manif", 0UL );
+ fd_topob_tile_in ( topo, "snapin", 0UL, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
if( FD_LIKELY( config->tiles.gui.enabled ) ) {
/**/ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_gui", 0UL );
}
+ if( FD_UNLIKELY( snapshot_lthash_disabled ) ) {
+ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL );
+ } else {
+ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ls", 0UL );
+ }
+ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_manif", 0UL );
+ if( FD_LIKELY( !snapshot_lthash_disabled ) ) {
+ FOR(lta_tile_cnt) fd_topob_tile_in( topo, "snapla", i, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ FOR(lta_tile_cnt) fd_topob_tile_out( topo, "snapla", i, "snapla_ls", i );
+ /**/ fd_topob_tile_in( topo, "snapls", 0UL, "metric_in", "snapin_ls", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ FOR(lta_tile_cnt) fd_topob_tile_in( topo, "snapls", 0UL, "metric_in", "snapla_ls", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
+ /**/ fd_topob_tile_out( topo, "snapls", 0UL, "snapls_ct", 0UL );
+ }
}
/**/ fd_topob_tile_in( topo, "repair", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
@@ -1061,6 +1103,7 @@ fd_topo_configure_tile( fd_topo_tile_t * tile,
tile->snapin.txncache_obj_id = fd_pod_query_ulong( config->topo.props, "txncache", ULONG_MAX );
tile->snapin.use_vinyl = !!config->firedancer.vinyl.enabled;
+ tile->snapin.lthash_disabled = !!config->development.snapshots.disable_lthash_verification;
if( tile->snapin.use_vinyl ) {
strcpy( tile->snapin.vinyl_path, config->paths.accounts );
tile->snapin.vinyl_meta_map_obj_id = fd_pod_query_ulong( config->topo.props, "vinyl.meta_map", ULONG_MAX );
@@ -1081,6 +1124,9 @@ fd_topo_configure_tile( fd_topo_tile_t * tile,
FD_TEST( in_wr_link_id!=ULONG_MAX );
fd_topo_link_t * in_wr_link = &config->topo.links[ in_wr_link_id ];
tile->snapwr.dcache_obj_id = in_wr_link->dcache_obj_id;
+ } else if( FD_UNLIKELY( !strcmp( tile->name, "snapla" ) ) ) {
+
+ } else if( FD_UNLIKELY( !strcmp( tile->name, "snapls" ) ) ) {
} else if( FD_UNLIKELY( !strcmp( tile->name, "repair" ) ) ) {
tile->repair.max_pending_shred_sets = config->tiles.shred.max_pending_shred_sets;
diff --git a/src/app/shared/commands/watch/watch.c b/src/app/shared/commands/watch/watch.c
index 8d15cf4b2eb..b09169d79aa 100644
--- a/src/app/shared/commands/watch/watch.c
+++ b/src/app/shared/commands/watch/watch.c
@@ -327,34 +327,57 @@ write_snapshots( config_t const * config,
ulong snapld_total_ticks = total_regime( &cur_tile[ fd_topo_find_tile( &config->topo, "snapld", 0UL )*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ fd_topo_find_tile( &config->topo, "snapld", 0UL )*FD_METRICS_TOTAL_SZ ] );
ulong snapdc_total_ticks = total_regime( &cur_tile[ fd_topo_find_tile( &config->topo, "snapdc", 0UL )*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ fd_topo_find_tile( &config->topo, "snapdc", 0UL )*FD_METRICS_TOTAL_SZ ] );
ulong snapin_total_ticks = total_regime( &cur_tile[ fd_topo_find_tile( &config->topo, "snapin", 0UL )*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ fd_topo_find_tile( &config->topo, "snapin", 0UL )*FD_METRICS_TOTAL_SZ ] );
+ ulong snapls_tile_idx = fd_topo_find_tile( &config->topo, "snapls", 0UL );
+ ulong snapls_total_ticks = snapls_tile_idx!=ULONG_MAX ? total_regime( &cur_tile[ snapls_tile_idx*FD_METRICS_TOTAL_SZ ] )-total_regime( &prev_tile[ snapls_tile_idx*FD_METRICS_TOTAL_SZ ] ) : 0UL;
snapct_total_ticks = fd_ulong_max( snapct_total_ticks, 1UL );
snapld_total_ticks = fd_ulong_max( snapld_total_ticks, 1UL );
snapdc_total_ticks = fd_ulong_max( snapdc_total_ticks, 1UL );
snapin_total_ticks = fd_ulong_max( snapin_total_ticks, 1UL );
+ snapls_total_ticks = fd_ulong_max( snapls_total_ticks, 1UL );
double snapct_backp_pct = 100.0*(double)diff_tile( config, "snapct", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapct_total_ticks;
double snapld_backp_pct = 100.0*(double)diff_tile( config, "snapld", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapld_total_ticks;
double snapdc_backp_pct = 100.0*(double)diff_tile( config, "snapdc", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapdc_total_ticks;
double snapin_backp_pct = 100.0*(double)diff_tile( config, "snapin", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapin_total_ticks;
+ double snapls_backp_pct = snapls_tile_idx!=ULONG_MAX ? 100.0*(double)diff_tile( config, "snapls", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) )/(double)snapls_total_ticks : 0.0;
double snapct_idle_pct = 100.0*(double)diff_tile( config, "snapct", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapct_total_ticks;
double snapld_idle_pct = 100.0*(double)diff_tile( config, "snapld", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapld_total_ticks;
double snapdc_idle_pct = 100.0*(double)diff_tile( config, "snapdc", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapdc_total_ticks;
double snapin_idle_pct = 100.0*(double)diff_tile( config, "snapin", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapin_total_ticks;
-
- PRINT( "⚡ \033[1m\033[93mSNAPSHOTS...\033[0m\033[22m \033[1mSTATE\033[22m %s \033[1mPCT\033[22m %.1f %% \033[1mRX\033[22m %3.f MB/s \033[1mACC\033[22m %3.1f M/s \033[1mBACKP\033[22m %3.0f%%,%3.0f%%,%3.0f%%,%3.0f%% \033[1mBUSY\033[22m %3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%\033[K\n",
- fd_snapct_state_str( state ),
- progress,
- megabytes_per_second,
- million_accounts_per_second,
- snapct_backp_pct,
- snapld_backp_pct,
- snapdc_backp_pct,
- snapin_backp_pct,
- 100.0-snapct_idle_pct-snapct_backp_pct,
- 100.0-snapld_idle_pct-snapld_backp_pct,
- 100.0-snapdc_idle_pct-snapdc_backp_pct,
- 100.0-snapin_idle_pct-snapin_backp_pct );
+ double snapls_idle_pct = snapls_tile_idx!=ULONG_MAX ? 100.0*(double)diff_tile( config, "snapls", prev_tile, cur_tile, MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) )/(double)snapls_total_ticks : 0.0;
+
+ if( FD_UNLIKELY( snapls_tile_idx!=ULONG_MAX ) ) {
+ PRINT( "⚡ \033[1m\033[93mSNAPSHOTS...\033[0m\033[22m \033[1mSTATE\033[22m %s \033[1mPCT\033[22m %.1f %% \033[1mRX\033[22m %3.f MB/s \033[1mACC\033[22m %3.1f M/s \033[1mBACKP\033[22m %3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%,%3.0f%% \033[1mBUSY\033[22m %3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%\033[K\n",
+ fd_snapct_state_str( state ),
+ progress,
+ megabytes_per_second,
+ million_accounts_per_second,
+ snapct_backp_pct,
+ snapld_backp_pct,
+ snapdc_backp_pct,
+ snapin_backp_pct,
+ snapls_backp_pct,
+ 100.0-snapct_idle_pct-snapct_backp_pct,
+ 100.0-snapld_idle_pct-snapld_backp_pct,
+ 100.0-snapdc_idle_pct-snapdc_backp_pct,
+ 100.0-snapin_idle_pct-snapin_backp_pct,
+ 100.0-snapls_idle_pct );
+ } else {
+ PRINT( "⚡ \033[1m\033[93mSNAPSHOTS...\033[0m\033[22m \033[1mSTATE\033[22m %s \033[1mPCT\033[22m %.1f %% \033[1mRX\033[22m %3.f MB/s \033[1mACC\033[22m %3.1f M/s \033[1mBACKP\033[22m %3.0f%%,%3.0f%%,%3.0f%%,%3.0f%% \033[1mBUSY\033[22m %3.0f%%,%3.0f%%,%3.0f%%,%3.0f%%\033[K\n",
+ fd_snapct_state_str( state ),
+ progress,
+ megabytes_per_second,
+ million_accounts_per_second,
+ snapct_backp_pct,
+ snapld_backp_pct,
+ snapdc_backp_pct,
+ snapin_backp_pct,
+ 100.0-snapct_idle_pct-snapct_backp_pct,
+ 100.0-snapld_idle_pct-snapld_backp_pct,
+ 100.0-snapdc_idle_pct-snapdc_backp_pct,
+ 100.0-snapin_idle_pct-snapin_backp_pct );
+ }
}
static uint
diff --git a/src/app/shared/fd_config.c b/src/app/shared/fd_config.c
index b4c9d421e23..8f230e6a924 100644
--- a/src/app/shared/fd_config.c
+++ b/src/app/shared/fd_config.c
@@ -466,6 +466,7 @@ fd_config_fill( fd_config_t * config,
static void
fd_config_validatef( fd_configf_t const * config ) {
CFG_HAS_NON_ZERO( layout.sign_tile_count );
+ CFG_HAS_NON_ZERO( layout.snapla_tile_count );
if( FD_UNLIKELY( config->layout.sign_tile_count < 2 ) ) {
FD_LOG_ERR(( "layout.sign_tile_count must be >= 2" ));
}
diff --git a/src/app/shared/fd_config.h b/src/app/shared/fd_config.h
index 73ea94ff76f..52a34dfed9a 100644
--- a/src/app/shared/fd_config.h
+++ b/src/app/shared/fd_config.h
@@ -115,6 +115,7 @@ struct fd_configf {
uint exec_tile_count; /* TODO: redundant ish with bank tile cnt */
uint sign_tile_count;
uint gossvf_tile_count;
+ uint snapla_tile_count;
} layout;
struct {
@@ -337,6 +338,10 @@ struct fd_config {
char affinity[ AFFINITY_SZ ];
} udpecho;
+ struct {
+ int disable_lthash_verification;
+ } snapshots;
+
struct {
char affinity[ AFFINITY_SZ ];
} snapshot_load;
diff --git a/src/app/shared/fd_config_parse.c b/src/app/shared/fd_config_parse.c
index a496ca3355d..8150f2fc22e 100644
--- a/src/app/shared/fd_config_parse.c
+++ b/src/app/shared/fd_config_parse.c
@@ -83,6 +83,7 @@ fd_config_extract_podf( uchar * pod,
CFG_POP ( uint, layout.exec_tile_count );
CFG_POP ( uint, layout.sign_tile_count );
CFG_POP ( uint, layout.gossvf_tile_count );
+ CFG_POP ( uint, layout.snapla_tile_count );
CFG_POP ( ulong, funk.max_account_records );
CFG_POP ( ulong, funk.heap_size_gib );
@@ -313,6 +314,7 @@ fd_config_extract_pod( uchar * pod,
CFG_POP ( cstr, development.pktgen.fake_dst_ip );
CFG_POP ( cstr, development.udpecho.affinity );
+ CFG_POP ( bool, development.snapshots.disable_lthash_verification );
if( FD_UNLIKELY( !config->is_firedancer ) ) {
CFG_POP ( bool, development.gui.websocket_compression );
diff --git a/src/disco/metrics/generate/types.py b/src/disco/metrics/generate/types.py
index 900922f8845..0c33e6d45c2 100644
--- a/src/disco/metrics/generate/types.py
+++ b/src/disco/metrics/generate/types.py
@@ -40,6 +40,8 @@ class Tile(Enum):
SNAPWR = 34
BENCHS = 35
SNAPWH = 36
+ SNAPLA = 37
+ SNAPLS = 38
class MetricType(Enum):
COUNTER = 0
diff --git a/src/disco/metrics/generated/fd_metrics_all.c b/src/disco/metrics/generated/fd_metrics_all.c
index db431fefaba..b07228ab4d2 100644
--- a/src/disco/metrics/generated/fd_metrics_all.c
+++ b/src/disco/metrics/generated/fd_metrics_all.c
@@ -69,6 +69,8 @@ const char * FD_METRICS_TILE_KIND_NAMES[FD_METRICS_TILE_KIND_CNT] = {
"snapwr",
"benchs",
"snapwh",
+ "snapla",
+ "snapls",
};
const ulong FD_METRICS_TILE_KIND_SIZES[FD_METRICS_TILE_KIND_CNT] = {
@@ -105,6 +107,8 @@ const ulong FD_METRICS_TILE_KIND_SIZES[FD_METRICS_TILE_KIND_CNT] = {
FD_METRICS_SNAPWR_TOTAL,
FD_METRICS_BENCHS_TOTAL,
FD_METRICS_SNAPWH_TOTAL,
+ FD_METRICS_SNAPLA_TOTAL,
+ FD_METRICS_SNAPLS_TOTAL,
};
const fd_metrics_meta_t * FD_METRICS_TILE_KIND_METRICS[FD_METRICS_TILE_KIND_CNT] = {
FD_METRICS_NET,
@@ -140,4 +144,6 @@ const fd_metrics_meta_t * FD_METRICS_TILE_KIND_METRICS[FD_METRICS_TILE_KIND_CNT]
FD_METRICS_SNAPWR,
FD_METRICS_BENCHS,
FD_METRICS_SNAPWH,
+ FD_METRICS_SNAPLA,
+ FD_METRICS_SNAPLS,
};
diff --git a/src/disco/metrics/generated/fd_metrics_all.h b/src/disco/metrics/generated/fd_metrics_all.h
index 733ce39e902..b82f78ca055 100644
--- a/src/disco/metrics/generated/fd_metrics_all.h
+++ b/src/disco/metrics/generated/fd_metrics_all.h
@@ -33,6 +33,8 @@
#include "fd_metrics_snapin.h"
#include "fd_metrics_snapwr.h"
#include "fd_metrics_snapwh.h"
+#include "fd_metrics_snapla.h"
+#include "fd_metrics_snapls.h"
#include "fd_metrics_metric.h"
#include "fd_metrics_ipecho.h"
#include "fd_metrics_backt.h"
@@ -174,7 +176,7 @@ extern const fd_metrics_meta_t FD_METRICS_ALL_LINK_OUT[FD_METRICS_ALL_LINK_OUT_T
#define FD_METRICS_TOTAL_SZ (8UL*254UL)
-#define FD_METRICS_TILE_KIND_CNT 33
+#define FD_METRICS_TILE_KIND_CNT 35
extern const char * FD_METRICS_TILE_KIND_NAMES[FD_METRICS_TILE_KIND_CNT];
extern const ulong FD_METRICS_TILE_KIND_SIZES[FD_METRICS_TILE_KIND_CNT];
extern const fd_metrics_meta_t * FD_METRICS_TILE_KIND_METRICS[FD_METRICS_TILE_KIND_CNT];
diff --git a/src/disco/metrics/generated/fd_metrics_snapla.c b/src/disco/metrics/generated/fd_metrics_snapla.c
new file mode 100644
index 00000000000..251b7719c08
--- /dev/null
+++ b/src/disco/metrics/generated/fd_metrics_snapla.c
@@ -0,0 +1,8 @@
+/* THIS FILE IS GENERATED BY gen_metrics.py. DO NOT HAND EDIT. */
+#include "fd_metrics_snapla.h"
+
+const fd_metrics_meta_t FD_METRICS_SNAPLA[FD_METRICS_SNAPLA_TOTAL] = {
+ DECLARE_METRIC( SNAPLA_STATE, GAUGE ),
+ DECLARE_METRIC( SNAPLA_FULL_ACCOUNTS_HASHED, GAUGE ),
+ DECLARE_METRIC( SNAPLA_INCREMENTAL_ACCOUNTS_HASHED, GAUGE ),
+};
diff --git a/src/disco/metrics/generated/fd_metrics_snapla.h b/src/disco/metrics/generated/fd_metrics_snapla.h
new file mode 100644
index 00000000000..4fd02fff35e
--- /dev/null
+++ b/src/disco/metrics/generated/fd_metrics_snapla.h
@@ -0,0 +1,30 @@
+#ifndef HEADER_fd_src_disco_metrics_generated_fd_metrics_snapla_h
+#define HEADER_fd_src_disco_metrics_generated_fd_metrics_snapla_h
+
+/* THIS FILE IS GENERATED BY gen_metrics.py. DO NOT HAND EDIT. */
+
+#include "../fd_metrics_base.h"
+#include "fd_metrics_enums.h"
+
+#define FD_METRICS_GAUGE_SNAPLA_STATE_OFF (16UL)
+#define FD_METRICS_GAUGE_SNAPLA_STATE_NAME "snapla_state"
+#define FD_METRICS_GAUGE_SNAPLA_STATE_TYPE (FD_METRICS_TYPE_GAUGE)
+#define FD_METRICS_GAUGE_SNAPLA_STATE_DESC "State of the tile. 0=IDLE, 1=PROCESSING, 2=FINISHING, 3=ERROR, 4=SHUTDOWN"
+#define FD_METRICS_GAUGE_SNAPLA_STATE_CVT (FD_METRICS_CONVERTER_NONE)
+
+#define FD_METRICS_GAUGE_SNAPLA_FULL_ACCOUNTS_HASHED_OFF (17UL)
+#define FD_METRICS_GAUGE_SNAPLA_FULL_ACCOUNTS_HASHED_NAME "snapla_full_accounts_hashed"
+#define FD_METRICS_GAUGE_SNAPLA_FULL_ACCOUNTS_HASHED_TYPE (FD_METRICS_TYPE_GAUGE)
+#define FD_METRICS_GAUGE_SNAPLA_FULL_ACCOUNTS_HASHED_DESC "Number of accounts hashed for the full snapshot during snapshot loading. Might decrease if snapshot load is aborted and restarted"
+#define FD_METRICS_GAUGE_SNAPLA_FULL_ACCOUNTS_HASHED_CVT (FD_METRICS_CONVERTER_NONE)
+
+#define FD_METRICS_GAUGE_SNAPLA_INCREMENTAL_ACCOUNTS_HASHED_OFF (18UL)
+#define FD_METRICS_GAUGE_SNAPLA_INCREMENTAL_ACCOUNTS_HASHED_NAME "snapla_incremental_accounts_hashed"
+#define FD_METRICS_GAUGE_SNAPLA_INCREMENTAL_ACCOUNTS_HASHED_TYPE (FD_METRICS_TYPE_GAUGE)
+#define FD_METRICS_GAUGE_SNAPLA_INCREMENTAL_ACCOUNTS_HASHED_DESC "Number of accounts hashed for the incremental snapshot during snapshot loading. Might decrease if snapshot load is aborted and restarted"
+#define FD_METRICS_GAUGE_SNAPLA_INCREMENTAL_ACCOUNTS_HASHED_CVT (FD_METRICS_CONVERTER_NONE)
+
+#define FD_METRICS_SNAPLA_TOTAL (3UL)
+extern const fd_metrics_meta_t FD_METRICS_SNAPLA[FD_METRICS_SNAPLA_TOTAL];
+
+#endif /* HEADER_fd_src_disco_metrics_generated_fd_metrics_snapla_h */
diff --git a/src/disco/metrics/generated/fd_metrics_snapls.c b/src/disco/metrics/generated/fd_metrics_snapls.c
new file mode 100644
index 00000000000..0a8bf2dcfe2
--- /dev/null
+++ b/src/disco/metrics/generated/fd_metrics_snapls.c
@@ -0,0 +1,8 @@
+/* THIS FILE IS GENERATED BY gen_metrics.py. DO NOT HAND EDIT. */
+#include "fd_metrics_snapls.h"
+
+const fd_metrics_meta_t FD_METRICS_SNAPLS[FD_METRICS_SNAPLS_TOTAL] = {
+ DECLARE_METRIC( SNAPLS_STATE, GAUGE ),
+ DECLARE_METRIC( SNAPLS_FULL_ACCOUNTS_HASHED, GAUGE ),
+ DECLARE_METRIC( SNAPLS_INCREMENTAL_ACCOUNTS_HASHED, GAUGE ),
+};
diff --git a/src/disco/metrics/generated/fd_metrics_snapls.h b/src/disco/metrics/generated/fd_metrics_snapls.h
new file mode 100644
index 00000000000..2636e60175a
--- /dev/null
+++ b/src/disco/metrics/generated/fd_metrics_snapls.h
@@ -0,0 +1,30 @@
+#ifndef HEADER_fd_src_disco_metrics_generated_fd_metrics_snapls_h
+#define HEADER_fd_src_disco_metrics_generated_fd_metrics_snapls_h
+
+/* THIS FILE IS GENERATED BY gen_metrics.py. DO NOT HAND EDIT. */
+
+#include "../fd_metrics_base.h"
+#include "fd_metrics_enums.h"
+
+#define FD_METRICS_GAUGE_SNAPLS_STATE_OFF (16UL)
+#define FD_METRICS_GAUGE_SNAPLS_STATE_NAME "snapls_state"
+#define FD_METRICS_GAUGE_SNAPLS_STATE_TYPE (FD_METRICS_TYPE_GAUGE)
+#define FD_METRICS_GAUGE_SNAPLS_STATE_DESC "State of the tile. 0=IDLE, 1=PROCESSING, 2=FINISHING, 3=ERROR, 4=SHUTDOWN"
+#define FD_METRICS_GAUGE_SNAPLS_STATE_CVT (FD_METRICS_CONVERTER_NONE)
+
+#define FD_METRICS_GAUGE_SNAPLS_FULL_ACCOUNTS_HASHED_OFF (17UL)
+#define FD_METRICS_GAUGE_SNAPLS_FULL_ACCOUNTS_HASHED_NAME "snapls_full_accounts_hashed"
+#define FD_METRICS_GAUGE_SNAPLS_FULL_ACCOUNTS_HASHED_TYPE (FD_METRICS_TYPE_GAUGE)
+#define FD_METRICS_GAUGE_SNAPLS_FULL_ACCOUNTS_HASHED_DESC "Number of accounts hashed for the full snapshot during snapshot loading. Might decrease if snapshot load is aborted and restarted"
+#define FD_METRICS_GAUGE_SNAPLS_FULL_ACCOUNTS_HASHED_CVT (FD_METRICS_CONVERTER_NONE)
+
+#define FD_METRICS_GAUGE_SNAPLS_INCREMENTAL_ACCOUNTS_HASHED_OFF (18UL)
+#define FD_METRICS_GAUGE_SNAPLS_INCREMENTAL_ACCOUNTS_HASHED_NAME "snapls_incremental_accounts_hashed"
+#define FD_METRICS_GAUGE_SNAPLS_INCREMENTAL_ACCOUNTS_HASHED_TYPE (FD_METRICS_TYPE_GAUGE)
+#define FD_METRICS_GAUGE_SNAPLS_INCREMENTAL_ACCOUNTS_HASHED_DESC "Number of accounts hashed for the incremental snapshot during snapshot loading. Might decrease if snapshot load is aborted and restarted"
+#define FD_METRICS_GAUGE_SNAPLS_INCREMENTAL_ACCOUNTS_HASHED_CVT (FD_METRICS_CONVERTER_NONE)
+
+#define FD_METRICS_SNAPLS_TOTAL (3UL)
+extern const fd_metrics_meta_t FD_METRICS_SNAPLS[FD_METRICS_SNAPLS_TOTAL];
+
+#endif /* HEADER_fd_src_disco_metrics_generated_fd_metrics_snapls_h */
diff --git a/src/disco/metrics/metrics.xml b/src/disco/metrics/metrics.xml
index 5fc85b7fe6e..21faf8c75b4 100644
--- a/src/disco/metrics/metrics.xml
+++ b/src/disco/metrics/metrics.xml
@@ -1064,6 +1064,18 @@ metric introduced.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/disco/stem/fd_stem.c b/src/disco/stem/fd_stem.c
index 7a46139747a..fa6fd861f38 100644
--- a/src/disco/stem/fd_stem.c
+++ b/src/disco/stem/fd_stem.c
@@ -356,6 +356,8 @@ STEM_(run1)( ulong in_cnt,
cr_max = fd_ulong_min( cr_max, out_depth[ cons_out[ cons_idx ] ] );
}
+ if( FD_UNLIKELY( burst>cr_max ) ) FD_LOG_ERR(( "one or more out links have insufficient depth for STEM_BURST %lu. cr_max is %lu", burst, cr_max ));
+
/* housekeeping init */
if( lazy<=0L ) lazy = fd_tempo_lazy_default( cr_max );
diff --git a/src/disco/topo/fd_topo.h b/src/disco/topo/fd_topo.h
index 59b0f8bfdad..f05e7e85b37 100644
--- a/src/disco/topo/fd_topo.h
+++ b/src/disco/topo/fd_topo.h
@@ -547,6 +547,7 @@ struct fd_topo_tile {
ulong funk_obj_id;
ulong txncache_obj_id;
+ uint lthash_disabled : 1;
uint use_vinyl : 1;
ulong vinyl_meta_map_obj_id;
ulong vinyl_meta_pool_obj_id;
diff --git a/src/disco/topo/fd_topob.c b/src/disco/topo/fd_topob.c
index faa187dea5e..65731ccca2e 100644
--- a/src/disco/topo/fd_topob.c
+++ b/src/disco/topo/fd_topob.c
@@ -386,6 +386,8 @@ fd_topob_auto_layout( fd_topo_t * topo,
"snapdc", /* FIREDANCER only */
"snapin", /* FIREDANCER only */
"snapwh", /* FIREDANCER only */
+ "snapla", /* FIREDANCER only */
+ "snapls", /* FIREDANCER only */
"arch_f", /* FIREDANCER only */
"arch_w", /* FIREDANCER only */
"vinyl", /* FIREDANCER only */
diff --git a/src/discof/restore/Local.mk b/src/discof/restore/Local.mk
index 3ba13c4c183..7c17f74ca5e 100644
--- a/src/discof/restore/Local.mk
+++ b/src/discof/restore/Local.mk
@@ -10,6 +10,8 @@ $(call add-objs,fd_snapin_tile fd_snapin_tile_funk fd_snapin_tile_vinyl,fd_disco
endif # FD_HAS_SSE
$(call add-objs,fd_snapwh_tile,fd_discof)
$(call add-objs,fd_snapwr_tile,fd_discof)
+$(call add-objs,fd_snapla_tile,fd_discof)
+$(call add-objs,fd_snapls_tile,fd_discof)
endif # FD_HAS_ALLOCA
$(call add-objs,utils/fd_ssparse,fd_discof)
$(call add-objs,utils/fd_ssmanifest_parser,fd_discof)
diff --git a/src/discof/restore/fd_snapct_tile.c b/src/discof/restore/fd_snapct_tile.c
index 92103f99c3e..70d99c0044f 100644
--- a/src/discof/restore/fd_snapct_tile.c
+++ b/src/discof/restore/fd_snapct_tile.c
@@ -35,10 +35,10 @@
#define SERVER_PEERS_MAX (FD_TOPO_SNAPSHOTS_SERVERS_MAX)
#define TOTAL_PEERS_MAX (GOSSIP_PEERS_MAX + SERVER_PEERS_MAX)
-#define IN_KIND_SNAPIN (0)
-#define IN_KIND_SNAPLD (1)
-#define IN_KIND_GOSSIP (2)
-#define MAX_IN_LINKS (3)
+#define IN_KIND_ACK (0)
+#define IN_KIND_SNAPLD (1)
+#define IN_KIND_GOSSIP (2)
+#define MAX_IN_LINKS (3)
#define TEMP_FULL_SNAP_NAME ".snapshot.tar.bz2-partial"
#define TEMP_INCR_SNAP_NAME ".incremental-snapshot.tar.bz2-partial"
@@ -1145,7 +1145,7 @@ returnable_frag( fd_snapct_tile_t * ctx,
gossip_frag( ctx, sig, sz, chunk );
} else if( ctx->in_kind[ in_idx ]==IN_KIND_SNAPLD ) {
snapld_frag( ctx, sig, sz, chunk, stem );
- } else if( ctx->in_kind[ in_idx ]==IN_KIND_SNAPIN ) {
+ } else if( ctx->in_kind[ in_idx ]==IN_KIND_ACK ) {
snapin_frag( ctx, sig );
} else FD_LOG_ERR(( "invalid in_kind %lu %u", in_idx, (uint)ctx->in_kind[ in_idx ] ));
return 0;
@@ -1293,7 +1293,7 @@ unprivileged_init( fd_topo_t * topo,
fd_memset( ctx->http_incr_snapshot_name, 0, PATH_MAX );
ctx->gossip_in_mem = NULL;
- int has_snapld_dc = 0, has_snapin_ct = 0;
+ int has_snapld_dc = 0, has_ack_loopback = 0;
FD_TEST( tile->in_cnt<=MAX_IN_LINKS );
for( ulong i=0UL; i<(tile->in_cnt); i++ ) {
fd_topo_link_t * in_link = &topo->links[ tile->in_link_id[ i ] ];
@@ -1305,13 +1305,13 @@ unprivileged_init( fd_topo_t * topo,
ctx->snapld_in_mem = topo->workspaces[ topo->objs[ in_link->dcache_obj_id ].wksp_id ].wksp;
FD_TEST( !has_snapld_dc );
has_snapld_dc = 1;
- } else if( 0==strcmp( in_link->name, "snapin_ct" ) ) {
- ctx->in_kind[ i ] = IN_KIND_SNAPIN;
- FD_TEST( !has_snapin_ct );
- has_snapin_ct = 1;
+ } else if( 0==strcmp( in_link->name, "snapin_ct" ) || 0==strcmp( in_link->name, "snapls_ct" ) ) {
+ ctx->in_kind[ i ] = IN_KIND_ACK;
+ FD_TEST( !has_ack_loopback );
+ has_ack_loopback = 1;
}
}
- FD_TEST( has_snapld_dc && has_snapin_ct );
+ FD_TEST( has_snapld_dc && has_ack_loopback );
FD_TEST( ctx->gossip_enabled==(ctx->gossip_in_mem!=NULL) );
ctx->predicted_incremental.full_slot = ULONG_MAX;
diff --git a/src/discof/restore/fd_snapdc_tile.c b/src/discof/restore/fd_snapdc_tile.c
index bba3657a650..657e28fcd24 100644
--- a/src/discof/restore/fd_snapdc_tile.c
+++ b/src/discof/restore/fd_snapdc_tile.c
@@ -289,6 +289,7 @@ unprivileged_init( fd_topo_t * topo,
if( FD_UNLIKELY( tile->out_cnt!=1UL ) ) FD_LOG_ERR(( "tile `" NAME "` has %lu outs, expected 1", tile->out_cnt ));
fd_topo_link_t * snapin_link = &topo->links[ tile->out_link_id[ 0UL ] ];
+ FD_TEST( 0==strcmp( snapin_link->name, "snapdc_in" ) );
ctx->out.wksp = topo->workspaces[ topo->objs[ snapin_link->dcache_obj_id ].wksp_id ].wksp;
ctx->out.chunk0 = fd_dcache_compact_chunk0( ctx->out.wksp, snapin_link->dcache );
ctx->out.wmark = fd_dcache_compact_wmark ( ctx->out.wksp, snapin_link->dcache, snapin_link->mtu );
diff --git a/src/discof/restore/fd_snapin_tile.c b/src/discof/restore/fd_snapin_tile.c
index 7e9e9b9100e..a162eee3228 100644
--- a/src/discof/restore/fd_snapin_tile.c
+++ b/src/discof/restore/fd_snapin_tile.c
@@ -160,7 +160,7 @@ static void
transition_malformed( fd_snapin_tile_t * ctx,
fd_stem_context_t * stem ) {
ctx->state = FD_SNAPSHOT_STATE_ERROR;
- fd_stem_publish( stem, ctx->ct_out.idx, FD_SNAPSHOT_MSG_CTRL_ERROR, 0UL, 0UL, 0UL, 0UL, 0UL );
+ fd_stem_publish( stem, ctx->out_ct_idx, FD_SNAPSHOT_MSG_CTRL_ERROR, 0UL, 0UL, 0UL, 0UL, 0UL );
}
static int
@@ -407,6 +407,19 @@ process_manifest( fd_snapin_tile_t * ctx ) {
manifest->txncache_fork_id = ctx->txncache_root_fork_id.val;
+ if( FD_LIKELY( !ctx->lthash_disabled ) ) {
+ if( FD_UNLIKELY( !manifest->has_accounts_lthash ) ) {
+ FD_LOG_WARNING(( "snapshot manifest missing accounts lthash field" ));
+ transition_malformed( ctx, ctx->stem );
+ return;
+ }
+
+ fd_lthash_value_t * expected_lthash = fd_chunk_to_laddr( ctx->hash_out.mem, ctx->hash_out.chunk );
+ fd_memcpy( expected_lthash, manifest->accounts_lthash, sizeof(fd_lthash_value_t) );
+ fd_stem_publish( ctx->stem, ctx->out_ct_idx, FD_SNAPSHOT_HASH_MSG_EXPECTED, ctx->hash_out.chunk, sizeof(fd_lthash_value_t), 0UL, 0UL, 0UL );
+ ctx->hash_out.chunk = fd_dcache_compact_next( ctx->hash_out.chunk, sizeof(fd_lthash_value_t), ctx->hash_out.chunk0, ctx->hash_out.wmark );
+ }
+
ulong sig = ctx->full ? fd_ssmsg_sig( FD_SSMSG_MANIFEST_FULL ) :
fd_ssmsg_sig( FD_SSMSG_MANIFEST_INCREMENTAL );
fd_stem_publish( ctx->stem, ctx->manifest_out.idx, sig, ctx->manifest_out.chunk, sizeof(fd_snapshot_manifest_t), 0UL, 0UL, 0UL );
@@ -434,11 +447,17 @@ handle_data_frag( fd_snapin_tile_t * ctx,
FD_TEST( chunk>=ctx->in.chunk0 && chunk<=ctx->in.wmark && sz<=ctx->in.mtu );
+ if( FD_UNLIKELY( !ctx->lthash_disabled && ctx->buffered_batch.batch_cnt>0UL ) ) {
+ fd_snapin_process_account_batch( ctx, NULL, &ctx->buffered_batch );
+ return 1;
+ }
+
for(;;) {
if( FD_UNLIKELY( sz-ctx->in.pos==0UL ) ) break;
uchar const * data = (uchar const *)fd_chunk_to_laddr_const( ctx->in.wksp, chunk ) + ctx->in.pos;
+ int early_exit = 0;
fd_ssparse_advance_result_t result[1];
int res = fd_ssparse_advance( ctx->ssparse, data, sz-ctx->in.pos, result );
switch( res ) {
@@ -492,10 +511,10 @@ handle_data_frag( fd_snapin_tile_t * ctx,
break;
}
case FD_SSPARSE_ADVANCE_ACCOUNT_HEADER:
- fd_snapin_process_account_header( ctx, result );
+ early_exit = fd_snapin_process_account_header( ctx, result );
break;
case FD_SSPARSE_ADVANCE_ACCOUNT_DATA:
- fd_snapin_process_account_data( ctx, result );
+ early_exit = fd_snapin_process_account_data( ctx, result );
/* We exepect ConfigKeys Vec to be length 2 */
if( FD_UNLIKELY( ctx->gui_out.idx!=ULONG_MAX && !memcmp( result->account_data.owner, fd_solana_config_program_id.key, sizeof(fd_hash_t) ) && result->account_data.data_sz && *(uchar *)result->account_data.data==2UL ) ) {
@@ -506,7 +525,7 @@ handle_data_frag( fd_snapin_tile_t * ctx,
}
break;
case FD_SSPARSE_ADVANCE_ACCOUNT_BATCH:
- fd_snapin_process_account_batch( ctx, result );
+ early_exit = fd_snapin_process_account_batch( ctx, result, NULL );
break;
case FD_SSPARSE_ADVANCE_DONE:
ctx->state = FD_SNAPSHOT_STATE_FINISHING;
@@ -524,6 +543,8 @@ handle_data_frag( fd_snapin_tile_t * ctx,
ctx->in.pos += result->bytes_consumed;
if( FD_LIKELY( ctx->full ) ) ctx->metrics.full_bytes_read += result->bytes_consumed;
else ctx->metrics.incremental_bytes_read += result->bytes_consumed;
+
+ if( FD_UNLIKELY( early_exit ) ) break;
}
int reprocess_frag = ctx->in.posct_out.idx, sig, 0UL, 0UL, 0UL, 0UL, 0UL );
+ fd_stem_publish( stem, ctx->out_ct_idx, sig, 0UL, 0UL, 0UL, 0UL, 0UL );
}
static inline int
@@ -725,6 +746,11 @@ privileged_init( fd_topo_t * topo,
memset( ctx, 0, sizeof(fd_snapin_tile_t) );
FD_TEST( fd_rng_secure( &ctx->seed, 8UL ) );
+ if( tile->snapin.use_vinyl && !tile->snapin.lthash_disabled ) {
+ FD_LOG_WARNING(( "lthash verficiation for vinyl not yet implemented" ));
+ tile->snapin.lthash_disabled = 1;
+ }
+
if( tile->snapin.use_vinyl ) {
ctx->use_vinyl = 1;
fd_snapin_vinyl_privileged_init( ctx, topo, tile );
@@ -770,10 +796,12 @@ unprivileged_init( fd_topo_t * topo,
ctx->full = 1;
ctx->state = FD_SNAPSHOT_STATE_IDLE;
+ ctx->lthash_disabled = tile->snapin.lthash_disabled;
ctx->boot_timestamp = fd_log_wallclock();
FD_TEST( fd_accdb_admin_join( ctx->accdb_admin, fd_topo_obj_laddr( topo, tile->snapin.funk_obj_id ) ) );
+ FD_TEST( fd_accdb_user_join ( ctx->accdb, fd_topo_obj_laddr( topo, tile->snapin.funk_obj_id ) ) );
fd_funk_txn_xid_copy( ctx->xid, fd_funk_root( ctx->accdb_admin->funk ) );
void * _txncache_shmem = fd_topo_obj_laddr( topo, tile->snapin.txncache_obj_id );
@@ -799,18 +827,27 @@ unprivileged_init( fd_topo_t * topo,
if( FD_UNLIKELY( tile->kind_id ) ) FD_LOG_ERR(( "There can only be one `" NAME "` tile" ));
if( FD_UNLIKELY( tile->in_cnt!=1UL ) ) FD_LOG_ERR(( "tile `" NAME "` has %lu ins, expected 1", tile->in_cnt ));
- ctx->ct_out = out1( topo, tile, "snapin_ct" );
ctx->manifest_out = out1( topo, tile, "snapin_manif" );
ctx->gui_out = out1( topo, tile, "snapin_gui" );
+ ulong out_link_ct_idx = fd_topo_find_tile_out_link( topo, tile, "snapin_ct", 0UL );
+ if( out_link_ct_idx==ULONG_MAX ) out_link_ct_idx = fd_topo_find_tile_out_link( topo, tile, "snapin_ls", 0UL );
+ if( FD_UNLIKELY( out_link_ct_idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile `" NAME "` missing required out link `snapin_ct` or `snapin_ls`" ));
+ fd_topo_link_t * snapin_out_link = &topo->links[ tile->out_link_id[ out_link_ct_idx ] ];
+ ctx->out_ct_idx = out_link_ct_idx;
- if( FD_UNLIKELY( ctx->ct_out.idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile `" NAME "` missing required out link `snapin_ct`" ));
+ if( FD_UNLIKELY( ctx->out_ct_idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile `" NAME "` missing required out link `snapin_ct` or `snapin_ls`" ));
if( FD_UNLIKELY( ctx->manifest_out.idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile `" NAME "` missing required out link `snapin_manif`" ));
+ if( 0==strcmp( snapin_out_link->name, "snapin_ls" ) ) {
+ ctx->hash_out = out1( topo, tile, "snapin_ls" );
+ }
+
fd_ssparse_reset( ctx->ssparse );
fd_ssmanifest_parser_init( ctx->manifest_parser, fd_chunk_to_laddr( ctx->manifest_out.mem, ctx->manifest_out.chunk ) );
fd_slot_delta_parser_init( ctx->slot_delta_parser );
fd_topo_link_t const * in_link = &topo->links[ tile->in_link_id[ 0UL ] ];
+ FD_TEST( 0==strcmp( in_link->name, "snapdc_in" ) );
fd_topo_wksp_t const * in_wksp = &topo->workspaces[ topo->objs[ in_link->dcache_obj_id ].wksp_id ];
ctx->in.wksp = in_wksp->wksp;
ctx->in.chunk0 = fd_dcache_compact_chunk0( ctx->in.wksp, in_link->dcache );
@@ -818,6 +855,9 @@ unprivileged_init( fd_topo_t * topo,
ctx->in.mtu = in_link->mtu;
ctx->in.pos = 0UL;
+ ctx->buffered_batch.batch_cnt = 0UL;
+ ctx->buffered_batch.remaining_idx = 0UL;
+
fd_memset( &ctx->flags, 0, sizeof(ctx->flags) );
if( tile->snapin.use_vinyl ) {
@@ -827,8 +867,8 @@ unprivileged_init( fd_topo_t * topo,
/* Control fragments can result in one extra publish to forward the
message down the pipeline, in addition to the result / malformed
- message / etc. */
-#define STEM_BURST 2UL
+ message. Can send one duplicate account message as well. */
+#define STEM_BURST 3UL
#define STEM_LAZY 1000L
diff --git a/src/discof/restore/fd_snapin_tile_funk.c b/src/discof/restore/fd_snapin_tile_funk.c
index a15eeb70d64..be72eef4501 100644
--- a/src/discof/restore/fd_snapin_tile_funk.c
+++ b/src/discof/restore/fd_snapin_tile_funk.c
@@ -3,7 +3,7 @@
#include "fd_snapin_tile_private.h"
#include "../../flamenco/accdb/fd_accdb_sync.h"
-void
+int
fd_snapin_process_account_header_funk( fd_snapin_tile_t * ctx,
fd_ssparse_advance_result_t * result ) {
fd_funk_t * funk = ctx->accdb_admin->funk;
@@ -11,6 +11,26 @@ fd_snapin_process_account_header_funk( fd_snapin_tile_t * ctx,
fd_funk_rec_key_t id = FD_LOAD( fd_funk_rec_key_t, result->account_header.pubkey );
fd_funk_rec_query_t query[1];
fd_funk_rec_t * rec = fd_funk_rec_query_try( funk, ctx->xid, &id, query );
+ fd_funk_rec_t const * existing_rec = rec;
+
+ int early_exit = 0;
+ if( !ctx->full && !existing_rec ) {
+ fd_accdb_peek_t peek[1];
+ if( fd_accdb_peek( ctx->accdb, peek, ctx->xid, result->account_header.pubkey ) ) {
+ existing_rec = peek->acc->rec;
+ }
+ }
+ if( FD_UNLIKELY( existing_rec ) ) {
+ fd_account_meta_t * meta = fd_funk_val( existing_rec, funk->wksp );
+ if( FD_UNLIKELY( meta ) ) {
+ if( FD_LIKELY( meta->slot>result->account_header.slot ) ) {
+ ctx->acc_data = NULL;
+ fd_snapin_send_duplicate_account( ctx, result->account_header.lamports, NULL, result->account_header.data_len, (uchar)result->account_header.executable, result->account_header.owner, result->account_header.pubkey, 0, &early_exit );
+ return early_exit;
+ }
+ fd_snapin_send_duplicate_account( ctx, meta->lamports, (uchar const *)meta + sizeof(fd_account_meta_t), meta->dlen, meta->executable, meta->owner, result->account_header.pubkey, 1, &early_exit);
+ }
+ }
int should_publish = 0;
fd_funk_rec_prepare_t prepare[1];
@@ -21,17 +41,6 @@ fd_snapin_process_account_header_funk( fd_snapin_tile_t * ctx,
}
fd_account_meta_t * meta = fd_funk_val( rec, funk->wksp );
- if( FD_UNLIKELY( meta ) ) {
- if( FD_LIKELY( meta->slot>result->account_header.slot ) ) {
- ctx->acc_data = NULL;
- return;
- }
-
- /* TODO: Reaching here means the existing value is a duplicate
- account. We need to hash the existing account and subtract that
- hash from the running lthash. */
- }
-
/* Allocate data space from heap, free old value (if any) */
fd_funk_val_flush( rec, funk->alloc, funk->wksp );
ulong const alloc_sz = sizeof(fd_account_meta_t)+result->account_header.data_len;
@@ -53,15 +62,21 @@ fd_snapin_process_account_header_funk( fd_snapin_tile_t * ctx,
ctx->metrics.accounts_inserted++;
if( FD_LIKELY( should_publish ) ) fd_funk_rec_publish( funk, prepare );
+ return early_exit;
}
-void
+int
fd_snapin_process_account_data_funk( fd_snapin_tile_t * ctx,
fd_ssparse_advance_result_t * result ) {
- if( FD_UNLIKELY( !ctx->acc_data ) ) return;
+ int early_exit = 0;
+ if( FD_UNLIKELY( !ctx->acc_data ) ) {
+ fd_snapin_send_duplicate_account_data( ctx, result->account_data.data, result->account_data.data_sz, &early_exit );
+ return early_exit;
+ }
fd_memcpy( ctx->acc_data, result->account_data.data, result->account_data.data_sz );
ctx->acc_data += result->account_data.data_sz;
+ return 0;
}
/* streamlined_insert inserts an unfragmented account.
@@ -110,9 +125,12 @@ streamlined_insert( fd_snapin_tile_t * ctx,
The main optimization implemented for funk is doing hash map memory
accesses in parallel to amortize DRAM latency. */
-void
+int
fd_snapin_process_account_batch_funk( fd_snapin_tile_t * ctx,
- fd_ssparse_advance_result_t * result ) {
+ fd_ssparse_advance_result_t * result,
+ buffered_account_batch_t * buffered_batch ) {
+ int early_exit = 0;
+ ulong start_idx = result ? 0 : buffered_batch->remaining_idx;
fd_funk_t * funk = ctx->accdb_admin->funk;
fd_funk_rec_map_t * rec_map = funk->rec_map;
fd_funk_rec_t * rec_tbl = funk->rec_pool->ele;
@@ -121,8 +139,8 @@ fd_snapin_process_account_batch_funk( fd_snapin_tile_t * ctx,
/* Derive map chains */
uint chain_idx[ FD_SSPARSE_ACC_BATCH_MAX ];
ulong chain_mask = rec_map->map->chain_cnt-1UL;
- for( ulong i=0UL; iaccount_batch.batch[ i ];
+ for( ulong i=start_idx; iaccount_batch.batch[ i ] : buffered_batch->batch[ i ];
uchar const * pubkey = frame+0x10UL;
ulong memo = fd_funk_rec_key_hash1( pubkey, rec_map->map->seed );
chain_idx[ i ] = (uint)( memo&chain_mask );
@@ -131,12 +149,12 @@ fd_snapin_process_account_batch_funk( fd_snapin_tile_t * ctx,
/* Parallel load hash chain heads */
uint map_node [ FD_SSPARSE_ACC_BATCH_MAX ];
uint chain_cnt[ FD_SSPARSE_ACC_BATCH_MAX ];
- for( ulong i=0UL; iaccount_batch.batch[ i ];
+ for( ulong i=start_idx; iaccount_batch.batch[ i ] : buffered_batch->batch[ i ];
uchar const * pubkey = frame+0x10UL;
int const has_node = jaccount_batch.batch[ i ];
- uchar const * pubkey = frame+0x10UL;
+ ulong insert_limit = FD_SSPARSE_ACC_BATCH_MAX;
+ for( ulong i=start_idx; iaccount_batch.slot : buffered_batch->slot;
+ uchar const * frame = result ? result->account_batch.batch[ i ] : buffered_batch->batch[ i ];
+ uchar const * pubkey = frame+0x10UL;
+ ulong data_len = fd_ulong_load_8_fast( frame+0x08UL );
+ ulong lamports = fd_ulong_load_8_fast( frame+0x30UL );
+ ulong rent_epoch = fd_ulong_load_8_fast( frame+0x38UL ); (void)rent_epoch;
+ _Bool executable = !!frame[ 0x60UL ];
+ uchar const * data = frame+0x88UL;
+ uchar owner[32]; memcpy( owner, frame+0x40UL, 32UL );
fd_funk_rec_key_t key = FD_LOAD( fd_funk_rec_key_t, pubkey );
fd_funk_rec_t * r = rec[ i ];
@@ -186,18 +212,52 @@ fd_snapin_process_account_batch_funk( fd_snapin_tile_t * ctx,
fd_account_meta_t const * existing = fd_funk_val( r, funk->wksp );
if( FD_UNLIKELY( !existing ) ) FD_LOG_HEXDUMP_NOTICE(( "r", r, sizeof(fd_funk_rec_t) ));
FD_TEST( existing );
- if( existing->slot > result->account_batch.slot ) {
+ if( existing->slot > slot ) {
rec[ i ] = NULL; /* skip record if existing value is newer */
+ /* send the skipped account to the subtracting hash tile */
+ fd_snapin_send_duplicate_account( ctx, lamports, data, data_len, executable, owner, pubkey, 1, &early_exit );
+ } else if( slot > existing->slot) {
+ /* send the to-be-replaced account to the subtracting hash tile */
+ fd_snapin_send_duplicate_account( ctx, existing->lamports, (uchar const *)existing + sizeof(fd_account_meta_t), existing->dlen, existing->executable, existing->owner, pubkey, 1, &early_exit );
+ } else { /* slot==existing->slot */
+ FD_TEST( 0 );
+ }
+
+ if( FD_LIKELY( early_exit ) ) {
+ /* buffer account batch if not already buffered */
+ if( FD_LIKELY( result && ibuffered_batch.batch_cnt==0UL );
+ fd_memcpy( ctx->buffered_batch.batch, result->account_batch.batch, sizeof(uchar const*)*FD_SSPARSE_ACC_BATCH_MAX );
+ ctx->buffered_batch.slot = result->account_batch.slot;
+ ctx->buffered_batch.batch_cnt = result->account_batch.batch_cnt;
+ ctx->buffered_batch.remaining_idx = i + 1UL;
+ }
+
+ insert_limit = i+1UL;
+ break;
}
}
}
/* Actually insert accounts */
- for( ulong i=0UL; iaccount_batch.batch[ i ] : buffered_batch->batch[ i ];
+ ulong slot = result ? result->account_batch.slot : buffered_batch->slot;
if( rec[ i ] ) {
- streamlined_insert( ctx, rec[ i ], result->account_batch.batch[ i ], result->account_batch.slot );
+ streamlined_insert( ctx, rec[ i ], frame, slot );
}
}
+
+ if( FD_LIKELY( buffered_batch ) ) {
+ if( FD_LIKELY( insert_limit==FD_SSPARSE_ACC_BATCH_MAX ) ) {
+ buffered_batch->batch_cnt = 0UL;
+ buffered_batch->remaining_idx = 0UL;
+ } else {
+ buffered_batch->remaining_idx = insert_limit;
+ }
+ }
+
+ return early_exit;
}
void
@@ -208,17 +268,13 @@ fd_snapin_read_account_funk( fd_snapin_tile_t * ctx,
ulong data_max ) {
memset( meta, 0, sizeof(fd_account_meta_t) );
- fd_accdb_user_t accdb_[1];
- fd_accdb_user_t * accdb = fd_accdb_user_join( fd_accdb_user_new( accdb_ ), ctx->accdb_admin->funk->shmem );
- FD_TEST( accdb );
-
/* Start a speculative database query.
It is assumed that no conflicting database accesses take place
while the account is being read from funk. */
fd_accdb_peek_t peek_[1];
- fd_accdb_peek_t * peek = fd_accdb_peek( accdb, peek_, ctx->xid, acct_addr );
- if( FD_UNLIKELY( !peek ) ) goto done;
+ fd_accdb_peek_t * peek = fd_accdb_peek( ctx->accdb, peek_, ctx->xid, acct_addr );
+ if( FD_UNLIKELY( !peek ) ) return;
ulong data_sz = fd_accdb_ref_data_sz( peek->acc );
if( FD_UNLIKELY( data_sz>data_max ) ) {
@@ -236,7 +292,4 @@ fd_snapin_read_account_funk( fd_snapin_tile_t * ctx,
FD_CRIT( fd_accdb_peek_test( peek ), "invalid read" );
fd_accdb_peek_drop( peek );
-
-done:
- fd_accdb_user_delete( fd_accdb_user_leave( accdb, NULL ) );
}
diff --git a/src/discof/restore/fd_snapin_tile_private.h b/src/discof/restore/fd_snapin_tile_private.h
index 077de100cf7..e3a535bc560 100644
--- a/src/discof/restore/fd_snapin_tile_private.h
+++ b/src/discof/restore/fd_snapin_tile_private.h
@@ -8,7 +8,9 @@
#include "utils/fd_ssparse.h"
#include "utils/fd_ssmanifest_parser.h"
#include "utils/fd_slot_delta_parser.h"
+#include "utils/fd_ssctrl.h"
#include "../../flamenco/accdb/fd_accdb_admin.h"
+#include "../../flamenco/accdb/fd_accdb_user.h"
#include "../../flamenco/runtime/fd_txncache.h"
#include "../../disco/stem/fd_stem.h"
#include "../../disco/topo/fd_topo.h"
@@ -32,15 +34,27 @@ struct fd_snapin_out_link {
};
typedef struct fd_snapin_out_link fd_snapin_out_link_t;
+struct buffered_account_batch {
+ uchar const * batch[ FD_SSPARSE_ACC_BATCH_MAX ];
+ ulong batch_cnt;
+ ulong slot;
+ /* index at which to start processing a buffered account batch */
+ ulong remaining_idx;
+};
+
+typedef struct buffered_account_batch buffered_account_batch_t;
+
struct fd_snapin_tile {
int state;
- uint full : 1; /* loading a full snapshot? */
- uint use_vinyl : 1; /* using vinyl-backed accdb? */
+ uint full : 1; /* loading a full snapshot? */
+ uint use_vinyl : 1; /* using vinyl-backed accdb? */
+ uint lthash_disabled : 1; /* disable lthash checking? */
ulong seed;
long boot_timestamp;
fd_accdb_admin_t accdb_admin[1];
+ fd_accdb_user_t accdb[1];
fd_txncache_t * txncache;
uchar * acc_data;
@@ -53,6 +67,8 @@ struct fd_snapin_tile {
fd_ssmanifest_parser_t * manifest_parser;
fd_slot_delta_parser_t * slot_delta_parser;
+ buffered_account_batch_t buffered_batch;
+
struct {
int manifest_done;
int status_cache_done;
@@ -83,9 +99,10 @@ struct fd_snapin_tile {
ulong pos;
} in;
- fd_snapin_out_link_t ct_out;
+ ulong out_ct_idx;
fd_snapin_out_link_t manifest_out;
fd_snapin_out_link_t gui_out;
+ fd_snapin_out_link_t hash_out;
struct {
uchar * bstream_mem;
@@ -121,9 +138,9 @@ typedef struct fd_snapin_tile fd_snapin_tile_t;
FD_PROTOTYPES_BEGIN
-void fd_snapin_process_account_header_funk( fd_snapin_tile_t * ctx, fd_ssparse_advance_result_t * result );
-void fd_snapin_process_account_data_funk ( fd_snapin_tile_t * ctx, fd_ssparse_advance_result_t * result );
-void fd_snapin_process_account_batch_funk ( fd_snapin_tile_t * ctx, fd_ssparse_advance_result_t * result );
+int fd_snapin_process_account_header_funk( fd_snapin_tile_t * ctx, fd_ssparse_advance_result_t * result );
+int fd_snapin_process_account_data_funk ( fd_snapin_tile_t * ctx, fd_ssparse_advance_result_t * result );
+int fd_snapin_process_account_batch_funk ( fd_snapin_tile_t * ctx, fd_ssparse_advance_result_t * result, buffered_account_batch_t * buffered_batch );
void
fd_snapin_read_account_funk( fd_snapin_tile_t * ctx,
@@ -235,34 +252,42 @@ FD_PROTOTYPES_END
FD_PROTOTYPES_BEGIN
-static inline void
+/* int return value for fd_snapin_process_account_header,
+fd_snapin_process_account_data, and fd_snapin_process_account_batch
+indicates whether to yield to stem for credit return */
+
+static inline int
fd_snapin_process_account_header( fd_snapin_tile_t * ctx,
fd_ssparse_advance_result_t * result ) {
if( ctx->use_vinyl ) {
fd_snapin_process_account_header_vinyl( ctx, result );
} else {
- fd_snapin_process_account_header_funk( ctx, result );
+ return fd_snapin_process_account_header_funk( ctx, result );
}
+ return 0;
}
-static inline void
+static inline int
fd_snapin_process_account_data( fd_snapin_tile_t * ctx,
fd_ssparse_advance_result_t * result ) {
if( ctx->use_vinyl ) {
fd_snapin_process_account_data_vinyl( ctx, result );
} else {
- fd_snapin_process_account_data_funk( ctx, result );
+ return fd_snapin_process_account_data_funk( ctx, result );
}
+ return 0;
}
-static inline void
+static inline int
fd_snapin_process_account_batch( fd_snapin_tile_t * ctx,
- fd_ssparse_advance_result_t * result ) {
+ fd_ssparse_advance_result_t * result,
+ buffered_account_batch_t * buffered_batch ) {
if( ctx->use_vinyl ) {
fd_snapin_process_account_batch_vinyl( ctx, result );
} else {
- fd_snapin_process_account_batch_funk( ctx, result );
+ return fd_snapin_process_account_batch_funk( ctx, result, buffered_batch );
}
+ return 0;
}
static inline void
@@ -278,6 +303,69 @@ fd_snapin_read_account( fd_snapin_tile_t * ctx,
}
}
+/* fd_snapin_send_duplicate_account sends a duplicate account message
+ with the signature FD_SNAPSHOT_HASH_MSG_SUB or
+ FD_SNAPSHOT_HASH_MSG_SUB_HDR, depending on if this duplicate account
+ contains valid account data. The message is only
+ sent if lthash verification is enabled in the snapshot loader.
+
+ lamports is account's lamports value. data is the account's data,
+ which can be optionally null. data_len is the length of the account
+ data. executable is the account's executable flag. owner points to
+ the account's owner (32 bytes). pubkey points to the account's
+ pubkey (32 bytes). early_exit is an optional pointer to an int flag
+ that is set to 1 if the caller should yield to stem following this
+ call. */
+static inline void
+fd_snapin_send_duplicate_account( fd_snapin_tile_t * ctx,
+ ulong lamports,
+ uchar const * data,
+ ulong data_len,
+ uchar executable,
+ uchar const * owner,
+ uchar const * pubkey,
+ int has_data,
+ int * early_exit ) {
+ if( FD_UNLIKELY( ctx->lthash_disabled ) ) return;
+
+ if( FD_LIKELY( has_data ) ) {
+ fd_snapshot_full_account_t * existing_account = fd_chunk_to_laddr( ctx->hash_out.mem, ctx->hash_out.chunk );
+ fd_snapshot_account_hdr_init( &existing_account->hdr, pubkey, owner, lamports, executable, data_len );
+ fd_memcpy( existing_account->data, data, data_len );
+ fd_stem_publish( ctx->stem, ctx->out_ct_idx, FD_SNAPSHOT_HASH_MSG_SUB, ctx->hash_out.chunk, sizeof(fd_snapshot_account_hdr_t)+data_len, 0UL, 0UL, 0UL );
+ ctx->hash_out.chunk = fd_dcache_compact_next( ctx->hash_out.chunk, sizeof(fd_snapshot_account_hdr_t)+data_len, ctx->hash_out.chunk0, ctx->hash_out.wmark );
+ } else {
+ fd_snapshot_account_hdr_t * acc_hdr = fd_chunk_to_laddr( ctx->hash_out.mem, ctx->hash_out.chunk );
+ fd_snapshot_account_hdr_init( acc_hdr, pubkey, owner, lamports, executable, data_len );
+ fd_stem_publish( ctx->stem, ctx->out_ct_idx, FD_SNAPSHOT_HASH_MSG_SUB_HDR, ctx->hash_out.chunk, sizeof(fd_snapshot_account_hdr_t), 0UL, 0UL, 0UL );
+ ctx->hash_out.chunk = fd_dcache_compact_next( ctx->hash_out.chunk, sizeof(fd_snapshot_account_hdr_t), ctx->hash_out.chunk0, ctx->hash_out.wmark );
+ }
+ if( FD_LIKELY( early_exit ) ) *early_exit = 1;
+}
+
+/* fd_snapin_send_duplicate_account_data sends a duplicate account
+ message with the signature FD_SNAPSHOT_HASH_MSG_SUB_DATA. The
+ message is only sent if lthash verification is enabled in the
+ snapshot loader.
+
+ data is the account's data, which cannot be null. data_len is the
+ length of the account data. early_exit is an optional pointer to an
+ int flag that is set to 1 if the caller should yield to stem
+ following this call. */
+static inline void
+fd_snapin_send_duplicate_account_data( fd_snapin_tile_t * ctx,
+ uchar const * data,
+ ulong data_sz,
+ int * early_exit ) {
+ if( FD_UNLIKELY( ctx->lthash_disabled ) ) return;
+
+ uchar * drop_account_data = fd_chunk_to_laddr( ctx->hash_out.mem, ctx->hash_out.chunk );
+ fd_memcpy( drop_account_data, data, data_sz );
+ fd_stem_publish( ctx->stem, ctx->out_ct_idx, FD_SNAPSHOT_HASH_MSG_SUB_DATA, ctx->hash_out.chunk, data_sz, 0UL, 0UL, 0UL );
+ ctx->hash_out.chunk = fd_dcache_compact_next( ctx->hash_out.chunk, data_sz, ctx->hash_out.chunk0, ctx->hash_out.wmark );
+ if( FD_LIKELY( early_exit ) ) *early_exit = 1;
+}
+
FD_PROTOTYPES_END
#endif /* HEADER_fd_discof_restore_fd_snapin_tile_private_h */
diff --git a/src/discof/restore/fd_snapla_tile.c b/src/discof/restore/fd_snapla_tile.c
new file mode 100644
index 00000000000..876c81f88e5
--- /dev/null
+++ b/src/discof/restore/fd_snapla_tile.c
@@ -0,0 +1,438 @@
+#include "../../disco/topo/fd_topo.h"
+#include "../../disco/metrics/fd_metrics.h"
+#include "../../ballet/lthash/fd_lthash.h"
+#include "../../ballet/lthash/fd_lthash_adder.h"
+
+#include "generated/fd_snapla_tile_seccomp.h"
+
+#include "utils/fd_ssctrl.h"
+#include "utils/fd_ssparse.h"
+#include "utils/fd_ssmanifest_parser.h"
+
+#define NAME "snapla"
+
+#define FD_SNAPLA_OUT_CTRL 0UL
+
+struct fd_snapla_tile {
+ int state;
+ int full;
+
+ ulong seed;
+ int hash_account;
+ ulong num_hash_tiles;
+ ulong hash_tile_idx;
+ ulong accounts_seen;
+
+ fd_lthash_adder_t adder[1];
+ uchar data[ FD_RUNTIME_ACC_SZ_MAX ];
+ ulong acc_data_sz;
+
+ fd_ssparse_t * ssparse;
+ fd_ssmanifest_parser_t * manifest_parser;
+ fd_lthash_value_t running_lthash;
+
+ struct {
+ uchar pubkey[ FD_HASH_FOOTPRINT ];
+ uchar owner[ FD_HASH_FOOTPRINT ];
+ ulong data_len;
+ ulong lamports;
+ int executable;
+ } account_hdr;
+
+ struct {
+ struct {
+ ulong accounts_hashed;
+ } full;
+
+ struct {
+ ulong accounts_hashed;
+ } incremental;
+ } metrics;
+
+ struct {
+ fd_wksp_t * wksp;
+ ulong chunk0;
+ ulong wmark;
+ ulong mtu;
+ ulong pos;
+ } in;
+
+ struct {
+ fd_wksp_t * wksp;
+ ulong chunk0;
+ ulong wmark;
+ ulong chunk;
+ ulong mtu;
+ } out;
+
+ fd_snapshot_manifest_t manifest[1];
+};
+
+typedef struct fd_snapla_tile fd_snapla_tile_t;
+
+static inline int
+should_shutdown( fd_snapla_tile_t * ctx ) {
+ return ctx->state==FD_SNAPSHOT_STATE_SHUTDOWN;
+}
+
+static ulong
+scratch_align( void ) {
+ return fd_ulong_max( alignof(fd_snapla_tile_t),
+ fd_ulong_max( fd_ssparse_align(), fd_ssmanifest_parser_align() ) );
+}
+
+static ulong
+scratch_footprint( fd_topo_tile_t const * tile ) {
+ (void)tile;
+ ulong l = FD_LAYOUT_INIT;
+ l = FD_LAYOUT_APPEND( l, alignof(fd_snapla_tile_t), sizeof(fd_snapla_tile_t) );
+ l = FD_LAYOUT_APPEND( l, fd_ssparse_align(), fd_ssparse_footprint( 1UL<<24UL ) );
+ l = FD_LAYOUT_APPEND( l, fd_ssmanifest_parser_align(), fd_ssmanifest_parser_footprint() );
+ return FD_LAYOUT_FINI( l, alignof(fd_snapla_tile_t) );
+}
+
+static void
+metrics_write( fd_snapla_tile_t * ctx ) {
+ FD_MGAUGE_SET( SNAPLA, FULL_ACCOUNTS_HASHED, ctx->metrics.full.accounts_hashed );
+ FD_MGAUGE_SET( SNAPLA, INCREMENTAL_ACCOUNTS_HASHED, ctx->metrics.incremental.accounts_hashed );
+ FD_MGAUGE_SET( SNAPLA, STATE, (ulong)(ctx->state) );
+}
+
+static void
+transition_malformed( fd_snapla_tile_t * ctx,
+ fd_stem_context_t * stem ) {
+ ctx->state = FD_SNAPSHOT_STATE_ERROR;
+ fd_stem_publish( stem, FD_SNAPLA_OUT_CTRL, FD_SNAPSHOT_MSG_CTRL_ERROR, 0UL, 0UL, 0UL, 0UL, 0UL );
+}
+
+static int
+should_hash_account( fd_snapla_tile_t * ctx ) {
+ return ctx->accounts_seen%ctx->num_hash_tiles==ctx->hash_tile_idx;
+}
+
+static void
+streamlined_hash( fd_snapla_tile_t * ctx,
+ uchar const * frame ) {
+ ulong data_len = fd_ulong_load_8_fast( frame+0x08UL );
+ uchar pubkey[32]; memcpy( pubkey, frame+0x10UL, 32UL );
+ ulong lamports = fd_ulong_load_8_fast( frame+0x30UL );
+ ulong rent_epoch = fd_ulong_load_8_fast( frame+0x38UL ); (void)rent_epoch;
+ uchar owner[32]; memcpy( owner, frame+0x40UL, 32UL );
+ _Bool executable = !!frame[ 0x60UL ];
+
+ if( FD_UNLIKELY( data_len > FD_RUNTIME_ACC_SZ_MAX ) ) FD_LOG_ERR(( "Found unusually large account (data_sz=%lu), aborting", data_len ));
+ if( FD_UNLIKELY( lamports==0UL ) ) return;
+
+ uchar executable_flag = executable & 0x1;
+
+ fd_lthash_adder_push_solana_account( ctx->adder,
+ &ctx->running_lthash,
+ pubkey,
+ frame+0x88UL,
+ data_len,
+ lamports,
+ executable_flag,
+ owner );
+
+ if( FD_LIKELY( ctx->full ) ) ctx->metrics.full.accounts_hashed++;
+ else ctx->metrics.incremental.accounts_hashed++;
+}
+
+static int
+handle_data_frag( fd_snapla_tile_t * ctx,
+ ulong chunk,
+ ulong sz,
+ fd_stem_context_t * stem ) {
+ if( FD_UNLIKELY( ctx->state==FD_SNAPSHOT_STATE_FINISHING ) ) {
+ FD_LOG_WARNING(( "received data fragment while in finishing state" ));
+ transition_malformed( ctx, stem );
+ return 0;
+ } else if( FD_UNLIKELY( ctx->state==FD_SNAPSHOT_STATE_ERROR ) ) {
+ /* Ignore all data frags after observing an error in the stream until
+ we receive fail & init control messages to restart processing. */
+ return 0;
+ } else if( FD_UNLIKELY( ctx->state!=FD_SNAPSHOT_STATE_PROCESSING ) ) {
+ FD_LOG_ERR(( "invalid state for data frag %d", ctx->state ));
+ }
+
+ FD_TEST( chunk>=ctx->in.chunk0 && chunk<=ctx->in.wmark && sz<=ctx->in.mtu );
+
+ for(;;) {
+ if( FD_UNLIKELY( sz-ctx->in.pos==0UL ) ) break;
+ uchar const * data = (uchar const *)fd_chunk_to_laddr_const( ctx->in.wksp, chunk ) + ctx->in.pos;
+
+ fd_ssparse_advance_result_t result[1];
+ int res = fd_ssparse_advance( ctx->ssparse, data, sz-ctx->in.pos, result );
+ switch( res ) {
+ case FD_SSPARSE_ADVANCE_ERROR:
+ transition_malformed( ctx, stem );
+ return 0;
+ case FD_SSPARSE_ADVANCE_AGAIN:
+ break;
+ case FD_SSPARSE_ADVANCE_STATUS_CACHE:
+ /* ignore */
+ break;
+ case FD_SSPARSE_ADVANCE_MANIFEST: {
+ int res = fd_ssmanifest_parser_consume( ctx->manifest_parser,
+ result->manifest.data,
+ result->manifest.data_sz,
+ result->manifest.acc_vec_map,
+ result->manifest.acc_vec_pool );
+ if( FD_UNLIKELY( res==FD_SSMANIFEST_PARSER_ADVANCE_ERROR ) ) {
+ transition_malformed( ctx, stem );
+ return 0;
+ }
+ break;
+ }
+ case FD_SSPARSE_ADVANCE_ACCOUNT_HEADER:
+ if( FD_LIKELY( should_hash_account( ctx ) && result->account_header.lamports!=0UL ) ) {
+ FD_TEST( ctx->acc_data_sz==0UL );
+ ctx->hash_account = 1;
+ fd_memcpy( ctx->account_hdr.pubkey, result->account_header.pubkey, FD_HASH_FOOTPRINT );
+ fd_memcpy( ctx->account_hdr.owner, result->account_header.owner, FD_HASH_FOOTPRINT );
+ ctx->account_hdr.data_len = result->account_header.data_len;
+ ctx->account_hdr.executable = result->account_header.executable;
+ ctx->account_hdr.lamports = result->account_header.lamports;
+ }
+ ctx->accounts_seen++;
+ break;
+ case FD_SSPARSE_ADVANCE_ACCOUNT_DATA:
+ if( FD_LIKELY( ctx->hash_account ) ) {
+ fd_memcpy( ctx->data + ctx->acc_data_sz, result->account_data.data, result->account_data.data_sz );
+ ctx->acc_data_sz += result->account_data.data_sz;
+ }
+ break;
+ case FD_SSPARSE_ADVANCE_ACCOUNT_BATCH: {
+ for( ulong i=0UL; iaccount_batch.batch_cnt; i++ ) {
+ if( FD_LIKELY( should_hash_account( ctx ) ) ) streamlined_hash( ctx, result->account_batch.batch[ i ] );
+ ctx->accounts_seen++;
+ }
+ break;
+ }
+ case FD_SSPARSE_ADVANCE_DONE:
+ ctx->state = FD_SNAPSHOT_STATE_FINISHING;
+ break;
+ default:
+ FD_LOG_ERR(( "unexpected fd_ssparse_advance result %d", res ));
+ break;
+ }
+
+ ctx->in.pos += result->bytes_consumed;
+ if( FD_LIKELY( ctx->hash_account && ctx->acc_data_sz==ctx->account_hdr.data_len ) ) {
+ fd_lthash_adder_push_solana_account( ctx->adder,
+ &ctx->running_lthash,
+ ctx->account_hdr.pubkey,
+ ctx->data,
+ ctx->account_hdr.data_len,
+ ctx->account_hdr.lamports,
+ (uchar)ctx->account_hdr.executable,
+ ctx->account_hdr.owner );
+ ctx->acc_data_sz = 0UL;
+ ctx->hash_account = 0;
+
+ if( FD_LIKELY( ctx->full ) ) ctx->metrics.full.accounts_hashed++;
+ else ctx->metrics.incremental.accounts_hashed++;
+ }
+ }
+
+ int reprocess_frag = ctx->in.posin.pos = 0UL;
+ return reprocess_frag;
+}
+
+static void
+handle_control_frag( fd_snapla_tile_t * ctx,
+ fd_stem_context_t * stem,
+ ulong sig ) {
+ switch( sig ) {
+ case FD_SNAPSHOT_MSG_CTRL_INIT_FULL:
+ case FD_SNAPSHOT_MSG_CTRL_INIT_INCR:
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_IDLE );
+ ctx->full = sig==FD_SNAPSHOT_MSG_CTRL_INIT_FULL;
+ ctx->state = FD_SNAPSHOT_STATE_PROCESSING;
+ fd_lthash_zero( &ctx->running_lthash );
+ fd_ssparse_reset( ctx->ssparse );
+ fd_ssmanifest_parser_init( ctx->manifest_parser, ctx->manifest );
+ fd_lthash_adder_new( ctx->adder );
+ break;
+
+ case FD_SNAPSHOT_MSG_CTRL_FAIL:
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_PROCESSING ||
+ ctx->state==FD_SNAPSHOT_STATE_FINISHING ||
+ ctx->state==FD_SNAPSHOT_STATE_ERROR );
+ ctx->state = FD_SNAPSHOT_STATE_IDLE;
+ fd_lthash_zero( &ctx->running_lthash );
+ fd_ssparse_reset( ctx->ssparse );
+ fd_ssmanifest_parser_init( ctx->manifest_parser, ctx->manifest );
+ fd_lthash_adder_new( ctx->adder );
+ break;
+
+ case FD_SNAPSHOT_MSG_CTRL_NEXT:
+ case FD_SNAPSHOT_MSG_CTRL_DONE:{
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_PROCESSING ||
+ ctx->state==FD_SNAPSHOT_STATE_FINISHING ||
+ ctx->state==FD_SNAPSHOT_STATE_ERROR );
+ if( FD_UNLIKELY( ctx->state!=FD_SNAPSHOT_STATE_FINISHING ) ) {
+ transition_malformed( ctx, stem );
+ return;
+ }
+ fd_lthash_adder_flush( ctx->adder, &ctx->running_lthash );
+ uchar * lthash_out = fd_chunk_to_laddr( ctx->out.wksp, ctx->out.chunk );
+ fd_memcpy( lthash_out, &ctx->running_lthash, sizeof(fd_lthash_value_t) );
+ fd_stem_publish( stem, 0UL, FD_SNAPSHOT_HASH_MSG_RESULT_ADD, ctx->out.chunk, FD_LTHASH_LEN_BYTES, 0UL, 0UL, 0UL );
+ ctx->out.chunk = fd_dcache_compact_next( ctx->out.chunk, FD_LTHASH_LEN_BYTES, ctx->out.chunk0, ctx->out.wmark );
+ ctx->state = FD_SNAPSHOT_STATE_IDLE;
+ break;
+ }
+
+ case FD_SNAPSHOT_MSG_CTRL_SHUTDOWN:
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_IDLE );
+ ctx->state = FD_SNAPSHOT_STATE_SHUTDOWN;
+ metrics_write( ctx ); /* ensures that shutdown state is written to metrics workspace before the tile actually shuts down */
+ break;
+
+ case FD_SNAPSHOT_MSG_CTRL_ERROR:
+ ctx->state = FD_SNAPSHOT_STATE_ERROR;
+ break;
+
+ default:
+ FD_LOG_ERR(( "unexpected control sig %lu", sig ));
+ return;
+ }
+
+ /* Forward the control message down the pipeline */
+ fd_stem_publish( stem, FD_SNAPLA_OUT_CTRL, sig, 0UL, 0UL, 0UL, 0UL, 0UL );
+}
+
+static inline int
+returnable_frag( fd_snapla_tile_t * ctx,
+ ulong in_idx FD_PARAM_UNUSED,
+ ulong seq FD_PARAM_UNUSED,
+ ulong sig,
+ ulong chunk,
+ ulong sz,
+ ulong ctl FD_PARAM_UNUSED,
+ ulong tsorig FD_PARAM_UNUSED,
+ ulong tspub FD_PARAM_UNUSED,
+ fd_stem_context_t * stem ) {
+ FD_TEST( ctx->state!=FD_SNAPSHOT_STATE_SHUTDOWN );
+
+ if( FD_UNLIKELY( sig==FD_SNAPSHOT_MSG_DATA ) ) return handle_data_frag( ctx, chunk, sz, stem );
+ else handle_control_frag( ctx, stem, sig );
+
+ return 0;
+}
+
+static ulong
+populate_allowed_fds( fd_topo_t const * topo FD_PARAM_UNUSED,
+ fd_topo_tile_t const * tile FD_PARAM_UNUSED,
+ ulong out_fds_cnt,
+ int * out_fds ) {
+ if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
+
+ ulong out_cnt = 0;
+ out_fds[ out_cnt++ ] = 2UL; /* stderr */
+ if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) ) {
+ out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
+ }
+
+ return out_cnt;
+}
+
+static ulong
+populate_allowed_seccomp( fd_topo_t const * topo FD_PARAM_UNUSED,
+ fd_topo_tile_t const * tile FD_PARAM_UNUSED,
+ ulong out_cnt,
+ struct sock_filter * out ) {
+ populate_sock_filter_policy_fd_snapla_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
+ return sock_filter_policy_fd_snapla_tile_instr_cnt;
+}
+
+static void
+privileged_init( fd_topo_t * topo,
+ fd_topo_tile_t * tile ) {
+ void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
+
+ FD_SCRATCH_ALLOC_INIT( l, scratch );
+ fd_snapla_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapla_tile_t), sizeof(fd_snapla_tile_t) );
+
+ FD_TEST( fd_rng_secure( &ctx->seed, 8UL ) );
+}
+
+static void
+unprivileged_init( fd_topo_t * topo,
+ fd_topo_tile_t * tile ) {
+ void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
+
+ FD_SCRATCH_ALLOC_INIT( l, scratch );
+ fd_snapla_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapla_tile_t), sizeof(fd_snapla_tile_t) );
+ void * _ssparse = FD_SCRATCH_ALLOC_APPEND( l, fd_ssparse_align(), fd_ssparse_footprint( 1UL<<24UL ));
+ void * _manifest_parser = FD_SCRATCH_ALLOC_APPEND( l, fd_ssmanifest_parser_align(), fd_ssmanifest_parser_footprint() );
+
+ if( FD_UNLIKELY( tile->in_cnt!=1UL ) ) FD_LOG_ERR(( "tile `" NAME "` has %lu ins, expected 1", tile->in_cnt ));
+ if( FD_UNLIKELY( tile->out_cnt!=1UL ) ) FD_LOG_ERR(( "tile `" NAME "` has %lu outs, expected 1", tile->out_cnt ));
+
+ fd_topo_link_t const * in_link = &topo->links[ tile->in_link_id[ 0UL ] ];
+ fd_topo_wksp_t const * in_wksp = &topo->workspaces[ topo->objs[ in_link->dcache_obj_id ].wksp_id ];
+ ctx->in.wksp = in_wksp->wksp;;
+ ctx->in.chunk0 = fd_dcache_compact_chunk0( ctx->in.wksp, in_link->dcache );
+ ctx->in.wmark = fd_dcache_compact_wmark( ctx->in.wksp, in_link->dcache, in_link->mtu );
+ ctx->in.mtu = in_link->mtu;
+ ctx->in.pos = 0UL;
+
+ fd_topo_link_t * out_link = &topo->links[ tile->out_link_id[ 0UL ] ];
+ ctx->out.wksp = topo->workspaces[ topo->objs[ out_link->dcache_obj_id ].wksp_id ].wksp;
+ ctx->out.chunk0 = fd_dcache_compact_chunk0( fd_wksp_containing( out_link->dcache ), out_link->dcache );
+ ctx->out.wmark = fd_dcache_compact_wmark ( ctx->out.wksp, out_link->dcache, out_link->mtu );
+ ctx->out.chunk = ctx->out.chunk0;
+ ctx->out.mtu = out_link->mtu;
+ FD_TEST( 0==strcmp( out_link->name, "snapla_ls" ) );
+
+ ctx->ssparse = fd_ssparse_new( _ssparse, 1UL<<24UL, 0UL );
+ FD_TEST( ctx->ssparse );
+
+ ctx->manifest_parser = fd_ssmanifest_parser_join( fd_ssmanifest_parser_new( _manifest_parser ) );
+ FD_TEST( ctx->manifest_parser );
+
+ fd_ssparse_batch_enable( ctx->ssparse, 1 );
+ fd_lthash_adder_new( ctx->adder );
+ fd_ssmanifest_parser_init( ctx->manifest_parser, ctx->manifest );
+
+ ctx->metrics.full.accounts_hashed = 0UL;
+ ctx->metrics.incremental.accounts_hashed = 0UL;
+
+ ctx->state = FD_SNAPSHOT_STATE_IDLE;
+ ctx->full = 1;
+ ctx->acc_data_sz = 0UL;
+ ctx->hash_account = 0;
+ ctx->num_hash_tiles = fd_topo_tile_name_cnt( topo, "snapla" );
+ ctx->hash_tile_idx = tile->kind_id;
+ ctx->accounts_seen = 0UL;
+ fd_lthash_zero( &ctx->running_lthash );
+}
+
+#define STEM_BURST 2UL /* one control message and one malformed message or one hash result message */
+#define STEM_LAZY 1000L
+
+#define STEM_CALLBACK_CONTEXT_TYPE fd_snapla_tile_t
+#define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_snapla_tile_t)
+
+#define STEM_CALLBACK_SHOULD_SHUTDOWN should_shutdown
+#define STEM_CALLBACK_METRICS_WRITE metrics_write
+#define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
+
+#include "../../disco/stem/fd_stem.c"
+
+fd_topo_run_tile_t fd_tile_snapla = {
+ .name = NAME,
+ .populate_allowed_fds = populate_allowed_fds,
+ .populate_allowed_seccomp = populate_allowed_seccomp,
+ .scratch_align = scratch_align,
+ .scratch_footprint = scratch_footprint,
+ .privileged_init = privileged_init,
+ .unprivileged_init = unprivileged_init,
+ .run = stem_run,
+};
+
+#undef NAME
+
diff --git a/src/discof/restore/fd_snapla_tile.seccomppolicy b/src/discof/restore/fd_snapla_tile.seccomppolicy
new file mode 100644
index 00000000000..4289b3fdafd
--- /dev/null
+++ b/src/discof/restore/fd_snapla_tile.seccomppolicy
@@ -0,0 +1,21 @@
+# logfile_fd: It can be disabled by configuration, but typically tiles
+# will open a log file on boot and write all messages there.
+unsigned int logfile_fd
+
+# logging: all log messages are written to a file and/or pipe
+#
+# 'WARNING' and above are written to the STDERR pipe, while all messages
+# are always written to the log file.
+#
+# arg 0 is the file descriptor to write to. The boot process ensures
+# that descriptor 2 is always STDERR.
+write: (or (eq (arg 0) 2)
+ (eq (arg 0) logfile_fd))
+
+# logging: 'WARNING' and above fsync the logfile to disk immediately
+#
+# arg 0 is the file descriptor to fsync.
+fsync: (eq (arg 0) logfile_fd)
+
+# shutdown: exit is called on shutdown
+exit: (eq (arg 0) 0)
diff --git a/src/discof/restore/fd_snapls_tile.c b/src/discof/restore/fd_snapls_tile.c
new file mode 100644
index 00000000000..51e3d170b90
--- /dev/null
+++ b/src/discof/restore/fd_snapls_tile.c
@@ -0,0 +1,453 @@
+#include "../../disco/topo/fd_topo.h"
+#include "../../disco/metrics/fd_metrics.h"
+#include "../../ballet/lthash/fd_lthash.h"
+#include "../../flamenco/runtime/fd_hashes.h"
+
+#include "utils/fd_ssctrl.h"
+
+#include "generated/fd_snapls_tile_seccomp.h"
+
+#define NAME "snapls"
+
+#define IN_KIND_SNAPIN (0)
+#define IN_KIND_SNAPLA (1)
+#define MAX_IN_LINKS (1 + FD_SNAPSHOT_MAX_SNAPLA_TILES)
+
+struct fd_snapls_tile {
+ int state;
+ int full;
+
+ fd_lthash_value_t running_lthash;
+
+ fd_blake3_t b3[1];
+ ulong acc_data_sz;
+ int hash_account;
+ ulong num_hash_tiles;
+
+ uchar in_kind[ MAX_IN_LINKS ];
+ ulong adder_in_offset;
+
+ ulong num_acks;
+ uchar acks[ 1 + FD_SNAPSHOT_MAX_SNAPLA_TILES ];
+
+ struct {
+ fd_lthash_value_t expected_lthash;
+ fd_lthash_value_t calculated_lthash;
+ ulong received_lthashes;
+ ulong ack_sig;
+ int awaiting_ack;
+ int hash_check_done;
+ } hash_accum;
+
+ struct {
+ uchar pubkey[ FD_HASH_FOOTPRINT ];
+ uchar owner[ FD_HASH_FOOTPRINT ];
+ ulong data_len;
+ int executable;
+ } account_hdr;
+
+ struct {
+ struct {
+ ulong accounts_hashed;
+ } full;
+
+ struct {
+ ulong accounts_hashed;
+ } incremental;
+ } metrics;
+
+ struct {
+ fd_wksp_t * wksp;
+ ulong chunk0;
+ ulong wmark;
+ ulong mtu;
+ ulong pos;
+ } in;
+
+ struct {
+ fd_wksp_t * wksp;
+ ulong chunk0;
+ ulong wmark;
+ ulong mtu;
+ } adder_in[ FD_SNAPSHOT_MAX_SNAPLA_TILES ];
+};
+
+typedef struct fd_snapls_tile fd_snapls_tile_t;
+
+static inline int
+should_shutdown( fd_snapls_tile_t * ctx ) {
+ return ctx->state==FD_SNAPSHOT_STATE_SHUTDOWN;
+}
+
+static ulong
+scratch_align( void ) {
+ return alignof(fd_snapls_tile_t);
+}
+
+static ulong
+scratch_footprint( fd_topo_tile_t const * tile ) {
+ (void)tile;
+ ulong l = FD_LAYOUT_INIT;
+ l = FD_LAYOUT_APPEND( l, alignof(fd_snapls_tile_t), sizeof(fd_snapls_tile_t) );
+ return FD_LAYOUT_FINI( l, alignof(fd_snapls_tile_t) );
+}
+
+static void
+metrics_write( fd_snapls_tile_t * ctx ) {
+ FD_MGAUGE_SET( SNAPLS, FULL_ACCOUNTS_HASHED, ctx->metrics.full.accounts_hashed );
+ FD_MGAUGE_SET( SNAPLS, INCREMENTAL_ACCOUNTS_HASHED, ctx->metrics.incremental.accounts_hashed );
+ FD_MGAUGE_SET( SNAPLS, STATE, (ulong)(ctx->state) );
+}
+
+static void
+transition_malformed( fd_snapls_tile_t * ctx,
+ fd_stem_context_t * stem ) {
+ ctx->state = FD_SNAPSHOT_STATE_ERROR;
+ fd_stem_publish( stem, 0UL, FD_SNAPSHOT_MSG_CTRL_ERROR, 0UL, 0UL, 0UL, 0UL, 0UL );
+}
+
+static void
+handle_data_frag( fd_snapls_tile_t * ctx,
+ ulong sig,
+ ulong chunk,
+ ulong sz ) {
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_PROCESSING );
+
+ switch( sig ) {
+ case FD_SNAPSHOT_HASH_MSG_SUB: {
+ fd_snapshot_full_account_t const * prev_acc = fd_chunk_to_laddr_const( ctx->in.wksp, chunk );
+
+ fd_lthash_value_t prev_lthash[1];
+ fd_hashes_account_lthash_simple( prev_acc->hdr.pubkey,
+ prev_acc->hdr.owner,
+ prev_acc->hdr.lamports,
+ prev_acc->hdr.executable,
+ prev_acc->data,
+ prev_acc->hdr.data_len,
+ prev_lthash );
+ fd_lthash_add( &ctx->running_lthash, prev_lthash );
+
+ if( FD_LIKELY( ctx->full ) ) ctx->metrics.full.accounts_hashed++;
+ else ctx->metrics.incremental.accounts_hashed++;
+ break;
+ }
+ case FD_SNAPSHOT_HASH_MSG_SUB_HDR: {
+ fd_snapshot_account_hdr_t const * acc = fd_chunk_to_laddr_const( ctx->in.wksp, chunk );
+
+ if( acc->lamports!=0UL ) {
+ ctx->hash_account = 1;
+ fd_blake3_init( ctx->b3 );
+ fd_blake3_append( ctx->b3, &acc->lamports, sizeof(ulong) );
+ ctx->account_hdr.data_len = acc->data_len;
+ ctx->account_hdr.executable = acc->executable;
+ memcpy( ctx->account_hdr.owner, acc->owner, FD_HASH_FOOTPRINT );
+ memcpy( ctx->account_hdr.pubkey, acc->pubkey, FD_HASH_FOOTPRINT );
+ }
+ break;
+ }
+ case FD_SNAPSHOT_HASH_MSG_SUB_DATA: {
+ if( FD_LIKELY( !ctx->hash_account ) ) break;
+
+ uchar const * acc_data = fd_chunk_to_laddr_const( ctx->in.wksp, chunk );
+ fd_blake3_append( ctx->b3, acc_data, sz );
+ ctx->acc_data_sz += sz;
+ break;
+ }
+ default:
+ FD_LOG_ERR(( "unexpected sig %lu in handle_data_frag", sig ));
+ return;
+ }
+
+ if( FD_LIKELY( ctx->hash_account && ctx->acc_data_sz==ctx->account_hdr.data_len ) ) {
+ fd_lthash_value_t account_lthash[1];
+ fd_lthash_zero( account_lthash );
+
+ uchar executable_flag = ctx->account_hdr.executable & 0x1;
+ fd_blake3_append( ctx->b3, &executable_flag, sizeof(uchar) );
+ fd_blake3_append( ctx->b3, ctx->account_hdr.owner, FD_HASH_FOOTPRINT );
+ fd_blake3_append( ctx->b3, ctx->account_hdr.pubkey, FD_HASH_FOOTPRINT );
+ fd_blake3_fini_2048( ctx->b3, account_lthash->bytes );
+ fd_lthash_add( &ctx->running_lthash, account_lthash );
+
+ ctx->acc_data_sz = 0UL;
+ ctx->hash_account = 0;
+
+ if( FD_LIKELY( ctx->full ) ) ctx->metrics.full.accounts_hashed++;
+ else ctx->metrics.incremental.accounts_hashed++;
+ }
+}
+
+static int
+recv_acks( fd_snapls_tile_t * ctx,
+ ulong in_idx ) {
+ FD_TEST( ctx->acks[ in_idx ]==0 );
+
+ ctx->acks[ in_idx ] = 1;
+ ctx->num_acks++;
+
+ if( FD_UNLIKELY( ctx->num_acks!=1UL+ctx->num_hash_tiles ) ) return 0;
+
+ fd_memset( ctx->acks, 0, sizeof(ctx->acks) );
+ ctx->num_acks = 0UL;
+ return 1;
+}
+
+static void
+handle_control_frag( fd_snapls_tile_t * ctx,
+ fd_stem_context_t * stem,
+ ulong sig,
+ ulong in_idx ) {
+ switch( sig ) {
+ case FD_SNAPSHOT_MSG_CTRL_INIT_FULL:
+ case FD_SNAPSHOT_MSG_CTRL_INIT_INCR: {
+ int done = recv_acks( ctx, in_idx );
+ if( !done ) return;
+
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_IDLE );
+ ctx->full = sig==FD_SNAPSHOT_MSG_CTRL_INIT_FULL;
+ ctx->state = FD_SNAPSHOT_STATE_PROCESSING;
+ fd_lthash_zero( &ctx->running_lthash );
+ break;
+ }
+
+ case FD_SNAPSHOT_MSG_CTRL_FAIL: {
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_PROCESSING ||
+ ctx->state==FD_SNAPSHOT_STATE_FINISHING ||
+ ctx->state==FD_SNAPSHOT_STATE_ERROR );
+ int done = recv_acks( ctx, in_idx );
+ if( !done ) return;
+
+ ctx->state = FD_SNAPSHOT_STATE_IDLE;
+ fd_lthash_zero( &ctx->running_lthash );
+ break;
+ }
+
+ case FD_SNAPSHOT_MSG_CTRL_NEXT:
+ case FD_SNAPSHOT_MSG_CTRL_DONE: {
+ int done = recv_acks( ctx, in_idx );
+ if( !done ) return;
+
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_PROCESSING );
+ if( FD_UNLIKELY( ctx->state!=FD_SNAPSHOT_STATE_PROCESSING ) ) {
+ transition_malformed( ctx, stem );
+ return;
+ }
+
+ ctx->hash_accum.ack_sig = sig;
+ ctx->hash_accum.awaiting_ack = 1;
+ ctx->state = FD_SNAPSHOT_STATE_IDLE;
+ return; /* the ack is sent when all hashes are received */
+ }
+
+ case FD_SNAPSHOT_MSG_CTRL_SHUTDOWN: {
+ int done = recv_acks( ctx, in_idx );
+ if( !done ) return;
+
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_IDLE );
+ ctx->state = FD_SNAPSHOT_STATE_SHUTDOWN;
+ metrics_write( ctx ); /* ensures that shutdown state is written to metrics workspace before the tile actually shuts down */
+ break;
+ }
+
+ case FD_SNAPSHOT_MSG_CTRL_ERROR:
+ ctx->state = FD_SNAPSHOT_STATE_ERROR;
+ break;
+
+ default:
+ FD_LOG_ERR(( "unexpected control sig %lu", sig ));
+ return;
+ }
+
+ /* Forward the control message down the pipeline */
+ fd_stem_publish( stem, 0UL, sig, 0UL, 0UL, 0UL, 0UL, 0UL );
+}
+
+static void
+handle_hash_frag( fd_snapls_tile_t * ctx,
+ ulong in_idx,
+ ulong sig,
+ ulong chunk,
+ ulong sz ) {
+ FD_TEST( ctx->state==FD_SNAPSHOT_STATE_PROCESSING || ctx->state==FD_SNAPSHOT_STATE_IDLE );
+ switch( sig ) {
+ case FD_SNAPSHOT_HASH_MSG_RESULT_ADD: {
+ FD_TEST( sz==sizeof(fd_lthash_value_t) );
+ fd_lthash_value_t const * result = fd_chunk_to_laddr_const( ctx->adder_in[ in_idx-ctx->adder_in_offset ].wksp, chunk );
+ fd_lthash_add( &ctx->hash_accum.calculated_lthash, result );
+ ctx->hash_accum.received_lthashes++;
+ break;
+ }
+ case FD_SNAPSHOT_HASH_MSG_EXPECTED: {
+ FD_TEST( sz==sizeof(fd_lthash_value_t) );
+ FD_TEST( ctx->in_kind[ in_idx ]==IN_KIND_SNAPIN );
+ fd_lthash_value_t const * result = fd_chunk_to_laddr_const( ctx->in.wksp, chunk );
+ fd_memcpy( &ctx->hash_accum.expected_lthash, result, sizeof(fd_lthash_value_t) );
+ break;
+ }
+ default:
+ FD_LOG_ERR(( "unexpected hash sig %lu", sig ));
+ break;
+ }
+
+}
+
+static inline int
+returnable_frag( fd_snapls_tile_t * ctx,
+ ulong in_idx FD_PARAM_UNUSED,
+ ulong seq FD_PARAM_UNUSED,
+ ulong sig,
+ ulong chunk,
+ ulong sz,
+ ulong ctl FD_PARAM_UNUSED,
+ ulong tsorig FD_PARAM_UNUSED,
+ ulong tspub FD_PARAM_UNUSED,
+ fd_stem_context_t * stem ) {
+ FD_TEST( ctx->state!=FD_SNAPSHOT_STATE_SHUTDOWN );
+
+ if( FD_LIKELY( sig==FD_SNAPSHOT_HASH_MSG_SUB ||
+ sig==FD_SNAPSHOT_HASH_MSG_SUB_HDR ||
+ sig==FD_SNAPSHOT_HASH_MSG_SUB_DATA ) ) handle_data_frag( ctx, sig, chunk, sz );
+ else if( FD_LIKELY( sig==FD_SNAPSHOT_HASH_MSG_RESULT_ADD ||
+ sig==FD_SNAPSHOT_HASH_MSG_EXPECTED ) ) handle_hash_frag( ctx, in_idx, sig, chunk, sz );
+ else handle_control_frag( ctx, stem, sig, in_idx );
+
+ return 0;
+}
+
+static void
+after_credit( fd_snapls_tile_t * ctx,
+ fd_stem_context_t * stem,
+ int * opt_poll_in FD_PARAM_UNUSED,
+ int * charge_busy FD_PARAM_UNUSED ) {
+ if( FD_UNLIKELY( ctx->hash_accum.received_lthashes==ctx->num_hash_tiles && ctx->hash_accum.awaiting_ack ) ) {
+ fd_lthash_sub( &ctx->hash_accum.calculated_lthash, &ctx->running_lthash );
+ if( FD_UNLIKELY( memcmp( &ctx->hash_accum.expected_lthash, &ctx->hash_accum.calculated_lthash, sizeof(fd_lthash_value_t) ) ) ) {
+ FD_LOG_WARNING(( "calculated accounts lthash %s does not match accounts lthash %s in snapshot manifest",
+ FD_LTHASH_ENC_32_ALLOCA( &ctx->hash_accum.calculated_lthash ),
+ FD_LTHASH_ENC_32_ALLOCA( &ctx->hash_accum.expected_lthash ) ));
+ transition_malformed( ctx, stem );
+ } else {
+ FD_LOG_NOTICE(( "calculated accounts lthash %s matches accounts lthash %s in snapshot manifest",
+ FD_LTHASH_ENC_32_ALLOCA( &ctx->hash_accum.calculated_lthash ),
+ FD_LTHASH_ENC_32_ALLOCA( &ctx->hash_accum.expected_lthash ) ));
+ }
+ ctx->hash_accum.received_lthashes = 0UL;
+ ctx->hash_accum.hash_check_done = 1;
+ }
+
+ if( FD_UNLIKELY( ctx->hash_accum.awaiting_ack && ctx->hash_accum.hash_check_done ) ) {
+ fd_stem_publish( stem, 0UL, ctx->hash_accum.ack_sig, 0UL, 0UL, 0UL, 0UL, 0UL );
+ ctx->hash_accum.awaiting_ack = 0;
+ ctx->hash_accum.hash_check_done = 0;
+ }
+}
+
+static ulong
+populate_allowed_fds( fd_topo_t const * topo FD_PARAM_UNUSED,
+ fd_topo_tile_t const * tile FD_PARAM_UNUSED,
+ ulong out_fds_cnt,
+ int * out_fds ) {
+ if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
+
+ ulong out_cnt = 0;
+ out_fds[ out_cnt++ ] = 2UL; /* stderr */
+ if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) ) {
+ out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
+ }
+
+ return out_cnt;
+}
+
+static ulong
+populate_allowed_seccomp( fd_topo_t const * topo FD_PARAM_UNUSED,
+ fd_topo_tile_t const * tile FD_PARAM_UNUSED,
+ ulong out_cnt,
+ struct sock_filter * out ) {
+ populate_sock_filter_policy_fd_snapls_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
+ return sock_filter_policy_fd_snapls_tile_instr_cnt;
+}
+
+static void
+unprivileged_init( fd_topo_t * topo,
+ fd_topo_tile_t * tile ) {
+ void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
+
+ FD_SCRATCH_ALLOC_INIT( l, scratch );
+ fd_snapls_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapls_tile_t), sizeof(fd_snapls_tile_t) );
+
+ ulong expected_in_cnt = 1UL + fd_topo_tile_name_cnt( topo, "snapla" );
+ if( FD_UNLIKELY( tile->in_cnt!=expected_in_cnt ) ) FD_LOG_ERR(( "tile `" NAME "` has %lu ins, expected %lu", tile->in_cnt, expected_in_cnt ));
+ if( FD_UNLIKELY( tile->out_cnt!=1UL ) ) FD_LOG_ERR(( "tile `" NAME "` has %lu outs, expected 1", tile->out_cnt ));
+
+ ulong adder_idx = 0UL;
+ for( ulong i=0UL; i<(tile->in_cnt); i++ ) {
+ fd_topo_link_t * in_link = &topo->links[ tile->in_link_id[ i ] ];
+ fd_topo_wksp_t const * in_wksp = &topo->workspaces[ topo->objs[ in_link->dcache_obj_id ].wksp_id ];
+ if( FD_LIKELY( 0==strcmp( in_link->name, "snapin_ls" ) ) ) {
+ ctx->in.wksp = in_wksp->wksp;;
+ ctx->in.chunk0 = fd_dcache_compact_chunk0( ctx->in.wksp, in_link->dcache );
+ ctx->in.wmark = fd_dcache_compact_wmark( ctx->in.wksp, in_link->dcache, in_link->mtu );
+ ctx->in.mtu = in_link->mtu;
+ ctx->in.pos = 0UL;
+ ctx->in_kind[ i ] = IN_KIND_SNAPIN;
+ } else if( FD_LIKELY( 0==strcmp( in_link->name, "snapla_ls" ) ) ) {
+ ctx->adder_in[ adder_idx ].wksp = in_wksp->wksp;
+ ctx->adder_in[ adder_idx ].chunk0 = fd_dcache_compact_chunk0( ctx->adder_in[ adder_idx ].wksp, in_link->dcache );
+ ctx->adder_in[ adder_idx ].wmark = fd_dcache_compact_wmark ( ctx->adder_in[ adder_idx ].wksp, in_link->dcache, in_link->mtu );
+ ctx->adder_in[ adder_idx ].mtu = in_link->mtu;
+ ctx->in_kind[ i ] = IN_KIND_SNAPLA;
+ if( FD_LIKELY( adder_idx==0UL ) ) ctx->adder_in_offset = i;
+ adder_idx++;
+ } else {
+ FD_LOG_ERR(( "tile `" NAME "` has unexpected in link name `%s`", in_link->name ));
+ }
+ }
+
+ fd_topo_link_t * out_link = &topo->links[ tile->out_link_id[ 0UL ] ];
+ FD_TEST( 0==strcmp( out_link->name, "snapls_ct" ) );
+
+ ctx->metrics.full.accounts_hashed = 0UL;
+ ctx->metrics.incremental.accounts_hashed = 0UL;
+
+ ctx->state = FD_SNAPSHOT_STATE_IDLE;
+ ctx->full = 1;
+ ctx->hash_account = 0;
+
+ ctx->num_hash_tiles = fd_topo_tile_name_cnt( topo, "snapla" );
+
+ ctx->hash_accum.received_lthashes = 0UL;
+ ctx->hash_accum.awaiting_ack = 0;
+ ctx->hash_accum.hash_check_done = 0;
+
+ ctx->num_acks = 0UL;
+ fd_memset( ctx->acks, 0, sizeof(ctx->acks) );
+
+ fd_lthash_zero( &ctx->hash_accum.calculated_lthash );
+ fd_lthash_zero( &ctx->running_lthash );
+}
+
+#define STEM_BURST 2UL /* one control message and one malformed message */
+#define STEM_LAZY 1000L
+
+#define STEM_CALLBACK_CONTEXT_TYPE fd_snapls_tile_t
+#define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_snapls_tile_t)
+
+#define STEM_CALLBACK_SHOULD_SHUTDOWN should_shutdown
+#define STEM_CALLBACK_METRICS_WRITE metrics_write
+#define STEM_CALLBACK_AFTER_CREDIT after_credit
+#define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
+
+#include "../../disco/stem/fd_stem.c"
+
+fd_topo_run_tile_t fd_tile_snapls = {
+ .name = NAME,
+ .populate_allowed_fds = populate_allowed_fds,
+ .populate_allowed_seccomp = populate_allowed_seccomp,
+ .scratch_align = scratch_align,
+ .scratch_footprint = scratch_footprint,
+ .unprivileged_init = unprivileged_init,
+ .run = stem_run,
+};
+
+#undef NAME
+
diff --git a/src/discof/restore/fd_snapls_tile.seccomppolicy b/src/discof/restore/fd_snapls_tile.seccomppolicy
new file mode 100644
index 00000000000..4289b3fdafd
--- /dev/null
+++ b/src/discof/restore/fd_snapls_tile.seccomppolicy
@@ -0,0 +1,21 @@
+# logfile_fd: It can be disabled by configuration, but typically tiles
+# will open a log file on boot and write all messages there.
+unsigned int logfile_fd
+
+# logging: all log messages are written to a file and/or pipe
+#
+# 'WARNING' and above are written to the STDERR pipe, while all messages
+# are always written to the log file.
+#
+# arg 0 is the file descriptor to write to. The boot process ensures
+# that descriptor 2 is always STDERR.
+write: (or (eq (arg 0) 2)
+ (eq (arg 0) logfile_fd))
+
+# logging: 'WARNING' and above fsync the logfile to disk immediately
+#
+# arg 0 is the file descriptor to fsync.
+fsync: (eq (arg 0) logfile_fd)
+
+# shutdown: exit is called on shutdown
+exit: (eq (arg 0) 0)
diff --git a/src/discof/restore/generated/fd_snapla_tile_seccomp.h b/src/discof/restore/generated/fd_snapla_tile_seccomp.h
new file mode 100644
index 00000000000..318f605b46b
--- /dev/null
+++ b/src/discof/restore/generated/fd_snapla_tile_seccomp.h
@@ -0,0 +1,68 @@
+/* THIS FILE WAS GENERATED BY generate_filters.py. DO NOT EDIT BY HAND! */
+#ifndef HEADER_fd_src_discof_restore_generated_fd_snapla_tile_seccomp_h
+#define HEADER_fd_src_discof_restore_generated_fd_snapla_tile_seccomp_h
+
+#include "../../../../src/util/fd_util_base.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(__i386__)
+# define ARCH_NR AUDIT_ARCH_I386
+#elif defined(__x86_64__)
+# define ARCH_NR AUDIT_ARCH_X86_64
+#elif defined(__aarch64__)
+# define ARCH_NR AUDIT_ARCH_AARCH64
+#else
+# error "Target architecture is unsupported by seccomp."
+#endif
+static const unsigned int sock_filter_policy_fd_snapla_tile_instr_cnt = 17;
+
+static void populate_sock_filter_policy_fd_snapla_tile( ulong out_cnt, struct sock_filter * out, unsigned int logfile_fd ) {
+ FD_TEST( out_cnt >= 17 );
+ struct sock_filter filter[17] = {
+ /* Check: Jump to RET_KILL_PROCESS if the script's arch != the runtime arch */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, arch ) ) ),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ARCH_NR, 0, /* RET_KILL_PROCESS */ 13 ),
+ /* loading syscall number in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, nr ) ) ),
+ /* allow write based on expression */
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_write, /* check_write */ 3, 0 ),
+ /* allow fsync based on expression */
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_fsync, /* check_fsync */ 6, 0 ),
+ /* allow exit based on expression */
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_exit, /* check_exit */ 7, 0 ),
+ /* none of the syscalls matched */
+ { BPF_JMP | BPF_JA, 0, 0, /* RET_KILL_PROCESS */ 8 },
+// check_write:
+ /* load syscall argument 0 in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_ALLOW */ 7, /* lbl_1 */ 0 ),
+// lbl_1:
+ /* load syscall argument 0 in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 5, /* RET_KILL_PROCESS */ 4 ),
+// check_fsync:
+ /* load syscall argument 0 in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 3, /* RET_KILL_PROCESS */ 2 ),
+// check_exit:
+ /* load syscall argument 0 in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* RET_ALLOW */ 1, /* RET_KILL_PROCESS */ 0 ),
+// RET_KILL_PROCESS:
+ /* KILL_PROCESS is placed before ALLOW since it's the fallthrough case. */
+ BPF_STMT( BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS ),
+// RET_ALLOW:
+ /* ALLOW has to be reached by jumping */
+ BPF_STMT( BPF_RET | BPF_K, SECCOMP_RET_ALLOW ),
+ };
+ fd_memcpy( out, filter, sizeof( filter ) );
+}
+
+#endif
diff --git a/src/discof/restore/generated/fd_snapls_tile_seccomp.h b/src/discof/restore/generated/fd_snapls_tile_seccomp.h
new file mode 100644
index 00000000000..02ed22a9621
--- /dev/null
+++ b/src/discof/restore/generated/fd_snapls_tile_seccomp.h
@@ -0,0 +1,68 @@
+/* THIS FILE WAS GENERATED BY generate_filters.py. DO NOT EDIT BY HAND! */
+#ifndef HEADER_fd_src_discof_restore_generated_fd_snapls_tile_seccomp_h
+#define HEADER_fd_src_discof_restore_generated_fd_snapls_tile_seccomp_h
+
+#include "../../../../src/util/fd_util_base.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(__i386__)
+# define ARCH_NR AUDIT_ARCH_I386
+#elif defined(__x86_64__)
+# define ARCH_NR AUDIT_ARCH_X86_64
+#elif defined(__aarch64__)
+# define ARCH_NR AUDIT_ARCH_AARCH64
+#else
+# error "Target architecture is unsupported by seccomp."
+#endif
+static const unsigned int sock_filter_policy_fd_snapls_tile_instr_cnt = 17;
+
+static void populate_sock_filter_policy_fd_snapls_tile( ulong out_cnt, struct sock_filter * out, unsigned int logfile_fd ) {
+ FD_TEST( out_cnt >= 17 );
+ struct sock_filter filter[17] = {
+ /* Check: Jump to RET_KILL_PROCESS if the script's arch != the runtime arch */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, arch ) ) ),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, ARCH_NR, 0, /* RET_KILL_PROCESS */ 13 ),
+ /* loading syscall number in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, ( offsetof( struct seccomp_data, nr ) ) ),
+ /* allow write based on expression */
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_write, /* check_write */ 3, 0 ),
+ /* allow fsync based on expression */
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_fsync, /* check_fsync */ 6, 0 ),
+ /* allow exit based on expression */
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, SYS_exit, /* check_exit */ 7, 0 ),
+ /* none of the syscalls matched */
+ { BPF_JMP | BPF_JA, 0, 0, /* RET_KILL_PROCESS */ 8 },
+// check_write:
+ /* load syscall argument 0 in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 2, /* RET_ALLOW */ 7, /* lbl_1 */ 0 ),
+// lbl_1:
+ /* load syscall argument 0 in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 5, /* RET_KILL_PROCESS */ 4 ),
+// check_fsync:
+ /* load syscall argument 0 in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, logfile_fd, /* RET_ALLOW */ 3, /* RET_KILL_PROCESS */ 2 ),
+// check_exit:
+ /* load syscall argument 0 in accumulator */
+ BPF_STMT( BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
+ BPF_JUMP( BPF_JMP | BPF_JEQ | BPF_K, 0, /* RET_ALLOW */ 1, /* RET_KILL_PROCESS */ 0 ),
+// RET_KILL_PROCESS:
+ /* KILL_PROCESS is placed before ALLOW since it's the fallthrough case. */
+ BPF_STMT( BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS ),
+// RET_ALLOW:
+ /* ALLOW has to be reached by jumping */
+ BPF_STMT( BPF_RET | BPF_K, SECCOMP_RET_ALLOW ),
+ };
+ fd_memcpy( out, filter, sizeof( filter ) );
+}
+
+#endif
diff --git a/src/discof/restore/utils/fd_ssctrl.h b/src/discof/restore/utils/fd_ssctrl.h
index 79f96fddb58..d5680faea38 100644
--- a/src/discof/restore/utils/fd_ssctrl.h
+++ b/src/discof/restore/utils/fd_ssctrl.h
@@ -2,6 +2,7 @@
#define HEADER_fd_src_discof_restore_utils_fd_ssctrl_h
#include "../../../util/net/fd_net_headers.h"
+#include "../../../flamenco/runtime/fd_runtime_const.h"
/* The snapshot tiles have a somewhat involved state machine, which is
controlled by snapct. Imagine first the following sequence:
@@ -70,6 +71,17 @@
#define FD_SNAPSHOT_MSG_CTRL_SHUTDOWN (7UL) /* No work left to do, perform final cleanup and shut down */
#define FD_SNAPSHOT_MSG_CTRL_ERROR (8UL) /* Some tile encountered an error with the current stream */
+/* snapla -> snapls */
+#define FD_SNAPSHOT_HASH_MSG_RESULT_ADD (9UL) /* Hash result sent from snapla to snapls */
+
+/* snapin -> snapls */
+#define FD_SNAPSHOT_HASH_MSG_EXPECTED (10UL) /* Hash result sent from snapin to snapls */
+
+/* snapin -> snapls */
+#define FD_SNAPSHOT_HASH_MSG_SUB (11UL) /* Duplicate account sent from snapin to snapls, includes account header and data */
+#define FD_SNAPSHOT_HASH_MSG_SUB_HDR (12UL) /* Duplicate account sent from snapin to snapls, only the account header, no data */
+#define FD_SNAPSHOT_HASH_MSG_SUB_DATA (13UL) /* Duplicate account sent from snapin to snapls, only the account data, no header */
+
/* Sent by snapct to tell snapld whether to load a local file or
download from a particular external peer. */
typedef struct fd_ssctrl_init {
@@ -83,4 +95,45 @@ typedef struct fd_ssctrl_meta {
char name[ PATH_MAX ];
} fd_ssctrl_meta_t;
+struct fd_snapshot_account_hdr {
+ uchar pubkey[ FD_PUBKEY_FOOTPRINT ];
+ uchar owner[ FD_PUBKEY_FOOTPRINT ];
+ ulong lamports;
+ uchar executable;
+ ulong data_len;
+};
+typedef struct fd_snapshot_account_hdr fd_snapshot_account_hdr_t;
+
+/* fd_snapshot_account_hdr_init initializes a fd_snapshot_account_hdr_t struct
+ with the appropriate account metadata fields. */
+static inline void
+fd_snapshot_account_hdr_init( fd_snapshot_account_hdr_t * account,
+ uchar const pubkey[ FD_PUBKEY_FOOTPRINT ],
+ uchar const owner[ FD_PUBKEY_FOOTPRINT ],
+ ulong lamports,
+ uchar executable,
+ ulong data_len ) {
+ fd_memcpy( account->pubkey, pubkey, FD_PUBKEY_FOOTPRINT );
+ fd_memcpy( account->owner, owner, FD_PUBKEY_FOOTPRINT );
+ account->lamports = lamports;
+ account->executable = executable;
+ account->data_len = data_len;
+}
+
+/* fd_snapshot_full_account is the contents of the
+ SNAPSHOT_HASH_MSG_SUB message. It contains a fd_snapshot_account_hdr_t
+ header and the corresponding account data in a single message.
+
+ For simplicity and conformance to burst limitations in snapin, the
+ entire duplicate account is sent in one message (one frag). Consider
+ caching the lthash of the duplicate account so we do not have to
+ send the entire account over. */
+struct fd_snapshot_full_account {
+ fd_snapshot_account_hdr_t hdr;
+ uchar data[ FD_RUNTIME_ACC_SZ_MAX ];
+};
+typedef struct fd_snapshot_full_account fd_snapshot_full_account_t;
+
+#define FD_SNAPSHOT_MAX_SNAPLA_TILES (8UL)
+
#endif /* HEADER_fd_src_discof_restore_utils_fd_ssctrl_h */
diff --git a/src/flamenco/runtime/fd_hashes.c b/src/flamenco/runtime/fd_hashes.c
index dd67683744d..7217975aebf 100644
--- a/src/flamenco/runtime/fd_hashes.c
+++ b/src/flamenco/runtime/fd_hashes.c
@@ -13,22 +13,39 @@ fd_hashes_account_lthash( fd_pubkey_t const * pubkey,
fd_account_meta_t const * account,
uchar const * data,
fd_lthash_value_t * lthash_out ) {
+ fd_hashes_account_lthash_simple( pubkey->uc,
+ account->owner,
+ account->lamports,
+ account->executable,
+ data,
+ account->dlen,
+ lthash_out );
+}
+
+void
+fd_hashes_account_lthash_simple( uchar const pubkey[ static FD_HASH_FOOTPRINT ],
+ uchar const owner[ static FD_HASH_FOOTPRINT ],
+ ulong lamports,
+ uchar executable,
+ uchar const * data,
+ ulong data_len,
+ fd_lthash_value_t * lthash_out ) {
fd_lthash_zero( lthash_out );
/* Accounts with zero lamports are not included in the hash, so they should always be treated as zero */
- if( FD_UNLIKELY( account->lamports == 0 ) ) {
+ if( FD_UNLIKELY( lamports == 0 ) ) {
return;
}
- uchar executable = account->executable & 0x1;
+ uchar executable_flag = executable & 0x1;
fd_blake3_t b3[1];
fd_blake3_init( b3 );
- fd_blake3_append( b3, &account->lamports, sizeof( ulong ) );
- fd_blake3_append( b3, data, account->dlen );
- fd_blake3_append( b3, &executable, sizeof( uchar ) );
- fd_blake3_append( b3, account->owner, FD_PUBKEY_FOOTPRINT );
- fd_blake3_append( b3, pubkey, FD_PUBKEY_FOOTPRINT );
+ fd_blake3_append( b3, &lamports, sizeof( ulong ) );
+ fd_blake3_append( b3, data, data_len );
+ fd_blake3_append( b3, &executable_flag, sizeof( uchar ) );
+ fd_blake3_append( b3, owner, FD_HASH_FOOTPRINT );
+ fd_blake3_append( b3, pubkey, FD_HASH_FOOTPRINT );
fd_blake3_fini_2048( b3, lthash_out->bytes );
}
diff --git a/src/flamenco/runtime/fd_hashes.h b/src/flamenco/runtime/fd_hashes.h
index 2ff623cae18..55e6179f6d5 100644
--- a/src/flamenco/runtime/fd_hashes.h
+++ b/src/flamenco/runtime/fd_hashes.h
@@ -56,6 +56,29 @@ fd_hashes_account_lthash( fd_pubkey_t const * pubkey,
uchar const * data,
fd_lthash_value_t * lthash_out );
+/* fd_hashes_account_lthash_simple is functionally the same as
+ fd_hashes_account_lthash, but with simpler arguments that detail
+ the exact parameters that go into the lthash.
+
+ pubkey points to the account's public key (32 bytes). owner points
+ to the account's owner (32 bytes). lamports is the account's
+ lamports. executable is the account's executable flag. data points
+ to the account data. data_len is the length of the account data.
+ lthash_out points to where the computed lthash value will be written
+ (2048 bytes).
+
+ On return, lthash_out contains the computed lthash. This function
+ assumes all pointers are valid and properly aligned. The account
+ data pointer must be readable for data_len bytes. */
+void
+fd_hashes_account_lthash_simple( uchar const pubkey[ static FD_HASH_FOOTPRINT ],
+ uchar const owner[ static FD_HASH_FOOTPRINT ],
+ ulong lamports,
+ uchar executable,
+ uchar const * data,
+ ulong data_len,
+ fd_lthash_value_t * lthash_out );
+
/* fd_hashes_update_lthash updates the bank's incremental lthash when an
account is modified during transaction execution. The bank lthash is
maintained incrementally by subtracting the old account hash and
diff --git a/src/flamenco/runtime/tests/run_backtest_ci.sh b/src/flamenco/runtime/tests/run_backtest_ci.sh
index 11b74906e7c..64b9f7502c7 100755
--- a/src/flamenco/runtime/tests/run_backtest_ci.sh
+++ b/src/flamenco/runtime/tests/run_backtest_ci.sh
@@ -2,7 +2,7 @@
set -e
src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-308392063-v2.3.0 -y 5 -m 2000000 -e 308392090 -c 2.3.0
-src/flamenco/runtime/tests/run_ledger_backtest.sh -l devnet-350814254-v2.3.0 -y 3 -m 2000000 -e 350814284 -c 2.3.0
+src/flamenco/runtime/tests/run_ledger_backtest.sh -l devnet-350814254-v2.3.0 -y 3 -m 2000000 -e 350814284 -c 2.3.0 -lt
src/flamenco/runtime/tests/run_ledger_backtest.sh -l testnet-281546597-v2.3.0 -y 3 -m 2000000 -e 281546597 -c 2.3.0
src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-324823213-v2.3.0 -y 4 -m 2000000 -e 324823214 -c 2.3.0
src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-325467935-v2.3.0 -y 4 -m 2000000 -e 325467936 -c 2.3.0
diff --git a/src/flamenco/runtime/tests/run_backtest_tests_all.sh b/src/flamenco/runtime/tests/run_backtest_tests_all.sh
index a8518e5a10d..c28070304bc 100755
--- a/src/flamenco/runtime/tests/run_backtest_tests_all.sh
+++ b/src/flamenco/runtime/tests/run_backtest_tests_all.sh
@@ -1,8 +1,8 @@
#!/bin/bash
set -e
-src/flamenco/runtime/tests/run_ledger_backtest.sh -l testnet-519-v2.3.0 -y 3 -m 2000000 -e 255312007 -c 2.3.0
-src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-257066033-v2.3.0 -y 3 -m 2000000 -e 257066038 -c 2.3.0
+src/flamenco/runtime/tests/run_ledger_backtest.sh -l testnet-519-v2.3.0 -y 3 -m 2000000 -e 255312007 -c 2.3.0 -lt
+src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-257066033-v2.3.0 -y 3 -m 2000000 -e 257066038 -c 2.3.0 -lt
src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-257066844-v2.3.0 -y 3 -m 2000000 -e 257066849 -c 2.3.0
src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-257067457-v2.3.0 -y 3 -m 2000000 -e 257067461 -c 2.3.0
src/flamenco/runtime/tests/run_ledger_backtest.sh -l mainnet-257068890-v2.3.0 -y 3 -m 2000000 -e 257068895 -c 2.3.0
diff --git a/src/flamenco/runtime/tests/run_ledger_backtest.sh b/src/flamenco/runtime/tests/run_ledger_backtest.sh
index 6a46deab183..e97d5702a42 100755
--- a/src/flamenco/runtime/tests/run_ledger_backtest.sh
+++ b/src/flamenco/runtime/tests/run_ledger_backtest.sh
@@ -26,6 +26,7 @@ SKIP_CHECKSUM=1
DEBUG=( )
WATCH=( )
LOG_LEVEL_STDERR=NOTICE
+DISABLE_LTHASH_VERIFICATION=true
if [[ -n "$CI" ]]; then
SKIP_CHECKSUM=0
@@ -123,6 +124,10 @@ while [[ $# -gt 0 ]]; do
shift
shift
;;
+ -lt|--lthash-verification)
+ DISABLE_LTHASH_VERIFICATION=false
+ shift
+ ;;
-*|--*)
echo "unknown option $1"
exit 1
@@ -253,7 +258,10 @@ echo "
snapshots = \"$DUMP/$LEDGER\"
[hugetlbfs]
mount_path = \"$HUGE_TLBFS_MOUNT_PATH\"
- allow_hugepage_increase = $HUGE_TLBFS_ALLOW_HUGEPAGE_INCREASE" > $DUMP_DIR/${LEDGER}_backtest.toml
+ allow_hugepage_increase = $HUGE_TLBFS_ALLOW_HUGEPAGE_INCREASE
+[development]
+ [development.snapshots]
+ disable_lthash_verification = $DISABLE_LTHASH_VERIFICATION" > $DUMP_DIR/${LEDGER}_backtest.toml
if [[ -z "$GENESIS" ]]; then
echo "[gossip]