Skip to content

Commit 154cd3c

Browse files
committed
Test we prefer first hops over route hints
We previously added logic that would avoid adding superflous candidates for route hints if we detect that we have a first hop for this channel. Here we add test coverage that we actually prefer the first hop over the route hint, but still consider the remaining hints.
1 parent 5d5d640 commit 154cd3c

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

lightning/src/routing/router.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7695,6 +7695,152 @@ mod tests {
76957695
assert_eq!(route.paths.len(), 1);
76967696
assert_eq!(route.get_total_amount(), amt_msat);
76977697
}
7698+
7699+
#[test]
7700+
fn first_hop_preferred_over_hint() {
7701+
// Check that if we have a first hop to a peer we'd always prefer that over a route hint
7702+
// they gave us, but we'd still consider all subsequent hints if they are more attractive.
7703+
let secp_ctx = Secp256k1::new();
7704+
let logger = Arc::new(ln_test_utils::TestLogger::new());
7705+
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, Arc::clone(&logger)));
7706+
let gossip_sync = P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger));
7707+
let scorer = ln_test_utils::TestScorer::new();
7708+
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
7709+
let random_seed_bytes = keys_manager.get_secure_random_bytes();
7710+
let config = UserConfig::default();
7711+
7712+
let amt_msat = 1_000_000;
7713+
let (our_privkey, our_node_id, privkeys, nodes) = get_nodes(&secp_ctx);
7714+
7715+
add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[0],
7716+
ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1);
7717+
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
7718+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
7719+
short_channel_id: 1,
7720+
timestamp: 1,
7721+
flags: 0,
7722+
cltv_expiry_delta: 42,
7723+
htlc_minimum_msat: 1_000,
7724+
htlc_maximum_msat: 10_000_000,
7725+
fee_base_msat: 800,
7726+
fee_proportional_millionths: 0,
7727+
excess_data: Vec::new()
7728+
});
7729+
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
7730+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
7731+
short_channel_id: 1,
7732+
timestamp: 1,
7733+
flags: 1,
7734+
cltv_expiry_delta: 42,
7735+
htlc_minimum_msat: 1_000,
7736+
htlc_maximum_msat: 10_000_000,
7737+
fee_base_msat: 800,
7738+
fee_proportional_millionths: 0,
7739+
excess_data: Vec::new()
7740+
});
7741+
7742+
add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[1],
7743+
ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 2);
7744+
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
7745+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
7746+
short_channel_id: 2,
7747+
timestamp: 2,
7748+
flags: 0,
7749+
cltv_expiry_delta: 42,
7750+
htlc_minimum_msat: 1_000,
7751+
htlc_maximum_msat: 10_000_000,
7752+
fee_base_msat: 800,
7753+
fee_proportional_millionths: 0,
7754+
excess_data: Vec::new()
7755+
});
7756+
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
7757+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
7758+
short_channel_id: 2,
7759+
timestamp: 2,
7760+
flags: 1,
7761+
cltv_expiry_delta: 42,
7762+
htlc_minimum_msat: 1_000,
7763+
htlc_maximum_msat: 10_000_000,
7764+
fee_base_msat: 800,
7765+
fee_proportional_millionths: 0,
7766+
excess_data: Vec::new()
7767+
});
7768+
7769+
let dest_node_id = nodes[2];
7770+
7771+
let route_hint = RouteHint(vec![RouteHintHop {
7772+
src_node_id: our_node_id,
7773+
short_channel_id: 44,
7774+
fees: RoutingFees {
7775+
base_msat: 234,
7776+
proportional_millionths: 0,
7777+
},
7778+
cltv_expiry_delta: 10,
7779+
htlc_minimum_msat: None,
7780+
htlc_maximum_msat: Some(5_000_000),
7781+
},
7782+
RouteHintHop {
7783+
src_node_id: nodes[0],
7784+
short_channel_id: 45,
7785+
fees: RoutingFees {
7786+
base_msat: 123,
7787+
proportional_millionths: 0,
7788+
},
7789+
cltv_expiry_delta: 10,
7790+
htlc_minimum_msat: None,
7791+
htlc_maximum_msat: None,
7792+
}]);
7793+
7794+
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
7795+
.with_route_hints(vec![route_hint]).unwrap()
7796+
.with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
7797+
let route_params = RouteParameters::from_payment_params_and_value(
7798+
payment_params, amt_msat);
7799+
7800+
// First create an insufficient first hop for channel with SCID 1 and check we'd use the
7801+
// route hint.
7802+
let first_hop = get_channel_details(Some(1), nodes[0],
7803+
channelmanager::provided_init_features(&config), 999_999);
7804+
let first_hops = vec![first_hop];
7805+
7806+
let route = get_route(&our_node_id, &route_params.clone(), &network_graph.read_only(),
7807+
Some(&first_hops.iter().collect::<Vec<_>>()), Arc::clone(&logger), &scorer,
7808+
&Default::default(), &random_seed_bytes).unwrap();
7809+
assert_eq!(route.paths.len(), 1);
7810+
assert_eq!(route.get_total_amount(), amt_msat);
7811+
assert_eq!(route.paths[0].hops.len(), 2);
7812+
assert_eq!(route.paths[0].hops[0].short_channel_id, 44);
7813+
assert_eq!(route.paths[0].hops[1].short_channel_id, 45);
7814+
assert_eq!(route.get_total_fees(), 123);
7815+
7816+
// Now check we would trust our first hop info, i.e., fail if we detect the route hint is
7817+
// for a first hop channel.
7818+
let mut first_hop = get_channel_details(Some(1), nodes[0], channelmanager::provided_init_features(&config), 999_999);
7819+
first_hop.outbound_scid_alias = Some(44);
7820+
let first_hops = vec![first_hop];
7821+
7822+
let route_res = get_route(&our_node_id, &route_params.clone(), &network_graph.read_only(),
7823+
Some(&first_hops.iter().collect::<Vec<_>>()), Arc::clone(&logger), &scorer,
7824+
&Default::default(), &random_seed_bytes);
7825+
assert!(route_res.is_err());
7826+
7827+
// Finally check we'd use the first hop if has sufficient outbound capacity. But we'd stil
7828+
// use the cheaper second hop of the route hint.
7829+
let mut first_hop = get_channel_details(Some(1), nodes[0],
7830+
channelmanager::provided_init_features(&config), 10_000_000);
7831+
first_hop.outbound_scid_alias = Some(44);
7832+
let first_hops = vec![first_hop];
7833+
7834+
let route = get_route(&our_node_id, &route_params.clone(), &network_graph.read_only(),
7835+
Some(&first_hops.iter().collect::<Vec<_>>()), Arc::clone(&logger), &scorer,
7836+
&Default::default(), &random_seed_bytes).unwrap();
7837+
assert_eq!(route.paths.len(), 1);
7838+
assert_eq!(route.get_total_amount(), amt_msat);
7839+
assert_eq!(route.paths[0].hops.len(), 2);
7840+
assert_eq!(route.paths[0].hops[0].short_channel_id, 1);
7841+
assert_eq!(route.paths[0].hops[1].short_channel_id, 45);
7842+
assert_eq!(route.get_total_fees(), 123);
7843+
}
76987844
}
76997845

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

0 commit comments

Comments
 (0)