Skip to content

Commit fe79793

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 d2242f6 commit fe79793

File tree

3 files changed

+68
-21
lines changed

3 files changed

+68
-21
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ pub enum FailureCode {
410410
}
411411

412412
impl Into<u16> for FailureCode {
413-
fn into(self) -> u16 {
413+
fn into(self) -> u16 {
414414
match self {
415415
FailureCode::TemporaryNodeFailure => 0x2000 | 2,
416416
FailureCode::RequiredNodeFeatureMissing => 0x4000 | 0x2000 | 3,
@@ -3546,7 +3546,7 @@ where
35463546
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
35473547
self.pending_outbound_payments
35483548
.send_payment_with_route(route, payment_hash, recipient_onion, payment_id,
3549-
&self.entropy_source, &self.node_signer, best_block_height,
3549+
&self.entropy_source, &self.node_signer, best_block_height, &self.pending_events,
35503550
|args| self.send_payment_along_path(args))
35513551
}
35523552

@@ -3556,7 +3556,7 @@ where
35563556
let best_block_height = self.best_block.read().unwrap().height();
35573557
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
35583558
self.pending_outbound_payments
3559-
.send_payment(payment_hash, recipient_onion, payment_id, retry_strategy, route_params,
3559+
.send_payment(payment_hash,recipient_onion, payment_id, retry_strategy, route_params,
35603560
&self.router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
35613561
&self.entropy_source, &self.node_signer, best_block_height, &self.logger,
35623562
&self.pending_events, |args| self.send_payment_along_path(args))
@@ -3568,7 +3568,7 @@ where
35683568
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
35693569
self.pending_outbound_payments.test_send_payment_internal(route, payment_hash, recipient_onion,
35703570
keysend_preimage, payment_id, recv_value_msat, onion_session_privs, &self.node_signer,
3571-
best_block_height, |args| self.send_payment_along_path(args))
3571+
best_block_height, &self.pending_events, |args| self.send_payment_along_path(args))
35723572
}
35733573

35743574
#[cfg(test)]
@@ -3644,7 +3644,7 @@ where
36443644
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
36453645
self.pending_outbound_payments.send_spontaneous_payment_with_route(
36463646
route, payment_preimage, recipient_onion, payment_id, &self.entropy_source,
3647-
&self.node_signer, best_block_height, |args| self.send_payment_along_path(args))
3647+
&self.node_signer, best_block_height, &self.pending_events, |args| self.send_payment_along_path(args))
36483648
}
36493649

36503650
/// Similar to [`ChannelManager::send_spontaneous_payment`], but will automatically find a route
@@ -3671,7 +3671,7 @@ where
36713671
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
36723672
self.pending_outbound_payments.send_probe(path, self.probing_cookie_secret,
36733673
&self.entropy_source, &self.node_signer, best_block_height,
3674-
|args| self.send_payment_along_path(args))
3674+
&self.pending_events, |args| self.send_payment_along_path(args))
36753675
}
36763676

36773677
/// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
@@ -6098,7 +6098,7 @@ where
60986098

