Skip to content

Commit 9f2a292

Browse files
committed
Implement bLIP-32 resolver discovery via network graph
Adds logic to scan the network graph for nodes that support both DNS resolution and Onion Messaging. - Iterates through the network graph to find up to 5 candidate nodes. - Validates node features for DNS and Onion Message support. - Adds error handling and logging for missing BIP-353 requirements.
1 parent 8a4307d commit 9f2a292

5 files changed

Lines changed: 71 additions & 18 deletions

File tree

bindings/ldk_node.udl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ enum NodeError {
366366
"AsyncPaymentServicesDisabled",
367367
"HrnParsingFailed",
368368
"HrnResolverNotConfigured",
369+
"NoDnsResolvers",
369370
};
370371

371372
dictionary NodeStatus {

src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ pub enum Error {
133133
HrnParsingFailed,
134134
/// A HRN resolver was not configured.
135135
HrnResolverNotConfigured,
136+
/// No nodes in the network graph support both DNS resolution and onion messaging, which are required for bLIP-32.
137+
NoDnsResolvers,
136138
}
137139

138140
impl fmt::Display for Error {
@@ -218,6 +220,9 @@ impl fmt::Display for Error {
218220
Self::HrnResolverNotConfigured => {
219221
write!(f, "A HRN resolver was not configured.")
220222
},
223+
Self::NoDnsResolvers => {
224+
write!(f, "No nodes in the network graph support both DNS resolution and onion messaging, which are required for bLIP-32.")
225+
},
221226
}
222227
}
223228
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,7 @@ impl Node {
980980
Arc::clone(&self.config),
981981
Arc::clone(&self.logger),
982982
Arc::clone(&self.hrn_resolver),
983+
Arc::clone(&self.network_graph),
983984
)
984985
}
985986

@@ -1001,6 +1002,7 @@ impl Node {
10011002
Arc::clone(&self.config),
10021003
Arc::clone(&self.logger),
10031004
Arc::clone(&self.hrn_resolver),
1005+
Arc::clone(&self.network_graph),
10041006
))
10051007
}
10061008

src/payment/unified.rs

Lines changed: 56 additions & 17 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;
@@ -34,7 +35,7 @@ use crate::error::Error;
3435
use crate::ffi::maybe_wrap;
3536
use crate::logger::{log_error, LdkLogger, Logger};
3637
use crate::payment::{Bolt11Payment, Bolt12Payment, OnchainPayment};
37-
use crate::types::HRNResolver;
38+
use crate::types::{Graph, HRNResolver};
3839
use crate::Config;
3940

4041
type Uri<'a> = bip21::Uri<'a, NetworkChecked, Extras>;
@@ -69,15 +70,24 @@ pub struct UnifiedPayment {
6970
config: Arc<Config>,
7071
logger: Arc<Logger>,
7172
hrn_resolver: Arc<Option<Arc<HRNResolver>>>,
73+
network_graph: Arc<Graph>,
7274
}
7375

