Skip to content

Commit 4a12b5f

Browse files
authored
Merge pull request #3171 from jkczyz/2024-07-propagate-error
Include routing failures in `Bolt12PaymentError`
2 parents a76ec06 + b697fbe commit 4a12b5f

File tree

2 files changed

+105
-55
lines changed

2 files changed

+105
-55
lines changed

lightning/src/ln/max_payment_path_len_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ fn bolt12_invoice_too_large_blinded_paths() {
388388
let invoice_om = nodes[1].onion_messenger.next_onion_message_for_peer(nodes[0].node.get_our_node_id()).unwrap();
389389
nodes[0].onion_messenger.handle_onion_message(&nodes[1].node.get_our_node_id(), &invoice_om);
390390
// TODO: assert on the invoice error once we support replying to invoice OMs with failure info
391-
nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Failed paying invoice: OnionPacketSizeExceeded", 1);
391+
nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Failed paying invoice: SendingFailed(OnionPacketSizeExceeded)", 1);
392392

393393
let events = nodes[0].node.get_and_clear_pending_events();
394394
assert_eq!(events.len(), 1);

lightning/src/ln/outbound_payment.rs

+104-54
Original file line numberDiff line numberDiff line change
@@ -510,11 +510,8 @@ pub enum Bolt12PaymentError {
510510
UnexpectedInvoice,
511511
/// Payment for an invoice with the corresponding [`PaymentId`] was already initiated.
512512
DuplicateInvoice,
513-
/// The [`BlindedPath`]s provided are too large and caused us to exceed the maximum onion hop data
514-
/// size of 1300 bytes.
515-
///
516-
/// [`BlindedPath`]: crate::blinded_path::BlindedPath
517-
OnionPacketSizeExceeded,
513+
/// The invoice was valid for the corresponding [`PaymentId`], but sending the payment failed.
514+
SendingFailed(RetryableSendFailure),
518515
}
519516

520517
/// Indicates that we failed to send a payment probe. Further errors may be surfaced later via
@@ -803,20 +800,24 @@ impl OutboundPayments {
803800
{
804801
let payment_hash = invoice.payment_hash();
805802
let max_total_routing_fee_msat;
803+
let retry_strategy;
806804
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
807805
hash_map::Entry::Occupied(entry) => match entry.get() {
808-
PendingOutboundPayment::AwaitingInvoice { retry_strategy, max_total_routing_fee_msat: max_total_fee, .. } => {
806+
PendingOutboundPayment::AwaitingInvoice {
807+
retry_strategy: retry, max_total_routing_fee_msat: max_total_fee, ..
808+
} => {
809+
retry_strategy = Some(*retry);
809810
max_total_routing_fee_msat = *max_total_fee;
810811
*entry.into_mut() = PendingOutboundPayment::InvoiceReceived {
811812
payment_hash,
812-
retry_strategy: *retry_strategy,
813+
retry_strategy: *retry,
813814
max_total_routing_fee_msat,
814815
};
815816
},
816817
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
817818
},
818819
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
819-
};
820+
}
820821

821822
let mut payment_params = PaymentParameters::from_bolt12_invoice(&invoice);
822823

@@ -842,25 +843,64 @@ impl OutboundPayments {
842843
let mut route_params = RouteParameters::from_payment_params_and_value(
843844
payment_params, amount_msat
844845
);
845-
onion_utils::set_max_path_length(
846-
&mut route_params, &RecipientOnionFields::spontaneous_empty(), None, best_block_height
847-
)
848-
.map_err(|()| {
849-
log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \
850-
hop_data length for payment with id {} and hash {}", payment_id, payment_hash);
851-
Bolt12PaymentError::OnionPacketSizeExceeded
852-
})?;
853846

854847
if let Some(max_fee_msat) = max_total_routing_fee_msat {
855848
route_params.max_total_routing_fee_msat = Some(max_fee_msat);
856849
}
857850

858-
self.find_route_and_send_payment(
859-
payment_hash, payment_id, route_params, router, first_hops, &inflight_htlcs,
860-
entropy_source, node_signer, best_block_height, logger, pending_events,
861-
&send_payment_along_path
851+
let recipient_onion = RecipientOnionFields {
852+
payment_secret: None,
853+
payment_metadata: None,
854+
custom_tlvs: vec![],
855+
};
856+
let route = match self.find_initial_route(
857+
payment_id, payment_hash, &recipient_onion, None, &mut route_params, router,
858+
&first_hops, &inflight_htlcs, node_signer, best_block_height, logger,
859+
) {
860+
Ok(route) => route,
861+
Err(e) => {
862+
let reason = match e {
863+
RetryableSendFailure::PaymentExpired => PaymentFailureReason::PaymentExpired,
864+
RetryableSendFailure::RouteNotFound => PaymentFailureReason::RouteNotFound,
865+
RetryableSendFailure::DuplicatePayment => PaymentFailureReason::UnexpectedError,
866+
RetryableSendFailure::OnionPacketSizeExceeded => PaymentFailureReason::UnexpectedError,
867+
};
868+
self.abandon_payment(payment_id, reason, pending_events);
869+
return Err(Bolt12PaymentError::SendingFailed(e));
870+
},
871+
};
872+
873+
let payment_params = Some(route_params.payment_params.clone());
874+
let (retryable_payment, onion_session_privs) = self.create_pending_payment(
875+
payment_hash, recipient_onion.clone(), None, &route,
876+
retry_strategy, payment_params, entropy_source, best_block_height
862877
);
878+
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
879+
hash_map::Entry::Occupied(entry) => match entry.get() {
880+
PendingOutboundPayment::InvoiceReceived { .. } => {
881+
*entry.into_mut() = retryable_payment;
882+
},
883+
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
884+
},
885+
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
886+
}
863887

888+
let result = self.pay_route_internal(
889+
&route, payment_hash, &recipient_onion, None, payment_id,
890+
Some(route_params.final_value_msat), onion_session_privs, node_signer,
891+
best_block_height, &send_payment_along_path
892+
);
893+
log_info!(
894+
logger, "Sending payment with id {} and hash {} returned {:?}", payment_id,
895+
payment_hash, result
896+
);
897+
if let Err(e) = result {
898+
self.handle_pay_route_err(
899+
e, payment_id, payment_hash, route, route_params, router, first_hops,
900+
&inflight_htlcs, entropy_source, node_signer, best_block_height, logger,
901+
pending_events, &send_payment_along_path
902+
);
903+
}
864904
Ok(())
865905
}
866906

@@ -928,25 +968,17 @@ impl OutboundPayments {
928968
!pmt.is_awaiting_invoice())
929969
}
930970

931-
/// Errors immediately on [`RetryableSendFailure`] error conditions. Otherwise, further errors may
932-
/// be surfaced asynchronously via [`Event::PaymentPathFailed`] and [`Event::PaymentFailed`].
933-
///
934-
/// [`Event::PaymentPathFailed`]: crate::events::Event::PaymentPathFailed
935-
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
936-
fn send_payment_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
937-
&self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
938-
keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, mut route_params: RouteParameters,
939-
router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
940-
node_signer: &NS, best_block_height: u32, logger: &L,
941-
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
942-
) -> Result<(), RetryableSendFailure>
971+
fn find_initial_route<R: Deref, NS: Deref, IH, L: Deref>(
972+
&self, payment_id: PaymentId, payment_hash: PaymentHash,
973+
recipient_onion: &RecipientOnionFields, keysend_preimage: Option<PaymentPreimage>,
974+
route_params: &mut RouteParameters, router: &R, first_hops: &Vec<ChannelDetails>,
975+
inflight_htlcs: &IH, node_signer: &NS, best_block_height: u32, logger: &L,
976+
) -> Result<Route, RetryableSendFailure>
943977
where
944978
R::Target: Router,
945-
ES::Target: EntropySource,
946979
NS::Target: NodeSigner,
947980
L::Target: Logger,
948981
IH: Fn() -> InFlightHtlcs,
949-
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
950982
{
951983
#[cfg(feature = "std")] {
952984
if has_expired(&route_params) {
@@ -957,7 +989,7 @@ impl OutboundPayments {
957989
}
958990

959991
onion_utils::set_max_path_length(
960-
&mut route_params, &recipient_onion, keysend_preimage, best_block_height
992+
route_params, recipient_onion, keysend_preimage, best_block_height
961993
)
962994
.map_err(|()| {
963995
log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \
@@ -966,7 +998,7 @@ impl OutboundPayments {
966998
})?;
967999

9681000
let mut route = router.find_route_with_id(
969-
&node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
1001+
&node_signer.get_node_id(Recipient::Node).unwrap(), route_params,
9701002
Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),
9711003
payment_hash, payment_id,
9721004
).map_err(|_| {
@@ -975,12 +1007,40 @@ impl OutboundPayments {
9751007
RetryableSendFailure::RouteNotFound
9761008
})?;
9771009

978-
if route.route_params.as_ref() != Some(&route_params) {
1010+
if route.route_params.as_ref() != Some(route_params) {
9791011
debug_assert!(false,
9801012
"Routers are expected to return a Route which includes the requested RouteParameters");
9811013
route.route_params = Some(route_params.clone());
9821014
}
9831015

1016+
Ok(route)
1017+
}
1018+
1019+
/// Errors immediately on [`RetryableSendFailure`] error conditions. Otherwise, further errors may
1020+
/// be surfaced asynchronously via [`Event::PaymentPathFailed`] and [`Event::PaymentFailed`].
1021+
///
1022+
/// [`Event::PaymentPathFailed`]: crate::events::Event::PaymentPathFailed
1023+
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
1024+
fn send_payment_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
1025+
&self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
1026+
keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, mut route_params: RouteParameters,
1027+
router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
1028+
node_signer: &NS, best_block_height: u32, logger: &L,
1029+
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
1030+
) -> Result<(), RetryableSendFailure>
1031+
where
1032+
R::Target: Router,
1033+
ES::Target: EntropySource,
1034+
NS::Target: NodeSigner,
1035+
L::Target: Logger,
1036+
IH: Fn() -> InFlightHtlcs,
1037+
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
1038+
{
1039+
let route = self.find_initial_route(
1040+
payment_id, payment_hash, &recipient_onion, keysend_preimage, &mut route_params, router,
1041+
&first_hops, &inflight_htlcs, node_signer, best_block_height, logger,
1042+
)?;
1043+
9841044
let onion_session_privs = self.add_new_pending_payment(payment_hash,
9851045
recipient_onion.clone(), payment_id, keysend_preimage, &route, Some(retry_strategy),
9861046
Some(route_params.payment_params.clone()), entropy_source, best_block_height)
@@ -1115,23 +1175,13 @@ impl OutboundPayments {
11151175
},
11161176
PendingOutboundPayment::AwaitingInvoice { .. } => {
11171177
log_error!(logger, "Payment not yet sent");
1178+
debug_assert!(false);
11181179
return
11191180
},
1120-
PendingOutboundPayment::InvoiceReceived { payment_hash, retry_strategy, .. } => {
1121-
let total_amount = route_params.final_value_msat;
1122-
let recipient_onion = RecipientOnionFields {
1123-
payment_secret: None,
1124-
payment_metadata: None,
1125-
custom_tlvs: vec![],
1126-
};
1127-
let retry_strategy = Some(*retry_strategy);
1128-
let payment_params = Some(route_params.payment_params.clone());
1129-
let (retryable_payment, onion_session_privs) = self.create_pending_payment(
1130-
*payment_hash, recipient_onion.clone(), None, &route,
1131-
retry_strategy, payment_params, entropy_source, best_block_height
1132-
);
1133-
*payment.into_mut() = retryable_payment;
1134-
(total_amount, recipient_onion, None, onion_session_privs)
1181+
PendingOutboundPayment::InvoiceReceived { .. } => {
1182+
log_error!(logger, "Payment already initiating");
1183+
debug_assert!(false);
1184+
return
11351185
},
11361186
PendingOutboundPayment::Fulfilled { .. } => {
11371187
log_error!(logger, "Payment already completed");
@@ -2273,7 +2323,7 @@ mod tests {
22732323
&&keys_manager, &EmptyNodeIdLookUp {}, &secp_ctx, 0, &&logger, &pending_events,
22742324
|_| panic!()
22752325
),
2276-
Ok(()),
2326+
Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::PaymentExpired)),
22772327
);
22782328
assert!(!outbound_payments.has_pending_payments());
22792329

@@ -2334,7 +2384,7 @@ mod tests {
23342384
&&keys_manager, &EmptyNodeIdLookUp {}, &secp_ctx, 0, &&logger, &pending_events,
23352385
|_| panic!()
23362386
),
2337-
Ok(()),
2387+
Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::RouteNotFound)),
23382388
);
23392389
assert!(!outbound_payments.has_pending_payments());
23402390

0 commit comments

Comments
 (0)