Skip to content

Commit c13b390

Browse files
Abandon payment if retry fails in process_pending_htlcs
1 parent 46923f2 commit c13b390

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

lightning/src/ln/channelmanager.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3385,7 +3385,8 @@ where
33853385

33863386
let best_block_height = self.best_block.read().unwrap().height();
33873387
self.pending_outbound_payments.check_retry_payments(&self.router, || self.list_usable_channels(),
3388-
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height, &self.logger,
3388+
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
3389+
&self.pending_events, &self.logger,
33893390
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
33903391
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv));
33913392

lightning/src/ln/outbound_payment.rs

+58-3
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,8 @@ impl OutboundPayments {
489489

490490
pub(super) fn check_retry_payments<R: Deref, ES: Deref, NS: Deref, SP, IH, FH, L: Deref>(
491491
&self, router: &R, first_hops: FH, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
492-
best_block_height: u32, logger: &L, send_payment_along_path: SP,
492+
best_block_height: u32, pending_events: &Mutex<Vec<events::Event>>, logger: &L,
493+
send_payment_along_path: SP,
493494
)
494495
where
495496
R::Target: Router,
@@ -523,10 +524,14 @@ impl OutboundPayments {
523524
}
524525
}
525526
}
527+
core::mem::drop(outbounds);
526528
if let Some((payment_id, route_params)) = retry_id_route_params {
527-
core::mem::drop(outbounds);
528529
if let Err(e) = self.pay_internal(payment_id, None, route_params, router, first_hops(), inflight_htlcs(), entropy_source, node_signer, best_block_height, logger, &send_payment_along_path) {
529530
log_info!(logger, "Errored retrying payment: {:?}", e);
531+
// If we error on retry, there is no chance of the payment succeeding and no HTLCs have
532+
// been irrevocably committed to, so we can safely abandon. See docs on `pay_internal` for
533+
// more information.
534+
self.abandon_payment(payment_id, pending_events);
530535
}
531536
} else { break }
532537
}
@@ -1205,18 +1210,19 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
12051210

12061211
#[cfg(test)]
12071212
mod tests {
1213+
use super::*;
12081214
use bitcoin::blockdata::constants::genesis_block;
12091215
use bitcoin::network::constants::Network;
12101216
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
12111217

12121218
use crate::ln::PaymentHash;
12131219
use crate::ln::channelmanager::{PaymentId, PaymentSendFailure};
12141220
use crate::ln::msgs::{ErrorAction, LightningError};
1215-
use crate::ln::outbound_payment::{OutboundPayments, Retry};
12161221
use crate::routing::gossip::NetworkGraph;
12171222
use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters};
12181223
use crate::sync::Arc;
12191224
use crate::util::errors::APIError;
1225+
use crate::util::events::Event;
12201226
use crate::util::test_utils;
12211227

12221228
#[test]
@@ -1301,4 +1307,53 @@ mod tests {
13011307
assert!(err.contains("Failed to find a route"));
13021308
} else { panic!("Unexpected error"); }
13031309
}
1310+
1311+
#[test]
1312+
fn fail_on_retry_error() {
1313+
let outbound_payments = OutboundPayments::new();
1314+
let logger = test_utils::TestLogger::new();
1315+
let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
1316+
let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
1317+
let router = test_utils::TestRouter::new(network_graph);
1318+
let secp_ctx = Secp256k1::new();
1319+
let session_priv = [42; 32];
1320+
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
1321+
{
1322+
let mut random_bytes_override = keys_manager.override_random_bytes.lock().unwrap();
1323+
*random_bytes_override = Some(session_priv);
1324+
}
1325+
1326+
let payment_params = PaymentParameters::from_node_id(
1327+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()), 0);
1328+
let route_params = RouteParameters {
1329+
payment_params,
1330+
final_value_msat: 1000,
1331+
final_cltv_expiry_delta: 0,
1332+
};
1333+
router.expect_find_route(route_params.clone(),
1334+
Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError }));
1335+
let our_payment_id = PaymentId([0; 32]);
1336+
let our_payment_hash = PaymentHash([0; 32]);
1337+
outbound_payments.add_new_pending_payment(our_payment_hash, None, our_payment_id, None,
1338+
&Route { paths: vec![], payment_params: None }, Some(Retry::Attempts(1)),
1339+
Some(route_params.payment_params.clone()), &&keys_manager, 0).unwrap();
1340+
{
1341+
let mut pending_outbounds = outbound_payments.pending_outbound_payments.lock().unwrap();
1342+
if let Some(PendingOutboundPayment::Retryable { ref mut total_msat, .. }) = pending_outbounds.get_mut(&our_payment_id) {
1343+
*total_msat += 1000;
1344+
}
1345+
}
1346+
1347+
let pending_events_mtx = Mutex::new(vec![]);
1348+
outbound_payments.check_retry_payments(&&router, || vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events_mtx, &&logger, |_, _, _, _, _, _, _, _, _| Ok(()));
1349+
let pending_events = pending_events_mtx.lock().unwrap();
1350+
assert_eq!(pending_events.len(), 1);
1351+
match pending_events[0] {
1352+
Event::PaymentFailed { payment_id, payment_hash } => {
1353+
assert_eq!(payment_id, our_payment_id);
1354+
assert_eq!(payment_hash, our_payment_hash);
1355+
},
1356+
_ => panic!()
1357+
}
1358+
}
13041359
}

0 commit comments

Comments
 (0)