Skip to content

Split out receive_htlcs from the forwarding pipeline - #3973

Closed
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs
Closed

Split out receive_htlcs from the forwarding pipeline#3973
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs

Conversation

@tnull

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Jul 30, 2025

Copy link
Copy Markdown

👋 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.

@tnulltnull self-assigned this Jul 30, 2025
@tnulltnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsJul 30, 2025
@codecov

codecovBot commented Jul 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.82456% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.61%. Comparing base (96f9242) to head (e08d3dc).
⚠️ Report is 1614 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channelmanager.rs78.43%10 Missing and 1 partial ⚠️
lightning/src/ln/onion_route_tests.rs79.48%8 Missing ⚠️
lightning/src/ln/payment_tests.rs83.33%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3973 +/- ##
==========================================
- Coverage 88.61% 88.61% -0.01% 
==========================================
Files 174 174 Lines 127640 127684 +44 Branches 127640 127684 +44 ==========================================
+ Hits 113113 113148 +35 - Misses 12046 12056 +10 + Partials 2481 2480 -1 
FlagCoverage Δ
tests88.61% <79.82%> (-0.01%)⬇️

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

☔ View full report in Codecov by Harness.
📢 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.

Comment threadlightning/src/ln/channelmanager.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940CompareJuly 30, 2025 15:00
Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnull removed the request for review from valentinewallaceJuly 31, 2025 06:01
@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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
Copy Markdown

🔔 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.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

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.

Comment threadlightning/src/ln/onion_route_tests.rs
continue;
}
},
_ => {},

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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?)

Copy link
Copy Markdown
ContributorAuthor

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.

Yes, they would. I guess it would be unreachable (or you could say the if short_channel_id == 0 is redundant), but the main point is that we just fall through to previous behavior.

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?)

You mean fail the deserialization?

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/payment_tests.rs
Comment threadlightning/src/ln/payment_tests.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ffCompareAugust 5, 2025 08:51
@tnull

tnull commented Aug 6, 2025

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It doesn't get removed though? And I put it in this commit as it's fixing a pre-existing bug that was surfaced by making the tests stricter: previously, pending_forwards was simply empty here and we weren't checking anything. Will move it to a separate, commit at the beginning.

/// 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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Added. Although I wonder if it would make sense to also add a separate field for trampoline to finally completely get away from the magic number?

}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();

Copy link
Copy Markdown
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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, good point. I guess we'd need to start writing both for a while (also cf. #3973 (comment)), and make sure we only migrate HTLCs for which we don't already track the same prev_htlc_id.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, I considered doing that, but chose not to as it would mean we'd have to keep the old types around to deserialize the legacy forwards on upgrade.

I agree it would be cleaner going forward to add an enum HTLCReceiveInfo and keep copies of HTLCForwardInfo/PendingAddHTLCInfo as LegacyHTLCForwardInfo and LegacyPendingAddHTLCInfo that we can eventually drop after some time.

Do you agree with that approach?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's what I was thinking basically.

@tnulltnull mentioned this pull request Aug 11, 2025
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),

forward_htlcs: Mutex::new(forward_htlcs),
receive_htlcs: Mutex::new(receive_htlcs),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Earlier in this method, we remove pending payments that are not present in the monitors, and ISTM we should be doing this for receive_htlcs now as well

Previously, the `pending_forwards` in this test would simply be empty,
having the test not run any of the subsequent logic.
Here we add a necessary `process_pending_update_add_htlcs()` step, and
will move to a more strict test logic in the following commits.
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.
.. to keep changes in the following commits minimal.
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
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from e681129 to e08d3dcCompareAugust 20, 2025 09:01
@tnulltnull removed the weekly goal Someone wants to land this this week label Nov 13, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

Hi @tnull,

Thanks for your contributions to rust-lightning!

After too many struggles with bugs, outages, contributor bans, and, finally, a multi-week CI ban, the rust-lightning project is moving off of GitHub for day-to-day development.

You can still file issues and access the git tree here, but PRs will now take place exclusively at https://git.rust-bitcoin.org/. As such, this PR has been migrated to https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/3973

