Skip to content

Commit 2d478dc

Browse files
Refresh async offers on set_inv_server_paths
On startup, we want the async recipient to be set up with async receive offers as soon as possible. Therefore, attempt to send out initial offer_paths_requests as soon as the recipient is configured with blinded paths to connect to the static invoice server.
1 parent 7878a00 commit 2d478dc

File tree

3 files changed

+101
-33
lines changed

3 files changed

+101
-33
lines changed

lightning/src/ln/async_payments_tests.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,38 @@ fn extract_payment_preimage(event: &Event) -> PaymentPreimage {
330330
}
331331
}
332332

333+
fn expect_offer_paths_requests(recipient: &Node, next_hop_nodes: &[&Node]) {
334+
// We want to check that the async recipient has enqueued at least one `OfferPathsRequest` and no
335+
// other message types. Check this by iterating through all their outbound onion messages, peeling
336+
// multiple times if the messages are forwarded through other nodes.
337+
let per_msg_recipient_msgs = recipient.onion_messenger.release_pending_msgs();
338+
let mut pk_to_msg = Vec::new();
339+
for (pk, msgs) in per_msg_recipient_msgs {
340+
for msg in msgs {
341+
pk_to_msg.push((pk, msg));
342+
}
343+
}
344+
let mut num_offer_paths_reqs: u8 = 0;
345+
while let Some((pk, msg)) = pk_to_msg.pop() {
346+
let node = next_hop_nodes.iter().find(|node| node.node.get_our_node_id() == pk).unwrap();
347+
let peeled_msg = node.onion_messenger.peel_onion_message(&msg).unwrap();
348+
match peeled_msg {
349+
PeeledOnion::AsyncPayments(AsyncPaymentsMessage::OfferPathsRequest(_), _, _) => {
350+
num_offer_paths_reqs += 1;
351+
},
352+
PeeledOnion::Forward(next_hop, msg) => {
353+
let next_pk = match next_hop {
354+
crate::blinded_path::message::NextMessageHop::NodeId(pk) => pk,
355+
_ => panic!(),
356+
};
357+
pk_to_msg.push((next_pk, msg));
358+
},
359+
_ => panic!("Unexpected message"),
360+
}
361+
}
362+
assert!(num_offer_paths_reqs > 0);
363+
}
364+
333365
fn advance_time_by(duration: Duration, node: &Node) {
334366
let target_time = (node.node.duration_since_epoch() + duration).as_secs() as u32;
335367
let block = create_dummy_block(node.best_block_hash(), target_time, Vec::new());
@@ -512,6 +544,7 @@ fn ignore_unexpected_static_invoice() {
512544
let inv_server_paths =
513545
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
514546
nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
547+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
515548

516549
// Initiate payment to the sender's intended offer.
517550
let valid_static_invoice =
@@ -609,6 +642,7 @@ fn async_receive_flow_success() {
609642
let inv_server_paths =
610643
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
611644
nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
645+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
612646

613647
let invoice_flow_res =
614648
pass_static_invoice_server_messages(&nodes[1], &nodes[2], recipient_id.clone());
@@ -671,6 +705,7 @@ fn expired_static_invoice_fail() {
671705
let inv_server_paths =
672706
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
673707
nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
708+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
674709

675710
let static_invoice =
676711
pass_static_invoice_server_messages(&nodes[1], &nodes[2], recipient_id.clone()).invoice;
@@ -746,6 +781,7 @@ fn timeout_unreleased_payment() {
746781
let inv_server_paths =
747782
server.node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
748783
recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
784+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
749785

750786
let static_invoice =
751787
pass_static_invoice_server_messages(server, recipient, recipient_id.clone()).invoice;
@@ -831,6 +867,7 @@ fn async_receive_mpp() {
831867
let inv_server_paths =
832868
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
833869
nodes[3].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
870+
expect_offer_paths_requests(&nodes[3], &[&nodes[0], &nodes[1], &nodes[2]]);
834871

835872
let static_invoice =
836873
pass_static_invoice_server_messages(&nodes[1], &nodes[3], recipient_id.clone()).invoice;
@@ -924,6 +961,7 @@ fn amount_doesnt_match_invreq() {
924961
let inv_server_paths =
925962
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
926963
nodes[3].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
964+
expect_offer_paths_requests(&nodes[3], &[&nodes[0], &nodes[1], &nodes[2]]);
927965

928966
let static_invoice =
929967
pass_static_invoice_server_messages(&nodes[1], &nodes[3], recipient_id.clone()).invoice;
@@ -1124,6 +1162,7 @@ fn invalid_async_receive_with_retry<F1, F2>(
11241162
let inv_server_paths =
11251163
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
11261164
nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
1165+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
11271166

11281167
// Set the random bytes so we can predict the offer nonce.
11291168
let hardcoded_random_bytes = [42; 32];
@@ -1251,6 +1290,7 @@ fn expired_static_invoice_message_path() {
12511290
let inv_server_paths =
12521291
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
12531292
nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
1293+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
12541294

12551295
let static_invoice =
12561296
pass_static_invoice_server_messages(&nodes[1], &nodes[2], recipient_id.clone()).invoice;
@@ -1315,6 +1355,7 @@ fn expired_static_invoice_payment_path() {
13151355
let inv_server_paths =
13161356
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
13171357
nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
1358+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
13181359

13191360
// Make sure all nodes are at the same block height in preparation for CLTV timeout things.
13201361
let node_max_height =
@@ -1576,10 +1617,12 @@ fn limit_offer_paths_requests() {
15761617
let inv_server_paths =
15771618
server.node.blinded_paths_for_async_recipient(recipient_id, None).unwrap();
15781619
recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
1620+
expect_offer_paths_requests(&nodes[1], &[&nodes[0]]);
15791621

15801622
// Up to TEST_MAX_UPDATE_ATTEMPTS offer_paths_requests are allowed to be sent out before the async
15811623
// recipient should give up.
1582-
for _ in 0..TEST_MAX_UPDATE_ATTEMPTS {
1624+
// Subtract 1 because we sent the first request when invoice server paths were set above.
1625+
for _ in 0..TEST_MAX_UPDATE_ATTEMPTS - 1 {
15831626
recipient.node.test_check_refresh_async_receive_offers();
15841627
let offer_paths_req = recipient
15851628
.onion_messenger
@@ -1744,6 +1787,7 @@ fn refresh_static_invoices() {
17441787
let inv_server_paths =
17451788
server.node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
17461789
recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
1790+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
17471791

17481792
// Set up the recipient to have one offer and an invoice with the static invoice server.
17491793
let flow_res = pass_static_invoice_server_messages(server, recipient, recipient_id.clone());
@@ -2136,6 +2180,7 @@ fn invoice_server_is_not_channel_peer() {
21362180
let inv_server_paths =
21372181
invoice_server.node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
21382182
recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
2183+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1], &nodes[3]]);
21392184
let invoice =
21402185
pass_static_invoice_server_messages(invoice_server, recipient, recipient_id.clone())
21412186
.invoice;

lightning/src/ln/channelmanager.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11905,7 +11905,8 @@ where
1190511905
pub fn set_paths_to_static_invoice_server(
1190611906
&self, paths_to_static_invoice_server: Vec<BlindedMessagePath>,
1190711907
) -> Result<(), ()> {
11908-
self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server)?;
11908+
let peers = self.get_peers_for_blinded_path();
11909+
self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server, peers)?;
1190911910

1191011911
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
1191111912
Ok(())

lightning/src/offers/flow.rs

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,24 @@ where
162162
/// [`Offer`]s with a static invoice server, so the server can serve [`StaticInvoice`]s to payers
163163
/// on our behalf when we're offline.
164164
///
165+
/// This method will also send out messages initiating async offer creation to the static invoice
166+
/// server, if any peers are connected.
167+
///
165168
/// This method only needs to be called once when the server first takes on the recipient as a
166169
/// client, or when the paths change, e.g. if the paths are set to expire at a particular time.
167170
#[cfg(async_payments)]
168171
pub fn set_paths_to_static_invoice_server(
169172
&self, paths_to_static_invoice_server: Vec<BlindedMessagePath>,
173+
peers: Vec<MessageForwardNode>,
170174
) -> Result<(), ()> {
171175
let mut cache = self.async_receive_offer_cache.lock().unwrap();
172176
cache.set_paths_to_static_invoice_server(paths_to_static_invoice_server.clone())?;
177+
core::mem::drop(cache);
178+
179+
// We'll only fail here if no peers are connected yet for us to create reply paths to outbound
180+
// offer_paths_requests, so ignore the error.
181+
let _ = self.check_refresh_async_offers(peers, false);
182+
173183
Ok(())
174184
}
175185

@@ -1252,49 +1262,61 @@ where
12521262
R::Target: Router,
12531263
{
12541264
// Terminate early if this node does not intend to receive async payments.
1255-
let mut cache = self.async_receive_offer_cache.lock().unwrap();
1256-
if cache.paths_to_static_invoice_server().is_empty() {
1257-
return Ok(());
1265+
{
1266+
let cache = self.async_receive_offer_cache.lock().unwrap();
1267+
if cache.paths_to_static_invoice_server().is_empty() {
1268+
return Ok(());
1269+
}
12581270
}
12591271

1272+
self.check_refresh_async_offers(peers.clone(), timer_tick_occurred)?;
1273+
1274+
if timer_tick_occurred {
1275+
self.check_refresh_static_invoices(peers, usable_channels, entropy, router);
1276+
}
1277+
1278+
Ok(())
1279+
}
1280+
1281+
#[cfg(async_payments)]
1282+
fn check_refresh_async_offers(
1283+
&self, peers: Vec<MessageForwardNode>, timer_tick_occurred: bool,
1284+
) -> Result<(), ()> {
12601285
let duration_since_epoch = self.duration_since_epoch();
1286+
let mut cache = self.async_receive_offer_cache.lock().unwrap();
12611287

12621288
// Update the cache to remove expired offers, and check to see whether we need new offers to be
12631289
// interactively built with the static invoice server.
12641290
let needs_new_offers =
12651291
cache.prune_expired_offers(duration_since_epoch, timer_tick_occurred);
1292+
if !needs_new_offers {
1293+
return Ok(());
1294+
}
12661295

12671296
// If we need new offers, send out offer paths request messages to the static invoice server.
1268-
if needs_new_offers {
1269-
let context = MessageContext::AsyncPayments(AsyncPaymentsContext::OfferPaths {
1270-
path_absolute_expiry: duration_since_epoch
1271-
.saturating_add(TEMP_REPLY_PATH_RELATIVE_EXPIRY),
1272-
});
1273-
let reply_paths = match self.create_blinded_paths(peers.clone(), context) {
1274-
Ok(paths) => paths,
1275-
Err(()) => {
1276-
return Err(());
1277-
},
1278-
};
1279-
1280-
// We can't fail past this point, so indicate to the cache that we've requested new offers.
1281-
cache.new_offers_requested();
1297+
let context = MessageContext::AsyncPayments(AsyncPaymentsContext::OfferPaths {
1298+
path_absolute_expiry: duration_since_epoch
1299+
.saturating_add(TEMP_REPLY_PATH_RELATIVE_EXPIRY),
1300+
});
1301+
let reply_paths = match self.create_blinded_paths(peers, context) {
1302+
Ok(paths) => paths,
1303+
Err(()) => {
1304+
return Err(());
1305+
},
1306+
};
12821307

1283-
let mut pending_async_payments_messages =
1284-
self.pending_async_payments_messages.lock().unwrap();
1285-
let message = AsyncPaymentsMessage::OfferPathsRequest(OfferPathsRequest {});
1286-
enqueue_onion_message_with_reply_paths(
1287-
message,
1288-
cache.paths_to_static_invoice_server(),
1289-
reply_paths,
1290-
&mut pending_async_payments_messages,
1291-
);
1292-
}
1293-
core::mem::drop(cache);
1308+
// We can't fail past this point, so indicate to the cache that we've requested new offers.
1309+
cache.new_offers_requested();
12941310

1295-
if timer_tick_occurred {
1296-
self.check_refresh_static_invoices(peers, usable_channels, entropy, router);
1297-
}
1311+
let mut pending_async_payments_messages =
1312+
self.pending_async_payments_messages.lock().unwrap();
1313+
let message = AsyncPaymentsMessage::OfferPathsRequest(OfferPathsRequest {});
1314+
enqueue_onion_message_with_reply_paths(
1315+
message,
1316+
cache.paths_to_static_invoice_server(),
1317+
reply_paths,
1318+
&mut pending_async_payments_messages,
1319+
);
12981320

12991321
Ok(())
13001322
}

0 commit comments

Comments
 (0)