From b52b1dc6cd52a639f56edc4b23d850a63ae4a4c6 Mon Sep 17 00:00:00 2001 From: Kunal Bhargava Date: Wed, 15 Oct 2025 14:50:49 +0000 Subject: [PATCH] backtest: refactor out of firedancer-dev --- src/app/backtest/Local.mk | 26 + src/app/backtest/backtest.c | 666 +++++++++++++++++++ src/app/backtest/ledgers.h | 1235 +++++++++++++++++++++++++++++++++++ src/app/backtest/main.c | 452 +++++++++++++ src/app/backtest/main.h | 12 + src/app/shared/fd_action.h | 10 +- 6 files changed, 2400 insertions(+), 1 deletion(-) create mode 100644 src/app/backtest/Local.mk create mode 100644 src/app/backtest/backtest.c create mode 100644 src/app/backtest/ledgers.h create mode 100644 src/app/backtest/main.c create mode 100644 src/app/backtest/main.h diff --git a/src/app/backtest/Local.mk b/src/app/backtest/Local.mk new file mode 100644 index 00000000000..f1e9f54dc3a --- /dev/null +++ b/src/app/backtest/Local.mk @@ -0,0 +1,26 @@ +ifdef FD_HAS_HOSTED +ifdef FD_HAS_LINUX +ifdef FD_HAS_ALLOCA +ifdef FD_HAS_DOUBLE +ifdef FD_HAS_INT128 +ifdef FD_HAS_ZSTD +ifdef FD_HAS_SECP256K1 + +.PHONY: backtest + +$(call add-objs,backtest,fd_backtest) + +$(call make-bin,backtest,main,fd_backtest fd_firedancer fddev_shared fdctl_shared fdctl_platform fd_discof fd_disco fd_choreo fd_flamenco fd_funk fd_quic fd_tls fd_reedsol fd_waltz fd_tango fd_ballet fd_util firedancer_version,$(SECP256K1_LIBS) $(ROCKSDB_LIBS) $(OPENSSL_LIBS)) + +backtest: $(OBJDIR)/bin/backtest + +else +$(warning backtest build disabled due to lack of zstd) +endif +endif +endif +endif +endif +endif +endif + diff --git a/src/app/backtest/backtest.c b/src/app/backtest/backtest.c new file mode 100644 index 00000000000..8f8502a7c43 --- /dev/null +++ b/src/app/backtest/backtest.c @@ -0,0 +1,666 @@ +/* The backtest command spawns a smaller topology for replaying shreds from + rocksdb (or other sources TBD) and reproduce the behavior of replay tile. + + The smaller topology is: + shred_out replay_exec + backtest-------------->replay------------->exec + ^ |^ | ^ | + |____________________|| | |________________| + replay_out | | exec_replay + | |------------------------------>no consumer + no producer------------- stake_out, send_out, poh_out + store_replay + +*/ +#define _GNU_SOURCE +#include +#include "../firedancer/topology.h" +#include "../shared/commands/configure/configure.h" +#include "../shared/commands/run/run.h" /* initialize_workspaces */ +#include "../shared/commands/watch/watch.h" +#include "../shared/fd_config.h" /* config_t */ +#include "../../disco/tiles.h" +#include "../../disco/topo/fd_topob.h" +#include "../../util/pod/fd_pod_format.h" +#include "../../discof/replay/fd_replay_tile.h" +#include "../../discof/restore/utils/fd_ssctrl.h" +#include "../../discof/restore/utils/fd_ssmsg.h" +#include "../../discof/tower/fd_tower_tile.h" +#include "../../discof/replay/fd_exec.h" +#include "../../ballet/lthash/fd_lthash.h" +#include "../../flamenco/runtime/context/fd_capture_ctx.h" +#include "../../disco/pack/fd_pack_cost.h" +#include "../../flamenco/progcache/fd_progcache_admin.h" + + +#include "main.h" +#include "ledgers.h" + +#include +#include +#include +#include + +extern fd_topo_obj_callbacks_t * CALLBACKS[]; +fd_topo_run_tile_t fdctl_tile_run( fd_topo_tile_t const * tile ); + +/* Global variable to store ledger name for access in topo function */ +static char g_ledger_name[64] = {0}; + +/* Forward declaration */ +static void apply_ledger_config( fd_ledger_config_t const * ledger_config, config_t * config ); + +/* Function to set the ledger name from main.c */ +void +backtest_set_ledger_name( char const * ledger_name ) { + strncpy( g_ledger_name, ledger_name, sizeof(g_ledger_name) - 1 ); + g_ledger_name[ sizeof(g_ledger_name) - 1 ] = '\0'; +} + +/* Global variable to store custom ledger config */ +static fd_ledger_config_t g_custom_ledger_config = {0}; +static int g_has_custom_config = 0; + +/* Function to set custom ledger configuration from main function */ +void +backtest_set_custom_config( fd_ledger_config_t * config ) { + if( config ) { + g_custom_ledger_config = *config; + g_has_custom_config = 1; + FD_LOG_NOTICE(( "Custom ledger configuration set from main function" )); + } +} + +/* Function to clear custom ledger configuration (for child processes) */ +void +backtest_clear_custom_config( void ) { + g_has_custom_config = 0; + memset( &g_custom_ledger_config, 0, sizeof(g_custom_ledger_config) ); +} + +/* Function to create custom ledger configuration from command-line arguments */ +void +create_custom_ledger_config( args_t * args ) { + if( args->backtest.ledger_name[0] == '\0' ) { + return; /* No ledger name provided */ + } + + /* Skip custom configuration creation if this is a child process (has --no-watch) */ + if( args->backtest.no_watch ) { + FD_LOG_NOTICE(( "Child process detected, clearing custom config" )); + /* Clear any inherited custom config from parent process */ + g_has_custom_config = 0; + memset( &g_custom_ledger_config, 0, sizeof(g_custom_ledger_config) ); + return; /* This is a child process, skip custom config creation */ + } + + /* Check if any custom configuration flags were provided */ + int has_custom_flags = 0; + if( args->backtest.cluster_version[0] != '\0' || + args->backtest.funk_pages != 0 || + args->backtest.index_max != 0 || + args->backtest.end_slot != 0 || + args->backtest.genesis || + args->backtest.has_incremental ) { + has_custom_flags = 1; + } + + if( !has_custom_flags ) { + return; + } + + /* Initialize custom config with defaults */ + memset( &g_custom_ledger_config, 0, sizeof(g_custom_ledger_config) ); + + /* Set the name */ + snprintf( g_custom_ledger_config.name, FD_LEDGER_NAME_MAX_LEN, "%s", args->backtest.ledger_name ); + + /* Set cluster version (default to "mainnet" if not provided) */ + if( args->backtest.cluster_version[0] != '\0' ) { + snprintf( g_custom_ledger_config.cluster_version, FD_LEDGER_CLUSTER_VERSION_MAX_LEN, "%s", args->backtest.cluster_version ); + } else { + snprintf( g_custom_ledger_config.cluster_version, FD_LEDGER_CLUSTER_VERSION_MAX_LEN, "mainnet" ); + } + + /* Set numeric values (use defaults if not provided) */ + g_custom_ledger_config.funk_pages = args->backtest.funk_pages ? args->backtest.funk_pages : 1UL; + g_custom_ledger_config.index_max = args->backtest.index_max ? args->backtest.index_max : 0UL; + g_custom_ledger_config.end_slot = args->backtest.end_slot ? args->backtest.end_slot : 0UL; + + /* Set boolean flags */ + g_custom_ledger_config.genesis = args->backtest.genesis; + g_custom_ledger_config.has_incremental = args->backtest.has_incremental; + + g_has_custom_config = 1; + FD_LOG_NOTICE(( "Created custom ledger configuration for: %s", g_custom_ledger_config.name )); +} + +/* Custom topo initialize function that applies ledger configuration before calling fd_topo_initialize */ +void +backtest_topo_initialize( config_t * config ) { + /* Apply ledger configuration if provided - this needs to happen before + fd_topo_initialize is called */ + if( g_ledger_name[0] != '\0' && strcmp( g_ledger_name, "backtest" ) != 0 ) { + fd_ledger_config_t const * ledger_config = fd_ledger_config_find( g_ledger_name ); + if( !ledger_config ) { + if( g_has_custom_config ) { + /* Check if the custom config name matches the current ledger name */ + /* If not, this might be a child process with inherited custom config */ + if( strcmp( g_custom_ledger_config.name, g_ledger_name ) != 0 ) { + FD_LOG_NOTICE(( "Custom config name mismatch (custom=%s, ledger=%s), clearing custom config", g_custom_ledger_config.name, g_ledger_name )); + g_has_custom_config = 0; + memset( &g_custom_ledger_config, 0, sizeof(g_custom_ledger_config) ); + /* Try to find predefined config again */ + ledger_config = fd_ledger_config_find( g_ledger_name ); + if( !ledger_config ) { + FD_LOG_ERR(( "Ledger configuration not found for: %s. Please provide a configuration flags.", g_ledger_name )); + return; + } + apply_ledger_config( ledger_config, config ); + } else { + FD_LOG_NOTICE(( "Using custom ledger configuration for: %s", g_ledger_name )); + apply_ledger_config( &g_custom_ledger_config, config ); + } + } else { + FD_LOG_ERR(( "Ledger configuration not found for: %s. Please provide a configuration flags.", g_ledger_name )); + return; + } + } else { + apply_ledger_config( ledger_config, config ); + } + } + + /* Call the original topo initialize function */ + fd_topo_initialize( config ); +} + +static void +backtest_topo( config_t * config ) { + + config->development.sandbox = 0; + config->development.no_clone = 1; + + ulong exec_tile_cnt = config->firedancer.layout.exec_tile_count; + + int disable_snap_loader = !config->gossip.entrypoints_cnt; + int solcap_enabled = strlen( config->capture.solcap_capture )>0; + + 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 ); + topo->gigantic_page_threshold = config->hugetlbfs.gigantic_page_threshold_mib << 20; + + ulong cpu_idx = 0; + + fd_topob_wksp( topo, "metric_in" ); + + /**********************************************************************/ + /* Add the backtest tile to topo */ + /**********************************************************************/ + fd_topob_wksp( topo, "backt" ); + fd_topo_tile_t * backt_tile = fd_topob_tile( topo, "backt", "backt", "metric_in", cpu_idx++, 0, 0 ); + + /**********************************************************************/ + /* Add the replay tile to topo */ + /**********************************************************************/ + fd_topob_wksp( topo, "replay" ); + fd_topo_tile_t * replay_tile = fd_topob_tile( topo, "replay", "replay", "metric_in", cpu_idx++, 0, 0 ); + + /* specified by [tiles.replay] */ + + fd_topob_wksp( topo, "funk" ); + fd_topo_obj_t * funk_obj = setup_topo_funk( topo, "funk", + config->firedancer.funk.max_account_records, + config->firedancer.funk.max_database_transactions, + config->firedancer.funk.heap_size_gib ); + fd_topob_tile_uses( topo, replay_tile, funk_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + + fd_topob_wksp( topo, "progcache" ); + fd_topo_obj_t * progcache_obj = setup_topo_progcache( topo, "progcache", + fd_progcache_est_rec_max( config->firedancer.runtime.program_cache.heap_size_mib<<20, + config->firedancer.runtime.program_cache.mean_cache_entry_size ), + config->firedancer.funk.max_database_transactions, + config->firedancer.runtime.program_cache.heap_size_mib<<20 ); + fd_topob_tile_uses( topo, replay_tile, progcache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + + /**********************************************************************/ + /* Add the executor tiles to topo */ + /**********************************************************************/ + fd_topob_wksp( topo, "exec" ); + #define FOR(cnt) for( ulong i=0UL; iallow_shutdown = 1; + snapld_tile->allow_shutdown = 1; + snapdc_tile->allow_shutdown = 1; + snapin_tile->allow_shutdown = 1; + } else { + fd_topob_wksp( topo, "genesi" ); + fd_topob_tile( topo, "genesi", "genesi", "metric_in", cpu_idx++, 0, 0 )->allow_shutdown = 1; + } + + /**********************************************************************/ + /* Setup backtest->replay link (shred_out) in topo */ + /**********************************************************************/ + + /* The repair tile is replaced by the backtest tile for the repair to + replay link. The frag interface is a "slice", ie. entry batch, + which is provided by the backtest tile, which reads in the entry + batches from the CLI-specified source (eg. RocksDB). */ + + fd_topob_wksp( topo, "shred_out" ); + fd_topob_link( topo, "shred_out", "shred_out", 65536UL, FD_SHRED_OUT_MTU, 1UL ); + fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "shred_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + fd_topob_tile_out( topo, "backt", 0UL, "shred_out", 0UL ); + + /**********************************************************************/ + /* Setup snapshot links in topo */ + /**********************************************************************/ + if( FD_LIKELY( !disable_snap_loader ) ) { + fd_topob_wksp( topo, "snapct_ld" ); + fd_topob_wksp( topo, "snapld_dc" ); + fd_topob_wksp( topo, "snapdc_in" ); + fd_topob_wksp( topo, "snapin_ct" ); + fd_topob_wksp( topo, "snapin_manif" ); + fd_topob_wksp( topo, "snapct_repr" ); + + 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 ); + 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 ); + 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 ); + fd_topob_tile_in ( topo, "snapld", 0UL, "metric_in", "snapct_ld", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + fd_topob_tile_out( topo, "snapld", 0UL, "snapld_dc", 0UL ); + 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, "replay", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + } 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 ); + fd_topob_tile_out( topo, "genesi", 0UL, "genesi_out", 0UL ); + fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + } + + /**********************************************************************/ + /* More backtest->replay links in topo */ + /**********************************************************************/ + + /* The tower tile is replaced by the backtest tile for the tower to + replay link. The backtest tile simply sends monotonically + increasing rooted slot numbers to the replay tile, once after each + "replayed a full slot" notification received from the replay tile. + This allows the replay tile to advance its watermark, and publish + various data structures. This is an oversimplified barebones mock + of the tower tile. */ + fd_topob_wksp( topo, "tower_out" ); + fd_topob_link( topo, "tower_out", "tower_out", 1024UL, sizeof(fd_tower_slot_done_t), 1UL ); + fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "tower_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + fd_topob_tile_out( topo, "backt", 0UL, "tower_out", 0UL ); + + /**********************************************************************/ + /* Setup replay->stake/send/poh links in topo w/o consumers */ + /**********************************************************************/ + fd_topob_wksp( topo, "replay_stake" ); + fd_topob_wksp( topo, "replay_poh" ); + + fd_topob_link( topo, "replay_stake", "replay_stake", 128UL, 40UL + 40200UL * 40UL, 1UL ); + ulong bank_tile_cnt = config->layout.bank_tile_count; + FOR(bank_tile_cnt) fd_topob_link( topo, "replay_poh", "replay_poh", 128UL, (4096UL*sizeof(fd_txn_p_t))+sizeof(fd_microblock_trailer_t), 1UL ); + + fd_topob_tile_out( topo, "replay", 0UL, "replay_stake", 0UL ); + FOR(bank_tile_cnt) fd_topob_tile_out( topo, "replay", 0UL, "replay_poh", i ); + + topo->links[ replay_tile->out_link_id[ fd_topo_find_tile_out_link( topo, replay_tile, "replay_stake", 0 ) ] ].permit_no_consumers = 1; + FOR(bank_tile_cnt) topo->links[ replay_tile->out_link_id[ fd_topo_find_tile_out_link( topo, replay_tile, "replay_poh", i ) ] ].permit_no_consumers = 1; + + /**********************************************************************/ + /* Setup replay->backtest link (replay_notif) in topo */ + /**********************************************************************/ + + fd_topob_wksp( topo, "replay_out" ); + fd_topob_link( topo, "replay_out", "replay_out", 8192UL, sizeof( fd_replay_message_t ), 1UL ); + fd_topob_tile_out( topo, "replay", 0UL, "replay_out", 0UL ); + fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + if( FD_LIKELY( !disable_snap_loader ) ) { + fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + } else { + fd_topob_tile_in ( topo, "backt", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + } + + /**********************************************************************/ + /* Setup replay->exec link in topo */ + /**********************************************************************/ + fd_topob_wksp( topo, "replay_exec" ); + fd_topob_link( topo, "replay_exec", "replay_exec", 16384UL, 2240UL, 1UL ); + fd_topob_tile_out( topo, "replay", 0UL, "replay_exec", 0UL ); + for( ulong i=0UL; ireplay links in topo, to send solcap account updates + so that they are serialized, and to notify replay tile that a txn + has been finalized by the exec tile. */ + /**********************************************************************/ + fd_topob_wksp( topo, "exec_replay" ); + + /* If solcap is enabled, we need to overload this link to also send + solcap account updates to the replay tile. We can't use a separate + link for this without introducing a race. This will get removed with solcap V2. */ + if( FD_UNLIKELY( solcap_enabled ) ) { + /* TODO: remove this with solcap V2 */ + FOR(exec_tile_cnt) fd_topob_link( topo, "exec_replay", "exec_replay", 1024UL, FD_CAPTURE_CTX_ACCOUNT_UPDATE_MSG_FOOTPRINT, 1UL ); + } else { + FOR(exec_tile_cnt) fd_topob_link( topo, "exec_replay", "exec_replay", 16384UL, sizeof(fd_exec_task_done_msg_t), 1UL ); + } + + FOR(exec_tile_cnt) fd_topob_tile_out( topo, "exec", i, "exec_replay", i ); + FOR(exec_tile_cnt) fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "exec_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + + /**********************************************************************/ + /* Setup the shared objs used by replay and exec tiles */ + /**********************************************************************/ + + fd_topob_wksp( topo, "store" ); + fd_topo_obj_t * store_obj = setup_topo_store( topo, "store", config->firedancer.store.max_completed_shred_sets, 1 ); + fd_topob_tile_uses( topo, backt_tile, store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + fd_topob_tile_uses( topo, replay_tile, store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + FD_TEST( fd_pod_insertf_ulong( topo->props, store_obj->id, "store" ) ); + + /* banks_obj shared by replay and exec tiles */ + fd_topob_wksp( topo, "banks" ); + fd_topo_obj_t * banks_obj = setup_topo_banks( topo, "banks", config->firedancer.runtime.max_live_slots, config->firedancer.runtime.max_fork_width ); + fd_topob_tile_uses( topo, replay_tile, banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + FOR(exec_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "exec", i ) ], banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + FD_TEST( fd_pod_insertf_ulong( topo->props, banks_obj->id, "banks" ) ); + + /* bank_hash_cmp_obj shared by replay and exec tiles */ + fd_topob_wksp( topo, "bh_cmp" ); + fd_topo_obj_t * bank_hash_cmp_obj = setup_topo_bank_hash_cmp( topo, "bh_cmp" ); + fd_topob_tile_uses( topo, replay_tile, bank_hash_cmp_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + FOR(exec_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "exec", i ) ], bank_hash_cmp_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + FD_TEST( fd_pod_insertf_ulong( topo->props, bank_hash_cmp_obj->id, "bh_cmp" ) ); + + /* txncache_obj, busy_obj and poh_slot_obj only by replay tile */ + fd_topob_wksp( topo, "txncache" ); + fd_topob_wksp( topo, "bank_busy" ); + fd_topo_obj_t * txncache_obj = setup_topo_txncache( topo, "txncache", + config->firedancer.runtime.max_live_slots, + fd_ulong_pow2_up( FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT ) ); + fd_topob_tile_uses( topo, replay_tile, txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + if( FD_LIKELY( !disable_snap_loader ) ) { + fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", 0UL ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + } + for( ulong i=0UL; itiles[ fd_topo_find_tile( topo, "exec", i ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + } + for( ulong i=0UL; itiles[ fd_topo_find_tile( topo, "bank", i ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + } + + FD_TEST( fd_pod_insertf_ulong( topo->props, txncache_obj->id, "txncache" ) ); + for( ulong i=0UL; iprops, busy_obj->id, "bank_busy.%lu", i ) ); + } + + if( FD_LIKELY( !disable_snap_loader ) ) { + fd_topob_tile_uses( topo, snapin_tile, funk_obj, FD_SHMEM_JOIN_MODE_READ_WRITE ); + } + + for( ulong i=0UL; itile_cnt; i++ ) { + fd_topo_tile_t * tile = &topo->tiles[ i ]; + fd_topo_configure_tile( tile, config ); + + if( !strcmp( tile->name, "replay" ) ) { + tile->replay.enable_bank_hash_cmp = 0; + tile->replay.enable_features_cnt = config->tiles.replay.enable_features_cnt; + for( ulong i = 0; i < tile->replay.enable_features_cnt; i++ ) { + strncpy( tile->replay.enable_features[i], config->tiles.replay.enable_features[i], sizeof(tile->replay.enable_features[i]) ); + } + } + } + + // fd_topob_auto_layout( topo, 0 ); + fd_topob_finish( topo, CALLBACKS ); +} + +extern int * fd_log_private_shared_lock; + +static void +backtest_cmd_topo( config_t * config ) { + backtest_topo( config ); +} + +static args_t +configure_args( void ) { + args_t args = { + .configure.command = CONFIGURE_CMD_INIT, + }; + + ulong stage_idx = 0UL; + args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_hugetlbfs; + args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_snapshots; + args.configure.stages[ stage_idx++ ] = NULL; + + return args; +} + +void +backtest_cmd_args( int * pargc, + char *** pargv, + args_t * args ) { + + args->backtest.no_watch = fd_env_strip_cmdline_contains( pargc, pargv, "--no-watch" ); + args->backtest.ci_mode = fd_env_strip_cmdline_contains( pargc, pargv, "--ci" ); + + if( *pargc > 0 && strncmp( **pargv, "--", 2 ) ) { + strncpy( args->backtest.ledger_name, **pargv, sizeof(args->backtest.ledger_name) - 1 ); + args->backtest.ledger_name[ sizeof(args->backtest.ledger_name) - 1 ] = '\0'; + } else { + args->backtest.ledger_name[0] = '\0'; + } + + *pargc = 0; +} + +void +backtest_cmd_perm( args_t * args FD_PARAM_UNUSED, + fd_cap_chk_t * chk, + config_t const * config ) { + args_t c_args = configure_args(); + configure_cmd_perm( &c_args, chk, config ); + run_cmd_perm( NULL, chk, config ); +} + +static void +apply_ledger_config( fd_ledger_config_t const * ledger_config, config_t * config ) { + if( !ledger_config ) return; + + + char * dump_dir = getenv("DUMP_DIR"); + if( dump_dir == NULL ) { + dump_dir = "dump"; + } + + // create var for ledger_path and set to dump_dir/ledger_config->name + char ledger_path[1024]; + strncpy( ledger_path, dump_dir, sizeof(ledger_path) - 1 ); + ledger_path[ sizeof(ledger_path) - 1 ] = '\0'; + strncat( ledger_path, "/", sizeof(ledger_path) - strlen(ledger_path) - 1 ); + ledger_path[ sizeof(ledger_path) - 1 ] = '\0'; + strncat( ledger_path, ledger_config->name, sizeof(ledger_path) - strlen(ledger_path) - 1 ); + ledger_path[ sizeof(ledger_path) - 1 ] = '\0'; + + if( access( ledger_path, F_OK ) != 0 ) { + FD_LOG_NOTICE(( "Ledger directory does not exist: %s, attempting to download...", ledger_path )); + + char mkdir_cmd[2048]; + snprintf( mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p %s", ledger_path ); + if( system( mkdir_cmd ) != 0 ) { + FD_LOG_ERR(( "Failed to create ledger directory: %s", ledger_path )); + return; + } + + char download_cmd[4096]; + snprintf( download_cmd, sizeof(download_cmd), + "gcloud storage cat gs://firedancer-ci-resources/%s.tar.gz | tar zxf - -C %s", + ledger_config->name, dump_dir ); + + FD_LOG_NOTICE(( "Downloading ledger from gcloud: %s", ledger_config->name )); + int download_result = system( download_cmd ); + + if( download_result != 0 ) { + FD_LOG_ERR(( "Failed to download ledger: %s", ledger_config->name )); + return; + } + + if( access( ledger_path, F_OK ) != 0 ) { + FD_LOG_ERR(( "Ledger directory still does not exist after download: %s", ledger_path )); + return; + } + + FD_LOG_NOTICE(( "Successfully downloaded and extracted ledger: %s", ledger_path )); + } + + if( chmod( ledger_path, 0700 ) != 0 ) { + FD_LOG_ERR(( "Failed to change permissions of ledger directory: %s", ledger_path )); + return; + } + + /* Set cluster version */ + strncpy( config->tiles.replay.cluster_version, ledger_config->cluster_version, + sizeof(config->tiles.replay.cluster_version) - 1 ); + config->tiles.replay.cluster_version[ sizeof(config->tiles.replay.cluster_version) - 1 ] = '\0'; + + /* Set funk configuration */ + config->firedancer.funk.heap_size_gib = ledger_config->funk_pages; + config->firedancer.funk.max_account_records = ledger_config->index_max; + + /* Set archiver configuration */ + config->tiles.archiver.enabled = 1; + config->tiles.archiver.end_slot = ledger_config->end_slot; + + strncpy( config->tiles.archiver.ingest_mode, "rocksdb", sizeof(config->tiles.archiver.ingest_mode) - 1 ); + config->tiles.archiver.ingest_mode[ sizeof(config->tiles.archiver.ingest_mode) - 1 ] = '\0'; + + strncpy( config->tiles.archiver.rocksdb_path, dump_dir, sizeof(config->tiles.archiver.rocksdb_path) - 1 ); + config->tiles.archiver.rocksdb_path[ sizeof(config->tiles.archiver.rocksdb_path) - 1 ] = '\0'; + strncat( config->tiles.archiver.rocksdb_path, "/", sizeof(config->tiles.archiver.rocksdb_path) - strlen(config->tiles.archiver.rocksdb_path) - 1 ); + config->tiles.archiver.rocksdb_path[ sizeof(config->tiles.archiver.rocksdb_path) - 1 ] = '\0'; + strncat( config->tiles.archiver.rocksdb_path, ledger_config->name, sizeof(config->tiles.archiver.rocksdb_path) - strlen(config->tiles.archiver.rocksdb_path) - 1 ); + config->tiles.archiver.rocksdb_path[ sizeof(config->tiles.archiver.rocksdb_path) - 1 ] = '\0'; + strncat( config->tiles.archiver.rocksdb_path, "/rocksdb", sizeof(config->tiles.archiver.rocksdb_path) - strlen(config->tiles.archiver.rocksdb_path) - 1 ); + config->tiles.archiver.rocksdb_path[ sizeof(config->tiles.archiver.rocksdb_path) - 1 ] = '\0'; + + // set paths to dump/ledger_config->name + strncpy( config->paths.snapshots, dump_dir, sizeof(config->paths.snapshots) - 1 ); + config->paths.snapshots[ sizeof(config->paths.snapshots) - 1 ] = '\0'; + strncat( config->paths.snapshots, "/", sizeof(config->paths.snapshots) - strlen(config->paths.snapshots) - 1 ); + config->paths.snapshots[ sizeof(config->paths.snapshots) - 1 ] = '\0'; + strncat( config->paths.snapshots, ledger_config->name, sizeof(config->paths.snapshots) - strlen(config->paths.snapshots) - 1 ); + config->paths.snapshots[ sizeof(config->paths.snapshots) - 1 ] = '\0'; + + /* Set snapshot configuration */ + config->firedancer.snapshots.incremental_snapshots = ledger_config->has_incremental; + config->firedancer.snapshots.sources.servers_cnt = 0; + config->firedancer.snapshots.sources.gossip.allow_any = false; + config->firedancer.snapshots.sources.gossip.allow_list_cnt = 0; + + /* Set gossip configuration based on genesis flag */ + if( ledger_config->genesis ) { + config->gossip.entrypoints_cnt = 0; /* No entrypoints for genesis mode */ + } else { + config->gossip.entrypoints_cnt = 1; + strncpy( config->gossip.entrypoints[0], "0.0.0.0:1", sizeof(config->gossip.entrypoints[0]) - 1 ); + config->gossip.entrypoints[0][ sizeof(config->gossip.entrypoints[0]) - 1 ] = '\0'; + } + + /* Set replay features if any */ + config->tiles.replay.enable_features_cnt = (uint)ledger_config->features_cnt; + for( ulong i = 0; i < ledger_config->features_cnt && i < FD_LEDGER_MAX_FEATURES; i++ ) { + strncpy( config->tiles.replay.enable_features[i], ledger_config->features[i], + sizeof(config->tiles.replay.enable_features[i]) - 1 ); + config->tiles.replay.enable_features[i][ sizeof(config->tiles.replay.enable_features[i]) - 1 ] = '\0'; + } + if( ledger_config->genesis ) { + strncpy( config->paths.genesis, dump_dir, sizeof(config->paths.genesis) - 1 ); + config->paths.genesis[ sizeof(config->paths.genesis) - 1 ] = '\0'; + strncat( config->paths.genesis, "/", sizeof(config->paths.genesis) - strlen(config->paths.genesis) - 1 ); + config->paths.genesis[ sizeof(config->paths.genesis) - 1 ] = '\0'; + strncat( config->paths.genesis, ledger_config->name, sizeof(config->paths.genesis) - strlen(config->paths.genesis) - 1 ); + config->paths.genesis[ sizeof(config->paths.genesis) - 1 ] = '\0'; + strncat( config->paths.genesis, "/", sizeof(config->paths.genesis) - strlen(config->paths.genesis) - 1 ); + config->paths.genesis[ sizeof(config->paths.genesis) - 1 ] = '\0'; + strncat( config->paths.genesis, "genesis.bin", sizeof(config->paths.genesis) - strlen(config->paths.genesis) - 1 ); + config->paths.genesis[ sizeof(config->paths.genesis) - 1 ] = '\0'; + + } +} + +static void +backtest_cmd_fn( args_t * args, + config_t * config ) { + args_t c_args = configure_args(); + configure_cmd_fn( &c_args, config ); + + /* Create custom ledger configuration if flags are provided */ + create_custom_ledger_config( args ); + + /* Store ledger name in global variable for access in topo function */ + strncpy( g_ledger_name, args->backtest.ledger_name, sizeof(g_ledger_name) - 1 ); + g_ledger_name[ sizeof(g_ledger_name) - 1 ] = '\0'; + + initialize_workspaces( config ); + initialize_stacks( config ); + + fd_log_private_shared_lock[ 1 ] = 0; + fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE ); + fd_topo_fill( &config->topo ); + + args_t watch_args; + int pipefd[2]; + if( !args->backtest.no_watch ) { + if( FD_UNLIKELY( pipe2( pipefd, O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + + watch_args.watch.drain_output_fd = pipefd[0]; + if( FD_UNLIKELY( -1==dup2( pipefd[ 1 ], STDERR_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) )); + } + + fd_topo_run_single_process( &config->topo, 2, config->uid, config->gid, fdctl_tile_run ); + if( args->backtest.no_watch ) { + for(;;) pause(); + } else { + watch_cmd_fn( &watch_args, config ); + } +} + +action_t fd_action_backtest = { + .name = "backtest", + .args = backtest_cmd_args, + .fn = backtest_cmd_fn, + .perm = backtest_cmd_perm, + .topo = backtest_cmd_topo, +}; diff --git a/src/app/backtest/ledgers.h b/src/app/backtest/ledgers.h new file mode 100644 index 00000000000..45a43a5f5da --- /dev/null +++ b/src/app/backtest/ledgers.h @@ -0,0 +1,1235 @@ +#ifndef HEADER_fd_src_app_backtest_ledgers_h +#define HEADER_fd_src_app_backtest_ledgers_h + +#include "../../util/fd_util_base.h" + +/* Maximum number of one-off features per ledger */ +#define FD_LEDGER_MAX_FEATURES (32UL) + +/* Maximum length of ledger name */ +#define FD_LEDGER_NAME_MAX_LEN (64UL) + +/* Maximum length of cluster version string */ +#define FD_LEDGER_CLUSTER_VERSION_MAX_LEN (16UL) + +/* Ledger test configuration structure */ +typedef struct fd_ledger_config { + char name[ FD_LEDGER_NAME_MAX_LEN ]; /* Ledger name (e.g., "mainnet-257066033-v2.3.0") */ + char cluster_version[ FD_LEDGER_CLUSTER_VERSION_MAX_LEN ]; /* Cluster version (e.g., "2.3.0") */ + + ulong funk_pages; /* Funk heap size in pages */ + ulong index_max; /* Maximum account records */ + ulong end_slot; /* End slot for replay */ + + int genesis; /* Whether to use genesis mode (1) or entrypoints (0) */ + int has_incremental; /* Whether incremental snapshots are enabled */ + + /* One-off features as a null-terminated array of strings */ + char features[ FD_LEDGER_MAX_FEATURES ][ 64UL ]; + ulong features_cnt; /* Number of features */ + +} fd_ledger_config_t; + +/* Individual ledger configurations */ +static const fd_ledger_config_t FD_LEDGER_TESTNET_519_V2_3_0 = { + .name = "testnet-519-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 255312007UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257066033_V2_3_0 = { + .name = "mainnet-257066033-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257066038UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257066844_V2_3_0 = { + .name = "mainnet-257066844-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257066849UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257067457_V2_3_0 = { + .name = "mainnet-257067457-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257067461UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257068890_V2_3_0 = { + .name = "mainnet-257068890-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257068895UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257181622_V2_3_0 = { + .name = "mainnet-257181622-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257181624UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_254462437_V2_3_0 = { + .name = "mainnet-254462437-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 16UL, + .index_max = 10000000UL, + .end_slot = 254462598UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_LOCAL_CLUSTER_V2_3_0 = { + .name = "local-cluster-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 1UL, + .index_max = 2000000UL, + .end_slot = 5010UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_262654839_V2_3_0 = { + .name = "mainnet-262654839-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 10000000UL, + .end_slot = 262654840UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257037451_V2_3_0 = { + .name = "mainnet-257037451-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257037454UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257035225_V2_3_0 = { + .name = "mainnet-257035225-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 4UL, + .index_max = 2000000UL, + .end_slot = 257035233UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257465453_V2_3_0 = { + .name = "mainnet-257465453-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 4UL, + .index_max = 10000000UL, + .end_slot = 257465454UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257058865_V2_3_0 = { + .name = "mainnet-257058865-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257058870UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257059815_V2_3_0 = { + .name = "mainnet-257059815-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257059818UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257061172_V2_3_0 = { + .name = "mainnet-257061172-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257061175UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257222682_V2_3_0 = { + .name = "mainnet-257222682-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257222688UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_264890264_V2_3_0 = { + .name = "mainnet-264890264-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 264890265UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257229353_V2_3_0 = { + .name = "mainnet-257229353-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 4UL, + .index_max = 2000000UL, + .end_slot = 257229357UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257257983_V2_3_0 = { + .name = "mainnet-257257983-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257257986UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_267728520_V2_3_0 = { + .name = "mainnet-267728520-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 267728522UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_267651942_V2_3_0 = { + .name = "mainnet-267651942-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 267651943UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_267081197_V2_3_0 = { + .name = "mainnet-267081197-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 267081198UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_267085604_V2_3_0 = { + .name = "mainnet-267085604-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 267085605UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_265688706_V2_3_0 = { + .name = "mainnet-265688706-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 265688707UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_265330432_V2_3_0 = { + .name = "mainnet-265330432-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 265330433UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_268575190_V2_3_0 = { + .name = "mainnet-268575190-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 268575191UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_268129380_V2_3_0 = { + .name = "mainnet-268129380-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 268129380UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_268163043_V2_3_0 = { + .name = "mainnet-268163043-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 268163043UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_269511381_V2_3_0 = { + .name = "mainnet-269511381-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 269511381UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_269567236_V2_3_0 = { + .name = "mainnet-269567236-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 269567236UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_266134813_V2_3_0 = { + .name = "mainnet-266134813-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 266134814UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_266545736_V2_3_0 = { + .name = "mainnet-266545736-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 266545737UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_267059180_V2_3_0 = { + .name = "mainnet-267059180-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 267059181UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_267580466_V2_3_0 = { + .name = "mainnet-267580466-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 267580467UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_268196194_V2_3_0 = { + .name = "mainnet-268196194-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 268196195UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_267766641_V2_3_0 = { + .name = "mainnet-267766641-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 267766642UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_269648145_V2_3_0 = { + .name = "mainnet-269648145-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 269648146UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_281688085_V2_3_0 = { + .name = "testnet-281688085-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 281688086UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_277660422_V2_3_0 = { + .name = "mainnet-277660422-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 277660423UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_277876060_V2_3_0 = { + .name = "mainnet-277876060-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 277876061UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_277927063_V2_3_0 = { + .name = "mainnet-277927063-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 277927065UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_281375356_V2_3_0 = { + .name = "mainnet-281375356-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 281375359UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_251418170_V2_3_0 = { + .name = "mainnet-251418170-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 251418233UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_282232100_V2_3_0 = { + .name = "mainnet-282232100-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 282232101UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_282151715_V2_3_0 = { + .name = "mainnet-282151715-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 282151717UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_286450148_V2_3_0 = { + .name = "mainnet-286450148-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 286450151UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MULTI_EPOCH_PER_200_V2_3_0 = { + .name = "multi-epoch-per-200-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 1UL, + .index_max = 2000000UL, + .end_slot = 984UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MULTI_EPOCH_PER_300_V2_3_0 = { + .name = "multi-epoch-per-300-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 1UL, + .index_max = 2000000UL, + .end_slot = 984UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MULTI_EPOCH_PER_500_V2_3_0 = { + .name = "multi-epoch-per-500-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 1UL, + .index_max = 2000000UL, + .end_slot = 984UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_297489336_V2_3_0 = { + .name = "testnet-297489336-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 297489363UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_300377724_V2_3_0 = { + .name = "mainnet-300377724-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 300377728UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_300645644_V2_3_0 = { + .name = "mainnet-300645644-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 300645644UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_300648964_V2_3_0 = { + .name = "mainnet-300648964-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 300648964UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_301359740_V2_3_0 = { + .name = "mainnet-301359740-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 301359740UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257181032_V2_3_0 = { + .name = "mainnet-257181032-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257181035UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257047660_V2_3_0 = { + .name = "mainnet-257047660-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257047662UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_257047659_V2_3_0 = { + .name = "mainnet-257047659-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 257047660UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_308445707_V2_3_0 = { + .name = "mainnet-308445707-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 308445711UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_307395181_V2_3_0 = { + .name = "testnet-307395181-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 307395190UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_308392063_V2_3_0 = { + .name = "mainnet-308392063-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 308392090UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_DEVNET_350814254_V2_3_0 = { + .name = "devnet-350814254-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 350814284UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_311586340_V2_3_0 = { + .name = "testnet-311586340-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 311586380UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_281546597_V2_3_0 = { + .name = "testnet-281546597-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 281546597UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_324823213_V2_3_0 = { + .name = "mainnet-324823213-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 4UL, + .index_max = 2000000UL, + .end_slot = 324823214UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_325467935_V2_3_0 = { + .name = "mainnet-325467935-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 4UL, + .index_max = 2000000UL, + .end_slot = 325467936UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_283927487_V2_3_0 = { + .name = "testnet-283927487-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 283927497UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_321168308_V2_3_0 = { + .name = "testnet-321168308-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 321168308UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_327324660_V2_3_0 = { + .name = "mainnet-327324660-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 4UL, + .index_max = 2000000UL, + .end_slot = 327324660UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_DEVNET_370199634_V2_3_0 = { + .name = "devnet-370199634-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 200000UL, + .end_slot = 370199634UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_330219081_V2_3_0 = { + .name = "mainnet-330219081-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 4UL, + .index_max = 2000000UL, + .end_slot = 330219082UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_DEVNET_372721907_V2_3_0 = { + .name = "devnet-372721907-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 372721910UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_331691646_V2_3_0 = { + .name = "mainnet-331691646-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 4UL, + .index_max = 2000000UL, + .end_slot = 331691647UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_DEVNET_378683870_V2_3_0 = { + .name = "devnet-378683870-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 378683872UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_DEVNET_380592002_V2_3_0 = { + .name = "devnet-380592002-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 380592006UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_336218682_V2_3_0 = { + .name = "testnet-336218682-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 336218683UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_340269866_V2_3_0 = { + .name = "testnet-340269866-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 340269872UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +// TODO: Add this ledger +static const fd_ledger_config_t FD_LEDGER_TESTNET_340272018_V2_3_0 = { + .name = "testnet-340272018-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 340272024UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_DEVNET_390056400_V2_3_0 = { + .name = "devnet-390056400-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 10UL, + .index_max = 2000000UL, + .end_slot = 390056406UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_346556000 = { + .name = "testnet-346556000", + .cluster_version = "2.3.0", + .funk_pages = 3UL, + .index_max = 2000000UL, + .end_slot = 346556337UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_346179946 = { + .name = "testnet-346179946", + .cluster_version = "2.3.0", + .funk_pages = 30UL, + .index_max = 90000000UL, + .end_slot = 346179950UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MULTI_BPF_LOADER_V2_3_0 = { + .name = "multi-bpf-loader-v2.3.0", + .cluster_version = "2.3.0", + .funk_pages = 1UL, + .index_max = 1000UL, + .end_slot = 108UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_LOCAL_MULTI_BOUNDARY = { + .name = "local-multi-boundary", + .cluster_version = "2.3.0", + .funk_pages = 1UL, + .index_max = 1000UL, + .end_slot = 2325UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_GENESIS_V3_0 = { + .name = "genesis-v3.0", + .cluster_version = "3.0.0", + .funk_pages = 1UL, + .index_max = 3000UL, + .end_slot = 1280UL, + .genesis = 1, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_LOCALNET_STAKE_V3_0_0 = { + .name = "localnet-stake-v3.0.0", + .cluster_version = "3.0.0", + .funk_pages = 1UL, + .index_max = 3000UL, + .end_slot = 541UL, + .genesis = 0, + .has_incremental = 0, + .features = { "" }, + .features_cnt = 0UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_368528500_STRICTER_ABI = { + .name = "mainnet-368528500-stricter-abi", + .cluster_version = "3.0.3", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 368528527UL, + .genesis = 0, + .has_incremental = 0, + .features = { "CxeBn9PVeeXbmjbNwLv6U4C6svNxnC4JX6mfkvgeMocM" }, + .features_cnt = 1UL, +}; + +static const fd_ledger_config_t FD_LEDGER_MAINNET_368528500_DIRECT_MAPPING = { + .name = "mainnet-368528500-direct-mapping", + .cluster_version = "3.0.3", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 368528527UL, + .genesis = 0, + .has_incremental = 0, + .features = { "CxeBn9PVeeXbmjbNwLv6U4C6svNxnC4JX6mfkvgeMocM", "9s3RKimHWS44rJcJ9P1rwCmn2TvMqtZQBmz815ZUUHqJ" }, + .features_cnt = 2UL, +}; + +static const fd_ledger_config_t FD_LEDGER_TESTNET_362107883_DIRECT_MAPPING_2 = { + .name = "testnet-362107883-direct-mapping-2", + .cluster_version = "3.0.3", + .funk_pages = 5UL, + .index_max = 2000000UL, + .end_slot = 362219427UL, + .genesis = 0, + .has_incremental = 0, + .features = { "CxeBn9PVeeXbmjbNwLv6U4C6svNxnC4JX6mfkvgeMocM", "9s3RKimHWS44rJcJ9P1rwCmn2TvMqtZQBmz815ZUUHqJ" }, + .features_cnt = 2UL, +}; + +/* Array of all ledger configurations for CI testing */ +#define ALL_LEDGERS \ + &FD_LEDGER_TESTNET_519_V2_3_0, \ + &FD_LEDGER_MAINNET_257066033_V2_3_0, \ + &FD_LEDGER_MAINNET_257066844_V2_3_0, \ + &FD_LEDGER_MAINNET_257067457_V2_3_0, \ + &FD_LEDGER_MAINNET_257068890_V2_3_0, \ + &FD_LEDGER_MAINNET_257181622_V2_3_0, \ + &FD_LEDGER_MAINNET_254462437_V2_3_0, \ + &FD_LEDGER_LOCAL_CLUSTER_V2_3_0, \ + &FD_LEDGER_MAINNET_262654839_V2_3_0, \ + &FD_LEDGER_MAINNET_257037451_V2_3_0, \ + &FD_LEDGER_MAINNET_257035225_V2_3_0, \ + &FD_LEDGER_MAINNET_257465453_V2_3_0, \ + &FD_LEDGER_MAINNET_257058865_V2_3_0, \ + &FD_LEDGER_MAINNET_257059815_V2_3_0, \ + &FD_LEDGER_MAINNET_257061172_V2_3_0, \ + &FD_LEDGER_MAINNET_257222682_V2_3_0, \ + &FD_LEDGER_MAINNET_264890264_V2_3_0, \ + &FD_LEDGER_MAINNET_257229353_V2_3_0, \ + &FD_LEDGER_MAINNET_257257983_V2_3_0, \ + &FD_LEDGER_MAINNET_267728520_V2_3_0, \ + &FD_LEDGER_MAINNET_267651942_V2_3_0, \ + &FD_LEDGER_MAINNET_267081197_V2_3_0, \ + &FD_LEDGER_MAINNET_267085604_V2_3_0, \ + &FD_LEDGER_MAINNET_265688706_V2_3_0, \ + &FD_LEDGER_MAINNET_265330432_V2_3_0, \ + &FD_LEDGER_MAINNET_268575190_V2_3_0, \ + &FD_LEDGER_MAINNET_268129380_V2_3_0, \ + &FD_LEDGER_MAINNET_268163043_V2_3_0, \ + &FD_LEDGER_MAINNET_269511381_V2_3_0, \ + &FD_LEDGER_MAINNET_269567236_V2_3_0, \ + &FD_LEDGER_MAINNET_266134813_V2_3_0, \ + &FD_LEDGER_MAINNET_266545736_V2_3_0, \ + &FD_LEDGER_MAINNET_267059180_V2_3_0, \ + &FD_LEDGER_MAINNET_267580466_V2_3_0, \ + &FD_LEDGER_MAINNET_268196194_V2_3_0, \ + &FD_LEDGER_MAINNET_267766641_V2_3_0, \ + &FD_LEDGER_MAINNET_269648145_V2_3_0, \ + &FD_LEDGER_TESTNET_281688085_V2_3_0, \ + &FD_LEDGER_MAINNET_277660422_V2_3_0, \ + &FD_LEDGER_MAINNET_277876060_V2_3_0, \ + &FD_LEDGER_MAINNET_277927063_V2_3_0, \ + &FD_LEDGER_MAINNET_281375356_V2_3_0, \ + &FD_LEDGER_MAINNET_251418170_V2_3_0, \ + &FD_LEDGER_MAINNET_282232100_V2_3_0, \ + &FD_LEDGER_MAINNET_282151715_V2_3_0, \ + &FD_LEDGER_MAINNET_286450148_V2_3_0, \ + &FD_LEDGER_MULTI_EPOCH_PER_200_V2_3_0, \ + &FD_LEDGER_MULTI_EPOCH_PER_300_V2_3_0, \ + &FD_LEDGER_MULTI_EPOCH_PER_500_V2_3_0, \ + &FD_LEDGER_TESTNET_297489336_V2_3_0, \ + &FD_LEDGER_MAINNET_300377724_V2_3_0, \ + &FD_LEDGER_MAINNET_300645644_V2_3_0, \ + &FD_LEDGER_MAINNET_300648964_V2_3_0, \ + &FD_LEDGER_MAINNET_301359740_V2_3_0, \ + &FD_LEDGER_MAINNET_257181032_V2_3_0, \ + &FD_LEDGER_MAINNET_257047660_V2_3_0, \ + &FD_LEDGER_MAINNET_257047659_V2_3_0, \ + &FD_LEDGER_MAINNET_308445707_V2_3_0, \ + &FD_LEDGER_TESTNET_307395181_V2_3_0, \ + &FD_LEDGER_MAINNET_308392063_V2_3_0, \ + &FD_LEDGER_DEVNET_350814254_V2_3_0, \ + &FD_LEDGER_TESTNET_311586340_V2_3_0, \ + &FD_LEDGER_TESTNET_281546597_V2_3_0, \ + &FD_LEDGER_MAINNET_324823213_V2_3_0, \ + &FD_LEDGER_MAINNET_325467935_V2_3_0, \ + &FD_LEDGER_TESTNET_283927487_V2_3_0, \ + &FD_LEDGER_TESTNET_321168308_V2_3_0, \ + &FD_LEDGER_MAINNET_327324660_V2_3_0, \ + &FD_LEDGER_DEVNET_370199634_V2_3_0, \ + &FD_LEDGER_MAINNET_330219081_V2_3_0, \ + &FD_LEDGER_DEVNET_372721907_V2_3_0, \ + &FD_LEDGER_MAINNET_331691646_V2_3_0, \ + &FD_LEDGER_DEVNET_378683870_V2_3_0, \ + &FD_LEDGER_DEVNET_380592002_V2_3_0, \ + &FD_LEDGER_TESTNET_336218682_V2_3_0, \ + &FD_LEDGER_TESTNET_340269866_V2_3_0, \ + &FD_LEDGER_DEVNET_390056400_V2_3_0, \ + &FD_LEDGER_TESTNET_346556000, \ + &FD_LEDGER_TESTNET_346179946, \ + &FD_LEDGER_MULTI_BPF_LOADER_V2_3_0, \ + &FD_LEDGER_LOCAL_MULTI_BOUNDARY, \ + &FD_LEDGER_GENESIS_V3_0, \ + &FD_LEDGER_LOCALNET_STAKE_V3_0_0, \ + &FD_LEDGER_MAINNET_368528500_STRICTER_ABI, \ + &FD_LEDGER_MAINNET_368528500_DIRECT_MAPPING, \ + &FD_LEDGER_TESTNET_362107883_DIRECT_MAPPING_2 + + +/* Array of ledger configurations specifically for CI testing (from run_backtest_ci.sh) */ +#define CI_LEDGERS \ + &FD_LEDGER_MAINNET_308392063_V2_3_0, \ + &FD_LEDGER_DEVNET_350814254_V2_3_0, \ + &FD_LEDGER_TESTNET_281546597_V2_3_0, \ + &FD_LEDGER_MAINNET_324823213_V2_3_0, \ + &FD_LEDGER_MAINNET_325467935_V2_3_0, \ + &FD_LEDGER_TESTNET_283927487_V2_3_0, \ + &FD_LEDGER_TESTNET_321168308_V2_3_0, \ + &FD_LEDGER_MAINNET_327324660_V2_3_0, \ + &FD_LEDGER_DEVNET_370199634_V2_3_0, \ + &FD_LEDGER_DEVNET_378683870_V2_3_0, \ + &FD_LEDGER_MAINNET_330219081_V2_3_0, \ + &FD_LEDGER_DEVNET_372721907_V2_3_0, \ + &FD_LEDGER_MAINNET_331691646_V2_3_0, \ + &FD_LEDGER_TESTNET_336218682_V2_3_0, \ + &FD_LEDGER_TESTNET_340269866_V2_3_0, \ + &FD_LEDGER_DEVNET_390056400_V2_3_0, \ + &FD_LEDGER_MAINNET_254462437_V2_3_0, \ + &FD_LEDGER_MULTI_EPOCH_PER_200_V2_3_0, \ + &FD_LEDGER_TESTNET_346556000, \ + &FD_LEDGER_MULTI_BPF_LOADER_V2_3_0, \ + &FD_LEDGER_DEVNET_380592002_V2_3_0, \ + &FD_LEDGER_LOCAL_MULTI_BOUNDARY, \ + &FD_LEDGER_GENESIS_V3_0, \ + &FD_LEDGER_LOCALNET_STAKE_V3_0_0, \ + &FD_LEDGER_MAINNET_368528500_STRICTER_ABI, \ + &FD_LEDGER_MAINNET_368528500_DIRECT_MAPPING, \ + &FD_LEDGER_TESTNET_362107883_DIRECT_MAPPING_2 + +/* Total number of ledger configurations */ +#define FD_LEDGER_CONFIG_COUNT (86UL) + +/* Total number of CI ledger configurations */ +#define FD_LEDGER_CI_CONFIG_COUNT (27UL) + +/* Array declaration for easy iteration */ +static const fd_ledger_config_t * const fd_ledger_configs[ FD_LEDGER_CONFIG_COUNT + 1UL ] = { + ALL_LEDGERS, + NULL +}; + +/* Array declaration for CI ledger iteration */ +static const fd_ledger_config_t * const fd_ledgers_ci_configs[ FD_LEDGER_CI_CONFIG_COUNT + 1UL ] = { + CI_LEDGERS, + NULL +}; + +/* Helper function to find a ledger configuration by name */ +static inline fd_ledger_config_t const * +fd_ledger_config_find( char const * name ) { + for( ulong i = 0UL; i < FD_LEDGER_CONFIG_COUNT; i++ ) { + if( !strcmp( fd_ledger_configs[ i ]->name, name ) ) { + return fd_ledger_configs[ i ]; + } + } + return NULL; +} + +/* Helper function to get ledger configuration by index */ +static inline fd_ledger_config_t const * +fd_ledger_config_get( ulong idx ) { + if( FD_UNLIKELY( idx >= FD_LEDGER_CONFIG_COUNT ) ) return NULL; + return fd_ledger_configs[ idx ]; +} + +#endif /* HEADER_fd_src_app_backtest_ledgers_h */ diff --git a/src/app/backtest/main.c b/src/app/backtest/main.c new file mode 100644 index 00000000000..1c734c23958 --- /dev/null +++ b/src/app/backtest/main.c @@ -0,0 +1,452 @@ +#include "../firedancer/topology.h" +#include "../firedancer/config.h" +#include "../shared_dev/boot/fd_dev_boot.h" +#include "../shared/fd_action.h" +#include "../shared/commands/configure/configure.h" +#include "main.h" +#include "ledgers.h" + +#include +#include +#include +#include +#include + +/* Forward declarations */ +void backtest_topo_initialize( config_t * config ); +void backtest_set_ledger_name( char const * ledger_name ); +void backtest_create_custom_config_from_args( int argc, char ** argv ); +void backtest_set_custom_config( fd_ledger_config_t * config ); +void backtest_clear_custom_config( void ); + +char const * FD_APP_NAME = "Firedancer"; +char const * FD_BINARY_NAME = "backtest"; + +extern fd_topo_obj_callbacks_t fd_obj_cb_mcache; +extern fd_topo_obj_callbacks_t fd_obj_cb_dcache; +extern fd_topo_obj_callbacks_t fd_obj_cb_fseq; +extern fd_topo_obj_callbacks_t fd_obj_cb_metrics; +extern fd_topo_obj_callbacks_t fd_obj_cb_opaque; +extern fd_topo_obj_callbacks_t fd_obj_cb_dbl_buf; +extern fd_topo_obj_callbacks_t fd_obj_cb_neigh4_hmap; +extern fd_topo_obj_callbacks_t fd_obj_cb_fib4; +extern fd_topo_obj_callbacks_t fd_obj_cb_keyswitch; +extern fd_topo_obj_callbacks_t fd_obj_cb_tile; +extern fd_topo_obj_callbacks_t fd_obj_cb_store; +extern fd_topo_obj_callbacks_t fd_obj_cb_fec_sets; +extern fd_topo_obj_callbacks_t fd_obj_cb_txncache; +extern fd_topo_obj_callbacks_t fd_obj_cb_banks; +extern fd_topo_obj_callbacks_t fd_obj_cb_funk; +extern fd_topo_obj_callbacks_t fd_obj_cb_bank_hash_cmp; + +fd_topo_obj_callbacks_t * CALLBACKS[] = { + &fd_obj_cb_mcache, + &fd_obj_cb_dcache, + &fd_obj_cb_fseq, + &fd_obj_cb_metrics, + &fd_obj_cb_opaque, + &fd_obj_cb_dbl_buf, + &fd_obj_cb_neigh4_hmap, + &fd_obj_cb_fib4, + &fd_obj_cb_keyswitch, + &fd_obj_cb_tile, + &fd_obj_cb_store, + &fd_obj_cb_fec_sets, + &fd_obj_cb_txncache, + &fd_obj_cb_banks, + &fd_obj_cb_funk, + &fd_obj_cb_bank_hash_cmp, + NULL, +}; + +configure_stage_t * STAGES[] = { + &fd_cfg_stage_kill, + &fd_cfg_stage_netns, + &fd_cfg_stage_hugetlbfs, + &fd_cfg_stage_sysctl, + &fd_cfg_stage_ethtool_channels, + &fd_cfg_stage_ethtool_offloads, + &fd_cfg_stage_ethtool_loopback, + &fd_cfg_stage_keys, + &fd_cfg_stage_genesis, + &fd_cfg_stage_snapshots, + NULL, +}; + +extern fd_topo_run_tile_t fd_tile_net; +extern fd_topo_run_tile_t fd_tile_netlnk; +extern fd_topo_run_tile_t fd_tile_sock; +extern fd_topo_run_tile_t fd_tile_quic; +extern fd_topo_run_tile_t fd_tile_verify; +extern fd_topo_run_tile_t fd_tile_dedup; +extern fd_topo_run_tile_t fd_tile_resolv; +extern fd_topo_run_tile_t fd_tile_pack; +extern fd_topo_run_tile_t fd_tile_bank; +extern fd_topo_run_tile_t fd_tile_poh; +extern fd_topo_run_tile_t fd_tile_shred; +extern fd_topo_run_tile_t fd_tile_sign; +extern fd_topo_run_tile_t fd_tile_metric; +extern fd_topo_run_tile_t fd_tile_cswtch; +extern fd_topo_run_tile_t fd_tile_gui; +extern fd_topo_run_tile_t fd_tile_rpc; +extern fd_topo_run_tile_t fd_tile_plugin; +extern fd_topo_run_tile_t fd_tile_bencho; +extern fd_topo_run_tile_t fd_tile_benchg; +extern fd_topo_run_tile_t fd_tile_benchs; +extern fd_topo_run_tile_t fd_tile_bundle; +extern fd_topo_run_tile_t fd_tile_pktgen; +extern fd_topo_run_tile_t fd_tile_udpecho; +extern fd_topo_run_tile_t fd_tile_genesi; +extern fd_topo_run_tile_t fd_tile_ipecho; + +extern fd_topo_run_tile_t fd_tile_gossvf; +extern fd_topo_run_tile_t fd_tile_gossip; +extern fd_topo_run_tile_t fd_tile_repair; +extern fd_topo_run_tile_t fd_tile_replay; +extern fd_topo_run_tile_t fd_tile_execor; +extern fd_topo_run_tile_t fd_tile_send; +extern fd_topo_run_tile_t fd_tile_tower; +extern fd_topo_run_tile_t fd_tile_backtest; +extern fd_topo_run_tile_t fd_tile_archiver_feeder; +extern fd_topo_run_tile_t fd_tile_archiver_writer; +extern fd_topo_run_tile_t fd_tile_archiver_playback; +extern fd_topo_run_tile_t fd_tile_shredcap; + +extern fd_topo_run_tile_t fd_tile_snapct; +extern fd_topo_run_tile_t fd_tile_snapld; +extern fd_topo_run_tile_t fd_tile_snapdc; +extern fd_topo_run_tile_t fd_tile_snapin; + +fd_topo_run_tile_t * TILES[] = { + &fd_tile_net, + &fd_tile_netlnk, + &fd_tile_sock, + &fd_tile_quic, + &fd_tile_verify, + &fd_tile_dedup, + &fd_tile_resolv, + &fd_tile_pack, + &fd_tile_bank, + &fd_tile_shred, + &fd_tile_sign, + &fd_tile_metric, + &fd_tile_cswtch, + &fd_tile_gui, + &fd_tile_rpc, + &fd_tile_plugin, + &fd_tile_bencho, + &fd_tile_benchg, + &fd_tile_benchs, + &fd_tile_bundle, + &fd_tile_gossvf, + &fd_tile_gossip, + &fd_tile_repair, + &fd_tile_replay, + &fd_tile_execor, + &fd_tile_poh, + &fd_tile_send, + &fd_tile_tower, + &fd_tile_archiver_feeder, + &fd_tile_archiver_writer, + &fd_tile_archiver_playback, + &fd_tile_shredcap, +#if FD_HAS_ROCKSDB + &fd_tile_backtest, +#endif + &fd_tile_bencho, + &fd_tile_benchg, + &fd_tile_benchs, + &fd_tile_pktgen, + &fd_tile_udpecho, + &fd_tile_snapct, + &fd_tile_snapld, + &fd_tile_snapdc, + &fd_tile_snapin, + &fd_tile_genesi, + &fd_tile_ipecho, + NULL, +}; + + +extern action_t fd_action_help; +extern action_t fd_action_version; +extern action_t fd_action_backtest; + +action_t * ACTIONS[] = { + &fd_action_help, + &fd_action_version, + &fd_action_backtest, + NULL, +}; + +int +main( int argc, + char ** argv ) { + fd_config_file_t _default = fd_config_file_default(); + fd_config_file_t testnet = fd_config_file_testnet(); + fd_config_file_t devnet = fd_config_file_devnet(); + fd_config_file_t mainnet = fd_config_file_mainnet(); + + fd_config_file_t * configs[] = { + &_default, + &testnet, + &devnet, + &mainnet, + NULL + }; + + // Always insert "backtest" as the first argument since this is the backtest binary + char ** new_argv = malloc( (ulong)(argc + 2) * sizeof(char*) ); + if( !new_argv ) { + fprintf( stderr, "Failed to allocate memory\n" ); + return 1; + } + + new_argv[0] = argv[0]; + new_argv[1] = "backtest"; + for( int i = 1; i < argc; i++ ) { + new_argv[i + 1] = argv[i]; + } + new_argv[argc + 1] = NULL; + + // Check for --ci flag + int ci_mode = 0; + for( int i = 1; i < argc; i++ ) { + if( strcmp( argv[i], "--ci" ) == 0 ) { + ci_mode = 1; + break; + } + } + + // Check if we should run all ledgers or just a specific one + if( argc > 1 && !ci_mode ) { + // Check if the first argument is a flag (starts with --) + if( strncmp( argv[1], "--", 2 ) == 0 ) { + // First argument is a flag, not a ledger name + // Don't set ledger name, let the normal flow handle it + int result = fd_dev_main( argc + 1, new_argv, 1, configs, backtest_topo_initialize ); + free( new_argv ); + return result; + } else if( argc > 2 && strcmp( argv[1], "backtest" ) == 0 ) { + // Second run: ledger name is in argv[2], but check if it's a flag + if( strncmp( argv[2], "--", 2 ) == 0 ) { + // Don't set ledger name if it's a flag + } else { + backtest_set_ledger_name( argv[2] ); + // Check for custom configuration flags + backtest_create_custom_config_from_args( argc, argv ); + } + } else { + // First run: ledger name is in argv[1] + backtest_set_ledger_name( argv[1] ); + // Check for custom configuration flags + backtest_create_custom_config_from_args( argc, argv ); + } + + // Run specific ledger - use normal flow + int result = fd_dev_main( argc + 1, new_argv, 1, configs, backtest_topo_initialize ); + free( new_argv ); + return result; + } else { + // No ledger name provided or --ci flag - run ledgers by spawning child processes + if( ci_mode ) { + printf("Running CI ledgers one by one:\n"); + } else { + printf("Running all available ledgers one by one:\n"); + } + + // Get the current executable path + char exe_path[1024]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if( len == -1 ) { + fprintf(stderr, "Failed to get executable path\n"); + free( new_argv ); + return 1; + } + exe_path[len] = '\0'; + + // Choose which ledger set to use + const char* ledgers[FD_LEDGER_CONFIG_COUNT]; + ulong ledger_count; + if( ci_mode ) { + // Use CI ledgers + ledger_count = FD_LEDGER_CI_CONFIG_COUNT; + for( ulong i=0; iname; + } else { + ledgers[i] = "unknown"; + } + } + } else { + // Use all ledgers + ledger_count = FD_LEDGER_CONFIG_COUNT; + for( ulong i=0; iname; + } else { + ledgers[i] = "unknown"; + } + } + } + + ulong failed_count = 0; + + // Start timing for CI mode + struct timespec ci_start_time, ci_end_time; + clock_gettime( CLOCK_MONOTONIC, &ci_start_time ); + + for( ulong i = 0; i < ledger_count; i++ ) { + printf("Running backtest %lu/%lu: %s\n", i + 1, ledger_count, ledgers[i]); + + // Prepare arguments for the child process + char * child_argv[4]; + child_argv[0] = exe_path; + child_argv[1] = (char *)ledgers[i]; + child_argv[2] = "--no-watch"; + child_argv[3] = NULL; + + // Spawn child process + pid_t pid = fork(); + if( pid == 0 ) { + // Child process + execv( exe_path, child_argv ); + fprintf(stderr, "Failed to exec backtest for ledger %s\n", ledgers[i]); + exit( 1 ); + } else if( pid > 0 ) { + // Parent process - wait for child to complete + int status; + waitpid( pid, &status, 0 ); + if( WIFEXITED( status ) ) { + int exit_code = WEXITSTATUS( status ); + if( exit_code == 0 ) { + printf("✅ Backtest completed successfully for ledger: %s\n", ledgers[i]); + } else { + printf("❌ Backtest failed for ledger %s with exit code %d\n", ledgers[i], exit_code); + failed_count++; + } + } else { + printf("❌ Backtest process for ledger %s was terminated abnormally\n", ledgers[i]); + failed_count++; + } + } else { + printf("❌ Failed to fork process for ledger %s\n", ledgers[i]); + failed_count++; + } + } + + // End timing for CI mode and display results + clock_gettime( CLOCK_MONOTONIC, &ci_end_time ); + + long elapsed_seconds = ci_end_time.tv_sec - ci_start_time.tv_sec; + long elapsed_nanoseconds = ci_end_time.tv_nsec - ci_start_time.tv_nsec; + + if( elapsed_nanoseconds < 0 ) { + elapsed_seconds--; + elapsed_nanoseconds += 1000000000L; + } + + double elapsed_seconds_double = (double)elapsed_seconds + (double)elapsed_nanoseconds / 1000000000.0; + + printf("⏱️ Backtest suite completed in %.3f seconds (%lu ledgers)\n", elapsed_seconds_double, ledger_count ); + + printf("Completed running all ledgers: %lu/%lu successful, %lu failed\n", + ledger_count - failed_count, ledger_count, failed_count); + + free( new_argv ); + return failed_count > 0 ? 1 : 0; + } +} + +/* Function to create custom ledger configuration from command-line arguments */ +void +backtest_create_custom_config_from_args( int argc, char ** argv ) { + /* Skip custom configuration creation if this is a child process (has --no-watch) */ + for( int i = 1; i < argc; i++ ) { + if( strcmp( argv[i], "--no-watch" ) == 0 ) { + backtest_clear_custom_config(); /* Clear any inherited custom config */ + return; /* This is a child process, skip custom config creation */ + } + } + + /* Check if any custom configuration flags were provided */ + int has_custom_flags = 0; + for( int i = 1; i < argc; i++ ) { + if( strcmp( argv[i], "--genesis" ) == 0 || + strcmp( argv[i], "--has-incremental" ) == 0 || + strcmp( argv[i], "--redownload" ) == 0 || + strcmp( argv[i], "--skip-checksum" ) == 0 || + strncmp( argv[i], "--cluster-version", 16 ) == 0 || + strncmp( argv[i], "--funk-pages", 12 ) == 0 || + strncmp( argv[i], "--index-max", 11 ) == 0 || + strncmp( argv[i], "--end-slot", 10 ) == 0 || + strncmp( argv[i], "--trash-hash", 12 ) == 0 || + strncmp( argv[i], "--restore-archive", 17 ) == 0 || + strncmp( argv[i], "--one-offs", 10 ) == 0 ) { + has_custom_flags = 1; + break; + } + } + + if( has_custom_flags ) { + printf("NOTICE: Custom configuration flags detected. Creating custom ledger configuration.\n"); + + /* Create custom configuration */ + fd_ledger_config_t custom_config = {0}; + + /* Set the ledger name (should be the first non-flag argument, but skip "backtest" if present) */ + for( int i = 1; i < argc; i++ ) { + if( strncmp( argv[i], "--", 2 ) != 0 && strcmp( argv[i], "backtest" ) != 0 ) { + snprintf( custom_config.name, FD_LEDGER_NAME_MAX_LEN, "%s", argv[i] ); + break; + } + } + + /* Parse flags and set configuration */ + for( int i = 1; i < argc; i++ ) { + if( strcmp( argv[i], "--genesis" ) == 0 ) { + custom_config.genesis = 1; + } else if( strcmp( argv[i], "--has-incremental" ) == 0 ) { + custom_config.has_incremental = 1; + } else if( strncmp( argv[i], "--cluster-version", 16 ) == 0 && i + 1 < argc ) { + snprintf( custom_config.cluster_version, FD_LEDGER_CLUSTER_VERSION_MAX_LEN, "%s", argv[i + 1] ); + i++; /* Skip the value */ + } else if( strncmp( argv[i], "--funk-pages", 12 ) == 0 && i + 1 < argc ) { + custom_config.funk_pages = (ulong)strtoul( argv[i + 1], NULL, 10 ); + i++; /* Skip the value */ + } else if( strncmp( argv[i], "--index-max", 11 ) == 0 && i + 1 < argc ) { + custom_config.index_max = (ulong)strtoul( argv[i + 1], NULL, 10 ); + i++; /* Skip the value */ + } else if( strncmp( argv[i], "--end-slot", 10 ) == 0 && i + 1 < argc ) { + custom_config.end_slot = (ulong)strtoul( argv[i + 1], NULL, 10 ); + i++; /* Skip the value */ + } else if( strncmp( argv[i], "--one-offs", 10 ) == 0 && i + 1 < argc ) { + /* Add single one-off feature to the list */ + if( custom_config.features_cnt < FD_LEDGER_MAX_FEATURES ) { + /* Trim whitespace */ + char * feature = argv[i + 1]; + while( *feature == ' ' || *feature == '\t' ) feature++; + char * end = feature + strlen(feature) - 1; + while( end > feature && (*end == ' ' || *end == '\t') ) end--; + *(end + 1) = '\0'; + + /* Copy feature name */ + snprintf( custom_config.features[custom_config.features_cnt], 64UL, "%s", feature ); + custom_config.features_cnt++; + } + i++; /* Skip the value */ + } + } + + /* Set defaults for unspecified values */ + if( custom_config.cluster_version[0] == '\0' ) { + snprintf( custom_config.cluster_version, FD_LEDGER_CLUSTER_VERSION_MAX_LEN, "mainnet" ); + } + if( custom_config.funk_pages == 0 ) { + custom_config.funk_pages = 1UL; + } + + /* Set the custom configuration */ + backtest_set_custom_config( &custom_config ); + } +} diff --git a/src/app/backtest/main.h b/src/app/backtest/main.h new file mode 100644 index 00000000000..cb008840bb8 --- /dev/null +++ b/src/app/backtest/main.h @@ -0,0 +1,12 @@ +#ifndef HEADER_fd_src_app_backtest_main_h +#define HEADER_fd_src_app_backtest_main_h + +#include "../shared/commands/configure/configure.h" + +extern configure_stage_t fd_cfg_stage_kill; +extern configure_stage_t fd_cfg_stage_netns; +extern configure_stage_t fd_cfg_stage_genesis; +extern configure_stage_t fd_cfg_stage_keys; + +#endif /* HEADER_fd_src_app_backtest_main_h */ + diff --git a/src/app/shared/fd_action.h b/src/app/shared/fd_action.h index a14a576c36b..a2b2809cafc 100644 --- a/src/app/shared/fd_action.h +++ b/src/app/shared/fd_action.h @@ -46,7 +46,15 @@ union fdctl_args { } dev; struct { - int no_watch; + int no_watch; + int ci_mode; + char ledger_name[ 64 ]; + char cluster_version[ 16 ]; + ulong funk_pages; + ulong index_max; + ulong end_slot; + int genesis; + int has_incremental; } backtest; struct {