Skip to content

Commit a329c4e

Browse files
authored
Merge pull request #208 from benthecarman/async-payments
Expose async payments role config
2 parents d1a0f9d + ddbb02d commit a329c4e

4 files changed

Lines changed: 57 additions & 3 deletions

File tree

contrib/ldk-server-config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ listening_addresses = ["localhost:9735"] # Lightning node listening address
77
alias = "ldk_server" # Lightning node alias
88
#pathfinding_scores_source_url = "" # External Pathfinding Scores Source
99
#rgs_server_url = "https://rapidsync.lightningdevkit.org/snapshot/v2/" # Optional: RGS URL for rapid gossip sync
10+
#async_payments_role = "client" # Optional async payments role: "client" or "server"
1011

1112
# Storage settings
1213
[storage.disk]

docs/configuration.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,13 @@ on macOS).
5151
### `[node]`
5252

5353
Core node settings: which Bitcoin network to use, Lightning peer listening and announcement
54-
addresses, the gRPC bind address, node alias, and optional Rapid Gossip Sync / pathfinding
55-
scores URLs.
54+
addresses, the gRPC bind address, node alias, optional Rapid Gossip Sync / pathfinding
55+
scores URLs, and the async payments role.
56+
57+
Set `async_payments_role = "client"` to ask peers to hold HTLCs where possible, allowing
58+
this node to go offline. Set `async_payments_role = "server"` to hold async payment HTLCs
59+
and onion messages for peers. The server role requires an announceable node configuration.
60+
Leave the field unset to disable async payments.
5661

5762
### `[storage.disk]`
5863

ldk-server/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ fn main() {
173173
builder.set_gossip_source_rgs(rgs_server_url);
174174
}
175175

