Skip to content

Commit 92c14f4

Browse files
committed
f - Reject invalid async LSPS2 static invoices
Fail async static-invoice path creation when no LSPS2 metadata exists. This prevents automatic refreshes from uploading unusable invoices. Co-Authored-By: HAL 9000
1 parent afcc599 commit 92c14f4

1 file changed

Lines changed: 61 additions & 2 deletions

File tree

lightning-liquidity/src/lsps2/router.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,14 @@ impl<R: Router, ES: EntropySource, MD: LSPS2Bolt12PaymentMetadataDecoder> Router
168168
first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs, amount_msats: Option<u64>,
169169
secp_ctx: &Secp256k1<T>,
170170
) -> Result<Vec<BlindedPaymentPath>, ()> {
171+
let all_params = self.metadata_lsps2_params(&tlvs.payment_context);
172+
if all_params.is_empty()
173+
&& first_hops.is_empty()
174+
&& matches!(&tlvs.payment_context, PaymentContext::AsyncBolt12Offer(_))
175+
{
176+
return Err(());
177+
}
178+
171179
// Retrieve paths through existing channels from the inner router.
172180
let inner_res = self.inner_router.create_blinded_payment_paths(
173181
recipient,
@@ -180,7 +188,6 @@ impl<R: Router, ES: EntropySource, MD: LSPS2Bolt12PaymentMetadataDecoder> Router
180188

181189
// If no LSPS2 parameters were present in the payment metadata, just fallback to the inner
182190
// router's paths.
183-
let all_params = self.metadata_lsps2_params(&tlvs.payment_context);
184191
if all_params.is_empty() {
185192
return inner_res;
186193
}
@@ -259,12 +266,14 @@ mod tests {
259266
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
260267

261268
use lightning::blinded_path::payment::{
262-
Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, ReceiveTlvs,
269+
AsyncBolt12OfferContext, BlindedPaymentPath, Bolt12OfferContext, Bolt12RefundContext,
270+
PaymentConstraints, PaymentContext, ReceiveTlvs,
263271
};
264272
use lightning::blinded_path::NodeIdLookUp;
265273
use lightning::ln::channel_state::ChannelDetails;
266274
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA;
267275
use lightning::offers::invoice_request::InvoiceRequestFields;
276+
use lightning::offers::nonce::Nonce;
268277
use lightning::offers::offer::OfferId;
269278
use lightning::routing::router::{InFlightHtlcs, Route, RouteParameters, Router};
270279
use lightning::sign::{EntropySource, NodeSigner, ReceiveAuthKey, Recipient};
@@ -383,6 +392,19 @@ mod tests {
383392
}
384393
}
385394

395+
fn async_bolt12_offer_tlvs(
396+
offer_nonce: Nonce, payment_metadata: Option<BTreeMap<u64, Vec<u8>>>,
397+
) -> ReceiveTlvs {
398+
ReceiveTlvs {
399+
payment_secret: PaymentSecret([2; 32]),
400+
payment_constraints: PaymentConstraints { max_cltv_expiry: 100, htlc_minimum_msat: 1 },
401+
payment_context: PaymentContext::AsyncBolt12Offer(AsyncBolt12OfferContext {
402+
offer_nonce,
403+
payment_metadata,
404+
}),
405+
}
406+
}
407+
386408
fn bolt12_refund_tlvs() -> ReceiveTlvs {
387409
ReceiveTlvs {
388410
payment_secret: PaymentSecret([2; 32]),
@@ -498,6 +520,43 @@ mod tests {
498520
assert_eq!(router.inner_router.create_blinded_payment_paths_calls(), 1);
499521
}
500522

523+
#[test]
524+
fn errors_for_async_offer_without_metadata_or_first_hops() {
525+
let inner_router = MockRouter::new();
526+
let recipient = pubkey(10);
527+
let secp_ctx = Secp256k1::new();
528+
let tlvs = async_bolt12_offer_tlvs(Nonce::from_entropy_source(TestEntropy), None);
529+
let inner_path = BlindedPaymentPath::new(
530+
&[],
531+
recipient,
532+
ReceiveAuthKey([3; 32]),
533+
tlvs.clone(),
534+
u64::MAX,
535+
MIN_FINAL_CLTV_EXPIRY_DELTA,
536+
&TestEntropy,
537+
&secp_ctx,
538+
)
539+
.unwrap();
540+
*inner_router.paths_to_return.lock().unwrap() = Some(vec![inner_path]);
541+
542+
let router = LSPS2BOLT12Router::new_with_payment_metadata_decoder(
543+
inner_router,
544+
TestEntropy,
545+
TestMetadataDecoder,
546+
);
547+
let result = router.create_blinded_payment_paths(
548+
recipient,
549+
ReceiveAuthKey([3; 32]),
550+
Vec::new(),
551+
tlvs,
552+
Some(10_000),
553+
&secp_ctx,
554+
);
555+
556+
assert!(result.is_err());
557+
assert_eq!(router.inner_router.create_blinded_payment_paths_calls(), 0);
558+
}
559+
501560
#[test]
502561
fn creates_paths_for_all_metadata_intercept_scids() {
503562
let inner_router = MockRouter::new();

0 commit comments

Comments
 (0)