diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 5f075e7853b..de35bf851f1 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -15,11 +15,11 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey}; use crate::sign::{EntropySource, NodeSigner, Recipient}; use crate::events::{self, PaymentFailureReason, Event, PaymentPurpose}; -use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; +use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret, features::{ NodeFeatures, ChannelFeatures}}; 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, RouteHop}; use crate::util::errors::APIError; use crate::util::logger::Logger; use crate::util::time::Time; @@ -921,6 +921,29 @@ impl OutboundPayments { payment_preimage: Some(payment_preimage), payment_secret, }; + let dummy_route = Route { + paths: vec![Path { + hops: vec![RouteHop { + pubkey: payer, + node_features: NodeFeatures::empty(), + short_channel_id: 0, + channel_features: ChannelFeatures::empty(), + fee_msat: 0, + cltv_expiry_delta: 0, + maybe_announced_channel: false, + }], + blinded_tail: None, + }], + route_params: Some(route_params.clone()), + }; + 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(); diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index addf7405d69..1175fa9a252 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -1529,6 +1529,30 @@ fn preflight_probes_yield_event_and_skip() { assert!(!nodes[0].node.has_pending_payments()); } +#[test] +fn test_self_payment() { + let chanmon_cfg = create_chanmon_cfgs(1); + let node_cfg = create_node_cfgs(1, &chanmon_cfg); + let node_chanmgr = create_node_chanmgrs(1, &node_cfg, &[None, None]); + let nodes = create_network(1, &node_cfg, &node_chanmgr); + let (payment_hash, payment_secret) = nodes[0].node.create_inbound_payment(Some(1000), 60, None).unwrap(); + let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), TEST_FINAL_CLTV); + let route_params = RouteParameters { + payment_params, + final_value_msat: 100000, + max_total_routing_fee_msat: None, + }; + let res = nodes[0].node.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)); + assert!(res.is_ok()); + let events = nodes[0].node.get_and_clear_pending_events(); + assert_eq!(events.len(), 2); + matches!(events[0], Event::PaymentSent { .. }); + matches!(events[1], Event::PaymentClaimable { .. }); + let pending_payments = nodes[0].node.list_recent_payments(); + assert_eq!(pending_payments.len(), 1); + matches!(pending_payments[0], RecentPaymentDetails::Fulfilled { .. }); +} + #[test] fn claimed_send_payment_idempotent() { // Tests that `send_payment` (and friends) are (reasonably) idempotent.