@@ -235,9 +235,12 @@ where
235235///
236236/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
237237/// in excess of the current time.
238+ /// You can specify a custom `min_final_cltv_expiry_delta`, or let LDK default it to
239+ /// `MIN_FINAL_CLTV_EXPIRY_DELTA`. The provided expiry must be at least `MIN_FINAL_CLTV_EXPIRY_DELTA`.
238240pub fn create_invoice_from_channelmanager < M : Deref , T : Deref , K : Deref , F : Deref , L : Deref > (
239241 channelmanager : & ChannelManager < M , T , K , F , L > , keys_manager : K , logger : L ,
240- network : Currency , amt_msat : Option < u64 > , description : String , invoice_expiry_delta_secs : u32
242+ network : Currency , amt_msat : Option < u64 > , description : String , invoice_expiry_delta_secs : u32 ,
243+ min_final_cltv_expiry_delta : Option < u32 > ,
241244) -> Result < Invoice , SignOrCreationError < ( ) > >
242245where
243246 M :: Target : chain:: Watch < <K :: Target as KeysInterface >:: Signer > ,
@@ -251,7 +254,7 @@ where
251254 . expect ( "for the foreseeable future this shouldn't happen" ) ;
252255 create_invoice_from_channelmanager_and_duration_since_epoch (
253256 channelmanager, keys_manager, logger, network, amt_msat, description, duration,
254- invoice_expiry_delta_secs
257+ invoice_expiry_delta_secs, min_final_cltv_expiry_delta ,
255258 )
256259}
257260
@@ -268,7 +271,7 @@ where
268271pub fn create_invoice_from_channelmanager_with_description_hash < M : Deref , T : Deref , K : Deref , F : Deref , L : Deref > (
269272 channelmanager : & ChannelManager < M , T , K , F , L > , keys_manager : K , logger : L ,
270273 network : Currency , amt_msat : Option < u64 > , description_hash : Sha256 ,
271- invoice_expiry_delta_secs : u32
274+ invoice_expiry_delta_secs : u32 , min_final_cltv_expiry_delta : Option < u32 > ,
272275) -> Result < Invoice , SignOrCreationError < ( ) > >
273276where
274277 M :: Target : chain:: Watch < <K :: Target as KeysInterface >:: Signer > ,
@@ -285,7 +288,7 @@ where
285288
286289 create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch (
287290 channelmanager, keys_manager, logger, network, amt_msat,
288- description_hash, duration, invoice_expiry_delta_secs
291+ description_hash, duration, invoice_expiry_delta_secs, min_final_cltv_expiry_delta ,
289292 )
290293}
291294
@@ -295,7 +298,7 @@ where
295298pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch < M : Deref , T : Deref , K : Deref , F : Deref , L : Deref > (
296299 channelmanager : & ChannelManager < M , T , K , F , L > , keys_manager : K , logger : L ,
297300 network : Currency , amt_msat : Option < u64 > , description_hash : Sha256 ,
298- duration_since_epoch : Duration , invoice_expiry_delta_secs : u32
301+ duration_since_epoch : Duration , invoice_expiry_delta_secs : u32 , min_final_cltv_expiry_delta : Option < u32 > ,
299302) -> Result < Invoice , SignOrCreationError < ( ) > >
300303where
301304 M :: Target : chain:: Watch < <K :: Target as KeysInterface >:: Signer > ,
@@ -307,7 +310,7 @@ where
307310 _create_invoice_from_channelmanager_and_duration_since_epoch (
308311 channelmanager, keys_manager, logger, network, amt_msat,
309312 InvoiceDescription :: Hash ( & description_hash) ,
310- duration_since_epoch, invoice_expiry_delta_secs
313+ duration_since_epoch, invoice_expiry_delta_secs, min_final_cltv_expiry_delta ,
311314 )
312315}
313316
@@ -317,7 +320,7 @@ where
317320pub fn create_invoice_from_channelmanager_and_duration_since_epoch < M : Deref , T : Deref , K : Deref , F : Deref , L : Deref > (
318321 channelmanager : & ChannelManager < M , T , K , F , L > , keys_manager : K , logger : L ,
319322 network : Currency , amt_msat : Option < u64 > , description : String , duration_since_epoch : Duration ,
320- invoice_expiry_delta_secs : u32
323+ invoice_expiry_delta_secs : u32 , min_final_cltv_expiry_delta : Option < u32 > ,
321324) -> Result < Invoice , SignOrCreationError < ( ) > >
322325where
323326 M :: Target : chain:: Watch < <K :: Target as KeysInterface >:: Signer > ,
@@ -331,14 +334,14 @@ where
331334 InvoiceDescription :: Direct (
332335 & Description :: new ( description) . map_err ( SignOrCreationError :: CreationError ) ?,
333336 ) ,
334- duration_since_epoch, invoice_expiry_delta_secs
337+ duration_since_epoch, invoice_expiry_delta_secs, min_final_cltv_expiry_delta ,
335338 )
336339}
337340
338341fn _create_invoice_from_channelmanager_and_duration_since_epoch < M : Deref , T : Deref , K : Deref , F : Deref , L : Deref > (
339342 channelmanager : & ChannelManager < M , T , K , F , L > , keys_manager : K , logger : L ,
340343 network : Currency , amt_msat : Option < u64 > , description : InvoiceDescription ,
341- duration_since_epoch : Duration , invoice_expiry_delta_secs : u32
344+ duration_since_epoch : Duration , invoice_expiry_delta_secs : u32 , min_final_cltv_expiry_delta : Option < u32 > ,
342345) -> Result < Invoice , SignOrCreationError < ( ) > >
343346where
344347 M :: Target : chain:: Watch < <K :: Target as KeysInterface >:: Signer > ,
@@ -347,6 +350,10 @@ where
347350 F :: Target : FeeEstimator ,
348351 L :: Target : Logger ,
349352{
353+ if min_final_cltv_expiry_delta. is_some ( ) && min_final_cltv_expiry_delta. unwrap ( ) < MIN_FINAL_CLTV_EXPIRY_DELTA {
354+ return Err ( SignOrCreationError :: CreationError ( CreationError :: MinFinalCltvExpiryDeltaTooShort ) ) ;
355+ }
356+
350357 // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
351358 // supply.
352359 let ( payment_hash, payment_secret) = channelmanager
@@ -370,7 +377,7 @@ where
370377 . payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
371378 . payment_secret ( payment_secret)
372379 . basic_mpp ( )
373- . min_final_cltv_expiry_delta ( MIN_FINAL_CLTV_EXPIRY_DELTA . into ( ) )
380+ . min_final_cltv_expiry_delta ( min_final_cltv_expiry_delta . unwrap_or ( MIN_FINAL_CLTV_EXPIRY_DELTA ) . into ( ) )
374381 . expiry_time ( Duration :: from_secs ( invoice_expiry_delta_secs. into ( ) ) ) ;
375382 if let Some ( amt) = amt_msat {
376383 invoice = invoice. amount_milli_satoshis ( amt) ;
@@ -683,7 +690,7 @@ impl<'a, S: Score> Score for ScorerAccountingForInFlightHtlcs<'a, S> {
683690#[ cfg( test) ]
684691mod test {
685692 use core:: time:: Duration ;
686- use crate :: { Currency , Description , InvoiceDescription } ;
693+ use crate :: { Currency , Description , InvoiceDescription , SignOrCreationError , CreationError } ;
687694 use bitcoin_hashes:: Hash ;
688695 use bitcoin_hashes:: sha256:: Hash as Sha256 ;
689696 use lightning:: chain:: keysinterface:: PhantomKeysManager ;
@@ -710,8 +717,9 @@ mod test {
710717 let invoice = create_invoice_from_channelmanager_and_duration_since_epoch (
711718 & nodes[ 1 ] . node , nodes[ 1 ] . keys_manager , nodes[ 1 ] . logger , Currency :: BitcoinTestnet ,
712719 Some ( 10_000 ) , "test" . to_string ( ) , Duration :: from_secs ( 1234567 ) ,
713- non_default_invoice_expiry_secs) . unwrap ( ) ;
720+ non_default_invoice_expiry_secs, None ) . unwrap ( ) ;
714721 assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 100_000 ) ) ;
722+ // If no `min_final_cltv_expiry_delta` is specified, then it should be `MIN_FINAL_CLTV_EXPIRY_DELTA`.
715723 assert_eq ! ( invoice. min_final_cltv_expiry_delta( ) , MIN_FINAL_CLTV_EXPIRY_DELTA as u64 ) ;
716724 assert_eq ! ( invoice. description( ) , InvoiceDescription :: Direct ( & Description ( "test" . to_string( ) ) ) ) ;
717725 assert_eq ! ( invoice. expiry_time( ) , Duration :: from_secs( non_default_invoice_expiry_secs. into( ) ) ) ;
@@ -775,7 +783,7 @@ mod test {
775783 let description_hash = crate :: Sha256 ( Hash :: hash ( "Testing description_hash" . as_bytes ( ) ) ) ;
776784 let invoice = crate :: utils:: create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch (
777785 & nodes[ 1 ] . node , nodes[ 1 ] . keys_manager , nodes[ 1 ] . logger , Currency :: BitcoinTestnet ,
778- Some ( 10_000 ) , description_hash, Duration :: from_secs ( 1234567 ) , 3600
786+ Some ( 10_000 ) , description_hash, Duration :: from_secs ( 1234567 ) , 3600 , None ,
779787 ) . unwrap ( ) ;
780788 assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 100_000 ) ) ;
781789 assert_eq ! ( invoice. min_final_cltv_expiry_delta( ) , MIN_FINAL_CLTV_EXPIRY_DELTA as u64 ) ;
@@ -967,7 +975,7 @@ mod test {
967975 let invoice = create_invoice_from_channelmanager_and_duration_since_epoch (
968976 & invoice_node. node , invoice_node. keys_manager , invoice_node. logger ,
969977 Currency :: BitcoinTestnet , invoice_amt, "test" . to_string ( ) , Duration :: from_secs ( 1234567 ) ,
970- 3600 ) . unwrap ( ) ;
978+ 3600 , None ) . unwrap ( ) ;
971979 let hints = invoice. private_routes ( ) ;
972980
973981 for hint in hints {
@@ -1494,4 +1502,33 @@ mod test {
14941502 }
14951503 assert ! ( chan_ids_to_match. is_empty( ) , "Unmatched short channel ids: {:?}" , chan_ids_to_match) ;
14961504 }
1505+
1506+ #[ test]
1507+ fn test_create_invoice_with_valid_custom_min_final_cltv_expiry_delta ( ) {
1508+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
1509+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
1510+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
1511+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
1512+ let invoice = crate :: utils:: create_invoice_from_channelmanager_and_duration_since_epoch (
1513+ & nodes[ 1 ] . node , nodes[ 1 ] . keys_manager , nodes[ 1 ] . logger , Currency :: BitcoinTestnet ,
1514+ Some ( 10_000 ) , "Some description" . into ( ) , Duration :: from_secs ( 1234567 ) , 3600 , Some ( 30 ) ,
1515+ ) . unwrap ( ) ;
1516+ assert_eq ! ( invoice. min_final_cltv_expiry_delta( ) , 30 as u64 ) ;
1517+ }
1518+
1519+ #[ test]
1520+ fn test_create_invoice_fails_with_invalid_custom_min_final_cltv_expiry_delta ( ) {
1521+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
1522+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
1523+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
1524+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
1525+ let result = crate :: utils:: create_invoice_from_channelmanager_and_duration_since_epoch (
1526+ & nodes[ 1 ] . node , nodes[ 1 ] . keys_manager , nodes[ 1 ] . logger , Currency :: BitcoinTestnet ,
1527+ Some ( 10_000 ) , "Some description" . into ( ) , Duration :: from_secs ( 1234567 ) , 3600 , Some ( MIN_FINAL_CLTV_EXPIRY_DELTA - 1 ) ,
1528+ ) ;
1529+ match result {
1530+ Err ( SignOrCreationError :: CreationError ( CreationError :: MinFinalCltvExpiryDeltaTooShort ) ) => { } ,
1531+ _ => panic ! ( ) ,
1532+ }
1533+ }
14971534}
0 commit comments