use crate::ln::features::{BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
-use crate::onion_message::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
+use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp};
use crate::sign::EntropySource;
use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer};
use crate::util::logger::{Level, Logger};
-use crate::util::chacha20::ChaCha20;
+use crate::crypto::chacha20::ChaCha20;
use crate::io;
use crate::prelude::*;
.filter(|details| details.counterparty.features.supports_route_blinding())
.filter(|details| amount_msats <= details.inbound_capacity_msat)
.filter(|details| amount_msats >= details.inbound_htlc_minimum_msat.unwrap_or(0))
- .filter(|details| amount_msats <= details.inbound_htlc_maximum_msat.unwrap_or(0))
+ .filter(|details| amount_msats <= details.inbound_htlc_maximum_msat.unwrap_or(u64::MAX))
.filter(|details| network_graph
.node(&NodeId::from_pubkey(&details.counterparty.node_id))
.map(|node_info| node_info.channels.len() >= MIN_PEER_CHANNELS)
None => return None,
};
let payment_relay: PaymentRelay = match details.counterparty.forwarding_info {
- Some(forwarding_info) => forwarding_info.into(),
+ Some(forwarding_info) => match forwarding_info.try_into() {
+ Ok(payment_relay) => payment_relay,
+ Err(()) => return None,
+ },
None => return None,
};
- // Avoid exposing esoteric CLTV expiry deltas
- let cltv_expiry_delta = match payment_relay.cltv_expiry_delta {
- 0..=40 => 40u32,
- 41..=80 => 80u32,
- 81..=144 => 144u32,
- 145..=216 => 216u32,
- _ => return None,
- };
-
+ let cltv_expiry_delta = payment_relay.cltv_expiry_delta as u32;
let payment_constraints = PaymentConstraints {
max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
features: BlindedHopFeatures::empty(),
},
node_id: details.counterparty.node_id,
- htlc_maximum_msat: details.inbound_htlc_maximum_msat.unwrap_or(0),
+ htlc_maximum_msat: details.inbound_htlc_maximum_msat.unwrap_or(u64::MAX),
})
})
.map(|forward_node| {
write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
(self.paths.len() as u64).write(writer)?;
let mut blinded_tails = Vec::new();
- for path in self.paths.iter() {
+ for (idx, path) in self.paths.iter().enumerate() {
(path.hops.len() as u8).write(writer)?;
- for (idx, hop) in path.hops.iter().enumerate() {
+ for hop in path.hops.iter() {
hop.write(writer)?;
- if let Some(blinded_tail) = &path.blinded_tail {
- if blinded_tails.is_empty() {
- blinded_tails = Vec::with_capacity(path.hops.len());
- for _ in 0..idx {
- blinded_tails.push(None);
- }
- }
- blinded_tails.push(Some(blinded_tail));
- } else if !blinded_tails.is_empty() { blinded_tails.push(None); }
}
+ if let Some(blinded_tail) = &path.blinded_tail {
+ if blinded_tails.is_empty() {
+ blinded_tails = Vec::with_capacity(path.hops.len());
+ for _ in 0..idx {
+ blinded_tails.push(None);
+ }
+ }
+ blinded_tails.push(Some(blinded_tail));
+ } else if !blinded_tails.is_empty() { blinded_tails.push(None); }
}
write_tlv_fields!(writer, {
// For compatibility with LDK versions prior to 0.0.117, we take the individual
(1, self.route_params.as_ref().map(|p| &p.payment_params), option),
(2, blinded_tails, optional_vec),
(3, self.route_params.as_ref().map(|p| p.final_value_msat), option),
- (5, self.route_params.as_ref().map(|p| p.max_total_routing_fee_msat), option),
+ (5, self.route_params.as_ref().and_then(|p| p.max_total_routing_fee_msat), option),
});
Ok(())
}
/// payment to fail. Future attempts for the same payment shouldn't be relayed through any of
/// these SCIDs.
pub previously_failed_channels: Vec<u64>,
+
+ /// A list of indices corresponding to blinded paths in [`Payee::Blinded::route_hints`] which this
+ /// payment was previously attempted over and which caused the payment to fail. Future attempts
+ /// for the same payment shouldn't be relayed through any of these blinded paths.
+ pub previously_failed_blinded_path_idxs: Vec<u64>,
}
impl Writeable for PaymentParameters {
(7, self.previously_failed_channels, required_vec),
(8, *blinded_hints, optional_vec),
(9, self.payee.final_cltv_expiry_delta(), option),
+ (11, self.previously_failed_blinded_path_idxs, required_vec),
});
Ok(())
}
(7, previously_failed_channels, optional_vec),
(8, blinded_route_hints, optional_vec),
(9, final_cltv_expiry_delta, (default_value, default_final_cltv_expiry_delta)),
+ (11, previously_failed_blinded_path_idxs, optional_vec),
});
let blinded_route_hints = blinded_route_hints.unwrap_or(vec![]);
let payee = if blinded_route_hints.len() != 0 {
max_channel_saturation_power_of_half: _init_tlv_based_struct_field!(max_channel_saturation_power_of_half, (default_value, unused)),
expiry_time,
previously_failed_channels: previously_failed_channels.unwrap_or(Vec::new()),
+ previously_failed_blinded_path_idxs: previously_failed_blinded_path_idxs.unwrap_or(Vec::new()),
})
}
}
max_path_count: DEFAULT_MAX_PATH_COUNT,
max_channel_saturation_power_of_half: DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF,
previously_failed_channels: Vec::new(),
+ previously_failed_blinded_path_idxs: Vec::new(),
}
}
max_path_count: DEFAULT_MAX_PATH_COUNT,
max_channel_saturation_power_of_half: DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF,
previously_failed_channels: Vec::new(),
+ previously_failed_blinded_path_idxs: Vec::new(),
}
}
pub fn with_max_channel_saturation_power_of_half(self, max_channel_saturation_power_of_half: u8) -> Self {
Self { max_channel_saturation_power_of_half, ..self }
}
+
+ pub(crate) fn insert_previously_failed_blinded_path(&mut self, failed_blinded_tail: &BlindedTail) {
+ let mut found_blinded_tail = false;
+ for (idx, (_, path)) in self.payee.blinded_route_hints().iter().enumerate() {
+ if failed_blinded_tail.hops == path.blinded_hops &&
+ failed_blinded_tail.blinding_point == path.blinding_point
+ {
+ self.previously_failed_blinded_path_idxs.push(idx as u64);
+ found_blinded_tail = true;
+ }
+ }
+ debug_assert!(found_blinded_tail);
+ }
}
/// The recipient of a payment, differing based on whether they've hidden their identity with route
#[cfg(any(ldk_bench, not(any(test, fuzzing))))]
const _GRAPH_NODE_FIXED_SIZE: usize = core::mem::size_of::<RouteGraphNode>() - 64;
+/// A [`CandidateRouteHop::FirstHop`] entry.
+#[derive(Clone, Debug)]
+pub struct FirstHopCandidate<'a> {
+ /// Channel details of the first hop
+ ///
+ /// [`ChannelDetails::get_outbound_payment_scid`] MUST be `Some` (indicating the channel
+ /// has been funded and is able to pay), and accessor methods may panic otherwise.
+ ///
+ /// [`find_route`] validates this prior to constructing a [`CandidateRouteHop`].
+ pub details: &'a ChannelDetails,
+ /// The node id of the payer, which is also the source side of this candidate route hop.
+ pub payer_node_id: &'a NodeId,
+}
+
+/// A [`CandidateRouteHop::PublicHop`] entry.
+#[derive(Clone, Debug)]
+pub struct PublicHopCandidate<'a> {
+ /// Information about the channel, including potentially its capacity and
+ /// direction-specific information.
+ pub info: DirectedChannelInfo<'a>,
+ /// The short channel ID of the channel, i.e. the identifier by which we refer to this
+ /// channel.
+ pub short_channel_id: u64,
+}
+
+/// A [`CandidateRouteHop::PrivateHop`] entry.
+#[derive(Clone, Debug)]
+pub struct PrivateHopCandidate<'a> {
+ /// Information about the private hop communicated via BOLT 11.
+ pub hint: &'a RouteHintHop,
+ /// Node id of the next hop in BOLT 11 route hint.
+ pub target_node_id: &'a NodeId
+}
+
+/// A [`CandidateRouteHop::Blinded`] entry.
+#[derive(Clone, Debug)]
+pub struct BlindedPathCandidate<'a> {
+ /// Information about the blinded path including the fee, HTLC amount limits, and
+ /// cryptographic material required to build an HTLC through the given path.
+ pub hint: &'a (BlindedPayInfo, BlindedPath),
+ /// Index of the hint in the original list of blinded hints.
+ ///
+ /// This is used to cheaply uniquely identify this blinded path, even though we don't have
+ /// a short channel ID for this hop.
+ hint_idx: usize,
+}
+
+/// A [`CandidateRouteHop::OneHopBlinded`] entry.
+#[derive(Clone, Debug)]
+pub struct OneHopBlindedPathCandidate<'a> {
+ /// Information about the blinded path including the fee, HTLC amount limits, and
+ /// cryptographic material required to build an HTLC terminating with the given path.
+ ///
+ /// Note that the [`BlindedPayInfo`] is ignored here.
+ pub hint: &'a (BlindedPayInfo, BlindedPath),
+ /// Index of the hint in the original list of blinded hints.
+ ///
+ /// This is used to cheaply uniquely identify this blinded path, even though we don't have
+ /// a short channel ID for this hop.
+ hint_idx: usize,
+}
+
/// A wrapper around the various hop representations.
///
/// Can be used to examine the properties of a hop,
#[derive(Clone, Debug)]
pub enum CandidateRouteHop<'a> {
/// A hop from the payer, where the outbound liquidity is known.
- FirstHop {
- /// Channel details of the first hop
- ///
- /// [`ChannelDetails::get_outbound_payment_scid`] MUST be `Some` (indicating the channel
- /// has been funded and is able to pay), and accessor methods may panic otherwise.
- ///
- /// [`find_route`] validates this prior to constructing a [`CandidateRouteHop`].
- details: &'a ChannelDetails,
- /// The node id of the payer, which is also the source side of this candidate route hop.
- payer_node_id: &'a NodeId,
- },
+ FirstHop(FirstHopCandidate<'a>),
/// A hop found in the [`ReadOnlyNetworkGraph`].
- PublicHop {
- /// Information about the channel, including potentially its capacity and
- /// direction-specific information.
- info: DirectedChannelInfo<'a>,
- /// The short channel ID of the channel, i.e. the identifier by which we refer to this
- /// channel.
- short_channel_id: u64,
- },
+ PublicHop(PublicHopCandidate<'a>),
/// A private hop communicated by the payee, generally via a BOLT 11 invoice.
///
/// Because BOLT 11 route hints can take multiple hops to get to the destination, this may not
/// terminate at the payee.
- PrivateHop {
- /// Information about the private hop communicated via BOLT 11.
- hint: &'a RouteHintHop,
- /// Node id of the next hop in BOLT 11 route hint.
- target_node_id: &'a NodeId
- },
+ PrivateHop(PrivateHopCandidate<'a>),
/// A blinded path which starts with an introduction point and ultimately terminates with the
/// payee.
///
///
/// Because blinded paths are "all or nothing", and we cannot use just one part of a blinded
/// path, the full path is treated as a single [`CandidateRouteHop`].
- Blinded {
- /// Information about the blinded path including the fee, HTLC amount limits, and
- /// cryptographic material required to build an HTLC through the given path.
- hint: &'a (BlindedPayInfo, BlindedPath),
- /// Index of the hint in the original list of blinded hints.
- ///
- /// This is used to cheaply uniquely identify this blinded path, even though we don't have
- /// a short channel ID for this hop.
- hint_idx: usize,
- },
+ Blinded(BlindedPathCandidate<'a>),
/// Similar to [`Self::Blinded`], but the path here only has one hop.
///
/// While we treat this similarly to [`CandidateRouteHop::Blinded`] in many respects (e.g.
///
/// This primarily exists to track that we need to included a blinded path at the end of our
/// [`Route`], even though it doesn't actually add an additional hop in the payment.
- OneHopBlinded {
- /// Information about the blinded path including the fee, HTLC amount limits, and
- /// cryptographic material required to build an HTLC terminating with the given path.
- ///
- /// Note that the [`BlindedPayInfo`] is ignored here.
- hint: &'a (BlindedPayInfo, BlindedPath),
- /// Index of the hint in the original list of blinded hints.
- ///
- /// This is used to cheaply uniquely identify this blinded path, even though we don't have
- /// a short channel ID for this hop.
- hint_idx: usize,
- },
+ OneHopBlinded(OneHopBlindedPathCandidate<'a>),
}
impl<'a> CandidateRouteHop<'a> {
#[inline]
fn short_channel_id(&self) -> Option<u64> {
match self {
- CandidateRouteHop::FirstHop { details, .. } => details.get_outbound_payment_scid(),
- CandidateRouteHop::PublicHop { short_channel_id, .. } => Some(*short_channel_id),
- CandidateRouteHop::PrivateHop { hint, .. } => Some(hint.short_channel_id),
- CandidateRouteHop::Blinded { .. } => None,
- CandidateRouteHop::OneHopBlinded { .. } => None,
+ CandidateRouteHop::FirstHop(hop) => hop.details.get_outbound_payment_scid(),
+ CandidateRouteHop::PublicHop(hop) => Some(hop.short_channel_id),
+ CandidateRouteHop::PrivateHop(hop) => Some(hop.hint.short_channel_id),
+ CandidateRouteHop::Blinded(_) => None,
+ CandidateRouteHop::OneHopBlinded(_) => None,
}
}
#[inline]
pub fn globally_unique_short_channel_id(&self) -> Option<u64> {
match self {
- CandidateRouteHop::FirstHop { details, .. } => if details.is_public { details.short_channel_id } else { None },
- CandidateRouteHop::PublicHop { short_channel_id, .. } => Some(*short_channel_id),
- CandidateRouteHop::PrivateHop { .. } => None,
- CandidateRouteHop::Blinded { .. } => None,
- CandidateRouteHop::OneHopBlinded { .. } => None,
+ CandidateRouteHop::FirstHop(hop) => if hop.details.is_public { hop.details.short_channel_id } else { None },
+ CandidateRouteHop::PublicHop(hop) => Some(hop.short_channel_id),
+ CandidateRouteHop::PrivateHop(_) => None,
+ CandidateRouteHop::Blinded(_) => None,
+ CandidateRouteHop::OneHopBlinded(_) => None,
}
}
// NOTE: This may alloc memory so avoid calling it in a hot code path.
fn features(&self) -> ChannelFeatures {
match self {
- CandidateRouteHop::FirstHop { details, .. } => details.counterparty.features.to_context(),
- CandidateRouteHop::PublicHop { info, .. } => info.channel().features.clone(),
- CandidateRouteHop::PrivateHop { .. } => ChannelFeatures::empty(),
- CandidateRouteHop::Blinded { .. } => ChannelFeatures::empty(),
- CandidateRouteHop::OneHopBlinded { .. } => ChannelFeatures::empty(),
+ CandidateRouteHop::FirstHop(hop) => hop.details.counterparty.features.to_context(),
+ CandidateRouteHop::PublicHop(hop) => hop.info.channel().features.clone(),
+ CandidateRouteHop::PrivateHop(_) => ChannelFeatures::empty(),
+ CandidateRouteHop::Blinded(_) => ChannelFeatures::empty(),
+ CandidateRouteHop::OneHopBlinded(_) => ChannelFeatures::empty(),
}
}
#[inline]
pub fn cltv_expiry_delta(&self) -> u32 {
match self {
- CandidateRouteHop::FirstHop { .. } => 0,
- CandidateRouteHop::PublicHop { info, .. } => info.direction().cltv_expiry_delta as u32,
- CandidateRouteHop::PrivateHop { hint, .. } => hint.cltv_expiry_delta as u32,
- CandidateRouteHop::Blinded { hint, .. } => hint.0.cltv_expiry_delta as u32,
- CandidateRouteHop::OneHopBlinded { .. } => 0,
+ CandidateRouteHop::FirstHop(_) => 0,
+ CandidateRouteHop::PublicHop(hop) => hop.info.direction().cltv_expiry_delta as u32,
+ CandidateRouteHop::PrivateHop(hop) => hop.hint.cltv_expiry_delta as u32,
+ CandidateRouteHop::Blinded(hop) => hop.hint.0.cltv_expiry_delta as u32,
+ CandidateRouteHop::OneHopBlinded(_) => 0,
}
}
#[inline]
pub fn htlc_minimum_msat(&self) -> u64 {
match self {
- CandidateRouteHop::FirstHop { details, .. } => details.next_outbound_htlc_minimum_msat,
- CandidateRouteHop::PublicHop { info, .. } => info.direction().htlc_minimum_msat,
- CandidateRouteHop::PrivateHop { hint, .. } => hint.htlc_minimum_msat.unwrap_or(0),
- CandidateRouteHop::Blinded { hint, .. } => hint.0.htlc_minimum_msat,
+ CandidateRouteHop::FirstHop(hop) => hop.details.next_outbound_htlc_minimum_msat,
+ CandidateRouteHop::PublicHop(hop) => hop.info.direction().htlc_minimum_msat,
+ CandidateRouteHop::PrivateHop(hop) => hop.hint.htlc_minimum_msat.unwrap_or(0),
+ CandidateRouteHop::Blinded(hop) => hop.hint.0.htlc_minimum_msat,
CandidateRouteHop::OneHopBlinded { .. } => 0,
}
}
#[inline]
pub fn fees(&self) -> RoutingFees {
match self {
- CandidateRouteHop::FirstHop { .. } => RoutingFees {
+ CandidateRouteHop::FirstHop(_) => RoutingFees {
base_msat: 0, proportional_millionths: 0,
},
- CandidateRouteHop::PublicHop { info, .. } => info.direction().fees,
- CandidateRouteHop::PrivateHop { hint, .. } => hint.fees,
- CandidateRouteHop::Blinded { hint, .. } => {
+ CandidateRouteHop::PublicHop(hop) => hop.info.direction().fees,
+ CandidateRouteHop::PrivateHop(hop) => hop.hint.fees,
+ CandidateRouteHop::Blinded(hop) => {
RoutingFees {
- base_msat: hint.0.fee_base_msat,
- proportional_millionths: hint.0.fee_proportional_millionths
+ base_msat: hop.hint.0.fee_base_msat,
+ proportional_millionths: hop.hint.0.fee_proportional_millionths
}
},
- CandidateRouteHop::OneHopBlinded { .. } =>
+ CandidateRouteHop::OneHopBlinded(_) =>
RoutingFees { base_msat: 0, proportional_millionths: 0 },
}
}
/// cached!
fn effective_capacity(&self) -> EffectiveCapacity {
match self {
- CandidateRouteHop::FirstHop { details, .. } => EffectiveCapacity::ExactLiquidity {
- liquidity_msat: details.next_outbound_htlc_limit_msat,
+ CandidateRouteHop::FirstHop(hop) => EffectiveCapacity::ExactLiquidity {
+ liquidity_msat: hop.details.next_outbound_htlc_limit_msat,
},
- CandidateRouteHop::PublicHop { info, .. } => info.effective_capacity(),
- CandidateRouteHop::PrivateHop { hint: RouteHintHop { htlc_maximum_msat: Some(max), .. }, .. } =>
+ CandidateRouteHop::PublicHop(hop) => hop.info.effective_capacity(),
+ CandidateRouteHop::PrivateHop(PrivateHopCandidate { hint: RouteHintHop { htlc_maximum_msat: Some(max), .. }, .. }) =>
EffectiveCapacity::HintMaxHTLC { amount_msat: *max },
- CandidateRouteHop::PrivateHop { hint: RouteHintHop { htlc_maximum_msat: None, .. }, .. } =>
+ CandidateRouteHop::PrivateHop(PrivateHopCandidate { hint: RouteHintHop { htlc_maximum_msat: None, .. }, .. }) =>
EffectiveCapacity::Infinite,
- CandidateRouteHop::Blinded { hint, .. } =>
- EffectiveCapacity::HintMaxHTLC { amount_msat: hint.0.htlc_maximum_msat },
- CandidateRouteHop::OneHopBlinded { .. } => EffectiveCapacity::Infinite,
+ CandidateRouteHop::Blinded(hop) =>
+ EffectiveCapacity::HintMaxHTLC { amount_msat: hop.hint.0.htlc_maximum_msat },
+ CandidateRouteHop::OneHopBlinded(_) => EffectiveCapacity::Infinite,
}
}
#[inline]
fn id(&self) -> CandidateHopId {
match self {
- CandidateRouteHop::Blinded { hint_idx, .. } => CandidateHopId::Blinded(*hint_idx),
- CandidateRouteHop::OneHopBlinded { hint_idx, .. } => CandidateHopId::Blinded(*hint_idx),
+ CandidateRouteHop::Blinded(hop) => CandidateHopId::Blinded(hop.hint_idx),
+ CandidateRouteHop::OneHopBlinded(hop) => CandidateHopId::Blinded(hop.hint_idx),
_ => CandidateHopId::Clear((self.short_channel_id().unwrap(), self.source() < self.target().unwrap())),
}
}
fn blinded_path(&self) -> Option<&'a BlindedPath> {
match self {
- CandidateRouteHop::Blinded { hint, .. } | CandidateRouteHop::OneHopBlinded { hint, .. } => {
+ CandidateRouteHop::Blinded(BlindedPathCandidate { hint, .. }) | CandidateRouteHop::OneHopBlinded(OneHopBlindedPathCandidate { hint, .. }) => {
Some(&hint.1)
},
_ => None,
}
}
+ fn blinded_hint_idx(&self) -> Option<usize> {
+ match self {
+ Self::Blinded(BlindedPathCandidate { hint_idx, .. }) |
+ Self::OneHopBlinded(OneHopBlindedPathCandidate { hint_idx, .. }) => {
+ Some(*hint_idx)
+ },
+ _ => None,
+ }
+ }
/// Returns the source node id of current hop.
///
/// Source node id refers to the node forwarding the HTLC through this hop.
#[inline]
pub fn source(&self) -> NodeId {
match self {
- CandidateRouteHop::FirstHop { payer_node_id, .. } => **payer_node_id,
- CandidateRouteHop::PublicHop { info, .. } => *info.source(),
- CandidateRouteHop::PrivateHop { hint, .. } => hint.src_node_id.into(),
- CandidateRouteHop::Blinded { hint, .. } => hint.1.introduction_node_id.into(),
- CandidateRouteHop::OneHopBlinded { hint, .. } => hint.1.introduction_node_id.into(),
+ CandidateRouteHop::FirstHop(hop) => *hop.payer_node_id,
+ CandidateRouteHop::PublicHop(hop) => *hop.info.source(),
+ CandidateRouteHop::PrivateHop(hop) => hop.hint.src_node_id.into(),
+ CandidateRouteHop::Blinded(hop) => hop.hint.1.introduction_node_id.into(),
+ CandidateRouteHop::OneHopBlinded(hop) => hop.hint.1.introduction_node_id.into(),
}
}
/// Returns the target node id of this hop, if known.
#[inline]
pub fn target(&self) -> Option<NodeId> {
match self {
- CandidateRouteHop::FirstHop { details, .. } => Some(details.counterparty.node_id.into()),
- CandidateRouteHop::PublicHop { info, .. } => Some(*info.target()),
- CandidateRouteHop::PrivateHop { target_node_id, .. } => Some(**target_node_id),
- CandidateRouteHop::Blinded { .. } => None,
- CandidateRouteHop::OneHopBlinded { .. } => None,
+ CandidateRouteHop::FirstHop(hop) => Some(hop.details.counterparty.node_id.into()),
+ CandidateRouteHop::PublicHop(hop) => Some(*hop.info.target()),
+ CandidateRouteHop::PrivateHop(hop) => Some(*hop.target_node_id),
+ CandidateRouteHop::Blinded(_) => None,
+ CandidateRouteHop::OneHopBlinded(_) => None,
}
}
}
impl<'a> fmt::Display for LoggedCandidateHop<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
- CandidateRouteHop::Blinded { hint, .. } | CandidateRouteHop::OneHopBlinded { hint, .. } => {
+ CandidateRouteHop::Blinded(BlindedPathCandidate { hint, .. }) | CandidateRouteHop::OneHopBlinded(OneHopBlindedPathCandidate { hint, .. }) => {
"blinded route hint with introduction node id ".fmt(f)?;
hint.1.introduction_node_id.fmt(f)?;
" and blinding point ".fmt(f)?;
hint.1.blinding_point.fmt(f)
},
- CandidateRouteHop::FirstHop { .. } => {
+ CandidateRouteHop::FirstHop(_) => {
"first hop with SCID ".fmt(f)?;
self.0.short_channel_id().unwrap().fmt(f)
},
- CandidateRouteHop::PrivateHop { .. } => {
+ CandidateRouteHop::PrivateHop(_) => {
"route hint with SCID ".fmt(f)?;
self.0.short_channel_id().unwrap().fmt(f)
},
(amount_to_transfer_over_msat < $next_hops_path_htlc_minimum_msat &&
recommended_value_msat >= $next_hops_path_htlc_minimum_msat));
- let payment_failed_on_this_channel = scid_opt.map_or(false,
- |scid| payment_params.previously_failed_channels.contains(&scid));
+ let payment_failed_on_this_channel = match scid_opt {
+ Some(scid) => payment_params.previously_failed_channels.contains(&scid),
+ None => match $candidate.blinded_hint_idx() {
+ Some(idx) => {
+ payment_params.previously_failed_blinded_path_idxs.contains(&(idx as u64))
+ },
+ None => false,
+ },
+ };
let (should_log_candidate, first_hop_details) = match $candidate {
- CandidateRouteHop::FirstHop { details, .. } => (true, Some(details)),
- CandidateRouteHop::PrivateHop { .. } => (true, None),
- CandidateRouteHop::Blinded { .. } => (true, None),
- CandidateRouteHop::OneHopBlinded { .. } => (true, None),
+ CandidateRouteHop::FirstHop(hop) => (true, Some(hop.details)),
+ CandidateRouteHop::PrivateHop(_) => (true, None),
+ CandidateRouteHop::Blinded(_) => (true, None),
+ CandidateRouteHop::OneHopBlinded(_) => (true, None),
_ => (false, None),
};
if !skip_node {
if let Some(first_channels) = first_hop_targets.get(&$node_id) {
for details in first_channels {
- let candidate = CandidateRouteHop::FirstHop {
+ let candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
details, payer_node_id: &our_node_id,
- };
+ });
add_entry!(&candidate, fee_to_target_msat,
$next_hops_value_contribution,
next_hops_path_htlc_minimum_msat, next_hops_path_penalty_msat,
if let Some((directed_channel, source)) = chan.as_directed_to(&$node_id) {
if first_hops.is_none() || *source != our_node_id {
if directed_channel.direction().enabled {
- let candidate = CandidateRouteHop::PublicHop {
+ let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
info: directed_channel,
short_channel_id: *chan_id,
- };
+ });
add_entry!(&candidate,
fee_to_target_msat,
$next_hops_value_contribution,
// place where it could be added.
payee_node_id_opt.map(|payee| first_hop_targets.get(&payee).map(|first_channels| {
for details in first_channels {
- let candidate = CandidateRouteHop::FirstHop {
+ let candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
details, payer_node_id: &our_node_id,
- };
+ });
let added = add_entry!(&candidate, 0, path_value_msat,
0, 0u64, 0, 0).is_some();
log_trace!(logger, "{} direct route to payee via {}",
network_nodes.get(&intro_node_id).is_some();
if !have_intro_node_in_graph || our_node_id == intro_node_id { continue }
let candidate = if hint.1.blinded_hops.len() == 1 {
- CandidateRouteHop::OneHopBlinded { hint, hint_idx }
- } else { CandidateRouteHop::Blinded { hint, hint_idx } };
+ CandidateRouteHop::OneHopBlinded(OneHopBlindedPathCandidate { hint, hint_idx })
+ } else { CandidateRouteHop::Blinded(BlindedPathCandidate { hint, hint_idx }) };
let mut path_contribution_msat = path_value_msat;
if let Some(hop_used_msat) = add_entry!(&candidate,
0, path_contribution_msat, 0, 0_u64, 0, 0)
sort_first_hop_channels(first_channels, &used_liquidities, recommended_value_msat,
our_node_pubkey);
for details in first_channels {
- let first_hop_candidate = CandidateRouteHop::FirstHop {
+ let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
details, payer_node_id: &our_node_id,
- };
+ });
let blinded_path_fee = match compute_fees(path_contribution_msat, candidate.fees()) {
Some(fee) => fee,
None => continue
let candidate = network_channels
.get(&hop.short_channel_id)
.and_then(|channel| channel.as_directed_to(&target))
- .map(|(info, _)| CandidateRouteHop::PublicHop {
+ .map(|(info, _)| CandidateRouteHop::PublicHop(PublicHopCandidate {
info,
short_channel_id: hop.short_channel_id,
- })
- .unwrap_or_else(|| CandidateRouteHop::PrivateHop { hint: hop, target_node_id: target });
+ }))
+ .unwrap_or_else(|| CandidateRouteHop::PrivateHop(PrivateHopCandidate { hint: hop, target_node_id: target }));
if let Some(hop_used_msat) = add_entry!(&candidate,
aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
sort_first_hop_channels(first_channels, &used_liquidities,
recommended_value_msat, our_node_pubkey);
for details in first_channels {
- let first_hop_candidate = CandidateRouteHop::FirstHop {
+ let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
details, payer_node_id: &our_node_id,
- };
+ });
add_entry!(&first_hop_candidate,
aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat,
sort_first_hop_channels(first_channels, &used_liquidities,
recommended_value_msat, our_node_pubkey);
for details in first_channels {
- let first_hop_candidate = CandidateRouteHop::FirstHop {
+ let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
details, payer_node_id: &our_node_id,
- };
+ });
add_entry!(&first_hop_candidate,
aggregate_next_hops_fee_msat,
aggregate_path_contribution_msat,
let target = ordered_hops.last().unwrap().0.candidate.target().unwrap_or(maybe_dummy_payee_node_id);
if let Some(first_channels) = first_hop_targets.get(&target) {
for details in first_channels {
- if let CandidateRouteHop::FirstHop { details: last_hop_details, .. }
+ if let CandidateRouteHop::FirstHop(FirstHopCandidate { details: last_hop_details, .. })
= ordered_hops.last().unwrap().0.candidate
{
if details.get_outbound_payment_scid() == last_hop_details.get_outbound_payment_scid() {
}
}
- // Means we succesfully traversed from the payer to the payee, now
+ // Means we successfully traversed from the payer to the payee, now
// save this path for the payment route. Also, update the liquidity
// remaining on the used hops, so that we take them into account
// while looking for more paths.
.filter(|(h, _)| h.candidate.short_channel_id().is_some())
{
let target = hop.candidate.target().expect("target is defined when short_channel_id is defined");
- let maybe_announced_channel = if let CandidateRouteHop::PublicHop { .. } = hop.candidate {
+ let maybe_announced_channel = if let CandidateRouteHop::PublicHop(_) = hop.candidate {
// If we sourced the hop from the graph we're sure the target node is announced.
true
- } else if let CandidateRouteHop::FirstHop { details, .. } = hop.candidate {
+ } else if let CandidateRouteHop::FirstHop(first_hop) = &hop.candidate {
// If this is a first hop we also know if it's announced.
- details.is_public
+ first_hop.details.is_public
} else {
// If we sourced it any other way, we double-check the network graph to see if
// there are announced channels between the endpoints. If so, the hop might be
use crate::routing::utxo::UtxoResult;
use crate::routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees,
- DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE, RouteParameters, CandidateRouteHop};
+ DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE, RouteParameters, CandidateRouteHop, PublicHopCandidate};
use crate::routing::scoring::{ChannelUsage, FixedPenaltyScorer, ScoreLookUp, ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
use crate::routing::test_utils::{add_channel, add_or_update_node, build_graph, build_line_graph, id_to_feature_flags, get_nodes, update_channel};
use crate::chain::transaction::OutPoint;
use crate::offers::invoice::BlindedPayInfo;
use crate::util::config::UserConfig;
use crate::util::test_utils as ln_test_utils;
- use crate::util::chacha20::ChaCha20;
+ use crate::crypto::chacha20::ChaCha20;
use crate::util::ser::{Readable, Writeable};
#[cfg(c_bindings)]
use crate::util::ser::Writer;
let channels = network_graph.channels();
let channel = channels.get(&5).unwrap();
let info = channel.as_directed_from(&NodeId::from_pubkey(&nodes[3])).unwrap();
- let candidate: CandidateRouteHop = CandidateRouteHop::PublicHop {
+ let candidate: CandidateRouteHop = CandidateRouteHop::PublicHop(PublicHopCandidate {
info: info.0,
short_channel_id: 5,
- };
+ });
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &scorer_params), 456);
// Then check we can get a normal route
fn do_min_htlc_overpay_violates_max_htlc(blinded_payee: bool) {
// Test that if overpaying to meet a later hop's min_htlc and causes us to violate an earlier
// hop's max_htlc, we don't consider that candidate hop valid. Previously we would add this hop
- // to `targets` and build an invalid path with it, and subsquently hit a debug panic asserting
+ // to `targets` and build an invalid path with it, and subsequently hit a debug panic asserting
// that the used liquidity for a hop was less than its available liquidity limit.
let secp_ctx = Secp256k1::new();
let logger = Arc::new(ln_test_utils::TestLogger::new());
}
break;
}
- // If we couldn't find a path with a higer amount, reduce and try again.
+ // If we couldn't find a path with a higher amount, reduce and try again.
score_amt /= 100;
}