@@ -24,7 +24,7 @@ use crate::types::payment::{PaymentHash, PaymentSecret, PaymentPreimage};
2424use crate :: ln:: chan_utils;
2525use crate :: ln:: msgs:: ChannelMessageHandler ;
2626use crate :: ln:: onion_utils;
27- use crate :: ln:: outbound_payment:: { IDEMPOTENCY_TIMEOUT_TICKS , Retry , RetryableSendFailure } ;
27+ use crate :: ln:: outbound_payment:: { IDEMPOTENCY_TIMEOUT_TICKS , ProbeSendFailure , Retry , RetryableSendFailure } ;
2828use crate :: routing:: gossip:: { EffectiveCapacity , RoutingFees } ;
2929use crate :: routing:: router:: { get_route, Path , PaymentParameters , Route , Router , RouteHint , RouteHintHop , RouteHop , RouteParameters } ;
3030use crate :: routing:: scoring:: ChannelUsage ;
@@ -1249,6 +1249,7 @@ fn sent_probe_is_probe_of_sending_node() {
12491249 // First check we refuse to build a single-hop probe
12501250 let ( route, _, _, _) = get_route_and_payment_hash ! ( & nodes[ 0 ] , nodes[ 1 ] , 100_000 ) ;
12511251 assert ! ( nodes[ 0 ] . node. send_probe( route. paths[ 0 ] . clone( ) ) . is_err( ) ) ;
1252+ assert ! ( nodes[ 0 ] . node. pending_outbound_payments. pending_outbound_payments. lock( ) . unwrap( ) . is_empty( ) ) ;
12521253
12531254 // Then build an actual two-hop probing path
12541255 let ( route, _, _, _) = get_route_and_payment_hash ! ( & nodes[ 0 ] , nodes[ 2 ] , 100_000 ) ;
@@ -4375,3 +4376,77 @@ fn test_non_strict_forwarding() {
43754376 let events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
43764377 expect_payment_failed_conditions_event ( events, payment_hash, false , PaymentFailedConditions :: new ( ) . blamed_scid ( routed_scid) ) ;
43774378}
4379+
4380+ #[ test]
4381+ fn remove_pending_outbounds_on_buggy_router ( ) {
4382+ // Ensure that if a payment errors due to a bogus route, we'll abandon the payment and remove the
4383+ // pending outbound from storage.
4384+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
4385+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
4386+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
4387+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
4388+ create_announced_chan_between_nodes ( & nodes, 0 , 1 ) ;
4389+
4390+ let amt_msat = 10_000 ;
4391+ let payment_id = PaymentId ( [ 42 ; 32 ] ) ;
4392+ let payment_params = PaymentParameters :: from_node_id ( nodes[ 1 ] . node . get_our_node_id ( ) , 0 )
4393+ . with_bolt11_features ( nodes[ 1 ] . node . bolt11_invoice_features ( ) ) . unwrap ( ) ;
4394+ let ( mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash ! ( nodes[ 0 ] , nodes[ 1 ] , payment_params, amt_msat) ;
4395+
4396+ // Extend the path by itself, essentially simulating route going through same channel twice
4397+ let cloned_hops = route. paths [ 0 ] . hops . clone ( ) ;
4398+ route. paths [ 0 ] . hops . extend_from_slice ( & cloned_hops) ;
4399+ let route_params = route. route_params . clone ( ) . unwrap ( ) ;
4400+ nodes[ 0 ] . router . expect_find_route ( route_params. clone ( ) , Ok ( route. clone ( ) ) ) ;
4401+
4402+ nodes[ 0 ] . node . send_payment (
4403+ payment_hash, RecipientOnionFields :: secret_only ( payment_secret) , payment_id, route_params,
4404+ Retry :: Attempts ( 1 ) // Even though another attempt is allowed, the payment should fail
4405+ ) . unwrap ( ) ;
4406+ let events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
4407+ assert_eq ! ( events. len( ) , 2 ) ;
4408+ match & events[ 0 ] {
4409+ Event :: PaymentPathFailed { failure, payment_failed_permanently, .. } => {
4410+ assert_eq ! ( failure, & PathFailure :: InitialSend {
4411+ err: APIError :: InvalidRoute { err: "Path went through the same channel twice" . to_string( ) }
4412+ } ) ;
4413+ assert ! ( !payment_failed_permanently) ;
4414+ } ,
4415+ _ => panic ! ( )
4416+ }
4417+ match events[ 1 ] {
4418+ Event :: PaymentFailed { reason, .. } => {
4419+ assert_eq ! ( reason. unwrap( ) , PaymentFailureReason :: UnexpectedError ) ;
4420+ } ,
4421+ _ => panic ! ( )
4422+ }
4423+ assert ! ( nodes[ 0 ] . node. pending_outbound_payments. pending_outbound_payments. lock( ) . unwrap( ) . is_empty( ) ) ;
4424+ }
4425+
4426+ #[ test]
4427+ fn remove_pending_outbound_probe_on_buggy_path ( ) {
4428+ // Ensure that if a probe errors due to a bogus route, we'll return an error and remove the
4429+ // pending outbound from storage.
4430+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
4431+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
4432+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
4433+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
4434+ create_announced_chan_between_nodes ( & nodes, 0 , 1 ) ;
4435+
4436+ let amt_msat = 10_000 ;
4437+ let payment_params = PaymentParameters :: from_node_id ( nodes[ 1 ] . node . get_our_node_id ( ) , 0 )
4438+ . with_bolt11_features ( nodes[ 1 ] . node . bolt11_invoice_features ( ) ) . unwrap ( ) ;
4439+ let ( mut route, _, _, _) = get_route_and_payment_hash ! ( nodes[ 0 ] , nodes[ 1 ] , payment_params, amt_msat) ;
4440+
4441+ // Extend the path by itself, essentially simulating route going through same channel twice
4442+ let cloned_hops = route. paths [ 0 ] . hops . clone ( ) ;
4443+ route. paths [ 0 ] . hops . extend_from_slice ( & cloned_hops) ;
4444+
4445+ assert_eq ! (
4446+ nodes[ 0 ] . node. send_probe( route. paths. pop( ) . unwrap( ) ) . unwrap_err( ) ,
4447+ ProbeSendFailure :: ParameterError (
4448+ APIError :: InvalidRoute { err: "Path went through the same channel twice" . to_string( ) }
4449+ )
4450+ ) ;
4451+ assert ! ( nodes[ 0 ] . node. pending_outbound_payments. pending_outbound_payments. lock( ) . unwrap( ) . is_empty( ) ) ;
4452+ }
0 commit comments