Skip to content

Commit

Permalink
experimenting new approach for self payment
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimirfomene committed Jan 4, 2024
1 parent ec170bd commit ab77097
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 45 deletions.
22 changes: 21 additions & 1 deletion lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PaymentPreimage> = 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))
Expand Down
98 changes: 54 additions & 44 deletions lightning/src/ln/outbound_payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -677,7 +678,7 @@ impl OutboundPayments {

pub(super) fn send_payment<R: Deref, ES: Deref, NS: Deref, IH, SP, L: Deref>(
&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<PaymentPreimage>, router: &R,
first_hops: Vec<ChannelDetails>, compute_inflight_htlcs: IH, entropy_source: &ES,
node_signer: &NS, best_block_height: u32, logger: &L,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
Expand All @@ -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)
}

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -882,7 +883,7 @@ impl OutboundPayments {
fn send_payment_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
&self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, route_params: RouteParameters,
router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
payment_preimage: Option<PaymentPreimage>, router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
node_signer: &NS, best_block_height: u32, logger: &L,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
) -> Result<(), RetryableSendFailure>
Expand All @@ -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
}
Expand Down

0 comments on commit ab77097

Please sign in to comment.