Skip to content

feat: Implement self payment #3934

New issue

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

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

Already on GitHub? Sign in to your account

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

Conversation

Prabhat1308
Copy link
Contributor

fixes #2462
revives #2573

This PR implements the option for self payment for ldk nodes .

PR is right now marked as draft as this is a very rough implementation and I am looking for suggestions / improvements , some of them
1 . should an option to make BlindedPath for self-routes be made ?
2 . should MPP be allowed ? (Personally I think this is a bad idea , we should use BlindedPath instead if we want privacy otherwise normal way should suffice)
3. Should all self-payments be spontaneous ? an observer might be able to figure out that that we are paying to ourselves if there are a lot of spontaneous payments !
4. My bad code ?

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Jul 15, 2025

👋 I see @joostjager was un-assigned.
If you'd like another reviewer assignment, please click here.

@Prabhat1308 Prabhat1308 changed the title feat: add rough logic for self payment feat: add logic for self payment Jul 15, 2025
@Prabhat1308 Prabhat1308 changed the title feat: add logic for self payment feat: Implement self payment Jul 15, 2025
@tnull tnull self-requested a review July 16, 2025 07:58
Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

I think it would be preferable if we'd avoid adding a special API for self-payments at this point. Can't we just detect that it's a self payment in send_payment and act accordingly?

///
/// # Returns
/// Returns the payment hash on success, or an APIError on failure.
pub fn send_self_payment(
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, not sure we want to add a special API just for this 1-hop circular route. Can we reuse the existing APIs, i.e. detect that we're the recipient and act accordingly? Then, for #3791, we'll need to add an API that allows longer circular routes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I originally thought of using send_payment_with_route since we know the route we want the payment to take . I should be able to incorporate it in that .

// Self payment scid is 0 to avoid confusion with real channels
/// A special short channel ID (0) used to indicate a self-payment.
/// When routing a payment to ourselves, the first hop will have this SCID.
pub const SCID_SELF_PAYMENT: u64 = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, when handling HTLCs we're already leaning on the short_channel_id == 0 special case for receiving. So I guess that's preexisting. Still, I'm not the biggest fan of such magic numbers. Do you think there would be a way to avoid this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A way I thought of is to change Route or RouteParameters to include a is_self_payment flag which can be used to detect if a payment is a self-payment or we could use if path.hops.first().unwrap().pubkey == our_node_pubkey but I don't feel good about the idea of having to pass our_node_pubkey deep in the api calls to send_payment_along_path. either way we would want to have a magic number for dummy scid (not specific for self-payments but general ) and settle on it ?

Copy link
Contributor

@tnull tnull Jul 17, 2025

Choose a reason for hiding this comment

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

or we could use if path.hops.first().unwrap().pubkey == our_node_pubkey but I don't feel good about the idea of having to pass our_node_pubkey deep in the api calls to send_payment_along_path

Hmm, but in ChannelManager (hence also in send_payment_along_path) we should have our pubkey readily available at self.our_network_pubkey, no?

@ldk-reviews-bot
Copy link

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@tnull tnull mentioned this pull request Jul 16, 2025
@Prabhat1308 Prabhat1308 force-pushed the probot/self-payment branch from 955c11c to 7d78e4e Compare July 19, 2025 18:04
Copy link

codecov bot commented Jul 19, 2025

Codecov Report

Attention: Patch coverage is 0% with 21 lines in your changes missing coverage. Please review.

Project coverage is 90.80%. Comparing base (42085b9) to head (7d78e4e).
Report is 92 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/routing/router.rs 0.00% 19 Missing ⚠️
lightning/src/ln/channelmanager.rs 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3934      +/-   ##
==========================================
+ Coverage   88.84%   90.80%   +1.96%     
==========================================
  Files         166      167       +1     
  Lines      119487   143472   +23985     
  Branches   119487   143472   +23985     
==========================================
+ Hits       106157   130285   +24128     
+ Misses      11006    10722     -284     
- Partials     2324     2465     +141     
Flag Coverage Δ
fuzz 22.72% <0.00%> (?)

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.

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Please undraft/request review when you think this is ready for another look or have any more particular questions!

Normal lightning workflow does not know how to handle self payment as the route created is to self . This will not be found in short_to_chan_info as there cant be channel created where both the nodes are the same. This handles that scenario by handling the payment before the lookup is hit
@Prabhat1308 Prabhat1308 marked this pull request as ready for review July 23, 2025 13:40
hops: vec![RouteHop {
pubkey: our_node_pubkey.clone(),
short_channel_id: 0 , // Dummy short_channel_id specifying self payment
fee_msat: 0, // Zero fees
Copy link

Choose a reason for hiding this comment

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

The cltv_expiry_delta field is currently hardcoded to MIN_FINAL_CLTV_EXPIRY_DELTA, but it should instead use the value from route_params.final_cltv_expiry_delta to maintain consistency with the requested payment parameters. This ensures that self-payments respect the same CLTV constraints as regular payments.

Spotted by Diamond

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

@Prabhat1308 Prabhat1308 force-pushed the probot/self-payment branch from 61ebe05 to 39643a5 Compare July 23, 2025 13:54
@joostjager
Copy link
Contributor

I don't think this is ready for 2nd reviewer, or is it?

Comment on lines +4957 to +4966
if keysend_preimage.is_some() {
pending_events.push_back((events::Event::PaymentSent {
payment_id: Some(payment_id),
payment_preimage: keysend_preimage.unwrap(),
payment_hash: *payment_hash,
amount_msat: Some(htlc_msat),
fee_paid_msat: Some(0), // No fees for self-payments
bolt12_invoice: None,
}, None));
}
Copy link

Choose a reason for hiding this comment

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

The implementation currently generates PaymentSent events only for spontaneous payments (with keysend_preimage), but not for Bolt11 invoice payments. This creates inconsistent behavior where Bolt11 self-payments will appear as claimable but never as sent.

For consistency in the payment lifecycle, both types of self-payments should generate both events. Consider modifying the code to handle Bolt11 self-payments similarly to spontaneous payments by generating the appropriate PaymentSent event when the payment is claimed.

Suggested change
if keysend_preimage.is_some() {
pending_events.push_back((events::Event::PaymentSent {
payment_id: Some(payment_id),
payment_preimage: keysend_preimage.unwrap(),
payment_hash: *payment_hash,
amount_msat: Some(htlc_msat),
fee_paid_msat: Some(0), // No fees for self-payments
bolt12_invoice: None,
}, None));
}
// Generate PaymentSent event for both keysend and Bolt11 self-payments
pending_events.push_back((events::Event::PaymentSent {
payment_id: Some(payment_id),
payment_preimage: keysend_preimage.unwrap_or_else(|| payment_preimage.unwrap()),
payment_hash: *payment_hash,
amount_msat: Some(htlc_msat),
fee_paid_msat: Some(0), // No fees for self-payments
bolt12_invoice: None,
}, None));

Spotted by Diamond

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

@Prabhat1308
Copy link
Contributor Author

I don't think this is ready for 2nd reviewer, or is it?

I just opened it from draft to get feedback and bot immediately assigned .

@joostjager joostjager removed their request for review July 23, 2025 16:51
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.

Allow self-payment
4 participants