Skip to content

Commit 82a2c84

Browse files
Reinstate ChannelManager::send_payment_with_route API
Support more ergonomically sending payments to specific routes. We removed the original version of this API because it was hard to work with, but the concept of sending a payment to a specific route is still useful. Previously, users were able to do this via manually matching the payment id in their router, but that's cumbersome when we could just handle it internally.
1 parent 79267d3 commit 82a2c84

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

@@ -4583,21 +4578,31 @@ where
45834578
}
45844579
}
45854580

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

46034608
/// Sends a payment to the route found using the provided [`RouteParameters`], retrying failed
@@ -4626,7 +4631,8 @@ where
46264631
/// [`ChannelManager::list_recent_payments`] for more information.
46274632
///
46284633
/// Routes are automatically found using the [`Router] provided on startup. To fix a route for a
4629-
/// particular payment, match the [`PaymentId`] passed to [`Router::find_route_with_id`].
4634+
/// particular payment, use [`Self::send_payment_with_route`] or match the [`PaymentId`] passed to
4635+
/// [`Router::find_route_with_id`].
46304636
///
46314637
/// [`Event::PaymentSent`]: events::Event::PaymentSent
46324638
/// [`Event::PaymentFailed`]: events::Event::PaymentFailed
@@ -14391,7 +14397,7 @@ mod tests {
1439114397
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
1439214398
use crate::ln::types::ChannelId;
1439314399
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
14394-
use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId};
14400+
use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, RecipientOnionFields, InterceptId};
1439514401
use crate::ln::functional_test_utils::*;
1439614402
use crate::ln::msgs::{self, ErrorAction};
1439714403
use crate::ln::msgs::ChannelMessageHandler;
@@ -14822,14 +14828,17 @@ mod tests {
1482214828
route.paths[1].hops[0].short_channel_id = chan_2_id;
1482314829
route.paths[1].hops[1].short_channel_id = chan_4_id;
1482414830

14825-
match nodes[0].node.send_payment_with_route(route, payment_hash,
14826-
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0))
14827-
.unwrap_err() {
14828-
PaymentSendFailure::ParameterError(APIError::APIMisuseError { ref err }) => {
14829-
assert!(regex::Regex::new(r"Payment secret is required for multi-path payments").unwrap().is_match(err))
14830-
},
14831-
_ => panic!("unexpected error")
14831+
nodes[0].node.send_payment_with_route(route, payment_hash,
14832+
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap();
14833+
let events = nodes[0].node.get_and_clear_pending_events();
14834+
assert_eq!(events.len(), 1);
14835+
match events[0] {
14836+
Event::PaymentFailed { reason, .. } => {
14837+
assert_eq!(reason.unwrap(), crate::events::PaymentFailureReason::UnexpectedError);
14838+
}
14839+
_ => panic!()
1483214840
}
14841+
nodes[0].logger.assert_log_contains("lightning::ln::outbound_payment", "Payment secret is required for multi-path payments", 2);
1483314842
assert!(nodes[0].node.list_recent_payments().is_empty());
1483414843
}
1483514844

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)