Skip to content

Commit ef2a657

Browse files
committed
Add tests for create invoice route hints filtering
1 parent 779887d commit ef2a657

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed

lightning-invoice/src/utils.rs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,9 @@ mod test {
338338
use lightning::util::enforcing_trait_impls::EnforcingSigner;
339339
use lightning::util::events::{MessageSendEvent, MessageSendEventsProvider, Event};
340340
use lightning::util::test_utils;
341+
use lightning::util::config::UserConfig;
341342
use utils::create_invoice_from_channelmanager_and_duration_since_epoch;
343+
use std::collections::HashSet;
342344

343345
#[test]
344346
fn test_from_channelmanager() {
@@ -393,6 +395,182 @@ mod test {
393395
assert_eq!(events.len(), 2);
394396
}
395397

398+
#[test]
399+
fn test_hints_includes_single_public_channels_to_nodes() {
400+
let chanmon_cfgs = create_chanmon_cfgs(3);
401+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
402+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
403+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
404+
let chan_1_0 = create_announced_chan_between_nodes_with_value(&nodes, 1, 0, 100000, 10001, InitFeatures::known(), InitFeatures::known());
405+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_1_0.0);
406+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_1_0.1);
407+
let chan_2_0 = create_announced_chan_between_nodes_with_value(&nodes, 2, 0, 100000, 10001, InitFeatures::known(), InitFeatures::known());
408+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_2_0.0);
409+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_2_0.1);
410+
411+
let invoice = create_invoice_from_channelmanager_and_duration_since_epoch(
412+
&nodes[0].node, nodes[0].keys_manager, Currency::BitcoinTestnet, Some(5000), "test".to_string(),
413+
Duration::from_secs(1234567)).unwrap();
414+
let hints = invoice.private_routes();
415+
416+
assert_eq!(hints.len(), 2);
417+
418+
let mut short_chan_ids = HashSet::new();
419+
short_chan_ids.insert(chan_1_0.0.contents.short_channel_id.clone());
420+
short_chan_ids.insert(chan_2_0.0.contents.short_channel_id.clone());
421+
for hint in hints {
422+
let hint_short_chan_id = (hint.0).0[0].short_channel_id;
423+
assert!(short_chan_ids.contains(&hint_short_chan_id));
424+
short_chan_ids.remove(&hint_short_chan_id);
425+
}
426+
}
427+
428+
#[test]
429+
fn test_hints_has_only_highest_inbound_capacity_channel() {
430+
let chanmon_cfgs = create_chanmon_cfgs(2);
431+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
432+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
433+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
434+
let chan_1_0_low_inbound_capacity = create_announced_chan_between_nodes_with_value(&nodes, 1, 0, 100_000, 0, InitFeatures::known(), InitFeatures::known());
435+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_1_0_low_inbound_capacity.0);
436+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_1_0_low_inbound_capacity.1);
437+
let chan_1_0_high_inbound_capacity = create_announced_chan_between_nodes_with_value(&nodes, 1, 0, 10_000_000, 0, InitFeatures::known(), InitFeatures::known());
438+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_1_0_high_inbound_capacity.0);
439+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_1_0_high_inbound_capacity.1);
440+
let chan_1_0_medium_inbound_capacity = create_announced_chan_between_nodes_with_value(&nodes, 1, 0, 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
441+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_1_0_medium_inbound_capacity.0);
442+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_1_0_medium_inbound_capacity.1);
443+
444+
let invoice = create_invoice_from_channelmanager_and_duration_since_epoch(
445+
&nodes[0].node, nodes[0].keys_manager, Currency::BitcoinTestnet, Some(5000), "test".to_string(),
446+
Duration::from_secs(1234567)).unwrap();
447+
let hints = invoice.private_routes();
448+
449+
assert_eq!(hints.len(), 1);
450+
451+
let mut short_chan_ids = HashSet::new();
452+
short_chan_ids.insert(chan_1_0_high_inbound_capacity.0.contents.short_channel_id.clone());
453+
for hint in hints {
454+
let hint_short_chan_id = (hint.0).0[0].short_channel_id;
455+
assert!(short_chan_ids.contains(&hint_short_chan_id));
456+
short_chan_ids.remove(&hint_short_chan_id);
457+
}
458+
}
459+
460+
#[test]
461+
fn test_no_hints_if_any_private_channels_exists() {
462+
let mut nodes_2_priv_channels_conf = UserConfig::default();
463+
nodes_2_priv_channels_conf.channel_options.announced_channel = false;
464+
let chanmon_cfgs = create_chanmon_cfgs(3);
465+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
466+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, Some(nodes_2_priv_channels_conf)]);
467+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
468+
let chan_1_0 = create_announced_chan_between_nodes_with_value(&nodes, 1, 0, 100000, 10001, InitFeatures::known(), InitFeatures::known());
469+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_1_0.0);
470+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_1_0.1);
471+
472+
// Creates a private channel between `nodes[2]` and `nodes[0]`. Note that create_*_chan
473+
// functions in utils can't be used, as they require announcement_signatures.
474+
nodes[2].node.create_channel(nodes[0].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
475+
let open_channel = get_event_msg!(nodes[2], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id());
476+
nodes[0].node.handle_open_channel(&nodes[2].node.get_our_node_id(), InitFeatures::known(), &open_channel);
477+
let accept_channel = get_event_msg!(nodes[0], MessageSendEvent::SendAcceptChannel, nodes[2].node.get_our_node_id());
478+
nodes[2].node.handle_accept_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &accept_channel);
479+
480+
let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[2], 100000, 42);
481+
nodes[2].node.funding_transaction_generated(&temporary_channel_id, tx.clone()).unwrap();
482+
nodes[0].node.handle_funding_created(&nodes[2].node.get_our_node_id(), &get_event_msg!(nodes[2], MessageSendEvent::SendFundingCreated, nodes[0].node.get_our_node_id()));
483+
check_added_monitors!(nodes[0], 1);
484+
485+
let cs_funding_signed = get_event_msg!(nodes[0], MessageSendEvent::SendFundingSigned, nodes[2].node.get_our_node_id());
486+
nodes[2].node.handle_funding_signed(&nodes[0].node.get_our_node_id(), &cs_funding_signed);
487+
check_added_monitors!(nodes[2], 1);
488+
489+
let conf_height = core::cmp::max(nodes[2].best_block_info().1 + 1, nodes[0].best_block_info().1 + 1);
490+
confirm_transaction_at(&nodes[2], &tx, conf_height);
491+
connect_blocks(&nodes[2], CHAN_CONFIRM_DEPTH - 1);
492+
confirm_transaction_at(&nodes[0], &tx, conf_height);
493+
connect_blocks(&nodes[0], CHAN_CONFIRM_DEPTH - 1);
494+
let as_funding_locked = get_event_msg!(nodes[2], MessageSendEvent::SendFundingLocked, nodes[0].node.get_our_node_id());
495+
nodes[2].node.handle_funding_locked(&nodes[0].node.get_our_node_id(), &get_event_msg!(nodes[0], MessageSendEvent::SendFundingLocked, nodes[2].node.get_our_node_id()));
496+
get_event_msg!(nodes[2], MessageSendEvent::SendChannelUpdate, nodes[0].node.get_our_node_id());
497+
nodes[0].node.handle_funding_locked(&nodes[2].node.get_our_node_id(), &as_funding_locked);
498+
get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[2].node.get_our_node_id());
499+
500+
// Ensure that the invoice doesn't include any route hints for any of `nodes[0]` channels,
501+
// even though all channels between `nodes[1]` and `nodes[0]` are public.
502+
let invoice = create_invoice_from_channelmanager_and_duration_since_epoch(
503+
&nodes[0].node, nodes[0].keys_manager, Currency::BitcoinTestnet, Some(5000), "test".to_string(),
504+
Duration::from_secs(1234567)).unwrap();
505+
let hints = invoice.private_routes();
506+
507+
assert!(hints.is_empty());
508+
}
509+
510+
#[test]
511+
fn test_hints_has_no_channels_with_lower_inbound_capacity_than_invoice_amt() {
512+
let chanmon_cfgs = create_chanmon_cfgs(3);
513+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
514+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
515+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
516+
let chan_1_0 = create_announced_chan_between_nodes_with_value(&nodes, 1, 0, 100_000, 0, InitFeatures::known(), InitFeatures::known());
517+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_1_0.0);
518+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_1_0.1);
519+
let chan_2_0 = create_announced_chan_between_nodes_with_value(&nodes, 2, 0, 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
520+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_2_0.0);
521+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_2_0.1);
522+
523+
// 1 msat above chan_1_0's inbound capacity
524+
let invoice_99_000_001_msat = create_invoice_from_channelmanager_and_duration_since_epoch(
525+
&nodes[0].node, nodes[0].keys_manager, Currency::BitcoinTestnet, Some(99_000_001), "test".to_string(),
526+
Duration::from_secs(1234567)).unwrap();
527+
let hints_99_000_001_msat = invoice_99_000_001_msat.private_routes();
528+
529+
assert_eq!(hints_99_000_001_msat.len(), 1);
530+
531+
let mut short_chan_ids_99_000_001_msat = HashSet::new();
532+
short_chan_ids_99_000_001_msat.insert(chan_2_0.0.contents.short_channel_id.clone());
533+
for hint in hints_99_000_001_msat {
534+
let hint_short_chan_id = (hint.0).0[0].short_channel_id;
535+
assert!(short_chan_ids_99_000_001_msat.contains(&hint_short_chan_id));
536+
short_chan_ids_99_000_001_msat.remove(&hint_short_chan_id);
537+
}
538+
539+
// Exactly at chan_1_0's inbound capacity
540+
let invoice_99_000_000_msat = create_invoice_from_channelmanager_and_duration_since_epoch(
541+
&nodes[0].node, nodes[0].keys_manager, Currency::BitcoinTestnet, Some(99_000_000), "test".to_string(),
542+
Duration::from_secs(1234567)).unwrap();
543+
let hints_99_000_000_msat = invoice_99_000_000_msat.private_routes();
544+
545+
assert_eq!(hints_99_000_000_msat.len(), 2);
546+
547+
let mut short_chan_ids_99_000_000_msat = HashSet::new();
548+
short_chan_ids_99_000_000_msat.insert(chan_1_0.0.contents.short_channel_id.clone());
549+
short_chan_ids_99_000_000_msat.insert(chan_2_0.0.contents.short_channel_id.clone());
550+
for hint in hints_99_000_000_msat {
551+
let hint_short_chan_id = (hint.0).0[0].short_channel_id;
552+
assert!(short_chan_ids_99_000_000_msat.contains(&hint_short_chan_id));
553+
short_chan_ids_99_000_000_msat.remove(&hint_short_chan_id);
554+
}
555+
556+
// An invoice with no specified amount should include all route hints.
557+
let invoice_no_specified_amount = create_invoice_from_channelmanager_and_duration_since_epoch(
558+
&nodes[0].node, nodes[0].keys_manager, Currency::BitcoinTestnet, None, "test".to_string(),
559+
Duration::from_secs(1234567)).unwrap();
560+
let hints_no_specified_amount = invoice_no_specified_amount.private_routes();
561+
562+
assert_eq!(hints_no_specified_amount.len(), 2);
563+
564+
let mut short_chan_ids_no_specified_amount = HashSet::new();
565+
short_chan_ids_no_specified_amount.insert(chan_1_0.0.contents.short_channel_id.clone());
566+
short_chan_ids_no_specified_amount.insert(chan_2_0.0.contents.short_channel_id.clone());
567+
for hint in hints_no_specified_amount {
568+
let hint_short_chan_id = (hint.0).0[0].short_channel_id;
569+
assert!(short_chan_ids_no_specified_amount.contains(&hint_short_chan_id));
570+
short_chan_ids_no_specified_amount.remove(&hint_short_chan_id);
571+
}
572+
}
573+
396574
#[test]
397575
#[cfg(feature = "std")]
398576
fn test_multi_node_receive() {

0 commit comments

Comments
 (0)