@@ -737,4 +737,197 @@ mod test {
737737 _ => panic ! ( "Unexpected event" )
738738 }
739739 }
740+
741+ #[ test]
742+ #[ cfg( feature = "std" ) ]
743+ fn test_multi_node_hints_includes_single_public_channels_to_participating_nodes ( ) {
744+ let mut chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
745+ let seed_1 = [ 42 as u8 ; 32 ] ;
746+ let seed_2 = [ 43 as u8 ; 32 ] ;
747+ let cross_node_seed = [ 44 as u8 ; 32 ] ;
748+ chanmon_cfgs[ 1 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_1, 43 , 44 , & cross_node_seed) ;
749+ chanmon_cfgs[ 2 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_2, 43 , 44 , & cross_node_seed) ;
750+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
751+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
752+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
753+ let chan_0_1 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
754+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1. 1 ) ;
755+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1. 0 ) ;
756+ let chan_0_2 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 2 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
757+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_0_2. 1 ) ;
758+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_2. 0 ) ;
759+
760+ let payment_amt = 10_000 ;
761+ let ( _, payment_hash, payment_secret) = {
762+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt) , 3600 ) . unwrap ( ) ;
763+ let payment_preimage = nodes[ 1 ] . node . get_payment_preimage ( payment_hash, payment_secret) . unwrap ( ) ;
764+ ( payment_preimage, payment_hash, payment_secret)
765+ } ;
766+ let route_hints = vec ! [
767+ nodes[ 1 ] . node. get_phantom_route_hints( ) ,
768+ nodes[ 2 ] . node. get_phantom_route_hints( ) ,
769+ ] ;
770+ let invoice = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( Some ( payment_amt) , "test" . to_string ( ) , payment_hash, payment_secret, route_hints, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
771+
772+ let hints = invoice. private_routes ( ) ;
773+ assert_eq ! ( hints. len( ) , 2 ) ;
774+
775+ let mut short_chan_ids = HashSet :: new ( ) ;
776+ short_chan_ids. insert ( chan_0_1. 0 . contents . short_channel_id . clone ( ) ) ;
777+ short_chan_ids. insert ( chan_0_2. 0 . contents . short_channel_id . clone ( ) ) ;
778+ for hint in hints {
779+ let hint_short_chan_id = ( hint. 0 ) . 0 [ 0 ] . short_channel_id ;
780+ assert ! ( short_chan_ids. contains( & hint_short_chan_id) ) ;
781+ short_chan_ids. remove ( & hint_short_chan_id) ;
782+ }
783+ }
784+
785+ #[ test]
786+ #[ cfg( feature = "std" ) ]
787+ fn test_multi_node_hints_has_only_highest_inbound_capacity_channel ( ) {
788+ let mut chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
789+ let seed_1 = [ 42 as u8 ; 32 ] ;
790+ let seed_2 = [ 43 as u8 ; 32 ] ;
791+ let cross_node_seed = [ 44 as u8 ; 32 ] ;
792+ chanmon_cfgs[ 1 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_1, 43 , 44 , & cross_node_seed) ;
793+ chanmon_cfgs[ 2 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_2, 43 , 44 , & cross_node_seed) ;
794+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
795+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
796+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
797+ let chan_0_1_low_inbound_capacity = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 100_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
798+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1_low_inbound_capacity. 1 ) ;
799+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1_low_inbound_capacity. 0 ) ;
800+ 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 ( ) ) ;
801+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1_high_inbound_capacity. 1 ) ;
802+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1_high_inbound_capacity. 0 ) ;
803+ 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 ( ) ) ;
804+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1_medium_inbound_capacity. 1 ) ;
805+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1_medium_inbound_capacity. 0 ) ;
806+ let chan_0_2 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 2 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
807+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_0_2. 1 ) ;
808+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_2. 0 ) ;
809+
810+ let payment_amt = 10_000 ;
811+ let ( _, payment_hash, payment_secret) = {
812+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt) , 3600 ) . unwrap ( ) ;
813+ let payment_preimage = nodes[ 1 ] . node . get_payment_preimage ( payment_hash, payment_secret) . unwrap ( ) ;
814+ ( payment_preimage, payment_hash, payment_secret)
815+ } ;
816+ let route_hints = vec ! [
817+ nodes[ 1 ] . node. get_phantom_route_hints( ) ,
818+ nodes[ 2 ] . node. get_phantom_route_hints( ) ,
819+ ] ;
820+ let invoice = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( Some ( payment_amt) , "test" . to_string ( ) , payment_hash, payment_secret, route_hints, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
821+
822+ let hints = invoice. private_routes ( ) ;
823+ assert_eq ! ( hints. len( ) , 2 ) ;
824+
825+ let mut short_chan_ids = HashSet :: new ( ) ;
826+ short_chan_ids. insert ( chan_0_1_high_inbound_capacity. 0 . contents . short_channel_id . clone ( ) ) ;
827+ short_chan_ids. insert ( chan_0_2. 0 . contents . short_channel_id . clone ( ) ) ;
828+ for hint in hints {
829+ let hint_short_chan_id = ( hint. 0 ) . 0 [ 0 ] . short_channel_id ;
830+ assert ! ( short_chan_ids. contains( & hint_short_chan_id) ) ;
831+ short_chan_ids. remove ( & hint_short_chan_id) ;
832+ }
833+ }
834+
835+ #[ test]
836+ #[ cfg( feature = "std" ) ]
837+ fn test_multi_node_hints_has_no_channels_with_lower_inbound_capacity_than_invoice_amt ( ) {
838+ let mut chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
839+ let seed_1 = [ 42 as u8 ; 32 ] ;
840+ let seed_2 = [ 43 as u8 ; 32 ] ;
841+ let cross_node_seed = [ 44 as u8 ; 32 ] ;
842+ chanmon_cfgs[ 1 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_1, 43 , 44 , & cross_node_seed) ;
843+ chanmon_cfgs[ 2 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_2, 43 , 44 , & cross_node_seed) ;
844+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
845+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
846+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
847+ let chan_0_1 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 100_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
848+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1. 1 ) ;
849+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1. 0 ) ;
850+ let chan_0_2 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 2 , 1_000_000 , 0 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
851+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_0_2. 1 ) ;
852+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_2. 0 ) ;
853+
854+ // 1 msat above chan_0_1's inbound capacity
855+ let payment_amt_99_000_001_msat = 99_000_001 ;
856+ let ( _, payment_hash_99_000_001_msat, payment_secret_99_000_001_msat) = {
857+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt_99_000_001_msat) , 3600 ) . unwrap ( ) ;
858+ let payment_preimage = nodes[ 1 ] . node . get_payment_preimage ( payment_hash, payment_secret) . unwrap ( ) ;
859+ ( payment_preimage, payment_hash, payment_secret)
860+ } ;
861+
862+ let route_hints_99_000_001_msat = vec ! [
863+ nodes[ 1 ] . node. get_phantom_route_hints( ) ,
864+ nodes[ 2 ] . node. get_phantom_route_hints( ) ,
865+ ] ;
866+
867+ let invoice_99_000_001_msat = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( Some ( payment_amt_99_000_001_msat) , "test" . to_string ( ) , payment_hash_99_000_001_msat, payment_secret_99_000_001_msat, route_hints_99_000_001_msat, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
868+
869+ let hints_99_000_001_msat = invoice_99_000_001_msat. private_routes ( ) ;
870+ assert_eq ! ( hints_99_000_001_msat. len( ) , 1 ) ;
871+
872+ let mut short_chan_ids_99_000_001_msat = HashSet :: new ( ) ;
873+ short_chan_ids_99_000_001_msat. insert ( chan_0_2. 0 . contents . short_channel_id . clone ( ) ) ;
874+ for hint in hints_99_000_001_msat {
875+ let hint_short_chan_id = ( hint. 0 ) . 0 [ 0 ] . short_channel_id ;
876+ assert ! ( short_chan_ids_99_000_001_msat. contains( & hint_short_chan_id) ) ;
877+ short_chan_ids_99_000_001_msat. remove ( & hint_short_chan_id) ;
878+ }
879+
880+ // Exactly at chan_0_1's inbound capacity
881+ let payment_amt_99_000_000_msat = 99_000_000 ;
882+ let ( _, payment_hash_99_000_000_msat, payment_secret_99_000_000_msat) = {
883+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt_99_000_000_msat) , 3600 ) . unwrap ( ) ;
884+ let payment_preimage = nodes[ 1 ] . node . get_payment_preimage ( payment_hash, payment_secret) . unwrap ( ) ;
885+ ( payment_preimage, payment_hash, payment_secret)
886+ } ;
887+
888+ let route_hints_99_000_000_msat = vec ! [
889+ nodes[ 1 ] . node. get_phantom_route_hints( ) ,
890+ nodes[ 2 ] . node. get_phantom_route_hints( ) ,
891+ ] ;
892+
893+ let invoice_99_000_000_msat = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( Some ( payment_amt_99_000_000_msat) , "test" . to_string ( ) , payment_hash_99_000_000_msat, payment_secret_99_000_000_msat, route_hints_99_000_000_msat, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
894+
895+ let hints_99_000_000_msat = invoice_99_000_000_msat. private_routes ( ) ;
896+ assert_eq ! ( hints_99_000_000_msat. len( ) , 2 ) ;
897+
898+ let mut short_chan_ids_99_000_000_msat = HashSet :: new ( ) ;
899+ short_chan_ids_99_000_000_msat. insert ( chan_0_1. 0 . contents . short_channel_id . clone ( ) ) ;
900+ short_chan_ids_99_000_000_msat. insert ( chan_0_2. 0 . contents . short_channel_id . clone ( ) ) ;
901+ for hint in hints_99_000_000_msat {
902+ let hint_short_chan_id = ( hint. 0 ) . 0 [ 0 ] . short_channel_id ;
903+ assert ! ( short_chan_ids_99_000_000_msat. contains( & hint_short_chan_id) ) ;
904+ short_chan_ids_99_000_000_msat. remove ( & hint_short_chan_id) ;
905+ }
906+
907+ // An invoice with no specified amount should include all participating nodes in the hints.
908+ let ( _, payment_hash_no_specified_amount, payment_secret_no_specified_amount) = {
909+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( None , 3600 ) . unwrap ( ) ;
910+ let payment_preimage = nodes[ 1 ] . node . get_payment_preimage ( payment_hash, payment_secret) . unwrap ( ) ;
911+ ( payment_preimage, payment_hash, payment_secret)
912+ } ;
913+
914+ let route_hints_no_specified_amount = vec ! [
915+ nodes[ 1 ] . node. get_phantom_route_hints( ) ,
916+ nodes[ 2 ] . node. get_phantom_route_hints( ) ,
917+ ] ;
918+
919+ let invoice_no_specified_amount = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( None , "test" . to_string ( ) , payment_hash_no_specified_amount, payment_secret_no_specified_amount, route_hints_no_specified_amount, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
920+
921+ let hints_no_specified_amount = invoice_no_specified_amount. private_routes ( ) ;
922+ assert_eq ! ( hints_no_specified_amount. len( ) , 2 ) ;
923+
924+ let mut short_chan_ids_no_specified_amount = HashSet :: new ( ) ;
925+ short_chan_ids_no_specified_amount. insert ( chan_0_1. 0 . contents . short_channel_id . clone ( ) ) ;
926+ short_chan_ids_no_specified_amount. insert ( chan_0_2. 0 . contents . short_channel_id . clone ( ) ) ;
927+ for hint in hints_no_specified_amount {
928+ let hint_short_chan_id = ( hint. 0 ) . 0 [ 0 ] . short_channel_id ;
929+ assert ! ( short_chan_ids_no_specified_amount. contains( & hint_short_chan_id) ) ;
930+ short_chan_ids_no_specified_amount. remove ( & hint_short_chan_id) ;
931+ }
932+ }
740933}
0 commit comments