If you log in using GitHub (or otherwise link your GitHub account from https://git.rust-bitcoin.org/user/settings/security), ownership of your PRs, issues, and comments will automatically transfer. To push updates to this PR, you'll need to use git push git@gitea-ssh.bitcoin.ninja:lightningdevkit/rust-lightning YOUR_LOCAL_COMMIT_OR_BRANCH:tnull/2025-07-split-out-receive-htlcs. This may require a permissions change - if it doesn't work initially just leave a comment and we'll get you access.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@tnull@ldk-reviews-bot@TheBlueMatt@martinsaposnic@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Split out `receive_htlcs` from the forwarding pipeline by tnull · Pull Request #3973 · lightningdevkit/rust-lightning · GitHub
Skip to content

Split out receive_htlcs from the forwarding pipeline - #3973

Closed
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs
Closed

Split out receive_htlcs from the forwarding pipeline#3973
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs

Conversation

@tnull

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Jul 30, 2025

Copy link
Copy Markdown

👋 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.

@tnulltnull self-assigned this Jul 30, 2025
@tnulltnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsJul 30, 2025
@codecov

codecovBot commented Jul 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.82456% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.61%. Comparing base (96f9242) to head (e08d3dc).
⚠️ Report is 1614 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channelmanager.rs78.43%10 Missing and 1 partial ⚠️
lightning/src/ln/onion_route_tests.rs79.48%8 Missing ⚠️
lightning/src/ln/payment_tests.rs83.33%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3973 +/- ##
==========================================
- Coverage 88.61% 88.61% -0.01% 
==========================================
Files 174 174 Lines 127640 127684 +44 Branches 127640 127684 +44 ==========================================
+ Hits 113113 113148 +35 - Misses 12046 12056 +10 + Partials 2481 2480 -1 
FlagCoverage Δ
tests88.61% <79.82%> (-0.01%)⬇️

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

☔ View full report in Codecov by Harness.
📢 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.

Comment threadlightning/src/ln/channelmanager.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940CompareJuly 30, 2025 15:00
Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnull removed the request for review from valentinewallaceJuly 31, 2025 06:01
@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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
Copy Markdown

🔔 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.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

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.

Comment threadlightning/src/ln/onion_route_tests.rs
continue;
}
},
_ => {},

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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?)

Copy link
Copy Markdown
ContributorAuthor

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.

Yes, they would. I guess it would be unreachable (or you could say the if short_channel_id == 0 is redundant), but the main point is that we just fall through to previous behavior.

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?)

You mean fail the deserialization?

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/payment_tests.rs
Comment threadlightning/src/ln/payment_tests.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ffCompareAugust 5, 2025 08:51
@tnull

tnull commented Aug 6, 2025

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It doesn't get removed though? And I put it in this commit as it's fixing a pre-existing bug that was surfaced by making the tests stricter: previously, pending_forwards was simply empty here and we weren't checking anything. Will move it to a separate, commit at the beginning.

/// 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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Added. Although I wonder if it would make sense to also add a separate field for trampoline to finally completely get away from the magic number?

}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();

Copy link
Copy Markdown
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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, good point. I guess we'd need to start writing both for a while (also cf. #3973 (comment)), and make sure we only migrate HTLCs for which we don't already track the same prev_htlc_id.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, I considered doing that, but chose not to as it would mean we'd have to keep the old types around to deserialize the legacy forwards on upgrade.

I agree it would be cleaner going forward to add an enum HTLCReceiveInfo and keep copies of HTLCForwardInfo/PendingAddHTLCInfo as LegacyHTLCForwardInfo and LegacyPendingAddHTLCInfo that we can eventually drop after some time.

Do you agree with that approach?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's what I was thinking basically.

@tnulltnull mentioned this pull request Aug 11, 2025
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),

forward_htlcs: Mutex::new(forward_htlcs),
receive_htlcs: Mutex::new(receive_htlcs),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Earlier in this method, we remove pending payments that are not present in the monitors, and ISTM we should be doing this for receive_htlcs now as well

Previously, the `pending_forwards` in this test would simply be empty,
having the test not run any of the subsequent logic.
Here we add a necessary `process_pending_update_add_htlcs()` step, and
will move to a more strict test logic in the following commits.
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.
.. to keep changes in the following commits minimal.
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
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from e681129 to e08d3dcCompareAugust 20, 2025 09:01
@tnulltnull removed the weekly goal Someone wants to land this this week label Nov 13, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

Hi @tnull,

Thanks for your contributions to rust-lightning!

After too many struggles with bugs, outages, contributor bans, and, finally, a multi-week CI ban, the rust-lightning project is moving off of GitHub for day-to-day development.

You can still file issues and access the git tree here, but PRs will now take place exclusively at https://git.rust-bitcoin.org/. As such, this PR has been migrated to https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/3973

