Skip to content

Commit 1d4bc1e

Browse files
committed
Hold the total_consistency_lock while in outbound_payment fns
We previously avoided holding the `total_consistency_lock` while doing crypto operations to build onions. However, now that we've abstracted out the outbound payment logic into a utility module, ensuring the state is consistent at all times is now abstracted away from code authors and reviewers, making it likely to break. Further, because we now call `send_payment_along_path` both with, and without, the `total_consistency_lock`, and because recursive read locks may deadlock, it would now be quite difficult to figure out which paths through `outbound_payment` need the lock and which don't. While it may slow writes somewhat, it's not really worth trying to figure out this mess, instead we just hold the `total_consistency_lock` before going into `outbound_payment` functions.
1 parent b5e5435 commit 1d4bc1e

File tree

2 files changed

+26
-14
lines changed

2 files changed

+26
-14
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,8 +2413,16 @@ where
24132413
})
24142414
}
24152415

2416-
// Only public for testing, this should otherwise never be called direcly
2417-
pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_params: &Option<PaymentParameters>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
2416+
#[cfg(test)]
2417+
pub(crate) fn test_send_payment_along_path(&self, path: &Vec<RouteHop>, payment_params: &Option<PaymentParameters>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
2418+
let _lck = self.total_consistency_lock.read().unwrap();
2419+
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv_bytes)
2420+
}
2421+
2422+
fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_params: &Option<PaymentParameters>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
2423+
// The top-level caller should hold the total_consistency_lock read lock.
2424+
debug_assert!(self.total_consistency_lock.try_write().is_err());
2425+
24182426
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
24192427
let prng_seed = self.entropy_source.get_secure_random_bytes();
24202428
let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
@@ -2427,8 +2435,6 @@ where
24272435
}
24282436
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
24292437

