diff --git a/src/app/firedancer/config/default.toml b/src/app/firedancer/config/default.toml index a4894b528cb..862ed38d89b 100644 --- a/src/app/firedancer/config/default.toml +++ b/src/app/firedancer/config/default.toml @@ -282,7 +282,8 @@ user = "" # present. # # TODO: implement the "at most one downloaded " logic. " - download = true + # TODO: default to true once integrated with gossip + download = false # A snapshot hash must be published in gossip by one of the # validator identities listed here for it to be accepted. The list @@ -324,10 +325,11 @@ user = "" # completing snapshot download. maximum_download_retry_abort = 5 - # The cluster to download snapshots from. This is a temporary - # config option that will be removed once the snapshot tiles - # are integrated with gossip - cluster = "testnet" + # List of initial peer addresses for snapshot download. These peers + # should be highly trusted. This list must not be empty when snapshot + # download is enabled. This is a temporary config option that will be + # removed once the snapshot tiles are integrated with gossip. + initial_peers = [] [rpc] # If nonzero, enable JSON RPC on this port, and use the next port diff --git a/src/app/firedancer/topology.c b/src/app/firedancer/topology.c index 922998de8be..3a3e7fb56b7 100644 --- a/src/app/firedancer/topology.c +++ b/src/app/firedancer/topology.c @@ -176,22 +176,70 @@ resolve_gossip_entrypoints( config_t * config ) { config->gossip.resolved_entrypoints_cnt = resolved_entrypoints; } +static int +resolve_initial_peer( char const * host_port, + fd_ip4_port_t * ip4_port ) { + + /* Split host:port */ + + char const * colon = strrchr( host_port, ':' ); + if( FD_UNLIKELY( !colon ) ) { + FD_LOG_ERR(( "invalid [snapshots.initial_peers] entry \"%s\": no port number", host_port )); + } + + char fqdn[ 255 ]; + ulong fqdn_len = (ulong)( colon-host_port ); + if( FD_UNLIKELY( fqdn_len>254 ) ) { + FD_LOG_ERR(( "invalid [snapshots.initial_peers] entry \"%s\": hostname too long", host_port )); + } + fd_memcpy( fqdn, host_port, fqdn_len ); + fqdn[ fqdn_len ] = '\0'; + + /* Parse port number */ + + char const * port_str = colon+1; + char const * endptr = NULL; + ulong port = strtoul( port_str, (char **)&endptr, 10 ); + if( FD_UNLIKELY( !endptr || !port || port>USHORT_MAX || *endptr!='\0' ) ) { + FD_LOG_ERR(( "invalid [snapshots.initial_peers] entry \"%s\": invalid port number", host_port )); + } + ip4_port->port = (ushort)port; + + /* Resolve hostname */ + int resolved = resolve_address( fqdn, &ip4_port->addr ); + return resolved; +} + +static void +resolve_initial_peers( config_t * config ) { + ulong initial_peers_cnt = config->firedancer.snapshots.initial_peers_cnt; + ulong resolved_initial_peers = 0UL; + for( ulong j=0UL; jfiredancer.snapshots.initial_peers[j], &config->firedancer.snapshots.resolved_initial_peers[resolved_initial_peers] ) ) { + resolved_initial_peers++; + } + } + config->firedancer.snapshots.resolved_initial_peers_cnt = resolved_initial_peers; +} + static void setup_snapshots( config_t * config, fd_topo_tile_t * tile ) { fd_memcpy( tile->snaprd.snapshots_path, config->paths.snapshots, PATH_MAX ); - fd_memcpy( tile->snaprd.cluster, config->firedancer.snapshots.cluster, sizeof(tile->snaprd.cluster) ); tile->snaprd.incremental_snapshot_fetch = config->firedancer.snapshots.incremental_snapshots; tile->snaprd.do_download = config->firedancer.snapshots.download; tile->snaprd.maximum_local_snapshot_age = config->firedancer.snapshots.maximum_local_snapshot_age; tile->snaprd.minimum_download_speed_mib = config->firedancer.snapshots.minimum_download_speed_mib; tile->snaprd.maximum_download_retry_abort = config->firedancer.snapshots.maximum_download_retry_abort; + tile->snaprd.initial_peers_cnt = fd_ulong_min( config->firedancer.snapshots.resolved_initial_peers_cnt, 16UL ); + fd_memcpy( tile->snaprd.initial_peers, config->firedancer.snapshots.resolved_initial_peers, tile->snaprd.initial_peers_cnt * sizeof(fd_ip4_port_t) ); /* TODO: set up known validators and known validators cnt */ } void fd_topo_initialize( config_t * config ) { resolve_gossip_entrypoints( config ); + resolve_initial_peers( config ); ulong net_tile_cnt = config->layout.net_tile_count; ulong shred_tile_cnt = config->layout.shred_tile_count; diff --git a/src/app/shared/fd_config.h b/src/app/shared/fd_config.h index cb8d65f0dbb..e606d7b1920 100644 --- a/src/app/shared/fd_config.h +++ b/src/app/shared/fd_config.h @@ -129,14 +129,17 @@ struct fd_configf { } runtime; struct { - int incremental_snapshots; - uint maximum_local_snapshot_age; - int download; - ulong known_validators_cnt; - char known_validators[ 16 ][ 256 ]; - uint minimum_download_speed_mib; - uint maximum_download_retry_abort; - char cluster[ 8UL ]; + int incremental_snapshots; + uint maximum_local_snapshot_age; + int download; + ulong known_validators_cnt; + char known_validators[ 16 ][ 256 ]; + uint minimum_download_speed_mib; + uint maximum_download_retry_abort; + ulong initial_peers_cnt; + char initial_peers[ 16 ][ 256 ]; + ulong resolved_initial_peers_cnt; + fd_ip4_port_t resolved_initial_peers[ 16 ]; } snapshots; struct { diff --git a/src/app/shared/fd_config_parse.c b/src/app/shared/fd_config_parse.c index b01a763210e..de5af92a6a9 100644 --- a/src/app/shared/fd_config_parse.c +++ b/src/app/shared/fd_config_parse.c @@ -5,13 +5,20 @@ static void fd_config_check_configf( fd_config_t * config, fd_configf_t * config_f ) { - (void)config_f; if( FD_UNLIKELY( strlen( config->paths.snapshots )>PATH_MAX-1UL ) ) { FD_LOG_ERR(( "[config->paths.snapshots] is too long (max %lu)", PATH_MAX-1UL )); } if( FD_UNLIKELY( config->paths.snapshots[ 0 ]!='\0' && config->paths.snapshots[ 0 ]!='/' ) ) { FD_LOG_ERR(( "[config->paths.snapshots] must be an absolute path and hence start with a '/'")); } + if( FD_UNLIKELY( config_f->snapshots.download && config_f->snapshots.initial_peers_cnt==0UL ) ) { + FD_LOG_ERR(( "[configf->snapshots.download] is true but [configf->snapshots.initial_peers] is not set. " + "Must specify at least one initial peer to download from if download is enabled" )); + } + if( FD_UNLIKELY( config_f->snapshots.initial_peers_cnt!=0UL && !config_f->snapshots.download ) ) { + FD_LOG_NOTICE(( "[configf->snapshots.initial_peers] is set but [configf->snapshots.download] is false. " + "This will not download snapshots from the initial peers" )); + } } fd_configh_t * @@ -109,7 +116,7 @@ fd_config_extract_podf( uchar * pod, CFG_POP_ARRAY( cstr, snapshots.known_validators ); CFG_POP ( uint, snapshots.minimum_download_speed_mib ); CFG_POP ( uint, snapshots.maximum_download_retry_abort ); - CFG_POP ( cstr, snapshots.cluster ); + CFG_POP_ARRAY( cstr, snapshots.initial_peers ); return config; } diff --git a/src/disco/topo/fd_topo.h b/src/disco/topo/fd_topo.h index 0b1dd70bd82..8db1b7bb995 100644 --- a/src/disco/topo/fd_topo.h +++ b/src/disco/topo/fd_topo.h @@ -479,12 +479,13 @@ struct fd_topo_tile { struct { char snapshots_path[ PATH_MAX ]; - char cluster[ 8UL ]; int incremental_snapshot_fetch; int do_download; uint maximum_local_snapshot_age; uint minimum_download_speed_mib; uint maximum_download_retry_abort; + ulong initial_peers_cnt; + fd_ip4_port_t initial_peers[ 16 ]; } snaprd; struct { diff --git a/src/discof/restore/fd_snaprd_tile.c b/src/discof/restore/fd_snaprd_tile.c index 7465adcd0a2..6734ccdb12e 100644 --- a/src/discof/restore/fd_snaprd_tile.c +++ b/src/discof/restore/fd_snaprd_tile.c @@ -646,27 +646,8 @@ unprivileged_init( fd_topo_t * topo, ctx->sshttp = fd_sshttp_join( fd_sshttp_new( _sshttp ) ); FD_TEST( ctx->sshttp ); - if( FD_LIKELY( !strcmp( tile->snaprd.cluster, "testnet" ) ) ) { - fd_ip4_port_t initial_peers[ 2UL ] = { - { .addr = FD_IP4_ADDR( 35 , 214, 172, 227 ), .port = 8899 }, - { .addr = FD_IP4_ADDR( 145, 40 , 95 , 69 ), .port = 8899 }, /* Solana testnet peer */ - }; - for( ulong i=0UL; i<2UL; i++ ) fd_ssping_add( ctx->ssping, initial_peers[ i ] ); - } else if( FD_LIKELY( !strcmp( tile->snaprd.cluster, "private" ) ) ) { - fd_ip4_port_t initial_peers[ 1UL ] = { - { .addr = FD_IP4_ADDR( 147, 28, 185, 47 ), .port = 8899 } /* A private cluster peer */ - }; - for( ulong i=0UL; i<1UL; i++ ) fd_ssping_add( ctx->ssping, initial_peers[ i ] ); - } else if (FD_LIKELY( !strcmp( tile->snaprd.cluster, "mainnet" ) ) ) { - fd_ip4_port_t initial_peers[ 3UL ] = { - { .addr = FD_IP4_ADDR( 149, 255, 37 , 130 ), .port = 8899 }, - { .addr = FD_IP4_ADDR( 34 , 1 , 238, 227 ), .port = 8899 }, - { .addr = FD_IP4_ADDR( 34 , 1 , 139, 131 ), .port = 8899 } - }; - for( ulong i=0UL; i<3UL; i++ ) fd_ssping_add( ctx->ssping, initial_peers[ i ] ); - } - else { - FD_LOG_ERR(( "unexpected cluster %s", tile->snaprd.cluster )); + for( ulong i=0UL; isnaprd.initial_peers_cnt; i++ ) { + fd_ssping_add( ctx->ssping, tile->snaprd.initial_peers[ i ] ); } if( FD_UNLIKELY( tile->out_cnt!=1UL ) ) FD_LOG_ERR(( "tile `" NAME "` has %lu outs, expected 1", tile->out_cnt ));