u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
- self.send_payment_internal(route, payment_hash, payment_secret, None, payment_id, None,
+ self.pay_route_internal(route, payment_hash, payment_secret, None, payment_id, None,
onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
- match self.send_payment_internal(route, payment_hash, &None, Some(preimage), payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) {
+ match self.pay_route_internal(route, payment_hash, &None, Some(preimage), payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) {
Ok(()) => Ok(payment_hash),
Err(e) => {
self.remove_outbound_if_all_failed(payment_id, &e);
let res = if let Some((payment_hash, payment_secret, retry_strategy)) = initial_send_info {
let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, &route, retry_strategy, Some(route_params.clone()), entropy_source, best_block_height)?;
- self.send_payment_internal(&route, payment_hash, payment_secret, None, payment_id, None, onion_session_privs, node_signer, best_block_height, send_payment_along_path)
+ self.pay_route_internal(&route, payment_hash, payment_secret, None, payment_id, None, onion_session_privs, node_signer, best_block_height, send_payment_along_path)
} else {
self.retry_payment_with_route(&route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path)
};
})),
}
};
- self.send_payment_internal(route, payment_hash, &payment_secret, None, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
+ self.pay_route_internal(route, payment_hash, &payment_secret, None, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
}
pub(super) fn send_probe<ES: Deref, NS: Deref, F>(
let route = Route { paths: vec![hops], payment_params: None };
let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, Retry::Attempts(0), None, entropy_source, best_block_height)?;
- match self.send_payment_internal(&route, payment_hash, &None, None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) {
+ match self.pay_route_internal(&route, payment_hash, &None, None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) {
Ok(()) => Ok((payment_hash, payment_id)),
Err(e) => {
self.remove_outbound_if_all_failed(payment_id, &e);
}
}
- fn send_payment_internal<NS: Deref, F>(
+ fn pay_route_internal<NS: Deref, F>(
&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]>, node_signer: &NS, best_block_height: u32,
F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
{
- self.send_payment_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id,
+ self.pay_route_internal(route, payment_hash, payment_secret, keysend_preimage, payment_id,
recv_value_msat, onion_session_privs, node_signer, best_block_height,
&send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
(2, payment_hash, required),
},
);
+
+#[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, PaymentSendFailure};
+ use crate::ln::msgs::{ErrorAction, LightningError};
+ use crate::ln::outbound_payment::{OutboundPayments, Retry};
+ use crate::routing::gossip::NetworkGraph;
+ use crate::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters};
+ use crate::sync::Arc;
+ use crate::util::errors::APIError;
+ use crate::util::test_utils;
+
+ #[test]
+ #[cfg(feature = "std")]
+ fn fails_paying_after_expiration() {
+ do_fails_paying_after_expiration(false);
+ do_fails_paying_after_expiration(true);
+ }
+ #[cfg(feature = "std")]
+ 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 router = test_utils::TestRouter::new(network_graph);
+ let secp_ctx = Secp256k1::new();
+ let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
+
+ let past_expiry_time = std::time::SystemTime::UNIX_EPOCH.elapsed().unwrap().as_secs() - 2;
+ let payment_params = PaymentParameters::from_node_id(
+ PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()))
+ .with_expiry_time(past_expiry_time);
+ let expired_route_params = RouteParameters {
+ payment_params,
+ final_value_msat: 0,
+ final_cltv_expiry_delta: 0,
+ };
+ let err = if on_retry {
+ outbound_payments.pay_internal(
+ PaymentId([0; 32]), None, expired_route_params, &&router, vec![], InFlightHtlcs::new(),
+ &&keys_manager, &&keys_manager, 0, &|_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()
+ } else {
+ outbound_payments.send_payment(
+ PaymentHash([0; 32]), &None, PaymentId([0; 32]), Retry::Attempts(0), expired_route_params,
+ &&router, vec![], InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, |_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()
+ };
+ if let PaymentSendFailure::ParameterError(APIError::APIMisuseError { err }) = err {
+ assert!(err.contains("Invoice expired"));
+ } else { panic!("Unexpected error"); }
+ }
+
+ #[test]
+ fn find_route_error() {
+ do_find_route_error(false);
+ do_find_route_error(true);
+ }
+ 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 router = test_utils::TestRouter::new(network_graph);
+ let secp_ctx = Secp256k1::new();
+ let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
+
+ router.expect_find_route(Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError }));
+
+ let payment_params = PaymentParameters::from_node_id(
+ PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()));
+ let route_params = RouteParameters {
+ payment_params,
+ final_value_msat: 0,
+ final_cltv_expiry_delta: 0,
+ };
+ let err = if on_retry {
+ outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), None, PaymentId([0; 32]),
+ &Route { paths: vec![], payment_params: None }, Retry::Attempts(1), Some(route_params.clone()),
+ &&keys_manager, 0).unwrap();
+ outbound_payments.pay_internal(
+ PaymentId([0; 32]), None, route_params, &&router, vec![], InFlightHtlcs::new(),
+ &&keys_manager, &&keys_manager, 0, &|_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()
+ } else {
+ outbound_payments.send_payment(
+ PaymentHash([0; 32]), &None, PaymentId([0; 32]), Retry::Attempts(0), route_params,
+ &&router, vec![], InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, |_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()
+ };
+ if let PaymentSendFailure::ParameterError(APIError::APIMisuseError { err }) = err {
+ assert!(err.contains("Failed to find a route"));
+ } else { panic!("Unexpected error"); }
+ }
+}