Skip to content

Commit 327918c

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 eed99e2 commit 327918c

File tree

2 files changed

+60
-16
lines changed

2 files changed

+60
-16
lines changed

lightning/src/events/mod.rs

Lines changed: 15 additions & 7 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
}
@@ -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)