Skip to content

Commit ea38b93

Browse files
get_route: fix path_min for first_hop<>network_node candidates
Previously, we would add a first_hop<>network_node channel that did not have enough contribution amount to cover the next channel's min htlc plus fees, because we were storing the next hop as having a path_min that did not include fees, and would add a connecting first_hop node that did not have enough contribution amount, leading to a debug panic upon invalid path construction.
1 parent d83295f commit ea38b93

File tree

1 file changed

+77
-4
lines changed

1 file changed

+77
-4
lines changed

lightning/src/routing/router.rs

+77-4
Original file line numberDiff line numberDiff line change
@@ -1819,10 +1819,11 @@ where L::Target: Logger {
18191819
// might violate htlc_minimum_msat on the hops which are next along the
18201820
// payment path (upstream to the payee). To avoid that, we recompute
18211821
// path fees knowing the final path contribution after constructing it.
1822-
let path_htlc_minimum_msat = cmp::max(
1823-
compute_fees_saturating($next_hops_path_htlc_minimum_msat, $candidate.fees())
1824-
.saturating_add($next_hops_path_htlc_minimum_msat),
1825-
$candidate.htlc_minimum_msat());
1822+
let curr_min = cmp::max(
1823+
$next_hops_path_htlc_minimum_msat, $candidate.htlc_minimum_msat()
1824+
);
1825+
let path_htlc_minimum_msat = compute_fees_saturating(curr_min, $candidate.fees())
1826+
.saturating_add(curr_min);
18261827
let hm_entry = dist.entry($src_node_id);
18271828
let old_entry = hm_entry.or_insert_with(|| {
18281829
// If there was previously no known way to access the source node
@@ -7448,6 +7449,78 @@ mod tests {
74487449
assert_eq!(route.paths.len(), 1);
74497450
assert_eq!(route.get_total_amount(), amt_msat);
74507451
}
7452+
7453+
#[test]
7454+
fn candidate_path_min() {
7455+
// Test that if a candidate first_hop<>network_node channel does not have enough contribution
7456+
// amount to cover the next channel's min htlc plus fees, we will not consider that candidate.
7457+
// Previously, we were storing RouteGraphNodes with a path_min that did not include fees, and
7458+
// would add a connecting first_hop node that did not have enough contribution amount, leading
7459+
// to a debug panic upon invalid path construction.
7460+
let secp_ctx = Secp256k1::new();
7461+
let logger = Arc::new(ln_test_utils::TestLogger::new());
7462+
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, Arc::clone(&logger)));
7463+
let gossip_sync = P2PGossipSync::new(network_graph.clone(), None, logger.clone());
7464+
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), network_graph.clone(), logger.clone());
7465+
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
7466+
let random_seed_bytes = keys_manager.get_secure_random_bytes();
7467+
let config = UserConfig::default();
7468+
7469+
// Values are taken from the fuzz input that uncovered this panic.
7470+
let amt_msat = 7_4009_8048;
7471+
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
7472+
let first_hops = vec![get_channel_details(
7473+
Some(200), nodes[0], channelmanager::provided_init_features(&config), 2_7345_2000
7474+
)];
7475+
7476+
add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[6], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
7477+
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
7478+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
7479+
short_channel_id: 6,
7480+
timestamp: 1,
7481+
flags: 0,
7482+
cltv_expiry_delta: (6 << 4) | 0,
7483+
htlc_minimum_msat: 0,
7484+
htlc_maximum_msat: MAX_VALUE_MSAT,
7485+
fee_base_msat: 0,
7486+
fee_proportional_millionths: 0,
7487+
excess_data: Vec::new()
7488+
});
7489+
add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[0], NodeFeatures::from_le_bytes(id_to_feature_flags(1)), 0);
7490+
7491+
let htlc_min = 2_5165_8240;
7492+
let blinded_hints = vec![
7493+
(BlindedPayInfo {
7494+
fee_base_msat: 1_6778_3453,
7495+
fee_proportional_millionths: 0,
7496+
htlc_minimum_msat: htlc_min,
7497+
htlc_maximum_msat: htlc_min * 100,
7498+
cltv_expiry_delta: 10,
7499+
features: BlindedHopFeatures::empty(),
7500+
}, BlindedPath {
7501+
introduction_node_id: nodes[0],
7502+
blinding_point: ln_test_utils::pubkey(42),
7503+
blinded_hops: vec![
7504+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
7505+
BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() }
7506+
],
7507+
})
7508+
];
7509+
let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
7510+
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
7511+
.with_bolt12_features(bolt12_features.clone()).unwrap();
7512+
let route_params = RouteParameters::from_payment_params_and_value(
7513+
payment_params, amt_msat);
7514+
let netgraph = network_graph.read_only();
7515+
7516+
if let Err(LightningError { err, .. }) = get_route(
7517+
&our_id, &route_params, &netgraph, Some(&first_hops.iter().collect::<Vec<_>>()),
7518+
Arc::clone(&logger), &scorer, &ProbabilisticScoringFeeParameters::default(),
7519+
&random_seed_bytes
7520+
) {
7521+
assert_eq!(err, "Failed to find a path to the given destination");
7522+
} else { panic!() }
7523+
}
74517524
}
74527525

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

0 commit comments

Comments
 (0)