Skip to content

Commit f9c3c56

Browse files
committed
Pass HRNResolver or DomainResolver into OnionMessenger
Inject specialized resolution capabilities into OnionMessenger to support outbound payments and third-party resolution services. This change refines the previous resolution logic by allowing the node to act as a robust BIP 353 participant. If configured as a service provider, the node utilizes a Domain Resolver to handle requests for other participants. Otherwise, it uses an HRN Resolver specifically for initiating its own outbound payments. Providing these as optional parameters in the Node constructor ensures the logic matches the node's designated role in the ecosystem.
1 parent edbb5ba commit f9c3c56

File tree

5 files changed

+155
-28
lines changed

5 files changed

+155
-28
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ default = []
3838
#lightning-transaction-sync = { version = "0.2.0", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
3939
#lightning-liquidity = { version = "0.2.0", features = ["std"] }
4040
#lightning-macros = { version = "0.2.0" }
41+
#lightning-dns-resolver = { version = "0.3.0" }
4142

4243
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "98393b3de3d8aec897e9ab783cb2418da504e204", features = ["std"] }
4344
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "98393b3de3d8aec897e9ab783cb2418da504e204" }
@@ -50,6 +51,7 @@ lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightnin
5051
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "98393b3de3d8aec897e9ab783cb2418da504e204", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
5152
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "98393b3de3d8aec897e9ab783cb2418da504e204", features = ["std"] }
5253
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "98393b3de3d8aec897e9ab783cb2418da504e204" }
54+
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "98393b3de3d8aec897e9ab783cb2418da504e204" }
5355

5456
bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] }
5557
bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]}
@@ -143,6 +145,7 @@ harness = false
143145
#lightning-transaction-sync = { path = "../rust-lightning/lightning-transaction-sync" }
144146
#lightning-liquidity = { path = "../rust-lightning/lightning-liquidity" }
145147
#lightning-macros = { path = "../rust-lightning/lightning-macros" }
148+
#lightning-dns-resolver = { path = "../rust-lightning/lightning-dns-resolver" }
146149

147150
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
148151
#lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
@@ -155,6 +158,7 @@ harness = false
155158
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
156159
#lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
157160
#lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
161+
#lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
158162

159163
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
160164
#lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
@@ -167,6 +171,7 @@ harness = false
167171
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
168172
#lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
169173
#lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
174+
#lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
170175

171176
#vss-client-ng = { path = "../vss-client" }
172177
#vss-client-ng = { git = "https://github.com/lightningdevkit/vss-client", branch = "main" }
@@ -183,3 +188,4 @@ harness = false
183188
#lightning-transaction-sync = { path = "../rust-lightning/lightning-transaction-sync" }
184189
#lightning-liquidity = { path = "../rust-lightning/lightning-liquidity" }
185190
#lightning-macros = { path = "../rust-lightning/lightning-macros" }
191+
#lightning-dns-resolver = { path = "../rust-lightning/lightning-dns-resolver" }

src/builder.rs

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::collections::HashMap;
99
use std::convert::TryInto;
1010
use std::default::Default;
1111
use std::path::PathBuf;
12-
use std::sync::{Arc, Mutex, Once, RwLock};
12+
use std::sync::{Arc, Mutex, Once, RwLock, Weak};
1313
use std::time::SystemTime;
1414
use std::{fmt, fs};
1515

