@@ -98,9 +98,11 @@ pub enum ChainSource {
9898 } ,
9999 Electrum {
100100 server_url : String ,
101+ force_wallet_full_scan : bool ,
101102 } ,
102103 Esplora {
103104 server_url : String ,
105+ force_wallet_full_scan : bool ,
104106 } ,
105107}
106108
@@ -125,6 +127,7 @@ struct ConfigBuilder {
125127 bitcoind_rpc_user : Option < String > ,
126128 bitcoind_rpc_password : Option < String > ,
127129 rescan_from_height : Option < u32 > ,
130+ force_wallet_full_scan : bool ,
128131 rgs_server_url : Option < String > ,
129132 lsps2 : Option < LiquidityConfig > ,
130133 log_level : Option < String > ,
@@ -257,6 +260,10 @@ impl ConfigBuilder {
257260 self . rescan_from_height = Some ( rescan_from_height) ;
258261 }
259262
263+ if args. force_wallet_full_scan {
264+ self . force_wallet_full_scan = true ;
265+ }
266+
260267 if let Some ( storage_dir_path) = & args. storage_dir_path {
261268 self . storage_dir_path = Some ( storage_dir_path. clone ( ) ) ;
262269 }
@@ -378,6 +385,13 @@ impl ConfigBuilder {
378385 }
379386
380387 let chain_source = if rpc_configured {
388+ if self . force_wallet_full_scan {
389+ return Err ( io:: Error :: new (
390+ io:: ErrorKind :: InvalidInput ,
391+ "`--force-wallet-full-scan` requires the Electrum or Esplora chain source." ,
392+ ) ) ;
393+ }
394+
381395 let rpc_address = self
382396 . bitcoind_rpc_address
383397 . ok_or_else ( || missing_field_err ( "bitcoind_rpc_address" ) ) ?;
@@ -404,15 +418,21 @@ impl ConfigBuilder {
404418 "`--rescan-from-height` requires the bitcoind RPC chain source." ,
405419 ) ) ;
406420 }
407- ChainSource :: Electrum { server_url : url }
421+ ChainSource :: Electrum {
422+ server_url : url,
423+ force_wallet_full_scan : self . force_wallet_full_scan ,
424+ }
408425 } else if let Some ( url) = self . esplora_url {
409426 if self . rescan_from_height . is_some ( ) {
410427 return Err ( io:: Error :: new (
411428 io:: ErrorKind :: InvalidInput ,
412429 "`--rescan-from-height` requires the bitcoind RPC chain source." ,
413430 ) ) ;
414431 }
415- ChainSource :: Esplora { server_url : url }
432+ ChainSource :: Esplora {
433+ server_url : url,
434+ force_wallet_full_scan : self . force_wallet_full_scan ,
435+ }
416436 } else {
417437 return Err ( io:: Error :: new ( io:: ErrorKind :: InvalidInput , "No valid Chain Source configured. Provide Bitcoind RPC, Electrum, or Esplora details." ) ) ;
418438 } ;
@@ -923,6 +943,13 @@ pub struct ArgsConfig {
923943 ) ]
924944 rescan_from_height : Option < u32 > ,
925945
946+ #[ arg(
947+ long,
948+ env = "LDK_SERVER_FORCE_WALLET_FULL_SCAN" ,
949+ help = "Force wallet full scans until one succeeds. Only supported with Electrum and Esplora chain sources."
950+ ) ]
951+ force_wallet_full_scan : bool ,
952+
926953 #[ arg(
927954 long,
928955 env = "LDK_SERVER_STORAGE_DIR_PATH" ,
@@ -1116,6 +1143,7 @@ mod tests {
11161143 bitcoind_rpc_user : Some ( String :: from ( "bitcoind-testuser_cli" ) ) ,
11171144 bitcoind_rpc_password : Some ( String :: from ( "bitcoind-testpassword_cli" ) ) ,
11181145 rescan_from_height : None ,
1146+ force_wallet_full_scan : false ,
11191147 storage_dir_path : Some ( String :: from ( "/tmp_cli" ) ) ,
11201148 node_alias : Some ( String :: from ( "LDK Server CLI" ) ) ,
11211149 pathfinding_scores_source_url : Some ( String :: from ( "https://example.com/" ) ) ,
@@ -1144,6 +1172,7 @@ mod tests {
11441172 bitcoind_rpc_user : None ,
11451173 bitcoind_rpc_password : None ,
11461174 rescan_from_height : None ,
1175+ force_wallet_full_scan : false ,
11471176 storage_dir_path : None ,
11481177 pathfinding_scores_source_url : None ,
11491178 node_async_payments_role : None ,
@@ -1327,11 +1356,13 @@ mod tests {
13271356 fs:: write ( storage_path. join ( config_file_name) , toml_config) . unwrap ( ) ;
13281357 let config = load_config ( & args_config) . unwrap ( ) ;
13291358
1330- let ChainSource :: Electrum { server_url } = config. chain_source else {
1359+ let ChainSource :: Electrum { server_url, force_wallet_full_scan } = config. chain_source
1360+ else {
13311361 panic ! ( "unexpected chain source" ) ;
13321362 } ;
13331363
13341364 assert_eq ! ( server_url, "ssl://electrum.blockstream.info:50002" ) ;
1365+ assert ! ( !force_wallet_full_scan) ;
13351366
13361367 // Test case where only bitcoind is set
13371368
@@ -2207,4 +2238,66 @@ mod tests {
22072238 assert ! ( err. to_string( ) . contains( "--rescan-from-height" ) ) ;
22082239 }
22092240 }
2241+
2242+ #[ test]
2243+ fn test_accepts_force_wallet_full_scan_arg ( ) {
2244+ let args_config =
2245+ ArgsConfig :: try_parse_from ( [ "ldk-server" , "--force-wallet-full-scan" ] ) . unwrap ( ) ;
2246+
2247+ assert ! ( args_config. force_wallet_full_scan) ;
2248+ }
2249+
2250+ #[ test]
2251+ fn test_force_wallet_full_scan_configures_electrum_and_esplora ( ) {
2252+ for ( config_file_name, chain_config) in [
2253+ (
2254+ "test_force_wallet_full_scan_electrum.toml" ,
2255+ r#"
2256+ [node]
2257+ network = "regtest"
2258+
2259+ [electrum]
2260+ server_url = "ssl://electrum.blockstream.info:50002"
2261+ "# ,
2262+ ) ,
2263+ (
2264+ "test_force_wallet_full_scan_esplora.toml" ,
2265+ r#"
2266+ [node]
2267+ network = "regtest"
2268+
2269+ [esplora]
2270+ server_url = "https://mempool.space/api"
2271+ "# ,
2272+ ) ,
2273+ ] {
2274+ let storage_path = std:: env:: temp_dir ( ) ;
2275+ let chain_config = format ! ( "{}{}" , chain_config, lsps2_service_config_for_feature( ) ) ;
2276+ fs:: write ( storage_path. join ( config_file_name) , chain_config) . unwrap ( ) ;
2277+ let mut args_config = empty_args_config ( ) ;
2278+ args_config. config_file =
2279+ Some ( storage_path. join ( config_file_name) . to_string_lossy ( ) . to_string ( ) ) ;
2280+ args_config. force_wallet_full_scan = true ;
2281+
2282+ let config = load_config ( & args_config) . unwrap ( ) ;
2283+ let force_wallet_full_scan = match config. chain_source {
2284+ ChainSource :: Electrum { force_wallet_full_scan, .. }
2285+ | ChainSource :: Esplora { force_wallet_full_scan, .. } => force_wallet_full_scan,
2286+ ChainSource :: Rpc { .. } => panic ! ( "unexpected chain source" ) ,
2287+ } ;
2288+
2289+ assert ! ( force_wallet_full_scan) ;
2290+ }
2291+ }
2292+
2293+ #[ test]
2294+ fn test_force_wallet_full_scan_rejects_bitcoind ( ) {
2295+ let mut args_config = default_args_config ( ) ;
2296+ args_config. force_wallet_full_scan = true ;
2297+
2298+ let err = load_config ( & args_config) . unwrap_err ( ) ;
2299+
2300+ assert_eq ! ( err. kind( ) , io:: ErrorKind :: InvalidInput ) ;
2301+ assert ! ( err. to_string( ) . contains( "--force-wallet-full-scan" ) ) ;
2302+ }
22102303}
0 commit comments