Skip to content

Commit 879e589

Browse files
committed
Add force-wallet-full-scan flag
Expose a startup option for Electrum and Esplora wallet recovery without accepting it for bitcoind RPC. Pass the request through LDK Node sync config so a full scan is forced until one succeeds. Co-Authored-By: HAL 9000
1 parent 21655f8 commit 879e589

2 files changed

Lines changed: 109 additions & 8 deletions

File tree

ldk-server/src/main.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use hex::DisplayHex;
2525
use hyper::server::conn::http2;
2626
use hyper_util::rt::{TokioExecutor, TokioIo};
2727
use ldk_node::bitcoin::Network;
28-
use ldk_node::config::Config;
28+
use ldk_node::config::{Config, ElectrumSyncConfig, EsploraSyncConfig};
2929
use ldk_node::lightning::events::ClosureReason;
3030
use ldk_node::lightning::ln::channelmanager::PaymentId;
3131
use ldk_node::lightning::ln::types::ChannelId;
@@ -183,11 +183,19 @@ fn main() {
183183
wallet_rescan_from_height,
184184
);
185185
},
186-
ChainSource::Electrum { server_url } => {
187-
builder.set_chain_source_electrum(server_url, None);
186+
ChainSource::Electrum { server_url, force_wallet_full_scan } => {
187+
let sync_config = force_wallet_full_scan.then(|| ElectrumSyncConfig {
188+
force_wallet_full_scan: true,
189+
..ElectrumSyncConfig::default()
190+
});
191+
builder.set_chain_source_electrum(server_url, sync_config);
188192
},
189-
ChainSource::Esplora { server_url } => {
190-
builder.set_chain_source_esplora(server_url, None);
193+
ChainSource::Esplora { server_url, force_wallet_full_scan } => {
194+
let sync_config = force_wallet_full_scan.then(|| EsploraSyncConfig {
195+
force_wallet_full_scan: true,
196+
..EsploraSyncConfig::default()
197+
});
198+
builder.set_chain_source_esplora(server_url, sync_config);
191199
},
192200
}
193201

ldk-server/src/util/config.rs

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)