@@ -5582,6 +5582,29 @@ impl<
55825582 }
55835583 }
55845584
5585+ fn route_params_for_fixed_route(route: &mut Route) -> RouteParameters {
5586+ let params = route.route_params.clone().unwrap_or_else(|| {
5587+ let (payee_node_id, cltv_delta) = route
5588+ .paths
5589+ .first()
5590+ .and_then(|path| {
5591+ path.hops.last().map(|hop| (hop.pubkey, hop.cltv_expiry_delta as u32))
5592+ })
5593+ .unwrap_or_else(|| {
5594+ (PublicKey::from_slice(&[2; 33]).unwrap(), MIN_FINAL_CLTV_EXPIRY_DELTA as u32)
5595+ });
5596+ let dummy_payment_params = PaymentParameters::from_node_id(payee_node_id, cltv_delta);
5597+ RouteParameters::from_payment_params_and_value(
5598+ dummy_payment_params,
5599+ route.get_total_amount(),
5600+ )
5601+ });
5602+ if route.route_params.is_none() {
5603+ route.route_params = Some(params.clone());
5604+ }
5605+ params
5606+ }
5607+
55855608 /// Sends a payment along a given route. See [`Self::send_payment`] for more info.
55865609 ///
55875610 /// LDK will not automatically retry this payment, though it may be manually re-sent after an
@@ -5593,25 +5616,51 @@ impl<
55935616 ) -> Result<(), RetryableSendFailure> {
55945617 let best_block_height = self.best_block.read().unwrap().height;
55955618 let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
5596- let route_params = route.route_params.clone().unwrap_or_else(|| {
5597- // Create a dummy route params since they're a required parameter but unused in this case
5598- let (payee_node_id, cltv_delta) = route.paths.first()
5599- .and_then(|path| path.hops.last().map(|hop| (hop.pubkey, hop.cltv_expiry_delta as u32)))
5600- .unwrap_or_else(|| (PublicKey::from_slice(&[2; 32]).unwrap(), MIN_FINAL_CLTV_EXPIRY_DELTA as u32));
5601- let dummy_payment_params = PaymentParameters::from_node_id(payee_node_id, cltv_delta);
5602- RouteParameters::from_payment_params_and_value(dummy_payment_params, route.get_total_amount())
5603- });
5604- if route.route_params.is_none() { route.route_params = Some(route_params.clone()); }
5619+ let route_params = Self::route_params_for_fixed_route(&mut route);
56055620 let router = FixedRouter::new(route);
56065621 let logger =
56075622 WithContext::for_payment(&self.logger, None, None, Some(payment_hash), payment_id);
56085623 self.pending_outbound_payments
56095624 .send_payment(payment_hash, recipient_onion, payment_id, Retry::Attempts(0),
5610- route_params, && router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
5625+ route_params, &router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
56115626 &self.entropy_source, &self.node_signer, best_block_height,
56125627 &self.pending_events, |args| self.send_payment_along_path(args), &logger)
56135628 }
56145629
5630+ /// Sends a spontaneous payment along a given route. See
5631+ /// [`Self::send_spontaneous_payment`] for more info.
5632+ ///
5633+ /// LDK will not automatically retry this payment, though it may be manually
5634+ /// re-sent after an
5635+ /// [`Event::PaymentFailed`] is generated.
5636+ pub fn send_spontaneous_payment_with_route(
5637+ &self, mut route: Route, payment_preimage: Option<PaymentPreimage>,
5638+ recipient_onion: RecipientOnionFields, payment_id: PaymentId,
5639+ ) -> Result<PaymentHash, RetryableSendFailure> {
5640+ let best_block_height = self.best_block.read().unwrap().height;
5641+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
5642+ let route_params = Self::route_params_for_fixed_route(&mut route);
5643+ let router = FixedRouter::new(route);
5644+ let payment_hash = payment_preimage.map(|preimage| preimage.into());
5645+ let logger = WithContext::for_payment(&self.logger, None, None, payment_hash, payment_id);
5646+ self.pending_outbound_payments.send_spontaneous_payment(
5647+ payment_preimage,
5648+ recipient_onion,
5649+ payment_id,
5650+ Retry::Attempts(0),
5651+ route_params,
5652+ &router,
5653+ self.list_usable_channels(),
5654+ || self.compute_inflight_htlcs(),
5655+ &self.entropy_source,
5656+ &self.node_signer,
5657+ best_block_height,
5658+ &self.pending_events,
5659+ |args| self.send_payment_along_path(args),
5660+ &logger,
5661+ )
5662+ }
5663+
56155664 /// Sends a payment to the route found using the provided [`RouteParameters`], retrying failed
56165665 /// payment paths based on the provided `Retry`.
56175666 ///
@@ -6115,6 +6164,98 @@ impl<
61156164 )
61166165 }
61176166
6167+ /// Performs a circular rebalancing payment: funds exit our node over `outbound_channel_id`,
6168+ /// traverse the Lightning Network, and re-enter our node through `inbound_channel_id`.
6169+ ///
6170+ /// This is a convenient helper for moving liquidity between two of our channels without
6171+ /// requiring a counterparty invoice. It is equivalent to constructing an appropriate circular
6172+ /// [`Route`] and sending a spontaneous (keysend) payment over it.
6173+ ///
6174+ /// # How it works
6175+ ///
6176+ /// The router finds a path from our node to the `inbound_channel_id`'s counterparty, forced to
6177+ /// start with `outbound_channel_id`. We then manually append a final hop back to ourselves over
6178+ /// the `inbound_channel_id`. The route is sent as a spontaneous payment.
6179+ ///
6180+ /// # Limitations
6181+ ///
6182+ /// - Only single-path routing (no MPP support) is currently available.
6183+ /// - The payment is not recorded by the `Scorer`.
6184+ ///
6185+ /// # Errors
6186+ ///
6187+ /// Returns [`RetryableSendFailure::RouteNotFound`] if channel validation fails or no route can be
6188+ /// found. Payment-level errors (e.g. HTLC failures mid-flight) are reported asynchronously
6189+ /// via [`Event::PaymentFailed`].
6190+ ///
6191+ /// [`Route`]: crate::routing::router::Route
6192+ /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
6193+ pub fn send_circular_payment(
6194+ &self, outbound_channel_id: ChannelId, inbound_channel_id: ChannelId, amount_msat: u64,
6195+ payment_id: PaymentId,
6196+ ) -> Result<PaymentHash, RetryableSendFailure> {
6197+ if outbound_channel_id == inbound_channel_id {
6198+ return Err(RetryableSendFailure::RouteNotFound);
6199+ }
6200+
6201+ let usable_channels = self.list_usable_channels();
6202+ let out_chan = usable_channels
6203+ .iter()
6204+ .find(|c| c.channel_id == outbound_channel_id)
6205+ .ok_or(RetryableSendFailure::RouteNotFound)?;
6206+
6207+ let in_chan = usable_channels
6208+ .iter()
6209+ .find(|c| c.channel_id == inbound_channel_id)
6210+ .ok_or(RetryableSendFailure::RouteNotFound)?;
6211+
6212+ let our_node_id = self.get_our_node_id();
6213+ let forwarding_info = in_chan
6214+ .counterparty
6215+ .forwarding_info
6216+ .as_ref()
6217+ .ok_or(RetryableSendFailure::RouteNotFound)?;
6218+ let dummy_payee = PublicKey::from_slice(&[2; 33]).unwrap();
6219+ let route_hint =
6220+ crate::routing::router::RouteHint(vec![crate::routing::router::RouteHintHop {
6221+ src_node_id: in_chan.counterparty.node_id,
6222+ short_channel_id: in_chan
6223+ .get_inbound_payment_scid()
6224+ .ok_or(RetryableSendFailure::RouteNotFound)?,
6225+ fees: lightning_types::routing::RoutingFees {
6226+ base_msat: forwarding_info.fee_base_msat,
6227+ proportional_millionths: forwarding_info.fee_proportional_millionths,
6228+ },
6229+ cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
6230+ htlc_minimum_msat: None,
6231+ htlc_maximum_msat: None,
6232+ }]);
6233+
6234+ let route_params = RouteParameters::from_payment_params_and_value(
6235+ PaymentParameters::from_node_id(dummy_payee, MIN_FINAL_CLTV_EXPIRY_DELTA as u32)
6236+ .with_route_hints(vec![route_hint])
6237+ .map_err(|_| RetryableSendFailure::RouteNotFound)?,
6238+ amount_msat,
6239+ );
6240+
6241+ let first_hops: [&ChannelDetails; 1] = [out_chan];
6242+ let inflight_htlcs = self.compute_inflight_htlcs();
6243+ let mut route = self
6244+ .router
6245+ .find_route(&our_node_id, &route_params, Some(&first_hops), inflight_htlcs)
6246+ .map_err(|_| RetryableSendFailure::RouteNotFound)?;
6247+
6248+ for path in route.paths.iter_mut() {
6249+ if let Some(last) = path.hops.last_mut() {
6250+ last.pubkey = our_node_id;
6251+ }
6252+ }
6253+
6254+ let preimage = PaymentPreimage(self.entropy_source.get_secure_random_bytes());
6255+ let onion = RecipientOnionFields::spontaneous_empty(amount_msat);
6256+ self.send_spontaneous_payment_with_route(route, Some(preimage), onion, payment_id)
6257+ }
6258+
61186259 /// Send a payment that is probing the given route for liquidity. We calculate the
61196260 /// [`PaymentHash`] of probes based on a static secret and a random [`PaymentId`], which allows
61206261 /// us to easily discern them from real payments.
0 commit comments