3
3
use { CreationError , Currency , DEFAULT_EXPIRY_TIME , Invoice , InvoiceBuilder , SignOrCreationError } ;
4
4
use payment:: { Payer , Router } ;
5
5
6
+ use crate :: { prelude:: * , Description , InvoiceDescription } ;
6
7
use bech32:: ToBase32 ;
7
8
use bitcoin_hashes:: { Hash , sha256} ;
8
- use crate :: prelude:: * ;
9
9
use lightning:: chain;
10
10
use lightning:: chain:: chaininterface:: { BroadcasterInterface , FeeEstimator } ;
11
11
use lightning:: chain:: keysinterface:: { Recipient , KeysInterface , Sign } ;
@@ -50,14 +50,96 @@ use sync::Mutex;
50
50
/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
51
51
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
52
52
pub 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` will be SHA-256 hashed and transformed in `description_hash` on the invoice
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 : String ,
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
+ _create_phantom_invoice :: < Signer , K > (
111
+ amt_msat,
112
+ InvoiceDescription :: Hash ( & crate :: Sha256 ( Hash :: hash ( description. as_bytes ( ) ) ) ) ,
113
+ payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
114
+ )
115
+ }
116
+
117
+ #[ cfg( feature = "std" ) ]
118
+ fn _create_phantom_invoice < Signer : Sign , K : Deref > (
119
+ amt_msat : Option < u64 > ,
120
+ description : InvoiceDescription ,
121
+ payment_hash : PaymentHash ,
122
+ payment_secret : PaymentSecret ,
123
+ phantom_route_hints : Vec < PhantomRouteHints > ,
124
+ keys_manager : K ,
125
+ network : Currency ,
126
+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
127
+ where
128
+ K :: Target : KeysInterface ,
129
+ {
56
130
if phantom_route_hints. len ( ) == 0 {
57
- return Err ( SignOrCreationError :: CreationError ( CreationError :: MissingRouteHints ) )
131
+ return Err ( SignOrCreationError :: CreationError (
132
+ CreationError :: MissingRouteHints ,
133
+ ) ) ;
58
134
}
59
- let mut invoice = InvoiceBuilder :: new ( network)
60
- . description ( description)
135
+ let invoice = match description {
136
+ InvoiceDescription :: Direct ( description) => {
137
+ InvoiceBuilder :: new ( network) . description ( description. 0 . clone ( ) )
138
+ }
139
+ InvoiceDescription :: Hash ( hash) => InvoiceBuilder :: new ( network) . description_hash ( hash. 0 ) ,
140
+ } ;
141
+
142
+ let mut invoice = invoice
61
143
. current_timestamp ( )
62
144
. payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
63
145
. payment_secret ( payment_secret)
@@ -126,12 +208,78 @@ where
126
208
let duration = SystemTime :: now ( ) . duration_since ( SystemTime :: UNIX_EPOCH )
127
209
. expect ( "for the foreseeable future this shouldn't happen" ) ;
128
210
create_invoice_from_channelmanager_and_duration_since_epoch (
129
- channelmanager,
130
- keys_manager,
131
- network,
132
- amt_msat,
133
- description,
134
- duration
211
+ channelmanager, keys_manager, network, amt_msat, description, duration
212
+ )
213
+ }
214
+
215
+ #[ cfg( feature = "std" ) ]
216
+ /// Utility to construct an invoice. Generally, unless you want to do something like a custom
217
+ /// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
218
+ /// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
219
+ /// doesn't have to store preimage/payment secret information and (b) `ChannelManager` can verify
220
+ /// that the payment secret is valid when the invoice is paid.
221
+ /// Use this variant if you want to hash the description and pass a `payment_hash` instead
222
+ pub fn create_invoice_from_channelmanager_with_description_hash <
223
+ Signer : Sign ,
224
+ M : Deref ,
225
+ T : Deref ,
226
+ K : Deref ,
227
+ F : Deref ,
228
+ L : Deref ,
229
+ > (
230
+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > ,
231
+ keys_manager : K ,
232
+ network : Currency ,
233
+ amt_msat : Option < u64 > ,
234
+ description : String ,
235
+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
236
+ where
237
+ M :: Target : chain:: Watch < Signer > ,
238
+ T :: Target : BroadcasterInterface ,
239
+ K :: Target : KeysInterface < Signer = Signer > ,
240
+ F :: Target : FeeEstimator ,
241
+ L :: Target : Logger ,
242
+ {
243
+ use std:: time:: SystemTime ;
244
+
245
+ let duration = SystemTime :: now ( )
246
+ . duration_since ( SystemTime :: UNIX_EPOCH )
247
+ . expect ( "for the foreseeable future this shouldn't happen" ) ;
248
+
249
+ create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch (
250
+ channelmanager, keys_manager, network, amt_msat, description, duration,
251
+ )
252
+ }
253
+
254
+ /// See [`create_invoice_from_channelmanager_with_description_hash`]
255
+ /// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
256
+ /// available and the current time is supplied by the caller.
257
+ pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch <
258
+ Signer : Sign ,
259
+ M : Deref ,
260
+ T : Deref ,
261
+ K : Deref ,
262
+ F : Deref ,
263
+ L : Deref ,
264
+ > (
265
+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > ,
266
+ keys_manager : K ,
267
+ network : Currency ,
268
+ amt_msat : Option < u64 > ,
269
+ description : String ,
270
+ duration_since_epoch : Duration ,
271
+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
272
+ where
273
+ M :: Target : chain:: Watch < Signer > ,
274
+ T :: Target : BroadcasterInterface ,
275
+ K :: Target : KeysInterface < Signer = Signer > ,
276
+ F :: Target : FeeEstimator ,
277
+ L :: Target : Logger ,
278
+ {
279
+ _create_invoice_from_channelmanager_and_duration_since_epoch (
280
+ channelmanager, keys_manager, network, amt_msat,
281
+ InvoiceDescription :: Hash ( & crate :: Sha256 ( Hash :: hash ( description. as_bytes ( ) ) ) ) ,
282
+ duration_since_epoch,
135
283
)
136
284
}
137
285
@@ -142,6 +290,37 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch<Signer: Sign,
142
290
channelmanager : & ChannelManager < Signer , M , T , K , F , L > , keys_manager : K , network : Currency ,
143
291
amt_msat : Option < u64 > , description : String , duration_since_epoch : Duration ,
144
292
) -> Result < Invoice , SignOrCreationError < ( ) > >
293
+ where
294
+ M :: Target : chain:: Watch < Signer > ,
295
+ T :: Target : BroadcasterInterface ,
296
+ K :: Target : KeysInterface < Signer = Signer > ,
297
+ F :: Target : FeeEstimator ,
298
+ L :: Target : Logger ,
299
+ {
300
+ _create_invoice_from_channelmanager_and_duration_since_epoch (
301
+ channelmanager, keys_manager, network, amt_msat,
302
+ InvoiceDescription :: Direct (
303
+ & Description :: new ( description) . map_err ( SignOrCreationError :: CreationError ) ?,
304
+ ) ,
305
+ duration_since_epoch,
306
+ )
307
+ }
308
+
309
+ fn _create_invoice_from_channelmanager_and_duration_since_epoch <
310
+ Signer : Sign ,
311
+ M : Deref ,
312
+ T : Deref ,
313
+ K : Deref ,
314
+ F : Deref ,
315
+ L : Deref ,
316
+ > (
317
+ channelmanager : & ChannelManager < Signer , M , T , K , F , L > ,
318
+ keys_manager : K ,
319
+ network : Currency ,
320
+ amt_msat : Option < u64 > ,
321
+ description : InvoiceDescription ,
322
+ duration_since_epoch : Duration ,
323
+ ) -> Result < Invoice , SignOrCreationError < ( ) > >
145
324
where
146
325
M :: Target : chain:: Watch < Signer > ,
147
326
T :: Target : BroadcasterInterface ,
@@ -153,12 +332,19 @@ where
153
332
154
333
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
155
334
// supply.
156
- let ( payment_hash, payment_secret) = channelmanager. create_inbound_payment (
157
- amt_msat, DEFAULT_EXPIRY_TIME . try_into ( ) . unwrap ( ) )
335
+ let ( payment_hash, payment_secret) = channelmanager
336
+ . create_inbound_payment ( amt_msat, DEFAULT_EXPIRY_TIME . try_into ( ) . unwrap ( ) )
158
337
. map_err ( |( ) | SignOrCreationError :: CreationError ( CreationError :: InvalidAmount ) ) ?;
159
338
let our_node_pubkey = channelmanager. get_our_node_id ( ) ;
160
- let mut invoice = InvoiceBuilder :: new ( network)
161
- . description ( description)
339
+
340
+ let invoice = match description {
341
+ InvoiceDescription :: Direct ( description) => {
342
+ InvoiceBuilder :: new ( network) . description ( description. 0 . clone ( ) )
343
+ }
344
+ InvoiceDescription :: Hash ( hash) => InvoiceBuilder :: new ( network) . description_hash ( hash. 0 ) ,
345
+ } ;
346
+
347
+ let mut invoice = invoice
162
348
. duration_since_epoch ( duration_since_epoch)
163
349
. payee_pub_key ( our_node_pubkey)
164
350
. payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
@@ -407,6 +593,31 @@ mod test {
407
593
assert_eq ! ( events. len( ) , 2 ) ;
408
594
}
409
595
596
+ #[ test]
597
+ fn test_create_invoice_with_description_hash ( ) {
598
+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
599
+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
600
+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
601
+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
602
+
603
+ let invoice = :: utils:: create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch (
604
+ & nodes[ 1 ] . node , nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet , Some ( 10_000 ) ,
605
+ "Testing description_hash" . to_string ( ) , Duration :: from_secs ( 1234567 ) ,
606
+ )
607
+ . unwrap ( ) ;
608
+ assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 100_000 ) ) ;
609
+ assert_eq ! (
610
+ invoice. min_final_cltv_expiry( ) ,
611
+ MIN_FINAL_CLTV_EXPIRY as u64
612
+ ) ;
613
+ assert_eq ! (
614
+ invoice. description( ) ,
615
+ InvoiceDescription :: Hash ( & crate :: Sha256 ( Sha256 :: hash(
616
+ "Testing description_hash" . as_bytes( )
617
+ ) ) )
618
+ ) ;
619
+ }
620
+
410
621
#[ test]
411
622
fn test_hints_includes_single_channels_to_nodes ( ) {
412
623
let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
@@ -688,6 +899,57 @@ mod test {
688
899
}
689
900
}
690
901
902
+ #[ test]
903
+ #[ cfg( feature = "std" ) ]
904
+ fn create_phantom_invoice_with_description_hash ( ) {
905
+ let mut chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
906
+ let seed_1 = [ 42 as u8 ; 32 ] ;
907
+ let seed_2 = [ 43 as u8 ; 32 ] ;
908
+ let cross_node_seed = [ 44 as u8 ; 32 ] ;
909
+ chanmon_cfgs[ 1 ] . keys_manager . backing = PhantomKeysManager :: new ( & seed_1, 43 , 44 , & cross_node_seed) ;
910
+ chanmon_cfgs[ 2 ] . keys_manager . backing =PhantomKeysManager :: new ( & seed_2, 43 , 44 , & cross_node_seed) ;
911
+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
912
+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
913
+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
914
+ let chan_0_1 = create_announced_chan_between_nodes_with_value (
915
+ & nodes, 0 , 1 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ,
916
+ ) ;
917
+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 1 ] . node . get_our_node_id ( ) , & chan_0_1. 1 ) ;
918
+ nodes[ 1 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_1. 0 ) ;
919
+ let chan_0_2 = create_announced_chan_between_nodes_with_value (
920
+ & nodes, 0 , 2 , 100000 , 10001 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ,
921
+ ) ;
922
+ nodes[ 0 ] . node . handle_channel_update ( & nodes[ 2 ] . node . get_our_node_id ( ) , & chan_0_2. 1 ) ;
923
+ nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_2. 0 ) ;
924
+
925
+ let payment_amt = 20_000 ;
926
+ let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt) , 3600 ) . unwrap ( ) ;
927
+ let route_hints = vec ! [
928
+ nodes[ 1 ] . node. get_phantom_route_hints( ) ,
929
+ nodes[ 2 ] . node. get_phantom_route_hints( ) ,
930
+ ] ;
931
+ let invoice = :: utils:: create_phantom_invoice_with_description_hash :: <
932
+ EnforcingSigner ,
933
+ & test_utils:: TestKeysInterface ,
934
+ > (
935
+ Some ( payment_amt) , "Description hash phantom invoice" . to_string ( ) , payment_hash,
936
+ payment_secret, route_hints, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ,
937
+ )
938
+ . unwrap ( ) ;
939
+
940
+ assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 200_000 ) ) ;
941
+ assert_eq ! (
942
+ invoice. min_final_cltv_expiry( ) ,
943
+ MIN_FINAL_CLTV_EXPIRY as u64
944
+ ) ;
945
+ assert_eq ! (
946
+ invoice. description( ) ,
947
+ InvoiceDescription :: Hash ( & crate :: Sha256 ( Sha256 :: hash(
948
+ "Description hash phantom invoice" . as_bytes( )
949
+ ) ) )
950
+ ) ;
951
+ }
952
+
691
953
#[ test]
692
954
#[ cfg( feature = "std" ) ]
693
955
fn test_multi_node_hints_includes_single_channels_to_participating_nodes ( ) {
0 commit comments