@@ -19,7 +19,7 @@ use ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
19
19
use ln:: msgs:: { DecodeError , ErrorAction , LightningError , MAX_VALUE_MSAT } ;
20
20
use routing:: scoring:: { ChannelUsage , Score } ;
21
21
use routing:: network_graph:: { DirectedChannelInfoWithUpdate , EffectiveCapacity , NetworkGraph , ReadOnlyNetworkGraph , NodeId , RoutingFees } ;
22
- use util:: ser:: { Writeable , Readable } ;
22
+ use util:: ser:: { Writeable , Readable , Writer } ;
23
23
use util:: logger:: { Level , Logger } ;
24
24
use util:: chacha20:: ChaCha20 ;
25
25
@@ -151,8 +151,8 @@ impl Readable for Route {
151
151
152
152
/// Parameters needed to find a [`Route`].
153
153
///
154
- /// Passed to [`find_route`] and also provided in [`Event::PaymentPathFailed`] for retrying a failed
155
- /// payment path.
154
+ /// Passed to [`find_route`] and [`build_route_from_hops`], but also provided in
155
+ /// [`Event::PaymentPathFailed`] for retrying a failed payment path.
156
156
///
157
157
/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
158
158
#[ derive( Clone , Debug ) ]
@@ -676,16 +676,11 @@ pub fn find_route<L: Deref, S: Score>(
676
676
) -> Result < Route , LightningError >
677
677
where L :: Target : Logger {
678
678
let network_graph = network. read_only ( ) ;
679
- match get_route (
680
- our_node_pubkey, & route_params. payment_params , & network_graph, first_hops, route_params. final_value_msat ,
681
- route_params. final_cltv_expiry_delta , logger, scorer, random_seed_bytes
682
- ) {
683
- Ok ( mut route) => {
684
- add_random_cltv_offset ( & mut route, & route_params. payment_params , & network_graph, random_seed_bytes) ;
685
- Ok ( route)
686
- } ,
687
- Err ( err) => Err ( err) ,
688
- }
679
+ let mut route = get_route ( our_node_pubkey, & route_params. payment_params , & network_graph, first_hops,
680
+ route_params. final_value_msat , route_params. final_cltv_expiry_delta , logger, scorer,
681
+ random_seed_bytes) ?;
682
+ add_random_cltv_offset ( & mut route, & route_params. payment_params , & network_graph, random_seed_bytes) ;
683
+ Ok ( route)
689
684
}
690
685
691
686
pub ( crate ) fn get_route < L : Deref , S : Score > (
@@ -1703,7 +1698,9 @@ where L::Target: Logger {
1703
1698
// destination, if the remaining CLTV expiry delta exactly matches a feasible path in the network
1704
1699
// graph. In order to improve privacy, this method obfuscates the CLTV expiry deltas along the
1705
1700
// payment path by adding a randomized 'shadow route' offset to the final hop.
1706
- fn add_random_cltv_offset ( route : & mut Route , payment_params : & PaymentParameters , network_graph : & ReadOnlyNetworkGraph , random_seed_bytes : & [ u8 ; 32 ] ) {
1701
+ fn add_random_cltv_offset ( route : & mut Route , payment_params : & PaymentParameters ,
1702
+ network_graph : & ReadOnlyNetworkGraph , random_seed_bytes : & [ u8 ; 32 ]
1703
+ ) {
1707
1704
let network_channels = network_graph. channels ( ) ;
1708
1705
let network_nodes = network_graph. nodes ( ) ;
1709
1706
@@ -1785,10 +1782,72 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters,
1785
1782
}
1786
1783
}
1787
1784
1785
+ /// Build a route from us (payer) with the given hops ending at the target node (payee), which
1786
+ /// may be used to simply probe the given path.
1787
+ ///
1788
+ /// Re-uses logic from `find_route`, so the restrictions described there also apply here.
1789
+ pub fn build_route_from_hops < L : Deref > (
1790
+ our_node_pubkey : & PublicKey , hops : & [ PublicKey ] , route_params : & RouteParameters , network : & NetworkGraph ,
1791
+ logger : L , random_seed_bytes : & [ u8 ; 32 ]
1792
+ ) -> Result < Route , LightningError >
1793
+ where L :: Target : Logger {
1794
+ let network_graph = network. read_only ( ) ;
1795
+ let mut route = build_route_from_hops_internal (
1796
+ our_node_pubkey, hops, & route_params. payment_params , & network_graph,
1797
+ route_params. final_value_msat , route_params. final_cltv_expiry_delta , logger, random_seed_bytes) ?;
1798
+ add_random_cltv_offset ( & mut route, & route_params. payment_params , & network_graph, random_seed_bytes) ;
1799
+ Ok ( route)
1800
+ }
1801
+
1802
+ fn build_route_from_hops_internal < L : Deref > (
1803
+ our_node_pubkey : & PublicKey , hops : & [ PublicKey ] , payment_params : & PaymentParameters ,
1804
+ network_graph : & ReadOnlyNetworkGraph , final_value_msat : u64 , final_cltv_expiry_delta : u32 ,
1805
+ logger : L , random_seed_bytes : & [ u8 ; 32 ]
1806
+ ) -> Result < Route , LightningError > where L :: Target : Logger {
1807
+
1808
+ struct HopScorer < ' a > {
1809
+ our_node_pubkey : & ' a PublicKey ,
1810
+ hops : & ' a [ PublicKey ] ,
1811
+ }
1812
+
1813
+ impl Score for HopScorer < ' _ > {
1814
+ fn channel_penalty_msat ( & self , _short_channel_id : u64 , source : & NodeId , target : & NodeId ,
1815
+ _usage : ChannelUsage ) -> u64
1816
+ {
1817
+ let mut cur_id = NodeId :: from_pubkey ( self . our_node_pubkey ) ;
1818
+ for i in 0 ..self . hops . len ( ) {
1819
+ let next_id = NodeId :: from_pubkey ( & self . hops [ i] ) ;
1820
+ if cur_id == * source && next_id == * target {
1821
+ return 0 ;
1822
+ }
1823
+ cur_id = next_id;
1824
+ }
1825
+ u64:: max_value ( )
1826
+ }
1827
+
1828
+ fn payment_path_failed ( & mut self , _path : & [ & RouteHop ] , _short_channel_id : u64 ) { }
1829
+
1830
+ fn payment_path_successful ( & mut self , _path : & [ & RouteHop ] ) { }
1831
+ }
1832
+
1833
+ impl < ' a > Writeable for HopScorer < ' a > {
1834
+ #[ inline]
1835
+ fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
1836
+ write_tlv_fields ! ( w, { } ) ;
1837
+ Ok ( ( ) )
1838
+ }
1839
+ }
1840
+
1841
+ let scorer = HopScorer { our_node_pubkey, hops } ;
1842
+
1843
+ get_route ( our_node_pubkey, payment_params, network_graph, None , final_value_msat,
1844
+ final_cltv_expiry_delta, logger, & scorer, random_seed_bytes)
1845
+ }
1846
+
1788
1847
#[ cfg( test) ]
1789
1848
mod tests {
1790
1849
use routing:: network_graph:: { NetworkGraph , NetGraphMsgHandler , NodeId } ;
1791
- use routing:: router:: { get_route, add_random_cltv_offset, default_node_features,
1850
+ use routing:: router:: { get_route, build_route_from_hops_internal , add_random_cltv_offset, default_node_features,
1792
1851
PaymentParameters , Route , RouteHint , RouteHintHop , RouteHop , RoutingFees ,
1793
1852
DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA , MAX_PATH_LENGTH_ESTIMATE } ;
1794
1853
use routing:: scoring:: { ChannelUsage , Score } ;
@@ -5486,6 +5545,26 @@ mod tests {
5486
5545
assert ! ( path_plausibility. iter( ) . all( |x| * x) ) ;
5487
5546
}
5488
5547
5548
+ #[ test]
5549
+ fn builds_correct_path_from_hops ( ) {
5550
+ let ( secp_ctx, network, _, _, logger) = build_graph ( ) ;
5551
+ let ( _, our_id, _, nodes) = get_nodes ( & secp_ctx) ;
5552
+ let network_graph = network. read_only ( ) ;
5553
+
5554
+ let keys_manager = test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
5555
+ let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
5556
+
5557
+ let payment_params = PaymentParameters :: from_node_id ( nodes[ 3 ] ) ;
5558
+ let hops = [ nodes[ 1 ] , nodes[ 2 ] , nodes[ 4 ] , nodes[ 3 ] ] ;
5559
+ let route = build_route_from_hops_internal ( & our_id, & hops, & payment_params,
5560
+ & network_graph, 100 , 0 , Arc :: clone ( & logger) , & random_seed_bytes) . unwrap ( ) ;
5561
+ let route_hop_pubkeys = route. paths [ 0 ] . iter ( ) . map ( |hop| hop. pubkey ) . collect :: < Vec < _ > > ( ) ;
5562
+ assert_eq ! ( hops. len( ) , route. paths[ 0 ] . len( ) ) ;
5563
+ for ( idx, hop_pubkey) in hops. iter ( ) . enumerate ( ) {
5564
+ assert ! ( * hop_pubkey == route_hop_pubkeys[ idx] ) ;
5565
+ }
5566
+ }
5567
+
5489
5568
#[ cfg( not( feature = "no-std" ) ) ]
5490
5569
pub ( super ) fn random_init_seed ( ) -> u64 {
5491
5570
// Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
0 commit comments