From ab7709712e685a181ad55918d2bf1c12b8904105 Mon Sep 17 00:00:00 2001 From: Vladimir Fomene Date: Wed, 15 Nov 2023 17:19:06 +0300 Subject: [PATCH] experimenting new approach for self payment --- lightning/src/ln/channelmanager.rs | 22 ++++++- lightning/src/ln/outbound_payment.rs | 98 +++++++++++++++------------- 2 files changed, 75 insertions(+), 45 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 37a8feb446d..ecd71dabd9f 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -3554,8 +3554,28 @@ where pub fn send_payment(&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<(), RetryableSendFailure> { let best_block_height = self.best_block.read().unwrap().height(); let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); + let mut preimage: Option = None; + if recipient_onion.payment_secret.is_some() { + if let Payee::Clear{node_id, .. } = route_params.payment_params.payee { + let is_payee_phantom = match self.node_signer.get_node_id(Recipient::PhantomNode) { + Ok(phantom_node_id) => node_id == phantom_node_id, + Err(_) => false, + }; + if node_id == self.get_our_node_id() || is_payee_phantom { + // check if this is a self payment. If so, we verify that we are paying the right amount. + if let Ok(payment_preimage) = self.get_payment_preimage(payment_hash, recipient_onion.payment_secret.unwrap()) { + preimage = Some(payment_preimage); + let payment_data = msgs::FinalOnionHopData{ payment_secret: recipient_onion.payment_secret.unwrap(), total_msat: route_params.final_value_msat}; + if let Err(_) = inbound_payment::verify(payment_hash, &payment_data, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) { + return Err(RetryableSendFailure::RecipientRejected); + } + } + } + } + } + self.pending_outbound_payments - .send_payment(payment_hash, recipient_onion, payment_id, retry_strategy, route_params, + .send_payment(payment_hash, recipient_onion, payment_id, retry_strategy, route_params, preimage, &self.router, self.list_usable_channels(), || self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height, &self.logger, &self.pending_events, |args| self.send_payment_along_path(args)) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index de4115bae7a..cc7a1b28bcb 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -19,7 +19,7 @@ use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, PaymentId}; use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason}; use crate::offers::invoice::Bolt12Invoice; -use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router}; +use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router, Payee}; use crate::util::errors::APIError; use crate::util::logger::Logger; use crate::util::time::Time; @@ -416,6 +416,7 @@ pub enum RetryableSendFailure { /// [`Event::PaymentSent`]: crate::events::Event::PaymentSent /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed DuplicatePayment, + RecipientRejected, } /// If a payment fails to send with [`ChannelManager::send_payment_with_route`], it can be in one @@ -677,7 +678,7 @@ impl OutboundPayments { pub(super) fn send_payment( &self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, - retry_strategy: Retry, route_params: RouteParameters, router: &R, + retry_strategy: Retry, route_params: RouteParameters, payment_preimage: Option, router: &R, first_hops: Vec, compute_inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, logger: &L, pending_events: &Mutex)>>, send_payment_along_path: SP, @@ -691,7 +692,7 @@ impl OutboundPayments { SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, { self.send_payment_internal(payment_id, payment_hash, recipient_onion, None, retry_strategy, - route_params, router, first_hops, &compute_inflight_htlcs, entropy_source, node_signer, + route_params, payment_preimage, router, first_hops, &compute_inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, &send_payment_along_path) } @@ -730,7 +731,7 @@ impl OutboundPayments { .unwrap_or_else(|| PaymentPreimage(entropy_source.get_secure_random_bytes())); let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner()); self.send_payment_internal(payment_id, payment_hash, recipient_onion, Some(preimage), - retry_strategy, route_params, router, first_hops, inflight_htlcs, entropy_source, + retry_strategy, route_params, Some(preimage), router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path) .map(|()| payment_hash) } @@ -882,7 +883,7 @@ impl OutboundPayments { fn send_payment_internal( &self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, keysend_preimage: Option, retry_strategy: Retry, route_params: RouteParameters, - router: &R, first_hops: Vec, inflight_htlcs: IH, entropy_source: &ES, + payment_preimage: Option, router: &R, first_hops: Vec, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, logger: &L, pending_events: &Mutex)>>, send_payment_along_path: SP, ) -> Result<(), RetryableSendFailure> @@ -909,47 +910,56 @@ impl OutboundPayments { payment_hash, payment_id, ) { Ok(res) => Some(res), - Err(error) => { - if error.err == "Cannot generate a route to ourselves" { - let payment_secret = match recipient_onion.payment_secret { - Some(secret) => secret, - None => PaymentSecret([0; 32]) + Err(_) => { + // The following code handles self payments. + if let Payee::Clear{node_id, .. } = route_params.payment_params.payee { + let is_payee_phantom = match node_signer.get_node_id(Recipient::PhantomNode) { + Ok(phantom_node_id) => node_id == phantom_node_id, + Err(_) => false, }; - let payment_preimage = PaymentPreimage([0; 32]); - let payment_purpose = match keysend_preimage { - Some(preimage) => PaymentPurpose::SpontaneousPayment(preimage), - None => PaymentPurpose::InvoicePayment { - payment_preimage: Some(payment_preimage), - payment_secret, - }, - }; - let dummy_route = Route { - paths: vec![Path { - hops: vec![], - blinded_tail: None, - }], - route_params: Some(route_params.clone()), - }; - - // We add a new pending payment only to mark it as fulfilled immediately, to protect against - // duplicate payments. - let _ = self.add_new_pending_payment(payment_hash, - recipient_onion.clone(), payment_id, keysend_preimage, &dummy_route, Some(retry_strategy), - Some(route_params.payment_params.clone()), entropy_source, best_block_height) - .map_err(|_| { - log_error!(logger, "Payment with id {} is already pending. New payment had payment hash {}", - payment_id, payment_hash); - RetryableSendFailure::DuplicatePayment + if node_id == payer || is_payee_phantom { + let payment_secret = recipient_onion.payment_secret.ok_or_else(|| { + RetryableSendFailure::RecipientRejected })?; - let mut pending_outbounds_lock = self.pending_outbound_payments.lock().unwrap(); - let payment = pending_outbounds_lock.get_mut(&payment_id).unwrap(); - payment.mark_fulfilled(); - let mut pending_events_lock = pending_events.lock().unwrap(); - pending_events_lock.push_back((Event::PaymentClaimed { receiver_node_id: Some(payer), payment_hash, - amount_msat: route_params.final_value_msat, purpose: payment_purpose, htlcs: vec![], sender_intended_total_msat: None }, None)); - pending_events_lock.push_back((Event::PaymentSent { payment_id: Some(payment_id), payment_preimage, - payment_hash, fee_paid_msat: None }, None)); - return Ok(()); + // It is safe to unwrap here because because if we reached this line it means + // the payment secret is present and we were able to get our preimage upstream in the + // channel manager. + let payment_preimage = payment_preimage.unwrap(); + let payment_purpose = match keysend_preimage { + Some(preimage) => PaymentPurpose::SpontaneousPayment(preimage), + None => PaymentPurpose::InvoicePayment { + payment_preimage: Some(payment_preimage), + payment_secret, + }, + }; + let dummy_route = Route { + paths: vec![Path { + hops: vec![], + blinded_tail: None, + }], + route_params: Some(route_params.clone()), + }; + + // We add a new pending payment only to mark it as fulfilled immediately, to protect against + // duplicate payments. + let _ = self.add_new_pending_payment(payment_hash, + recipient_onion.clone(), payment_id, keysend_preimage, &dummy_route, Some(retry_strategy), + Some(route_params.payment_params.clone()), entropy_source, best_block_height) + .map_err(|_| { + log_error!(logger, "Payment with id {} is already pending. New payment had payment hash {}", + payment_id, payment_hash); + RetryableSendFailure::DuplicatePayment + })?; + let mut pending_outbounds_lock = self.pending_outbound_payments.lock().unwrap(); + let payment = pending_outbounds_lock.get_mut(&payment_id).unwrap(); + payment.mark_fulfilled(); + let mut pending_events_lock = pending_events.lock().unwrap(); + pending_events_lock.push_back((Event::PaymentClaimed { receiver_node_id: Some(payer), payment_hash, + amount_msat: route_params.final_value_msat, purpose: payment_purpose, htlcs: vec![], sender_intended_total_msat: None }, None)); + pending_events_lock.push_back((Event::PaymentSent { payment_id: Some(payment_id), payment_preimage, + payment_hash, fee_paid_msat: None }, None)); + return Ok(()); + } } None }