33use { CreationError , Currency , DEFAULT_EXPIRY_TIME , Invoice , InvoiceBuilder , SignOrCreationError } ;
44use payment:: { Payer , Router } ;
55
6+ use crate :: { prelude:: * , Description , InvoiceDescription , Sha256 } ;
67use bech32:: ToBase32 ;
78use bitcoin_hashes:: { Hash , sha256} ;
8- use crate :: prelude:: * ;
99use lightning:: chain;
1010use lightning:: chain:: chaininterface:: { BroadcasterInterface , FeeEstimator } ;
1111use lightning:: chain:: keysinterface:: { Recipient , KeysInterface , Sign } ;
@@ -50,14 +50,97 @@ use sync::Mutex;
5050/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
5151/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
5252pub fn create_phantom_invoice < Signer : Sign , K : Deref > (
53- amt_msat : Option < u64 > , description : String , payment_hash : PaymentHash , payment_secret :
54- PaymentSecret , phantom_route_hints : Vec < PhantomRouteHints > , keys_manager : K , network : Currency
55- ) -> Result < Invoice , SignOrCreationError < ( ) > > where K :: Target : KeysInterface {
53+ amt_msat : Option < u64 > ,
54+ description : String ,
55+ payment_hash : PaymentHash ,
56+ payment_secret : PaymentSecret ,
57+ phantom_route_hints : Vec < PhantomRouteHints > ,
58+ keys_manager : K ,
59+ network : Currency ,
60+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
61+ where
62+ K :: Target : KeysInterface ,
63+ {
64+ let description = Description :: new ( description) . map_err ( SignOrCreationError :: CreationError ) ?;
65+ let description = InvoiceDescription :: Direct ( & description, ) ;
66+ _create_phantom_invoice :: < Signer , K > (
67+ amt_msat, description, payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
68+ )
69+ }
70+
71+ #[ cfg( feature = "std" ) ]
72+ /// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
73+ /// See [`PhantomKeysManager`] for more information on phantom node payments.
74+ ///
75+ /// `phantom_route_hints` parameter:
76+ /// * Contains channel info for all nodes participating in the phantom invoice
77+ /// * Entries are retrieved from a call to [`ChannelManager::get_phantom_route_hints`] on each
78+ /// participating node
79+ /// * It is fine to cache `phantom_route_hints` and reuse it across invoices, as long as the data is
80+ /// updated when a channel becomes disabled or closes
81+ /// * Note that if too many channels are included in [`PhantomRouteHints::channels`], the invoice
82+ /// may be too long for QR code scanning. To fix this, `PhantomRouteHints::channels` may be pared
83+ /// down
84+ ///
85+ /// `description_hash` is a SHA-256 hash of the description text
86+ ///
87+ /// `payment_hash` and `payment_secret` come from [`ChannelManager::create_inbound_payment`] or
88+ /// [`ChannelManager::create_inbound_payment_for_hash`]. These values can be retrieved from any
89+ /// participating node.
90+ ///
91+ /// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
92+ /// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
93+ /// requirement).
94+ ///
95+ /// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
96+ /// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
97+ /// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
98+ pub fn create_phantom_invoice_with_description_hash < Signer : Sign , K : Deref > (
99+ amt_msat : Option < u64 > ,
100+ description_hash : Sha256 ,
101+ payment_hash : PaymentHash ,
102+ payment_secret : PaymentSecret ,
103+ phantom_route_hints : Vec < PhantomRouteHints > ,
104+ keys_manager : K ,
105+ network : Currency ,
106+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
107+ where
108+ K :: Target : KeysInterface ,
109+ {
110+
111+ _create_phantom_invoice :: < Signer , K > (
112+ amt_msat,
113+ InvoiceDescription :: Hash ( & description_hash) ,
114+ payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
115+ )
116+ }
117+
118+ #[ cfg( feature = "std" ) ]
119+ fn _create_phantom_invoice < Signer : Sign , K : Deref > (
120+ amt_msat : Option < u64 > ,
121+ description : InvoiceDescription ,
122+ payment_hash : PaymentHash ,
123+ payment_secret : PaymentSecret ,
124+ phantom_route_hints : Vec < PhantomRouteHints > ,
125+ keys_manager : K ,
126+ network : Currency ,
127+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
128+ where
129+ K :: Target : KeysInterface ,
130+ {
56131 if phantom_route_hints. len ( ) == 0 {
57- return Err ( SignOrCreationError :: CreationError ( CreationError :: MissingRouteHints ) )
132+ return Err ( SignOrCreationError :: CreationError (
133+ CreationError :: MissingRouteHints ,
134+ ) ) ;
58135 }
59- let mut invoice = InvoiceBuilder :: new ( network)
60- . description ( description)
136+ let invoice = match description {
137+ InvoiceDescription :: Direct ( description) => {
138+ InvoiceBuilder :: new ( network) . description ( description. 0 . clone ( ) )
139+ }
140+ InvoiceDescription :: Hash ( hash) => InvoiceBuilder :: new ( network) . description_hash ( hash. 0 ) ,
141+ } ;
142+
143+ let mut invoice = invoice
61144 . current_timestamp ( )
62145 . payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
63146 . payment_secret ( payment_secret)
@@ -126,12 +209,78 @@ where
126209 let duration = SystemTime :: now ( ) . duration_since ( SystemTime :: UNIX_EPOCH )
127210 . expect ( "for the foreseeable future this shouldn't happen" ) ;
128211 create_invoice_from_channelmanager_and_duration_since_epoch (
129- channelmanager,
130- keys_manager,
131- network,
132- amt_msat,
133- description,
134- duration
212+ channelmanager, keys_manager, network, amt_msat, description, duration
213+ )
214+ }
215+
216+ #[ cfg( feature = "std" ) ]
217+ /// Utility to construct an invoice. Generally, unless you want to do something like a custom
218+ /// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
219+ /// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
220+ /// doesn't have to store preimage/payment secret information and (b) `ChannelManager` can verify
221+ /// that the payment secret is valid when the invoice is paid.
222+ /// Use this variant if you want to pass the `description_hash` to the invoice.
223+ pub fn create_invoice_from_channelmanager_with_description_hash <
224+ Signer : Sign ,
225+ M : Deref ,
226+ T : Deref ,
227+ K : Deref ,
228+ F : Deref ,
229+ L : Deref ,
230+ > (
231+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > ,
232+ keys_manager : K ,
233+ network : Currency ,
234+ amt_msat : Option < u64 > ,
235+ description_hash : Sha256 ,
236+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
237+ where
238+ M :: Target : chain:: Watch < Signer > ,
239+ T :: Target : BroadcasterInterface ,
240+ K :: Target : KeysInterface < Signer = Signer > ,
241+ F :: Target : FeeEstimator ,
242+ L :: Target : Logger ,
243+ {
244+ use std:: time:: SystemTime ;
245+
246+ let duration = SystemTime :: now ( )
247+ . duration_since ( SystemTime :: UNIX_EPOCH )
248+ . expect ( "for the foreseeable future this shouldn't happen" ) ;
249+
250+ create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch (
251+ channelmanager, keys_manager, network, amt_msat, description_hash, duration,
252+ )
253+ }
254+
255+ /// See [`create_invoice_from_channelmanager_with_description_hash`]
256+ /// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
257+ /// available and the current time is supplied by the caller.
258+ pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch <
259+ Signer : Sign ,
260+ M : Deref ,
261+ T : Deref ,
262+ K : Deref ,
263+ F : Deref ,
264+ L : Deref ,
265+ > (
266+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > ,
267+ keys_manager : K ,
268+ network : Currency ,
269+ amt_msat : Option < u64 > ,
270+ description_hash : Sha256 ,
271+ duration_since_epoch : Duration ,
272+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
273+ where
274+ M :: Target : chain:: Watch < Signer > ,
275+ T :: Target : BroadcasterInterface ,
276+ K :: Target : KeysInterface < Signer = Signer > ,
277+ F :: Target : FeeEstimator ,
278+ L :: Target : Logger ,
279+ {
280+ _create_invoice_from_channelmanager_and_duration_since_epoch (
281+ channelmanager, keys_manager, network, amt_msat,
282+ InvoiceDescription :: Hash ( & description_hash) ,
283+ duration_since_epoch,
135284 )
136285}
137286
@@ -142,6 +291,37 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch<Signer: Sign,
142291 channelmanager : & ChannelManager < Signer , M , T , K , F , L > , keys_manager : K , network : Currency ,
143292 amt_msat : Option < u64 > , description : String , duration_since_epoch : Duration ,
144293) -> Result < Invoice , SignOrCreationError < ( ) > >
294+ where
295+ M :: Target : chain:: Watch < Signer > ,
296+ T :: Target : BroadcasterInterface ,
297+ K :: Target : KeysInterface < Signer = Signer > ,
298+ F :: Target : FeeEstimator ,
299+ L :: Target : Logger ,
300+ {
301+ _create_invoice_from_channelmanager_and_duration_since_epoch (
302+ channelmanager, keys_manager, network, amt_msat,
303+ InvoiceDescription :: Direct (
304+ & Description :: new ( description) . map_err ( SignOrCreationError :: CreationError ) ?,
305+ ) ,
306+ duration_since_epoch,
307+ )
308+ }
309+
310+ fn _create_invoice_from_channelmanager_and_duration_since_epoch <
311+ Signer : Sign ,
312+ M : Deref ,
313+ T : Deref ,
314+ K : Deref ,
315+ F : Deref ,
316+ L : Deref ,
317+ > (
318+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > ,
319+ keys_manager : K ,
320+ network : Currency ,
321+ amt_msat : Option < u64 > ,
322+ description : InvoiceDescription ,
323+ duration_since_epoch : Duration ,
324+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
145325where
146326 M :: Target : chain:: Watch < Signer > ,
147327 T :: Target : BroadcasterInterface ,
@@ -153,12 +333,19 @@ where
153333
154334 // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
155335 // supply.
156- let ( payment_hash, payment_secret) = channelmanager. create_inbound_payment (
157- amt_msat, DEFAULT_EXPIRY_TIME . try_into ( ) . unwrap ( ) )
336+ let ( payment_hash, payment_secret) = channelmanager
337+ . create_inbound_payment ( amt_msat, DEFAULT_EXPIRY_TIME . try_into ( ) . unwrap ( ) )
158338 . map_err ( |( ) | SignOrCreationError :: CreationError ( CreationError :: InvalidAmount ) ) ?;
159339 let our_node_pubkey = channelmanager. get_our_node_id ( ) ;
160- let mut invoice = InvoiceBuilder :: new ( network)
161- . description ( description)
340+
341+ let invoice = match description {
342+ InvoiceDescription :: Direct ( description) => {
343+ InvoiceBuilder :: new ( network) . description ( description. 0 . clone ( ) )
344+ }
345+ InvoiceDescription :: Hash ( hash) => InvoiceBuilder :: new ( network) . description_hash ( hash. 0 ) ,
346+ } ;
347+
348+ let mut invoice = invoice
162349 . duration_since_epoch ( duration_since_epoch)
163350 . payee_pub_key ( our_node_pubkey)
164351 . payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
@@ -407,6 +594,31 @@ mod test {
407594 assert_eq ! ( events. len( ) , 2 ) ;
408595 }
409596
597+ #[ test]
598+ fn test_create_invoice_with_description_hash ( ) {
599+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
600+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
601+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
602+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
603+ let description_hash = crate :: Sha256 ( Hash :: hash ( "Testing description_hash" . as_bytes ( ) ) ) ;
604+ let invoice = :: utils:: create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch (
605+ & nodes[ 1 ] . node , nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet , Some ( 10_000 ) ,
606+ description_hash, Duration :: from_secs ( 1234567 ) ,
607+ )
608+ . unwrap ( ) ;
609+ assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 100_000 ) ) ;
610+ assert_eq ! (
611+ invoice. min_final_cltv_expiry( ) ,
612+ MIN_FINAL_CLTV_EXPIRY as u64
613+ ) ;
614+ assert_eq ! (
615+ invoice. description( ) ,
616+ InvoiceDescription :: Hash ( & crate :: Sha256 ( Sha256 :: hash(
617+ "Testing description_hash" . as_bytes( )
618+ ) ) )
619+ ) ;
620+ }
621+
410622 #[ test]
411623 fn test_hints_includes_single_channels_to_nodes ( ) {
412624 let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
@@ -688,6 +900,59 @@ mod test {
688900 }
689901 }
690902
903+ #[ test]
904+ #[ cfg( feature = "std" ) ]
905+ fn create_phantom_invoice_with_description_hash ( ) {
906+ let mut chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
907+ let seed_1 = [ 42 as u8 ; 32 ] ;
908+ let seed_2 = [ 43 as u8 ; 32 ] ;
909+ let cross_node_seed = [ 44 as u8 ; 32 ] ;
910+ chanmon_cfgs[ 1 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_1, 43 , 44 , & cross_node_seed) ;
911+ chanmon_cfgs[ 2 ] . keys_manager . backing =PhantomKeysManager :: new ( & seed_2, 43 , 44 , & cross_node_seed) ;
912+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
913+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
914+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
915+ let chan_0_1 = create_announced_chan_between_nodes_with_value (
916+ & nodes, 0 , 1 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ,
917+ ) ;
918+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1. 1 ) ;
919+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1. 0 ) ;
920+ let chan_0_2 = create_announced_chan_between_nodes_with_value (
921+ & nodes, 0 , 2 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ,
922+ ) ;
923+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_0_2. 1 ) ;
924+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_2. 0 ) ;
925+
926+ let payment_amt = 20_000 ;
927+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt) , 3600 ) . unwrap ( ) ;
928+ let route_hints = vec ! [
929+ nodes[ 1 ] . node. get_phantom_route_hints( ) ,
930+ nodes[ 2 ] . node. get_phantom_route_hints( ) ,
931+ ] ;
932+
933+ let description_hash = crate :: Sha256 ( Hash :: hash ( "Description hash phantom invoice" . as_bytes ( ) ) ) ;
934+ let invoice = :: utils:: create_phantom_invoice_with_description_hash :: <
935+ EnforcingSigner ,
936+ & test_utils:: TestKeysInterface ,
937+ > (
938+ Some ( payment_amt) , description_hash, payment_hash, payment_secret, route_hints,
939+ & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ,
940+ )
941+ . unwrap ( ) ;
942+
943+ assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 200_000 ) ) ;
944+ assert_eq ! (
945+ invoice. min_final_cltv_expiry( ) ,
946+ MIN_FINAL_CLTV_EXPIRY as u64
947+ ) ;
948+ assert_eq ! (
949+ invoice. description( ) ,
950+ InvoiceDescription :: Hash ( & crate :: Sha256 ( Sha256 :: hash(
951+ "Description hash phantom invoice" . as_bytes( )
952+ ) ) )
953+ ) ;
954+ }
955+
691956 #[ test]
692957 #[ cfg( feature = "std" ) ]
693958 fn test_multi_node_hints_includes_single_channels_to_participating_nodes ( ) {
0 commit comments