Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 84 additions & 1 deletion lightning/src/ln/async_payments_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,6 @@ fn create_static_invoice_builder<'a>(
payment_secret,
relative_expiry_secs,
recipient.node.list_usable_channels(),
recipient.node.test_get_peers_for_blinded_path(),
)
.unwrap()
}
Expand Down Expand Up @@ -3286,6 +3285,90 @@ fn async_payment_mpp() {
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, expected_route, keysend_preimage));
}

#[test]
fn fallback_to_one_hop_release_htlc_path() {
// Check that if the sender's LSP's message router fails to find a blinded path when creating a
// path for the release_held_htlc message, they will fall back to manually creating a 1-hop
// blinded path.
let chanmon_cfgs = create_chanmon_cfgs(4);
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);

let (sender_cfg, recipient_cfg) = (often_offline_node_cfg(), often_offline_node_cfg());
let mut sender_lsp_cfg = test_default_channel_config();
sender_lsp_cfg.enable_htlc_hold = true;
let mut invoice_server_cfg = test_default_channel_config();
invoice_server_cfg.accept_forwards_to_priv_channels = true;

let node_chanmgrs = create_node_chanmgrs(
4,
&node_cfgs,
&[Some(sender_cfg), Some(sender_lsp_cfg), Some(invoice_server_cfg), Some(recipient_cfg)],
);
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0);
// Make sure all nodes are at the same block height
let node_max_height =
nodes.iter().map(|node| node.blocks.lock().unwrap().len()).max().unwrap() as u32;
connect_blocks(&nodes[0], node_max_height - nodes[0].best_block_info().1);
connect_blocks(&nodes[1], node_max_height - nodes[1].best_block_info().1);
connect_blocks(&nodes[2], node_max_height - nodes[2].best_block_info().1);
connect_blocks(&nodes[3], node_max_height - nodes[3].best_block_info().1);
let sender = &nodes[0];
let sender_lsp = &nodes[1];
let invoice_server = &nodes[2];
let recipient = &nodes[3];

let amt_msat = 5000;
let (static_invoice, peer_node_id, static_invoice_om) =
build_async_offer_and_init_payment(amt_msat, &nodes);

// Force the sender_lsp's call to MessageRouter::create_blinded_paths to fail so it has to fall
// back to a 1-hop blinded path when creating the paths for its revoke_and_ack message.
sender_lsp.message_router.create_blinded_paths_res_override.lock().unwrap().1 = Some(Err(()));

let payment_hash =
lock_in_htlc_for_static_invoice(&static_invoice_om, peer_node_id, sender, sender_lsp);

// Check that we actually had to fall back to a 1-hop path.
assert!(sender_lsp.message_router.create_blinded_paths_res_override.lock().unwrap().0 > 0);
sender_lsp.message_router.create_blinded_paths_res_override.lock().unwrap().1 = None;

sender_lsp.node.process_pending_htlc_forwards();
let (peer_id, held_htlc_om) =
extract_held_htlc_available_oms(sender, &[sender_lsp, invoice_server, recipient])
.pop()
.unwrap();
recipient.onion_messenger.handle_onion_message(peer_id, &held_htlc_om);

// The release_htlc OM should go straight to the sender's LSP since they created a 1-hop blinded
// path to themselves for receiving it.
let release_htlc_om = recipient
.onion_messenger
.next_onion_message_for_peer(sender_lsp.node.get_our_node_id())
.unwrap();
sender_lsp
.onion_messenger
.handle_onion_message(recipient.node.get_our_node_id(), &release_htlc_om);

sender_lsp.node.process_pending_htlc_forwards();
let mut events = sender_lsp.node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
let ev = remove_first_msg_event_to_node(&invoice_server.node.get_our_node_id(), &mut events);
check_added_monitors!(sender_lsp, 1);

let path: &[&Node] = &[invoice_server, recipient];
let args = PassAlongPathArgs::new(sender_lsp, path, amt_msat, payment_hash, ev);
let claimable_ev = do_pass_along_path(args).unwrap();

let route: &[&[&Node]] = &[&[sender_lsp, invoice_server, recipient]];
let keysend_preimage = extract_payment_preimage(&claimable_ev);
let (res, _) =
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
}

