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,81 @@ 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
53+ amt_msat : Option < u64 > , description : String , payment_hash : PaymentHash , payment_secret : PaymentSecret ,
54+ phantom_route_hints : Vec < PhantomRouteHints > , keys_manager : K , network : Currency ,
5555) -> Result < Invoice , SignOrCreationError < ( ) > > where K :: Target : KeysInterface {
56+ let description = Description :: new ( description) . map_err ( SignOrCreationError :: CreationError ) ?;
57+ let description = InvoiceDescription :: Direct ( & description, ) ;
58+ _create_phantom_invoice :: < Signer , K > (
59+ amt_msat, description, payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
60+ )
61+ }
62+
63+ #[ cfg( feature = "std" ) ]
64+ /// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
65+ /// See [`PhantomKeysManager`] for more information on phantom node payments.
66+ ///
67+ /// `phantom_route_hints` parameter:
68+ /// * Contains channel info for all nodes participating in the phantom invoice
69+ /// * Entries are retrieved from a call to [`ChannelManager::get_phantom_route_hints`] on each
70+ /// participating node
71+ /// * It is fine to cache `phantom_route_hints` and reuse it across invoices, as long as the data is
72+ /// updated when a channel becomes disabled or closes
73+ /// * Note that if too many channels are included in [`PhantomRouteHints::channels`], the invoice
74+ /// may be too long for QR code scanning. To fix this, `PhantomRouteHints::channels` may be pared
75+ /// down
76+ ///
77+ /// `description_hash` is a SHA-256 hash of the description text
78+ ///
79+ /// `payment_hash` and `payment_secret` come from [`ChannelManager::create_inbound_payment`] or
80+ /// [`ChannelManager::create_inbound_payment_for_hash`]. These values can be retrieved from any
81+ /// participating node.
82+ ///
83+ /// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
84+ /// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
85+ /// requirement).
86+ ///
87+ /// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
88+ /// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
89+ /// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
90+ pub fn create_phantom_invoice_with_description_hash < Signer : Sign , K : Deref > (
91+ amt_msat : Option < u64 > , description_hash : Sha256 , payment_hash : PaymentHash ,
92+ payment_secret : PaymentSecret , phantom_route_hints : Vec < PhantomRouteHints > ,
93+ keys_manager : K , network : Currency ,
94+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
95+ where
96+ K :: Target : KeysInterface ,
97+ {
98+
99+ _create_phantom_invoice :: < Signer , K > (
100+ amt_msat,
101+ InvoiceDescription :: Hash ( & description_hash) ,
102+ payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
103+ )
104+ }
105+
106+ #[ cfg( feature = "std" ) ]
107+ fn _create_phantom_invoice < Signer : Sign , K : Deref > (
108+ amt_msat : Option < u64 > , description : InvoiceDescription , payment_hash : PaymentHash ,
109+ payment_secret : PaymentSecret , phantom_route_hints : Vec < PhantomRouteHints > ,
110+ keys_manager : K , network : Currency ,
111+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
112+ where
113+ K :: Target : KeysInterface ,
114+ {
56115 if phantom_route_hints. len ( ) == 0 {
57- return Err ( SignOrCreationError :: CreationError ( CreationError :: MissingRouteHints ) )
116+ return Err ( SignOrCreationError :: CreationError (
117+ CreationError :: MissingRouteHints ,
118+ ) ) ;
58119 }
59- let mut invoice = InvoiceBuilder :: new ( network)
60- . description ( description)
120+ let invoice = match description {
121+ InvoiceDescription :: Direct ( description) => {
122+ InvoiceBuilder :: new ( network) . description ( description. 0 . clone ( ) )
123+ }
124+ InvoiceDescription :: Hash ( hash) => InvoiceBuilder :: new ( network) . description_hash ( hash. 0 ) ,
125+ } ;
126+
127+ let mut invoice = invoice
61128 . current_timestamp ( )
62129 . payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
63130 . payment_secret ( payment_secret)
@@ -126,12 +193,61 @@ where
126193 let duration = SystemTime :: now ( ) . duration_since ( SystemTime :: UNIX_EPOCH )
127194 . expect ( "for the foreseeable future this shouldn't happen" ) ;
128195 create_invoice_from_channelmanager_and_duration_since_epoch (
129- channelmanager,
130- keys_manager,
131- network,
132- amt_msat,
133- description,
134- duration
196+ channelmanager, keys_manager, network, amt_msat, description, duration
197+ )
198+ }
199+
200+ #[ cfg( feature = "std" ) ]
201+ /// Utility to construct an invoice. Generally, unless you want to do something like a custom
202+ /// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
203+ /// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
204+ /// doesn't have to store preimage/payment secret information and (b) `ChannelManager` can verify
205+ /// that the payment secret is valid when the invoice is paid.
206+ /// Use this variant if you want to pass the `description_hash` to the invoice.
207+ pub fn create_invoice_from_channelmanager_with_description_hash < Signer : Sign , M : Deref , T : Deref ,
208+ K : Deref , F : Deref , L : Deref ,
209+ > (
210+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > , keys_manager : K , network : Currency ,
211+ amt_msat : Option < u64 > , description_hash : Sha256 ,
212+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
213+ where
214+ M :: Target : chain:: Watch < Signer > ,
215+ T :: Target : BroadcasterInterface ,
216+ K :: Target : KeysInterface < Signer = Signer > ,
217+ F :: Target : FeeEstimator ,
218+ L :: Target : Logger ,
219+ {
220+ use std:: time:: SystemTime ;
221+
222+ let duration = SystemTime :: now ( )
223+ . duration_since ( SystemTime :: UNIX_EPOCH )
224+ . expect ( "for the foreseeable future this shouldn't happen" ) ;
225+
226+ create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch (
227+ channelmanager, keys_manager, network, amt_msat, description_hash, duration,
228+ )
229+ }
230+
231+ /// See [`create_invoice_from_channelmanager_with_description_hash`]
232+ /// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
233+ /// available and the current time is supplied by the caller.
234+ pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch <
235+ Signer : Sign , M : Deref , T : Deref , K : Deref , F : Deref , L : Deref ,
236+ > (
237+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > , keys_manager : K , network : Currency ,
238+ amt_msat : Option < u64 > , description_hash : Sha256 , duration_since_epoch : Duration ,
239+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
240+ where
241+ M :: Target : chain:: Watch < Signer > ,
242+ T :: Target : BroadcasterInterface ,
243+ K :: Target : KeysInterface < Signer = Signer > ,
244+ F :: Target : FeeEstimator ,
245+ L :: Target : Logger ,
246+ {
247+ _create_invoice_from_channelmanager_and_duration_since_epoch (
248+ channelmanager, keys_manager, network, amt_msat,
249+ InvoiceDescription :: Hash ( & description_hash) ,
250+ duration_since_epoch,
135251 )
136252}
137253
@@ -142,6 +258,28 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch<Signer: Sign,
142258 channelmanager : & ChannelManager < Signer , M , T , K , F , L > , keys_manager : K , network : Currency ,
143259 amt_msat : Option < u64 > , description : String , duration_since_epoch : Duration ,
144260) -> Result < Invoice , SignOrCreationError < ( ) > >
261+ where
262+ M :: Target : chain:: Watch < Signer > ,
263+ T :: Target : BroadcasterInterface ,
264+ K :: Target : KeysInterface < Signer = Signer > ,
265+ F :: Target : FeeEstimator ,
266+ L :: Target : Logger ,
267+ {
268+ _create_invoice_from_channelmanager_and_duration_since_epoch (
269+ channelmanager, keys_manager, network, amt_msat,
270+ InvoiceDescription :: Direct (
271+ & Description :: new ( description) . map_err ( SignOrCreationError :: CreationError ) ?,
272+ ) ,
273+ duration_since_epoch,
274+ )
275+ }
276+
277+ fn _create_invoice_from_channelmanager_and_duration_since_epoch <
278+ Signer : Sign , M : Deref , T : Deref , K : Deref , F : Deref , L : Deref ,
279+ > (
280+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > , keys_manager : K , network : Currency ,
281+ amt_msat : Option < u64 > , description : InvoiceDescription , duration_since_epoch : Duration ,
282+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
145283where
146284 M :: Target : chain:: Watch < Signer > ,
147285 T :: Target : BroadcasterInterface ,
@@ -153,12 +291,19 @@ where
153291
154292 // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
155293 // supply.
156- let ( payment_hash, payment_secret) = channelmanager. create_inbound_payment (
157- amt_msat, DEFAULT_EXPIRY_TIME . try_into ( ) . unwrap ( ) )
294+ let ( payment_hash, payment_secret) = channelmanager
295+ . create_inbound_payment ( amt_msat, DEFAULT_EXPIRY_TIME . try_into ( ) . unwrap ( ) )
158296 . map_err ( |( ) | SignOrCreationError :: CreationError ( CreationError :: InvalidAmount ) ) ?;
159297 let our_node_pubkey = channelmanager. get_our_node_id ( ) ;
160- let mut invoice = InvoiceBuilder :: new ( network)
161- . description ( description)
298+
299+ let invoice = match description {
300+ InvoiceDescription :: Direct ( description) => {
301+ InvoiceBuilder :: new ( network) . description ( description. 0 . clone ( ) )
302+ }
303+ InvoiceDescription :: Hash ( hash) => InvoiceBuilder :: new ( network) . description_hash ( hash. 0 ) ,
304+ } ;
305+
306+ let mut invoice = invoice
162307 . duration_since_epoch ( duration_since_epoch)
163308 . payee_pub_key ( our_node_pubkey)
164309 . payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
@@ -407,6 +552,31 @@ mod test {
407552 assert_eq ! ( events. len( ) , 2 ) ;
408553 }
409554
555+ #[ test]
556+ fn test_create_invoice_with_description_hash ( ) {
557+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
558+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
559+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
560+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
561+ let description_hash = crate :: Sha256 ( Hash :: hash ( "Testing description_hash" . as_bytes ( ) ) ) ;
562+ let invoice = :: utils:: create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch (
563+ & nodes[ 1 ] . node , nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet , Some ( 10_000 ) ,
564+ description_hash, Duration :: from_secs ( 1234567 ) ,
565+ )
566+ . unwrap ( ) ;
567+ assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 100_000 ) ) ;
568+ assert_eq ! (
569+ invoice. min_final_cltv_expiry( ) ,
570+ MIN_FINAL_CLTV_EXPIRY as u64
571+ ) ;
572+ assert_eq ! (
573+ invoice. description( ) ,
574+ InvoiceDescription :: Hash ( & crate :: Sha256 ( Sha256 :: hash(
575+ "Testing description_hash" . as_bytes( )
576+ ) ) )
577+ ) ;
578+ }
579+
410580 #[ test]
411581 fn test_hints_includes_single_channels_to_nodes ( ) {
412582 let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
@@ -688,6 +858,58 @@ mod test {
688858 }
689859 }
690860
861+ #[ test]
862+ #[ cfg( feature = "std" ) ]
863+ fn create_phantom_invoice_with_description_hash ( ) {
864+ let mut chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
865+ let seed_1 = [ 42 as u8 ; 32 ] ;
866+ let seed_2 = [ 43 as u8 ; 32 ] ;
867+ let cross_node_seed = [ 44 as u8 ; 32 ] ;
868+ chanmon_cfgs[ 1 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_1, 43 , 44 , & cross_node_seed) ;
869+ chanmon_cfgs[ 2 ] . keys_manager . backing =PhantomKeysManager :: new ( & seed_2, 43 , 44 , & cross_node_seed) ;
870+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
871+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
872+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
873+ let chan_0_1 = create_announced_chan_between_nodes_with_value (
874+ & nodes, 0 , 1 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ,
875+ ) ;
876+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1. 1 ) ;
877+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1. 0 ) ;
878+ let chan_0_2 = create_announced_chan_between_nodes_with_value (
879+ & nodes, 0 , 2 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ,
880+ ) ;
881+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_0_2. 1 ) ;
882+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_2. 0 ) ;
883+
884+ let payment_amt = 20_000 ;
885+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt) , 3600 ) . unwrap ( ) ;
886+ let route_hints = vec ! [
887+ nodes[ 1 ] . node. get_phantom_route_hints( ) ,
888+ nodes[ 2 ] . node. get_phantom_route_hints( ) ,
889+ ] ;
890+
891+ let description_hash = crate :: Sha256 ( Hash :: hash ( "Description hash phantom invoice" . as_bytes ( ) ) ) ;
892+ let invoice = :: utils:: create_phantom_invoice_with_description_hash :: <
893+ EnforcingSigner ,
894+ & test_utils:: TestKeysInterface ,
895+ > (
896+ Some ( payment_amt) , description_hash, payment_hash, payment_secret, route_hints,
897+ & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ,
898+ ) . unwrap ( ) ;
899+
900+ assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 200_000 ) ) ;
901+ assert_eq ! (
902+ invoice. min_final_cltv_expiry( ) ,
903+ MIN_FINAL_CLTV_EXPIRY as u64
904+ ) ;
905+ assert_eq ! (
906+ invoice. description( ) ,
907+ InvoiceDescription :: Hash ( & crate :: Sha256 ( Sha256 :: hash(
908+ "Description hash phantom invoice" . as_bytes( )
909+ ) ) )
910+ ) ;
911+ }
912+
691913 #[ test]
692914 #[ cfg( feature = "std" ) ]
693915 fn test_multi_node_hints_includes_single_channels_to_participating_nodes ( ) {
0 commit comments