From 59364c1277762020c8c4c671de0e6d7aea3701ec Mon Sep 17 00:00:00 2001 From: Caroline Li Date: Tue, 4 Nov 2025 22:17:13 +0000 Subject: [PATCH] snapshots: lthash --- book/api/metrics-generated.md | 24 + src/app/firedancer-dev/commands/backtest.c | 61 ++- .../firedancer-dev/commands/snapshot_load.c | 106 +++- src/app/firedancer-dev/main.c | 4 + src/app/firedancer/config/default.toml | 18 + src/app/firedancer/main.c | 4 + src/app/firedancer/topology.c | 68 ++- src/app/shared/commands/watch/watch.c | 51 +- src/app/shared/fd_config.c | 1 + src/app/shared/fd_config.h | 5 + src/app/shared/fd_config_parse.c | 2 + src/disco/metrics/generate/types.py | 2 + src/disco/metrics/generated/fd_metrics_all.c | 6 + src/disco/metrics/generated/fd_metrics_all.h | 4 +- .../metrics/generated/fd_metrics_snapla.c | 8 + .../metrics/generated/fd_metrics_snapla.h | 30 ++ .../metrics/generated/fd_metrics_snapls.c | 8 + .../metrics/generated/fd_metrics_snapls.h | 30 ++ src/disco/metrics/metrics.xml | 12 + src/disco/stem/fd_stem.c | 2 + src/disco/topo/fd_topo.h | 1 + src/disco/topo/fd_topob.c | 2 + src/discof/restore/Local.mk | 2 + src/discof/restore/fd_snapct_tile.c | 22 +- src/discof/restore/fd_snapdc_tile.c | 1 + src/discof/restore/fd_snapin_tile.c | 58 ++- src/discof/restore/fd_snapin_tile_funk.c | 127 +++-- src/discof/restore/fd_snapin_tile_private.h | 114 ++++- src/discof/restore/fd_snapla_tile.c | 438 +++++++++++++++++ .../restore/fd_snapla_tile.seccomppolicy | 21 + src/discof/restore/fd_snapls_tile.c | 453 ++++++++++++++++++ .../restore/fd_snapls_tile.seccomppolicy | 21 + .../generated/fd_snapla_tile_seccomp.h | 68 +++ .../generated/fd_snapls_tile_seccomp.h | 68 +++ src/discof/restore/utils/fd_ssctrl.h | 53 ++ src/flamenco/runtime/fd_hashes.c | 31 +- src/flamenco/runtime/fd_hashes.h | 23 + src/flamenco/runtime/tests/run_backtest_ci.sh | 2 +- .../runtime/tests/run_backtest_tests_all.sh | 4 +- .../runtime/tests/run_ledger_backtest.sh | 10 +- 40 files changed, 1835 insertions(+), 130 deletions(-) create mode 100644 src/disco/metrics/generated/fd_metrics_snapla.c create mode 100644 src/disco/metrics/generated/fd_metrics_snapla.h create mode 100644 src/disco/metrics/generated/fd_metrics_snapls.c create mode 100644 src/disco/metrics/generated/fd_metrics_snapls.h create mode 100644 src/discof/restore/fd_snapla_tile.c create mode 100644 src/discof/restore/fd_snapla_tile.seccomppolicy create mode 100644 src/discof/restore/fd_snapls_tile.c create mode 100644 src/discof/restore/fd_snapls_tile.seccomppolicy create mode 100644 src/discof/restore/generated/fd_snapla_tile_seccomp.h create mode 100644 src/discof/restore/generated/fd_snapls_tile_seccomp.h 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]