Skip to content

Commit 0e0613e

Browse files
committed
Add support for sending to human-readable names that resolve to Bolt12 Offers
BIP 353 introduced human-readable names which has been implemented in LDK (0.1). This commit adds support for sending to HRNs that resolve to Bolt12 Offers by using the LDK pay_for_offer_from_human_readable_name method in ChannelManager.
1 parent 40a3aeb commit 0e0613e

File tree

5 files changed

+868
-1
lines changed

5 files changed

+868
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ panic = 'abort' # Abort on panic
2828
default = []
2929

3030
[dependencies]
31-
lightning = { version = "0.1.0", features = ["std"] }
31+
lightning = { version = "0.1.0", features = ["std", "dnssec"] }
3232
lightning-types = { version = "0.2.0" }
3333
lightning-invoice = { version = "0.33.0", features = ["std"] }
3434
lightning-net-tokio = { version = "0.1.0" }

bindings/ldk_node.udl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ enum NodeError {
302302
"InsufficientFunds",
303303
"LiquiditySourceUnavailable",
304304
"LiquidityFeeTooHigh",
305+
"HrnParsingFailed",
305306
};
306307

307308
dictionary NodeStatus {
@@ -854,3 +855,6 @@ typedef string OrderId;
854855

855856
[Custom]
856857
typedef string DateTime;
858+
859+
[Custom]
860+
typedef string HumanReadableName;

src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ pub enum Error {
120120
LiquiditySourceUnavailable,
121121
/// The given operation failed due to the LSP's required opening fee being too high.
122122
LiquidityFeeTooHigh,
123+
/// Parsing a Human-Readable Name has failed
124+
HrnParsingFailed,
123125
}
124126

125127
impl fmt::Display for Error {
@@ -193,6 +195,9 @@ impl fmt::Display for Error {
193195
Self::LiquidityFeeTooHigh => {
194196
write!(f, "The given operation failed due to the LSP's required opening fee being too high.")
195197
},
198+
Self::HrnParsingFailed => {
199+
write!(f, "Failed to parse a human-readable name.")
200+
},
196201
}
197202
}
198203
}

src/payment/bolt12.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,72 @@ impl Bolt12Payment {
270270
}
271271
}
272272

273+
/// Send a payment to an offer resolved from a human-readable name [BIP 353].
274+
///
275+
/// Paying to human-readable names makes it more intuitive to make payments for offers
276+
/// as users can simply send payments to HRNs such as `user@example.com`.
277+
///
278+
/// This can be used to pay so-called "zero-amount" offers, i.e., an offer that leaves the
279+
/// amount paid to be determined by the user.
280+
///
281+
/// `dns_resolvers` should be a list of node Destinations that are configured for dns resolution (as outlined in bLIP 32).
282+
/// These nodes can be found by running a search through the `NetworkGraph` to find nodes that announce the
283+
/// `dns_resolver` feature flag.
284+
pub fn send_to_human_readable_name(
285+
&self, name: &str, amount_msat: u64, dns_resolvers: Vec<Destination>,
286+
) -> Result<PaymentId, Error> {
287+
let rt_lock = self.runtime.read().unwrap();
288+
if rt_lock.is_none() {
289+
return Err(Error::NotRunning);
290+
}
291+
292+
let hrn = HumanReadableName::from_encoded(&name).map_err(|_| Error::HrnParsingFailed)?;
293+
294+
let mut random_bytes = [0u8; 32];
295+
rand::thread_rng().fill_bytes(&mut random_bytes);
296+
let payment_id = PaymentId(random_bytes);
297+
let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
298+
let max_total_routing_fee_msat = None;
299+
300+
match self.channel_manager.pay_for_offer_from_human_readable_name(
301+
hrn.clone(),
302+
amount_msat,
303+
payment_id,
304+
retry_strategy,
305+
max_total_routing_fee_msat,
306+
dns_resolvers,
307+
) {
308+
Ok(()) => {
309+
log_info!(self.logger, "Initiated sending {} msats to {}", amount_msat, name);
310+
let kind = PaymentKind::HrnBolt12Offer { hrn };
311+
let payment = PaymentDetails::new(
312+
payment_id,
313+
kind,
314+
Some(amount_msat),
315+
None,
316+
PaymentDirection::Outbound,
317+
PaymentStatus::Pending,
318+
);
319+
self.payment_store.insert(payment)?;
320+
Ok(payment_id)
321+
},
322+
Err(()) => {
323+
log_error!(self.logger, "Failed to send payment to {}", name);
324+
let kind = PaymentKind::HrnBolt12Offer { hrn };
325+
let payment = PaymentDetails::new(
326+
payment_id,
327+
kind,
328+
Some(amount_msat),
329+
None,
330+
PaymentDirection::Outbound,
331+
PaymentStatus::Pending,
332+
);
333+
self.payment_store.insert(payment)?;
334+
Err(Error::PaymentSendingFailed)
335+
},
336+
}
337+
}
338+
273339
pub(crate) fn receive_inner(
274340
&self, amount_msat: u64, description: &str, expiry_secs: Option<u32>, quantity: Option<u64>,
275341
) -> Result<LdkOffer, Error> {

0 commit comments

Comments
 (0)