use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
use crate::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA as LDK_DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA;
-use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils::HTLCFailReason;
use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, RoutePath, Router};
use crate::util::errors::APIError;
debug_assert_eq!(paths.len(), path_results.len());
for (path, path_res) in paths.into_iter().zip(path_results) {
if let Err(e) = path_res {
- let failed_scid = if let APIError::InvalidRoute { .. } = e {
- None
- } else {
+ if let APIError::MonitorUpdateInProgress = e { continue }
+ let mut failed_scid = None;
+ if let APIError::ChannelUnavailable { .. } = e {
let scid = path[0].short_channel_id;
+ failed_scid = Some(scid);
route_params.payment_params.previously_failed_channels.push(scid);
- Some(scid)
- };
+ }
events.push(events::Event::PaymentPathFailed {
payment_id: Some(payment_id),
payment_hash,
payment_failed_permanently: false,
- network_update: None,
- all_paths_failed: false,
+ failure: events::PathFailure::InitialSend { err: e },
path,
short_channel_id: failed_scid,
retry: None,
u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
if route.paths.len() < 1 {
- return Err(PaymentSendFailure::ParameterError(APIError::InvalidRoute{err: "There must be at least one path to send over"}));
+ return Err(PaymentSendFailure::ParameterError(APIError::InvalidRoute{err: "There must be at least one path to send over".to_owned()}));
}
if payment_secret.is_none() && route.paths.len() > 1 {
- return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_string()}));
+ return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_owned()}));
}
let mut total_value = 0;
let our_node_id = node_signer.get_node_id(Recipient::Node).unwrap(); // TODO no unwrap
let mut path_errs = Vec::with_capacity(route.paths.len());
'path_check: for path in route.paths.iter() {
if path.len() < 1 || path.len() > 20 {
- path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size"}));
+ path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size".to_owned()}));
continue 'path_check;
}
for (idx, hop) in path.iter().enumerate() {
if idx != path.len() - 1 && hop.pubkey == our_node_id {
- path_errs.push(Err(APIError::InvalidRoute{err: "Path went through us but wasn't a simple rebalance loop to us"}));
+ path_errs.push(Err(APIError::InvalidRoute{err: "Path went through us but wasn't a simple rebalance loop to us".to_owned()}));
continue 'path_check;
}
}
awaiting_retry
});
- let mut all_paths_failed = false;
let mut full_failure_ev = None;
let mut pending_retry_ev = false;
let mut retry = None;
is_retryable_now = false;
}
if payment.get().remaining_parts() == 0 {
- all_paths_failed = true;
if payment.get().abandoned() {
if !payment_is_probe {
full_failure_ev = Some(events::Event::PaymentFailed {
payment_id: Some(*payment_id),
payment_hash: payment_hash.clone(),
payment_failed_permanently: !payment_retryable,
- network_update,
- all_paths_failed,
+ failure: events::PathFailure::OnPath { network_update },
path: path.clone(),
short_channel_id,
retry,
#[cfg(test)]
mod tests {
- use bitcoin::blockdata::constants::genesis_block;
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use crate::ln::PaymentHash;
use crate::ln::channelmanager::PaymentId;
+ use crate::ln::features::{ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError};
use crate::ln::outbound_payment::{OutboundPayments, Retry, RetryableSendFailure};
use crate::routing::gossip::NetworkGraph;
- use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters};
+ use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters};
use crate::sync::{Arc, Mutex};
- use crate::util::events::Event;
+ use crate::util::errors::APIError;
+ use crate::util::events::{Event, PathFailure};
use crate::util::test_utils;
#[test]
fn do_fails_paying_after_expiration(on_retry: bool) {
let outbound_payments = OutboundPayments::new();
let logger = test_utils::TestLogger::new();
- let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
- let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
+ let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = Mutex::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer);
let secp_ctx = Secp256k1::new();
fn do_find_route_error(on_retry: bool) {
let outbound_payments = OutboundPayments::new();
let logger = test_utils::TestLogger::new();
- let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
- let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
+ let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = Mutex::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer);
let secp_ctx = Secp256k1::new();
} else { panic!("Unexpected error"); }
}
}
+
+ #[test]
+ fn initial_send_payment_path_failed_evs() {
+ let outbound_payments = OutboundPayments::new();
+ let logger = test_utils::TestLogger::new();
+ let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
+ let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
+ let scorer = Mutex::new(test_utils::TestScorer::new());
+ let router = test_utils::TestRouter::new(network_graph, &scorer);
+ let secp_ctx = Secp256k1::new();
+ let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
+
+ let sender_pk = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+ let receiver_pk = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap());
+ let payment_params = PaymentParameters::from_node_id(sender_pk, 0);
+ let route_params = RouteParameters {
+ payment_params: payment_params.clone(),
+ final_value_msat: 0,
+ final_cltv_expiry_delta: 0,
+ };
+ let failed_scid = 42;
+ let route = Route {
+ paths: vec![vec![RouteHop {
+ pubkey: receiver_pk,
+ node_features: NodeFeatures::empty(),
+ short_channel_id: failed_scid,
+ channel_features: ChannelFeatures::empty(),
+ fee_msat: 0,
+ cltv_expiry_delta: 0,
+ }]],
+ payment_params: Some(payment_params),
+ };
+ router.expect_find_route(route_params.clone(), Ok(route.clone()));
+ let mut route_params_w_failed_scid = route_params.clone();
+ route_params_w_failed_scid.payment_params.previously_failed_channels.push(failed_scid);
+ router.expect_find_route(route_params_w_failed_scid, Ok(route.clone()));
+ router.expect_find_route(route_params.clone(), Ok(route.clone()));
+ router.expect_find_route(route_params.clone(), Ok(route.clone()));
+
+ // Ensure that a ChannelUnavailable error will result in blaming an scid in the
+ // PaymentPathFailed event.
+ let pending_events = Mutex::new(Vec::new());
+ outbound_payments.send_payment(
+ PaymentHash([0; 32]), &None, PaymentId([0; 32]), Retry::Attempts(0), route_params.clone(),
+ &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger,
+ &pending_events,
+ |_, _, _, _, _, _, _, _, _| Err(APIError::ChannelUnavailable { err: "test".to_owned() }))
+ .unwrap();
+ let mut events = pending_events.lock().unwrap();
+ assert_eq!(events.len(), 2);
+ if let Event::PaymentPathFailed {
+ short_channel_id,
+ failure: PathFailure::InitialSend { err: APIError::ChannelUnavailable { .. }}, .. } = events[0]
+ {
+ assert_eq!(short_channel_id, Some(failed_scid));
+ } else { panic!("Unexpected event"); }
+ if let Event::PaymentFailed { .. } = events[1] { } else { panic!("Unexpected event"); }
+ events.clear();
+ core::mem::drop(events);
+
+ // Ensure that a MonitorUpdateInProgress "error" will not result in a PaymentPathFailed event.
+ outbound_payments.send_payment(
+ PaymentHash([0; 32]), &None, PaymentId([0; 32]), Retry::Attempts(0), route_params.clone(),
+ &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger,
+ &pending_events, |_, _, _, _, _, _, _, _, _| Err(APIError::MonitorUpdateInProgress))
+ .unwrap();
+ {
+ let events = pending_events.lock().unwrap();
+ assert_eq!(events.len(), 0);
+ }
+
+ // Ensure that any other error will result in a PaymentPathFailed event but no blamed scid.
+ outbound_payments.send_payment(
+ PaymentHash([0; 32]), &None, PaymentId([1; 32]), Retry::Attempts(0), route_params.clone(),
+ &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger,
+ &pending_events,
+ |_, _, _, _, _, _, _, _, _| Err(APIError::APIMisuseError { err: "test".to_owned() }))
+ .unwrap();
+ let events = pending_events.lock().unwrap();
+ assert_eq!(events.len(), 2);
+ if let Event::PaymentPathFailed {
+ short_channel_id,
+ failure: PathFailure::InitialSend { err: APIError::APIMisuseError { .. }}, .. } = events[0]
+ {
+ assert_eq!(short_channel_id, None);
+ } else { panic!("Unexpected event"); }
+ if let Event::PaymentFailed { .. } = events[1] { } else { panic!("Unexpected event"); }
+ }
}