@@ -35,8 +35,18 @@ use crate::util::ser::WithoutLength;
35
35
use crate::util::test_utils;
36
36
use lightning_invoice::RawBolt11Invoice;
37
37
#[cfg(async_payments)] use {
38
+ crate::blinded_path::message::{BlindedMessagePath, MessageContext, OffersContext},
38
39
crate::ln::inbound_payment,
40
+ crate::ln::msgs::OnionMessageHandler,
41
+ crate::offers::nonce::Nonce,
42
+ crate::onion_message::async_payments::{AsyncPaymentsMessage, AsyncPaymentsMessageHandler, ReleaseHeldHtlc},
43
+ crate::onion_message::messenger::{Destination, MessageSendInstructions, MessageRouter, PeeledOnion},
44
+ crate::onion_message::offers::OffersMessage,
45
+ crate::onion_message::packet::ParsedOnionMessageContents,
46
+ crate::types::features::Bolt12InvoiceFeatures,
39
47
crate::types::payment::PaymentPreimage,
48
+
49
+ core::convert::Infallible,
40
50
};
41
51
42
52
fn blinded_payment_path(
@@ -101,6 +111,25 @@ pub fn get_blinded_route_parameters(
101
111
)
102
112
}
103
113
114
+ #[cfg(async_payments)]
115
+ fn extract_invoice_request_reply_path<'a, 'b, 'c>(
116
+ invreq_recipient: &Node<'a, 'b, 'c>, message: &msgs::OnionMessage
117
+ ) -> BlindedMessagePath {
118
+ match invreq_recipient.onion_messenger.peel_onion_message(message) {
119
+ Ok(PeeledOnion::Receive(invreq, context, reply_path)) => {
120
+ assert!(
121
+ matches!(invreq, ParsedOnionMessageContents::Offers(OffersMessage::InvoiceRequest(_)))
122
+ );
123
+ assert!(
124
+ matches!(context, Some(MessageContext::Offers(OffersContext::InvoiceRequest { .. })))
125
+ );
126
+ reply_path.unwrap()
127
+ },
128
+ Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
129
+ Err(e) => panic!("Failed to process onion message {:?}", e),
130
+ }
131
+ }
132
+
104
133
#[test]
105
134
fn one_hop_blinded_path() {
106
135
do_one_hop_blinded_path(true);
@@ -1416,6 +1445,210 @@ fn custom_tlvs_to_blinded_path() {
1416
1445
);
1417
1446
}
1418
1447
1448
+ #[test]
1449
+ #[cfg(async_payments)]
1450
+ fn static_invoice_unknown_required_features() {
1451
+ // Test that we will fail to pay a static invoice with unsupported required features.
1452
+ let secp_ctx = Secp256k1::new();
1453
+ let chanmon_cfgs = create_chanmon_cfgs(3);
1454
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
1455
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
1456
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
1457
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
1458
+ create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
1459
+
1460
+ let blinded_paths_to_always_online_node = nodes[1].message_router.create_blinded_paths(
1461
+ nodes[1].node.get_our_node_id(),
1462
+ MessageContext::Offers(OffersContext::InvoiceRequest { nonce: Nonce([42; 16]) }),
1463
+ Vec::new(), &secp_ctx
1464
+ ).unwrap();
1465
+ let (offer_builder, nonce) =
1466
+ nodes[2].node.create_async_receive_offer_builder(blinded_paths_to_always_online_node).unwrap();
1467
+ let offer = offer_builder.build().unwrap();
1468
+ let static_invoice_unknown_req_features = nodes[2].node.create_static_invoice_builder(
1469
+ &offer, nonce, None
1470
+ )
1471
+ .unwrap()
1472
+ .features_unchecked(Bolt12InvoiceFeatures::unknown())
1473
+ .build_and_sign(&secp_ctx).unwrap();
1474
+
1475
+ let amt_msat = 5000;
1476
+ let payment_id = PaymentId([1; 32]);
1477
+ nodes[0].node.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), None).unwrap();
1478
+
1479
+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1480
+ // recipient's LSP yet, instead manually construct the response.
1481
+ let invreq_om = nodes[0].onion_messenger.next_onion_message_for_peer(nodes[1].node.get_our_node_id()).unwrap();
1482
+ let invreq_reply_path = extract_invoice_request_reply_path(&nodes[1], &invreq_om);
1483
+ nodes[1].onion_messenger.send_onion_message(
1484
+ ParsedOnionMessageContents::<Infallible>::Offers(OffersMessage::StaticInvoice(static_invoice_unknown_req_features)),
1485
+ MessageSendInstructions::WithoutReplyPath { destination: Destination::BlindedPath(invreq_reply_path) }
1486
+ ).unwrap();
1487
+
1488
+ let static_invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
1489
+ nodes[0].onion_messenger.handle_onion_message(nodes[1].node.get_our_node_id(), &static_invoice_om);
1490
+ let events = nodes[0].node.get_and_clear_pending_events();
1491
+ assert_eq!(events.len(), 1);
1492
+ match events[0] {
1493
+ Event::PaymentFailed { payment_hash, payment_id: ev_payment_id, reason } => {
1494
+ assert_eq!(payment_hash, None);
1495
+ assert_eq!(payment_id, ev_payment_id);
1496
+ assert_eq!(reason, Some(PaymentFailureReason::UnknownRequiredFeatures));
1497
+ },
1498
+ _ => panic!()
1499
+ }
1500
+ }
1501
+
1502
+ #[test]
1503
+ #[cfg(async_payments)]
1504
+ fn ignore_unexpected_static_invoice() {
1505
+ // Test that we'll ignore unexpected static invoices, invoices that don't match our invoice
1506
+ // request, and duplicate invoices.
1507
+ let secp_ctx = Secp256k1::new();
1508
+ let chanmon_cfgs = create_chanmon_cfgs(3);
1509
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
1510
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
1511
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
1512
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
1513
+ create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
1514
+
1515
+ // Initiate payment to the sender's intended offer.
1516
+ let blinded_paths_to_always_online_node = nodes[1].message_router.create_blinded_paths(
1517
+ nodes[1].node.get_our_node_id(),
1518
+ MessageContext::Offers(OffersContext::InvoiceRequest { nonce: Nonce([42; 16]) }),
1519
+ Vec::new(), &secp_ctx
1520
+ ).unwrap();
1521
+ let (offer_builder, offer_nonce) = nodes[2].node.create_async_receive_offer_builder(blinded_paths_to_always_online_node.clone()).unwrap();
1522
+ let offer = offer_builder.build().unwrap();
1523
+ let amt_msat = 5000;
1524
+ let payment_id = PaymentId([1; 32]);
1525
+ nodes[0].node.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), None).unwrap();
1526
+
1527
+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1528
+ // recipient's LSP yet, instead manually construct the responses below.
1529
+ let invreq_om = nodes[0].onion_messenger.next_onion_message_for_peer(nodes[1].node.get_our_node_id()).unwrap();
1530
+ let invreq_reply_path = extract_invoice_request_reply_path(&nodes[1], &invreq_om);
1531
+
1532
+ // Create a static invoice to be sent over the reply path containing the original payment_id, but
1533
+ // the static invoice corresponds to a different offer than was originally paid.
1534
+ let unexpected_static_invoice = {
1535
+ let (offer_builder, nonce) = nodes[2].node.create_async_receive_offer_builder(blinded_paths_to_always_online_node).unwrap();
1536
+ let sender_unintended_offer = offer_builder.build().unwrap();
1537
+
1538
+ nodes[2].node.create_static_invoice_builder(
1539
+ &sender_unintended_offer, nonce, None
1540
+ ).unwrap().build_and_sign(&secp_ctx).unwrap()
1541
+ };
1542
+
1543
+ // Check that we'll ignore the unexpected static invoice.
1544
+ nodes[1].onion_messenger.send_onion_message(
1545
+ ParsedOnionMessageContents::<Infallible>::Offers(OffersMessage::StaticInvoice(unexpected_static_invoice)),
1546
+ MessageSendInstructions::WithoutReplyPath { destination: Destination::BlindedPath(invreq_reply_path.clone()) }
1547
+ ).unwrap();
1548
+ let unexpected_static_invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
1549
+ nodes[0].onion_messenger.handle_onion_message(nodes[1].node.get_our_node_id(), &unexpected_static_invoice_om);
1550
+ let async_pmts_msgs = AsyncPaymentsMessageHandler::release_pending_messages(nodes[0].node);
1551
+ assert!(async_pmts_msgs.is_empty());
1552
+ assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
1553
+
1554
+ // A valid static invoice corresponding to the correct offer will succeed and cause us to send a
1555
+ // held_htlc_available onion message.
1556
+ let valid_static_invoice = nodes[2].node.create_static_invoice_builder(
1557
+ &offer, offer_nonce, None
1558
+ ).unwrap().build_and_sign(&secp_ctx).unwrap();
1559
+
1560
+ nodes[1].onion_messenger.send_onion_message(
1561
+ ParsedOnionMessageContents::<Infallible>::Offers(OffersMessage::StaticInvoice(valid_static_invoice.clone())),
1562
+ MessageSendInstructions::WithoutReplyPath { destination: Destination::BlindedPath(invreq_reply_path.clone()) }
1563
+ ).unwrap();
1564
+ let static_invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
1565
+ nodes[0].onion_messenger.handle_onion_message(nodes[1].node.get_our_node_id(), &static_invoice_om);
1566
+ let async_pmts_msgs = AsyncPaymentsMessageHandler::release_pending_messages(nodes[0].node);
1567
+ assert!(!async_pmts_msgs.is_empty());
1568
+ assert!(async_pmts_msgs.into_iter().all(|(msg, _)| matches!(msg, AsyncPaymentsMessage::HeldHtlcAvailable(_))));
1569
+
1570
+ // Receiving a duplicate invoice will have no effect.
1571
+ nodes[1].onion_messenger.send_onion_message(
1572
+ ParsedOnionMessageContents::<Infallible>::Offers(OffersMessage::StaticInvoice(valid_static_invoice)),
1573
+ MessageSendInstructions::WithoutReplyPath { destination: Destination::BlindedPath(invreq_reply_path) }
1574
+ ).unwrap();
1575
+ let dup_static_invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
1576
+ nodes[0].onion_messenger.handle_onion_message(nodes[1].node.get_our_node_id(), &dup_static_invoice_om);
1577
+ let async_pmts_msgs = AsyncPaymentsMessageHandler::release_pending_messages(nodes[0].node);
1578
+ assert!(async_pmts_msgs.is_empty());
1579
+ }
1580
+
1581
+ #[test]
1582
+ #[cfg(async_payments)]
1583
+ fn pays_static_invoice() {
1584
+ // Test that we support the async payments flow up to and including sending the actual payment.
1585
+ // Async receive is not yet supported so we don't complete the payment yet.
1586
+ let secp_ctx = Secp256k1::new();
1587
+ let chanmon_cfgs = create_chanmon_cfgs(3);
1588
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
1589
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
1590
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
1591
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
1592
+ create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
1593
+
1594
+ let blinded_paths_to_always_online_node = nodes[1].message_router.create_blinded_paths(
1595
+ nodes[1].node.get_our_node_id(),
1596
+ MessageContext::Offers(OffersContext::InvoiceRequest { nonce: Nonce([42; 16]) }),
1597
+ Vec::new(), &secp_ctx
1598
+ ).unwrap();
1599
+ let (offer_builder, offer_nonce) = nodes[2].node.create_async_receive_offer_builder(blinded_paths_to_always_online_node).unwrap();
1600
+ let offer = offer_builder.build().unwrap();
1601
+ let amt_msat = 5000;
1602
+ let payment_id = PaymentId([1; 32]);
1603
+ let static_invoice = nodes[2].node.create_static_invoice_builder(
1604
+ &offer, offer_nonce, None
1605
+ ).unwrap().build_and_sign(&secp_ctx).unwrap();
1606
+
1607
+ nodes[0].node.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), None).unwrap();
1608
+
1609
+ // Don't forward the invreq since we don't support retrieving the static invoice from the
1610
+ // recipient's LSP yet, instead manually construct the response.
1611
+ let invreq_om = nodes[0].onion_messenger.next_onion_message_for_peer(nodes[1].node.get_our_node_id()).unwrap();
1612
+ let invreq_reply_path = extract_invoice_request_reply_path(&nodes[1], &invreq_om);
1613
+
1614
+ nodes[1].onion_messenger.send_onion_message(
1615
+ ParsedOnionMessageContents::<Infallible>::Offers(OffersMessage::StaticInvoice(static_invoice)),
1616
+ MessageSendInstructions::WithoutReplyPath { destination: Destination::BlindedPath(invreq_reply_path) }
1617
+ ).unwrap();
1618
+ let static_invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
1619
+ nodes[0].onion_messenger.handle_onion_message(nodes[1].node.get_our_node_id(), &static_invoice_om);
1620
+ let mut async_pmts_msgs = AsyncPaymentsMessageHandler::release_pending_messages(nodes[0].node);
1621
+ assert!(!async_pmts_msgs.is_empty());
1622
+ assert!(async_pmts_msgs.iter().all(|(msg, _)| matches!(msg, AsyncPaymentsMessage::HeldHtlcAvailable(_))));
1623
+
1624
+ // Manually send the message and context releasing the HTLC since the recipient doesn't support
1625
+ // responding themselves yet.
1626
+ let held_htlc_avail_reply_path = match async_pmts_msgs.pop().unwrap().1 {
1627
+ MessageSendInstructions::WithSpecifiedReplyPath { reply_path, .. } => reply_path,
1628
+ _ => panic!()
1629
+ };
1630
+ nodes[2].onion_messenger.send_onion_message(
1631
+ ParsedOnionMessageContents::<Infallible>::AsyncPayments(
1632
+ AsyncPaymentsMessage::ReleaseHeldHtlc(ReleaseHeldHtlc {}),
1633
+ ),
1634
+ MessageSendInstructions::WithoutReplyPath {
1635
+ destination: Destination::BlindedPath(held_htlc_avail_reply_path)
1636
+ }
1637
+ ).unwrap();
1638
+
1639
+ let release_held_htlc_om = nodes[2].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
1640
+ nodes[0].onion_messenger.handle_onion_message(nodes[2].node.get_our_node_id(), &release_held_htlc_om);
1641
+
1642
+ // Check that we've queued the HTLCs of the async keysend payment.
1643
+ let htlc_updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
1644
+ assert_eq!(htlc_updates.update_add_htlcs.len(), 1);
1645
+ check_added_monitors!(nodes[0], 1);
1646
+
1647
+ // Receiving a duplicate release_htlc message doesn't result in duplicate payment.
1648
+ nodes[0].onion_messenger.handle_onion_message(nodes[2].node.get_our_node_id(), &release_held_htlc_om);
1649
+ assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
1650
+ }
1651
+
1419
1652
fn secret_from_hex(hex: &str) -> SecretKey {
1420
1653
SecretKey::from_slice(&<Vec<u8>>::from_hex(hex).unwrap()).unwrap()
1421
1654
}
0 commit comments