Skip to content

Commit 5c0163f

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 1999952 commit 5c0163f

10 files changed

+155
-151
lines changed

fuzz/src/chanmon_consistency.rs

+9-17
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,18 +112,13 @@ 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-
}
128122
Err(msgs::LightningError {
129123
err: String::from("Not implemented"),
130124
action: msgs::ErrorAction::IgnoreError,
@@ -518,7 +512,7 @@ fn send_payment(
518512
PaymentParameters::from_node_id(source.get_our_node_id(), TEST_FINAL_CLTV),
519513
amt,
520514
);
521-
source.router.next_routes.lock().unwrap().push_back(Route {
515+
let route = Route {
522516
paths: vec![Path {
523517
hops: vec![RouteHop {
524518
pubkey: dest.get_our_node_id(),
@@ -532,11 +526,10 @@ fn send_payment(
532526
blinded_tail: None,
533527
}],
534528
route_params: Some(route_params.clone()),
535-
});
529+
};
536530
let onion = RecipientOnionFields::secret_only(payment_secret);
537531
let payment_id = PaymentId(payment_id);
538-
let res =
539-
source.send_payment(payment_hash, onion, payment_id, route_params, Retry::Attempts(0));
532+
let res = source.send_payment_with_route(route, payment_hash, onion, payment_id);
540533
match res {
541534
Err(err) => {
542535
panic!("Errored with {:?} on initial payment send", err);
@@ -592,7 +585,7 @@ fn send_hop_payment(
592585
PaymentParameters::from_node_id(source.get_our_node_id(), TEST_FINAL_CLTV),
593586
amt,
594587
);
595-
source.router.next_routes.lock().unwrap().push_back(Route {
588+
let route = Route {
596589
paths: vec![Path {
597590
hops: vec![
598591
RouteHop {
@@ -617,11 +610,10 @@ fn send_hop_payment(
617610
blinded_tail: None,
618611
}],
619612
route_params: Some(route_params.clone()),
620-
});
613+
};
621614
let onion = RecipientOnionFields::secret_only(payment_secret);
622615
let payment_id = PaymentId(payment_id);
623-
let res =
624-
source.send_payment(payment_hash, onion, payment_id, route_params, Retry::Attempts(0));
616+
let res = source.send_payment_with_route(route, payment_hash, onion, payment_id);
625617
match res {
626618
Err(err) => {
627619
panic!("Errored with {:?} on initial payment send", err);
@@ -640,7 +632,7 @@ fn send_hop_payment(
640632
pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
641633
let out = SearchingOutput::new(underlying_out);
642634
let broadcast = Arc::new(TestBroadcaster {});
643-
let router = FuzzRouter { next_routes: Mutex::new(VecDeque::new()) };
635+
let router = FuzzRouter {};
644636

645637
macro_rules! make_node {
646638
($node_id: expr, $fee_estimator: expr) => {{

lightning/src/chain/channelmonitor.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -5092,7 +5092,7 @@ mod tests {
50925092
use crate::chain::chaininterface::LowerBoundedFeeEstimator;
50935093

50945094
use super::ChannelMonitorUpdateStep;
5095-
use crate::{check_added_monitors, check_spends, get_local_commitment_txn, get_monitor, get_route_and_payment_hash, unwrap_send_err};
5095+
use crate::{check_added_monitors, check_spends, get_local_commitment_txn, get_monitor, get_route_and_payment_hash};
50965096
use crate::chain::{BestBlock, Confirm};
50975097
use crate::chain::channelmonitor::{ChannelMonitor, WithChannelMonitor};
50985098
use crate::chain::package::{weight_offered_htlc, weight_received_htlc, weight_revoked_offered_htlc, weight_revoked_received_htlc, WEIGHT_REVOKED_OUTPUT};
@@ -5102,10 +5102,9 @@ mod tests {
51025102
use crate::types::payment::{PaymentPreimage, PaymentHash};
51035103
use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, RevocationBasepoint, RevocationKey};
51045104
use crate::ln::chan_utils::{self,HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
5105-
use crate::ln::channelmanager::{PaymentSendFailure, PaymentId, RecipientOnionFields};
5105+
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
51065106
use crate::ln::functional_test_utils::*;
51075107
use crate::ln::script::ShutdownScript;
5108-
use crate::util::errors::APIError;
51095108
use crate::util::test_utils::{TestLogger, TestBroadcaster, TestFeeEstimator};
51105109
use crate::util::ser::{ReadableArgs, Writeable};
51115110
use crate::util::logger::Logger;
@@ -5166,9 +5165,9 @@ mod tests {
51665165
// If the ChannelManager tries to update the channel, however, the ChainMonitor will pass
51675166
// the update through to the ChannelMonitor which will refuse it (as the channel is closed).
51685167
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 100_000);
5169-
unwrap_send_err!(nodes[1].node.send_payment_with_route(route, payment_hash,
5170-
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
5171-
), false, APIError::MonitorUpdateInProgress, {});
5168+
nodes[1].node.send_payment_with_route(route, payment_hash,
5169+
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
5170+
).unwrap();
51725171
check_added_monitors!(nodes[1], 1);
51735172

51745173
// 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::{RAACommitmentOrder, PaymentSendFailure, PaymentId, RecipientOnionFields};
22+
use crate::ln::channelmanager::{RAACommitmentOrder, PaymentId, RecipientOnionFields};
2323
use crate::ln::channel::{AnnouncementSigsState, ChannelPhase};
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

+32-33
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, CachingRouter, 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;
@@ -2396,10 +2394,7 @@ where
23962394
fee_estimator: LowerBoundedFeeEstimator<F>,
23972395
chain_monitor: M,
23982396
tx_broadcaster: T,
2399-
#[cfg(fuzzing)]
2400-
pub router: R,
2401-
#[cfg(not(fuzzing))]
2402-
router: R,
2397+
router: CachingRouter<R>,
24032398
message_router: MR,
24042399

24052400
/// See `ChannelManager` struct-level documentation for lock order requirements.
@@ -3512,7 +3507,7 @@ where
35123507
fee_estimator: LowerBoundedFeeEstimator::new(fee_est),
35133508
chain_monitor,
35143509
tx_broadcaster,
3515-
router,
3510+
router: CachingRouter::new(router),
35163511
message_router,
35173512

35183513
best_block: RwLock::new(params.best_block),
@@ -4621,21 +4616,21 @@ where
46214616
}
46224617
}
46234618

4624-
// Deprecated send method, for testing use [`Self::send_payment`] and
4625-
// [`TestRouter::expect_find_route`] instead.
4626-
//
4627-
// [`TestRouter::expect_find_route`]: crate::util::test_utils::TestRouter::expect_find_route
4628-
#[cfg(test)]
4629-
pub(crate) fn send_payment_with_route(
4619+
/// Sends a payment along a given route. See [`Self::send_payment`] for more info.
4620+
/// [`Route::route_params`] is required to be `Some`.
4621+
pub fn send_payment_with_route(
46304622
&self, route: Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
46314623
payment_id: PaymentId
4632-
) -> Result<(), PaymentSendFailure> {
4624+
) -> Result<(), RetryableSendFailure> {
46334625
let best_block_height = self.best_block.read().unwrap().height;
46344626
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
4627+
let route_params = route.route_params.clone().ok_or(RetryableSendFailure::RouteNotFound)?;
4628+
self.router.route_cache.lock().unwrap().insert(payment_id, route);
46354629
self.pending_outbound_payments
4636-
.send_payment_with_route(&route, payment_hash, recipient_onion, payment_id,
4637-
&self.entropy_source, &self.node_signer, best_block_height,
4638-
|args| self.send_payment_along_path(args))
4630+
.send_payment(payment_hash, recipient_onion, payment_id, Retry::Attempts(0),
4631+
route_params, &self.router, self.list_usable_channels(), ||
4632+
self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
4633+
&self.logger, &self.pending_events, |args| self.send_payment_along_path(args))
46394634
}
46404635

46414636
/// Sends a payment to the route found using the provided [`RouteParameters`], retrying failed
@@ -4664,7 +4659,8 @@ where
46644659
/// [`ChannelManager::list_recent_payments`] for more information.
46654660
///
46664661
/// Routes are automatically found using the [`Router] provided on startup. To fix a route for a
4667-
/// particular payment, match the [`PaymentId`] passed to [`Router::find_route_with_id`].
4662+
/// particular payment, use [`Self::send_payment_with_route`] or match the [`PaymentId`] passed to
4663+
/// [`Router::find_route_with_id`].
46684664
///
46694665
/// [`Event::PaymentSent`]: events::Event::PaymentSent
46704666
/// [`Event::PaymentFailed`]: events::Event::PaymentFailed
@@ -4679,7 +4675,7 @@ where
46794675
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
46804676
self.pending_outbound_payments
46814677
.send_payment(payment_hash, recipient_onion, payment_id, retry_strategy, route_params,
4682-
&*self.router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
4678+
&self.router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
46834679
&self.entropy_source, &self.node_signer, best_block_height, &self.logger,
46844680
&self.pending_events, |args| self.send_payment_along_path(args))
46854681
}
@@ -4756,7 +4752,7 @@ where
47564752
let features = self.bolt12_invoice_features();
47574753
self.pending_outbound_payments
47584754
.send_payment_for_bolt12_invoice(
4759-
invoice, payment_id, &*self.router, self.list_usable_channels(), features,
4755+
invoice, payment_id, &self.router, self.list_usable_channels(), features,
47604756
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, &self,
47614757
&self.secp_ctx, best_block_height, &self.logger, &self.pending_events,
47624758
|args| self.send_payment_along_path(args)
@@ -4831,7 +4827,7 @@ where
48314827
let mut res = Ok(());
48324828
PersistenceNotifierGuard::optionally_notify(self, || {
48334829
let outbound_pmts_res = self.pending_outbound_payments.send_payment_for_static_invoice(
4834-
payment_id, &*self.router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
4830+
payment_id, &self.router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
48354831
&self.entropy_source, &self.node_signer, &self, &self.secp_ctx, best_block_height,
48364832
&self.logger, &self.pending_events, |args| self.send_payment_along_path(args)
48374833
);
@@ -4907,7 +4903,7 @@ where
49074903
let best_block_height = self.best_block.read().unwrap().height;
49084904
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
49094905
self.pending_outbound_payments.send_spontaneous_payment(payment_preimage, recipient_onion,
4910-
payment_id, retry_strategy, route_params, &*self.router, self.list_usable_channels(),
4906+
payment_id, retry_strategy, route_params, &self.router, self.list_usable_channels(),
49114907
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
49124908
&self.logger, &self.pending_events, |args| self.send_payment_along_path(args))
49134909
}
@@ -6263,7 +6259,7 @@ where
62636259
}
62646260

62656261
let best_block_height = self.best_block.read().unwrap().height;
6266-
self.pending_outbound_payments.check_retry_payments(&*self.router, || self.list_usable_channels(),
6262+
self.pending_outbound_payments.check_retry_payments(&self.router, || self.list_usable_channels(),
62676263
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
62686264
&self.pending_events, &self.logger, |args| self.send_payment_along_path(args));
62696265

@@ -14108,7 +14104,7 @@ where
1410814104
fee_estimator: bounded_fee_estimator,
1410914105
chain_monitor: args.chain_monitor,
1411014106
tx_broadcaster: args.tx_broadcaster,
14111-
router: args.router,
14107+
router: CachingRouter::new(args.router),
1411214108
message_router: args.message_router,
1411314109

1411414110
best_block: RwLock::new(BestBlock::new(best_block_hash, best_block_height)),
@@ -14352,7 +14348,7 @@ mod tests {
1435214348
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
1435314349
use crate::ln::types::ChannelId;
1435414350
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
14355-
use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId};
14351+
use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, RecipientOnionFields, InterceptId};
1435614352
use crate::ln::functional_test_utils::*;
1435714353
use crate::ln::msgs::{self, ErrorAction};
1435814354
use crate::ln::msgs::ChannelMessageHandler;
@@ -14778,14 +14774,17 @@ mod tests {
1477814774
route.paths[1].hops[0].short_channel_id = chan_2_id;
1477914775
route.paths[1].hops[1].short_channel_id = chan_4_id;
1478014776

14781-
match nodes[0].node.send_payment_with_route(route, payment_hash,
14782-
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0))
14783-
.unwrap_err() {
14784-
PaymentSendFailure::ParameterError(APIError::APIMisuseError { ref err }) => {
14785-
assert!(regex::Regex::new(r"Payment secret is required for multi-path payments").unwrap().is_match(err))
14786-
},
14787-
_ => panic!("unexpected error")
14777+
nodes[0].node.send_payment_with_route(route, payment_hash,
14778+
RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap();
14779+
let events = nodes[0].node.get_and_clear_pending_events();
14780+
assert_eq!(events.len(), 1);
14781+
match events[0] {
14782+
Event::PaymentFailed { reason, .. } => {
14783+
assert_eq!(reason.unwrap(), crate::events::PaymentFailureReason::UnexpectedError);
14784+
}
14785+
_ => panic!()
1478814786
}
14787+
nodes[0].logger.assert_log_contains("lightning::ln::outbound_payment", "Payment secret is required for multi-path payments", 2);
1478914788
assert!(nodes[0].node.pending_outbound_payments.pending_outbound_payments.lock().unwrap().is_empty());
1479014789
}
1479114790

0 commit comments

Comments
 (0)