60996099
let per_peer_state = self.per_peer_state.read().unwrap();
61006100
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
6101-
.ok_or_else(|| {
6101+
.ok_or_else(|| {
61026102
debug_assert!(false);
61036103
MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.temporary_channel_id.clone())
61046104
})?;

lightning/src/ln/outbound_payment.rs

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ 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, PaymentId};
2020
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
@@ -698,7 +698,7 @@ impl OutboundPayments {
698698
pub(super) fn send_payment_with_route<ES: Deref, NS: Deref, F>(
699699
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
700700
payment_id: PaymentId, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
701-
send_payment_along_path: F
701+
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: F
702702
) -> Result<(), PaymentSendFailure>
703703
where
704704
ES::Target: EntropySource,
@@ -707,7 +707,7 @@ impl OutboundPayments {
707707
{
708708
let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, None, route, None, None, entropy_source, best_block_height)?;
709709
self.pay_route_internal(route, payment_hash, recipient_onion, None, payment_id, None,
710-
onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
710+
onion_session_privs, node_signer, best_block_height, pending_events, &send_payment_along_path)
711711
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
712712
}
713713

@@ -738,7 +738,8 @@ impl OutboundPayments {
738738
pub(super) fn send_spontaneous_payment_with_route<ES: Deref, NS: Deref, F>(
739739
&self, route: &Route, payment_preimage: Option<PaymentPreimage>,
740740
recipient_onion: RecipientOnionFields, payment_id: PaymentId, entropy_source: &ES,
741-
node_signer: &NS, best_block_height: u32, send_payment_along_path: F
741+
node_signer: &NS, best_block_height: u32, pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
742+
send_payment_along_path: F
742743
) -> Result<PaymentHash, PaymentSendFailure>
743744
where
744745
ES::Target: EntropySource,
@@ -752,7 +753,7 @@ impl OutboundPayments {
752753
payment_id, Some(preimage), &route, None, None, entropy_source, best_block_height)?;
753754

754755
match self.pay_route_internal(route, payment_hash, recipient_onion, Some(preimage),
755-
payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path
756+
payment_id, None, onion_session_privs, node_signer, best_block_height, pending_events, &send_payment_along_path
756757
) {
757758
Ok(()) => Ok(payment_hash),
758759
Err(e) => {
@@ -903,7 +904,7 @@ impl OutboundPayments {
903904
}
904905

905906
let mut route = router.find_route_with_id(
906-
&node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
907+
&node_signer.get_node_id(Recipient::Node).unwrap(),&route_params,
907908
Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),
908909
payment_hash, payment_id,
909910
).map_err(|_| {
@@ -928,7 +929,7 @@ impl OutboundPayments {
928929
})?;
929930

930931
let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage, payment_id, None,
931-
onion_session_privs, node_signer, best_block_height, &send_payment_along_path);
932+
onion_session_privs, node_signer, best_block_height, pending_events, &send_payment_along_path);
932933
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
933934
payment_id, payment_hash, res);
934935
if let Err(e) = res {
@@ -1087,7 +1088,7 @@ impl OutboundPayments {
10871088
};
10881089
let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage,
10891090
payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height,
1090-
&send_payment_along_path);
1091+
pending_events, &send_payment_along_path);
10911092
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
10921093
if let Err(e) = res {
10931094
self.handle_pay_route_err(e, payment_id, payment_hash, route, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path);
@@ -1173,7 +1174,7 @@ impl OutboundPayments {
11731174

11741175
pub(super) fn send_probe<ES: Deref, NS: Deref, F>(
11751176
&self, path: Path, probing_cookie_secret: [u8; 32], entropy_source: &ES, node_signer: &NS,
1176-
best_block_height: u32, send_payment_along_path: F
1177+
best_block_height: u32, pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: F
11771178
) -> Result<(PaymentHash, PaymentId), PaymentSendFailure>
11781179
where
11791180
ES::Target: EntropySource,
@@ -1197,7 +1198,7 @@ impl OutboundPayments {
11971198
entropy_source, best_block_height)?;
11981199

11991200
match self.pay_route_internal(&route, payment_hash, RecipientOnionFields::spontaneous_empty(),
1200-
None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path
1201+
None, payment_id, None, onion_session_privs, node_signer, best_block_height, pending_events, &send_payment_along_path
12011202
) {
12021203
Ok(()) => Ok((payment_hash, payment_id)),
12031204
Err(e) => {
@@ -1307,7 +1308,7 @@ impl OutboundPayments {
13071308
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
13081309
keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>,
13091310
onion_session_privs: Vec<[u8; 32]>, node_signer: &NS, best_block_height: u32,
1310-
send_payment_along_path: &F
1311+
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: &F
13111312
) -> Result<(), PaymentSendFailure>
13121313
where
13131314
NS::Target: NodeSigner,
@@ -1321,6 +1322,42 @@ impl OutboundPayments {
13211322
{
13221323
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_owned()}));
13231324
}
1325+
1326+
if let Some(path) = route.paths.get(0) {
1327+
// handle self payment if path doesn't have a blinded tail.
1328+
if path.blinded_tail.is_none() {
1329+
let last_hop = path.hops.last().unwrap();
1330+
if node_signer.get_node_id(Recipient::Node).unwrap() == last_hop.pubkey {
1331+
let payment_secret = match recipient_onion.payment_secret {
1332+
Some(secret) => secret,
1333+
None => PaymentSecret([0; 32])
1334+
};
1335+
1336+
let payment_preimage = PaymentPreimage([0; 32]);
1337+
let payment_purpose = PaymentPurpose::InvoicePayment {
1338+
payment_preimage: Some(payment_preimage),
1339+
payment_secret,
1340+
};
1341+
1342+
let mut pending_outbounds_lock = self.pending_outbound_payments.lock().unwrap();
1343+
let payment = pending_outbounds_lock.get_mut(&payment_id).unwrap();
1344+
payment.mark_fulfilled();
1345+
1346+
let mut pending_events_lock = pending_events.lock().unwrap();
1347+
pending_events_lock.push_back((Event::PaymentSent { payment_id: Some(payment_id), payment_preimage,
1348+
payment_hash, fee_paid_msat: None }, None));
1349+
let amt_to_receive = match &route.route_params {
1350+
Some(route_params) => route_params.final_value_msat,
1351+
None => if recv_value_msat.is_some() { recv_value_msat.unwrap() } else { 0 },
1352+
};
1353+
pending_events_lock.push_back((Event::PaymentClaimable { receiver_node_id: Some(last_hop.pubkey), payment_hash,
1354+
onion_fields: Some(recipient_onion), amount_msat: amt_to_receive, counterparty_skimmed_fee_msat: 0,
1355+
purpose: payment_purpose, via_channel_id: None, via_user_channel_id: None, claim_deadline: None }, None));
1356+
return Ok(());
1357+
}
1358+
}
1359+
}
1360+
13241361
let mut total_value = 0;
13251362
let our_node_id = node_signer.get_node_id(Recipient::Node).unwrap(); // TODO no unwrap
13261363
let mut path_errs = Vec::with_capacity(route.paths.len());
@@ -1430,15 +1467,15 @@ impl OutboundPayments {
14301467
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
14311468
keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>,
14321469
onion_session_privs: Vec<[u8; 32]>, node_signer: &NS, best_block_height: u32,
1433-
send_payment_along_path: F
1470+
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: F
14341471
) -> Result<(), PaymentSendFailure>
14351472
where
14361473
NS::Target: NodeSigner,
14371474
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
14381475
{
14391476
self.pay_route_internal(route, payment_hash, recipient_onion, keysend_preimage, payment_id,
14401477
recv_value_msat, onion_session_privs, node_signer, best_block_height,
1441-
&send_payment_along_path)
1478+
pending_events, &send_payment_along_path)
14421479
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
14431480
}
14441481

lightning/src/routing/router.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,17 @@ where L::Target: Logger {
14961496
let our_node_id = NodeId::from_pubkey(&our_node_pubkey);
14971497

14981498
if payee_node_id_opt.map_or(false, |payee| payee == our_node_id) {
1499-
return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError});
1499+
let dummy_path = Path { hops: vec![RouteHop {
1500+
pubkey: our_node_pubkey.clone(),
1501+
short_channel_id: 0,
1502+
node_features: NodeFeatures::empty(),
1503+
channel_features: ChannelFeatures::empty(),
1504+
fee_msat: 0,
1505+
cltv_expiry_delta: 0,
1506+
maybe_announced_channel: false,
1507+
1508+
}], blinded_tail: None };
1509+
return Ok(Route { paths: vec![dummy_path], route_params: Some(route_params.clone()) });
15001510
}
15011511
if our_node_id == maybe_dummy_payee_node_id {
15021512
return Err(LightningError{err: "Invalid origin node id provided, use a different one".to_owned(), action: ErrorAction::IgnoreError});

0 commit comments

Comments
 (0)