@@ -2300,12 +2300,19 @@ where L::Target: Logger {
23002300					. chain ( payment_path. hops . iter ( ) . map ( |( hop,  _) | & hop. node_id ) ) ; 
23012301				for  ( prev_hop,  ( hop,  _) )  in  prev_hop_iter. zip ( payment_path. hops . iter ( ) )  { 
23022302					let  spent_on_hop_msat = value_contribution_msat + hop. next_hops_fee_msat ; 
2303+ 					let  hop_max_msat = max_htlc_from_capacity ( 
2304+ 						hop. candidate . effective_capacity ( ) ,  channel_saturation_pow_half
2305+ 					) ; 
2306+ 					let  used_liquidity_candidate = used_liquidities
2307+ 						. get ( & hop. candidate . id ( * prev_hop < hop. node_id ) ) 
2308+ 						. map_or ( spent_on_hop_msat,  |used_liquidity_msat|
2309+ 							used_liquidity_msat + spent_on_hop_msat) ; 
2310+ 					if  used_liquidity_candidate > hop_max_msat {  break  ' path_construction } 
2311+ 
23032312					let  used_liquidity_msat = used_liquidities
23042313						. entry ( hop. candidate . id ( * prev_hop < hop. node_id ) ) 
23052314						. and_modify ( |used_liquidity_msat| * used_liquidity_msat += spent_on_hop_msat) 
23062315						. or_insert ( spent_on_hop_msat) ; 
2307- 					let  hop_capacity = hop. candidate . effective_capacity ( ) ; 
2308- 					let  hop_max_msat = max_htlc_from_capacity ( hop_capacity,  channel_saturation_pow_half) ; 
23092316					if  * used_liquidity_msat == hop_max_msat { 
23102317						// If this path used all of this channel's available liquidity, we know 
23112318						// this path will not be selected again in the next loop iteration. 
@@ -7185,6 +7192,134 @@ mod tests {
71857192			assert_eq ! ( err,  "Failed to find a path to the given destination" ) ; 
71867193		}  else  {  panic ! ( )  } 
71877194	} 
7195+ 
7196+ 	#[ test]  
7197+ 	fn  min_htlc_overpay_violates_max_htlc ( )  { 
7198+ 		// Test that if overpaying to meet a later hop's min_htlc and causes us to violate an earlier 
7199+ 		// hop's max_htlc, we discard that invalid path. Previously we would consider the path to be 
7200+ 		// valid and hit a debug panic asserting that the used liquidity for a hop was less than its 
7201+ 		// available liquidity limit. 
7202+ 		let  secp_ctx = Secp256k1 :: new ( ) ; 
7203+ 		let  logger = Arc :: new ( ln_test_utils:: TestLogger :: new ( ) ) ; 
7204+ 		let  network_graph = Arc :: new ( NetworkGraph :: new ( Network :: Testnet ,  Arc :: clone ( & logger) ) ) ; 
7205+ 		let  scorer = ln_test_utils:: TestScorer :: new ( ) ; 
7206+ 		let  keys_manager = ln_test_utils:: TestKeysInterface :: new ( & [ 0u8 ;  32 ] ,  Network :: Testnet ) ; 
7207+ 		let  random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ; 
7208+ 		let  config = UserConfig :: default ( ) ; 
7209+ 
7210+ 		// Values are taken from the fuzz input that uncovered this panic. 
7211+ 		let  amt_msat = 7_4009_8048 ; 
7212+ 		let  ( _,  our_id,  _,  nodes)  = get_nodes ( & secp_ctx) ; 
7213+ 		let  first_hop_outbound_capacity = 2_7345_2000 ; 
7214+ 		let  first_hops = vec ! [ get_channel_details( 
7215+ 			Some ( 200 ) ,  nodes[ 0 ] ,  channelmanager:: provided_init_features( & config) , 
7216+ 			first_hop_outbound_capacity
7217+ 		) ] ; 
7218+ 
7219+ 		let  route_hint = RouteHint ( vec ! [ RouteHintHop  { 
7220+ 			src_node_id:  nodes[ 0 ] , 
7221+ 			short_channel_id:  44 , 
7222+ 			fees:  RoutingFees  { 
7223+ 				base_msat:  1_6778_3453 , 
7224+ 				proportional_millionths:  0 , 
7225+ 			} , 
7226+ 			cltv_expiry_delta:  10 , 
7227+ 			htlc_minimum_msat:  Some ( 2_5165_8240 ) , 
7228+ 			htlc_maximum_msat:  Some ( 251_6582_4000 ) , 
7229+ 		} ] ) ; 
7230+ 
7231+ 		let  payment_params = PaymentParameters :: from_node_id ( nodes[ 1 ] ,  42 ) 
7232+ 			. with_route_hints ( vec ! [ route_hint] ) . unwrap ( ) 
7233+ 			. with_bolt11_features ( channelmanager:: provided_invoice_features ( & config) ) . unwrap ( ) ; 
7234+ 
7235+ 		let  netgraph = network_graph. read_only ( ) ; 
7236+ 		let  route_params = RouteParameters :: from_payment_params_and_value ( 
7237+ 			payment_params,  amt_msat) ; 
7238+ 		if  let  Err ( LightningError  {  err,  .. } )  = get_route ( 
7239+ 			& our_id,  & route_params,  & netgraph,  Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , 
7240+ 			Arc :: clone ( & logger) ,  & scorer,  & ( ) ,  & random_seed_bytes
7241+ 		)  { 
7242+ 			assert_eq ! ( err,  "Failed to find a path to the given destination" ) ; 
7243+ 		}  else  {  panic ! ( )  } 
7244+ 	} 
7245+ 
7246+ 	#[ test]  
7247+ 	fn  previously_used_liquidity_violates_max_htlc ( )  { 
7248+ 		// Test that if a previously found path causes us to violate a newly found path hop's max_htlc, 
7249+ 		// we will discard that invalid path. Previously we would consider the path to be valid and hit 
7250+ 		// a debug panic asserting that the used liquidity for a hop was less than its available 
7251+ 		// liquidity limit. 
7252+ 		let  secp_ctx = Secp256k1 :: new ( ) ; 
7253+ 		let  logger = Arc :: new ( ln_test_utils:: TestLogger :: new ( ) ) ; 
7254+ 		let  network_graph = Arc :: new ( NetworkGraph :: new ( Network :: Testnet ,  Arc :: clone ( & logger) ) ) ; 
7255+ 		let  scorer = ln_test_utils:: TestScorer :: new ( ) ; 
7256+ 		let  keys_manager = ln_test_utils:: TestKeysInterface :: new ( & [ 0u8 ;  32 ] ,  Network :: Testnet ) ; 
7257+ 		let  random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ; 
7258+ 		let  config = UserConfig :: default ( ) ; 
7259+ 
7260+ 		// Values are taken from the fuzz input that uncovered this panic. 
7261+ 		let  amt_msat = 52_4288 ; 
7262+ 		let  ( _,  our_id,  _,  nodes)  = get_nodes ( & secp_ctx) ; 
7263+ 		let  first_hops = vec ! [ get_channel_details( 
7264+ 			Some ( 161 ) ,  nodes[ 0 ] ,  channelmanager:: provided_init_features( & config) ,  486_4000 
7265+ 		) ,  get_channel_details( 
7266+ 			Some ( 122 ) ,  nodes[ 0 ] ,  channelmanager:: provided_init_features( & config) ,  179_5000 
7267+ 		) ] ; 
7268+ 
7269+ 		let  route_hints = vec ! [ RouteHint ( vec![ RouteHintHop  { 
7270+ 			src_node_id:  nodes[ 0 ] , 
7271+ 			short_channel_id:  42 , 
7272+ 			fees:  RoutingFees  { 
7273+ 				base_msat:  0 , 
7274+ 				proportional_millionths:  0 , 
7275+ 			} , 
7276+ 			cltv_expiry_delta:  10 , 
7277+ 			htlc_minimum_msat:  Some ( 1_4392 ) , 
7278+ 			htlc_maximum_msat:  Some ( 143_9200 ) , 
7279+ 		} ] ) ,  RouteHint ( vec![ RouteHintHop  { 
7280+ 			src_node_id:  nodes[ 0 ] , 
7281+ 			short_channel_id:  43 , 
7282+ 			fees:  RoutingFees  { 
7283+ 				base_msat:  425_9840 , 
7284+ 				proportional_millionths:  0 , 
7285+ 			} , 
7286+ 			cltv_expiry_delta:  10 , 
7287+ 			htlc_minimum_msat:  Some ( 19_7401 ) , 
7288+ 			htlc_maximum_msat:  Some ( 1974_0100 ) , 
7289+ 		} ] ) ,  RouteHint ( vec![ RouteHintHop  { 
7290+ 			src_node_id:  nodes[ 0 ] , 
7291+ 			short_channel_id:  44 , 
7292+ 			fees:  RoutingFees  { 
7293+ 				base_msat:  0 , 
7294+ 				proportional_millionths:  0 , 
7295+ 			} , 
7296+ 			cltv_expiry_delta:  10 , 
7297+ 			htlc_minimum_msat:  Some ( 1027 ) , 
7298+ 			htlc_maximum_msat:  Some ( 10_2700 ) , 
7299+ 		} ] ) ,  RouteHint ( vec![ RouteHintHop  { 
7300+ 			src_node_id:  nodes[ 0 ] , 
7301+ 			short_channel_id:  45 , 
7302+ 			fees:  RoutingFees  { 
7303+ 				base_msat:  0 , 
7304+ 				proportional_millionths:  0 , 
7305+ 			} , 
7306+ 			cltv_expiry_delta:  10 , 
7307+ 			htlc_minimum_msat:  Some ( 6_5535 ) , 
7308+ 			htlc_maximum_msat:  Some ( 655_3500 ) , 
7309+ 		} ] ) ] ; 
7310+ 
7311+ 		let  payment_params = PaymentParameters :: from_node_id ( nodes[ 1 ] ,  42 ) 
7312+ 			. with_route_hints ( route_hints) . unwrap ( ) 
7313+ 			. with_bolt11_features ( channelmanager:: provided_invoice_features ( & config) ) . unwrap ( ) ; 
7314+ 
7315+ 		let  netgraph = network_graph. read_only ( ) ; 
7316+ 		let  route_params = RouteParameters :: from_payment_params_and_value ( 
7317+ 			payment_params,  amt_msat) ; 
7318+ 		assert ! ( get_route( 
7319+ 			& our_id,  & route_params,  & netgraph,  Some ( & first_hops. iter( ) . collect:: <Vec <_>>( ) ) , 
7320+ 			Arc :: clone( & logger) ,  & scorer,  & ( ) ,  & random_seed_bytes
7321+ 		) . is_ok( ) ) ; 
7322+ 	} 
71887323} 
71897324
71907325#[ cfg( all( any( test,  ldk_bench) ,  not( feature = "no-std" ) ) ) ]  
0 commit comments