2430-
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
2431-
24322438
let err: Result<(), _> = loop {
24332439
let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.first().unwrap().short_channel_id) {
24342440
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}),
@@ -2555,6 +2561,7 @@ where
25552561
/// [`ChannelMonitorUpdateStatus::InProgress`]: crate::chain::ChannelMonitorUpdateStatus::InProgress
25562562
pub fn send_payment(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
25572563
let best_block_height = self.best_block.read().unwrap().height();
2564+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
25582565
self.pending_outbound_payments
25592566
.send_payment_with_route(route, payment_hash, payment_secret, payment_id, &self.entropy_source, &self.node_signer, best_block_height,
25602567
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
@@ -2565,6 +2572,7 @@ where
25652572
/// `route_params` and retry failed payment paths based on `retry_strategy`.
25662573
pub fn send_payment_with_retry(&self, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<(), RetryableSendFailure> {
25672574
let best_block_height = self.best_block.read().unwrap().height();
2575+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
25682576
self.pending_outbound_payments
25692577
.send_payment(payment_hash, payment_secret, payment_id, retry_strategy, route_params,
25702578
&self.router, self.list_usable_channels(), || self.compute_inflight_htlcs(),
@@ -2577,6 +2585,7 @@ where
25772585
#[cfg(test)]
25782586
fn test_send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: Vec<[u8; 32]>) -> Result<(), PaymentSendFailure> {
25792587
let best_block_height = self.best_block.read().unwrap().height();
2588+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
25802589
self.pending_outbound_payments.test_send_payment_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id, recv_value_msat, onion_session_privs, &self.node_signer, best_block_height,
25812590
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
25822591
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
@@ -2627,6 +2636,7 @@ where
26272636
/// [`send_payment`]: Self::send_payment
26282637
pub fn send_spontaneous_payment(&self, route: &Route, payment_preimage: Option<PaymentPreimage>, payment_id: PaymentId) -> Result<PaymentHash, PaymentSendFailure> {
26292638
let best_block_height = self.best_block.read().unwrap().height();
2639+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
26302640
self.pending_outbound_payments.send_spontaneous_payment_with_route(
26312641
route, payment_preimage, payment_id, &self.entropy_source, &self.node_signer,
26322642
best_block_height,
@@ -2643,6 +2653,7 @@ where
26432653
/// [`PaymentParameters::for_keysend`]: crate::routing::router::PaymentParameters::for_keysend
26442654
pub fn send_spontaneous_payment_with_retry(&self, payment_preimage: Option<PaymentPreimage>, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<PaymentHash, RetryableSendFailure> {
26452655
let best_block_height = self.best_block.read().unwrap().height();
2656+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
26462657
self.pending_outbound_payments.send_spontaneous_payment(payment_preimage, payment_id,
26472658
retry_strategy, route_params, &self.router, self.list_usable_channels(),
26482659
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
@@ -2656,6 +2667,7 @@ where
26562667
/// us to easily discern them from real payments.
26572668
pub fn send_probe(&self, hops: Vec<RouteHop>) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
26582669
let best_block_height = self.best_block.read().unwrap().height();
2670+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
26592671
self.pending_outbound_payments.send_probe(hops, self.probing_cookie_secret, &self.entropy_source, &self.node_signer, best_block_height,
26602672
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
26612673
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
@@ -7841,7 +7853,7 @@ mod tests {
78417853
// indicates there are more HTLCs coming.
78427854
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
78437855
let session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, Some(payment_secret), payment_id, &mpp_route).unwrap();
7844-
nodes[0].node.send_payment_along_path(&mpp_route.paths[0], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None, session_privs[0]).unwrap();
7856+
nodes[0].node.test_send_payment_along_path(&mpp_route.paths[0], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None, session_privs[0]).unwrap();
78457857
check_added_monitors!(nodes[0], 1);
78467858
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
78477859
assert_eq!(events.len(), 1);
@@ -7871,7 +7883,7 @@ mod tests {
78717883
expect_payment_failed!(nodes[0], our_payment_hash, true);
78727884

78737885
// Send the second half of the original MPP payment.
7874-
nodes[0].node.send_payment_along_path(&mpp_route.paths[1], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None, session_privs[1]).unwrap();
7886+
nodes[0].node.test_send_payment_along_path(&mpp_route.paths[1], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None, session_privs[1]).unwrap();
78757887
check_added_monitors!(nodes[0], 1);
78767888
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
78777889
assert_eq!(events.len(), 1);

lightning/src/ln/functional_tests.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4083,7 +4083,7 @@ fn do_test_htlc_timeout(send_partial_mpp: bool) {
40834083
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
40844084
let payment_id = PaymentId([42; 32]);
40854085
let session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, Some(payment_secret), payment_id, &route).unwrap();
4086-
nodes[0].node.send_payment_along_path(&route.paths[0], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None, session_privs[0]).unwrap();
4086+
nodes[0].node.test_send_payment_along_path(&route.paths[0], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None, session_privs[0]).unwrap();
40874087
check_added_monitors!(nodes[0], 1);
40884088
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
40894089
assert_eq!(events.len(), 1);
@@ -9141,20 +9141,20 @@ fn test_inconsistent_mpp_params() {
91419141
dup_route.paths.push(route.paths[1].clone());
91429142
nodes[0].node.test_add_new_pending_payment(our_payment_hash, Some(our_payment_secret), payment_id, &dup_route).unwrap()
91439143
};
9144-
{
9145-
nodes[0].node.send_payment_along_path(&route.paths[0], &payment_params_opt, &our_payment_hash, &Some(our_payment_secret), 15_000_000, cur_height, payment_id, &None, session_privs[0]).unwrap();
9146-
check_added_monitors!(nodes[0], 1);
9144+
nodes[0].node.test_send_payment_along_path(&route.paths[0], &payment_params_opt, &our_payment_hash, &Some(our_payment_secret), 15_000_000, cur_height, payment_id, &None, session_privs[0]).unwrap();
9145+
check_added_monitors!(nodes[0], 1);
91479146

9147+
{
91489148
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
91499149
assert_eq!(events.len(), 1);
91509150
pass_along_path(&nodes[0], &[&nodes[1], &nodes[3]], 15_000_000, our_payment_hash, Some(our_payment_secret), events.pop().unwrap(), false, None);
91519151
}
91529152
assert!(nodes[3].node.get_and_clear_pending_events().is_empty());
91539153

9154-
{
9155-
nodes[0].node.send_payment_along_path(&route.paths[1], &payment_params_opt, &our_payment_hash, &Some(our_payment_secret), 14_000_000, cur_height, payment_id, &None, session_privs[1]).unwrap();
9156-
check_added_monitors!(nodes[0], 1);
9154+
nodes[0].node.test_send_payment_along_path(&route.paths[1], &payment_params_opt, &our_payment_hash, &Some(our_payment_secret), 14_000_000, cur_height, payment_id, &None, session_privs[1]).unwrap();
9155+
check_added_monitors!(nodes[0], 1);
91579156

9157+
{
91589158
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
91599159
assert_eq!(events.len(), 1);
91609160
let payment_event = SendEvent::from_event(events.pop().unwrap());
@@ -9197,7 +9197,7 @@ fn test_inconsistent_mpp_params() {
91979197

91989198
expect_payment_failed_conditions(&nodes[0], our_payment_hash, true, PaymentFailedConditions::new().mpp_parts_remain());
91999199

9200-
nodes[0].node.send_payment_along_path(&route.paths[1], &payment_params_opt, &our_payment_hash, &Some(our_payment_secret), 15_000_000, cur_height, payment_id, &None, session_privs[2]).unwrap();
9200+
nodes[0].node.test_send_payment_along_path(&route.paths[1], &payment_params_opt, &our_payment_hash, &Some(our_payment_secret), 15_000_000, cur_height, payment_id, &None, session_privs[2]).unwrap();
92019201
check_added_monitors!(nodes[0], 1);
92029202

92039203
let mut events = nodes[0].node.get_and_clear_pending_msg_events();

0 commit comments

Comments
 (0)