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