Skip to content

Commit c611d5f

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 3bd395f commit c611d5f

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

lightning/src/ln/channelmanager.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ where
788788
/// When acquiring this lock in read mode, rather than acquiring it directly, call
789789
/// `PersistenceNotifierGuard::notify_on_drop(..)` and pass the lock to it, to ensure the
790790
/// Notifier the lock contains sends out a notification when the lock is released.
791-
total_consistency_lock: RwLock<()>,
791+
pub(crate) total_consistency_lock: RwLock<()>,
792792

793793
persistence_notifier: Notifier,
794794

@@ -2312,6 +2312,8 @@ where
23122312

23132313
// Only public for testing, this should otherwise never be called direcly
23142314
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> {
2315+
debug_assert!(self.total_consistency_lock.try_write().is_err()); // Caller should hold this
2316+
23152317
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
23162318
let prng_seed = self.entropy_source.get_secure_random_bytes();
23172319
let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
@@ -2324,8 +2326,6 @@ where
23242326
}
23252327
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
23262328

2327-
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
2328-
23292329
let err: Result<(), _> = loop {
23302330
let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.first().unwrap().short_channel_id) {
23312331
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}),
@@ -2460,6 +2460,7 @@ where
24602460
/// [`PeerManager::process_events`]: crate::ln::peer_handler::PeerManager::process_events
24612461
pub fn send_payment(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
24622462
let best_block_height = self.best_block.read().unwrap().height();
2463+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
24632464
self.pending_outbound_payments
24642465
.send_payment_with_route(route, payment_hash, payment_secret, payment_id, &self.entropy_source, &self.node_signer, best_block_height,
24652466
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
@@ -2470,6 +2471,7 @@ where
24702471
/// `route_params` and retry failed payment paths based on `retry_strategy`.
24712472
pub fn send_payment_with_retry(&self, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<(), PaymentSendFailure> {
24722473
let best_block_height = self.best_block.read().unwrap().height();
2474+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
24732475
self.pending_outbound_payments
24742476
.send_payment(payment_hash, payment_secret, payment_id, retry_strategy, route_params,
24752477
&self.router, self.list_usable_channels(), self.compute_inflight_htlcs(),
@@ -2481,6 +2483,7 @@ where
24812483
#[cfg(test)]
24822484
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> {
24832485
let best_block_height = self.best_block.read().unwrap().height();
2486+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
24842487
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,
24852488
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
24862489
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
@@ -2505,6 +2508,7 @@ where
25052508
/// [`abandon_payment`]: [`ChannelManager::abandon_payment`]
25062509
pub fn retry_payment(&self, route: &Route, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
25072510
let best_block_height = self.best_block.read().unwrap().height();
2511+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
25082512
self.pending_outbound_payments.retry_payment_with_route(route, payment_id, &self.entropy_source, &self.node_signer, best_block_height,
25092513
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
25102514
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
@@ -2554,6 +2558,7 @@ where
25542558
/// [`send_payment`]: Self::send_payment
25552559
pub fn send_spontaneous_payment(&self, route: &Route, payment_preimage: Option<PaymentPreimage>, payment_id: PaymentId) -> Result<PaymentHash, PaymentSendFailure> {
25562560
let best_block_height = self.best_block.read().unwrap().height();
2561+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
25572562
self.pending_outbound_payments.send_spontaneous_payment(route, payment_preimage, payment_id, &self.entropy_source, &self.node_signer, best_block_height,
25582563
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
25592564
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
@@ -2564,6 +2569,7 @@ where
25642569
/// us to easily discern them from real payments.
25652570
pub fn send_probe(&self, hops: Vec<RouteHop>) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
25662571
let best_block_height = self.best_block.read().unwrap().height();
2572+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
25672573
self.pending_outbound_payments.send_probe(hops, self.probing_cookie_secret, &self.entropy_source, &self.node_signer, best_block_height,
25682574
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
25692575
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
@@ -7751,7 +7757,10 @@ mod tests {
77517757
// indicates there are more HTLCs coming.
77527758
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.
77537759
let session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, Some(payment_secret), payment_id, &mpp_route).unwrap();
7754-
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();
7760+
{
7761+
let _lck = nodes[0].node.total_consistency_lock.read().unwrap();
7762+
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();
7763+
}
77557764
check_added_monitors!(nodes[0], 1);
77567765
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
77577766
assert_eq!(events.len(), 1);
@@ -7781,7 +7790,10 @@ mod tests {
77817790
expect_payment_failed!(nodes[0], our_payment_hash, true);
77827791

77837792
// Send the second half of the original MPP payment.
7784-
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();
7793+
{
7794+
let _lck = nodes[0].node.total_consistency_lock.read().unwrap();
7795+
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();
7796+
}
77857797
check_added_monitors!(nodes[0], 1);
77867798
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
77877799
assert_eq!(events.len(), 1);

lightning/src/ln/functional_tests.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -4063,7 +4063,10 @@ fn do_test_htlc_timeout(send_partial_mpp: bool) {
40634063
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.
40644064
let payment_id = PaymentId([42; 32]);
40654065
let session_privs = nodes[0].node.test_add_new_pending_payment(our_payment_hash, Some(payment_secret), payment_id, &route).unwrap();
4066-
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();
4066+
{
4067+
let _lck = nodes[0].node.total_consistency_lock.read().unwrap();
4068+
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();
4069+
}
40674070
check_added_monitors!(nodes[0], 1);
40684071
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
40694072
assert_eq!(events.len(), 1);
@@ -9105,19 +9108,25 @@ fn test_inconsistent_mpp_params() {
91059108
nodes[0].node.test_add_new_pending_payment(our_payment_hash, Some(our_payment_secret), payment_id, &dup_route).unwrap()
91069109
};
91079110
{
9111+
let _lck = nodes[0].node.total_consistency_lock.read().unwrap();
91089112
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();
9109-
check_added_monitors!(nodes[0], 1);
9113+
}
9114+
check_added_monitors!(nodes[0], 1);
91109115

9116+
{
91119117
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
91129118
assert_eq!(events.len(), 1);
91139119
pass_along_path(&nodes[0], &[&nodes[1], &nodes[3]], 15_000_000, our_payment_hash, Some(our_payment_secret), events.pop().unwrap(), false, None);
91149120
}
91159121
assert!(nodes[3].node.get_and_clear_pending_events().is_empty());
91169122

91179123
{
9124+
let _lck = nodes[0].node.total_consistency_lock.read().unwrap();
91189125
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();
9119-
check_added_monitors!(nodes[0], 1);
9126+
}
9127+
check_added_monitors!(nodes[0], 1);
91209128

9129+
{
91219130
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
91229131
assert_eq!(events.len(), 1);
91239132
let payment_event = SendEvent::from_event(events.pop().unwrap());
@@ -9160,7 +9169,10 @@ fn test_inconsistent_mpp_params() {
91609169

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

9163-
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();
9172+
{
9173+
let _lck = nodes[0].node.total_consistency_lock.read().unwrap();
9174+
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();
9175+
}
91649176
check_added_monitors!(nodes[0], 1);
91659177

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

0 commit comments

Comments
 (0)