Skip to content

Commit b469327

Browse files
Support receiving async payment HTLCs
After a lot of setup in prior commits, here we finally finish support for receiving HTLCs paid to static BOLT 12 invoices. It amounts to verifying the invoice request contained within the onion and generating the right PaymentPurpose for the claimable event.
1 parent b733c2e commit b469327

File tree

2 files changed

+62
-18
lines changed

2 files changed

+62
-18
lines changed

lightning/src/events/mod.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::ln::msgs;
2828
use crate::ln::types::ChannelId;
2929
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
3030
use crate::offers::invoice::Bolt12Invoice;
31+
use crate::offers::invoice_request::VerifiedInvoiceRequest;
3132
use crate::onion_message::messenger::Responder;
3233
use crate::routing::gossip::NetworkUpdate;
3334
use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters};
@@ -180,7 +181,7 @@ impl PaymentPurpose {
180181

181182
pub(crate) fn from_parts(
182183
payment_preimage: Option<PaymentPreimage>, payment_secret: PaymentSecret,
183-
payment_context: Option<PaymentContext>,
184+
payment_context: Option<PaymentContext>, invreq: Option<VerifiedInvoiceRequest>,
184185
) -> Result<Self, ()> {
185186
match payment_context {
186187
None => {
@@ -203,11 +204,18 @@ impl PaymentPurpose {
203204
payment_context: context,
204205
})
205206
},
206-
Some(PaymentContext::AsyncBolt12Offer(_context)) => {
207-
// This code will change to return Self::Bolt12OfferPayment when we add support for async
208-
// receive.
209-
Err(())
210-
},
207+
Some(PaymentContext::AsyncBolt12Offer(_)) => {
208+
let invoice_request = invreq.ok_or(())?;
209+
if payment_preimage.is_none() { return Err(()) }
210+
Ok(PaymentPurpose::Bolt12OfferPayment {
211+
payment_preimage,
212+
payment_secret,
213+
payment_context: Bolt12OfferContext {
214+
offer_id: invoice_request.offer_id,
215+
invoice_request: invoice_request.fields(),
216+
},
217+
})
218+
}
211219
}
212220
}
213221
}
@@ -1190,12 +1198,12 @@ pub enum Event {
11901198
/// events generated or serialized by versions prior to 0.0.122.
11911199
next_user_channel_id: Option<u128>,
11921200
/// The node id of the previous node.
1193-
///
1201+
///
11941202
/// This is only `None` for HTLCs received prior to 0.1 or for events serialized by
11951203
/// versions prior to 0.1
11961204
prev_node_id: Option<PublicKey>,
11971205
/// The node id of the next node.
1198-
///
1206+
///
11991207
/// This is only `None` for HTLCs received prior to 0.1 or for events serialized by
12001208
/// versions prior to 0.1
12011209
next_node_id: Option<PublicKey>,
@@ -1860,7 +1868,7 @@ impl MaybeReadable for Event {
18601868
(13, payment_id, option),
18611869
});
18621870
let purpose = match payment_secret {
1863-
Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context)
1871+
Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context, None)
18641872
.map_err(|()| msgs::DecodeError::InvalidValue)?,
18651873
None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
18661874
None => return Err(msgs::DecodeError::InvalidValue),

lightning/src/ln/channelmanager.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use crate::events::FundingInfo;
3636
use crate::blinded_path::message::{AsyncPaymentsContext, MessageContext, OffersContext};
3737
use crate::blinded_path::NodeIdLookUp;
3838
use crate::blinded_path::message::{BlindedMessagePath, MessageForwardNode};
39-
use crate::blinded_path::payment::{BlindedPaymentPath, Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, UnauthenticatedReceiveTlvs};
39+
use crate::blinded_path::payment::{AsyncBolt12OfferContext, BlindedPaymentPath, Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, UnauthenticatedReceiveTlvs};
4040
use crate::chain;
4141
use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock};
4242
use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
@@ -89,7 +89,6 @@ use crate::util::ser::TransactionU16LenLimited;
8989
use crate::util::logger::{Level, Logger, WithContext};
9090
use crate::util::errors::APIError;
9191
#[cfg(async_payments)] use {
92-
crate::blinded_path::payment::AsyncBolt12OfferContext,
9392
crate::offers::offer::Amount,
9493
crate::offers::static_invoice::{DEFAULT_RELATIVE_EXPIRY as STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY, StaticInvoice, StaticInvoiceBuilder},
9594
};
@@ -6013,7 +6012,7 @@ where
60136012
let blinded_failure = routing.blinded_failure();
60146013
let (
60156014
cltv_expiry, onion_payload, payment_data, payment_context, phantom_shared_secret,
6016-
mut onion_fields, has_recipient_created_payment_secret, _invoice_request_opt
6015+
mut onion_fields, has_recipient_created_payment_secret, invoice_request_opt
60176016
) = match routing {
60186017
PendingHTLCRouting::Receive {
60196018
payment_data, payment_metadata, payment_context,
@@ -6227,6 +6226,7 @@ where
62276226
payment_preimage,
62286227
payment_data.payment_secret,
62296228
payment_context,
6229+
None,
62306230
) {
62316231
Ok(purpose) => purpose,
62326232
Err(()) => {
@@ -6235,14 +6235,50 @@ where
62356235
};
62366236
check_total_value!(purpose);
62376237
},
6238-
OnionPayload::Spontaneous(preimage) => {
6239-
if payment_context.is_some() {
6240-
if !matches!(payment_context, Some(PaymentContext::AsyncBolt12Offer(_))) {
6241-
log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context);
6238+
OnionPayload::Spontaneous(keysend_preimage) => {
6239+
let purpose = if let Some(PaymentContext::AsyncBolt12Offer(
6240+
AsyncBolt12OfferContext { offer_nonce }
6241+
)) = payment_context {
6242+
let payment_data = match payment_data {
6243+
Some(data) => data,
6244+
None => {
6245+
debug_assert!(false, "We checked that payment_data is Some above");
6246+
fail_htlc!(claimable_htlc, payment_hash);
6247+
},
6248+
};
6249+
6250+
let verified_invreq = match invoice_request_opt
6251+
.and_then(|invreq| invreq.verify_using_recipient_data(
6252+
offer_nonce, &self.inbound_payment_key, &self.secp_ctx
6253+
).ok())
6254+
{
6255+
Some(verified_invreq) => {
6256+
if let Some(invreq_amt_msat) = verified_invreq.amount_msats() {
6257+
if payment_data.total_msat < invreq_amt_msat {
6258+
fail_htlc!(claimable_htlc, payment_hash);
6259+
}
6260+
}
6261+
verified_invreq
6262+
},
6263+
None => {
6264+
fail_htlc!(claimable_htlc, payment_hash);
6265+
}
6266+
};
6267+
match events::PaymentPurpose::from_parts(
6268+
Some(keysend_preimage), payment_data.payment_secret, payment_context,
6269+
Some(verified_invreq),
6270+
) {
6271+
Ok(purpose) => purpose,
6272+
Err(()) => {
6273+
fail_htlc!(claimable_htlc, payment_hash);
6274+
}
62426275
}
6276+
} else if payment_context.is_some() {
6277+
log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context);
62436278
fail_htlc!(claimable_htlc, payment_hash);
6244-
}
6245-
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
6279+
} else {
6280+
events::PaymentPurpose::SpontaneousPayment(keysend_preimage)
6281+
};
62466282
check_total_value!(purpose);
62476283
}
62486284
}

0 commit comments

Comments
 (0)