Skip to content

Split out receive_htlcs from the forwarding pipeline #3973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

tnull
Copy link
Contributor

@tnull tnull commented Jul 30, 2025

This is another preparatory step for receiver-side delays that we split out to err on the side of smaller, more reviewable PRs, especially since these changes are a nice cleanup in any case, IMO.

Previously, we'd store receiving HTLCs side-by-side with HTLCs forwards in the forwards_htlcs map under SCID 0.
Here, we opt to split out a separate receive_htlcs field which cleans up the logic, also omitting the 0 magic value.

Moreover, some of the tests manipulating forward_htlcs were previously just iterating, but not actually checking whether the expected entries were present and they were actually changed. Here we make these test cases stricter to ensure they'd able to catch any unwanted behavior we'd introduce while introducing receive_htlcs.

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Jul 30, 2025

👋 Thanks for assigning @TheBlueMatt as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull tnull self-assigned this Jul 30, 2025
@tnull tnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnull tnull moved this to Goal: Merge in Weekly Goals Jul 30, 2025
Copy link

codecov bot commented Jul 30, 2025

Codecov Report

❌ Patch coverage is 74.33628% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.00%. Comparing base (8fd7cbf) to head (e681129).
⚠️ Report is 54 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/channelmanager.rs 66.00% 15 Missing and 2 partials ⚠️
lightning/src/ln/onion_route_tests.rs 79.48% 8 Missing ⚠️
lightning/src/ln/payment_tests.rs 83.33% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3973      +/-   ##
==========================================
+ Coverage   88.93%   89.00%   +0.07%     
==========================================
  Files         174      174              
  Lines      123880   124439     +559     
  Branches   123880   124439     +559     
==========================================
+ Hits       110169   110763     +594     
+ Misses      11260    11202      -58     
- Partials     2451     2474      +23     
Flag Coverage Δ
fuzzing 22.22% <28.00%> (-0.41%) ⬇️
tests 88.83% <74.33%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tnull tnull force-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940 Compare July 30, 2025 15:00
@tnull tnull removed the request for review from valentinewallace July 31, 2025 06:01
@ldk-reviews-bot
Copy link

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @valentinewallace! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @valentinewallace! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

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

Basically LGTM!

Could you confirm one thing for me: if we fail to receive an HTLC that's destined for us, it will then be put into the forward_htlcs map keyed with the scid of the previous hop? That's how I'm reading the behavior of fail_htlc_backwards_internal atm. If that's the case, may want to note that on the forward_htlcs field.

continue;
}
},
_ => {},
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: could debug_assert!(false) here if only to indicate we should never hit this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I intentionally let all other cases fall through to the the previous behavior. In particular, it seems that trampoline forwards would also be added under SCID 0 but we wouldn't want to push them to receive_htlcs.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't Trampoline forwards also be an AddHTLC? So the bottom _ match arm would still be unreachable. It also seems like we should fail here since any failures against SCID 0 should fail anyway (we won't be able to fail cause no such channel exists?)

panic!("Expected pending receive");
};
assert_eq!(nodes[3].node.receive_htlcs.lock().unwrap().len(), 1);
match nodes[3].node.receive_htlcs.lock().unwrap().get_mut(0).unwrap() {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: could've avoided a bit of rewriting here and below by doing match .. get_mut(0).unwrap() in the first commit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm yeah. I don't fully recall why I did it this way. Is it fine for you to leave it as-is now?

@@ -681,29 +680,25 @@ fn test_reject_mpp_keysend_htlc_mismatching_secret() {
let update_add_3 = update_3.update_add_htlcs[0].clone();
nodes[3].node.handle_update_add_htlc(node_c_id, &update_add_3);
commitment_signed_dance!(nodes[3], nodes[2], update_3.commitment_signed, false, true);
expect_htlc_failure_conditions(nodes[3].node.get_and_clear_pending_events(), &[]);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not clear on the reason for removing this, mind explaining? I thought there shouldn't be a behavior change here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure! The event queue is actually empty here, so this is a no-op, which we simply made a bit more explicit with the assert below.

Happy to drop the diff, but FWIW it might be worth cleaning up all instances of expect_htlc_failure_conditions(.., &[]); ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably fine, in the future I think it would be a bit more reviewable to either separate out or note in the commit message.

tnull added 5 commits August 5, 2025 10:48
Previously, some of the tests manipulating `forward_htlcs` were just
iterating, but not actually checking whether the expected entries were
present and they were actually changed. Here we make these test cases
more strict.
Previously, we'd store receiving HTLCs side-by-side with HTLCs forwards
in the `forwards_htlcs` field under SCID 0.

Here, we opt to split out a separate `receive_htlcs` field, also
omitting the 0 magic value.
@tnull tnull force-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ff Compare August 5, 2025 08:51
@tnull
Copy link
Contributor Author

tnull commented Aug 6, 2025

Basically LGTM!

Could you confirm one thing for me: if we fail to receive an HTLC that's destined for us, it will then be put into the forward_htlcs map keyed with the scid of the previous hop? That's how I'm reading the behavior of fail_htlc_backwards_internal atm. If that's the case, may want to note that on the forward_htlcs field.

Yes, I think this is correct, and the same hold for pending_intercepted_htlcs, AFAICT. I now added respective comments.

}) => forward_info.outgoing_cltv_value += 1,
_ => {},
}
nodes[1].node.process_pending_update_add_htlcs();
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this line gets removed in a later commit? It doesn't seem to belong in this commit.

@@ -2537,7 +2539,7 @@ pub struct ChannelManager<
/// See `ChannelManager` struct-level documentation for lock order requirements.
pending_outbound_payments: OutboundPayments,

/// SCID/SCID Alias -> forward infos. Key of 0 means payments received.
Copy link
Contributor

Choose a reason for hiding this comment

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

Could document that 0 actually means trampoline now.

@@ -15091,6 +15124,8 @@ where
}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't it break downgrades to only write the new vec, and not also write the receives in the legacy forward_htlcs?

Comment on lines +1176 to +1181
assert_eq!(nodes[1].node.forward_htlcs.lock().unwrap().len(), 1);
if let Some((_, pending_forwards)) =
nodes[1].node.forward_htlcs.lock().unwrap().iter_mut().next()
{
assert_eq!(pending_forwards.len(), 1);
match pending_forwards.get_mut(0).unwrap() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This repeated pattern really looks like it could be in a test util function or macro.

continue;
}
},
_ => {},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't Trampoline forwards also be an AddHTLC? So the bottom _ match arm would still be unreachable. It also seems like we should fail here since any failures against SCID 0 should fail anyway (we won't be able to fail cause no such channel exists?)

#[cfg(test)]
pub(super) receive_htlcs: Mutex<Vec<HTLCForwardInfo>>,
#[cfg(not(test))]
receive_htlcs: Mutex<Vec<HTLCForwardInfo>>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

ISTM we should change the type here because we still have the issues from the combined map (panics in process_forward_htlcs for receives and panics in process_receive_htlcs for forwards) but now dont have the reason for the (one map). Also the panic messages in those methods are wrong now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
weekly goal Someone wants to land this this week
Projects
Status: Goal: Merge
Development

Successfully merging this pull request may close these issues.

5 participants