diff --git a/contrib/ldk-server-config.toml b/contrib/ldk-server-config.toml index 5311a246..9510e3f4 100644 --- a/contrib/ldk-server-config.toml +++ b/contrib/ldk-server-config.toml @@ -8,6 +8,7 @@ alias = "ldk_server" # Lightning node alias #pathfinding_scores_source_url = "https://rapidsync.lightningdevkit.org/scoring/scorer.bin" # External pathfinding scores source (optional, defaults to this URL on mainnet; set to "" to disable) #rgs_server_url = "https://rapidsync.lightningdevkit.org/snapshot/v2/" # Optional: RGS URL for rapid gossip sync #async_payments_role = "client" # Optional async payments role: "client" or "server" +#enable_zero_fee_commitments = false # Enable zero-fee commitment channels (default: false) # Background probing service (optional) # CAUTION: Probes send real HTLCs and can lock outbound liquidity until they time out. diff --git a/docs/configuration.md b/docs/configuration.md index 03a7c204..c31926ed 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -52,13 +52,18 @@ on macOS). Core node settings: which Bitcoin network to use, Lightning peer listening and announcement addresses, the gRPC bind address, node alias, optional Rapid Gossip Sync / pathfinding -scores URLs, and the async payments role. +scores URLs, the async payments role, and zero-fee commitment channels. Set `async_payments_role = "client"` to ask peers to hold HTLCs where possible, allowing this node to go offline. Set `async_payments_role = "server"` to hold async payment HTLCs and onion messages for peers. The server role requires an announceable node configuration. Leave the field unset to disable async payments. +Set `enable_zero_fee_commitments = true` to prefer zero-fee commitment channels, +falling back to other channel types supported by the peer. Enabling this setting requires a chain +source that supports Bitcoin Core's `submitpackage` RPC, and relays TRUC, P2A, and ephemeral +dust. The default is `false`. + ### `[probing]` Enables LDK Node's background probing service to train the payment scorer with current diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index cc8e0471..28ea10de 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -156,6 +156,8 @@ fn main() { ldk_node_config.announcement_addresses = config_file.announcement_addrs; ldk_node_config.network = config_file.network; ldk_node_config.hrn_config = config_file.hrn_config; + ldk_node_config.anchor_channels_config.enable_zero_fee_commitments = + config_file.enable_zero_fee_commitments; let mut builder = Builder::from_config(ldk_node_config); builder.set_log_facade_logger(); diff --git a/ldk-server/src/util/config.rs b/ldk-server/src/util/config.rs index 3fc33993..b8028ef6 100644 --- a/ldk-server/src/util/config.rs +++ b/ldk-server/src/util/config.rs @@ -70,6 +70,7 @@ pub struct Config { pub pathfinding_scores_source_url: Option, pub probing_config: Option, pub async_payments_role: Option, + pub enable_zero_fee_commitments: bool, pub metrics_enabled: bool, pub poll_metrics_interval: Option, pub metrics_username: Option, @@ -144,6 +145,7 @@ struct ConfigBuilder { pathfinding_scores_source_url: Option, probing: Option, async_payments_role: Option, + enable_zero_fee_commitments: Option, metrics_enabled: Option, poll_metrics_interval: Option, metrics_username: Option, @@ -167,6 +169,8 @@ impl ConfigBuilder { node.pathfinding_scores_source_url.or(self.pathfinding_scores_source_url.clone()); self.async_payments_role = node.async_payments_role.or(self.async_payments_role.clone()); + self.enable_zero_fee_commitments = + node.enable_zero_fee_commitments.or(self.enable_zero_fee_commitments); self.rgs_server_url = node.rgs_server_url.or(self.rgs_server_url.clone()); } @@ -286,6 +290,10 @@ impl ConfigBuilder { self.async_payments_role = Some(async_payments_role.clone()); } + if let Some(enable_zero_fee_commitments) = args.node_enable_zero_fee_commitments { + self.enable_zero_fee_commitments = Some(enable_zero_fee_commitments); + } + if args.has_probing_options() { let probing = self.probing.get_or_insert_with(ProbingTomlConfig::default); if let Some(probing_strategy) = &args.probing_strategy { @@ -595,6 +603,7 @@ impl ConfigBuilder { pathfinding_scores_source_url, probing_config, async_payments_role, + enable_zero_fee_commitments: self.enable_zero_fee_commitments.unwrap_or(false), metrics_enabled, poll_metrics_interval, metrics_username, @@ -633,6 +642,7 @@ struct NodeConfig { alias: Option, pathfinding_scores_source_url: Option, async_payments_role: Option, + enable_zero_fee_commitments: Option, rgs_server_url: Option, } @@ -1110,6 +1120,13 @@ pub struct ArgsConfig { )] node_async_payments_role: Option, + #[arg( + long, + env = "LDK_SERVER_NODE_ENABLE_ZERO_FEE_COMMITMENTS", + help = "Whether to enable zero-fee commitment channels. Defaults to false." + )] + node_enable_zero_fee_commitments: Option, + #[arg( long, env = "LDK_SERVER_PROBING_STRATEGY", @@ -1289,6 +1306,7 @@ mod tests { alias = "LDK Server" rgs_server_url = "https://rapidsync.lightningdevkit.org/snapshot/v2/" async_payments_role = "client" + enable_zero_fee_commitments = true [tls] cert_path = "/path/to/tls.crt" @@ -1348,6 +1366,7 @@ mod tests { node_alias: Some(String::from("LDK Server CLI")), pathfinding_scores_source_url: Some(String::from("https://example.com/")), node_async_payments_role: Some(String::from("server")), + node_enable_zero_fee_commitments: Some(false), probing_strategy: None, probing_top_node_count: None, probing_max_hops: None, @@ -1383,6 +1402,7 @@ mod tests { storage_dir_path: None, pathfinding_scores_source_url: None, node_async_payments_role: None, + node_enable_zero_fee_commitments: None, probing_strategy: None, probing_top_node_count: None, probing_max_hops: None, @@ -1497,6 +1517,7 @@ mod tests { pathfinding_scores_source_url: None, probing_config: None, async_payments_role: Some(AsyncPaymentsRole::Client), + enable_zero_fee_commitments: true, metrics_enabled: false, poll_metrics_interval: None, metrics_username: None, @@ -1522,6 +1543,7 @@ mod tests { assert_eq!(config.log_file_path, expected.log_file_path); assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url); assert!(matches!(config.async_payments_role, Some(AsyncPaymentsRole::Client))); + assert_eq!(config.enable_zero_fee_commitments, expected.enable_zero_fee_commitments); assert_eq!(config.metrics_enabled, expected.metrics_enabled); assert_eq!(config.tor_config, expected.tor_config); @@ -1939,6 +1961,7 @@ mod tests { pathfinding_scores_source_url: Some("https://example.com/".to_string()), probing_config: None, async_payments_role: Some(AsyncPaymentsRole::Server), + enable_zero_fee_commitments: false, metrics_enabled: false, poll_metrics_interval: None, metrics_username: None, @@ -1961,6 +1984,7 @@ mod tests { assert!(config.lsps2_service_config.is_none()); assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url); assert!(matches!(config.async_payments_role, Some(AsyncPaymentsRole::Server))); + assert_eq!(config.enable_zero_fee_commitments, expected.enable_zero_fee_commitments); assert_eq!(config.metrics_enabled, expected.metrics_enabled); assert_eq!(config.tor_config, expected.tor_config); assert_eq!(config.log_max_size_bytes, expected.log_max_size_bytes); @@ -2059,6 +2083,7 @@ mod tests { pathfinding_scores_source_url: Some("https://example.com/".to_string()), probing_config: None, async_payments_role: Some(AsyncPaymentsRole::Server), + enable_zero_fee_commitments: false, metrics_enabled: false, poll_metrics_interval: None, metrics_username: None, @@ -2085,6 +2110,7 @@ mod tests { assert_eq!(config.lsps2_service_config.is_some(), expected.lsps2_service_config.is_some()); assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url); assert!(matches!(config.async_payments_role, Some(AsyncPaymentsRole::Server))); + assert_eq!(config.enable_zero_fee_commitments, expected.enable_zero_fee_commitments); assert_eq!(config.metrics_enabled, expected.metrics_enabled); assert_eq!(config.tor_config, expected.tor_config); } @@ -2490,6 +2516,20 @@ mod tests { assert_eq!(args_config.probing_cooldown_secs, Some(1800)); } + #[test] + fn test_accepts_zero_fee_commitments_arg() { + for (value, expected) in [("true", true), ("false", false)] { + let args_config = ArgsConfig::try_parse_from([ + "ldk-server", + "--node-enable-zero-fee-commitments", + value, + ]) + .unwrap(); + + assert_eq!(args_config.node_enable_zero_fee_commitments, Some(expected)); + } + } + #[test] fn test_probing_args_override_strategy_specific_file_options() { let mut builder = ConfigBuilder {