Skip to content

Commit 31bf6b1

Browse files
Router: fix blinded route hint path_min_htlc calc
See previous commit, we had a bug where we would find invalid paths due to underestimating the min_htlc of downstream hops when adding upstream route candidates.
1 parent 46a5414 commit 31bf6b1

File tree

1 file changed

+103
-61
lines changed

1 file changed

+103
-61
lines changed

lightning/src/routing/router.rs

Lines changed: 103 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,9 +2061,19 @@ where L::Target: Logger {
20612061
Some(fee) => fee,
20622062
None => continue
20632063
};
2064+
let path_min = {
2065+
let blinded_path_min = candidate.htlc_minimum_msat();
2066+
let path_min_fees = match compute_fees(blinded_path_min, candidate.fees()) {
2067+
Some(fee) => fee,
2068+
None => continue
2069+
};
2070+
match blinded_path_min.checked_add(path_min_fees) {
2071+
Some(min) => min,
2072+
None => continue
2073+
}
2074+
};
20642075
add_entry!(first_hop_candidate, our_node_id, intro_node_id, blinded_path_fee,
2065-
path_contribution_msat, candidate.htlc_minimum_msat(), 0_u64,
2066-
candidate.cltv_expiry_delta(),
2076+
path_contribution_msat, path_min, 0_u64, candidate.cltv_expiry_delta(),
20672077
candidate.blinded_path().map_or(1, |bp| bp.blinded_hops.len() as u8));
20682078
}
20692079
}
@@ -7190,6 +7200,10 @@ mod tests {
71907200

71917201
#[test]
71927202
fn min_htlc_overpay_violates_max_htlc() {
7203+
do_min_htlc_overpay_violates_max_htlc(true);
7204+
do_min_htlc_overpay_violates_max_htlc(false);
7205+
}
7206+
fn do_min_htlc_overpay_violates_max_htlc(blinded_payee: bool) {
71937207
// Test that if overpaying to meet a later hop's min_htlc and causes us to violate an earlier
71947208
// hop's max_htlc, we discard that invalid path. Previously we would consider the path to be
71957209
// valid and hit a debug panic asserting that the used liquidity for a hop was less than its
@@ -7211,21 +7225,45 @@ mod tests {
72117225
first_hop_outbound_capacity
72127226
)];
72137227

7214-
let route_hint = RouteHint(vec![RouteHintHop {
7215-
src_node_id: nodes[0],
7216-
short_channel_id: 44,
7217-
fees: RoutingFees {
7218-
base_msat: 1_6778_3453,
7219-
proportional_millionths: 0,
7220-
},
7221-
cltv_expiry_delta: 10,
7222-
htlc_minimum_msat: Some(2_5165_8240),
7223-
htlc_maximum_msat: None,
7224-
}]);
7228+
let base_fee = 1_6778_3453;
7229+
let htlc_min = 2_5165_8240;
7230+
let payment_params = if blinded_payee {
7231+
let blinded_path = BlindedPath {
7232+
introduction_node_id: nodes[0],
7233+
blinding_point: ln_test_utils::pubkey(42),
7234+
blinded_hops: vec![
7235+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
7236+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() }
7237+
],
7238+
};
7239+
let blinded_payinfo = BlindedPayInfo {
7240+
fee_base_msat: base_fee,
7241+
fee_proportional_millionths: 0,
7242+
htlc_minimum_msat: htlc_min,
7243+
htlc_maximum_msat: htlc_min * 1000,
7244+
cltv_expiry_delta: 0,
7245+
features: BlindedHopFeatures::empty(),
7246+
};
7247+
let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
7248+
PaymentParameters::blinded(vec![(blinded_payinfo, blinded_path)])
7249+
.with_bolt12_features(bolt12_features.clone()).unwrap()
7250+
} else {
7251+
let route_hint = RouteHint(vec![RouteHintHop {
7252+
src_node_id: nodes[0],
7253+
short_channel_id: 44,
7254+
fees: RoutingFees {
7255+
base_msat: base_fee,
7256+
proportional_millionths: 0,
7257+
},
7258+
cltv_expiry_delta: 10,
7259+
htlc_minimum_msat: Some(htlc_min),
7260+
htlc_maximum_msat: None,
7261+
}]);
72257262

7226-
let payment_params = PaymentParameters::from_node_id(nodes[1], 42)
7227-
.with_route_hints(vec![route_hint]).unwrap()
7228-
.with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
7263+
PaymentParameters::from_node_id(nodes[1], 42)
7264+
.with_route_hints(vec![route_hint]).unwrap()
7265+
.with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
7266+
};
72297267