@@ -19,12 +19,13 @@ use bitcoin::bip32::{ChildNumber, Xpriv};
1919
use bitcoin::key::Secp256k1;
2020
use bitcoin::secp256k1::PublicKey;
2121
use bitcoin::{BlockHash, Network};
22+
use bitcoin_payment_instructions::dns_resolver::DNSHrnResolver;
2223
use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver;
2324
use lightning::chain::{chainmonitor, BestBlock};
2425
use lightning::ln::channelmanager::{self, ChainParameters, ChannelManagerReadArgs};
2526
use lightning::ln::msgs::{RoutingMessageHandler, SocketAddress};
2627
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
27-
use lightning::log_trace;
28+
use lightning::onion_message::dns_resolution::DNSResolverMessageHandler;
2829
use lightning::routing::gossip::NodeAlias;
2930
use lightning::routing::router::DefaultRouter;
3031
use lightning::routing::scoring::{
@@ -39,14 +40,16 @@ use lightning::util::persist::{
3940
};
4041
use lightning::util::ser::ReadableArgs;
4142
use lightning::util::sweep::OutputSweeper;
43+
use lightning::{log_trace, log_warn};
44+
use lightning_dns_resolver::OMDomainResolver;
4245
use lightning_persister::fs_store::v1::FilesystemStore;
4346
use vss_client::headers::VssHeaderProvider;
4447

4548
use crate::chain::ChainSource;
4649
use crate::config::{
4750
default_user_config, may_announce_channel, AnnounceError, AsyncPaymentsRole,
48-
BitcoindRestClientConfig, Config, ElectrumSyncConfig, EsploraSyncConfig, TorConfig,
49-
DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL,
51+
BitcoindRestClientConfig, Config, ElectrumSyncConfig, EsploraSyncConfig, HRNResolverConfig,
52+
TorConfig, DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL,
5053
};
5154
use crate::connection::ConnectionManager;
5255
use crate::entropy::NodeEntropy;
@@ -77,8 +80,8 @@ use crate::runtime::{Runtime, RuntimeSpawner};
7780
use crate::tx_broadcaster::TransactionBroadcaster;
7881
use crate::types::{
7982
AsyncPersister, ChainMonitor, ChannelManager, DynStore, DynStoreWrapper, GossipSync, Graph,
80-
KeysManager, MessageRouter, OnionMessenger, PaymentStore, PeerManager, PendingPaymentStore,
81-
Persister, SyncAndAsyncKVStore,
83+
HRNResolver, KeysManager, MessageRouter, OnionMessenger, PaymentStore, PeerManager,
84+
PendingPaymentStore, Persister, SyncAndAsyncKVStore,
8285
};
8386
use crate::wallet::persist::KVStoreWalletPersister;
8487
use crate::wallet::Wallet;
@@ -193,6 +196,8 @@ pub enum BuildError {
193196
NetworkMismatch,
194197
/// The role of the node in an asynchronous payments context is not compatible with the current configuration.
195198
AsyncPaymentsConfigMismatch,
199+
/// An attempt to setup a DNS Resolver failed.
200+
DNSResolverSetupFailed,
196201
}
197202

198203
impl fmt::Display for BuildError {
@@ -226,6 +231,9 @@ impl fmt::Display for BuildError {
226231
"The async payments role is not compatible with the current configuration."
227232
)
228233
},
234+
Self::DNSResolverSetupFailed => {
235+
write!(f, "An attempt to setup a DNS resolver has failed.")
236+
},
229237
}
230238
}
231239
}
@@ -1651,7 +1659,75 @@ fn build_with_store_internal(
16511659
})?;
16521660
}
16531661

