Skip to content

Commit fd43e2d

Browse files
Add phantom invoice route hints filtering tests
1 parent ff44017 commit fd43e2d

File tree

1 file changed

+253
-1
lines changed

1 file changed

+253
-1
lines changed

lightning-invoice/src/utils.rs

Lines changed: 253 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ mod test {
338338
use bitcoin_hashes::sha256::Hash as Sha256;
339339
use lightning::chain::keysinterface::PhantomKeysManager;
340340
use lightning::ln::{PaymentPreimage, PaymentHash};
341-
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY;
341+
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY};
342342
use lightning::ln::functional_test_utils::*;
343343
use lightning::ln::features::InitFeatures;
344344
use lightning::ln::msgs::ChannelMessageHandler;
@@ -661,4 +661,256 @@ mod test {
661661
_ => panic!("Unexpected event")
662662
}
663663
}
664+
665+
#[test]
666+
#[cfg(feature = "std")]
667+
fn test_multi_node_hints_includes_single_public_channels_to_participating_nodes() {
668+
let mut chanmon_cfgs = create_chanmon_cfgs(3);
669+
let seed_1 = [42 as u8; 32];
670+
let seed_2 = [43 as u8; 32];
671+
let cross_node_seed = [44 as u8; 32];
672+
chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
673+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
674+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
675+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
676+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
677+
let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
678+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1.1);
679+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1.0);
680+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
681+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
682+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
683+
684+
let mut short_chan_ids = HashSet::new();
685+
short_chan_ids.insert(chan_0_1.0.contents.short_channel_id.clone());
686+
short_chan_ids.insert(chan_0_2.0.contents.short_channel_id.clone());
687+
688+
match_multi_node_invoice_routes(
689+
Some(10_000),
690+
&nodes[1],
691+
vec![&nodes[1], &nodes[2],],
692+
short_chan_ids.clone(),
693+
false
694+
);
695+
}
696+
697+
#[test]
698+
#[cfg(feature = "std")]
699+
fn test_multi_node_hints_includes_one_channel_of_each_counterparty_nodes_per_participating_node() {
700+
let mut chanmon_cfgs = create_chanmon_cfgs(4);
701+
let seed_1 = [42 as u8; 32];
702+
let seed_2 = [43 as u8; 32];
703+
let cross_node_seed = [44 as u8; 32];
704+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
705+
chanmon_cfgs[3].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
706+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
707+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
708+
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
709+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
710+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
711+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
712+
let chan_0_3 = create_announced_chan_between_nodes_with_value(&nodes, 0, 3, 1000000, 10001, InitFeatures::known(), InitFeatures::known());
713+
nodes[0].node.handle_channel_update(&nodes[3].node.get_our_node_id(), &chan_0_3.1);
714+
nodes[3].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_3.0);
715+
let chan_1_3 = create_announced_chan_between_nodes_with_value(&nodes, 1, 3, 3_000_000, 10005, InitFeatures::known(), InitFeatures::known());
716+
nodes[1].node.handle_channel_update(&nodes[3].node.get_our_node_id(), &chan_1_3.1);
717+
nodes[3].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_1_3.0);
718+
719+
let mut short_chan_ids = HashSet::new();
720+
short_chan_ids.insert(chan_0_2.0.contents.short_channel_id.clone());
721+
short_chan_ids.insert(chan_0_3.0.contents.short_channel_id.clone());
722+
short_chan_ids.insert(chan_1_3.0.contents.short_channel_id.clone());
723+
724+
match_multi_node_invoice_routes(
725+
Some(10_000),
726+
&nodes[2],
727+
vec![&nodes[2], &nodes[3],],
728+
short_chan_ids.clone(),
729+
false
730+
);
731+
}
732+
733+
#[test]
734+
#[cfg(feature = "std")]
735+
fn test_multi_node_with_private_channel_hints_includes_only_phantom_route() {
736+
let mut chanmon_cfgs = create_chanmon_cfgs(3);
737+
let seed_1 = [42 as u8; 32];
738+
let seed_2 = [43 as u8; 32];
739+
let cross_node_seed = [44 as u8; 32];
740+
chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
741+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
742+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
743+
let mut nodes_2_priv_channels_conf = UserConfig::default();
744+
nodes_2_priv_channels_conf.channel_options.announced_channel = false;
745+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, Some(nodes_2_priv_channels_conf)]);
746+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
747+
let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
748+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1.1);
749+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1.0);
750+
751+
let chan_2_0 = create_private_chan_between_nodes_with_value(&nodes, 2, 0, 100000, 10001, InitFeatures::known(), InitFeatures::known());
752+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_2_0.1);
753+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_2_0.0);
754+
755+
// Hints should include `chan_0_1` from as `nodes[1]` only have public channels, and no
756+
// channels for `nodes[2]` as it contains private channels.
757+
let mut short_chan_ids = HashSet::new();
758+
short_chan_ids.insert(chan_0_1.0.contents.short_channel_id.clone());
759+
760+
match_multi_node_invoice_routes(
761+
Some(10_000),
762+
&nodes[1],
763+
vec![&nodes[1], &nodes[2],],
764+
short_chan_ids.clone(),
765+
true
766+
);
767+
}
768+
769+
#[test]
770+
#[cfg(feature = "std")]
771+
fn test_multi_node_hints_has_only_highest_inbound_capacity_channel() {
772+
let mut chanmon_cfgs = create_chanmon_cfgs(3);
773+
let seed_1 = [42 as u8; 32];
774+
let seed_2 = [43 as u8; 32];
775+
let cross_node_seed = [44 as u8; 32];
776+
chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
777+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
778+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
779+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
780+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
781+
let chan_0_1_low_inbound_capacity = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0, InitFeatures::known(), InitFeatures::known());
782+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1_low_inbound_capacity.1);
783+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1_low_inbound_capacity.0);
784+
let chan_0_1_high_inbound_capacity = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 0, InitFeatures::known(), InitFeatures::known());
785+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1_high_inbound_capacity.1);
786+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1_high_inbound_capacity.0);
787+
let chan_0_1_medium_inbound_capacity = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
788+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1_medium_inbound_capacity.1);
789+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1_medium_inbound_capacity.0);
790+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
791+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
792+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
793+
794+
let mut short_chan_ids = HashSet::new();
795+
short_chan_ids.insert(chan_0_1_high_inbound_capacity.0.contents.short_channel_id.clone());
796+
short_chan_ids.insert(chan_0_2.0.contents.short_channel_id.clone());
797+
798+
match_multi_node_invoice_routes(
799+
Some(10_000),
800+
&nodes[1],
801+
vec![&nodes[1], &nodes[2],],
802+
short_chan_ids.clone(),
803+
false
804+
);
805+
}
806+
807+
#[test]
808+
#[cfg(feature = "std")]
809+
fn test_multi_node_hints_has_no_channels_with_lower_inbound_capacity_than_invoice_amt() {
810+
let mut chanmon_cfgs = create_chanmon_cfgs(3);
811+
let seed_1 = [42 as u8; 32];
812+
let seed_2 = [43 as u8; 32];
813+
let cross_node_seed = [44 as u8; 32];
814+
chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
815+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
816+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
817+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
818+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
819+
let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0, InitFeatures::known(), InitFeatures::known());
820+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1.1);
821+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1.0);
822+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
823+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
824+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
825+
826+
// 1 msat above chan_0_1's inbound capacity
827+
let mut short_chan_ids_99_000_001_msat = HashSet::new();
828+
short_chan_ids_99_000_001_msat.insert(chan_0_2.0.contents.short_channel_id.clone());
829+
830+
match_multi_node_invoice_routes(
831+
Some(99_000_001),
832+
&nodes[1],
833+
vec![&nodes[1], &nodes[2],],
834+
short_chan_ids_99_000_001_msat.clone(),
835+
false
836+
);
837+
838+
// Exactly at chan_0_1's inbound capacity
839+
let mut short_chan_ids_99_000_000_msat = HashSet::new();
840+
short_chan_ids_99_000_000_msat.insert(chan_0_1.0.contents.short_channel_id.clone());
841+
short_chan_ids_99_000_000_msat.insert(chan_0_2.0.contents.short_channel_id.clone());
842+
843+
844+
match_multi_node_invoice_routes(
845+
Some(99_000_000),
846+
&nodes[1],
847+
vec![&nodes[1], &nodes[2],],
848+
short_chan_ids_99_000_000_msat.clone(),
849+
false
850+
);
851+
852+
// Invoices with a higher amount than any of our channels' inbound capacity should result
853+
// in no hints.
854+
match_multi_node_invoice_routes(
855+
Some(99_000_000_1),
856+
&nodes[1],
857+
vec![&nodes[1], &nodes[2],],
858+
HashSet::new(),
859+
false
860+
);
861+
862+
// An invoice with no specified amount should include all participating nodes in the hints.
863+
let mut short_chan_ids_no_specified_amount = HashSet::new();
864+
short_chan_ids_no_specified_amount.insert(chan_0_1.0.contents.short_channel_id.clone());
865+
short_chan_ids_no_specified_amount.insert(chan_0_2.0.contents.short_channel_id.clone());
866+
867+
match_multi_node_invoice_routes(
868+
None,
869+
&nodes[1],
870+
vec![&nodes[1], &nodes[2],],
871+
short_chan_ids_no_specified_amount.clone(),
872+
false
873+
);
874+
}
875+
876+
#[cfg(feature = "std")]
877+
fn match_multi_node_invoice_routes<'a, 'b: 'a, 'c: 'b>(
878+
invoice_amt: Option<u64>,
879+
invoice_node: &Node<'a, 'b, 'c>,
880+
network_multi_nodes: Vec<&Node<'a, 'b, 'c>>,
881+
mut chan_ids_to_match: HashSet<u64>,
882+
nodes_contains_private_channels: bool
883+
){
884+
let (payment_hash, payment_secret) = invoice_node.node.create_inbound_payment(invoice_amt, 3600).unwrap();
885+
let phantom_route_hints = network_multi_nodes.iter()
886+
.map(|node| node.node.get_phantom_route_hints())
887+
.collect::<Vec<PhantomRouteHints>>();
888+
let phantom_scids = phantom_route_hints.iter()
889+
.map(|route_hint| route_hint.phantom_scid)
890+
.collect::<HashSet<u64>>();
891+
892+
let invoice = ::utils::create_phantom_invoice::<EnforcingSigner, &test_utils::TestKeysInterface>(invoice_amt, "test".to_string(), payment_hash, payment_secret, phantom_route_hints, &invoice_node.keys_manager, Currency::BitcoinTestnet).unwrap();
893+
894+
let invoice_hints = invoice.private_routes();
895+
896+
for hint in invoice_hints {
897+
let hints = &(hint.0).0;
898+
match hints.len() {
899+
1 => {
900+
assert!(nodes_contains_private_channels);
901+
let phantom_scid = hints[0].short_channel_id;
902+
assert!(phantom_scids.contains(&phantom_scid));
903+
},
904+
2 => {
905+
let hint_short_chan_id = hints[0].short_channel_id;
906+
assert!(chan_ids_to_match.contains(&hint_short_chan_id));
907+
chan_ids_to_match.remove(&hint_short_chan_id);
908+
let phantom_scid = hints[1].short_channel_id;
909+
assert!(phantom_scids.contains(&phantom_scid));
910+
},
911+
_ => panic!("Incorrect hint length generated")
912+
}
913+
}
914+
assert!(chan_ids_to_match.is_empty());
915+
}
664916
}

0 commit comments

Comments
 (0)