Skip to content

Commit 0454d45

Browse files
authored
Merge pull request #3534 from valentinewallace/2025-01-send-with-route
Reinstate `ChannelManager::send_payment_with_route` API
2 parents ed7befc + 82a2c84 commit 0454d45

10 files changed

+179
-146
lines changed

fuzz/src/chanmon_consistency.rs

+10-21
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
4848
use lightning::ln::channel_state::ChannelDetails;
4949
use lightning::ln::channelmanager::{
5050
ChainParameters, ChannelManager, ChannelManagerReadArgs, PaymentId, RecentPaymentDetails,
51-
RecipientOnionFields, Retry,
51+
RecipientOnionFields,
5252
};
5353
use lightning::ln::functional_test_utils::*;
5454
use lightning::ln::inbound_payment::ExpandedKey;
@@ -82,7 +82,6 @@ use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey}
8282

8383
use lightning::io::Cursor;
8484
use std::cmp::{self, Ordering};
85-
use std::collections::VecDeque;
8685
use std::mem;
8786
use std::sync::atomic;
8887
use std::sync::{Arc, Mutex};
@@ -113,22 +112,14 @@ impl FeeEstimator for FuzzEstimator {
113112
}
114113
}
115114

116-
struct FuzzRouter {
117-
pub next_routes: Mutex<VecDeque<Route>>,
118-
}
115+
struct FuzzRouter {}
119116

