use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
-use crate::sign::{EntropySource, NodeSigner, Recipient};
+use crate::blinded_path::{IntroductionNode, NodeIdLookUp};
+use crate::blinded_path::payment::advance_path_by_one;
use crate::events::{self, PaymentFailureReason};
-use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, PaymentId};
+use crate::ln::types::{PaymentHash, PaymentPreimage, PaymentSecret};
+use crate::ln::channel_state::ChannelDetails;
+use crate::ln::channelmanager::{EventCompletionAction, HTLCSource, PaymentId};
+use crate::ln::onion_utils;
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
use crate::offers::invoice::Bolt12Invoice;
-use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
+use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
+use crate::sign::{EntropySource, NodeSigner, Recipient};
use crate::util::errors::APIError;
use crate::util::logger::Logger;
use crate::util::time::Time;
-#[cfg(all(not(feature = "no-std"), test))]
+#[cfg(all(feature = "std", test))]
use crate::util::time::tests::SinceEpoch;
use crate::util::ser::ReadableArgs;
keysend_preimage: Option<PaymentPreimage>,
custom_tlvs: Vec<(u64, Vec<u8>)>,
pending_amt_msat: u64,
- /// Used to track the fee paid. Only present if the payment was serialized on 0.0.103+.
+ /// Used to track the fee paid. Present iff the payment was serialized on 0.0.103+.
pending_fee_msat: Option<u64>,
/// The total payment amount across all paths, used to verify that a retry is not overpaying.
total_msat: u64,
params.previously_failed_channels.push(scid);
}
}
+ pub fn insert_previously_failed_blinded_path(&mut self, blinded_tail: &BlindedTail) {
+ if let PendingOutboundPayment::Retryable { payment_params: Some(params), .. } = self {
+ params.insert_previously_failed_blinded_path(blinded_tail);
+ }
+ }
fn is_awaiting_invoice(&self) -> bool {
match self {
PendingOutboundPayment::AwaitingInvoice { .. } => true,
}
fn mark_fulfilled(&mut self) {
- let mut session_privs = HashSet::new();
+ let mut session_privs = new_hash_set();
core::mem::swap(&mut session_privs, match self {
PendingOutboundPayment::Legacy { session_privs } |
PendingOutboundPayment::Retryable { session_privs, .. } |
fn mark_abandoned(&mut self, reason: PaymentFailureReason) {
if let PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } = self {
- let mut our_session_privs = HashSet::new();
+ let mut our_session_privs = new_hash_set();
core::mem::swap(&mut our_session_privs, session_privs);
*self = PendingOutboundPayment::Abandoned {
session_privs: our_session_privs,
};
} else if let PendingOutboundPayment::InvoiceReceived { payment_hash, .. } = self {
*self = PendingOutboundPayment::Abandoned {
- session_privs: HashSet::new(),
+ session_privs: new_hash_set(),
payment_hash: *payment_hash,
reason: Some(reason)
};
if insert_res {
if let PendingOutboundPayment::Retryable {
ref mut pending_amt_msat, ref mut pending_fee_msat,
- ref mut remaining_max_total_routing_fee_msat, ..
+ ref mut remaining_max_total_routing_fee_msat, ..
} = self {
*pending_amt_msat += path.final_value_msat();
let path_fee_msat = path.fee_msat();
/// retry, and may retry multiple failed HTLCs at once if they failed around the same time and
/// were retried along a route from a single call to [`Router::find_route_with_id`].
Attempts(u32),
- #[cfg(not(feature = "no-std"))]
+ #[cfg(feature = "std")]
/// Time elapsed before abandoning retries for a payment. At least one attempt at payment is made;
/// see [`PaymentParameters::expiry_time`] to avoid any attempt at payment after a specific time.
///
Timeout(core::time::Duration),
}
-#[cfg(feature = "no-std")]
+#[cfg(not(feature = "std"))]
impl_writeable_tlv_based_enum!(Retry,
;
(0, Attempts)
);
-#[cfg(not(feature = "no-std"))]
+#[cfg(feature = "std")]
impl_writeable_tlv_based_enum!(Retry,
;
(0, Attempts),
(Retry::Attempts(max_retry_count), PaymentAttempts { count, .. }) => {
max_retry_count > count
},
- #[cfg(all(not(feature = "no-std"), not(test)))]
+ #[cfg(all(feature = "std", not(test)))]
(Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) =>
*max_duration >= crate::util::time::MonotonicTime::now().duration_since(*first_attempted_at),
- #[cfg(all(not(feature = "no-std"), test))]
+ #[cfg(all(feature = "std", test))]
(Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) =>
*max_duration >= SinceEpoch::now().duration_since(*first_attempted_at),
}
/// it means the result of the first attempt is not known yet.
pub(crate) count: u32,
/// This field is only used when retry is `Retry::Timeout` which is only build with feature std
- #[cfg(not(feature = "no-std"))]
+ #[cfg(feature = "std")]
first_attempted_at: T,
- #[cfg(feature = "no-std")]
+ #[cfg(not(feature = "std"))]
phantom: core::marker::PhantomData<T>,
}
-#[cfg(not(any(feature = "no-std", test)))]
-type ConfiguredTime = crate::util::time::MonotonicTime;
-#[cfg(feature = "no-std")]
+#[cfg(not(feature = "std"))]
type ConfiguredTime = crate::util::time::Eternity;
-#[cfg(all(not(feature = "no-std"), test))]
+#[cfg(all(feature = "std", not(test)))]
+type ConfiguredTime = crate::util::time::MonotonicTime;
+#[cfg(all(feature = "std", test))]
type ConfiguredTime = SinceEpoch;
impl<T: Time> PaymentAttemptsUsingTime<T> {
pub(crate) fn new() -> Self {
PaymentAttemptsUsingTime {
count: 0,
- #[cfg(not(feature = "no-std"))]
+ #[cfg(feature = "std")]
first_attempted_at: T::now(),
- #[cfg(feature = "no-std")]
+ #[cfg(not(feature = "std"))]
phantom: core::marker::PhantomData,
}
}
impl<T: Time> Display for PaymentAttemptsUsingTime<T> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
- #[cfg(feature = "no-std")]
+ #[cfg(not(feature = "std"))]
return write!(f, "attempts: {}", self.count);
- #[cfg(not(feature = "no-std"))]
+ #[cfg(feature = "std")]
return write!(
f,
"attempts: {}, duration: {}s",
/// [`Event::PaymentSent`]: crate::events::Event::PaymentSent
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
DuplicatePayment,
+ /// The [`RecipientOnionFields::payment_metadata`], [`RecipientOnionFields::custom_tlvs`], or
+ /// [`BlindedPath`]s provided are too large and caused us to exceed the maximum onion packet size
+ /// of 1300 bytes.
+ ///
+ /// [`BlindedPath`]: crate::blinded_path::BlindedPath
+ OnionPacketSizeExceeded,
}
/// If a payment fails to send with [`ChannelManager::send_payment_with_route`], it can be in one
},
}
-/// An error when attempting to pay a BOLT 12 invoice.
+/// An error when attempting to pay a [`Bolt12Invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
-pub(super) enum Bolt12PaymentError {
+pub enum Bolt12PaymentError {
/// The invoice was not requested.
UnexpectedInvoice,
/// Payment for an invoice with the corresponding [`PaymentId`] was already initiated.
pub(super) struct SendAlongPathArgs<'a> {
pub path: &'a Path,
pub payment_hash: &'a PaymentHash,
- pub recipient_onion: RecipientOnionFields,
+ pub recipient_onion: &'a RecipientOnionFields,
pub total_value: u64,
pub cur_height: u32,
pub payment_id: PaymentId,
impl OutboundPayments {
pub(super) fn new() -> Self {
Self {
- pending_outbound_payments: Mutex::new(HashMap::new()),
+ pending_outbound_payments: Mutex::new(new_hash_map()),
retry_lock: Mutex::new(()),
}
}
F: Fn(SendAlongPathArgs) -> Result<(), APIError>
{
let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, None, route, None, None, entropy_source, best_block_height)?;
- self.pay_route_internal(route, payment_hash, recipient_onion, None, payment_id, None,
+ self.pay_route_internal(route, payment_hash, &recipient_onion, 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 onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(),
payment_id, Some(preimage), &route, None, None, entropy_source, best_block_height)?;
- match self.pay_route_internal(route, payment_hash, recipient_onion, Some(preimage),
+ match self.pay_route_internal(route, payment_hash, &recipient_onion, Some(preimage),
payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path
) {
Ok(()) => Ok(payment_hash),
}
}
- pub(super) fn send_payment_for_bolt12_invoice<R: Deref, ES: Deref, NS: Deref, IH, SP, L: Deref>(
+ pub(super) fn send_payment_for_bolt12_invoice<
+ R: Deref, ES: Deref, NS: Deref, NL: Deref, IH, SP, L: Deref
+ >(
&self, invoice: &Bolt12Invoice, payment_id: PaymentId, router: &R,
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
- best_block_height: u32, logger: &L,
+ node_id_lookup: &NL, secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32,
+ logger: &L,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
send_payment_along_path: SP,
) -> Result<(), Bolt12PaymentError>
R::Target: Router,
ES::Target: EntropySource,
NS::Target: NodeSigner,
+ NL::Target: NodeIdLookUp,
L::Target: Logger,
IH: Fn() -> InFlightHtlcs,
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
};
- let pay_params = PaymentParameters::from_bolt12_invoice(&invoice);
+ let mut payment_params = PaymentParameters::from_bolt12_invoice(&invoice);
+
+ // Advance any blinded path where the introduction node is our node.
+ if let Ok(our_node_id) = node_signer.get_node_id(Recipient::Node) {
+ for (_, path) in payment_params.payee.blinded_route_hints_mut().iter_mut() {
+ let introduction_node_id = match path.introduction_node {
+ IntroductionNode::NodeId(pubkey) => pubkey,
+ IntroductionNode::DirectedShortChannelId(direction, scid) => {
+ match node_id_lookup.next_node_id(scid) {
+ Some(next_node_id) => *direction.select_pubkey(&our_node_id, &next_node_id),
+ None => continue,
+ }
+ },
+ };
+ if introduction_node_id == our_node_id {
+ let _ = advance_path_by_one(path, node_signer, node_id_lookup, secp_ctx);
+ }
+ }
+ }
+
let amount_msat = invoice.amount_msats();
- let mut route_params = RouteParameters::from_payment_params_and_value(pay_params, amount_msat);
+ let mut route_params = RouteParameters::from_payment_params_and_value(
+ payment_params, amount_msat
+ );
if let Some(max_fee_msat) = max_total_routing_fee_msat {
route_params.max_total_routing_fee_msat = Some(max_fee_msat);
}
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
fn send_payment_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
&self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
- keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, route_params: RouteParameters,
+ keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, mut route_params: RouteParameters,
router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES,
node_signer: &NS, best_block_height: u32, logger: &L,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
}
}
+ onion_utils::set_max_path_length(
+ &mut route_params, &recipient_onion, keysend_preimage, best_block_height
+ )
+ .map_err(|()| {
+ log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \
+ hop_data length for payment with id {} and hash {}", payment_id, payment_hash);
+ RetryableSendFailure::OnionPacketSizeExceeded
+ })?;
+
let mut route = router.find_route_with_id(
&node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),
RetryableSendFailure::DuplicatePayment
})?;
- let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage, payment_id, None,
- onion_session_privs, node_signer, best_block_height, &send_payment_along_path);
+ let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
+ keysend_preimage, payment_id, None, onion_session_privs, node_signer,
+ best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
if let Err(e) = res {
}
}
};
- let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage,
+ let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height,
&send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
RecipientOnionFields::secret_only(payment_secret), payment_id, None, &route, None, None,
entropy_source, best_block_height)?;
- match self.pay_route_internal(&route, payment_hash, RecipientOnionFields::spontaneous_empty(),
+ let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
+ match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
retry_strategy,
attempts: PaymentAttempts::new(),
payment_params,
- session_privs: HashSet::new(),
+ session_privs: new_hash_set(),
pending_amt_msat: 0,
pending_fee_msat: Some(0),
payment_hash,
}
fn pay_route_internal<NS: Deref, F>(
- &self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
+ &self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
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,
send_payment_along_path: &F
debug_assert_eq!(route.paths.len(), onion_session_privs.len());
for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.into_iter()) {
let mut path_res = send_payment_along_path(SendAlongPathArgs {
- path: &path, payment_hash: &payment_hash, recipient_onion: recipient_onion.clone(),
- total_value, cur_height, payment_id, keysend_preimage: &keysend_preimage, session_priv_bytes
+ path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
+ cur_height, payment_id, keysend_preimage: &keysend_preimage,
+ session_priv_bytes
});
match path_res {
Ok(_) => {},
NS::Target: NodeSigner,
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
- self.pay_route_internal(route, payment_hash, recipient_onion, keysend_preimage, payment_id,
- recv_value_msat, onion_session_privs, node_signer, best_block_height,
- &send_payment_along_path)
+ self.pay_route_internal(route, payment_hash, &recipient_onion,
+ 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 })
}
#[cfg(test)]
let DecodedOnionFailure {
network_update, short_channel_id, payment_failed_permanently, onion_error_code,
- onion_error_data
+ onion_error_data, failed_within_blinded_path
} = onion_error.decode_onion_failure(secp_ctx, logger, &source);
#[cfg(not(test))]
- let DecodedOnionFailure { network_update, short_channel_id, payment_failed_permanently } =
- onion_error.decode_onion_failure(secp_ctx, logger, &source);
+ let DecodedOnionFailure {
+ network_update, short_channel_id, payment_failed_permanently, failed_within_blinded_path
+ } = onion_error.decode_onion_failure(secp_ctx, logger, &source);
let payment_is_probe = payment_is_probe(payment_hash, &payment_id, probing_cookie_secret);
let mut session_priv_bytes = [0; 32];
// next-hop is needlessly blaming us!
payment.get_mut().insert_previously_failed_scid(scid);
}
+ if failed_within_blinded_path {
+ debug_assert!(short_channel_id.is_none());
+ if let Some(bt) = &path.blinded_tail {
+ payment.get_mut().insert_previously_failed_blinded_path(&bt);
+ } else { debug_assert!(false); }
+ }
if payment_is_probe || !is_retryable_now || payment_failed_permanently {
let reason = if payment_failed_permanently {
#[cfg(test)]
mod tests {
- use bitcoin::network::constants::Network;
+ use bitcoin::network::Network;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use core::time::Duration;
+ use crate::blinded_path::EmptyNodeIdLookUp;
use crate::events::{Event, PathFailure, PaymentFailureReason};
- use crate::ln::PaymentHash;
+ use crate::ln::types::PaymentHash;
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
use crate::ln::features::{ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError};
let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new());
- let router = test_utils::TestRouter::new(network_graph, &scorer);
+ let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new());
- let router = test_utils::TestRouter::new(network_graph, &scorer);
+ let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new());
- let router = test_utils::TestRouter::new(network_graph, &scorer);
+ let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new());
- let router = test_utils::TestRouter::new(network_graph, &scorer);
+ let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
+ let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let pending_events = Mutex::new(VecDeque::new());
assert!(outbound_payments.has_pending_payments());
let created_at = now() - DEFAULT_RELATIVE_EXPIRY;
- let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+ let invoice = OfferBuilder::new(recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
assert_eq!(
outbound_payments.send_payment_for_bolt12_invoice(
&invoice, payment_id, &&router, vec![], || InFlightHtlcs::new(), &&keys_manager,
- &&keys_manager, 0, &&logger, &pending_events, |_| panic!()
+ &&keys_manager, &EmptyNodeIdLookUp {}, &secp_ctx, 0, &&logger, &pending_events,
+ |_| panic!()
),
Ok(()),
);
let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new());
- let router = test_utils::TestRouter::new(network_graph, &scorer);
+ let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
+ let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let pending_events = Mutex::new(VecDeque::new());
let payment_id = PaymentId([0; 32]);
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
- let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+ let invoice = OfferBuilder::new(recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
assert_eq!(
outbound_payments.send_payment_for_bolt12_invoice(
&invoice, payment_id, &&router, vec![], || InFlightHtlcs::new(), &&keys_manager,
- &&keys_manager, 0, &&logger, &pending_events, |_| panic!()
+ &&keys_manager, &EmptyNodeIdLookUp {}, &secp_ctx, 0, &&logger, &pending_events,
+ |_| panic!()
),
Ok(()),
);
assert!(pending_events.lock().unwrap().is_empty());
}
- #[test]
- fn fails_paying_for_bolt12_invoice() {
- let logger = test_utils::TestLogger::new();
- let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
- let scorer = RwLock::new(test_utils::TestScorer::new());
- let router = test_utils::TestRouter::new(network_graph, &scorer);
- let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
-
- let pending_events = Mutex::new(VecDeque::new());
- let outbound_payments = OutboundPayments::new();
- let payment_id = PaymentId([0; 32]);
- let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
-
- let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
- .amount_msats(1000)
- .build().unwrap()
- .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
- .build().unwrap()
- .sign(payer_sign).unwrap()
- .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
- .build().unwrap()
- .sign(recipient_sign).unwrap();
-
- assert!(
- outbound_payments.add_new_awaiting_invoice(
- payment_id, expiration, Retry::Attempts(0),
- Some(invoice.amount_msats() / 100 + 50_000)
- ).is_ok()
- );
- assert!(outbound_payments.has_pending_payments());
-
- let route_params = RouteParameters::from_payment_params_and_value(
- PaymentParameters::from_bolt12_invoice(&invoice),
- invoice.amount_msats(),
- );
- router.expect_find_route(
- route_params.clone(), Ok(Route { paths: vec![], route_params: Some(route_params) })
- );
-
- assert_eq!(
- outbound_payments.send_payment_for_bolt12_invoice(
- &invoice, payment_id, &&router, vec![], || InFlightHtlcs::new(), &&keys_manager,
- &&keys_manager, 0, &&logger, &pending_events, |_| panic!()
- ),
- Ok(()),
- );
- assert!(!outbound_payments.has_pending_payments());
-
- let payment_hash = invoice.payment_hash();
- let reason = Some(PaymentFailureReason::UnexpectedError);
-
- assert!(!pending_events.lock().unwrap().is_empty());
- assert_eq!(
- pending_events.lock().unwrap().pop_front(),
- Some((Event::PaymentFailed { payment_id, payment_hash, reason }, None)),
- );
- assert!(pending_events.lock().unwrap().is_empty());
- }
-
#[test]
fn sends_payment_for_bolt12_invoice() {
let logger = test_utils::TestLogger::new();
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = RwLock::new(test_utils::TestScorer::new());
- let router = test_utils::TestRouter::new(network_graph, &scorer);
+ let router = test_utils::TestRouter::new(network_graph, &logger, &scorer);
+ let secp_ctx = Secp256k1::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
let pending_events = Mutex::new(VecDeque::new());
let payment_id = PaymentId([0; 32]);
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
- let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+ let invoice = OfferBuilder::new(recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
assert_eq!(
outbound_payments.send_payment_for_bolt12_invoice(
&invoice, payment_id, &&router, vec![], || InFlightHtlcs::new(), &&keys_manager,
- &&keys_manager, 0, &&logger, &pending_events, |_| panic!()
+ &&keys_manager, &EmptyNodeIdLookUp {}, &secp_ctx, 0, &&logger, &pending_events,
+ |_| panic!()
),
Err(Bolt12PaymentError::UnexpectedInvoice),
);
assert_eq!(
outbound_payments.send_payment_for_bolt12_invoice(
&invoice, payment_id, &&router, vec![], || InFlightHtlcs::new(), &&keys_manager,
- &&keys_manager, 0, &&logger, &pending_events, |_| Ok(())
+ &&keys_manager, &EmptyNodeIdLookUp {}, &secp_ctx, 0, &&logger, &pending_events,
+ |_| Ok(())
),
Ok(()),
);
assert_eq!(
outbound_payments.send_payment_for_bolt12_invoice(
&invoice, payment_id, &&router, vec![], || InFlightHtlcs::new(), &&keys_manager,
- &&keys_manager, 0, &&logger, &pending_events, |_| panic!()
+ &&keys_manager, &EmptyNodeIdLookUp {}, &secp_ctx, 0, &&logger, &pending_events,
+ |_| panic!()
),
Err(Bolt12PaymentError::DuplicateInvoice),
);