#[test]
fn fail_held_htlcs_when_cfg_unset() {
// Test that if we receive a held HTLC but `UserConfig::enable_htlc_hold` is unset, we will fail
Expand Down
69 changes: 24 additions & 45 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5441,12 +5441,10 @@ where
}

fn check_refresh_async_receive_offer_cache(&self, timer_tick_occurred: bool) {
let peers = self.get_peers_for_blinded_path();
let channels = self.list_usable_channels();
let entropy = &*self.entropy_source;
let router = &*self.router;
let refresh_res = self.flow.check_refresh_async_receive_offer_cache(
peers,
channels,
entropy,
router,
Expand Down Expand Up @@ -5534,10 +5532,7 @@ where
);
}
} else {
let reply_path = HeldHtlcReplyPath::ToUs {
payment_id,
peers: self.get_peers_for_blinded_path(),
};
let reply_path = HeldHtlcReplyPath::ToUs { payment_id };
let enqueue_held_htlc_available_res =
self.flow.enqueue_held_htlc_available(invoice, reply_path);
if enqueue_held_htlc_available_res.is_err() {
Expand Down Expand Up @@ -8077,6 +8072,8 @@ where

should_persist
});

self.flow.set_peers(self.get_peers_for_blinded_path());
}

/// Indicates that the preimage for payment_hash is unknown or the received amount is incorrect
Expand Down Expand Up @@ -10427,16 +10424,20 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
emit_initial_channel_ready_event!(pending_events, chan);
}

Ok(())
} else {
try_channel_entry!(self, peer_state, Err(ChannelError::close(
"Got a channel_ready message for an unfunded channel!".into())), chan_entry)
}
},
hash_map::Entry::Vacant(_) => {
Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id))
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id))
}
}
core::mem::drop(peer_state_lock);
core::mem::drop(per_peer_state);

self.flow.set_peers(self.get_peers_for_blinded_path());
Ok(())
}

fn internal_shutdown(
Expand Down Expand Up @@ -10530,6 +10531,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
self.fail_htlc_backwards_internal(&source, &hash, &reason, receiver, None);
}

self.flow.set_peers(self.get_peers_for_blinded_path());
Ok(())
}

Expand Down Expand Up @@ -12257,9 +12259,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
/// [`Offer`]: crate::offers::offer::Offer
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
pub fn create_offer_builder(&$self) -> Result<$builder, Bolt12SemanticError> {
let builder = $self.flow.create_offer_builder(
&*$self.entropy_source, $self.get_peers_for_blinded_path()
)?;
let builder = $self.flow.create_offer_builder(&*$self.entropy_source)?;

Ok(builder.into())
}
Expand All @@ -12282,9 +12282,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
where
ME::Target: MessageRouter,
{
let builder = $self.flow.create_offer_builder_using_router(
router, &*$self.entropy_source, $self.get_peers_for_blinded_path()
)?;
let builder = $self.flow.create_offer_builder_using_router(router, &*$self.entropy_source)?;

Ok(builder.into())
}
Expand Down Expand Up @@ -12338,8 +12336,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
let entropy = &*$self.entropy_source;

let builder = $self.flow.create_refund_builder(
entropy, amount_msats, absolute_expiry,
payment_id, $self.get_peers_for_blinded_path()
entropy, amount_msats, absolute_expiry, payment_id
)?;

let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
Expand Down Expand Up @@ -12382,8 +12379,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
let entropy = &*$self.entropy_source;

let builder = $self.flow.create_refund_builder_using_router(
router, entropy, amount_msats, absolute_expiry,
payment_id, $self.get_peers_for_blinded_path()
router, entropy, amount_msats, absolute_expiry, payment_id
)?;

let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
Expand Down Expand Up @@ -12455,8 +12451,7 @@ where
pub fn set_paths_to_static_invoice_server(
&self, paths_to_static_invoice_server: Vec<BlindedMessagePath>,
) -> Result<(), ()> {
let peers = self.get_peers_for_blinded_path();
self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server, peers)?;
self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server)?;

let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
Ok(())
Expand Down Expand Up @@ -12636,10 +12631,7 @@ where
let invoice_request = builder.build_and_sign()?;
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);

