Skip to content

Commit 3d9456c

Browse files
feat: allow self payment
This PR solves issue #2462. If we asked to pay an invoice that we generated ourselves. We generate PaymentSent and PaymentClaimable event and mark the payment as fulfilled in our set of outbound payments. This PR is important because we realized users can easily screw up self payments when they implement it themselves. See here: https://lists.linuxfoundation.org/pipermail/lightning-dev/2023-June/003983.html
1 parent 61d896d commit 3d9456c

File tree

3 files changed

+62
-14
lines changed

3 files changed

+62
-14
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3348,8 +3348,17 @@ where
33483348
pub fn send_payment(&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<(), RetryableSendFailure> {
33493349
let best_block_height = self.best_block.read().unwrap().height();
33503350
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
3351+
let preimage = match route_params.payment_params.payee {
3352+
Payee::Clear { node_id, .. } if node_id == self.get_our_node_id() => {
3353+
match recipient_onion.payment_secret {
3354+
Some(payment_secret) => self.get_payment_preimage(payment_hash, payment_secret).ok(),
3355+
None => None,
3356+
}
3357+
},
3358+
_ => None,
3359+
};
33513360
self.pending_outbound_payments
3352-
.send_payment(payment_hash, recipient_onion, payment_id, retry_strategy, route_params,
3361+
.send_payment(payment_hash, preimage, recipient_onion, payment_id, retry_strategy, route_params,
33533362
&self.router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
33543363
&self.entropy_source, &self.node_signer, best_block_height, &self.logger,
33553364
&self.pending_events, |args| self.send_payment_along_path(args))

lightning/src/ln/outbound_payment.rs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ use bitcoin::hashes::sha256::Hash as Sha256;
1414
use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
1515

1616
use crate::sign::{EntropySource, NodeSigner, Recipient};
17-
use crate::events::{self, PaymentFailureReason};
17+
use crate::events::{self, PaymentFailureReason, Event, PaymentPurpose};
1818
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
1919
use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
2020
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
21-
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
21+
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router, Payee};
2222
use crate::util::errors::APIError;
2323
use crate::util::logger::Logger;
2424
use crate::util::time::Time;
@@ -552,7 +552,7 @@ impl OutboundPayments {
552552
}
553553

554554
pub(super) fn send_payment<R: Deref, ES: Deref, NS: Deref, IH, SP, L: Deref>(
555-
&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId,
555+
&self, payment_hash: PaymentHash, payment_preimage: Option<PaymentPreimage>, recipient_onion: RecipientOnionFields, payment_id: PaymentId,
556556
retry_strategy: Retry, route_params: RouteParameters, router: &R,
557557
first_hops: Vec<ChannelDetails>, compute_inflight_htlcs: IH, entropy_source: &ES,
558558
node_signer: &NS, best_block_height: u32, logger: &L,
@@ -566,7 +566,7 @@ impl OutboundPayments {
566566
IH: Fn() -> InFlightHtlcs,
567567
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
568568
{
569-
self.send_payment_internal(payment_id, payment_hash, recipient_onion, None, retry_strategy,
569+
self.send_payment_internal(payment_id, payment_hash, payment_preimage, recipient_onion, None, retry_strategy,
570570
route_params, router, first_hops, &compute_inflight_htlcs, entropy_source, node_signer,
571571
best_block_height, logger, pending_events, &send_payment_along_path)
572572
}
@@ -605,7 +605,7 @@ impl OutboundPayments {
605605
let preimage = payment_preimage
606606
.unwrap_or_else(|| PaymentPreimage(entropy_source.get_secure_random_bytes()));
607607
let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
608-
self.send_payment_internal(payment_id, payment_hash, recipient_onion, Some(preimage),
608+
self.send_payment_internal(payment_id, payment_hash, None, recipient_onion, Some(preimage),
609609
retry_strategy, route_params, router, first_hops, inflight_htlcs, entropy_source,
610610
node_signer, best_block_height, logger, pending_events, send_payment_along_path)
611611
.map(|()| payment_hash)
@@ -706,7 +706,7 @@ impl OutboundPayments {
706706
/// [`Event::PaymentPathFailed`]: crate::events::Event::PaymentPathFailed
707707
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
708708
fn send_payment_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
709-
&self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
709+
&self, payment_id: PaymentId, payment_hash: PaymentHash, payment_preimage: Option<PaymentPreimage>, recipient_onion: RecipientOnionFields,
710710
keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, route_params: RouteParameters,
711711
router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
712712
node_signer: &NS, best_block_height: u32, logger: &L,
@@ -728,8 +728,9 @@ impl OutboundPayments {
728728
}
729729
}
730730

731+
let payer_pubkey = node_signer.get_node_id(Recipient::Node).unwrap();
731732
let route = router.find_route_with_id(
732-
&node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
733+
&payer_pubkey, &route_params,
733734
Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),
734735
payment_hash, payment_id,
735736
).map_err(|_| {
@@ -747,6 +748,35 @@ impl OutboundPayments {
747748
RetryableSendFailure::DuplicatePayment
748749
})?;
749750

751+
752+
match route_params.payment_params.payee {
753+
Payee::Clear { node_id, .. } if node_id == payer_pubkey => {
754+
let payment_secret = match recipient_onion.payment_secret {
755+
Some(secret) => secret,
756+
None => PaymentSecret([0; 32])
757+
};
758+
let payment_purpose = PaymentPurpose::InvoicePayment {
759+
payment_preimage,
760+
payment_secret,
761+
};
762+
763+
let payment_preimage = payment_preimage
764+
.unwrap_or_else(|| PaymentPreimage(entropy_source.get_secure_random_bytes()));
765+
let mut pending_events_lock = pending_events.lock().unwrap();
766+
pending_events_lock.push_back((Event::PaymentSent { payment_id: Some(payment_id), payment_preimage,
767+
payment_hash, fee_paid_msat: None }, None));
768+
pending_events_lock.push_back((Event::PaymentClaimable { receiver_node_id: Some(payer_pubkey), payment_hash,
769+
onion_fields: Some(recipient_onion), amount_msat: route_params.final_value_msat, counterparty_skimmed_fee_msat: 0,
770+
purpose: payment_purpose, via_channel_id: None, via_user_channel_id: None, claim_deadline: None }, None));
771+
772+
let mut pending_outbounds_lock = self.pending_outbound_payments.lock().unwrap();
773+
let payment = pending_outbounds_lock.get_mut(&payment_id).unwrap();
774+
payment.mark_fulfilled();
775+
return Ok(());
776+
},
777+
_ => {},
778+
}
779+
750780
let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage, payment_id, None,
751781
onion_session_privs, node_signer, best_block_height, &send_payment_along_path);
752782
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
@@ -1586,7 +1616,7 @@ mod tests {
15861616
} else { panic!("Unexpected event"); }
15871617
} else {
15881618
let err = outbound_payments.send_payment(
1589-
PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
1619+
PaymentHash([0; 32]), None, RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
15901620
Retry::Attempts(0), expired_route_params, &&router, vec![], || InFlightHtlcs::new(),
15911621
&&keys_manager, &&keys_manager, 0, &&logger, &pending_events, |_| Ok(())).unwrap_err();
15921622
if let RetryableSendFailure::PaymentExpired = err { } else { panic!("Unexpected error"); }
@@ -1631,7 +1661,7 @@ mod tests {
16311661
if let Event::PaymentFailed { .. } = events[0].0 { } else { panic!("Unexpected event"); }
16321662
} else {
16331663
let err = outbound_payments.send_payment(
1634-
PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
1664+
PaymentHash([0; 32]), None, RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
16351665
Retry::Attempts(0), route_params, &&router, vec![], || InFlightHtlcs::new(),
16361666
&&keys_manager, &&keys_manager, 0, &&logger, &pending_events, |_| Ok(())).unwrap_err();
16371667
if let RetryableSendFailure::RouteNotFound = err {
@@ -1679,7 +1709,7 @@ mod tests {
16791709
// PaymentPathFailed event.
16801710
let pending_events = Mutex::new(VecDeque::new());
16811711
outbound_payments.send_payment(
1682-
PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
1712+
PaymentHash([0; 32]), None, RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
16831713
Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(),
16841714
&&keys_manager, &&keys_manager, 0, &&logger, &pending_events,
16851715
|_| Err(APIError::ChannelUnavailable { err: "test".to_owned() })).unwrap();
@@ -1697,15 +1727,15 @@ mod tests {
16971727

16981728
// Ensure that a MonitorUpdateInProgress "error" will not result in a PaymentPathFailed event.
16991729
outbound_payments.send_payment(
1700-
PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
1730+
PaymentHash([0; 32]), None, RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
17011731
Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(),
17021732
&&keys_manager, &&keys_manager, 0, &&logger, &pending_events,
17031733
|_| Err(APIError::MonitorUpdateInProgress)).unwrap();
17041734
assert_eq!(pending_events.lock().unwrap().len(), 0);
17051735

17061736
// Ensure that any other error will result in a PaymentPathFailed event but no blamed scid.
17071737
outbound_payments.send_payment(
1708-
PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([1; 32]),
1738+
PaymentHash([0; 32]), None, RecipientOnionFields::spontaneous_empty(), PaymentId([1; 32]),
17091739
Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(),
17101740
&&keys_manager, &&keys_manager, 0, &&logger, &pending_events,
17111741
|_| Err(APIError::APIMisuseError { err: "test".to_owned() })).unwrap();

lightning/src/routing/router.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1423,7 +1423,16 @@ where L::Target: Logger {
14231423
let our_node_id = NodeId::from_pubkey(&our_node_pubkey);
14241424

14251425
if payee_node_id_opt.map_or(false, |payee| payee == our_node_id) {
1426-
return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError});
1426+
let dummy_path = Path { hops: vec![RouteHop {
1427+
pubkey: our_node_pubkey.clone(),
1428+
short_channel_id: 0,
1429+
node_features: NodeFeatures::empty(),
1430+
channel_features: ChannelFeatures::empty(),
1431+
fee_msat: 0,
1432+
cltv_expiry_delta: 0,
1433+
1434+
}], blinded_tail: None };
1435+
return Ok(Route { paths: vec![dummy_path], payment_params: Some(payment_params.clone()) });
14271436
}
14281437

14291438
if final_value_msat > MAX_VALUE_MSAT {

0 commit comments

Comments
 (0)