120117
impl Router for FuzzRouter {
121118
fn find_route(
122119
&self, _payer: &PublicKey, _params: &RouteParameters,
123120
_first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs,
124121
) -> Result<Route, msgs::LightningError> {
125-
if let Some(route) = self.next_routes.lock().unwrap().pop_front() {
126-
return Ok(route);
127-
}
128-
Err(msgs::LightningError {
129-
err: String::from("Not implemented"),
130-
action: msgs::ErrorAction::IgnoreError,
131-
})
122+
unreachable!()
132123
}
133124

134125
fn create_blinded_payment_paths<T: secp256k1::Signing + secp256k1::Verification>(
@@ -518,7 +509,7 @@ fn send_payment(
518509
PaymentParameters::from_node_id(source.get_our_node_id(), TEST_FINAL_CLTV),
519510
amt,
520511
);
521-
source.router.next_routes.lock().unwrap().push_back(Route {
512+
let route = Route {
522513
paths: vec![Path {
523514
hops: vec![RouteHop {
524515
pubkey: dest.get_our_node_id(),
@@ -532,11 +523,10 @@ fn send_payment(
532523
blinded_tail: None,
533524
}],
534525
route_params: Some(route_params.clone()),
535-
});
526+
};
536527
let onion = RecipientOnionFields::secret_only(payment_secret);
537528
let payment_id = PaymentId(payment_id);
538-
let res =
539-
source.send_payment(payment_hash, onion, payment_id, route_params, Retry::Attempts(0));
529+
let res = source.send_payment_with_route(route, payment_hash, onion, payment_id);
540530
match res {
541531
Err(err) => {
542532
panic!("Errored with {:?} on initial payment send", err);
@@ -592,7 +582,7 @@ fn send_hop_payment(
592582
PaymentParameters::from_node_id(source.get_our_node_id(), TEST_FINAL_CLTV),
593583
amt,
594584
);
595-
source.router.next_routes.lock().unwrap().push_back(Route {
585+
let route = Route {
596586
paths: vec![Path {
597587
hops: vec![
598588
RouteHop {
@@ -617,11 +607,10 @@ fn send_hop_payment(
617607
blinded_tail: None,
618608
}],
619609
route_params: Some(route_params.clone()),
620-
});
610+
};
621611
let onion = RecipientOnionFields::secret_only(payment_secret);
622612
let payment_id = PaymentId(payment_id);
623-
let res =
624-
source.send_payment(payment_hash, onion, payment_id, route_params, Retry::Attempts(0));
613+
let res = source.send_payment_with_route(route, payment_hash, onion, payment_id);
625614
match res {
626615
Err(err) => {
627616
panic!("Errored with {:?} on initial payment send", err);
@@ -640,7 +629,7 @@ fn send_hop_payment(
640629
pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
641630
let out = SearchingOutput::new(underlying_out);
642631
let broadcast = Arc::new(TestBroadcaster {});
643-
let router = FuzzRouter { next_routes: Mutex::new(VecDeque::new()) };
632+
let router = FuzzRouter {};
644633

645634
macro_rules! make_node {
646635
($node_id: expr, $fee_estimator: expr) => {{

lightning/src/chain/channelmonitor.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -5096,7 +5096,7 @@ mod tests {
50965096
use crate::chain::chaininterface::LowerBoundedFeeEstimator;
50975097

50985098
use super::ChannelMonitorUpdateStep;
5099-
use crate::{check_added_monitors, check_spends, get_local_commitment_txn, get_monitor, get_route_and_payment_hash, unwrap_send_err};
5099+
use crate::{check_added_monitors, check_spends, get_local_commitment_txn, get_monitor, get_route_and_payment_hash};
51005100
use crate::chain::{BestBlock, Confirm};
51015101
use crate::chain::channelmonitor::{ChannelMonitor, WithChannelMonitor};
51025102
use crate::chain::package::{weight_offered_htlc, weight_received_htlc, weight_revoked_offered_htlc, weight_revoked_received_htlc, WEIGHT_REVOKED_OUTPUT};
@@ -5106,10 +5106,9 @@ mod tests {
51065106
use crate::types::payment::{PaymentPreimage, PaymentHash};
51075107
use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, RevocationBasepoint, RevocationKey};
51085108
use crate::ln::chan_utils::{self,HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
5109-
use crate::ln::channelmanager::{PaymentSendFailure, PaymentId, RecipientOnionFields};
5109+
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
51105110
use crate::ln::functional_test_utils::*;
51115111
use crate::ln::script::ShutdownScript;
5112-
use crate::util::errors::APIError;
51135112
use crate::util::test_utils::{TestLogger, TestBroadcaster, TestFeeEstimator};
51145113
use crate::util::ser::{ReadableArgs, Writeable};
51155114
use crate::util::logger::Logger;
@@ -5170,9 +5169,9 @@ mod tests {
51705169
// If the ChannelManager tries to update the channel, however, the ChainMonitor will pass
51715170
// the update through to the ChannelMonitor which will refuse it (as the channel is closed).
51725171
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 100_000);
5173-
unwrap_send_err!(nodes[1].node.send_payment_with_route(route, payment_hash,
5174-
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
5175-
), false, APIError::MonitorUpdateInProgress, {});
5172+
nodes[1].node.send_payment_with_route(route, payment_hash,
5173+
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
5174+
).unwrap();
51765175
check_added_monitors!(nodes[1], 1);
51775176

51785177
// Build a new ChannelMonitorUpdate which contains both the failing commitment tx update

lightning/src/ln/chanmon_update_fail_tests.rs

+13-20
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@ use crate::chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor};
1919
use crate::chain::transaction::OutPoint;
2020
use crate::chain::{ChannelMonitorUpdateStatus, Listen, Watch};
2121
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose, ClosureReason, HTLCDestination};
22-
use crate::ln::channelmanager::{PaymentId, PaymentSendFailure, RAACommitmentOrder, RecipientOnionFields};
22+
use crate::ln::channelmanager::{PaymentId, RAACommitmentOrder, RecipientOnionFields};
2323
use crate::ln::channel::AnnouncementSigsState;
2424
use crate::ln::msgs;
2525
use crate::ln::types::ChannelId;
2626
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler};
2727
use crate::util::test_channel_signer::TestChannelSigner;
28-
use crate::util::errors::APIError;
2928
use crate::util::ser::{ReadableArgs, Writeable};
3029
use crate::util::test_utils::TestBroadcaster;
3130

@@ -133,9 +132,9 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) {
133132
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
134133

135134
{
136-
unwrap_send_err!(nodes[0].node.send_payment_with_route(route, payment_hash_1,
137-
RecipientOnionFields::secret_only(payment_secret_1), PaymentId(payment_hash_1.0)
138-
), false, APIError::MonitorUpdateInProgress, {});
135+
nodes[0].node.send_payment_with_route(route, payment_hash_1,
136+
RecipientOnionFields::secret_only(payment_secret_1), PaymentId(payment_hash_1.0)
137+
).unwrap();
139138
check_added_monitors!(nodes[0], 1);
140139
}
141140

@@ -190,9 +189,9 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) {
190189
let (route, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(&nodes[0], nodes[1], 1000000);
191190
{
192191
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
193-
unwrap_send_err!(nodes[0].node.send_payment_with_route(route, payment_hash_2,
194-
RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)
195-
), false, APIError::MonitorUpdateInProgress, {});
192+
nodes[0].node.send_payment_with_route(route, payment_hash_2,
193+
RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)
194+
).unwrap();
196195
check_added_monitors!(nodes[0], 1);
197196
}
198197

@@ -257,9 +256,9 @@ fn do_test_monitor_temporary_update_fail(disconnect_count: usize) {
257256
let (route, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000);
258257
{
259258
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
260-
unwrap_send_err!(nodes[0].node.send_payment_with_route(route, payment_hash_2,
261-
RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)
262-
), false, APIError::MonitorUpdateInProgress, {});
259+
nodes[0].node.send_payment_with_route(route, payment_hash_2,
260+
RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)
261+
).unwrap();
263262
check_added_monitors!(nodes[0], 1);
264263
}
265264

@@ -2004,16 +2003,10 @@ fn test_path_paused_mpp() {
20042003
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed);
20052004
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
20062005

2007-
// Now check that we get the right return value, indicating that the first path succeeded but
2008-
// the second got a MonitorUpdateInProgress err. This implies
2009-
// PaymentSendFailure::PartialFailure as some paths succeeded, preventing retry.
2010-
if let Err(PaymentSendFailure::PartialFailure { results, ..}) = nodes[0].node.send_payment_with_route(
2006+
// The first path should have succeeded with the second getting a MonitorUpdateInProgress err.
2007+
nodes[0].node.send_payment_with_route(
20112008
route, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
2012-
) {
2013-
assert_eq!(results.len(), 2);
2014-
if let Ok(()) = results[0] {} else { panic!(); }
2015-
if let Err(APIError::MonitorUpdateInProgress) = results[1] {} else { panic!(); }
2016-
} else { panic!(); }
2009+
).unwrap();
20172010
check_added_monitors!(nodes[0], 2);
20182011
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed);
20192012

lightning/src/ln/channelmanager.rs

+35-26
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ use crate::ln::channel_state::ChannelDetails;
5555
use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
5656
#[cfg(any(feature = "_test_utils", test))]
5757
use crate::types::features::Bolt11InvoiceFeatures;
58-
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, Payee, PaymentParameters, RouteParameters, Router};
59-
#[cfg(test)]
60-
use crate::routing::router::Route;
58+
use crate::routing::router::{BlindedTail, FixedRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, Router};
6159
use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundHTLCErr, NextPacketDetails};
6260
use crate::ln::msgs;
6361
use crate::ln::onion_utils;
@@ -2398,9 +2396,6 @@ where
23982396
fee_estimator: LowerBoundedFeeEstimator<F>,
23992397
chain_monitor: M,
24002398
tx_broadcaster: T,
2401-
#[cfg(fuzzing)]
2402-
pub router: R,
2403-
#[cfg(not(fuzzing))]
24042399
router: R,
24052400
message_router: MR,
24062401

@@ -4586,21 +4581,31 @@ where
45864581
}
45874582
}
45884583

4589-
// Deprecated send method, for testing use [`Self::send_payment`] and
4590-
// [`TestRouter::expect_find_route`] instead.
4591-
//
4592-
// [`TestRouter::expect_find_route`]: crate::util::test_utils::TestRouter::expect_find_route
4593-
#[cfg(test)]
4594-
pub(crate) fn send_payment_with_route(
4595-
&self, route: Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
4584+
/// Sends a payment along a given route. See [`Self::send_payment`] for more info.
4585+
///
4586+
/// LDK will not automatically retry this payment, though it may be manually re-sent after an
4587+
/// [`Event::PaymentFailed`] is generated.
4588+
pub fn send_payment_with_route(
4589+
&self, mut route: Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
45964590
payment_id: PaymentId
4597-
) -> Result<(), PaymentSendFailure> {
4591+
) -> Result<(), RetryableSendFailure> {
45984592
let best_block_height = self.best_block.read().unwrap().height;
45994593
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
4594+
let route_params = route.route_params.clone().unwrap_or_else(|| {
4595+
// Create a dummy route params since they're a required parameter but unused in this case
4596+
let (payee_node_id, cltv_delta) = route.paths.first()
4597+
.and_then(|path| path.hops.last().map(|hop| (hop.pubkey, hop.cltv_expiry_delta as u32)))
4598+
.unwrap_or_else(|| (PublicKey::from_slice(&[2; 32]).unwrap(), MIN_FINAL_CLTV_EXPIRY_DELTA as u32));
4599+
let dummy_payment_params = PaymentParameters::from_node_id(payee_node_id, cltv_delta);
4600+
RouteParameters::from_payment_params_and_value(dummy_payment_params, route.get_total_amount())
4601+
});
4602+
if route.route_params.is_none() { route.route_params = Some(route_params.clone()); }
4603+
let router = FixedRouter::new(route);
46004604
self.pending_outbound_payments
4601-
.send_payment_with_route(&route, payment_hash, recipient_onion, payment_id,
4602-
&self.entropy_source, &self.node_signer, best_block_height,
4603-
|args| self.send_payment_along_path(args))
4605+
.send_payment(payment_hash, recipient_onion, payment_id, Retry::Attempts(0),
4606+
route_params, &&router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
4607+
&self.entropy_source, &self.node_signer, best_block_height, &self.logger,
4608+
&self.pending_events, |args| self.send_payment_along_path(args))
46044609
}
46054610

46064611
/// Sends a payment to the route found using the provided [`RouteParameters`], retrying failed
@@ -4629,7 +4634,8 @@ where
46294634
/// [`ChannelManager::list_recent_payments`] for more information.
46304635
///
46314636
/// Routes are automatically found using the [`Router] provided on startup. To fix a route for a
4632-
/// particular payment, match the [`PaymentId`] passed to [`Router::find_route_with_id`].
4637+
/// particular payment, use [`Self::send_payment_with_route`] or match the [`PaymentId`] passed to
4638+
/// [`Router::find_route_with_id`].
46334639
///
46344640
/// [`Event::PaymentSent`]: events::Event::PaymentSent
46354641
/// [`Event::PaymentFailed`]: events::Event::PaymentFailed
@@ -14358,7 +14364,7 @@ mod tests {
1435814364
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
1435914365
use crate::ln::types::ChannelId;
1436014366
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
14361-
use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId};
14367+
use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, RecipientOnionFields, InterceptId};
1436214368
use crate::ln::functional_test_utils::*;
1436314369
use crate::ln::msgs::{self, ErrorAction};
1436414370
use crate::ln::msgs::ChannelMessageHandler;
@@ -14789,14 +14795,17 @@ mod tests {
1478914795
route.paths[1].hops[0].short_channel_id = chan_2_id;
1479014796
route.paths[1].hops[1].short_channel_id = chan_4_id;
1479114797

14792-
match nodes[0].node.send_payment_with_route(route, payment_hash,
14793-
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0))
14794-
.unwrap_err() {
14795-
PaymentSendFailure::ParameterError(APIError::APIMisuseError { ref err }) => {
14796-
assert!(regex::Regex::new(r"Payment secret is required for multi-path payments").unwrap().is_match(err))
14797-
},
14798-
_ => panic!("unexpected error")
14798+
nodes[0].node.send_payment_with_route(route, payment_hash,
14799+
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap();
14800+
let events = nodes[0].node.get_and_clear_pending_events();
14801+
assert_eq!(events.len(), 1);
14802+
match events[0] {
14803+
Event::PaymentFailed { reason, .. } => {
14804+
assert_eq!(reason.unwrap(), crate::events::PaymentFailureReason::UnexpectedError);
14805+
}
14806+
_ => panic!()
1479914807
}
14808+
nodes[0].logger.assert_log_contains("lightning::ln::outbound_payment", "Payment secret is required for multi-path payments", 2);
1480014809
assert!(nodes[0].node.list_recent_payments().is_empty());
1480114810
}
1480214811

lightning/src/ln/functional_test_utils.rs

+19-22
Original file line numberDiff line numberDiff line change
@@ -1069,30 +1069,27 @@ macro_rules! get_local_commitment_txn {
10691069
/// Check the error from attempting a payment.
10701070
#[macro_export]
10711071
macro_rules! unwrap_send_err {
1072-
($res: expr, $all_failed: expr, $type: pat, $check: expr) => {
1073-
match &$res {
1074-
&Err(PaymentSendFailure::AllFailedResendSafe(ref fails)) if $all_failed => {
1075-
assert_eq!(fails.len(), 1);
1076-
match fails[0] {
1077-
$type => { $check },
1078-
_ => panic!(),
1079-
}
1080-
},
1081-
&Err(PaymentSendFailure::PartialFailure { ref results, .. }) if !$all_failed => {
1082-
assert_eq!(results.len(), 1);
1083-
match results[0] {
1084-
Err($type) => { $check },
1085-
_ => panic!(),
1086-
}
1087-
},
1088-
&Err(PaymentSendFailure::PathParameterError(ref result)) if !$all_failed => {
1089-
assert_eq!(result.len(), 1);
1090-
match result[0] {
1091-
Err($type) => { $check },
1092-
_ => panic!(),
1072+
($node: expr, $res: expr, $all_failed: expr, $type: pat, $check: expr) => {
1073+
assert!($res.is_ok());
1074+
let events = $node.node.get_and_clear_pending_events();
1075+
assert!(events.len() == 2);
1076+
match &events[0] {
1077+
crate::events::Event::PaymentPathFailed { failure, .. } => {
1078+
match failure {
1079+
crate::events::PathFailure::InitialSend { err } => {
1080+
match err {
1081+
$type => { $check },
1082+
_ => panic!()
1083+
}
1084+
},
1085+
_ => panic!()
10931086
}
10941087
},
1095-
_ => {panic!()},
1088+
_ => panic!()
1089+
}
1090+
match &events[1] {
1091+
crate::events::Event::PaymentFailed { .. } => {},
1092+
_ => panic!()
10961093
}
10971094
}
10981095
}

0 commit comments

Comments
 (0)