Skip to content

Commit a0d7cc7

Browse files
Pathfinding: ignore blinded route hints where we are the intro node
See tests, but the fuzzer found several panics from not fully ignoring these hints. We should support these route hints eventually, but it will involve some reworking of the Path/BlindedTail structs.
1 parent 91a5363 commit a0d7cc7

File tree

1 file changed

+111
-1
lines changed

1 file changed

+111
-1
lines changed

lightning/src/routing/router.rs

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2042,7 +2042,7 @@ where L::Target: Logger {
20422042
// in the regular network graph.
20432043
first_hop_targets.get(&intro_node_id).is_some() ||
20442044
network_nodes.get(&intro_node_id).is_some();
2045-
if !have_intro_node_in_graph { continue }
2045+
if !have_intro_node_in_graph || our_node_id == intro_node_id { continue }
20462046
let candidate = if hint.1.blinded_hops.len() == 1 {
20472047
CandidateRouteHop::OneHopBlinded { hint, hint_idx }
20482048
} else { CandidateRouteHop::Blinded { hint, hint_idx } };
@@ -7075,6 +7075,116 @@ mod tests {
70757075
assert_eq!(route.get_total_fees(), blinded_payinfo.fee_base_msat as u64);
70767076
assert_eq!(route.get_total_amount(), amt_msat);
70777077
}
7078+
7079+
#[test]
7080+
fn we_are_intro_node_candidate_hops() {
7081+
// This previously led to a panic in the router because we'd generate a Path with only a
7082+
// BlindedTail and 0 unblinded hops, due to the only candidate hops being blinded route hints
7083+
// where the origin node is the intro node. We now fully disallow considering candidate hops
7084+
// where the origin node is the intro node.
7085+
let (secp_ctx, network_graph, _, _, logger) = build_graph();
7086+
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
7087+
let scorer = ln_test_utils::TestScorer::new();
7088+
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
7089+
let random_seed_bytes = keys_manager.get_secure_random_bytes();
7090+
let config = UserConfig::default();
7091+
7092+
// Values are taken from the fuzz input that uncovered this panic.
7093+
let amt_msat = 21_7020_5185_1423_0019;
7094+
7095+
let blinded_path = BlindedPath {
7096+
introduction_node_id: our_id,
7097+
blinding_point: ln_test_utils::pubkey(42),
7098+
blinded_hops: vec![
7099+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
7100+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() }
7101+
],
7102+
};
7103+
let blinded_payinfo = BlindedPayInfo {
7104+
fee_base_msat: 5052_9027,
7105+
fee_proportional_millionths: 5052_9027,
7106+
htlc_minimum_msat: 21_7020_5185_1423_0019,
7107+
htlc_maximum_msat: 1844_6744_0737_0955_1615,
7108+
cltv_expiry_delta: 0,
7109+
features: BlindedHopFeatures::empty(),
7110+
};
7111+
let mut blinded_hints = vec![
7112+
(blinded_payinfo.clone(), blinded_path.clone()),
7113+
(blinded_payinfo.clone(), blinded_path.clone()),
7114+
];
7115+
blinded_hints[1].1.introduction_node_id = nodes[6];
7116+
7117+
let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
7118+
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
7119+
.with_bolt12_features(bolt12_features.clone()).unwrap();
7120+
7121+
let netgraph = network_graph.read_only();
7122+
let route_params = RouteParameters::from_payment_params_and_value(
7123+
payment_params, amt_msat);
7124+
if let Err(LightningError { err, .. }) = get_route(
7125+
&our_id, &route_params, &netgraph, None, Arc::clone(&logger), &scorer, &(), &random_seed_bytes
7126+
) {
7127+
assert_eq!(err, "Failed to find a path to the given destination");
7128+
} else { panic!() }
7129+
}
7130+
7131+
#[test]
7132+
fn we_are_intro_node_bp_in_final_path_fee_calc() {
7133+
// This previously led to a debug panic in the router because we'd find an invalid Path with
7134+
// 0 unblinded hops and a blinded tail, leading to the generation of a final
7135+
// PaymentPathHop::fee_msat that included both the blinded path fees and the final value of
7136+
// the payment, when it was intended to only include the final value of the payment.
7137+
let (secp_ctx, network_graph, _, _, logger) = build_graph();
7138+
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
7139+
let scorer = ln_test_utils::TestScorer::new();
7140+
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
7141+
let random_seed_bytes = keys_manager.get_secure_random_bytes();
7142+
let config = UserConfig::default();
7143+
7144+
// Values are taken from the fuzz input that uncovered this panic.
7145+
let amt_msat = 21_7020_5185_1423_0019;
7146+
7147+
let blinded_path = BlindedPath {
7148+
introduction_node_id: our_id,
7149+
blinding_point: ln_test_utils::pubkey(42),
7150+
blinded_hops: vec![
7151+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
7152+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() }
7153+
],
7154+
};
7155+
let blinded_payinfo = BlindedPayInfo {
7156+
fee_base_msat: 10_4425_1395,
7157+
fee_proportional_millionths: 1_6973_9011,
7158+
htlc_minimum_msat: 21_7301_9934_9094_0931,
7159+
htlc_maximum_msat: 1844_6744_0737_0955_1615,
7160+
cltv_expiry_delta: 0,
7161+
features: BlindedHopFeatures::empty(),
7162+
};
7163+
let mut blinded_hints = vec![
7164+
(blinded_payinfo.clone(), blinded_path.clone()),
7165+
(blinded_payinfo.clone(), blinded_path.clone()),
7166+
(blinded_payinfo.clone(), blinded_path.clone()),
7167+
];
7168+
blinded_hints[1].0.fee_base_msat = 5052_9027;
7169+
blinded_hints[1].0.fee_proportional_millionths = 5052_9027;
7170+
blinded_hints[1].0.htlc_minimum_msat = 21_7020_5185_1423_0019;
7171+
blinded_hints[1].0.htlc_maximum_msat = 1844_6744_0737_0955_1615;
7172+
7173+
blinded_hints[2].1.introduction_node_id = nodes[6];
7174+
7175+
let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
7176+
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
7177+
.with_bolt12_features(bolt12_features.clone()).unwrap();
7178+
7179+
let netgraph = network_graph.read_only();
7180+
let route_params = RouteParameters::from_payment_params_and_value(
7181+
payment_params, amt_msat);
7182+
if let Err(LightningError { err, .. }) = get_route(
7183+
&our_id, &route_params, &netgraph, None, Arc::clone(&logger), &scorer, &(), &random_seed_bytes
7184+
) {
7185+
assert_eq!(err, "Failed to find a path to the given destination");
7186+
} else { panic!() }
7187+
}
70787188
}
70797189

70807190
#[cfg(all(any(test, ldk_bench), not(feature = "no-std")))]

0 commit comments

Comments
 (0)