Skip to content

Commit f2d6fb7

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 41d0fd6 commit f2d6fb7

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 = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std"] }
4344
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
@@ -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 = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
5152
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7", features = ["std"] }
5253
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
54+
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "dcf0c203e166da2348bef12b2e5eff4a250cdec7" }
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"]}
@@ -144,6 +146,7 @@ harness = false
144146
#lightning-transaction-sync = { path = "../rust-lightning/lightning-transaction-sync" }
145147
#lightning-liquidity = { path = "../rust-lightning/lightning-liquidity" }
146148
#lightning-macros = { path = "../rust-lightning/lightning-macros" }
149+
#lightning-dns-resolver = { path = "../rust-lightning/lightning-dns-resolver" }
147150

148151
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
149152
#lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
@@ -156,6 +159,7 @@ harness = false
156159
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
157160
#lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
158161
#lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
162+
#lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", branch = "main" }
159163

160164
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
161165
#lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
@@ -168,6 +172,7 @@ harness = false
168172
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
169173
#lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
170174
#lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
175+
#lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "21e9a9c0ef80021d0669f2a366f55d08ba8d9b03" }
171176

172177
#vss-client-ng = { path = "../vss-client" }
173178
#vss-client-ng = { git = "https://github.com/lightningdevkit/vss-client", branch = "main" }
@@ -184,3 +189,4 @@ harness = false
184189
#lightning-transaction-sync = { path = "../rust-lightning/lightning-transaction-sync" }
185190
#lightning-liquidity = { path = "../rust-lightning/lightning-liquidity" }
186191
#lightning-macros = { path = "../rust-lightning/lightning-macros" }
192+
#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, DynStoreRef, DynStoreWrapper,
80-
GossipSync, Graph, KeysManager, MessageRouter, OnionMessenger, PaymentStore, PeerManager,
81-
PendingPaymentStore, SyncAndAsyncKVStore,
83+
GossipSync, Graph, HRNResolver, KeysManager, MessageRouter, OnionMessenger, PaymentStore,
84+
PeerManager, PendingPaymentStore, 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
}
@@ -1717,7 +1725,75 @@ fn build_with_store_internal(
17171725
})?;
17181726
}
17191727

1720-
let hrn_resolver = Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph)));
1728+
// This hook resolves a circular dependency:
1729+
// 1. PeerManager requires OnionMessenger (via MessageHandler).
1730+
// 2. OnionMessenger (via HRN resolver) needs to call PeerManager::process_events.
1731+
//
1732+
// We provide the resolver with a Weak pointer via this Mutex-protected "hook."
1733+
// This allows us to initialize the resolver before the PeerManager exists,
1734+
// and prevents a reference cycle (memory leak).
1735+
let peer_manager_hook: Arc<Mutex<Option<Weak<PeerManager>>>> = Arc::new(Mutex::new(None));
1736+
let hrn_resolver;
1737+
1738+
let runtime_handle = runtime.handle();
1739+
1740+
let om_resolver: Arc<dyn DNSResolverMessageHandler + Send + Sync> = match &config
1741+
.hrn_config
1742+
.resolution_config
1743+
{
1744+
HRNResolverConfig::Blip32 => {
1745+
let hrn_res =
1746+
Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph)));
1747+
hrn_resolver = HRNResolver::Onion(Arc::clone(&hrn_res));
1748+
1749+
// We clone the hook because it's moved into a Send + Sync closure that outlives this scope.
1750+
let pm_hook_clone = Arc::clone(&peer_manager_hook);
1751+
hrn_res.register_post_queue_action(Box::new(move || {
1752+
if let Ok(guard) = pm_hook_clone.lock() {
1753+
if let Some(pm) = guard.as_ref().and_then(|weak| weak.upgrade()) {
1754+
pm.process_events();
1755+
}
1756+
}
1757+
}));
1758+
hrn_res as Arc<dyn DNSResolverMessageHandler + Send + Sync>
1759+
},
1760+
HRNResolverConfig::Dns { dns_server_address, enable_hrn_resolution_service, .. } => {
1761+
let addr = dns_server_address.parse().map_err(|_| {
1762+
log_error!(logger, "Failed to parse DNS server address: {}", dns_server_address);
1763+
BuildError::DNSResolverSetupFailed
1764+
})?;
1765+
1766+
if *enable_hrn_resolution_service && may_announce_channel(&config).is_ok() {
1767+
let hrn_res = Arc::new(DNSHrnResolver(addr));
1768+
hrn_resolver = HRNResolver::Local(hrn_res);
1769+
1770+
Arc::new(OMDomainResolver::<IgnoringMessageHandler>::with_runtime(
1771+
addr,
1772+
None,
1773+
Some(runtime_handle.clone()),
1774+
)) as Arc<dyn DNSResolverMessageHandler + Send + Sync>
1775+
} else {
1776+
if *enable_hrn_resolution_service {
1777+
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.");
1778+
}
1779+
1780+
// Fallback/Default: Onion resolver
1781+
let hrn_res =
1782+
Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph)));
1783+
hrn_resolver = HRNResolver::Onion(Arc::clone(&hrn_res));
1784+
1785+
let pm_hook_clone = Arc::clone(&peer_manager_hook);
1786+
hrn_res.register_post_queue_action(Box::new(move || {
1787+
if let Ok(guard) = pm_hook_clone.lock() {
1788+
if let Some(pm) = guard.as_ref().and_then(|weak| weak.upgrade()) {
1789+
pm.process_events();
1790+
}
1791+
}
1792+
}));
1793+
hrn_res as Arc<dyn DNSResolverMessageHandler + Send + Sync>
1794+
}
1795+
},
1796+
};
17211797