72307268
let netgraph = network_graph.read_only();
72317269
let route_params = RouteParameters::from_payment_params_and_value(
@@ -7240,6 +7278,11 @@ mod tests {
72407278

72417279
#[test]
72427280
fn previously_used_liquidity_violates_max_htlc() {
7281+
do_previously_used_liquidity_violates_max_htlc(true);
7282+
do_previously_used_liquidity_violates_max_htlc(false);
7283+
7284+
}
7285+
fn do_previously_used_liquidity_violates_max_htlc(blinded_payee: bool) {
72437286
// Test that if a candidate hop would cause us to violate an upstream hop's available
72447287
// contribution amount due to a previously found path, we will not consider that candidate in
72457288
// path construction. Previously we would construct an invalid path and hit a debug panic
@@ -7261,51 +7304,50 @@ mod tests {
72617304
Some(122), nodes[0], channelmanager::provided_init_features(&config), 179_5000
72627305
)];
72637306

7264-
let route_hints = vec![RouteHint(vec![RouteHintHop {
7265-
src_node_id: nodes[0],
7266-
short_channel_id: 42,
7267-
fees: RoutingFees {
7268-
base_msat: 0,
7269-
proportional_millionths: 0,
7270-
},
7271-
cltv_expiry_delta: 10,
7272-
htlc_minimum_msat: Some(1_4392),
7273-
htlc_maximum_msat: Some(143_9200),
7274-
}]), RouteHint(vec![RouteHintHop {
7275-
src_node_id: nodes[0],
7276-
short_channel_id: 43,
7277-
fees: RoutingFees {
7278-
base_msat: 425_9840,
7279-
proportional_millionths: 0,
7280-
},
7281-
cltv_expiry_delta: 10,
7282-
htlc_minimum_msat: Some(19_7401),
7283-
htlc_maximum_msat: Some(1974_0100),
7284-
}]), RouteHint(vec![RouteHintHop {
7285-
src_node_id: nodes[0],
7286-
short_channel_id: 44,
7287-
fees: RoutingFees {
7288-
base_msat: 0,
7289-
proportional_millionths: 0,
7290-
},
7291-
cltv_expiry_delta: 10,
7292-
htlc_minimum_msat: Some(1027),
7293-
htlc_maximum_msat: Some(10_2700),
7294-
}]), RouteHint(vec![RouteHintHop {
7295-
src_node_id: nodes[0],
7296-
short_channel_id: 45,
7297-
fees: RoutingFees {
7298-
base_msat: 0,
7299-
proportional_millionths: 0,
7300-
},
7301-
cltv_expiry_delta: 10,
7302-
htlc_minimum_msat: Some(6_5535),
7303-
htlc_maximum_msat: Some(655_3500),
7304-
}])];
7305-
7306-
let payment_params = PaymentParameters::from_node_id(nodes[1], 42)
7307-
.with_route_hints(route_hints).unwrap()
7308-
.with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
7307+
let base_fees = [0, 425_9840, 0, 0];
7308+
let htlc_mins = [1_4392, 19_7401, 1027, 6_5535];
7309+
let payment_params = if blinded_payee {
7310+
let blinded_path = BlindedPath {
7311+
introduction_node_id: nodes[0],
7312+
blinding_point: ln_test_utils::pubkey(42),
7313+
blinded_hops: vec![
7314+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
7315+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() }
7316+
],
7317+
};
7318+
let mut blinded_hints = Vec::new();
7319+
for (base_fee, htlc_min) in base_fees.iter().zip(htlc_mins.iter()) {
7320+
blinded_hints.push((BlindedPayInfo {
7321+
fee_base_msat: *base_fee,
7322+
fee_proportional_millionths: 0,
7323+
htlc_minimum_msat: *htlc_min,
7324+
htlc_maximum_msat: htlc_min * 100,
7325+
cltv_expiry_delta: 10,
7326+
features: BlindedHopFeatures::empty(),
7327+
}, blinded_path.clone()));
7328+
}
7329+
let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
7330+
PaymentParameters::blinded(blinded_hints.clone())
7331+
.with_bolt12_features(bolt12_features.clone()).unwrap()
7332+
} else {
7333+
let mut route_hints = Vec::new();
7334+
for (idx, (base_fee, htlc_min)) in base_fees.iter().zip(htlc_mins.iter()).enumerate() {
7335+
route_hints.push(RouteHint(vec![RouteHintHop {
7336+
src_node_id: nodes[0],
7337+
short_channel_id: 42 + idx as u64,
7338+
fees: RoutingFees {
7339+
base_msat: *base_fee,
7340+
proportional_millionths: 0,
7341+
},
7342+
cltv_expiry_delta: 10,
7343+
htlc_minimum_msat: Some(*htlc_min),
7344+
htlc_maximum_msat: Some(htlc_min * 100),
7345+
}]));
7346+
}
7347+
PaymentParameters::from_node_id(nodes[1], 42)
7348+
.with_route_hints(route_hints).unwrap()
7349+
.with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
7350+
};
73097351

73107352
let netgraph = network_graph.read_only();
73117353
let route_params = RouteParameters::from_payment_params_and_value(

0 commit comments

Comments
 (0)