If you log in using GitHub (or otherwise link your GitHub account from https://git.rust-bitcoin.org/user/settings/security), ownership of your PRs, issues, and comments will automatically transfer. To push updates to this PR, you'll need to use git push git@gitea-ssh.bitcoin.ninja:lightningdevkit/rust-lightning YOUR_LOCAL_COMMIT_OR_BRANCH:tnull/2025-07-split-out-receive-htlcs. This may require a permissions change - if it doesn't work initially just leave a comment and we'll get you access.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@tnull@ldk-reviews-bot@TheBlueMatt@martinsaposnic@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Split out `receive_htlcs` from the forwarding pipeline by tnull · Pull Request #3973 · lightningdevkit/rust-lightning · GitHub
Skip to content

Split out receive_htlcs from the forwarding pipeline - #3973

Closed
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs
Closed

Split out receive_htlcs from the forwarding pipeline#3973
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs

Conversation

@tnull

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Jul 30, 2025

Copy link
Copy Markdown

👋 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.

@tnulltnull self-assigned this Jul 30, 2025
@tnulltnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsJul 30, 2025
@codecov

codecovBot commented Jul 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.82456% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.61%. Comparing base (96f9242) to head (e08d3dc).
⚠️ Report is 1614 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channelmanager.rs78.43%10 Missing and 1 partial ⚠️
lightning/src/ln/onion_route_tests.rs79.48%8 Missing ⚠️
lightning/src/ln/payment_tests.rs83.33%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3973 +/- ##
==========================================
- Coverage 88.61% 88.61% -0.01% 
==========================================
Files 174 174 Lines 127640 127684 +44 Branches 127640 127684 +44 ==========================================
+ Hits 113113 113148 +35 - Misses 12046 12056 +10 + Partials 2481 2480 -1 
FlagCoverage Δ
tests88.61% <79.82%> (-0.01%)⬇️

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

☔ View full report in Codecov by Harness.
📢 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.

Comment threadlightning/src/ln/channelmanager.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940CompareJuly 30, 2025 15:00
Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnull removed the request for review from valentinewallaceJuly 31, 2025 06:01
@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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
Copy Markdown

🔔 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.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

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.

Comment threadlightning/src/ln/onion_route_tests.rs
continue;
}
},
_ => {},

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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?)

Copy link
Copy Markdown
ContributorAuthor

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.

Yes, they would. I guess it would be unreachable (or you could say the if short_channel_id == 0 is redundant), but the main point is that we just fall through to previous behavior.

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?)

You mean fail the deserialization?

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/payment_tests.rs
Comment threadlightning/src/ln/payment_tests.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ffCompareAugust 5, 2025 08:51
@tnull

tnull commented Aug 6, 2025

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It doesn't get removed though? And I put it in this commit as it's fixing a pre-existing bug that was surfaced by making the tests stricter: previously, pending_forwards was simply empty here and we weren't checking anything. Will move it to a separate, commit at the beginning.

/// 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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Added. Although I wonder if it would make sense to also add a separate field for trampoline to finally completely get away from the magic number?

}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();

Copy link
Copy Markdown
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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, good point. I guess we'd need to start writing both for a while (also cf. #3973 (comment)), and make sure we only migrate HTLCs for which we don't already track the same prev_htlc_id.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, I considered doing that, but chose not to as it would mean we'd have to keep the old types around to deserialize the legacy forwards on upgrade.

I agree it would be cleaner going forward to add an enum HTLCReceiveInfo and keep copies of HTLCForwardInfo/PendingAddHTLCInfo as LegacyHTLCForwardInfo and LegacyPendingAddHTLCInfo that we can eventually drop after some time.

Do you agree with that approach?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's what I was thinking basically.

@tnulltnull mentioned this pull request Aug 11, 2025
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),

forward_htlcs: Mutex::new(forward_htlcs),
receive_htlcs: Mutex::new(receive_htlcs),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Earlier in this method, we remove pending payments that are not present in the monitors, and ISTM we should be doing this for receive_htlcs now as well

Previously, the `pending_forwards` in this test would simply be empty,
having the test not run any of the subsequent logic.
Here we add a necessary `process_pending_update_add_htlcs()` step, and
will move to a more strict test logic in the following commits.
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.
.. to keep changes in the following commits minimal.
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
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from e681129 to e08d3dcCompareAugust 20, 2025 09:01
@tnulltnull removed the weekly goal Someone wants to land this this week label Nov 13, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

Hi @tnull,

Thanks for your contributions to rust-lightning!

After too many struggles with bugs, outages, contributor bans, and, finally, a multi-week CI ban, the rust-lightning project is moving off of GitHub for day-to-day development.

You can still file issues and access the git tree here, but PRs will now take place exclusively at https://git.rust-bitcoin.org/. As such, this PR has been migrated to https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/3973