7476
impl UnifiedPayment {
7577
pub(crate) fn new(
7678
onchain_payment: Arc<OnchainPayment>, bolt11_invoice: Arc<Bolt11Payment>,
7779
bolt12_payment: Arc<Bolt12Payment>, config: Arc<Config>, logger: Arc<Logger>,
78-
hrn_resolver: Arc<Option<Arc<HRNResolver>>>,
80+
hrn_resolver: Arc<Option<Arc<HRNResolver>>>, network_graph: Arc<Graph>,
7981
) -> Self {
80-
Self { onchain_payment, bolt11_invoice, bolt12_payment, config, logger, hrn_resolver }
82+
Self {
83+
onchain_payment,
84+
bolt11_invoice,
85+
bolt12_payment,
86+
config,
87+
logger,
88+
hrn_resolver,
89+
network_graph,
90+
}
8191
}
8292

8393
/// Generates a URI with an on-chain address, [BOLT 11] invoice and [BOLT 12] offer.
@@ -165,25 +175,54 @@ impl UnifiedPayment {
165175
&self, uri_str: &str, amount_msat: Option<u64>,
166176
route_parameters: Option<RouteParametersConfig>, #[cfg(hrn_tests)] test_offer: &Offer,
167177
) -> Result<UnifiedPaymentResult, Error> {
168-
let resolver = self.hrn_resolver.as_ref().clone().ok_or_else(|| {
169-
log_error!(self.logger, "No HRN resolver configured. Cannot resolve HRNs.");
170-
Error::HrnResolverNotConfigured
171-
})?;
172-
178+
let resolver;
173179
let target_network;
174180

175-
target_network = if let Ok(_) = HumanReadableName::from_encoded(uri_str) {
176-
#[cfg(hrn_tests)]
177-
{
178-
bitcoin::Network::Bitcoin
181+
if let Ok(_) = HumanReadableName::from_encoded(uri_str) {
182+
resolver = self.hrn_resolver.as_ref().clone().ok_or_else(|| {
183+
log_error!(self.logger, "No HRN resolver configured. Cannot resolve HRNs.");
184+
Error::HrnResolverNotConfigured
185+
})?;
186+
187+
if let HRNResolver::Onion(_) = &*resolver {
188+
let dns_resolvers = {
189+
let mut resolvers = Vec::new();
190+
let network_graph = self.network_graph.read_only();
191+
192+
for (node_id, node) in network_graph.nodes().unordered_iter() {
193+
if let Some(info) = &node.announcement_info {
194+
let supports_dns = info.features().supports_dns_resolution();
195+
let supports_om = info.features().supports_onion_messages();
196+
if supports_dns && supports_om {
197+
resolvers.push(node_id.clone());
198+
}
199+
}
200+
if resolvers.len() > 5 {
201+
break;
202+
}
203+
}
204+
resolvers
205+
};
206+
207+
if dns_resolvers.is_empty() {
208+
log_error!(self.logger, "No nodes in the network graph support both DNS resolution and onion messaging, which are required for bLIP-32. Please ensure you have at least one such node in your network graph before making BIP-353 payments.");
209+
return Err(Error::NoDnsResolvers);
210+
}
179211
}
180-
#[cfg(not(hrn_tests))]
181-
{
182-
self.config.network
212+
target_network = {
213+
#[cfg(hrn_tests)]
214+
{
215+
bitcoin::Network::Bitcoin
216+
}
217+
#[cfg(not(hrn_tests))]
218+
{
219+
self.config.network
220+
}
183221
}
184222
} else {
185-
self.config.network
186-
};
223+
resolver = Arc::new(HRNResolver::Dummy(DummyHrnResolver));
224+
target_network = self.config.network;
225+
}
187226

188227
let parse_fut =
189228
PaymentInstructions::parse(uri_str, target_network, resolver.as_ref(), false);

src/types.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex};
1313
use bitcoin_payment_instructions::amount::Amount;
1414
use bitcoin_payment_instructions::dns_resolver::DNSHrnResolver;
1515
use bitcoin_payment_instructions::hrn_resolution::{
16-
HrnResolutionFuture, HrnResolver, HumanReadableName, LNURLResolutionFuture,
16+
DummyHrnResolver, HrnResolutionFuture, HrnResolver, HumanReadableName, LNURLResolutionFuture,
1717
};
1818
use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver;
1919

@@ -304,20 +304,23 @@ pub(crate) type OnionMessenger = lightning::onion_message::messenger::OnionMesse
304304
pub enum HRNResolver {
305305
Onion(Arc<LDKOnionMessageDNSSECHrnResolver<Arc<Graph>, Arc<Logger>>>),
306306
Local(Arc<DNSHrnResolver>),
307+
Dummy(DummyHrnResolver),
307308
}
308309

309310
impl HrnResolver for HRNResolver {
310311
fn resolve_hrn<'a>(&'a self, hrn: &'a HumanReadableName) -> HrnResolutionFuture<'a> {
311312
match self {
312313
HRNResolver::Onion(inner) => inner.resolve_hrn(hrn),
313314
HRNResolver::Local(inner) => inner.resolve_hrn(hrn),
315+
HRNResolver::Dummy(inner) => inner.resolve_hrn(hrn),
314316
}
315317
}
316318

317319
fn resolve_lnurl<'a>(&'a self, url: &'a str) -> HrnResolutionFuture<'a> {
318320
match self {
319321
HRNResolver::Onion(inner) => inner.resolve_lnurl(url),
320322
HRNResolver::Local(inner) => inner.resolve_lnurl(url),
323+
HRNResolver::Dummy(inner) => inner.resolve_lnurl(url),
321324
}
322325
}
323326

@@ -331,6 +334,9 @@ impl HrnResolver for HRNResolver {
331334
HRNResolver::Local(inner) => {
332335
inner.resolve_lnurl_to_invoice(callback_url, amount, expected_description_hash)
333336
},
337+
HRNResolver::Dummy(inner) => {
338+
inner.resolve_lnurl_to_invoice(callback_url, amount, expected_description_hash)
339+
},
334340
}
335341
}
336342
}

0 commit comments

Comments
 (0)