1654-
let hrn_resolver = Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph)));
1662+
// This hook resolves a circular dependency:
1663+
// 1. PeerManager requires OnionMessenger (via MessageHandler).
1664+
// 2. OnionMessenger (via HRN resolver) needs to call PeerManager::process_events.
1665+
//
1666+
// We provide the resolver with a Weak pointer via this Mutex-protected "hook."
1667+
// This allows us to initialize the resolver before the PeerManager exists,
1668+
// and prevents a reference cycle (memory leak).
1669+
let peer_manager_hook: Arc<Mutex<Option<Weak<PeerManager>>>> = Arc::new(Mutex::new(None));
1670+
let hrn_resolver;
1671+
1672+
let runtime_handle = runtime.handle();
1673+
1674+
let om_resolver: Arc<dyn DNSResolverMessageHandler + Send + Sync> = match &config
1675+
.hrn_config
1676+
.resolution_config
1677+
{
1678+
HRNResolverConfig::Blip32 => {
1679+
let hrn_res =
1680+
Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph)));
1681+
hrn_resolver = HRNResolver::Onion(Arc::clone(&hrn_res));
1682+
1683+
// We clone the hook because it's moved into a Send + Sync closure that outlives this scope.
1684+
let pm_hook_clone = Arc::clone(&peer_manager_hook);
1685+
hrn_res.register_post_queue_action(Box::new(move || {
1686+
if let Ok(guard) = pm_hook_clone.lock() {
1687+
if let Some(pm) = guard.as_ref().and_then(|weak| weak.upgrade()) {
1688+
pm.process_events();
1689+
}
1690+
}
1691+
}));
1692+
hrn_res as Arc<dyn DNSResolverMessageHandler + Send + Sync>
1693+
},
1694+
HRNResolverConfig::Dns { dns_server_address, enable_hrn_resolution_service, .. } => {
1695+
let addr = dns_server_address.parse().map_err(|_| {
1696+
log_error!(logger, "Failed to parse DNS server address: {}", dns_server_address);
1697+
BuildError::DNSResolverSetupFailed
1698+
})?;
1699+
1700+
if *enable_hrn_resolution_service && may_announce_channel(&config).is_ok() {
1701+
let hrn_res = Arc::new(DNSHrnResolver(addr));
1702+
hrn_resolver = HRNResolver::Local(hrn_res);
1703+
1704+
Arc::new(OMDomainResolver::<IgnoringMessageHandler>::with_runtime(
1705+
addr,
1706+
None,
1707+
Some(runtime_handle.clone()),
1708+
)) as Arc<dyn DNSResolverMessageHandler + Send + Sync>
1709+
} else {
1710+
if *enable_hrn_resolution_service {
1711+
log_warn!(logger, "Unable to act as an HRN resolution service. To act as an HRN resolution service, the node must be configured to announce channels.");
1712+
}
1713+
1714+
// Fallback/Default: Onion resolver
1715+
let hrn_res =
1716+
Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph)));
1717+
hrn_resolver = HRNResolver::Onion(Arc::clone(&hrn_res));
1718+
1719+
let pm_hook_clone = Arc::clone(&peer_manager_hook);
1720+
hrn_res.register_post_queue_action(Box::new(move || {
1721+
if let Ok(guard) = pm_hook_clone.lock() {
1722+
if let Some(pm) = guard.as_ref().and_then(|weak| weak.upgrade()) {
1723+
pm.process_events();
1724+
}
1725+
}
1726+
}));
1727+
hrn_res as Arc<dyn DNSResolverMessageHandler + Send + Sync>
1728+
}
1729+
},
1730+
};
16551731

16561732
// Initialize the PeerManager
16571733
let onion_messenger: Arc<OnionMessenger> =
@@ -1664,7 +1740,7 @@ fn build_with_store_internal(
16641740
message_router,
16651741
Arc::clone(&channel_manager),
16661742
Arc::clone(&channel_manager),
1667-
Arc::clone(&hrn_resolver),
1743+
Arc::clone(&om_resolver),
16681744
IgnoringMessageHandler {},
16691745
))
16701746
} else {
@@ -1676,7 +1752,7 @@ fn build_with_store_internal(
16761752
message_router,
16771753
Arc::clone(&channel_manager),
16781754
Arc::clone(&channel_manager),
1679-
Arc::clone(&hrn_resolver),
1755+
Arc::clone(&om_resolver),
16801756
IgnoringMessageHandler {},
16811757
))
16821758
};
@@ -1808,12 +1884,7 @@ fn build_with_store_internal(
18081884
Arc::clone(&keys_manager),
18091885
));
18101886

