Skip to content

Commit 788ef71

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 953f49d commit 788ef71

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>,
@@ -1870,7 +1878,7 @@ impl MaybeReadable for Event {
18701878
(13, payment_id, option),
18711879
});
18721880
let purpose = match payment_secret {
1873-
Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context)
1881+
Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context, None)
18741882
.map_err(|()| msgs::DecodeError::InvalidValue)?,
18751883
None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
18761884
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
};
@@ -6038,7 +6037,7 @@ where
60386037
let blinded_failure = routing.blinded_failure();
60396038
let (
60406039
cltv_expiry, onion_payload, payment_data, payment_context, phantom_shared_secret,
6041-
mut onion_fields, has_recipient_created_payment_secret, _invoice_request_opt
6040+
mut onion_fields, has_recipient_created_payment_secret, invoice_request_opt
60426041
) = match routing {
60436042
PendingHTLCRouting::Receive {
60446043
payment_data, payment_metadata, payment_context,
@@ -6252,6 +6251,7 @@ where
62526251
payment_preimage,
62536252
payment_data.payment_secret,
62546253
payment_context,
6254+
None,
62556255
) {
62566256
Ok(purpose) => purpose,
62576257
Err(()) => {
@@ -6260,14 +6260,50 @@ where
62606260
};
62616261
check_total_value!(purpose);
62626262
},
6263-
OnionPayload::Spontaneous(preimage) => {
6264-
if payment_context.is_some() {
6265-
if !matches!(payment_context, Some(PaymentContext::AsyncBolt12Offer(_))) {
6266-
log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context);
6263+
OnionPayload::Spontaneous(keysend_preimage) => {
6264+
let purpose = if let Some(PaymentContext::AsyncBolt12Offer(
6265+
AsyncBolt12OfferContext { offer_nonce }
6266+
)) = payment_context {
6267+
let payment_data = match payment_data {
6268+
Some(data) => data,
6269+
None => {
6270+
debug_assert!(false, "We checked that payment_data is Some above");
6271+
fail_htlc!(claimable_htlc, payment_hash);
6272+
},
6273+
};
6274+
6275+
let verified_invreq = match invoice_request_opt
6276+
.and_then(|invreq| invreq.verify_using_recipient_data(
6277+
offer_nonce, &self.inbound_payment_key, &self.secp_ctx
6278+
).ok())
6279+
{
6280+
Some(verified_invreq) => {
6281+
if let Some(invreq_amt_msat) = verified_invreq.amount_msats() {
6282+
if payment_data.total_msat < invreq_amt_msat {
6283+
fail_htlc!(claimable_htlc, payment_hash);
6284+
}
6285+
}
6286+
verified_invreq
6287+
},
6288+
None => {
6289+
fail_htlc!(claimable_htlc, payment_hash);
6290+
}
6291+
};
6292+
match events::PaymentPurpose::from_parts(
6293+
Some(keysend_preimage), payment_data.payment_secret, payment_context,
6294+
Some(verified_invreq),
6295+
) {
6296+
Ok(purpose) => purpose,
6297+
Err(()) => {
6298+
fail_htlc!(claimable_htlc, payment_hash);
6299+
}
62676300
}
6301+
} else if payment_context.is_some() {
6302+
log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context);
62686303
fail_htlc!(claimable_htlc, payment_hash);
6269-
}
6270-
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
6304+
} else {
6305+
events::PaymentPurpose::SpontaneousPayment(keysend_preimage)
6306+
};
62716307
check_total_value!(purpose);
62726308
}
62736309
}

0 commit comments

Comments
 (0)