If you log in using GitHub (or otherwise link your GitHub account from https://git.rust-bitcoin.org/user/settings/security), ownership of your PRs, issues, and comments will automatically transfer. To push updates to this PR, you'll need to use git push git@gitea-ssh.bitcoin.ninja:lightningdevkit/rust-lightning YOUR_LOCAL_COMMIT_OR_BRANCH:tnull/2025-07-split-out-receive-htlcs. This may require a permissions change - if it doesn't work initially just leave a comment and we'll get you access.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@tnull@ldk-reviews-bot@TheBlueMatt@martinsaposnic@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Split out `receive_htlcs` from the forwarding pipeline by tnull · Pull Request #3973 · lightningdevkit/rust-lightning · GitHub
Skip to content

Split out receive_htlcs from the forwarding pipeline - #3973

Closed
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs
Closed

Split out receive_htlcs from the forwarding pipeline#3973
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs

Conversation

@tnull

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Jul 30, 2025

Copy link
Copy Markdown

👋 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.

@tnulltnull self-assigned this Jul 30, 2025
@tnulltnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsJul 30, 2025
@codecov

codecovBot commented Jul 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.82456% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.61%. Comparing base (96f9242) to head (e08d3dc).
⚠️ Report is 1614 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channelmanager.rs78.43%10 Missing and 1 partial ⚠️
lightning/src/ln/onion_route_tests.rs79.48%8 Missing ⚠️
lightning/src/ln/payment_tests.rs83.33%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3973 +/- ##
==========================================
- Coverage 88.61% 88.61% -0.01% 
==========================================
Files 174 174 Lines 127640 127684 +44 Branches 127640 127684 +44 ==========================================
+ Hits 113113 113148 +35 - Misses 12046 12056 +10 + Partials 2481 2480 -1 
FlagCoverage Δ
tests88.61% <79.82%> (-0.01%)⬇️

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

☔ View full report in Codecov by Harness.
📢 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.

Comment threadlightning/src/ln/channelmanager.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940CompareJuly 30, 2025 15:00
Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnull removed the request for review from valentinewallaceJuly 31, 2025 06:01
@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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
Copy Markdown

🔔 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.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

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.

Comment threadlightning/src/ln/onion_route_tests.rs
continue;
}
},
_ => {},

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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?)

Copy link
Copy Markdown
ContributorAuthor

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.

Yes, they would. I guess it would be unreachable (or you could say the if short_channel_id == 0 is redundant), but the main point is that we just fall through to previous behavior.

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?)

You mean fail the deserialization?

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/payment_tests.rs
Comment threadlightning/src/ln/payment_tests.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ffCompareAugust 5, 2025 08:51
@tnull

tnull commented Aug 6, 2025

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It doesn't get removed though? And I put it in this commit as it's fixing a pre-existing bug that was surfaced by making the tests stricter: previously, pending_forwards was simply empty here and we weren't checking anything. Will move it to a separate, commit at the beginning.

/// 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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Added. Although I wonder if it would make sense to also add a separate field for trampoline to finally completely get away from the magic number?

}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();

Copy link
Copy Markdown
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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, good point. I guess we'd need to start writing both for a while (also cf. #3973 (comment)), and make sure we only migrate HTLCs for which we don't already track the same prev_htlc_id.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, I considered doing that, but chose not to as it would mean we'd have to keep the old types around to deserialize the legacy forwards on upgrade.

I agree it would be cleaner going forward to add an enum HTLCReceiveInfo and keep copies of HTLCForwardInfo/PendingAddHTLCInfo as LegacyHTLCForwardInfo and LegacyPendingAddHTLCInfo that we can eventually drop after some time.

Do you agree with that approach?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's what I was thinking basically.

@tnulltnull mentioned this pull request Aug 11, 2025
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),

forward_htlcs: Mutex::new(forward_htlcs),
receive_htlcs: Mutex::new(receive_htlcs),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Earlier in this method, we remove pending payments that are not present in the monitors, and ISTM we should be doing this for receive_htlcs now as well

Previously, the `pending_forwards` in this test would simply be empty,
having the test not run any of the subsequent logic.
Here we add a necessary `process_pending_update_add_htlcs()` step, and
will move to a more strict test logic in the following commits.
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.
.. to keep changes in the following commits minimal.
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
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from e681129 to e08d3dcCompareAugust 20, 2025 09:01
@tnulltnull removed the weekly goal Someone wants to land this this week label Nov 13, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

Hi @tnull,

Thanks for your contributions to rust-lightning!

After too many struggles with bugs, outages, contributor bans, and, finally, a multi-week CI ban, the rust-lightning project is moving off of GitHub for day-to-day development.

You can still file issues and access the git tree here, but PRs will now take place exclusively at https://git.rust-bitcoin.org/. As such, this PR has been migrated to https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/3973