self.flow.enqueue_invoice_request(
invoice_request.clone(), payment_id, nonce,
self.get_peers_for_blinded_path()
)?;
self.flow.enqueue_invoice_request(invoice_request.clone(), payment_id, nonce,)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a syntax error in this line - the trailing comma before the closing parenthesis is invalid. The comma after nonce should be removed to make this valid Rust syntax:

self.flow.enqueue_invoice_request(invoice_request.clone(), payment_id, nonce)?;
Suggested change
self.flow.enqueue_invoice_request(invoice_request.clone(), payment_id, nonce,)?;
self.flow.enqueue_invoice_request(invoice_request.clone(), payment_id, nonce)?;

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.


let retryable_invoice_request = RetryableInvoiceRequest {
invoice_request: invoice_request.clone(),
Expand Down Expand Up @@ -12694,7 +12686,7 @@ where

let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;

self.flow.enqueue_invoice(invoice.clone(), refund, self.get_peers_for_blinded_path())?;
self.flow.enqueue_invoice(invoice.clone(), refund)?;

Ok(invoice)
},
Expand Down Expand Up @@ -12758,14 +12750,7 @@ where
optional_params.payer_note,
)?;

self.flow
.enqueue_dns_onion_message(
onion_message,
context,
dns_resolvers,
self.get_peers_for_blinded_path(),
)
.map_err(|_| ())
self.flow.enqueue_dns_onion_message(onion_message, context, dns_resolvers).map_err(|_| ())
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
Expand Down Expand Up @@ -12906,8 +12891,7 @@ where
pub fn blinded_paths_for_async_recipient(
&self, recipient_id: Vec<u8>, relative_expiry: Option<Duration>,
) -> Result<Vec<BlindedMessagePath>, ()> {
let peers = self.get_peers_for_blinded_path();
self.flow.blinded_paths_for_async_recipient(recipient_id, relative_expiry, peers)
self.flow.blinded_paths_for_async_recipient(recipient_id, relative_expiry)
}

pub(super) fn duration_since_epoch(&self) -> Duration {
Expand Down Expand Up @@ -12941,11 +12925,6 @@ where
.collect::<Vec<_>>()
}

#[cfg(test)]
pub(super) fn test_get_peers_for_blinded_path(&self) -> Vec<MessageForwardNode> {
self.get_peers_for_blinded_path()
}

#[cfg(test)]
/// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to
/// [`Router::create_blinded_payment_paths`].
Expand Down Expand Up @@ -13391,6 +13370,8 @@ where
for (err, counterparty_node_id) in failed_channels.drain(..) {
let _ = handle_error!(self, err, counterparty_node_id);
}

self.flow.set_peers(self.get_peers_for_blinded_path());
}

#[rustfmt::skip]
Expand Down Expand Up @@ -13503,6 +13484,7 @@ where
// until we have some peer connection(s) to receive onion messages over, so as a minor optimization
// refresh the cache when a peer connects.
self.check_refresh_async_receive_offer_cache(false);
self.flow.set_peers(self.get_peers_for_blinded_path());
res
}

Expand Down Expand Up @@ -14725,9 +14707,8 @@ where
{
let RetryableInvoiceRequest { invoice_request, nonce, .. } = retryable_invoice_request;

let peers = self.get_peers_for_blinded_path();
let enqueue_invreq_res =
self.flow.enqueue_invoice_request(invoice_request, payment_id, nonce, peers);
self.flow.enqueue_invoice_request(invoice_request, payment_id, nonce);
if enqueue_invreq_res.is_err() {
log_warn!(
self.logger,
Expand Down Expand Up @@ -14935,9 +14916,8 @@ where
&self, message: OfferPathsRequest, context: AsyncPaymentsContext,
responder: Option<Responder>,
) -> Option<(OfferPaths, ResponseInstruction)> {
let peers = self.get_peers_for_blinded_path();
let (message, reply_path_context) =
match self.flow.handle_offer_paths_request(&message, context, peers) {
match self.flow.handle_offer_paths_request(&message, context) {
Some(msg) => msg,
None => return None,
};
Expand All @@ -14955,7 +14935,6 @@ where
message,
context,
responder.clone(),
self.get_peers_for_blinded_path(),
self.list_usable_channels(),
&*self.entropy_source,
&*self.router,
Expand Down
Loading
Loading