Skip to content

Commit f5d65d4

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 f5d65d4

File tree

2 files changed

+55
-14
lines changed

2 files changed

+55
-14
lines changed

lightning/src/events/mod.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,10 @@ impl PaymentPurpose {
178178
}
179179
}
180180

181+
/// Errors when provided an `AsyncBolt12OfferContext`, see below.
181182
pub(crate) fn from_parts(
182183
payment_preimage: Option<PaymentPreimage>, payment_secret: PaymentSecret,
183-
payment_context: Option<PaymentContext>,
184+
payment_context: Option<PaymentContext>
184185
) -> Result<Self, ()> {
185186
match payment_context {
186187
None => {
@@ -203,11 +204,12 @@ 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.
207+
Some(PaymentContext::AsyncBolt12Offer(_)) => {
208+
// Callers are expected to convert from `AsyncBolt12OfferContext` to `Bolt12OfferContext`
209+
// using the invoice request provided in the payment onion prior to calling this method.
210+
debug_assert!(false);
209211
Err(())
210-
},
212+
}
211213
}
212214
}
213215
}

lightning/src/ln/channelmanager.rs

+48-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,
@@ -6271,14 +6270,54 @@ where
62716270
};
62726271
check_total_value!(purpose);
62736272
},
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);
6273+
OnionPayload::Spontaneous(keysend_preimage) => {
6274+
let purpose = if let Some(PaymentContext::AsyncBolt12Offer(
6275+
AsyncBolt12OfferContext { offer_nonce }
6276+
)) = payment_context {
6277+
let payment_data = match payment_data {
6278+
Some(data) => data,
6279+
None => {
6280+
debug_assert!(false, "We checked that payment_data is Some above");
6281+
fail_htlc!(claimable_htlc, payment_hash);
6282+
},
6283+
};
6284+
6285+
let verified_invreq = match invoice_request_opt
6286+
.and_then(|invreq| invreq.verify_using_recipient_data(
6287+
offer_nonce, &self.inbound_payment_key, &self.secp_ctx
6288+
).ok())
6289+
{
6290+
Some(verified_invreq) => {
6291+
if let Some(invreq_amt_msat) = verified_invreq.amount_msats() {
6292+
if payment_data.total_msat < invreq_amt_msat {
6293+
fail_htlc!(claimable_htlc, payment_hash);
6294+
}
6295+
}
6296+
verified_invreq
6297+
},
6298+
None => {
6299+
fail_htlc!(claimable_htlc, payment_hash);
6300+
}
6301+
};
6302+
let payment_purpose_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
6303+
offer_id: verified_invreq.offer_id,
6304+
invoice_request: verified_invreq.fields(),
6305+
});
6306+
match events::PaymentPurpose::from_parts(
6307+
Some(keysend_preimage), payment_data.payment_secret,
6308+
Some(payment_purpose_context),
6309+
) {
6310+
Ok(purpose) => purpose,
6311+
Err(()) => {
6312+
fail_htlc!(claimable_htlc, payment_hash);
6313+
}
62786314
}
6315+
} else if payment_context.is_some() {
6316+
log_trace!(self.logger, "Failing new HTLC with payment_hash {}: received a keysend payment to a non-async payments context {:#?}", payment_hash, payment_context);
62796317
fail_htlc!(claimable_htlc, payment_hash);
6280-
}
6281-
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
6318+
} else {
6319+
events::PaymentPurpose::SpontaneousPayment(keysend_preimage)
6320+
};
62826321
check_total_value!(purpose);
62836322
}
62846323
}

0 commit comments

Comments
 (0)