If you log in using GitHub (or otherwise link your GitHub account from https://git.rust-bitcoin.org/user/settings/security), ownership of your PRs, issues, and comments will automatically transfer. To push updates to this PR, you'll need to use git push git@gitea-ssh.bitcoin.ninja:lightningdevkit/rust-lightning YOUR_LOCAL_COMMIT_OR_BRANCH:tnull/2025-07-split-out-receive-htlcs. This may require a permissions change - if it doesn't work initially just leave a comment and we'll get you access.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@tnull@ldk-reviews-bot@TheBlueMatt@martinsaposnic@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Split out `receive_htlcs` from the forwarding pipeline by tnull · Pull Request #3973 · lightningdevkit/rust-lightning · GitHub
Skip to content

Split out receive_htlcs from the forwarding pipeline - #3973

Closed
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs
Closed

Split out receive_htlcs from the forwarding pipeline#3973
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs

Conversation

@tnull

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Jul 30, 2025

Copy link
Copy Markdown

👋 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.

@tnulltnull self-assigned this Jul 30, 2025
@tnulltnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsJul 30, 2025
@codecov

codecovBot commented Jul 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.82456% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.61%. Comparing base (96f9242) to head (e08d3dc).
⚠️ Report is 1614 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channelmanager.rs78.43%10 Missing and 1 partial ⚠️
lightning/src/ln/onion_route_tests.rs79.48%8 Missing ⚠️
lightning/src/ln/payment_tests.rs83.33%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3973 +/- ##
==========================================
- Coverage 88.61% 88.61% -0.01% 
==========================================
Files 174 174 Lines 127640 127684 +44 Branches 127640 127684 +44 ==========================================
+ Hits 113113 113148 +35 - Misses 12046 12056 +10 + Partials 2481 2480 -1 
FlagCoverage Δ
tests88.61% <79.82%> (-0.01%)⬇️

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

☔ View full report in Codecov by Harness.
📢 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.

Comment threadlightning/src/ln/channelmanager.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940CompareJuly 30, 2025 15:00
Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnull removed the request for review from valentinewallaceJuly 31, 2025 06:01
@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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
Copy Markdown

🔔 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.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

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.

Comment threadlightning/src/ln/onion_route_tests.rs
continue;
}
},
_ => {},

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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?)

Copy link
Copy Markdown
ContributorAuthor

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.

Yes, they would. I guess it would be unreachable (or you could say the if short_channel_id == 0 is redundant), but the main point is that we just fall through to previous behavior.

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?)

You mean fail the deserialization?

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/payment_tests.rs
Comment threadlightning/src/ln/payment_tests.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ffCompareAugust 5, 2025 08:51
@tnull

tnull commented Aug 6, 2025

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It doesn't get removed though? And I put it in this commit as it's fixing a pre-existing bug that was surfaced by making the tests stricter: previously, pending_forwards was simply empty here and we weren't checking anything. Will move it to a separate, commit at the beginning.

/// 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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Added. Although I wonder if it would make sense to also add a separate field for trampoline to finally completely get away from the magic number?

}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();

Copy link
Copy Markdown
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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, good point. I guess we'd need to start writing both for a while (also cf. #3973 (comment)), and make sure we only migrate HTLCs for which we don't already track the same prev_htlc_id.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, I considered doing that, but chose not to as it would mean we'd have to keep the old types around to deserialize the legacy forwards on upgrade.

I agree it would be cleaner going forward to add an enum HTLCReceiveInfo and keep copies of HTLCForwardInfo/PendingAddHTLCInfo as LegacyHTLCForwardInfo and LegacyPendingAddHTLCInfo that we can eventually drop after some time.

Do you agree with that approach?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's what I was thinking basically.

@tnulltnull mentioned this pull request Aug 11, 2025
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),

forward_htlcs: Mutex::new(forward_htlcs),
receive_htlcs: Mutex::new(receive_htlcs),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Earlier in this method, we remove pending payments that are not present in the monitors, and ISTM we should be doing this for receive_htlcs now as well

Previously, the `pending_forwards` in this test would simply be empty,
having the test not run any of the subsequent logic.
Here we add a necessary `process_pending_update_add_htlcs()` step, and
will move to a more strict test logic in the following commits.
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.
.. to keep changes in the following commits minimal.
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
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from e681129 to e08d3dcCompareAugust 20, 2025 09:01
@tnulltnull removed the weekly goal Someone wants to land this this week label Nov 13, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

Hi @tnull,

Thanks for your contributions to rust-lightning!

After too many struggles with bugs, outages, contributor bans, and, finally, a multi-week CI ban, the rust-lightning project is moving off of GitHub for day-to-day development.

You can still file issues and access the git tree here, but PRs will now take place exclusively at https://git.rust-bitcoin.org/. As such, this PR has been migrated to https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/3973