1811-
let peer_manager_clone = Arc::downgrade(&peer_manager);
1812-
hrn_resolver.register_post_queue_action(Box::new(move || {
1813-
if let Some(upgraded_pointer) = peer_manager_clone.upgrade() {
1814-
upgraded_pointer.process_events();
1815-
}
1816-
}));
1887+
*peer_manager_hook.lock().unwrap() = Some(Arc::downgrade(&peer_manager));
18171888

18181889
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
18191890

@@ -1927,7 +1998,7 @@ fn build_with_store_internal(
19271998
node_metrics,
19281999
om_mailbox,
19292000
async_payments_role,
1930-
hrn_resolver,
2001+
hrn_resolver: Arc::new(hrn_resolver),
19312002
#[cfg(cycle_tests)]
19322003
_leak_checker,
19332004
})

src/payment/unified.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use bip21::{DeserializationError, DeserializeParams, Param, SerializeParams};
2323
use bitcoin::address::NetworkChecked;
2424
use bitcoin::{Amount, Txid};
2525
use bitcoin_payment_instructions::amount::Amount as BPIAmount;
26+
use bitcoin_payment_instructions::hrn_resolution::DummyHrnResolver;
2627
use bitcoin_payment_instructions::{PaymentInstructions, PaymentMethod};
2728
use lightning::ln::channelmanager::PaymentId;
2829
use lightning::offers::offer::Offer;
@@ -165,12 +166,16 @@ impl UnifiedPayment {
165166
&self, uri_str: &str, amount_msat: Option<u64>,
166167
route_parameters: Option<RouteParametersConfig>,
167168
) -> Result<UnifiedPaymentResult, Error> {
168-
let parse_fut = PaymentInstructions::parse(
169-
uri_str,
170-
self.config.network,
171-
self.hrn_resolver.as_ref(),
172-
false,
173-
);
169+
let resolver;
170+
171+
if let Ok(_) = HumanReadableName::from_encoded(uri_str) {
172+
resolver = Arc::clone(&self.hrn_resolver);
173+
} else {
174+
resolver = Arc::new(HRNResolver::Dummy(DummyHrnResolver));
175+
}
176+
177+
let parse_fut =
178+
PaymentInstructions::parse(uri_str, self.config.network, resolver.as_ref(), false);
174179

175180
let instructions =
176181
tokio::time::timeout(Duration::from_secs(HRN_RESOLUTION_TIMEOUT_SECS), parse_fut)
@@ -196,7 +201,7 @@ impl UnifiedPayment {
196201
Error::InvalidAmount
197202
})?;
198203

199-
let fut = instr.set_amount(amt, self.hrn_resolver.as_ref());
204+
let fut = instr.set_amount(amt, &*resolver);
200205