17221798
// Initialize the PeerManager
17231799
let onion_messenger: Arc<OnionMessenger> =
@@ -1730,7 +1806,7 @@ fn build_with_store_internal(
17301806
message_router,
17311807
Arc::clone(&channel_manager),
17321808
Arc::clone(&channel_manager),
1733-
Arc::clone(&hrn_resolver),
1809+
Arc::clone(&om_resolver),
17341810
IgnoringMessageHandler {},
17351811
))
17361812
} else {
@@ -1742,7 +1818,7 @@ fn build_with_store_internal(
17421818
message_router,
17431819
Arc::clone(&channel_manager),
17441820
Arc::clone(&channel_manager),
1745-
Arc::clone(&hrn_resolver),
1821+
Arc::clone(&om_resolver),
17461822
IgnoringMessageHandler {},
17471823
))
17481824
};
@@ -1873,12 +1949,7 @@ fn build_with_store_internal(
18731949
Arc::clone(&keys_manager),
18741950
));
18751951

1876-
let peer_manager_clone = Arc::downgrade(&peer_manager);
1877-
hrn_resolver.register_post_queue_action(Box::new(move || {
1878-
if let Some(upgraded_pointer) = peer_manager_clone.upgrade() {
1879-
upgraded_pointer.process_events();
1880-
}
1881-
}));
1952+
*peer_manager_hook.lock().unwrap() = Some(Arc::downgrade(&peer_manager));
18821953

18831954
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
18841955

@@ -1992,7 +2063,7 @@ fn build_with_store_internal(
19922063
node_metrics,
19932064
om_mailbox,
19942065
async_payments_role,
1995-
hrn_resolver,
2066+
hrn_resolver: Arc::new(hrn_resolver),
19962067
#[cfg(cycle_tests)]
19972068
_leak_checker,
19982069
})

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, ChannelShutdownState};
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};
@@ -318,11 +326,49 @@ pub(crate) type OnionMessenger = lightning::onion_message::messenger::OnionMesse
318326
Arc<MessageRouter>,
319327
Arc<ChannelManager>,
320328
Arc<ChannelManager>,
321-
Arc<HRNResolver>,
329+
Arc<dyn DNSResolverMessageHandler + Sync + Send>,
322330
IgnoringMessageHandler,
323331
>;
324332

325-
pub(crate) type HRNResolver = LDKOnionMessageDNSSECHrnResolver<Arc<Graph>, Arc<Logger>>;
333+
pub enum HRNResolver {
334+
Onion(Arc<LDKOnionMessageDNSSECHrnResolver<Arc<Graph>, Arc<Logger>>>),
335+
Local(Arc<DNSHrnResolver>),
336+
Dummy(DummyHrnResolver),
337+
}
338+
339+
impl HrnResolver for HRNResolver {
340+
fn resolve_hrn<'a>(&'a self, hrn: &'a HumanReadableName) -> HrnResolutionFuture<'a> {
341+
match self {
342+
HRNResolver::Onion(inner) => inner.resolve_hrn(hrn),
343+
HRNResolver::Local(inner) => inner.resolve_hrn(hrn),
344+
HRNResolver::Dummy(inner) => inner.resolve_hrn(hrn),
345+
}
346+
}
347+
348+
fn resolve_lnurl<'a>(&'a self, url: &'a str) -> HrnResolutionFuture<'a> {
349+
match self {
350+
HRNResolver::Onion(inner) => inner.resolve_lnurl(url),
351+
HRNResolver::Local(inner) => inner.resolve_lnurl(url),
352+
HRNResolver::Dummy(inner) => inner.resolve_lnurl(url),
353+
}
354+
}
355+
356+
fn resolve_lnurl_to_invoice<'a>(
357+
&'a self, callback_url: String, amount: Amount, expected_description_hash: [u8; 32],
358+
) -> LNURLResolutionFuture<'a> {
359+
match self {
360+
HRNResolver::Onion(inner) => {
361+
inner.resolve_lnurl_to_invoice(callback_url, amount, expected_description_hash)
362+
},
363+
HRNResolver::Local(inner) => {
364+
inner.resolve_lnurl_to_invoice(callback_url, amount, expected_description_hash)
365+
},
366+
HRNResolver::Dummy(inner) => {
367+
inner.resolve_lnurl_to_invoice(callback_url, amount, expected_description_hash)
368+
},
369+
}
370+
}
371+
}
326372

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

0 commit comments

Comments
 (0)