If you log in using GitHub (or otherwise link your GitHub account from https://git.rust-bitcoin.org/user/settings/security), ownership of your PRs, issues, and comments will automatically transfer. To push updates to this PR, you'll need to use git push git@gitea-ssh.bitcoin.ninja:lightningdevkit/rust-lightning YOUR_LOCAL_COMMIT_OR_BRANCH:tnull/2025-07-split-out-receive-htlcs. This may require a permissions change - if it doesn't work initially just leave a comment and we'll get you access.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@tnull@ldk-reviews-bot@TheBlueMatt@martinsaposnic@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Split out `receive_htlcs` from the forwarding pipeline by tnull · Pull Request #3973 · lightningdevkit/rust-lightning · GitHub
Skip to content

Split out receive_htlcs from the forwarding pipeline - #3973

Closed
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs
Closed

Split out receive_htlcs from the forwarding pipeline#3973
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs

Conversation

@tnull

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Jul 30, 2025

Copy link
Copy Markdown

👋 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.

@tnulltnull self-assigned this Jul 30, 2025
@tnulltnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsJul 30, 2025
@codecov

codecovBot commented Jul 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.82456% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.61%. Comparing base (96f9242) to head (e08d3dc).
⚠️ Report is 1614 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channelmanager.rs78.43%10 Missing and 1 partial ⚠️
lightning/src/ln/onion_route_tests.rs79.48%8 Missing ⚠️
lightning/src/ln/payment_tests.rs83.33%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3973 +/- ##
==========================================
- Coverage 88.61% 88.61% -0.01% 
==========================================
Files 174 174 Lines 127640 127684 +44 Branches 127640 127684 +44 ==========================================
+ Hits 113113 113148 +35 - Misses 12046 12056 +10 + Partials 2481 2480 -1 
FlagCoverage Δ
tests88.61% <79.82%> (-0.01%)⬇️

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

☔ View full report in Codecov by Harness.
📢 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.

Comment threadlightning/src/ln/channelmanager.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940CompareJuly 30, 2025 15:00
Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnull removed the request for review from valentinewallaceJuly 31, 2025 06:01
@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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
Copy Markdown

🔔 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.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

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.

Comment threadlightning/src/ln/onion_route_tests.rs
continue;
}
},
_ => {},

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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?)

Copy link
Copy Markdown
ContributorAuthor

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.

Yes, they would. I guess it would be unreachable (or you could say the if short_channel_id == 0 is redundant), but the main point is that we just fall through to previous behavior.

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?)

You mean fail the deserialization?

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/payment_tests.rs
Comment threadlightning/src/ln/payment_tests.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ffCompareAugust 5, 2025 08:51
@tnull

tnull commented Aug 6, 2025

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It doesn't get removed though? And I put it in this commit as it's fixing a pre-existing bug that was surfaced by making the tests stricter: previously, pending_forwards was simply empty here and we weren't checking anything. Will move it to a separate, commit at the beginning.

/// 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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Added. Although I wonder if it would make sense to also add a separate field for trampoline to finally completely get away from the magic number?

}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();

Copy link
Copy Markdown
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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, good point. I guess we'd need to start writing both for a while (also cf. #3973 (comment)), and make sure we only migrate HTLCs for which we don't already track the same prev_htlc_id.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, I considered doing that, but chose not to as it would mean we'd have to keep the old types around to deserialize the legacy forwards on upgrade.

I agree it would be cleaner going forward to add an enum HTLCReceiveInfo and keep copies of HTLCForwardInfo/PendingAddHTLCInfo as LegacyHTLCForwardInfo and LegacyPendingAddHTLCInfo that we can eventually drop after some time.

Do you agree with that approach?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's what I was thinking basically.

@tnulltnull mentioned this pull request Aug 11, 2025
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),

forward_htlcs: Mutex::new(forward_htlcs),
receive_htlcs: Mutex::new(receive_htlcs),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Earlier in this method, we remove pending payments that are not present in the monitors, and ISTM we should be doing this for receive_htlcs now as well

Previously, the `pending_forwards` in this test would simply be empty,
having the test not run any of the subsequent logic.
Here we add a necessary `process_pending_update_add_htlcs()` step, and
will move to a more strict test logic in the following commits.
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.
.. to keep changes in the following commits minimal.
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
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from e681129 to e08d3dcCompareAugust 20, 2025 09:01
@tnulltnull removed the weekly goal Someone wants to land this this week label Nov 13, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

Hi @tnull,

Thanks for your contributions to rust-lightning!

After too many struggles with bugs, outages, contributor bans, and, finally, a multi-week CI ban, the rust-lightning project is moving off of GitHub for day-to-day development.

You can still file issues and access the git tree here, but PRs will now take place exclusively at https://git.rust-bitcoin.org/. As such, this PR has been migrated to https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/3973