176+
if let Err(e) = builder.set_async_payments_role(config_file.async_payments_role) {
177+
error!("Failed to configure async payments role: {e}");
178+
std::process::exit(-1);
179+
}
180+
176181
if let Some(lsps2_client_config) = config_file.lsps2_client_config {
177182
builder.set_liquidity_source_lsps2(
178183
lsps2_client_config.node_id,

ldk-server/src/util/config.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::{fs, io};
1515
use clap::Parser;
1616
use ldk_node::bitcoin::secp256k1::PublicKey;
1717
use ldk_node::bitcoin::Network;
18-
use ldk_node::config::{HRNResolverConfig, HumanReadableNamesConfig};
18+
use ldk_node::config::{AsyncPaymentsRole, HRNResolverConfig, HumanReadableNamesConfig};
1919
use ldk_node::lightning::ln::msgs::SocketAddress;
2020
use ldk_node::lightning::routing::gossip::NodeAlias;
2121
use ldk_node::liquidity::LSPS2ServiceConfig;
@@ -57,6 +57,7 @@ pub struct Config {
5757
pub log_level: LevelFilter,
5858
pub log_file_path: Option<String>,
5959
pub pathfinding_scores_source_url: Option<String>,
60+
pub async_payments_role: Option<AsyncPaymentsRole>,
6061
pub metrics_enabled: bool,
6162
pub poll_metrics_interval: Option<u64>,
6263
pub metrics_username: Option<String>,
@@ -111,6 +112,7 @@ struct ConfigBuilder {
111112
log_level: Option<String>,
112113
log_file_path: Option<String>,
113114
pathfinding_scores_source_url: Option<String>,
115+
async_payments_role: Option<String>,
114116
metrics_enabled: Option<bool>,
115117
poll_metrics_interval: Option<u64>,
116118
metrics_username: Option<String>,
@@ -132,6 +134,8 @@ impl ConfigBuilder {
132134
self.alias = node.alias.or(self.alias.clone());
133135
self.pathfinding_scores_source_url =
134136
node.pathfinding_scores_source_url.or(self.pathfinding_scores_source_url.clone());
137+
self.async_payments_role =
138+
node.async_payments_role.or(self.async_payments_role.clone());
135139
self.rgs_server_url = node.rgs_server_url.or(self.rgs_server_url.clone());
136140
}
137141

@@ -230,6 +234,10 @@ impl ConfigBuilder {
230234
self.pathfinding_scores_source_url = Some(pathfinding_scores_source_url.clone());
231235
}
232236

237+
if let Some(async_payments_role) = &args.node_async_payments_role {
238+
self.async_payments_role = Some(async_payments_role.clone());
239+
}
240+
233241
if args.metrics_enabled {
234242
self.metrics_enabled = Some(true);
235243
}
@@ -383,6 +391,9 @@ impl ConfigBuilder {
383391

384392
let pathfinding_scores_source_url = self.pathfinding_scores_source_url;
385393

394+
let async_payments_role =
395+
self.async_payments_role.as_deref().map(parse_async_payments_role).transpose()?;
396+
386397
let metrics_enabled = self.metrics_enabled.unwrap_or(false);
387398

388399
let poll_metrics_interval = self.poll_metrics_interval;
@@ -429,6 +440,7 @@ impl ConfigBuilder {
429440
log_level,
430441
log_file_path: self.log_file_path,
431442
pathfinding_scores_source_url,
443+
async_payments_role,
432444
metrics_enabled,
433445
poll_metrics_interval,
434446
metrics_username,
@@ -463,6 +475,7 @@ struct NodeConfig {
463475
grpc_service_address: Option<String>,
464476
alias: Option<String>,
465477
pathfinding_scores_source_url: Option<String>,
478+
async_payments_role: Option<String>,
466479
rgs_server_url: Option<String>,
467480
}
468481

@@ -606,6 +619,20 @@ fn parse_dns_server_address(addr: &str) -> io::Result<SocketAddress> {
606619
})
607620
}
608621

622+
fn parse_async_payments_role(role: &str) -> io::Result<AsyncPaymentsRole> {
623+
match role.trim().to_ascii_lowercase().as_str() {
624+
"client" => Ok(AsyncPaymentsRole::Client),
625+
"server" => Ok(AsyncPaymentsRole::Server),
626+
other => Err(io::Error::new(
627+
io::ErrorKind::InvalidInput,
628+
format!(
629+
"Invalid async payments role '{}' configured; expected 'client' or 'server'",
630+
other
631+
),
632+
)),
633+
}
634+
}
635+
609636
#[derive(Deserialize, Serialize)]
610637
struct LiquidityConfig {
611638
lsps2_client: Option<LSPSClientTomlConfig>,
@@ -768,6 +795,13 @@ pub struct ArgsConfig {
768795
)]
769796
pathfinding_scores_source_url: Option<String>,
770797

798+
#[arg(
799+
long,
800+
env = "LDK_SERVER_NODE_ASYNC_PAYMENTS_ROLE",
801+
help = "The async payments role for the node. Valid values are `client` or `server`."
802+
)]
803+
node_async_payments_role: Option<String>,
804+
771805
#[arg(
772806
long,
773807
env = "LDK_SERVER_METRICS_ENABLED",
@@ -884,6 +918,7 @@ mod tests {
884918
grpc_service_address = "127.0.0.1:3002"
885919
alias = "LDK Server"
886920
rgs_server_url = "https://rapidsync.lightningdevkit.org/snapshot/v2/"
921+
async_payments_role = "client"
887922
888923
[tls]
889924
cert_path = "/path/to/tls.crt"
@@ -936,6 +971,7 @@ mod tests {
936971
storage_dir_path: Some(String::from("/tmp_cli")),
937972
node_alias: Some(String::from("LDK Server CLI")),
938973
pathfinding_scores_source_url: Some(String::from("https://example.com/")),
974+
node_async_payments_role: Some(String::from("server")),
939975
metrics_enabled: false,
940976
poll_metrics_interval: None,
941977
metrics_username: None,
@@ -957,6 +993,7 @@ mod tests {
957993
bitcoind_rpc_password: None,
958994
storage_dir_path: None,
959995
pathfinding_scores_source_url: None,
996+
node_async_payments_role: None,
960997
metrics_enabled: false,
961998
poll_metrics_interval: None,
962999
metrics_username: None,
@@ -1030,6 +1067,7 @@ mod tests {
10301067
log_level: LevelFilter::Trace,
10311068
log_file_path: Some("/var/log/ldk-server.log".to_string()),
10321069
pathfinding_scores_source_url: None,
1070+
async_payments_role: Some(AsyncPaymentsRole::Client),
10331071
metrics_enabled: false,
10341072
poll_metrics_interval: None,
10351073
metrics_username: None,
@@ -1054,6 +1092,7 @@ mod tests {
10541092
assert_eq!(config.log_level, expected.log_level);
10551093
assert_eq!(config.log_file_path, expected.log_file_path);
10561094
assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url);
1095+
assert!(matches!(config.async_payments_role, Some(AsyncPaymentsRole::Client)));
10571096
assert_eq!(config.metrics_enabled, expected.metrics_enabled);
10581097
assert_eq!(config.tor_config, expected.tor_config);
10591098

@@ -1338,6 +1377,7 @@ mod tests {
13381377
log_level: LevelFilter::Trace,
13391378
log_file_path: Some("/var/log/ldk-server.log".to_string()),
13401379
pathfinding_scores_source_url: Some("https://example.com/".to_string()),
1380+
async_payments_role: Some(AsyncPaymentsRole::Server),
13411381
metrics_enabled: false,
13421382
poll_metrics_interval: None,
13431383
metrics_username: None,
@@ -1355,6 +1395,7 @@ mod tests {
13551395
assert_eq!(config.rgs_server_url, expected.rgs_server_url);
13561396
assert!(config.lsps2_service_config.is_none());
13571397
assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url);
1398+
assert!(matches!(config.async_payments_role, Some(AsyncPaymentsRole::Server)));
13581399
assert_eq!(config.metrics_enabled, expected.metrics_enabled);
13591400
assert_eq!(config.tor_config, expected.tor_config);
13601401
}
@@ -1446,6 +1487,7 @@ mod tests {
14461487
log_level: LevelFilter::Trace,
14471488
log_file_path: Some("/var/log/ldk-server.log".to_string()),
14481489
pathfinding_scores_source_url: Some("https://example.com/".to_string()),
1490+
async_payments_role: Some(AsyncPaymentsRole::Server),
14491491
metrics_enabled: false,
14501492
poll_metrics_interval: None,
14511493
metrics_username: None,
@@ -1467,6 +1509,7 @@ mod tests {
14671509
#[cfg(feature = "experimental-lsps2-support")]
14681510
assert_eq!(config.lsps2_service_config.is_some(), expected.lsps2_service_config.is_some());
14691511
assert_eq!(config.pathfinding_scores_source_url, expected.pathfinding_scores_source_url);
1512+
assert!(matches!(config.async_payments_role, Some(AsyncPaymentsRole::Server)));
14701513
assert_eq!(config.metrics_enabled, expected.metrics_enabled);
14711514
assert_eq!(config.tor_config, expected.tor_config);
14721515
}

0 commit comments

Comments
 (0)