Skip to content

Commit 0dca4eb

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 82ad53c commit 0dca4eb

File tree

2 files changed

+60
-16
lines changed

2 files changed

+60
-16
lines changed

lightning/src/events/mod.rs

+15-7
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

+45-9
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};
@@ -87,7 +87,6 @@ use crate::util::ser::TransactionU16LenLimited;
8787
use crate::util::logger::{Level, Logger, WithContext};
8888
use crate::util::errors::APIError;
8989
#[cfg(async_payments)] use {
90-
crate::blinded_path::payment::AsyncBolt12OfferContext,
9190
crate::offers::offer::Amount,
9291
crate::offers::static_invoice::{DEFAULT_RELATIVE_EXPIRY as STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY, StaticInvoice, StaticInvoiceBuilder},
9392
};
@@ -6047,7 +6046,7 @@ where
60476046
let blinded_failure = routing.blinded_failure();
60486047
let (
60496048
cltv_expiry, onion_payload, payment_data, payment_context, phantom_shared_secret,
6050-
mut onion_fields, has_recipient_created_payment_secret, _invoice_request_opt
6049+
mut onion_fields, has_recipient_created_payment_secret, invoice_request_opt
60516050
) = match routing {
60526051
PendingHTLCRouting::Receive {
60536052
payment_data, payment_metadata, payment_context,
@@ -6263,6 +6262,7 @@ where
62636262
payment_preimage,
62646263
payment_data.payment_secret,
62656264
payment_context,
6265+
None,
62666266
) {
62676267
Ok(purpose) => purpose,
62686268
Err(()) => {
@@ -6271,14 +6271,50 @@ where
62716271
};
62726272
check_total_value!(purpose);
62736273
},
6274-
OnionPayload::Spontaneous(preimage) => {
6275-
if payment_context.is_some() {
6276-
if !matches!(payment_context, Some(PaymentContext::AsyncBolt12Offer(_))) {
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);
6274+
OnionPayload::Spontaneous(keysend_preimage) => {
6275+
let purpose = if let Some(PaymentContext::AsyncBolt12Offer(
6276+
AsyncBolt12OfferContext { offer_nonce }
6277+
)) = payment_context {
6278+
let payment_data = match payment_data {
6279+
Some(data) => data,
6280+
None => {
6281+
debug_assert!(false, "We checked that payment_data is Some above");
6282+
fail_htlc!(claimable_htlc, payment_hash);
6283+
},
6284+
};
6285+
6286+
let verified_invreq = match invoice_request_opt
6287+
.and_then(|invreq| invreq.verify_using_recipient_data(
6288+
offer_nonce, &self.inbound_payment_key, &self.secp_ctx
6289+
).ok())
6290+
{
6291+
Some(verified_invreq) => {
6292+
if let Some(invreq_amt_msat) = verified_invreq.amount_msats() {
6293+
if payment_data.total_msat < invreq_amt_msat {
6294+
fail_htlc!(claimable_htlc, payment_hash);
6295+
}
6296+
}
6297+
verified_invreq
6298+
},
6299+
None => {
6300+
fail_htlc!(claimable_htlc, payment_hash);
6301+
}
6302+
};
6303+
match events::PaymentPurpose::from_parts(
6304+
Some(keysend_preimage), payment_data.payment_secret, payment_context,
6305+
Some(verified_invreq),
6306+
) {
6307+
Ok(purpose) => purpose,
6308+
Err(()) => {
6309+
fail_htlc!(claimable_htlc, payment_hash);
6310+
}
62786311
}
6312+
} else if payment_context.is_some() {
6313+
log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context);
62796314
fail_htlc!(claimable_htlc, payment_hash);
6280-
}
6281-
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
6315+
} else {
6316+
events::PaymentPurpose::SpontaneousPayment(keysend_preimage)
6317+
};
62826318
check_total_value!(purpose);
62836319
}
62846320
}

0 commit comments

Comments
 (0)