If you log in using GitHub (or otherwise link your GitHub account from https://git.rust-bitcoin.org/user/settings/security), ownership of your PRs, issues, and comments will automatically transfer. To push updates to this PR, you'll need to use git push git@gitea-ssh.bitcoin.ninja:lightningdevkit/rust-lightning YOUR_LOCAL_COMMIT_OR_BRANCH:tnull/2025-07-split-out-receive-htlcs. This may require a permissions change - if it doesn't work initially just leave a comment and we'll get you access.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@tnull@ldk-reviews-bot@TheBlueMatt@martinsaposnic@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Split out `receive_htlcs` from the forwarding pipeline by tnull · Pull Request #3973 · lightningdevkit/rust-lightning · GitHub
Skip to content

Split out receive_htlcs from the forwarding pipeline - #3973

Closed
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs
Closed

Split out receive_htlcs from the forwarding pipeline#3973
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs

Conversation

@tnull

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Jul 30, 2025

Copy link
Copy Markdown

👋 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.

@tnulltnull self-assigned this Jul 30, 2025
@tnulltnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsJul 30, 2025
@codecov

codecovBot commented Jul 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.82456% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.61%. Comparing base (96f9242) to head (e08d3dc).
⚠️ Report is 1614 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channelmanager.rs78.43%10 Missing and 1 partial ⚠️
lightning/src/ln/onion_route_tests.rs79.48%8 Missing ⚠️
lightning/src/ln/payment_tests.rs83.33%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3973 +/- ##
==========================================
- Coverage 88.61% 88.61% -0.01% 
==========================================
Files 174 174 Lines 127640 127684 +44 Branches 127640 127684 +44 ==========================================
+ Hits 113113 113148 +35 - Misses 12046 12056 +10 + Partials 2481 2480 -1 
FlagCoverage Δ
tests88.61% <79.82%> (-0.01%)⬇️

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

☔ View full report in Codecov by Harness.
📢 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.

Comment threadlightning/src/ln/channelmanager.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940CompareJuly 30, 2025 15:00
Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnull removed the request for review from valentinewallaceJuly 31, 2025 06:01
@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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
Copy Markdown

🔔 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.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

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.

Comment threadlightning/src/ln/onion_route_tests.rs
continue;
}
},
_ => {},

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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?)

Copy link
Copy Markdown
ContributorAuthor

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.

Yes, they would. I guess it would be unreachable (or you could say the if short_channel_id == 0 is redundant), but the main point is that we just fall through to previous behavior.

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?)

You mean fail the deserialization?

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/payment_tests.rs
Comment threadlightning/src/ln/payment_tests.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ffCompareAugust 5, 2025 08:51
@tnull

tnull commented Aug 6, 2025

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It doesn't get removed though? And I put it in this commit as it's fixing a pre-existing bug that was surfaced by making the tests stricter: previously, pending_forwards was simply empty here and we weren't checking anything. Will move it to a separate, commit at the beginning.

/// 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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Added. Although I wonder if it would make sense to also add a separate field for trampoline to finally completely get away from the magic number?

}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();

Copy link
Copy Markdown
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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, good point. I guess we'd need to start writing both for a while (also cf. #3973 (comment)), and make sure we only migrate HTLCs for which we don't already track the same prev_htlc_id.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, I considered doing that, but chose not to as it would mean we'd have to keep the old types around to deserialize the legacy forwards on upgrade.

I agree it would be cleaner going forward to add an enum HTLCReceiveInfo and keep copies of HTLCForwardInfo/PendingAddHTLCInfo as LegacyHTLCForwardInfo and LegacyPendingAddHTLCInfo that we can eventually drop after some time.

Do you agree with that approach?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's what I was thinking basically.

@tnulltnull mentioned this pull request Aug 11, 2025
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),

forward_htlcs: Mutex::new(forward_htlcs),
receive_htlcs: Mutex::new(receive_htlcs),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Earlier in this method, we remove pending payments that are not present in the monitors, and ISTM we should be doing this for receive_htlcs now as well

Previously, the `pending_forwards` in this test would simply be empty,
having the test not run any of the subsequent logic.
Here we add a necessary `process_pending_update_add_htlcs()` step, and
will move to a more strict test logic in the following commits.
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.
.. to keep changes in the following commits minimal.
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
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from e681129 to e08d3dcCompareAugust 20, 2025 09:01
@tnulltnull removed the weekly goal Someone wants to land this this week label Nov 13, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

Hi @tnull,

Thanks for your contributions to rust-lightning!

After too many struggles with bugs, outages, contributor bans, and, finally, a multi-week CI ban, the rust-lightning project is moving off of GitHub for day-to-day development.

You can still file issues and access the git tree here, but PRs will now take place exclusively at https://git.rust-bitcoin.org/. As such, this PR has been migrated to https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/3973

