Skip to content

Commit 5a00c7c

Browse files
committed
fixup! Add support for sending to human-readable names that resolve to Bolt12 Offers
1 parent 60578ef commit 5a00c7c

File tree

5 files changed

+100
-18
lines changed

5 files changed

+100
-18
lines changed

bindings/ldk_node.udl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ interface Bolt12Payment {
200200
[Throws=NodeError]
201201
PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note);
202202
[Throws=NodeError]
203-
PaymentId send_to_human_readable_name([ByRef]string name, u64 amount_msat);
203+
PaymentId send_to_human_readable_name(HumanReadableName hrn, u64 amount_msat);
204204
[Throws=NodeError]
205205
Offer receive(u64 amount_msat, [ByRef]string description, u32? expiry_secs, u64? quantity);
206206
[Throws=NodeError]
@@ -253,6 +253,13 @@ interface LSPS1Liquidity {
253253
LSPS1OrderStatus check_order_status(OrderId order_id);
254254
};
255255

256+
interface HumanReadableName {
257+
[Name=from_encoded]
258+
constructor([ByRef] string encoded);
259+
string user();
260+
string domain();
261+
};
262+
256263
[Error]
257264
enum NodeError {
258265
"AlreadyRunning",

src/event.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,8 +1417,20 @@ where
14171417
);
14181418
}
14191419
},
1420-
LdkEvent::InvoiceReceived { .. } => {
1421-
debug_assert!(false, "We currently don't handle BOLT12 invoices manually, so this event should never be emitted.");
1420+
LdkEvent::InvoiceReceived { payment_id, invoice, context: _, responder: _ } => {
1421+
let update = PaymentDetailsUpdate {
1422+
hash: Some(Some(invoice.payment_hash())),
1423+
quantity: invoice.quantity(),
1424+
..PaymentDetailsUpdate::new(payment_id)
1425+
};
1426+
1427+
match self.payment_store.update(&update) {
1428+
Ok(_) => {},
1429+
Err(e) => {
1430+
log_error!(self.logger, "Failed to access payment store: {}", e);
1431+
return Err(ReplayEvent());
1432+
},
1433+
};
14221434
},
14231435
LdkEvent::ConnectionNeeded { node_id, addresses } => {
14241436
let runtime_lock = self.runtime.read().unwrap();

src/payment/bolt12.rs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use lightning::offers::invoice::Bolt12Invoice;
2020
use lightning::offers::offer::{Amount, Offer, Quantity};
2121
use lightning::offers::parse::Bolt12SemanticError;
2222
use lightning::offers::refund::Refund;
23-
use lightning::onion_message::dns_resolution::HumanReadableName;
23+
use lightning::onion_message::dns_resolution::HumanReadableName as LdkHumanReadableName;
2424
use lightning::onion_message::messenger::Destination;
2525
use lightning::util::string::UntrustedString;
2626

@@ -30,6 +30,20 @@ use std::num::NonZeroU64;
3030
use std::sync::{Arc, RwLock};
3131
use std::time::{Duration, SystemTime, UNIX_EPOCH};
3232

33+
#[cfg(not(feature = "uniffi"))]
34+
type HumanReadableName = LdkHumanReadableName;
35+
#[cfg(feature = "uniffi")]
36+
type HumanReadableName = Arc<crate::uniffi_types::HumanReadableName>;
37+
38+
#[cfg(not(feature = "uniffi"))]
39+
pub fn maybe_convert_hrn(hrn: HumanReadableName) -> LdkHumanReadableName {
40+
hrn.clone()
41+
}
42+
#[cfg(feature = "uniffi")]
43+
pub fn maybe_convert_hrn(hrn: HumanReadableName) -> LdkHumanReadableName {
44+
hrn.inner.clone()
45+
}
46+
3347
/// A payment handler allowing to create and pay [BOLT 12] offers and refunds.
3448
///
3549
/// Should be retrieved by calling [`Node::bolt12_payment`].
@@ -265,30 +279,30 @@ impl Bolt12Payment {
265279
/// This can be used to pay so-called "zero-amount" offers, i.e., an offer that leaves the
266280
/// amount paid to be determined by the user.
267281
///
268-
/// If `dns_resolvers_node_ids` in Config is set to `None`, this operation will fail.
282+
/// If `dns_resolvers_node_ids` in [`Config.hrn_config`] is empty, this operation will fail.
269283
pub fn send_to_human_readable_name(
270-
&self, name: &str, amount_msat: u64,
284+
&self, hrn: HumanReadableName, amount_msat: u64,
271285
) -> Result<PaymentId, Error> {
272286
let rt_lock = self.runtime.read().unwrap();
273287
if rt_lock.is_none() {
274288
return Err(Error::NotRunning);
275289
}
276290

277-
let hrn = HumanReadableName::from_encoded(&name).map_err(|_| Error::HrnParsingFailed)?;
278-
279291
let mut random_bytes = [0u8; 32];
280292
rand::thread_rng().fill_bytes(&mut random_bytes);
281293
let payment_id = PaymentId(random_bytes);
282294
let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
283295
let max_total_routing_fee_msat = None;
284296

285-
let dns_resolvers = match &self.config.dns_resolvers_node_ids {
286-
Some(dns_resolvers) => Ok(dns_resolvers.clone()),
287-
None => Err(Error::DnsResolversNotConfigured),
288-
}?;
297+
let destinations: Vec<Destination> = self
298+
.config
299+
.hrn_config
300+
.dns_resolvers_node_ids
301+
.iter()
302+
.map(|node_id| Destination::Node(*node_id))
303+
.collect();
289304

290-
let destinations: Vec<Destination> =
291-
dns_resolvers.into_iter().map(|public_key| Destination::Node(public_key)).collect();
305+
let hrn = maybe_convert_hrn(hrn);
292306

293307
match self.channel_manager.pay_for_offer_from_human_readable_name(
294308
hrn.clone(),
@@ -299,7 +313,7 @@ impl Bolt12Payment {
299313
destinations,
300314
) {
301315
Ok(()) => {
302-
log_info!(self.logger, "Initiated sending {} msats to {}", amount_msat, name);
316+
log_info!(self.logger, "Initiated sending {} msats to {:?}", amount_msat, hrn);
303317
let kind = PaymentKind::Bolt12Offer {
304318
hash: None,
305319
preimage: None,
@@ -320,7 +334,7 @@ impl Bolt12Payment {
320334
Ok(payment_id)
321335
},
322336
Err(()) => {
323-
log_error!(self.logger, "Failed to send payment to {}", name);
337+
log_error!(self.logger, "Failed to send payment to {:?}", hrn);
324338
let kind = PaymentKind::Bolt12Offer {
325339
hash: None,
326340
preimage: None,
@@ -335,7 +349,7 @@ impl Bolt12Payment {
335349
Some(amount_msat),
336350
None,
337351
PaymentDirection::Outbound,
338-
PaymentStatus::Pending,
352+
PaymentStatus::Failed,
339353
);
340354
self.payment_store.insert(payment)?;
341355
Err(Error::PaymentSendingFailed)

src/payment/store.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ impl_writeable_tlv_based_enum!(PaymentKind,
470470
(2, preimage, option),
471471
(3, quantity, option),
472472
(4, secret, option),
473-
(6, offer_id, required),
473+
(6, offer_id, option),
474474
},
475475
(8, Spontaneous) => {
476476
(0, hash, required),
@@ -542,6 +542,7 @@ pub(crate) struct PaymentDetailsUpdate {
542542
pub direction: Option<PaymentDirection>,
543543
pub status: Option<PaymentStatus>,
544544
pub confirmation_status: Option<ConfirmationStatus>,
545+
pub quantity: Option<u64>,
545546
}
546547

547548
impl PaymentDetailsUpdate {
@@ -557,6 +558,7 @@ impl PaymentDetailsUpdate {
557558
direction: None,
558559
status: None,
559560
confirmation_status: None,
561+
quantity: None,
560562
}
561563
}
562564
}
@@ -584,6 +586,11 @@ impl From<&PaymentDetails> for PaymentDetailsUpdate {
584586
_ => None,
585587
};
586588

589+
let quantity = match value.kind {
590+
PaymentKind::Bolt12Offer { quantity, .. } => quantity,
591+
_ => None,
592+
};
593+
587594
Self {
588595
id: value.id,
589596
hash: Some(hash),
@@ -595,6 +602,7 @@ impl From<&PaymentDetails> for PaymentDetailsUpdate {
595602
direction: Some(value.direction),
596603
status: Some(value.status),
597604
confirmation_status,
605+
quantity,
598606
}
599607
}
600608
}

src/uniffi_types.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub use lightning::ln::types::ChannelId;
2828
pub use lightning::offers::invoice::Bolt12Invoice;
2929
pub use lightning::offers::offer::{Offer, OfferId};
3030
pub use lightning::offers::refund::Refund;
31+
pub use lightning::onion_message::dns_resolution::HumanReadableName as LdkHumanReadableName;
3132
pub use lightning::routing::gossip::{NodeAlias, NodeId, RoutingFees};
3233
pub use lightning::util::string::UntrustedString;
3334

@@ -656,6 +657,46 @@ impl UniffiCustomTypeConverter for DateTime {
656657
}
657658
}
658659

660+
pub struct HumanReadableName {
661+
pub inner: LdkHumanReadableName,
662+
}
663+
664+
impl HumanReadableName {
665+
/// Returns the underlying HumanReadableName [`LdkHumanReadableName`]
666+
pub fn into_inner(&self) -> LdkHumanReadableName {
667+
self.inner.clone()
668+
}
669+
670+
pub fn from_encoded(encoded: &str) -> Self {
671+
let hrn = match LdkHumanReadableName::from_encoded(encoded) {
672+
Ok(hrn) => Ok(hrn),
673+
Err(e) => Err(format!("Error creating HRN {:?}", e)),
674+
};
675+
676+
Self { inner: hrn.expect("Error creating HRN") }
677+
}
678+
679+
pub fn user(&self) -> String {
680+
self.inner.user().to_string()
681+
}
682+
683+
pub fn domain(&self) -> String {
684+
self.inner.domain().to_string()
685+
}
686+
}
687+
688+
impl From<LdkHumanReadableName> for HumanReadableName {
689+
fn from(ldk_hrn: LdkHumanReadableName) -> Self {
690+
HumanReadableName { inner: ldk_hrn }
691+
}
692+
}
693+
694+
impl From<HumanReadableName> for LdkHumanReadableName {
695+
fn from(wrapper: HumanReadableName) -> Self {
696+
wrapper.into_inner()
697+
}
698+
}
699+
659700
#[cfg(test)]
660701
mod tests {
661702
use super::*;

0 commit comments

Comments
 (0)