@@ -29,7 +29,7 @@ use crate::io;
29
29
use crate :: prelude:: * ;
30
30
use crate :: sync:: Mutex ;
31
31
use alloc:: collections:: BinaryHeap ;
32
- use core:: cmp;
32
+ use core:: { cmp, fmt } ;
33
33
use core:: ops:: Deref ;
34
34
35
35
/// A [`Router`] implemented using [`find_route`].
@@ -493,9 +493,6 @@ const MAX_PATH_LENGTH_ESTIMATE: u8 = 19;
493
493
/// Information used to route a payment.
494
494
#[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
495
495
pub struct PaymentParameters {
496
- /// The node id of the payee.
497
- pub payee_pubkey : PublicKey ,
498
-
499
496
/// Features supported by the payee.
500
497
///
501
498
/// May be set from the payee's invoice or via [`for_keysend`]. May be `None` if the invoice
@@ -551,7 +548,7 @@ impl Writeable for PaymentParameters {
551
548
Payee :: Blinded { route_hints } => blinded_hints = route_hints,
552
549
}
553
550
write_tlv_fields ! ( writer, {
554
- ( 0 , self . payee_pubkey , required ) ,
551
+ ( 0 , self . payee . node_id ( ) , option ) ,
555
552
( 1 , self . max_total_cltv_expiry_delta, required) ,
556
553
( 2 , self . features, option) ,
557
554
( 3 , self . max_path_count, required) ,
@@ -569,7 +566,7 @@ impl Writeable for PaymentParameters {
569
566
impl ReadableArgs < u32 > for PaymentParameters {
570
567
fn read < R : io:: Read > ( reader : & mut R , default_final_cltv_expiry_delta : u32 ) -> Result < Self , DecodeError > {
571
568
_init_and_read_tlv_fields ! ( reader, {
572
- ( 0 , payee_pubkey, required ) ,
569
+ ( 0 , payee_pubkey, option ) ,
573
570
( 1 , max_total_cltv_expiry_delta, ( default_value, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA ) ) ,
574
571
( 2 , features, option) ,
575
572
( 3 , max_path_count, ( default_value, DEFAULT_MAX_PATH_COUNT ) ) ,
@@ -583,13 +580,15 @@ impl ReadableArgs<u32> for PaymentParameters {
583
580
let clear_route_hints = route_hints. unwrap_or ( vec ! [ ] ) ;
584
581
let blinded_route_hints = blinded_route_hints. unwrap_or ( vec ! [ ] ) ;
585
582
let payee = if blinded_route_hints. len ( ) != 0 {
586
- if clear_route_hints. len ( ) != 0 { return Err ( DecodeError :: InvalidValue ) }
583
+ if clear_route_hints. len ( ) != 0 || payee_pubkey . is_some ( ) { return Err ( DecodeError :: InvalidValue ) }
587
584
Payee :: Blinded { route_hints : blinded_route_hints }
588
585
} else {
589
- Payee :: Clear { route_hints : clear_route_hints }
586
+ Payee :: Clear {
587
+ route_hints : clear_route_hints,
588
+ node_id : payee_pubkey. ok_or ( DecodeError :: InvalidValue ) ?,
589
+ }
590
590
} ;
591
591
Ok ( Self {
592
- payee_pubkey : _init_tlv_based_struct_field ! ( payee_pubkey, required) ,
593
592
max_total_cltv_expiry_delta : _init_tlv_based_struct_field ! ( max_total_cltv_expiry_delta, ( default_value, unused) ) ,
594
593
features,
595
594
max_path_count : _init_tlv_based_struct_field ! ( max_path_count, ( default_value, unused) ) ,
@@ -610,9 +609,8 @@ impl PaymentParameters {
610
609
/// provided.
611
610
pub fn from_node_id ( payee_pubkey : PublicKey , final_cltv_expiry_delta : u32 ) -> Self {
612
611
Self {
613
- payee_pubkey,
614
612
features : None ,
615
- payee : Payee :: Clear { route_hints : vec ! [ ] } ,
613
+ payee : Payee :: Clear { node_id : payee_pubkey , route_hints : vec ! [ ] } ,
616
614
expiry_time : None ,
617
615
max_total_cltv_expiry_delta : DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA ,
618
616
max_path_count : DEFAULT_MAX_PATH_COUNT ,
@@ -644,8 +642,8 @@ impl PaymentParameters {
644
642
pub fn with_route_hints ( self , route_hints : Vec < RouteHint > ) -> Result < Self , ( ) > {
645
643
match self . payee {
646
644
Payee :: Blinded { .. } => Err ( ( ) ) ,
647
- Payee :: Clear { .. } =>
648
- Ok ( Self { payee : Payee :: Clear { route_hints } , ..self } )
645
+ Payee :: Clear { node_id , .. } =>
646
+ Ok ( Self { payee : Payee :: Clear { route_hints, node_id } , ..self } )
649
647
}
650
648
}
651
649
@@ -691,11 +689,22 @@ pub enum Payee {
691
689
} ,
692
690
/// The recipient included these route hints in their BOLT11 invoice.
693
691
Clear {
692
+ /// The node id of the payee.
693
+ node_id : PublicKey ,
694
694
/// Hints for routing to the payee, containing channels connecting the payee to public nodes.
695
695
route_hints : Vec < RouteHint > ,
696
696
} ,
697
697
}
698
698
699
+ impl Payee {
700
+ fn node_id ( & self ) -> Option < PublicKey > {
701
+ match self {
702
+ Self :: Clear { node_id, .. } => Some ( * node_id) ,
703
+ _ => None ,
704
+ }
705
+ }
706
+ }
707
+
699
708
/// A list of hops along a payment path terminating with a channel to the recipient.
700
709
#[ derive( Clone , Debug , Hash , Eq , PartialEq ) ]
701
710
pub struct RouteHint ( pub Vec < RouteHintHop > ) ;
@@ -1080,6 +1089,21 @@ fn default_node_features() -> NodeFeatures {
1080
1089
features
1081
1090
}
1082
1091
1092
+ struct LoggedPayeePubkey ( Option < PublicKey > ) ;
1093
+ impl fmt:: Display for LoggedPayeePubkey {
1094
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
1095
+ match self . 0 {
1096
+ Some ( pk) => {
1097
+ "payee node id " . fmt ( f) ?;
1098
+ pk. fmt ( f)
1099
+ } ,
1100
+ None => {
1101
+ "blinded payee" . fmt ( f)
1102
+ } ,
1103
+ }
1104
+ }
1105
+ }
1106
+
1083
1107
/// Finds a route from us (payer) to the given target node (payee).
1084
1108
///
1085
1109
/// If the payee provided features in their invoice, they should be provided via `params.payee`.
@@ -1129,10 +1153,16 @@ pub(crate) fn get_route<L: Deref, S: Score>(
1129
1153
_random_seed_bytes : & [ u8 ; 32 ]
1130
1154
) -> Result < Route , LightningError >
1131
1155
where L :: Target : Logger {
1132
- let payee_node_id = NodeId :: from_pubkey ( & payment_params. payee_pubkey ) ;
1156
+ // If we're routing to a blinded recipient, we won't have their node id. Therefore, keep the
1157
+ // unblinded payee id as an option. We also need a non-optional "payee id" for path construction,
1158
+ // so use a dummy id for this in the blinded case.
1159
+ let payee_node_id_opt = payment_params. payee . node_id ( ) . map ( |pk| NodeId :: from_pubkey ( & pk) ) ;
1160
+ const DUMMY_BLINDED_PAYEE_ID : [ u8 ; 33 ] = [ 42u8 ; 33 ] ;
1161
+ let maybe_dummy_payee_pk = payment_params. payee . node_id ( ) . unwrap_or_else ( || PublicKey :: from_slice ( & DUMMY_BLINDED_PAYEE_ID ) . unwrap ( ) ) ;
1162
+ let maybe_dummy_payee_node_id = NodeId :: from_pubkey ( & maybe_dummy_payee_pk) ;
1133
1163
let our_node_id = NodeId :: from_pubkey ( & our_node_pubkey) ;
1134
1164
1135
- if payee_node_id == our_node_id {
1165
+ if payee_node_id_opt . map_or ( false , |payee| payee == our_node_id) {
1136
1166
return Err ( LightningError { err : "Cannot generate a route to ourselves" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
1137
1167
}
1138
1168
@@ -1145,10 +1175,10 @@ where L::Target: Logger {
1145
1175
}
1146
1176
1147
1177
match & payment_params. payee {
1148
- Payee :: Clear { route_hints } => {
1178
+ Payee :: Clear { route_hints, node_id } => {
1149
1179
for route in route_hints. iter ( ) {
1150
1180
for hop in & route. 0 {
1151
- if hop. src_node_id == payment_params . payee_pubkey {
1181
+ if hop. src_node_id == * node_id {
1152
1182
return Err ( LightningError { err : "Route hint cannot have the payee as the source." . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
1153
1183
}
1154
1184
}
@@ -1231,14 +1261,13 @@ where L::Target: Logger {
1231
1261
false
1232
1262
} else if let Some ( features) = & payment_params. features {
1233
1263
features. supports_basic_mpp ( )
1234
- } else if let Some ( node) = network_nodes. get ( & payee_node_id) {
1235
- if let Some ( node_info) = node. announcement_info . as_ref ( ) {
1236
- node_info. features . supports_basic_mpp ( )
1237
- } else { false }
1264
+ } else if let Some ( payee) = payee_node_id_opt {
1265
+ network_nodes. get ( & payee) . map_or ( false , |node| node. announcement_info . as_ref ( ) . map_or ( false ,
1266
+ |info| info. features . supports_basic_mpp ( ) ) )
1238
1267
} else { false } ;
1239
1268
1240
- log_trace ! ( logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph" , our_node_pubkey,
1241
- payment_params. payee_pubkey , if allow_mpp { "with" } else { "without" } ,
1269
+ log_trace ! ( logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph" , our_node_pubkey,
1270
+ LoggedPayeePubkey ( payment_params. payee . node_id ( ) ) , if allow_mpp { "with" } else { "without" } ,
1242
1271
first_hops. map( |hops| hops. len( ) ) . unwrap_or( 0 ) , if first_hops. is_some( ) { "" } else { "not " } ) ;
1243
1272
1244
1273
// Step (1).
@@ -1341,7 +1370,8 @@ where L::Target: Logger {
1341
1370
} ) ;
1342
1371
}
1343
1372
1344
- log_trace ! ( logger, "Building path from {} (payee) to {} (us/payer) for value {} msat." , payment_params. payee_pubkey, our_node_pubkey, final_value_msat) ;
1373
+ log_trace ! ( logger, "Building path from {} to payer {} for value {} msat." ,
1374
+ LoggedPayeePubkey ( payment_params. payee. node_id( ) ) , our_node_pubkey, final_value_msat) ;
1345
1375
1346
1376
macro_rules! add_entry {
1347
1377
// Adds entry which goes from $src_node_id to $dest_node_id over the $candidate hop.
@@ -1590,7 +1620,7 @@ where L::Target: Logger {
1590
1620
// Entries are added to dist in add_entry!() when there is a channel from a node.
1591
1621
// Because there are no channels from payee, it will not have a dist entry at this point.
1592
1622
// If we're processing any other node, it is always be the result of a channel from it.
1593
- assert_eq !( $node_id, payee_node_id ) ;
1623
+ debug_assert_eq !( $node_id, maybe_dummy_payee_node_id ) ;
1594
1624
false
1595
1625
} ;
1596
1626
@@ -1650,35 +1680,35 @@ where L::Target: Logger {
1650
1680
1651
1681
// If first hop is a private channel and the only way to reach the payee, this is the only
1652
1682
// place where it could be added.
1653
- if let Some ( first_channels ) = first_hop_targets. get ( & payee_node_id ) {
1683
+ payee_node_id_opt . map ( |payee| first_hop_targets. get ( & payee ) . map ( |first_channels| {
1654
1684
for details in first_channels {
1655
1685
let candidate = CandidateRouteHop :: FirstHop { details } ;
1656
- let added = add_entry ! ( candidate, our_node_id, payee_node_id , 0 , path_value_msat,
1686
+ let added = add_entry ! ( candidate, our_node_id, payee , 0 , path_value_msat,
1657
1687
0 , 0u64 , 0 , 0 ) ;
1658
1688
log_trace ! ( logger, "{} direct route to payee via SCID {}" ,
1659
1689
if added { "Added" } else { "Skipped" } , candidate. short_channel_id( ) ) ;
1660
1690
}
1661
- }
1691
+ } ) ) ;
1662
1692
1663
1693
// Add the payee as a target, so that the payee-to-payer
1664
1694
// search algorithm knows what to start with.
1665
- match network_nodes. get ( & payee_node_id ) {
1695
+ payee_node_id_opt . map ( |payee| match network_nodes. get ( & payee ) {
1666
1696
// The payee is not in our network graph, so nothing to add here.
1667
1697
// There is still a chance of reaching them via last_hops though,
1668
1698
// so don't yet fail the payment here.
1669
1699
// If not, targets.pop() will not even let us enter the loop in step 2.
1670
1700
None => { } ,
1671
1701
Some ( node) => {
1672
- add_entries_to_cheapest_to_target_node ! ( node, payee_node_id , 0 , path_value_msat, 0 , 0u64 , 0 , 0 ) ;
1702
+ add_entries_to_cheapest_to_target_node ! ( node, payee , 0 , path_value_msat, 0 , 0u64 , 0 , 0 ) ;
1673
1703
} ,
1674
- }
1704
+ } ) ;
1675
1705
1676
1706
// Step (2).
1677
1707
// If a caller provided us with last hops, add them to routing targets. Since this happens
1678
1708
// earlier than general path finding, they will be somewhat prioritized, although currently
1679
1709
// it matters only if the fees are exactly the same.
1680
1710
let route_hints = match & payment_params. payee {
1681
- Payee :: Clear { route_hints } => route_hints,
1711
+ Payee :: Clear { route_hints, .. } => route_hints,
1682
1712
_ => return Err ( LightningError { err : "Routing to blinded paths isn't supported yet" . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ,
1683
1713
} ;
1684
1714
for route in route_hints. iter ( ) . filter ( |route| !route. 0 . is_empty ( ) ) {
@@ -1693,7 +1723,7 @@ where L::Target: Logger {
1693
1723
// We start building the path from reverse, i.e., from payee
1694
1724
// to the first RouteHintHop in the path.
1695
1725
let hop_iter = route. 0 . iter ( ) . rev ( ) ;
1696
- let prev_hop_iter = core:: iter:: once ( & payment_params . payee_pubkey ) . chain (
1726
+ let prev_hop_iter = core:: iter:: once ( & maybe_dummy_payee_pk ) . chain (
1697
1727
route. 0 . iter ( ) . skip ( 1 ) . rev ( ) . map ( |hop| & hop. src_node_id ) ) ;
1698
1728
let mut hop_used = true ;
1699
1729
let mut aggregate_next_hops_fee_msat: u64 = 0 ;
@@ -1853,7 +1883,7 @@ where L::Target: Logger {
1853
1883
// save this path for the payment route. Also, update the liquidity
1854
1884
// remaining on the used hops, so that we take them into account
1855
1885
// while looking for more paths.
1856
- if ordered_hops. last ( ) . unwrap ( ) . 0 . node_id == payee_node_id {
1886
+ if ordered_hops. last ( ) . unwrap ( ) . 0 . node_id == maybe_dummy_payee_node_id {
1857
1887
break ' path_walk;
1858
1888
}
1859
1889
@@ -1936,7 +1966,7 @@ where L::Target: Logger {
1936
1966
// If we found a path back to the payee, we shouldn't try to process it again. This is
1937
1967
// the equivalent of the `elem.was_processed` check in
1938
1968
// add_entries_to_cheapest_to_target_node!() (see comment there for more info).
1939
- if node_id == payee_node_id { continue ' path_construction; }
1969
+ if node_id == maybe_dummy_payee_node_id { continue ' path_construction; }
1940
1970
1941
1971
// Otherwise, since the current target node is not us,
1942
1972
// keep "unrolling" the payment graph from payee to payer by
@@ -2106,7 +2136,7 @@ where L::Target: Logger {
2106
2136
paths,
2107
2137
payment_params : Some ( payment_params. clone ( ) ) ,
2108
2138
} ;
2109
- log_info ! ( logger, "Got route to {} : {}" , payment_params . payee_pubkey , log_route!( route) ) ;
2139
+ log_info ! ( logger, "Got route: {}" , log_route!( route) ) ;
2110
2140
Ok ( route)
2111
2141
}
2112
2142
0 commit comments