If you log in using GitHub (or otherwise link your GitHub account from https://git.rust-bitcoin.org/user/settings/security), ownership of your PRs, issues, and comments will automatically transfer. To push updates to this PR, you'll need to use git push git@gitea-ssh.bitcoin.ninja:lightningdevkit/rust-lightning YOUR_LOCAL_COMMIT_OR_BRANCH:tnull/2025-07-split-out-receive-htlcs. This may require a permissions change - if it doesn't work initially just leave a comment and we'll get you access.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@tnull@ldk-reviews-bot@TheBlueMatt@martinsaposnic@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Split out `receive_htlcs` from the forwarding pipeline by tnull · Pull Request #3973 · lightningdevkit/rust-lightning · GitHub
Skip to content

Split out receive_htlcs from the forwarding pipeline - #3973

Closed
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs
Closed

Split out receive_htlcs from the forwarding pipeline#3973
tnull wants to merge 5 commits into
lightningdevkit:mainfrom
tnull:2025-07-split-out-receive-htlcs

Conversation

@tnull

Copy link
Copy Markdown
Contributor

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

ldk-reviews-bot commented Jul 30, 2025

Copy link
Copy Markdown

👋 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.

@tnulltnull self-assigned this Jul 30, 2025
@tnulltnull added the weekly goal Someone wants to land this this week label Jul 30, 2025
@tnulltnull moved this to Goal: Merge in Weekly GoalsJul 30, 2025
@codecov

codecovBot commented Jul 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.82456% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.61%. Comparing base (96f9242) to head (e08d3dc).
⚠️ Report is 1614 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/channelmanager.rs78.43%10 Missing and 1 partial ⚠️
lightning/src/ln/onion_route_tests.rs79.48%8 Missing ⚠️
lightning/src/ln/payment_tests.rs83.33%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3973 +/- ##
==========================================
- Coverage 88.61% 88.61% -0.01% 
==========================================
Files 174 174 Lines 127640 127684 +44 Branches 127640 127684 +44 ==========================================
+ Hits 113113 113148 +35 - Misses 12046 12056 +10 + Partials 2481 2480 -1 
FlagCoverage Δ
tests88.61% <79.82%> (-0.01%)⬇️

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

☔ View full report in Codecov by Harness.
📢 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.

Comment threadlightning/src/ln/channelmanager.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from b360839 to 2f5e940CompareJuly 30, 2025 15:00
Comment threadlightning/src/ln/channelmanager.rs Outdated
@tnull
tnull removed the request for review from valentinewallaceJuly 31, 2025 06:01
@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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
Copy Markdown

🔔 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.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

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.

Comment threadlightning/src/ln/onion_route_tests.rs
continue;
}
},
_ => {},

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
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?)

Copy link
Copy Markdown
ContributorAuthor

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.

Yes, they would. I guess it would be unreachable (or you could say the if short_channel_id == 0 is redundant), but the main point is that we just fall through to previous behavior.

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?)

You mean fail the deserialization?

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/ln/payment_tests.rs
Comment threadlightning/src/ln/payment_tests.rs
@tnull
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from cad3d32 to c6ba5ffCompareAugust 5, 2025 08:51
@tnull

tnull commented Aug 6, 2025

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

It doesn't get removed though? And I put it in this commit as it's fixing a pre-existing bug that was surfaced by making the tests stricter: previously, pending_forwards was simply empty here and we weren't checking anything. Will move it to a separate, commit at the beginning.

/// 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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Added. Although I wonder if it would make sense to also add a separate field for trampoline to finally completely get away from the magic number?

}
}

let receive_htlcs = self.receive_htlcs.lock().unwrap();

Copy link
Copy Markdown
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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, good point. I guess we'd need to start writing both for a while (also cf. #3973 (comment)), and make sure we only migrate HTLCs for which we don't already track the same prev_htlc_id.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Okay, I considered doing that, but chose not to as it would mean we'd have to keep the old types around to deserialize the legacy forwards on upgrade.

I agree it would be cleaner going forward to add an enum HTLCReceiveInfo and keep copies of HTLCForwardInfo/PendingAddHTLCInfo as LegacyHTLCForwardInfo and LegacyPendingAddHTLCInfo that we can eventually drop after some time.

Do you agree with that approach?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's what I was thinking basically.

@tnulltnull mentioned this pull request Aug 11, 2025
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),

forward_htlcs: Mutex::new(forward_htlcs),
receive_htlcs: Mutex::new(receive_htlcs),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Earlier in this method, we remove pending payments that are not present in the monitors, and ISTM we should be doing this for receive_htlcs now as well

Previously, the `pending_forwards` in this test would simply be empty,
having the test not run any of the subsequent logic.
Here we add a necessary `process_pending_update_add_htlcs()` step, and
will move to a more strict test logic in the following commits.
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.
.. to keep changes in the following commits minimal.
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
tnullforce-pushed the 2025-07-split-out-receive-htlcs branch from e681129 to e08d3dcCompareAugust 20, 2025 09:01
@tnulltnull removed the weekly goal Someone wants to land this this week label Nov 13, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

Hi @tnull,

Thanks for your contributions to rust-lightning!

After too many struggles with bugs, outages, contributor bans, and, finally, a multi-week CI ban, the rust-lightning project is moving off of GitHub for day-to-day development.

You can still file issues and access the git tree here, but PRs will now take place exclusively at https://git.rust-bitcoin.org/. As such, this PR has been migrated to https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/pulls/3973

If you log in using GitHub (or otherwise link your GitHub account from https://git.rust-bitcoin.org/user/settings/security), ownership of your PRs, issues, and comments will automatically transfer. To push updates to this PR, you'll need to use git push git@gitea-ssh.bitcoin.ninja:lightningdevkit/rust-lightning YOUR_LOCAL_COMMIT_OR_BRANCH:tnull/2025-07-split-out-receive-htlcs. This may require a permissions change - if it doesn't work initially just leave a comment and we'll get you access.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@tnull@ldk-reviews-bot@TheBlueMatt@martinsaposnic@valentinewallace