@@ -14,8 +14,7 @@ use std::collections::{HashMap, HashSet};
1414use std:: env;
1515use std:: future:: Future ;
1616use std:: path:: PathBuf ;
17- use std:: sync:: atomic:: { AtomicU16 , Ordering } ;
18- use std:: sync:: { Arc , LazyLock , RwLock } ;
17+ use std:: sync:: { Arc , RwLock } ;
1918use std:: time:: Duration ;
2019
2120use bitcoin:: hashes:: hex:: FromHex ;
@@ -274,13 +273,9 @@ pub(crate) fn random_storage_path() -> PathBuf {
274273 temp_path
275274}
276275
277- static BASE_PORT : LazyLock < u16 > = LazyLock :: new ( || {
278- env:: var ( "LDK_NODE_TEST_BASE_PORT" ) . ok ( ) . and_then ( |v| v. parse ( ) . ok ( ) ) . unwrap_or ( 20000 )
279- } ) ;
280- static NEXT_PORT : LazyLock < AtomicU16 > = LazyLock :: new ( || AtomicU16 :: new ( * BASE_PORT ) ) ;
281-
282- pub ( crate ) fn generate_listening_addresses ( ) -> Vec < SocketAddress > {
283- let port = NEXT_PORT . fetch_add ( 2 , Ordering :: Relaxed ) ;
276+ pub ( crate ) fn random_listening_addresses ( ) -> Vec < SocketAddress > {
277+ let mut rng = rng ( ) ;
278+ let port = rng. random_range ( 10000 ..65000u16 ) ;
284279 vec ! [
285280 SocketAddress :: TcpIpV4 { addr: [ 127 , 0 , 0 , 1 ] , port } ,
286281 SocketAddress :: TcpIpV4 { addr: [ 127 , 0 , 0 , 1 ] , port: port + 1 } ,
@@ -310,8 +305,8 @@ pub(crate) fn random_config(anchor_channels: bool) -> TestConfig {
310305 println ! ( "Setting random LDK storage dir: {}" , rand_dir. display( ) ) ;
311306 node_config. storage_dir_path = rand_dir. to_str ( ) . unwrap ( ) . to_owned ( ) ;
312307
313- let listening_addresses = generate_listening_addresses ( ) ;
314- println ! ( "Setting LDK listening addresses: {:?}" , listening_addresses) ;
308+ let listening_addresses = random_listening_addresses ( ) ;
309+ println ! ( "Setting random LDK listening addresses: {:?}" , listening_addresses) ;
315310 node_config. listening_addresses = Some ( listening_addresses) ;
316311
317312 let alias = random_node_alias ( ) ;
@@ -430,81 +425,99 @@ pub(crate) fn setup_two_nodes_with_store(
430425}
431426
432427pub ( crate ) fn setup_node ( chain_source : & TestChainSource , config : TestConfig ) -> TestNode {
433- setup_builder ! ( builder, config. node_config) ;
434- match chain_source {
435- TestChainSource :: Esplora ( electrsd) => {
436- let esplora_url = format ! ( "http://{}" , electrsd. esplora_url. as_ref( ) . unwrap( ) ) ;
437- let mut sync_config = EsploraSyncConfig :: default ( ) ;
438- sync_config. background_sync_config = None ;
439- builder. set_chain_source_esplora ( esplora_url. clone ( ) , Some ( sync_config) ) ;
440- } ,
441- TestChainSource :: Electrum ( electrsd) => {
442- let electrum_url = format ! ( "tcp://{}" , electrsd. electrum_url) ;
443- let mut sync_config = ElectrumSyncConfig :: default ( ) ;
444- sync_config. background_sync_config = None ;
445- builder. set_chain_source_electrum ( electrum_url. clone ( ) , Some ( sync_config) ) ;
446- } ,
447- TestChainSource :: BitcoindRpcSync ( bitcoind) => {
448- let rpc_host = bitcoind. params . rpc_socket . ip ( ) . to_string ( ) ;
449- let rpc_port = bitcoind. params . rpc_socket . port ( ) ;
450- let values = bitcoind. params . get_cookie_values ( ) . unwrap ( ) . unwrap ( ) ;
451- let rpc_user = values. user ;
452- let rpc_password = values. password ;
453- builder. set_chain_source_bitcoind_rpc ( rpc_host, rpc_port, rpc_user, rpc_password) ;
454- } ,
455- TestChainSource :: BitcoindRestSync ( bitcoind) => {
456- let rpc_host = bitcoind. params . rpc_socket . ip ( ) . to_string ( ) ;
457- let rpc_port = bitcoind. params . rpc_socket . port ( ) ;
458- let values = bitcoind. params . get_cookie_values ( ) . unwrap ( ) . unwrap ( ) ;
459- let rpc_user = values. user ;
460- let rpc_password = values. password ;
461- let rest_host = bitcoind. params . rpc_socket . ip ( ) . to_string ( ) ;
462- let rest_port = bitcoind. params . rpc_socket . port ( ) ;
463- builder. set_chain_source_bitcoind_rest (
464- rest_host,
465- rest_port,
466- rpc_host,
467- rpc_port,
468- rpc_user,
469- rpc_password,
428+ for attempt in 0 ..5 {
429+ let mut node_config = config. node_config . clone ( ) ;
430+ if attempt > 0 {
431+ let new_addrs = random_listening_addresses ( ) ;
432+ println ! (
433+ "Retrying with new listening addresses (attempt {}): {:?}" ,
434+ attempt + 1 ,
435+ new_addrs
470436 ) ;
471- } ,
472- }
437+ node_config . listening_addresses = Some ( new_addrs ) ;
438+ }
473439
474- match & config. log_writer {
475- TestLogWriter :: FileWriter => {
476- builder. set_filesystem_logger ( None , None ) ;
477- } ,
478- TestLogWriter :: LogFacade => {
479- builder. set_log_facade_logger ( ) ;
480- } ,
481- TestLogWriter :: Custom ( custom_log_writer) => {
482- builder. set_custom_logger ( Arc :: clone ( custom_log_writer) ) ;
483- } ,
484- }
440+ setup_builder ! ( builder, node_config) ;
441+ match chain_source {
442+ TestChainSource :: Esplora ( electrsd) => {
443+ let esplora_url = format ! ( "http://{}" , electrsd. esplora_url. as_ref( ) . unwrap( ) ) ;
444+ let mut sync_config = EsploraSyncConfig :: default ( ) ;
445+ sync_config. background_sync_config = None ;
446+ builder. set_chain_source_esplora ( esplora_url. clone ( ) , Some ( sync_config) ) ;
447+ } ,
448+ TestChainSource :: Electrum ( electrsd) => {
449+ let electrum_url = format ! ( "tcp://{}" , electrsd. electrum_url) ;
450+ let mut sync_config = ElectrumSyncConfig :: default ( ) ;
451+ sync_config. background_sync_config = None ;
452+ builder. set_chain_source_electrum ( electrum_url. clone ( ) , Some ( sync_config) ) ;
453+ } ,
454+ TestChainSource :: BitcoindRpcSync ( bitcoind) => {
455+ let rpc_host = bitcoind. params . rpc_socket . ip ( ) . to_string ( ) ;
456+ let rpc_port = bitcoind. params . rpc_socket . port ( ) ;
457+ let values = bitcoind. params . get_cookie_values ( ) . unwrap ( ) . unwrap ( ) ;
458+ let rpc_user = values. user ;
459+ let rpc_password = values. password ;
460+ builder. set_chain_source_bitcoind_rpc ( rpc_host, rpc_port, rpc_user, rpc_password) ;
461+ } ,
462+ TestChainSource :: BitcoindRestSync ( bitcoind) => {
463+ let rpc_host = bitcoind. params . rpc_socket . ip ( ) . to_string ( ) ;
464+ let rpc_port = bitcoind. params . rpc_socket . port ( ) ;
465+ let values = bitcoind. params . get_cookie_values ( ) . unwrap ( ) . unwrap ( ) ;
466+ let rpc_user = values. user ;
467+ let rpc_password = values. password ;
468+ let rest_host = bitcoind. params . rpc_socket . ip ( ) . to_string ( ) ;
469+ let rest_port = bitcoind. params . rpc_socket . port ( ) ;
470+ builder. set_chain_source_bitcoind_rest (
471+ rest_host,
472+ rest_port,
473+ rpc_host,
474+ rpc_port,
475+ rpc_user,
476+ rpc_password,
477+ ) ;
478+ } ,
479+ }
485480
486- builder. set_async_payments_role ( config. async_payments_role ) . unwrap ( ) ;
481+ match & config. log_writer {
482+ TestLogWriter :: FileWriter => {
483+ builder. set_filesystem_logger ( None , None ) ;
484+ } ,
485+ TestLogWriter :: LogFacade => {
486+ builder. set_log_facade_logger ( ) ;
487+ } ,
488+ TestLogWriter :: Custom ( custom_log_writer) => {
489+ builder. set_custom_logger ( Arc :: clone ( custom_log_writer) ) ;
490+ } ,
491+ }
487492
488- if config. recovery_mode {
489- builder. set_wallet_recovery_mode ( ) ;
490- }
493+ builder. set_async_payments_role ( config. async_payments_role ) . unwrap ( ) ;
491494
492- let node = match config. store_type {
493- TestStoreType :: TestSyncStore => {
494- let kv_store = TestSyncStore :: new ( config. node_config . storage_dir_path . into ( ) ) ;
495- builder. build_with_store ( config. node_entropy . into ( ) , kv_store) . unwrap ( )
496- } ,
497- TestStoreType :: Sqlite => builder. build ( config. node_entropy . into ( ) ) . unwrap ( ) ,
498- } ;
495+ if config. recovery_mode {
496+ builder. set_wallet_recovery_mode ( ) ;
497+ }
499498
500- if config. recovery_mode {
501- builder. set_wallet_recovery_mode ( ) ;
502- }
499+ let node = match config. store_type {
500+ TestStoreType :: TestSyncStore => {
501+ let kv_store = TestSyncStore :: new ( node_config. storage_dir_path . into ( ) ) ;
502+ builder. build_with_store ( config. node_entropy . clone ( ) . into ( ) , kv_store) . unwrap ( )
503+ } ,
504+ TestStoreType :: Sqlite => builder. build ( config. node_entropy . clone ( ) . into ( ) ) . unwrap ( ) ,
505+ } ;
503506
504- node. start ( ) . unwrap ( ) ;
505- assert ! ( node. status( ) . is_running) ;
506- assert ! ( node. status( ) . latest_fee_rate_cache_update_timestamp. is_some( ) ) ;
507- node
507+ match node. start ( ) {
508+ Ok ( ( ) ) => {
509+ assert ! ( node. status( ) . is_running) ;
510+ assert ! ( node. status( ) . latest_fee_rate_cache_update_timestamp. is_some( ) ) ;
511+ return node;
512+ } ,
513+ Err ( NodeError :: InvalidSocketAddress ) => {
514+ eprintln ! ( "node.start() failed with InvalidSocketAddress, retrying..." ) ;
515+ continue ;
516+ } ,
517+ Err ( e) => panic ! ( "node.start() failed: {:?}" , e) ,
518+ }
519+ }
520+ panic ! ( "Failed to start node after 5 attempts due to port collisions" )
508521}
509522
510523pub ( crate ) async fn generate_blocks_and_wait < E : ElectrumApi > (
0 commit comments