Skip to content

Add 'send_circular_payment' helper and 'send_spontaneous_payment_with…#4616

Open
Ferryx349 wants to merge 1 commit into
lightningdevkit:mainfrom
Ferryx349:cir-reb
Open

Add 'send_circular_payment' helper and 'send_spontaneous_payment_with…#4616
Ferryx349 wants to merge 1 commit into
lightningdevkit:mainfrom
Ferryx349:cir-reb

Conversation

@Ferryx349
Copy link
Copy Markdown

@Ferryx349 Ferryx349 commented May 17, 2026

Fixes- #3791
This PR adds two new methods to ChannelManager:

send_circular_payment

A helper for circular channel rebalancing. It:

  1. Validates that two provided channel IDs are distinct and usable.
  2. Calls find_route targeting the inbound channel's counterparty, with first_hops restricted to only the outbound channel to force the router to use it as the first hop.
  3. Manually appends a final RouteHop back to our own node over the inbound channel's SCID, with fee_msat set to the full payment amount as required for the last hop and the preceding hop's fee_msat adjusted to counterparty's forwarding fee.
  4. Sends result as a spontaneous payment via send_spontaneous_payment_with_route.

The existing guard in pay_route_internal already permits our node as the last hop in a path ("simple rebalance loop to us"), so no changes to the payment sending internals were needed.

Tests

  1. test_circular_payment_rebalance: covers the happy path (0→1→2→0), same-channel error, and channel-not-found error.
  2. test_circular_payment_no_route: verifies RouteNotFound is returned when no path exists between the two counterparties.

This is my first contribution to LDK. I tried to follow the patterns established by send_payment_with_route and send_spontaneous_preflight_probes as closely as possible. Happy to revise anything that doesn't match project conventions.

CC: @tnull

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented May 17, 2026

I've assigned @wpaulino 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.

@ldk-reviews-bot ldk-reviews-bot requested a review from wpaulino May 17, 2026 03:32
Comment thread lightning/src/ln/channelmanager.rs
Comment thread lightning/src/ln/channelmanager.rs Outdated
Comment thread lightning/src/ln/channelmanager.rs Outdated
Comment thread lightning/src/ln/channelmanager.rs
@ldk-claude-review-bot
Copy link
Copy Markdown
Collaborator

ldk-claude-review-bot commented May 17, 2026

Review Summary

New Issues Found

Inline comment posted:

  1. lightning/src/ln/channelmanager.rs:6218-6219 — Potential integer overflow in forwarding fee calculation. The unchecked fee_proportional_millionths * amount_msat can overflow u64. The channel forwarding code uses checked_mul for the same operation.

Prior Comments Status

The issues from the previous review pass appear to have been addressed:

  • cltv_expiry_delta is now correctly updated on prev_last (line 6247)
  • forwarding_info None case returns early with an error (line 6217)
  • forwarding_info.cltv_expiry_delta is used in PaymentParameters (line 6220, 6223)
  • DRY issue resolved via route_params_for_fixed_route helper
  • [2; 32] bug fixed to [2; 33] (line 5594)

The prior comments about doc quality on send_spontaneous_payment_with_route and the prev_last.cltv_expiry_delta update still apply if they haven't been explicitly acknowledged/resolved by the author.

Cross-cutting notes

  • The &&router to &router change in send_payment_with_route (line 5625) is a harmless cleanup — both work due to the blanket impl<T: Router + ?Sized, R: Deref<Target = T>> Router for R at router.rs:293.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 17, 2026

Codecov Report

❌ Patch coverage is 94.23077% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.43%. Comparing base (946ee09) to head (e6dfc3a).
⚠️ Report is 24 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/channelmanager.rs 94.23% 2 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4616      +/-   ##
==========================================
+ Coverage   86.11%   86.43%   +0.32%     
==========================================
  Files         157      158       +1     
  Lines      108841   109420     +579     
  Branches   108841   109420     +579     
==========================================
+ Hits        93725    94578     +853     
+ Misses      12497    12299     -198     
+ Partials     2619     2543      -76     
Flag Coverage Δ
fuzzing-fake-hashes 5.07% <0.00%> (?)
fuzzing-real-hashes 22.73% <7.69%> (?)
tests 86.16% <94.23%> (+0.05%) ⬆️

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

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

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

Comment thread lightning/src/ln/channelmanager.rs Outdated
Comment thread lightning/src/ln/channelmanager.rs Outdated
Comment thread lightning/src/ln/channelmanager.rs Outdated
…_route' to ChannelManager

Signed-off-by: ABHAY PANDEY <pandeyabhay967@gmail.com>
Comment on lines +6218 to +6219
let forwarding_fee = forwarding_info.fee_base_msat as u64
+ (forwarding_info.fee_proportional_millionths as u64 * amount_msat) / 1000000;
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.

Nit/correctness: The multiplication fee_proportional_millionths as u64 * amount_msat can overflow u64 for extreme (but valid) values of the two operands. The channel forwarding code at channel.rs:11335 uses checked_mul for the same fee calculation:

let fee = amt_to_forward.checked_mul(config.forwarding_fee_proportional_millionths as u64)
    .and_then(|prop_fee| (prop_fee / 1000000).checked_add(config.forwarding_fee_base_msat as u64));

Consider using checked arithmetic here to be consistent and avoid a wrapping fee (which would cause a confusing async payment failure):

Suggested change
let forwarding_fee = forwarding_info.fee_base_msat as u64
+ (forwarding_info.fee_proportional_millionths as u64 * amount_msat) / 1000000;
let forwarding_fee = (forwarding_info.fee_proportional_millionths as u64)
.checked_mul(amount_msat)
.and_then(|prop_fee| (prop_fee / 1_000_000)
.checked_add(forwarding_info.fee_base_msat as u64))
.ok_or(RetryableSendFailure::RouteNotFound)?;

Sign up for free to 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.

3 participants