@@ -68,7 +68,7 @@ pub struct Route {
6868 /// last RouteHop in each path must be the same.
6969 /// Each entry represents a list of hops, NOT INCLUDING our own, where the last hop is the
7070 /// destination. Thus, this must always be at least length one. While the maximum length of any
71- /// given path is variable, keeping the length of any path to less than 20 should currently
71+ /// given path is variable, keeping the length of any path to less than 19 should currently
7272 /// ensure it is viable.
7373 pub paths : Vec < Vec < RouteHop > > ,
7474 /// The `payment_params` parameter passed to [`find_route`].
@@ -327,6 +327,8 @@ struct RouteGraphNode {
327327 /// All penalties incurred from this hop on the way to the destination, as calculated using
328328 /// channel scoring.
329329 path_penalty_msat : u64 ,
330+ /// The number of hops walked up to this node.
331+ path_length_to_node : u8 ,
330332}
331333
332334impl cmp:: Ord for RouteGraphNode {
@@ -796,11 +798,11 @@ where L::Target: Logger {
796798 // The main heap containing all candidate next-hops sorted by their score (max(A* fee,
797799 // htlc_minimum)). Ideally this would be a heap which allowed cheap score reduction instead of
798800 // adding duplicate entries when we find a better path to a given node.
799- let mut targets = BinaryHeap :: new ( ) ;
801+ let mut targets: BinaryHeap < RouteGraphNode > = BinaryHeap :: new ( ) ;
800802
801803 // Map from node_id to information about the best current path to that node, including feerate
802804 // information.
803- let mut dist = HashMap :: with_capacity ( network_nodes. len ( ) ) ;
805+ let mut dist: HashMap < NodeId , PathBuildingHop > = HashMap :: with_capacity ( network_nodes. len ( ) ) ;
804806
805807 // During routing, if we ignore a path due to an htlc_minimum_msat limit, we set this,
806808 // indicating that we may wish to try again with a higher value, potentially paying to meet an
@@ -829,6 +831,20 @@ where L::Target: Logger {
829831 // - when we want to stop looking for new paths.
830832 let mut already_collected_value_msat = 0 ;
831833
834+ // We only consider paths shorter than our maximum length estimate.
835+ // In the legacy onion format, the maximum number of hops used to be a fixed value of 20.
836+ // However, in the TLV onion format, there is no fixed maximum length, but the `hop_payloads`
837+ // field is always 1300 bytes. As the `tlv_payload` for each hop may vary in length, we have to
838+ // estimate how many hops the route may have so that it actually fits the `hop_payloads` field.
839+ //
840+ // We estimate 3+32 (payload length and HMAC) + 2+8 (amt_to_forward) + 2+4 (outgoing_cltv_value) +
841+ // 2+8 (short_channel_id) = 61 bytes for each intermediate hop and 3+32
842+ // (payload length and HMAC) + 2+8 (amt_to_forward) + 2+4 (outgoing_cltv_value) + 2+32+8
843+ // (payment_secret and total_msat) = 93 bytes for the final hop.
844+ // Since the length of the potentially included `payment_metadata` is unkown to us, we round
845+ // down from (1300-93) / 61 = 19.78... to arrive at a conservative estimate of 19.
846+ const MAX_PATH_LENGTH_ESTIMATE : u8 = 19 ;
847+
832848 for ( _, channels) in first_hop_targets. iter_mut ( ) {
833849 // Sort the first_hops channels to the same node(s) in priority order of which channel we'd
834850 // most like to use.
@@ -860,8 +876,8 @@ where L::Target: Logger {
860876 // since that value has to be transferred over this channel.
861877 // Returns whether this channel caused an update to `targets`.
862878 ( $candidate: expr, $src_node_id: expr, $dest_node_id: expr, $next_hops_fee_msat: expr,
863- $next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr,
864- $next_hops_path_penalty_msat: expr, $next_hops_cltv_delta: expr ) => { {
879+ $next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr,
880+ $next_hops_path_penalty_msat: expr, $next_hops_cltv_delta: expr , $next_hops_path_length : expr ) => { {
865881 // We "return" whether we updated the path at the end, via this:
866882 let mut did_add_update_path_to_src_node = false ;
867883 // Channels to self should not be used. This is more of belt-and-suspenders, because in
@@ -905,6 +921,9 @@ where L::Target: Logger {
905921 } ;
906922 // Verify the liquidity offered by this channel complies to the minimal contribution.
907923 let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat;
924+ // Do not consider candidate hops that would exceed the maximum path length.
925+ let path_length_to_node = $next_hops_path_length + 1 ;
926+ let doesnt_exceed_max_path_length = path_length_to_node <= MAX_PATH_LENGTH_ESTIMATE ;
908927
909928 // Do not consider candidates that exceed the maximum total cltv expiry limit.
910929 // In order to already account for some of the privacy enhancing random CLTV
@@ -939,9 +958,11 @@ where L::Target: Logger {
939958 // bother considering this channel. If retrying with recommended_value_msat may
940959 // allow us to hit the HTLC minimum limit, set htlc_minimum_limit so that we go
941960 // around again with a higher amount.
942- if contributes_sufficient_value && doesnt_exceed_cltv_delta_limit && may_overpay_to_meet_path_minimum_msat {
961+ if contributes_sufficient_value && doesnt_exceed_max_path_length &&
962+ doesnt_exceed_cltv_delta_limit && may_overpay_to_meet_path_minimum_msat {
943963 hit_minimum_limit = true ;
944- } else if contributes_sufficient_value && doesnt_exceed_cltv_delta_limit && over_path_minimum_msat {
964+ } else if contributes_sufficient_value && doesnt_exceed_max_path_length &&
965+ doesnt_exceed_cltv_delta_limit && over_path_minimum_msat {
945966 // Note that low contribution here (limited by available_liquidity_msat)
946967 // might violate htlc_minimum_msat on the hops which are next along the
947968 // payment path (upstream to the payee). To avoid that, we recompute
@@ -1038,6 +1059,7 @@ where L::Target: Logger {
10381059 value_contribution_msat: value_contribution_msat,
10391060 path_htlc_minimum_msat,
10401061 path_penalty_msat,
1062+ path_length_to_node,
10411063 } ;
10421064
10431065 // Update the way of reaching $src_node_id with the given short_channel_id (from $dest_node_id),
@@ -1121,7 +1143,9 @@ where L::Target: Logger {
11211143 // meaning how much will be paid in fees after this node (to the best of our knowledge).
11221144 // This data can later be helpful to optimize routing (pay lower fees).
11231145 macro_rules! add_entries_to_cheapest_to_target_node {
1124- ( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr, $next_hops_path_penalty_msat: expr, $next_hops_cltv_delta: expr ) => {
1146+ ( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $next_hops_value_contribution: expr,
1147+ $next_hops_path_htlc_minimum_msat: expr, $next_hops_path_penalty_msat: expr,
1148+ $next_hops_cltv_delta: expr, $next_hops_path_length: expr ) => {
11251149 let skip_node = if let Some ( elem) = dist. get_mut( & $node_id) {
11261150 let was_processed = elem. was_processed;
11271151 elem. was_processed = true ;
@@ -1138,7 +1162,10 @@ where L::Target: Logger {
11381162 if let Some ( first_channels) = first_hop_targets. get( & $node_id) {
11391163 for details in first_channels {
11401164 let candidate = CandidateRouteHop :: FirstHop { details } ;
1141- add_entry!( candidate, our_node_id, $node_id, $fee_to_target_msat, $next_hops_value_contribution, $next_hops_path_htlc_minimum_msat, $next_hops_path_penalty_msat, $next_hops_cltv_delta) ;
1165+ add_entry!( candidate, our_node_id, $node_id, $fee_to_target_msat,
1166+ $next_hops_value_contribution,
1167+ $next_hops_path_htlc_minimum_msat, $next_hops_path_penalty_msat,
1168+ $next_hops_cltv_delta, $next_hops_path_length) ;
11421169 }
11431170 }
11441171
@@ -1161,7 +1188,12 @@ where L::Target: Logger {
11611188 info: directed_channel. with_update( ) . unwrap( ) ,
11621189 short_channel_id: * chan_id,
11631190 } ;
1164- add_entry!( candidate, * source, $node_id, $fee_to_target_msat, $next_hops_value_contribution, $next_hops_path_htlc_minimum_msat, $next_hops_path_penalty_msat, $next_hops_cltv_delta) ;
1191+ add_entry!( candidate, * source, $node_id,
1192+ $fee_to_target_msat,
1193+ $next_hops_value_contribution,
1194+ $next_hops_path_htlc_minimum_msat,
1195+ $next_hops_path_penalty_msat,
1196+ $next_hops_cltv_delta, $next_hops_path_length) ;
11651197 }
11661198 }
11671199 }
@@ -1188,8 +1220,10 @@ where L::Target: Logger {
11881220 if let Some ( first_channels) = first_hop_targets. get ( & payee_node_id) {
11891221 for details in first_channels {
11901222 let candidate = CandidateRouteHop :: FirstHop { details } ;
1191- let added = add_entry ! ( candidate, our_node_id, payee_node_id, 0 , path_value_msat, 0 , 0u64 , 0 ) ;
1192- log_trace ! ( logger, "{} direct route to payee via SCID {}" , if added { "Added" } else { "Skipped" } , candidate. short_channel_id( ) ) ;
1223+ let added = add_entry ! ( candidate, our_node_id, payee_node_id, 0 , path_value_msat,
1224+ 0 , 0u64 , 0 , 0 ) ;
1225+ log_trace ! ( logger, "{} direct route to payee via SCID {}" ,
1226+ if added { "Added" } else { "Skipped" } , candidate. short_channel_id( ) ) ;
11931227 }
11941228 }
11951229
@@ -1202,7 +1236,7 @@ where L::Target: Logger {
12021236 // If not, targets.pop() will not even let us enter the loop in step 2.
12031237 None => { } ,
12041238 Some ( node) => {
1205- add_entries_to_cheapest_to_target_node ! ( node, payee_node_id, 0 , path_value_msat, 0 , 0u64 , 0 ) ;
1239+ add_entries_to_cheapest_to_target_node ! ( node, payee_node_id, 0 , path_value_msat, 0 , 0u64 , 0 , 0 ) ;
12061240 } ,
12071241 }
12081242
@@ -1229,6 +1263,7 @@ where L::Target: Logger {
12291263 let mut aggregate_next_hops_path_htlc_minimum_msat: u64 = 0 ;
12301264 let mut aggregate_next_hops_path_penalty_msat: u64 = 0 ;
12311265 let mut aggregate_next_hops_cltv_delta: u32 = 0 ;
1266+ let mut aggregate_next_hops_path_length: u8 = 0 ;
12321267
12331268 for ( idx, ( hop, prev_hop_id) ) in hop_iter. zip ( prev_hop_iter) . enumerate ( ) {
12341269 let source = NodeId :: from_pubkey ( & hop. src_node_id ) ;
@@ -1244,23 +1279,34 @@ where L::Target: Logger {
12441279 . unwrap_or_else ( || CandidateRouteHop :: PrivateHop { hint : hop } ) ;
12451280 let capacity_msat = candidate. effective_capacity ( ) . as_msat ( ) ;
12461281 aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
1247- . saturating_add ( scorer. channel_penalty_msat ( hop. short_channel_id , final_value_msat, capacity_msat, & source, & target) ) ;
1282+ . saturating_add ( scorer. channel_penalty_msat ( hop. short_channel_id ,
1283+ final_value_msat, capacity_msat, & source, & target) ) ;
12481284
12491285 aggregate_next_hops_cltv_delta = aggregate_next_hops_cltv_delta
12501286 . saturating_add ( hop. cltv_expiry_delta as u32 ) ;
12511287
1252- if !add_entry ! ( candidate, source, target, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat, aggregate_next_hops_cltv_delta) {
1253- // If this hop was not used then there is no use checking the preceding hops
1254- // in the RouteHint. We can break by just searching for a direct channel between
1255- // last checked hop and first_hop_targets
1288+ aggregate_next_hops_path_length = aggregate_next_hops_path_length
1289+ . saturating_add ( 1 ) ;
1290+
1291+ if !add_entry ! ( candidate, source, target, aggregate_next_hops_fee_msat,
1292+ path_value_msat, aggregate_next_hops_path_htlc_minimum_msat,
1293+ aggregate_next_hops_path_penalty_msat,
1294+ aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length) {
1295+ // If this hop was not used then there is no use checking the preceding
1296+ // hops in the RouteHint. We can break by just searching for a direct
1297+ // channel between last checked hop and first_hop_targets.
12561298 hop_used = false ;
12571299 }
12581300
12591301 // Searching for a direct channel between last checked hop and first_hop_targets
12601302 if let Some ( first_channels) = first_hop_targets. get ( & NodeId :: from_pubkey ( & prev_hop_id) ) {
12611303 for details in first_channels {
12621304 let candidate = CandidateRouteHop :: FirstHop { details } ;
1263- add_entry ! ( candidate, our_node_id, NodeId :: from_pubkey( & prev_hop_id) , aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat, aggregate_next_hops_cltv_delta) ;
1305+ add_entry ! ( candidate, our_node_id, NodeId :: from_pubkey( & prev_hop_id) ,
1306+ aggregate_next_hops_fee_msat, path_value_msat,
1307+ aggregate_next_hops_path_htlc_minimum_msat,
1308+ aggregate_next_hops_path_penalty_msat, aggregate_next_hops_cltv_delta,
1309+ aggregate_next_hops_path_length) ;
12641310 }
12651311 }
12661312
@@ -1296,7 +1342,13 @@ where L::Target: Logger {
12961342 if let Some ( first_channels) = first_hop_targets. get ( & NodeId :: from_pubkey ( & hop. src_node_id ) ) {
12971343 for details in first_channels {
12981344 let candidate = CandidateRouteHop :: FirstHop { details } ;
1299- add_entry ! ( candidate, our_node_id, NodeId :: from_pubkey( & hop. src_node_id) , aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat, aggregate_next_hops_cltv_delta) ;
1345+ add_entry ! ( candidate, our_node_id,
1346+ NodeId :: from_pubkey( & hop. src_node_id) ,
1347+ aggregate_next_hops_fee_msat, path_value_msat,
1348+ aggregate_next_hops_path_htlc_minimum_msat,
1349+ aggregate_next_hops_path_penalty_msat,
1350+ aggregate_next_hops_cltv_delta,
1351+ aggregate_next_hops_path_length) ;
13001352 }
13011353 }
13021354 }
@@ -1319,13 +1371,13 @@ where L::Target: Logger {
13191371 // Both these cases (and other cases except reaching recommended_value_msat) mean that
13201372 // paths_collection will be stopped because found_new_path==false.
13211373 // This is not necessarily a routing failure.
1322- ' path_construction: while let Some ( RouteGraphNode { node_id, lowest_fee_to_node, total_cltv_delta, value_contribution_msat, path_htlc_minimum_msat, path_penalty_msat, .. } ) = targets. pop ( ) {
1374+ ' path_construction: while let Some ( RouteGraphNode { node_id, lowest_fee_to_node, total_cltv_delta, value_contribution_msat, path_htlc_minimum_msat, path_penalty_msat, path_length_to_node , .. } ) = targets. pop ( ) {
13231375
13241376 // Since we're going payee-to-payer, hitting our node as a target means we should stop
13251377 // traversing the graph and arrange the path out of what we found.
13261378 if node_id == our_node_id {
13271379 let mut new_entry = dist. remove ( & our_node_id) . unwrap ( ) ;
1328- let mut ordered_hops = vec ! ( ( new_entry. clone( ) , default_node_features. clone( ) ) ) ;
1380+ let mut ordered_hops: Vec < ( PathBuildingHop , NodeFeatures ) > = vec ! ( ( new_entry. clone( ) , default_node_features. clone( ) ) ) ;
13291381
13301382 ' path_walk: loop {
13311383 let mut features_set = false ;
@@ -1441,7 +1493,9 @@ where L::Target: Logger {
14411493 match network_nodes. get ( & node_id) {
14421494 None => { } ,
14431495 Some ( node) => {
1444- add_entries_to_cheapest_to_target_node ! ( node, node_id, lowest_fee_to_node, value_contribution_msat, path_htlc_minimum_msat, path_penalty_msat, total_cltv_delta) ;
1496+ add_entries_to_cheapest_to_target_node ! ( node, node_id, lowest_fee_to_node,
1497+ value_contribution_msat, path_htlc_minimum_msat, path_penalty_msat,
1498+ total_cltv_delta, path_length_to_node) ;
14451499 } ,
14461500 }
14471501 }
@@ -1703,7 +1757,7 @@ mod tests {
17031757 use chain:: keysinterface:: KeysInterface ;
17041758 use ln:: features:: { ChannelFeatures , InitFeatures , InvoiceFeatures , NodeFeatures } ;
17051759 use ln:: msgs:: { ErrorAction , LightningError , OptionalField , UnsignedChannelAnnouncement , ChannelAnnouncement , RoutingMessageHandler ,
1706- NodeAnnouncement , UnsignedNodeAnnouncement , ChannelUpdate , UnsignedChannelUpdate } ;
1760+ NodeAnnouncement , UnsignedNodeAnnouncement , ChannelUpdate , UnsignedChannelUpdate } ;
17071761 use ln:: channelmanager;
17081762 use util:: test_utils;
17091763 use util:: chacha20:: ChaCha20 ;
@@ -1836,7 +1890,7 @@ mod tests {
18361890 }
18371891
18381892 fn get_nodes ( secp_ctx : & Secp256k1 < All > ) -> ( SecretKey , PublicKey , Vec < SecretKey > , Vec < PublicKey > ) {
1839- let privkeys: Vec < SecretKey > = ( 2 ..10 ) . map ( |i| {
1893+ let privkeys: Vec < SecretKey > = ( 2 ..22 ) . map ( |i| {
18401894 SecretKey :: from_slice ( & hex:: decode ( format ! ( "{:02x}" , i) . repeat ( 32 ) ) . unwrap ( ) [ ..] ) . unwrap ( )
18411895 } ) . collect ( ) ;
18421896
@@ -1863,6 +1917,57 @@ mod tests {
18631917 }
18641918 }
18651919
1920+ fn build_line_graph ( ) -> (
1921+ Secp256k1 < All > , sync:: Arc < NetworkGraph > , NetGraphMsgHandler < sync:: Arc < NetworkGraph > ,
1922+ sync:: Arc < test_utils:: TestChainSource > , sync:: Arc < crate :: util:: test_utils:: TestLogger > > ,
1923+ sync:: Arc < test_utils:: TestChainSource > , sync:: Arc < test_utils:: TestLogger > ,
1924+ ) {
1925+ let secp_ctx = Secp256k1 :: new ( ) ;
1926+ let logger = Arc :: new ( test_utils:: TestLogger :: new ( ) ) ;
1927+ let chain_monitor = Arc :: new ( test_utils:: TestChainSource :: new ( Network :: Testnet ) ) ;
1928+ let network_graph = Arc :: new ( NetworkGraph :: new ( genesis_block ( Network :: Testnet ) . header . block_hash ( ) ) ) ;
1929+ let net_graph_msg_handler = NetGraphMsgHandler :: new ( Arc :: clone ( & network_graph) , None , Arc :: clone ( & logger) ) ;
1930+
1931+ // Build network from our_id to node 20:
1932+ // our_id -1(1)2- node0 -1(2)2- node1 - ... - node20
1933+ let ( our_privkey, _, privkeys, _) = get_nodes ( & secp_ctx) ;
1934+
1935+ for ( idx, ( cur_privkey, next_privkey) ) in core:: iter:: once ( & our_privkey)
1936+ . chain ( privkeys. iter ( ) ) . zip ( privkeys. iter ( ) ) . enumerate ( ) {
1937+ let cur_short_channel_id = ( idx as u64 ) + 1 ;
1938+ add_channel ( & net_graph_msg_handler, & secp_ctx, & cur_privkey, & next_privkey,
1939+ ChannelFeatures :: from_le_bytes ( id_to_feature_flags ( 1 ) ) , cur_short_channel_id) ;
1940+ update_channel ( & net_graph_msg_handler, & secp_ctx, & cur_privkey, UnsignedChannelUpdate {
1941+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
1942+ short_channel_id : cur_short_channel_id,
1943+ timestamp : idx as u32 ,
1944+ flags : 0 ,
1945+ cltv_expiry_delta : 0 ,
1946+ htlc_minimum_msat : 0 ,
1947+ htlc_maximum_msat : OptionalField :: Absent ,
1948+ fee_base_msat : 0 ,
1949+ fee_proportional_millionths : 0 ,
1950+ excess_data : Vec :: new ( )
1951+ } ) ;
1952+ update_channel ( & net_graph_msg_handler, & secp_ctx, & next_privkey, UnsignedChannelUpdate {
1953+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
1954+ short_channel_id : cur_short_channel_id,
1955+ timestamp : ( idx as u32 ) +1 ,
1956+ flags : 1 ,
1957+ cltv_expiry_delta : 0 ,
1958+ htlc_minimum_msat : 0 ,
1959+ htlc_maximum_msat : OptionalField :: Absent ,
1960+ fee_base_msat : 0 ,
1961+ fee_proportional_millionths : 0 ,
1962+ excess_data : Vec :: new ( )
1963+ } ) ;
1964+ add_or_update_node ( & net_graph_msg_handler, & secp_ctx, next_privkey,
1965+ NodeFeatures :: from_le_bytes ( id_to_feature_flags ( 1 ) ) , 0 ) ;
1966+ }
1967+
1968+ ( secp_ctx, network_graph, net_graph_msg_handler, chain_monitor, logger)
1969+ }
1970+
18661971 fn build_graph ( ) -> (
18671972 Secp256k1 < All > ,
18681973 sync:: Arc < NetworkGraph > ,
@@ -5213,6 +5318,35 @@ mod tests {
52135318 }
52145319 }
52155320
5321+ #[ test]
5322+ fn limits_path_length ( ) {
5323+ let ( secp_ctx, network, _, _, logger) = build_line_graph ( ) ;
5324+ let ( _, our_id, _, nodes) = get_nodes ( & secp_ctx) ;
5325+ let network_graph = network. read_only ( ) ;
5326+
5327+ let scorer = test_utils:: TestScorer :: with_penalty ( 0 ) ;
5328+ let keys_manager = test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
5329+ let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
5330+
5331+ // First check we can actually create a long route on this graph.
5332+ let feasible_payment_params = PaymentParameters :: from_node_id ( nodes[ 18 ] ) ;
5333+ let route = get_route ( & our_id, & feasible_payment_params, & network_graph, None , 100 , 0 ,
5334+ Arc :: clone ( & logger) , & scorer, & random_seed_bytes) . unwrap ( ) ;
5335+ let path = route. paths [ 0 ] . iter ( ) . map ( |hop| hop. short_channel_id ) . collect :: < Vec < _ > > ( ) ;
5336+ assert ! ( path. len( ) == 19 ) ;
5337+
5338+ // But we can't create a path surpassing the 19-hop MAX_PATH_LENGTH_ESTIMATE limit.
5339+ let fail_payment_params = PaymentParameters :: from_node_id ( nodes[ 19 ] ) ;
5340+ match get_route ( & our_id, & fail_payment_params, & network_graph, None , 100 , 0 ,
5341+ Arc :: clone ( & logger) , & scorer, & random_seed_bytes)
5342+ {
5343+ Err ( LightningError { err, .. } ) => {
5344+ assert_eq ! ( err, "Failed to find a path to the given destination" ) ;
5345+ } ,
5346+ Ok ( _) => panic ! ( "Expected error" ) ,
5347+ }
5348+ }
5349+
52165350 #[ test]
52175351 fn adds_and_limits_cltv_offset ( ) {
52185352 let ( secp_ctx, network_graph, _, _, logger) = build_graph ( ) ;
0 commit comments