@@ -5,22 +5,127 @@ use payment::{Payer, Router};
55
66use bech32:: ToBase32 ;
77use bitcoin_hashes:: Hash ;
8+ use bitcoin_hashes:: sha256:: Hash as Sha256 ;
89use crate :: prelude:: * ;
910use lightning:: chain;
1011use lightning:: chain:: chaininterface:: { BroadcasterInterface , FeeEstimator } ;
1112use lightning:: chain:: keysinterface:: { Sign , KeysInterface } ;
1213use lightning:: ln:: { PaymentHash , PaymentPreimage , PaymentSecret } ;
13- use lightning:: ln:: channelmanager:: { ChannelDetails , ChannelManager , PaymentId , PaymentSendFailure , MIN_FINAL_CLTV_EXPIRY } ;
14+ use lightning:: ln:: channelmanager:: { ChannelDetails , ChannelManager , PaymentId , PaymentSendFailure , MIN_FINAL_CLTV_EXPIRY , MIN_CLTV_EXPIRY_DELTA } ;
1415use lightning:: ln:: msgs:: LightningError ;
1516use lightning:: routing:: scoring:: Score ;
1617use lightning:: routing:: network_graph:: { NetworkGraph , RoutingFees } ;
1718use lightning:: routing:: router:: { Route , RouteHint , RouteHintHop , RouteParameters , find_route} ;
1819use lightning:: util:: logger:: Logger ;
20+ use secp256k1:: Secp256k1 ;
1921use secp256k1:: key:: PublicKey ;
2022use core:: convert:: TryInto ;
2123use core:: ops:: Deref ;
2224use core:: time:: Duration ;
2325
26+ #[ cfg( feature = "std" ) ]
27+ /// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
28+ /// This works because the invoice officially pays to a fake node (the "phantom"). Useful for
29+ /// load-balancing payments between multiple nodes.
30+ ///
31+ /// `channels` parameter:
32+ /// * Contains a list of tuples of `(phantom_node_scid, real_node_pubkey,
33+ /// real_channel_details)`, where `phantom_node_scid` is unique to each `channels` entry
34+ /// * `phantom_node_scid`s are retrieved from [`ChannelManager::get_phantom_scid`]
35+ /// * `real_channel_details` are retrieved from [`ChannelManager::list_channels`]
36+ /// * Contains channel info for all nodes participating in the phantom invoice
37+ /// * For increased privacy, it is ideal but not required to generate new `phantom_node_scid`s for
38+ /// each new invoice
39+ /// * It is fine to cache this information and reuse it across invoices, as long as you periodically
40+ /// check that no provided real channels have become unavailable
41+ /// * Note that including too many channels here could result in making the invoice too long for QR
42+ /// code scanning
43+ ///
44+ /// `payment_hash` and `payment_secret` come from [`ChannelManager::create_inbound_payment`] or
45+ /// [`ChannelManager::create_inbound_payment_for_hash`].
46+ ///
47+ /// See [`KeysInterface::get_phantom_secret`] for more requirements on supporting phantom node
48+ /// payments.
49+ ///
50+ /// [`KeysInterface::get_phantom_secret`]: lightning::chain::keysinterface::KeysInterface::get_phantom_secret
51+ pub fn create_phantom_invoice < Signer : Sign , K : Deref > (
52+ amt_msat : Option < u64 > , description : String , payment_hash : PaymentHash , payment_secret :
53+ PaymentSecret , channels : & [ ( u64 , PublicKey , & ChannelDetails ) ] , keys_manager : K , network : Currency
54+ ) -> Result < Invoice , SignOrCreationError < ( ) > > where K :: Target : KeysInterface {
55+ if channels. len ( ) == 0 {
56+ return Err ( SignOrCreationError :: CreationError ( CreationError :: MissingRouteHints ) )
57+ }
58+ let phantom_secret = match keys_manager. get_phantom_secret ( channels[ 0 ] . 0 ) {
59+ Ok ( s) => s,
60+ Err ( ( ) ) => return Err ( SignOrCreationError :: CreationError ( CreationError :: InvalidPhantomScid ) )
61+ } ;
62+ let mut route_hints = vec ! [ ] ;
63+ for ( phantom_scid, real_node_pubkey, channel) in channels {
64+ let short_channel_id = match channel. short_channel_id {
65+ Some ( id) => id,
66+ None => continue ,
67+ } ;
68+ let forwarding_info = match & channel. counterparty . forwarding_info {
69+ Some ( info) => info. clone ( ) ,
70+ None => continue ,
71+ } ;
72+ route_hints. push ( RouteHint ( vec ! [
73+ RouteHintHop {
74+ src_node_id: channel. counterparty. node_id,
75+ short_channel_id,
76+ fees: RoutingFees {
77+ base_msat: forwarding_info. fee_base_msat,
78+ proportional_millionths: forwarding_info. fee_proportional_millionths,
79+ } ,
80+ cltv_expiry_delta: forwarding_info. cltv_expiry_delta,
81+ htlc_minimum_msat: None ,
82+ htlc_maximum_msat: None ,
83+ } ,
84+ RouteHintHop {
85+ src_node_id: * real_node_pubkey,
86+ short_channel_id: * phantom_scid,
87+ fees: RoutingFees {
88+ base_msat: 0 ,
89+ proportional_millionths: 0 ,
90+ } ,
91+ cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA ,
92+ htlc_minimum_msat: None ,
93+ htlc_maximum_msat: None ,
94+ } ] )
95+ ) ;
96+ }
97+ let phantom_pubkey = PublicKey :: from_secret_key ( & Secp256k1 :: new ( ) , & phantom_secret) ;
98+ let mut invoice = InvoiceBuilder :: new ( network)
99+ . description ( description)
100+ . current_timestamp ( )
101+ . payee_pub_key ( phantom_pubkey)
102+ . payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
103+ . payment_secret ( payment_secret)
104+ . min_final_cltv_expiry ( MIN_FINAL_CLTV_EXPIRY . into ( ) ) ;
105+ if let Some ( amt) = amt_msat {
106+ invoice = invoice. amount_milli_satoshis ( amt) ;
107+ }
108+ for hint in route_hints {
109+ invoice = invoice. private_route ( hint) ;
110+ }
111+
112+ let raw_invoice = match invoice. build_raw ( ) {
113+ Ok ( inv) => inv,
114+ Err ( e) => return Err ( SignOrCreationError :: CreationError ( e) )
115+ } ;
116+ let hrp_str = raw_invoice. hrp . to_string ( ) ;
117+ let hrp_bytes = hrp_str. as_bytes ( ) ;
118+ let data_without_signature = raw_invoice. data . to_base32 ( ) ;
119+ let invoice_preimage = RawInvoice :: construct_invoice_preimage ( hrp_bytes, & data_without_signature) ;
120+ let secp_ctx = Secp256k1 :: new ( ) ;
121+ let invoice_preimage_msg = secp256k1:: Message :: from_slice ( & Sha256 :: hash ( & invoice_preimage) ) . unwrap ( ) ;
122+ let signed_raw_invoice = raw_invoice. sign ( |_| Ok ( secp_ctx. sign_recoverable ( & invoice_preimage_msg, & phantom_secret) ) ) ;
123+ match signed_raw_invoice {
124+ Ok ( inv) => Ok ( Invoice :: from_signed ( inv) . unwrap ( ) ) ,
125+ Err ( e) => Err ( SignOrCreationError :: SignError ( e) )
126+ }
127+ }
128+
24129#[ cfg( feature = "std" ) ]
25130/// Utility to construct an invoice. Generally, unless you want to do something like a custom
26131/// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
@@ -193,15 +298,20 @@ where
193298mod test {
194299 use core:: time:: Duration ;
195300 use { Currency , Description , InvoiceDescription } ;
196- use lightning:: ln:: PaymentHash ;
197- use lightning:: ln:: channelmanager:: MIN_FINAL_CLTV_EXPIRY ;
301+ use bitcoin_hashes:: Hash ;
302+ use bitcoin_hashes:: sha256:: Hash as Sha256 ;
303+ use lightning:: chain:: keysinterface:: KeysManager ;
304+ use lightning:: ln:: { PaymentPreimage , PaymentHash } ;
305+ use lightning:: ln:: channelmanager:: { ChannelDetails , MIN_FINAL_CLTV_EXPIRY } ;
198306 use lightning:: ln:: functional_test_utils:: * ;
199307 use lightning:: ln:: features:: InitFeatures ;
200308 use lightning:: ln:: msgs:: ChannelMessageHandler ;
201309 use lightning:: routing:: router:: { Payee , RouteParameters , find_route} ;
202- use lightning:: util:: events:: MessageSendEventsProvider ;
310+ use lightning:: util:: enforcing_trait_impls:: EnforcingSigner ;
311+ use lightning:: util:: events:: { MessageSendEvent , MessageSendEventsProvider , Event , PaymentPurpose } ;
203312 use lightning:: util:: test_utils;
204313 use utils:: create_invoice_from_channelmanager_and_duration_since_epoch;
314+ use secp256k1:: PublicKey ;
205315
206316 #[ test]
207317 fn test_from_channelmanager ( ) {
@@ -255,4 +365,135 @@ mod test {
255365 let events = nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
256366 assert_eq ! ( events. len( ) , 2 ) ;
257367 }
368+
369+ #[ test]
370+ fn test_multi_node_receive ( ) {
371+ #[ cfg( feature = "std" ) ]
372+ do_test_multi_node_receive ( true ) ;
373+ #[ cfg( feature = "std" ) ]
374+ do_test_multi_node_receive ( false ) ;
375+ }
376+
377+ #[ cfg( feature = "std" ) ]
378+ fn do_test_multi_node_receive ( user_generated_pmt_hash : bool ) {
379+ let mut chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
380+ let seed_1 = [ 42 as u8 ; 32 ] ;
381+ let seed_2 = [ 43 as u8 ; 32 ] ;
382+ let cross_node_seed = [ 44 as u8 ; 32 ] ;
383+ chanmon_cfgs[ 1 ] . keys_manager . backing = KeysManager :: new_multi_receive ( & seed_1, 43 , 44 , & cross_node_seed) ;
384+ chanmon_cfgs[ 2 ] . keys_manager . backing = KeysManager :: new_multi_receive ( & seed_2, 43 , 44 , & cross_node_seed) ;
385+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
386+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
387+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
388+ let chan_0_1 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
389+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1. 1 ) ;
390+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1. 0 ) ;
391+ let chan_0_2 = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 2 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
392+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_0_2. 1 ) ;
393+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_2. 0 ) ;
394+
395+ let payment_amt = 10_000 ;
396+ let ( payment_preimage, payment_hash, payment_secret) = {
397+ if user_generated_pmt_hash {
398+ let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
399+ let payment_hash = PaymentHash ( Sha256 :: hash ( & payment_preimage. 0 [ ..] ) . into_inner ( ) ) ;
400+ let payment_secret = nodes[ 1 ] . node . create_inbound_payment_for_hash ( payment_hash, Some ( payment_amt) , 3600 ) . unwrap ( ) ;
401+ ( payment_preimage, payment_hash, payment_secret)
402+ } else {
403+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt) , 3600 ) . unwrap ( ) ;
404+ let payment_preimage = nodes[ 1 ] . node . get_payment_preimage ( payment_hash, payment_secret) . unwrap ( ) ;
405+ ( payment_preimage, payment_hash, payment_secret)
406+ }
407+ } ;
408+ let channels_1 = nodes[ 1 ] . node . list_channels ( ) ;
409+ let channels_2 = nodes[ 2 ] . node . list_channels ( ) ;
410+ let mut route_hints = channels_1. iter ( ) . map ( |e| ( nodes[ 1 ] . node . get_phantom_scid ( ) , nodes[ 1 ] . node . get_our_node_id ( ) , e) ) . collect :: < Vec < ( u64 , PublicKey , & ChannelDetails ) > > ( ) ;
411+ for channel in channels_2. iter ( ) {
412+ route_hints. push ( ( nodes[ 2 ] . node . get_phantom_scid ( ) , nodes[ 2 ] . node . get_our_node_id ( ) , & channel) ) ;
413+ }
414+ 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 ( ) ;
415+
416+ assert_eq ! ( invoice. min_final_cltv_expiry( ) , MIN_FINAL_CLTV_EXPIRY as u64 ) ;
417+ assert_eq ! ( invoice. description( ) , InvoiceDescription :: Direct ( & Description ( "test" . to_string( ) ) ) ) ;
418+ assert_eq ! ( invoice. route_hints( ) . len( ) , 2 ) ;
419+ assert ! ( !invoice. features( ) . unwrap( ) . supports_basic_mpp( ) ) ;
420+
421+ let payee = Payee :: from_node_id ( invoice. recover_payee_pub_key ( ) )
422+ . with_features ( invoice. features ( ) . unwrap ( ) . clone ( ) )
423+ . with_route_hints ( invoice. route_hints ( ) ) ;
424+ let params = RouteParameters {
425+ payee,
426+ final_value_msat : invoice. amount_milli_satoshis ( ) . unwrap ( ) ,
427+ final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
428+ } ;
429+ let first_hops = nodes[ 0 ] . node . list_usable_channels ( ) ;
430+ let network_graph = node_cfgs[ 0 ] . network_graph ;
431+ let logger = test_utils:: TestLogger :: new ( ) ;
432+ let scorer = test_utils:: TestScorer :: with_fixed_penalty ( 0 ) ;
433+ let route = find_route (
434+ & nodes[ 0 ] . node . get_our_node_id ( ) , & params, network_graph,
435+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , & logger, & scorer,
436+ ) . unwrap ( ) ;
437+ let ( payment_event, fwd_idx) = {
438+ let mut payment_hash = PaymentHash ( [ 0 ; 32 ] ) ;
439+ payment_hash. 0 . copy_from_slice ( & invoice. payment_hash ( ) . as_ref ( ) [ 0 ..32 ] ) ;
440+ nodes[ 0 ] . node . send_payment ( & route, payment_hash, & Some ( invoice. payment_secret ( ) . clone ( ) ) ) . unwrap ( ) ;
441+ let mut added_monitors = nodes[ 0 ] . chain_monitor . added_monitors . lock ( ) . unwrap ( ) ;
442+ assert_eq ! ( added_monitors. len( ) , 1 ) ;
443+ added_monitors. clear ( ) ;
444+
445+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
446+ assert_eq ! ( events. len( ) , 1 ) ;
447+ let fwd_idx = match events[ 0 ] {
448+ MessageSendEvent :: UpdateHTLCs { node_id, .. } => {
449+ if node_id == nodes[ 1 ] . node . get_our_node_id ( ) {
450+ 1
451+ } else { 2 }
452+ } ,
453+ _ => panic ! ( "Unexpected event" )
454+ } ;
455+ ( SendEvent :: from_event ( events. remove ( 0 ) ) , fwd_idx)
456+ } ;
457+ nodes[ fwd_idx] . node . handle_update_add_htlc ( & nodes[ 0 ] . node . get_our_node_id ( ) , & payment_event. msgs [ 0 ] ) ;
458+ commitment_signed_dance ! ( nodes[ fwd_idx] , nodes[ 0 ] , & payment_event. commitment_msg, false , true ) ;
459+ expect_pending_htlcs_forwardable ! ( nodes[ fwd_idx] ) ;
460+ let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some ( payment_preimage) } ;
461+ let events = nodes[ fwd_idx] . node . get_and_clear_pending_events ( ) ;
462+ assert_eq ! ( events. len( ) , 2 ) ;
463+ match events[ 0 ] {
464+ Event :: PendingHTLCsForwardable { .. } => { } ,
465+ _ => panic ! ( "Unexpected event" ) ,
466+ }
467+ match events[ 1 ] {
468+ Event :: PaymentReceived { payment_hash : ref hash, ref purpose, amt } => {
469+ assert_eq ! ( * hash, payment_hash) ;
470+ assert_eq ! ( amt, payment_amt) ;
471+ match purpose {
472+ PaymentPurpose :: InvoicePayment { payment_preimage, payment_secret : secret, .. } => {
473+ assert_eq ! ( * payment_preimage, payment_preimage_opt) ;
474+ assert_eq ! ( * secret, payment_secret) ;
475+ } ,
476+ _ => panic ! ( "Unexpected payment purpose" ) ,
477+ }
478+ } ,
479+ _ => panic ! ( "Unexpected event" ) ,
480+ }
481+ do_claim_payment_along_route ( & nodes[ 0 ] , & vec ! ( & vec!( & nodes[ fwd_idx] ) [ ..] ) , false , payment_preimage) ;
482+ let events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
483+ assert_eq ! ( events. len( ) , 2 ) ;
484+ match events[ 0 ] {
485+ Event :: PaymentSent { payment_preimage : ref ev_preimage, payment_hash : ref ev_hash, ref fee_paid_msat, .. } => {
486+ assert_eq ! ( payment_preimage, * ev_preimage) ;
487+ assert_eq ! ( payment_hash, * ev_hash) ;
488+ assert_eq ! ( fee_paid_msat, & Some ( 0 ) ) ;
489+ } ,
490+ _ => panic ! ( "Unexpected event" )
491+ }
492+ match events[ 1 ] {
493+ Event :: PaymentPathSuccessful { payment_hash : hash, .. } => {
494+ assert_eq ! ( hash, Some ( payment_hash) ) ;
495+ } ,
496+ _ => panic ! ( "Unexpected event" )
497+ }
498+ }
258499}
0 commit comments