@@ -1055,8 +1055,8 @@ pub enum CandidateRouteHop<'a> {
1055
1055
hint : & ' a ( BlindedPayInfo , BlindedPath ) ,
1056
1056
/// Index of the hint in the original list of blinded hints.
1057
1057
///
1058
- /// This is used to build a [`CandidateHopId`] that uniquely identifies this blinded path,
1059
- /// even though we don't have a short channel ID for this hop.
1058
+ /// This is used to cheaply uniquely identify this blinded path, even though we don't have
1059
+ /// a short channel ID for this hop.
1060
1060
hint_idx : usize ,
1061
1061
} ,
1062
1062
/// Similar to [`Self::Blinded`], but the path here only has one hop.
@@ -1078,18 +1078,29 @@ pub enum CandidateRouteHop<'a> {
1078
1078
hint : & ' a ( BlindedPayInfo , BlindedPath ) ,
1079
1079
/// Index of the hint in the original list of blinded hints.
1080
1080
///
1081
- /// This is used to build a [`CandidateHopId`] that uniquely identifies this blinded path,
1082
- /// even though we don't have a short channel ID for this hop.
1081
+ /// This is used to cheaply uniquely identify this blinded path, even though we don't have
1082
+ /// a short channel ID for this hop.
1083
1083
hint_idx : usize ,
1084
1084
} ,
1085
1085
}
1086
1086
1087
1087
impl < ' a > CandidateRouteHop < ' a > {
1088
- /// Returns short_channel_id if known.
1089
- /// For `FirstHop` we assume [`ChannelDetails::get_outbound_payment_scid`] is always set, this assumption is checked in
1090
- /// [`find_route`] method.
1091
- /// For `Blinded` and `OneHopBlinded` we return `None` because next hop is not known.
1092
- pub fn short_channel_id ( & self ) -> Option < u64 > {
1088
+ /// Returns the short channel ID for this hop, if one is known.
1089
+ ///
1090
+ /// This SCID could be an alias or a globally unique SCID, and thus is only expected to
1091
+ /// uniquely identify this channel in conjunction with the [`CandidateRouteHop::source`].
1092
+ ///
1093
+ /// Returns `Some` as long as the candidate is a [`CandidateRouteHop::PublicHop`], a
1094
+ /// [`CandidateRouteHop::PrivateHop`] from a BOLT 11 route hint, or a
1095
+ /// [`CandidateRouteHop::FirstHop`] with a known [`ChannelDetails::get_outbound_payment_scid`]
1096
+ /// (which is always true for channels which are funded and ready for use).
1097
+ ///
1098
+ /// In other words, this should always return `Some` as long as the candidate hop is not a
1099
+ /// [`CandidateRouteHop::Blinded`] or a [`CandidateRouteHop::OneHopBlinded`].
1100
+ ///
1101
+ /// Note that this is deliberately not public as it is somewhat of a footgun because it doesn't
1102
+ /// define a global namespace.
1103
+ fn short_channel_id ( & self ) -> Option < u64 > {
1093
1104
match self {
1094
1105
CandidateRouteHop :: FirstHop { details, .. } => details. get_outbound_payment_scid ( ) ,
1095
1106
CandidateRouteHop :: PublicHop { short_channel_id, .. } => Some ( * short_channel_id) ,
@@ -1099,6 +1110,21 @@ impl<'a> CandidateRouteHop<'a> {
1099
1110
}
1100
1111
}
1101
1112
1113
+ /// Returns the globally unique short channel ID for this hop, if one is known.
1114
+ ///
1115
+ /// This only returns `Some` if the channel is public (either our own, or one we've learned
1116
+ /// from the public network graph), and thus the short channel ID we have for this channel is
1117
+ /// globally unique and identifies this channel in a global namespace.
1118
+ pub fn globally_unique_short_channel_id ( & self ) -> Option < u64 > {
1119
+ match self {
1120
+ CandidateRouteHop :: FirstHop { details, .. } => if details. is_public { details. short_channel_id } else { None } ,
1121
+ CandidateRouteHop :: PublicHop { short_channel_id, .. } => Some ( * short_channel_id) ,
1122
+ CandidateRouteHop :: PrivateHop { .. } => None ,
1123
+ CandidateRouteHop :: Blinded { .. } => None ,
1124
+ CandidateRouteHop :: OneHopBlinded { .. } => None ,
1125
+ }
1126
+ }
1127
+
1102
1128
// NOTE: This may alloc memory so avoid calling it in a hot code path.
1103
1129
fn features ( & self ) -> ChannelFeatures {
1104
1130
match self {
@@ -1167,10 +1193,10 @@ impl<'a> CandidateRouteHop<'a> {
1167
1193
}
1168
1194
}
1169
1195
1170
- /// Returns the id of this hop.
1171
- /// For `Blinded` and `OneHopBlinded` we return `CandidateHopId::Blinded` with `hint_idx` because we don't know the channel id.
1172
- /// For any other option we return `CandidateHopId::Clear` because we know the channel id and the direction .
1173
- pub fn id ( & self ) -> CandidateHopId {
1196
+ /// Returns an ID describing the given hop.
1197
+ ///
1198
+ /// See the docs on [ `CandidateHopId`] for when this is, or is not, unique .
1199
+ fn id ( & self ) -> CandidateHopId {
1174
1200
match self {
1175
1201
CandidateRouteHop :: Blinded { hint_idx, .. } => CandidateHopId :: Blinded ( * hint_idx) ,
1176
1202
CandidateRouteHop :: OneHopBlinded { hint_idx, .. } => CandidateHopId :: Blinded ( * hint_idx) ,
@@ -1215,12 +1241,17 @@ impl<'a> CandidateRouteHop<'a> {
1215
1241
}
1216
1242
}
1217
1243
1218
- /// A wrapper around the various hop id representations.
1244
+ /// A unique(ish) identifier for a specific [`CandidateRouteHop`].
1245
+ ///
1246
+ /// For blinded paths, this ID is unique only within a given [`find_route`] call.
1247
+ ///
1248
+ /// For other hops, because SCIDs between private channels and public channels can conflict, this
1249
+ /// isn't guaranteed to be unique at all.
1219
1250
///
1220
- /// `CandidateHopId::Clear` is used to identify a hop with a known short channel id and direction.
1221
- /// `CandidateHopId::Blinded` is used to identify a blinded hop `hint_idx` .
1251
+ /// For our uses, this is generally fine, but it is not public as it is otherwise a rather
1252
+ /// difficult-to-use API .
1222
1253
#[ derive( Clone , Copy , Eq , Hash , Ord , PartialOrd , PartialEq ) ]
1223
- pub enum CandidateHopId {
1254
+ enum CandidateHopId {
1224
1255
/// Contains (scid, src_node_id < target_node_id)
1225
1256
Clear ( ( u64 , bool ) ) ,
1226
1257
/// Index of the blinded route hint in [`Payee::Blinded::route_hints`].
@@ -2446,8 +2477,10 @@ where L::Target: Logger {
2446
2477
let target = ordered_hops. last ( ) . unwrap ( ) . 0 . candidate . target ( ) . unwrap_or ( maybe_dummy_payee_node_id) ;
2447
2478
if let Some ( first_channels) = first_hop_targets. get ( & target) {
2448
2479
for details in first_channels {
2449
- if let Some ( scid) = ordered_hops. last ( ) . unwrap ( ) . 0 . candidate . short_channel_id ( ) {
2450
- if details. get_outbound_payment_scid ( ) . unwrap ( ) == scid {
2480
+ if let CandidateRouteHop :: FirstHop { details : last_hop_details, .. }
2481
+ = ordered_hops. last ( ) . unwrap ( ) . 0 . candidate
2482
+ {
2483
+ if details. get_outbound_payment_scid ( ) == last_hop_details. get_outbound_payment_scid ( ) {
2451
2484
ordered_hops. last_mut ( ) . unwrap ( ) . 1 = details. counterparty . features . to_context ( ) ;
2452
2485
features_set = true ;
2453
2486
break ;
0 commit comments