201206
tokio::time::timeout(Duration::from_secs(HRN_RESOLUTION_TIMEOUT_SECS), fut)
202207
.await
@@ -235,7 +240,6 @@ impl UnifiedPayment {
235240
match method {
236241
PaymentMethod::LightningBolt12(offer) => {
237242
let offer = maybe_wrap(offer.clone());
238-
239243
let payment_result = if let Ok(hrn) = HumanReadableName::from_encoded(uri_str) {
240244
let hrn = maybe_wrap(hrn.clone());
241245
self.bolt12_payment.send_using_amount_inner(&offer, amount_msat.unwrap_or(0), None, None, route_parameters, Some(hrn))

src/runtime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl Runtime {
208208
);
209209
}
210210

211-
fn handle(&self) -> &tokio::runtime::Handle {
211+
pub(crate) fn handle(&self) -> &tokio::runtime::Handle {
212212
match &self.mode {
213213
RuntimeMode::Owned(rt) => rt.handle(),
214214
RuntimeMode::Handle(handle) => handle,

src/types.rs

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@ use std::future::Future;
1010
use std::pin::Pin;
1111
use std::sync::{Arc, Mutex};
1212

13+
use bitcoin_payment_instructions::amount::Amount;
14+
use bitcoin_payment_instructions::dns_resolver::DNSHrnResolver;
15+
use bitcoin_payment_instructions::hrn_resolution::{
16+
DummyHrnResolver, HrnResolutionFuture, HrnResolver, HumanReadableName, LNURLResolutionFuture,
17+
};
18+
use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver;
19+
1320
use bitcoin::secp256k1::PublicKey;
1421
use bitcoin::{OutPoint, ScriptBuf};
15-
use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver;
22+
1623
use lightning::chain::chainmonitor;
1724
use lightning::impl_writeable_tlv_based;
1825
use lightning::ln::channel_state::ChannelDetails as LdkChannelDetails;
1926
use lightning::ln::msgs::{RoutingMessageHandler, SocketAddress};
2027
use lightning::ln::peer_handler::IgnoringMessageHandler;
2128
use lightning::ln::types::ChannelId;
29+
use lightning::onion_message::dns_resolution::DNSResolverMessageHandler;
2230
use lightning::routing::gossip;
2331
use lightning::routing::router::DefaultRouter;
2432
use lightning::routing::scoring::{CombinedScorer, ProbabilisticScoringFeeParameters};
@@ -289,11 +297,49 @@ pub(crate) type OnionMessenger = lightning::onion_message::messenger::OnionMesse
289297
Arc<MessageRouter>,
290298
Arc<ChannelManager>,
291299
Arc<ChannelManager>,
292-
Arc<HRNResolver>,
300+
Arc<dyn DNSResolverMessageHandler + Sync + Send>,
293301
IgnoringMessageHandler,
294302
>;
295303

296-
pub(crate) type HRNResolver = LDKOnionMessageDNSSECHrnResolver<Arc<Graph>, Arc<Logger>>;
304+
pub enum HRNResolver {
305+
Onion(Arc<LDKOnionMessageDNSSECHrnResolver<Arc<Graph>, Arc<Logger>>>),
306+
Local(Arc<DNSHrnResolver>),
307+
Dummy(DummyHrnResolver),
308+
}
309+
310+
impl HrnResolver for HRNResolver {
311+
fn resolve_hrn<'a>(&'a self, hrn: &'a HumanReadableName) -> HrnResolutionFuture<'a> {
312+
match self {
313+
HRNResolver::Onion(inner) => inner.resolve_hrn(hrn),
314+
HRNResolver::Local(inner) => inner.resolve_hrn(hrn),
315+
HRNResolver::Dummy(inner) => inner.resolve_hrn(hrn),
316+
}
317+
}
318+
319+
fn resolve_lnurl<'a>(&'a self, url: &'a str) -> HrnResolutionFuture<'a> {
320+
match self {
321+
HRNResolver::Onion(inner) => inner.resolve_lnurl(url),
322+
HRNResolver::Local(inner) => inner.resolve_lnurl(url),
323+
HRNResolver::Dummy(inner) => inner.resolve_lnurl(url),
324+
}
325+
}
326+
327+
fn resolve_lnurl_to_invoice<'a>(
328+
&'a self, callback_url: String, amount: Amount, expected_description_hash: [u8; 32],
329+
) -> LNURLResolutionFuture<'a> {
330+
match self {
331+
HRNResolver::Onion(inner) => {
332+
inner.resolve_lnurl_to_invoice(callback_url, amount, expected_description_hash)
333+
},
334+
HRNResolver::Local(inner) => {
335+
inner.resolve_lnurl_to_invoice(callback_url, amount, expected_description_hash)
336+
},
337+
HRNResolver::Dummy(inner) => {
338+
inner.resolve_lnurl_to_invoice(callback_url, amount, expected_description_hash)
339+
},
340+
}
341+
}
342+
}
297343

298344
pub(crate) type MessageRouter = lightning::onion_message::messenger::DefaultMessageRouter<
299345
Arc<Graph>,

0 commit comments

Comments
 (0)