Skip to content

Commit c923806

Browse files
Abandon payment if retry fails in process_pending_htlcs
1 parent 3a85b9c commit c923806

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 2 additions & 1 deletion
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

Lines changed: 59 additions & 3 deletions
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,13 @@ 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.
533+
self.abandon_payment(payment_id, pending_events);
530534
}
531535
} else { break }
532536
}
@@ -1192,18 +1196,19 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
11921196

11931197
#[cfg(test)]
11941198
mod tests {
1199+
use super::*;
11951200
use bitcoin::blockdata::constants::genesis_block;
11961201
use bitcoin::network::constants::Network;
11971202
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
11981203

11991204
use crate::ln::PaymentHash;
12001205
use crate::ln::channelmanager::{PaymentId, PaymentSendFailure};
12011206
use crate::ln::msgs::{ErrorAction, LightningError};
1202-
use crate::ln::outbound_payment::{OutboundPayments, Retry};
12031207
use crate::routing::gossip::NetworkGraph;
12041208
use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters};
12051209
use crate::sync::Arc;
12061210
use crate::util::errors::APIError;
1211+
use crate::util::events::Event;
12071212
use crate::util::test_utils;
12081213

12091214
#[test]
@@ -1288,4 +1293,55 @@ mod tests {
12881293
assert!(err.contains("Failed to find a route"));
12891294
} else { panic!("Unexpected error"); }
12901295
}
1296+
1297+
#[test]
1298+
fn fail_on_retry_error() {
1299+
let outbound_payments = OutboundPayments::new();
1300+
let logger = test_utils::TestLogger::new();
1301+
let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
1302+
let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
1303+
let router = test_utils::TestRouter::new(network_graph);
1304+
let secp_ctx = Secp256k1::new();
1305+
let session_priv = [42; 32];
1306+
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
1307+
{
1308+
let mut random_bytes_override = keys_manager.override_random_bytes.lock().unwrap();
1309+
*random_bytes_override = Some(session_priv);
1310+
}
1311+
1312+
let payment_params = PaymentParameters::from_node_id(
1313+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()), 0);
1314+
let route_params = RouteParameters {
1315+
payment_params,
1316+
final_value_msat: 1000,
1317+
final_cltv_expiry_delta: 0,
1318+
};
1319+
router.expect_find_route(route_params.clone(),
1320+
Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError }));
1321+
let our_payment_id = PaymentId([0; 32]);
1322+
let our_payment_hash = PaymentHash([0; 32]);
1323+
outbound_payments.add_new_pending_payment(our_payment_hash, None, our_payment_id, None,
1324+
&Route { paths: vec![], payment_params: None }, Some(Retry::Attempts(1)),
1325+
Some(route_params.payment_params.clone()), &&keys_manager, 0).unwrap();
1326+
{
1327+
// Simulate that there are more sats that need to be sent that are pending, so that
1328+
// check_retry_payments flags it for retry.
1329+
let mut pending_outbounds = outbound_payments.pending_outbound_payments.lock().unwrap();
1330+
if let Some(PendingOutboundPayment::Retryable { ref mut total_msat, .. }) = pending_outbounds.get_mut(&our_payment_id) {
1331+
*total_msat += 1000;
1332+
}
1333+
}
1334+
1335+
let pending_events_mtx = Mutex::new(vec![]);
1336+
outbound_payments.check_retry_payments(&&router, || vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &pending_events_mtx, &&logger, |_, _, _, _, _, _, _, _, _| Ok(()));
1337+
let pending_events = pending_events_mtx.lock().unwrap();
1338+
assert_eq!(pending_events.len(), 1);
1339+
match pending_events[0] {
1340+
Event::PaymentFailed { payment_id, payment_hash } => {
1341+
assert_eq!(payment_id, our_payment_id);
1342+
assert_eq!(payment_hash, our_payment_hash);
1343+
},
1344+
_ => panic!()
1345+
}
1346+
}
12911347
}

0 commit comments

Comments
 (0)