diff --git a/src/builder.rs b/src/builder.rs index 54e79f4b3a..7d53d4a7d5 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -529,7 +529,7 @@ impl NodeBuilder { /// 0-confirmation channels opened by this LSP. If `false`, 0-confirmation /// acceptance for this peer falls back to [`Config::trusted_peers_0conf`]. /// - /// May be called multiple times to register several LSPs. Duplicate `node_id`s are ignored. + /// May be called multiple times to register several LSPs. Re-adding an existing `node_id` updates its address, token, and 0conf trust settings. A running node uses the new address on the next start. /// /// [bLIP-50 / LSPS0]: https://github.com/lightning/blips/blob/master/blip-0050.md pub fn add_liquidity_source( @@ -539,7 +539,12 @@ impl NodeBuilder { let liquidity_source_config = self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default()); - if liquidity_source_config.lsp_nodes.iter().any(|n| n.node_id == node_id) { + if let Some(existing) = + liquidity_source_config.lsp_nodes.iter_mut().find(|n| n.node_id == node_id) + { + existing.address = address; + existing.token = token; + existing.trust_peer_0conf = trust_peer_0conf; return self; } @@ -1189,7 +1194,7 @@ impl Builder { /// 0-confirmation channels opened by this LSP. If `false`, 0-confirmation /// acceptance for this peer falls back to [`Config::trusted_peers_0conf`]. /// - /// May be called multiple times to register several LSPs. Duplicate `node_id`s are ignored. + /// May be called multiple times to register several LSPs. Re-adding an existing `node_id` updates its address, token, and 0conf trust settings. A running node uses the new address on the next start. /// /// [bLIP-50 / LSPS0]: https://github.com/lightning/blips/blob/master/blip-0050.md pub fn add_liquidity_source( @@ -2938,4 +2943,54 @@ mod tests { builder.set_runtime(multi_thread_runtime.handle().clone()).unwrap(); assert!(builder.setup_runtime(&logger).is_ok(), "a multi-threaded runtime should be used"); } + + #[test] + fn add_liquidity_source_updates_existing_lsp_in_place() { + use std::str::FromStr; + + use bitcoin::secp256k1::PublicKey; + use lightning::ln::msgs::SocketAddress; + + let mut builder = NodeBuilder::new(); + let node_id = PublicKey::from_str( + "0276607124ebe6a6c9338517b6f485825b27c2dcc0b9fc2aa6a4c0df91194e5993", + ) + .unwrap(); + let first_address = SocketAddress::from_str("127.0.0.1:9735").unwrap(); + let second_address = SocketAddress::from_str("127.0.0.2:9735").unwrap(); + + // Register an LSP, then re-add the same node_id with a new address and + // different 0conf/trust settings. Re-adding must update the existing + // entry in place rather than silently dropping the new values. + builder.add_liquidity_source(node_id, first_address.clone(), None, true); + builder.add_liquidity_source( + node_id, + second_address.clone(), + Some("token".to_owned()), + false, + ); + + let config = builder + .liquidity_source_config + .as_ref() + .expect("liquidity source config should be initialized"); + assert_eq!( + config.lsp_nodes.len(), + 1, + "re-adding the same node_id must not create a duplicate entry", + ); + assert_eq!( + config.lsp_nodes[0].address, second_address, + "re-adding must update the address in place", + ); + assert_eq!( + config.lsp_nodes[0].token, + Some("token".to_owned()), + "re-adding must update the token in place", + ); + assert!( + !config.lsp_nodes[0].trust_peer_0conf, + "re-adding must update the 0conf trust setting in place", + ); + } } diff --git a/src/liquidity/mod.rs b/src/liquidity/mod.rs index c1442b0fc9..ed1546e2ba 100644 --- a/src/liquidity/mod.rs +++ b/src/liquidity/mod.rs @@ -156,7 +156,9 @@ impl Liquidity { /// The given `token` will be used by the LSP to authenticate the user. /// `trust_peer_0conf` controls whether the node will accept 0-confirmation channels opened by this /// LSP. Note this supersedes [`Config::trusted_peers_0conf`] for this peer. - /// Duplicate `node_id`s are ignored. + /// Re-adding an existing `node_id` updates its address/token/0conf settings in place. A live + /// peer is left connected; the new address is used on the next start (same restart-only + /// rediscovery as #956). New `node_id`s still connect and discover immediately. pub fn add_liquidity_source( &self, node_id: PublicKey, address: SocketAddress, token: Option, trust_peer_0conf: bool, @@ -167,11 +169,24 @@ impl Liquidity { { let mut lsp_nodes = self.liquidity_source.lsp_nodes.write().expect("lock"); - if lsp_nodes.iter().any(|n| n.node_id == node_id) { - log_info!(self.logger, "LSP node {} already added, skipping.", node_id); + if let Some(existing) = lsp_nodes.iter_mut().find(|n| n.node_id == node_id) { + let changed = existing.address != address + || existing.token != token + || existing.trust_peer_0conf != trust_peer_0conf; + if !changed { + log_info!(self.logger, "LSP node {} already added, skipping.", node_id); + return Ok(()); + } + log_info!( + self.logger, + "Updating existing LSP node {} config in place; reconnect on next start.", + node_id + ); + existing.address = address; + existing.token = token; + existing.trust_peer_0conf = trust_peer_0conf; return Ok(()); } - lsp_nodes.push(LspNode { node_id, address: address.clone(),