Skip to content

Commit d93aecc

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 e0d18a1 commit d93aecc

File tree

2 files changed

+62
-18
lines changed

2 files changed

+62
-18
lines changed

lightning/src/events/mod.rs

+17-9
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>,
@@ -1872,7 +1880,7 @@ impl MaybeReadable for Event {
18721880
(13, payment_id, option),
18731881
});
18741882
let purpose = match payment_secret {
1875-
Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context)
1883+
Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context, None)
18761884
.map_err(|()| msgs::DecodeError::InvalidValue)?,
18771885
None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
18781886
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};
@@ -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
};
@@ -6010,7 +6009,7 @@ where
60106009
let blinded_failure = routing.blinded_failure();
60116010
let (
60126011
cltv_expiry, onion_payload, payment_data, payment_context, phantom_shared_secret,
6013-
mut onion_fields, has_recipient_created_payment_secret, _invoice_request_opt
6012+
mut onion_fields, has_recipient_created_payment_secret, invoice_request_opt
60146013
) = match routing {
60156014
PendingHTLCRouting::Receive {
60166015
payment_data, payment_metadata, payment_context,
@@ -6224,6 +6223,7 @@ where
62246223
payment_preimage,
62256224
payment_data.payment_secret,
62266225
payment_context,
6226+
None,
62276227
) {
62286228
Ok(purpose) => purpose,
62296229
Err(()) => {
@@ -6232,14 +6232,50 @@ where
62326232
};
62336233
check_total_value!(purpose);
62346234
},
6235-
OnionPayload::Spontaneous(preimage) => {
6236-
if payment_context.is_some() {
6237-
if !matches!(payment_context, Some(PaymentContext::AsyncBolt12Offer(_))) {
6238-
log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context);
6235+
OnionPayload::Spontaneous(keysend_preimage) => {
6236+
let purpose = if let Some(PaymentContext::AsyncBolt12Offer(
6237+
AsyncBolt12OfferContext { offer_nonce }
6238+
)) = payment_context {
6239+
let payment_data = match payment_data {
6240+
Some(data) => data,
6241+
None => {
6242+
debug_assert!(false, "We checked that payment_data is Some above");
6243+
fail_htlc!(claimable_htlc, payment_hash);
6244+
},
6245+
};
6246+
6247+
let verified_invreq = match invoice_request_opt
6248+
.and_then(|invreq| invreq.verify_using_recipient_data(
6249+
offer_nonce, &self.inbound_payment_key, &self.secp_ctx
6250+
).ok())
6251+
{
6252+
Some(verified_invreq) => {
6253+
if let Some(invreq_amt_msat) = verified_invreq.amount_msats() {
6254+
if payment_data.total_msat < invreq_amt_msat {
6255+
fail_htlc!(claimable_htlc, payment_hash);
6256+
}
6257+
}
6258+
verified_invreq
6259+
},
6260+
None => {
6261+
fail_htlc!(claimable_htlc, payment_hash);
6262+
}
6263+
};
6264+
match events::PaymentPurpose::from_parts(
6265+
Some(keysend_preimage), payment_data.payment_secret, payment_context,
6266+
Some(verified_invreq),
6267+
) {
6268+
Ok(purpose) => purpose,
6269+
Err(()) => {
6270+
fail_htlc!(claimable_htlc, payment_hash);
6271+
}
62396272
}
6273+
} else if payment_context.is_some() {
6274+
log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context);
62406275
fail_htlc!(claimable_htlc, payment_hash);
6241-
}
6242-
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
6276+
} else {
6277+
events::PaymentPurpose::SpontaneousPayment(keysend_preimage)
6278+
};
62436279
check_total_value!(purpose);
62446280
}
62456281
}

0 commit comments

Comments
 (0)