Skip to content

Commit 2e9c089

Browse files
Add phantom invoice route hints filtering tests
1 parent 77e5aa7 commit 2e9c089

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
@@ -349,7 +349,7 @@ mod test {
349349
use bitcoin_hashes::sha256::Hash as Sha256;
350350
use lightning::chain::keysinterface::PhantomKeysManager;
351351
use lightning::ln::{PaymentPreimage, PaymentHash};
352-
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY;
352+
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY};
353353
use lightning::ln::functional_test_utils::*;
354354
use lightning::ln::features::InitFeatures;
355355
use lightning::ln::msgs::ChannelMessageHandler;
@@ -712,4 +712,256 @@ mod test {
712712
_ => panic!("Unexpected event")
713713
}
714714
}
715+
716+
#[test]
717+
#[cfg(feature = "std")]
718+
fn test_multi_node_hints_includes_single_public_channels_to_participating_nodes() {
719+
let mut chanmon_cfgs = create_chanmon_cfgs(3);
720+
let seed_1 = [42 as u8; 32];
721+
let seed_2 = [43 as u8; 32];
722+
let cross_node_seed = [44 as u8; 32];
723+
chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
724+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
725+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
726+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
727+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
728+
let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
729+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1.1);
730+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1.0);
731+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
732+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
733+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
734+
735+
let mut short_chan_ids = HashSet::new();
736+
short_chan_ids.insert(chan_0_1.0.contents.short_channel_id.clone());
737+
short_chan_ids.insert(chan_0_2.0.contents.short_channel_id.clone());
738+
739+
match_multi_node_invoice_routes(
740+
Some(10_000),
741+
&nodes[1],
742+
vec![&nodes[1], &nodes[2],],
743+
short_chan_ids.clone(),
744+
false
745+
);
746+
}
747+
748+
#[test]
749+
#[cfg(feature = "std")]
750+
fn test_multi_node_hints_includes_one_channel_of_each_counterparty_nodes_per_participating_node() {
751+
let mut chanmon_cfgs = create_chanmon_cfgs(4);
752+
let seed_1 = [42 as u8; 32];
753+
let seed_2 = [43 as u8; 32];
754+
let cross_node_seed = [44 as u8; 32];
755+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
756+
chanmon_cfgs[3].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
757+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
758+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
759+
let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
760+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
761+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
762+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
763+
let chan_0_3 = create_announced_chan_between_nodes_with_value(&nodes, 0, 3, 1000000, 10001, InitFeatures::known(), InitFeatures::known());
764+
nodes[0].node.handle_channel_update(&nodes[3].node.get_our_node_id(), &chan_0_3.1);
765+
nodes[3].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_3.0);
766+
let chan_1_3 = create_announced_chan_between_nodes_with_value(&nodes, 1, 3, 3_000_000, 10005, InitFeatures::known(), InitFeatures::known());
767+
nodes[1].node.handle_channel_update(&nodes[3].node.get_our_node_id(), &chan_1_3.1);
768+
nodes[3].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_1_3.0);
769+
770+
let mut short_chan_ids = HashSet::new();
771+
short_chan_ids.insert(chan_0_2.0.contents.short_channel_id.clone());
772+
short_chan_ids.insert(chan_0_3.0.contents.short_channel_id.clone());
773+
short_chan_ids.insert(chan_1_3.0.contents.short_channel_id.clone());
774+
775+
match_multi_node_invoice_routes(
776+
Some(10_000),
777+
&nodes[2],
778+
vec![&nodes[2], &nodes[3],],
779+
short_chan_ids.clone(),
780+
false
781+
);
782+
}
783+
784+
#[test]
785+
#[cfg(feature = "std")]
786+
fn test_multi_node_with_private_channel_hints_includes_only_phantom_route() {
787+
let mut chanmon_cfgs = create_chanmon_cfgs(3);
788+
let seed_1 = [42 as u8; 32];
789+
let seed_2 = [43 as u8; 32];
790+
let cross_node_seed = [44 as u8; 32];
791+
chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
792+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
793+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
794+
let mut nodes_2_priv_channels_conf = UserConfig::default();
795+
nodes_2_priv_channels_conf.channel_options.announced_channel = false;
796+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, Some(nodes_2_priv_channels_conf)]);
797+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
798+
let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
799+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1.1);
800+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1.0);
801+
802+
let chan_2_0 = create_private_chan_between_nodes_with_value(&nodes, 2, 0, 100000, 10001, InitFeatures::known(), InitFeatures::known());
803+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_2_0.1);
804+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_2_0.0);
805+
806+
// Hints should include `chan_0_1` from as `nodes[1]` only have public channels, and no
807+
// channels for `nodes[2]` as it contains private channels.
808+
let mut short_chan_ids = HashSet::new();
809+
short_chan_ids.insert(chan_0_1.0.contents.short_channel_id.clone());
810+
811+
match_multi_node_invoice_routes(
812+
Some(10_000),
813+
&nodes[1],
814+
vec![&nodes[1], &nodes[2],],
815+
short_chan_ids.clone(),
816+
true
817+
);
818+
}
819+
820+
#[test]
821+
#[cfg(feature = "std")]
822+
fn test_multi_node_hints_has_only_highest_inbound_capacity_channel() {
823+
let mut chanmon_cfgs = create_chanmon_cfgs(3);
824+
let seed_1 = [42 as u8; 32];
825+
let seed_2 = [43 as u8; 32];
826+
let cross_node_seed = [44 as u8; 32];
827+
chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
828+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
829+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
830+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
831+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
832+
let chan_0_1_low_inbound_capacity = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0, InitFeatures::known(), InitFeatures::known());
833+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1_low_inbound_capacity.1);
834+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1_low_inbound_capacity.0);
835+
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());
836+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1_high_inbound_capacity.1);
837+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1_high_inbound_capacity.0);
838+
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());
839+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1_medium_inbound_capacity.1);
840+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1_medium_inbound_capacity.0);
841+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
842+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
843+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
844+
845+
let mut short_chan_ids = HashSet::new();
846+
short_chan_ids.insert(chan_0_1_high_inbound_capacity.0.contents.short_channel_id.clone());
847+
short_chan_ids.insert(chan_0_2.0.contents.short_channel_id.clone());
848+
849+
match_multi_node_invoice_routes(
850+
Some(10_000),
851+
&nodes[1],
852+
vec![&nodes[1], &nodes[2],],
853+
short_chan_ids.clone(),
854+
false
855+
);
856+
}
857+
858+
#[test]
859+
#[cfg(feature = "std")]
860+
fn test_multi_node_hints_has_no_channels_with_lower_inbound_capacity_than_invoice_amt() {
861+
let mut chanmon_cfgs = create_chanmon_cfgs(3);
862+
let seed_1 = [42 as u8; 32];
863+
let seed_2 = [43 as u8; 32];
864+
let cross_node_seed = [44 as u8; 32];
865+
chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
866+
chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
867+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
868+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
869+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
870+
let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100_000, 0, InitFeatures::known(), InitFeatures::known());
871+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1.1);
872+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1.0);
873+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
874+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
875+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
876+
877+
// 1 msat above chan_0_1's inbound capacity
878+
let mut short_chan_ids_99_000_001_msat = HashSet::new();
879+
short_chan_ids_99_000_001_msat.insert(chan_0_2.0.contents.short_channel_id.clone());
880+
881+
match_multi_node_invoice_routes(
882+
Some(99_000_001),
883+
&nodes[1],
884+
vec![&nodes[1], &nodes[2],],
885+
short_chan_ids_99_000_001_msat.clone(),
886+
false
887+
);
888+
889+
// Exactly at chan_0_1's inbound capacity
890+
let mut short_chan_ids_99_000_000_msat = HashSet::new();
891+
short_chan_ids_99_000_000_msat.insert(chan_0_1.0.contents.short_channel_id.clone());
892+
short_chan_ids_99_000_000_msat.insert(chan_0_2.0.contents.short_channel_id.clone());
893+
894+
895+
match_multi_node_invoice_routes(
896+
Some(99_000_000),
897+
&nodes[1],
898+
vec![&nodes[1], &nodes[2],],
899+
short_chan_ids_99_000_000_msat.clone(),
900+
false
901+
);
902+
903+
// Invoices with a higher amount than any of our channels' inbound capacity should result
904+
// in no hints.
905+
match_multi_node_invoice_routes(
906+
Some(99_000_000_1),
907+
&nodes[1],
908+
vec![&nodes[1], &nodes[2],],
909+
HashSet::new(),
910+
false
911+
);
912+
913+
// An invoice with no specified amount should include all participating nodes in the hints.
914+
let mut short_chan_ids_no_specified_amount = HashSet::new();
915+
short_chan_ids_no_specified_amount.insert(chan_0_1.0.contents.short_channel_id.clone());
916+
short_chan_ids_no_specified_amount.insert(chan_0_2.0.contents.short_channel_id.clone());
917+
918+
match_multi_node_invoice_routes(
919+
None,
920+
&nodes[1],
921+
vec![&nodes[1], &nodes[2],],
922+
short_chan_ids_no_specified_amount.clone(),
923+
false
924+
);
925+
}
926+
927+
#[cfg(feature = "std")]
928+
fn match_multi_node_invoice_routes<'a, 'b: 'a, 'c: 'b>(
929+
invoice_amt: Option<u64>,
930+
invoice_node: &Node<'a, 'b, 'c>,
931+
network_multi_nodes: Vec<&Node<'a, 'b, 'c>>,
932+
mut chan_ids_to_match: HashSet<u64>,
933+
nodes_contains_private_channels: bool
934+
){
935+
let (payment_hash, payment_secret) = invoice_node.node.create_inbound_payment(invoice_amt, 3600).unwrap();
936+
let phantom_route_hints = network_multi_nodes.iter()
937+
.map(|node| node.node.get_phantom_route_hints())
938+
.collect::<Vec<PhantomRouteHints>>();
939+
let phantom_scids = phantom_route_hints.iter()
940+
.map(|route_hint| route_hint.phantom_scid)
941+
.collect::<HashSet<u64>>();
942+
943+
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();
944+
945+
let invoice_hints = invoice.private_routes();
946+
947+
for hint in invoice_hints {
948+
let hints = &(hint.0).0;
949+
match hints.len() {
950+
1 => {
951+
assert!(nodes_contains_private_channels);
952+
let phantom_scid = hints[0].short_channel_id;
953+
assert!(phantom_scids.contains(&phantom_scid));
954+
},
955+
2 => {
956+
let hint_short_chan_id = hints[0].short_channel_id;
957+
assert!(chan_ids_to_match.contains(&hint_short_chan_id));
958+
chan_ids_to_match.remove(&hint_short_chan_id);
959+
let phantom_scid = hints[1].short_channel_id;
960+
assert!(phantom_scids.contains(&phantom_scid));
961+
},
962+
_ => panic!("Incorrect hint length generated")
963+
}
964+
}
965+
assert!(chan_ids_to_match.is_empty());
966+
}
715967
}

0 commit comments

Comments
 (0)