Skip to content

Commit edd3030

Browse files
authored
Merge pull request lightningdevkit#1474 from dunxen/2022-05-actually-add-expiry
lightning-invoice/utils: Actually add expiry to invoices
2 parents 29727a3 + 3369b29 commit edd3030

File tree

2 files changed

+48
-20
lines changed

2 files changed

+48
-20
lines changed

lightning-invoice/src/payment.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1558,7 +1558,7 @@ mod tests {
15581558

15591559
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
15601560
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
1561-
duration_since_epoch()).unwrap())
1561+
duration_since_epoch(), 3600).unwrap())
15621562
.is_ok());
15631563
let htlc_msgs = nodes[0].node.get_and_clear_pending_msg_events();
15641564
assert_eq!(htlc_msgs.len(), 2);
@@ -1604,7 +1604,7 @@ mod tests {
16041604

16051605
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
16061606
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
1607-
duration_since_epoch()).unwrap())
1607+
duration_since_epoch(), 3600).unwrap())
16081608
.is_ok());
16091609
let htlc_msgs = nodes[0].node.get_and_clear_pending_msg_events();
16101610
assert_eq!(htlc_msgs.len(), 2);
@@ -1686,7 +1686,7 @@ mod tests {
16861686

16871687
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
16881688
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
1689-
duration_since_epoch()).unwrap())
1689+
duration_since_epoch(), 3600).unwrap())
16901690
.is_ok());
16911691
let htlc_updates = SendEvent::from_node(&nodes[0]);
16921692
check_added_monitors!(nodes[0], 1);

lightning-invoice/src/utils.rs

+45-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Convenient utilities to create an invoice.
22
3-
use {CreationError, Currency, DEFAULT_EXPIRY_TIME, Invoice, InvoiceBuilder, SignOrCreationError};
3+
use {CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError};
44
use payment::{Payer, Router};
55

66
use crate::{prelude::*, Description, InvoiceDescription, Sha256};
@@ -20,7 +20,6 @@ use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
2020
use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route};
2121
use lightning::util::logger::Logger;
2222
use secp256k1::PublicKey;
23-
use core::convert::TryInto;
2423
use core::ops::Deref;
2524
use core::time::Duration;
2625
use sync::Mutex;
@@ -162,7 +161,8 @@ fn _create_phantom_invoice<Signer: Sign, K: Deref>(
162161
.current_timestamp()
163162
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
164163
.payment_secret(payment_secret)
165-
.min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into());
164+
.min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into())
165+
.expiry_time(Duration::from_secs(invoice_expiry_delta_secs.into()));
166166
if let Some(amt) = amt_msat {
167167
invoice = invoice.amount_milli_satoshis(amt);
168168
}
@@ -212,9 +212,12 @@ fn _create_phantom_invoice<Signer: Sign, K: Deref>(
212212
/// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
213213
/// doesn't have to store preimage/payment secret information and (b) `ChannelManager` can verify
214214
/// that the payment secret is valid when the invoice is paid.
215+
///
216+
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
217+
/// in excess of the current time.
215218
pub fn create_invoice_from_channelmanager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
216219
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
217-
amt_msat: Option<u64>, description: String
220+
amt_msat: Option<u64>, description: String, invoice_expiry_delta_secs: u32
218221
) -> Result<Invoice, SignOrCreationError<()>>
219222
where
220223
M::Target: chain::Watch<Signer>,
@@ -227,7 +230,8 @@ where
227230
let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
228231
.expect("for the foreseeable future this shouldn't happen");
229232
create_invoice_from_channelmanager_and_duration_since_epoch(
230-
channelmanager, keys_manager, network, amt_msat, description, duration
233+
channelmanager, keys_manager, network, amt_msat,
234+
description, duration, invoice_expiry_delta_secs
231235
)
232236
}
233237

@@ -238,9 +242,12 @@ where
238242
/// doesn't have to store preimage/payment secret information and (b) `ChannelManager` can verify
239243
/// that the payment secret is valid when the invoice is paid.
240244
/// Use this variant if you want to pass the `description_hash` to the invoice.
245+
///
246+
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
247+
/// in excess of the current time.
241248
pub fn create_invoice_from_channelmanager_with_description_hash<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
242249
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
243-
amt_msat: Option<u64>, description_hash: Sha256,
250+
amt_msat: Option<u64>, description_hash: Sha256, invoice_expiry_delta_secs: u32
244251
) -> Result<Invoice, SignOrCreationError<()>>
245252
where
246253
M::Target: chain::Watch<Signer>,
@@ -256,7 +263,8 @@ where
256263
.expect("for the foreseeable future this shouldn't happen");
257264

258265
create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch(
259-
channelmanager, keys_manager, network, amt_msat, description_hash, duration,
266+
channelmanager, keys_manager, network, amt_msat,
267+
description_hash, duration, invoice_expiry_delta_secs
260268
)
261269
}
262270

@@ -266,6 +274,7 @@ where
266274
pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
267275
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
268276
amt_msat: Option<u64>, description_hash: Sha256, duration_since_epoch: Duration,
277+
invoice_expiry_delta_secs: u32
269278
) -> Result<Invoice, SignOrCreationError<()>>
270279
where
271280
M::Target: chain::Watch<Signer>,
@@ -277,7 +286,7 @@ where
277286
_create_invoice_from_channelmanager_and_duration_since_epoch(
278287
channelmanager, keys_manager, network, amt_msat,
279288
InvoiceDescription::Hash(&description_hash),
280-
duration_since_epoch,
289+
duration_since_epoch, invoice_expiry_delta_secs
281290
)
282291
}
283292

@@ -287,6 +296,7 @@ where
287296
pub fn create_invoice_from_channelmanager_and_duration_since_epoch<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
288297
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
289298
amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
299+
invoice_expiry_delta_secs: u32
290300
) -> Result<Invoice, SignOrCreationError<()>>
291301
where
292302
M::Target: chain::Watch<Signer>,
@@ -300,13 +310,14 @@ where
300310
InvoiceDescription::Direct(
301311
&Description::new(description).map_err(SignOrCreationError::CreationError)?,
302312
),
303-
duration_since_epoch,
313+
duration_since_epoch, invoice_expiry_delta_secs
304314
)
305315
}
306316

307317
fn _create_invoice_from_channelmanager_and_duration_since_epoch<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
308318
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
309319
amt_msat: Option<u64>, description: InvoiceDescription, duration_since_epoch: Duration,
320+
invoice_expiry_delta_secs: u32
310321
) -> Result<Invoice, SignOrCreationError<()>>
311322
where
312323
M::Target: chain::Watch<Signer>,
@@ -320,7 +331,7 @@ where
320331
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
321332
// supply.
322333
let (payment_hash, payment_secret) = channelmanager
323-
.create_inbound_payment(amt_msat, DEFAULT_EXPIRY_TIME.try_into().unwrap())
334+
.create_inbound_payment(amt_msat, invoice_expiry_delta_secs)
324335
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
325336
let our_node_pubkey = channelmanager.get_our_node_id();
326337

@@ -337,7 +348,8 @@ where
337348
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
338349
.payment_secret(payment_secret)
339350
.basic_mpp()
340-
.min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into());
351+
.min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into())
352+
.expiry_time(Duration::from_secs(invoice_expiry_delta_secs.into()));
341353
if let Some(amt) = amt_msat {
342354
invoice = invoice.amount_milli_satoshis(amt);
343355
}
@@ -526,12 +538,14 @@ mod test {
526538
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
527539
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
528540
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
541+
let non_default_invoice_expiry_secs = 4200;
529542
let invoice = create_invoice_from_channelmanager_and_duration_since_epoch(
530543
&nodes[1].node, nodes[1].keys_manager, Currency::BitcoinTestnet, Some(10_000), "test".to_string(),
531-
Duration::from_secs(1234567)).unwrap();
544+
Duration::from_secs(1234567), non_default_invoice_expiry_secs).unwrap();
532545
assert_eq!(invoice.amount_pico_btc(), Some(100_000));
533546
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
534547
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
548+
assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
535549

536550
// Invoice SCIDs should always use inbound SCID aliases over the real channel ID, if one is
537551
// available.
@@ -592,7 +606,7 @@ mod test {
592606
let description_hash = crate::Sha256(Hash::hash("Testing description_hash".as_bytes()));
593607
let invoice = ::utils::create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch(
594608
&nodes[1].node, nodes[1].keys_manager, Currency::BitcoinTestnet, Some(10_000),
595-
description_hash, Duration::from_secs(1234567),
609+
description_hash, Duration::from_secs(1234567), 3600
596610
).unwrap();
597611
assert_eq!(invoice.amount_pico_btc(), Some(100_000));
598612
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
@@ -752,7 +766,7 @@ mod test {
752766
) {
753767
let invoice = create_invoice_from_channelmanager_and_duration_since_epoch(
754768
&invoice_node.node, invoice_node.keys_manager, Currency::BitcoinTestnet, invoice_amt, "test".to_string(),
755-
Duration::from_secs(1234567)).unwrap();
769+
Duration::from_secs(1234567), 3600).unwrap();
756770
let hints = invoice.private_routes();
757771

758772
for hint in hints {
@@ -799,8 +813,14 @@ mod test {
799813
} else {
800814
None
801815
};
816+
let non_default_invoice_expiry_secs = 4200;
802817

803-
let invoice = ::utils::create_phantom_invoice::<EnforcingSigner, &test_utils::TestKeysInterface>(Some(payment_amt), payment_hash, "test".to_string(), 3600, route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet).unwrap();
818+
let invoice = ::utils::create_phantom_invoice::<
819+
EnforcingSigner, &test_utils::TestKeysInterface
820+
>(
821+
Some(payment_amt), payment_hash, "test".to_string(), non_default_invoice_expiry_secs,
822+
route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet
823+
).unwrap();
804824
let (payment_hash, payment_secret) = (PaymentHash(invoice.payment_hash().into_inner()), *invoice.payment_secret());
805825
let payment_preimage = if user_generated_pmt_hash {
806826
user_payment_preimage
@@ -811,6 +831,7 @@ mod test {
811831
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
812832
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
813833
assert_eq!(invoice.route_hints().len(), 2);
834+
assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
814835
assert!(!invoice.features().unwrap().supports_basic_mpp());
815836

816837
let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key())
@@ -931,10 +952,17 @@ mod test {
931952
];
932953

933954
let description_hash = crate::Sha256(Hash::hash("Description hash phantom invoice".as_bytes()));
934-
let invoice = ::utils::create_phantom_invoice_with_description_hash::<EnforcingSigner,&test_utils::TestKeysInterface>(Some(payment_amt), None, 3600, description_hash, route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet).unwrap();
935-
955+
let non_default_invoice_expiry_secs = 4200;
956+
let invoice = ::utils::create_phantom_invoice_with_description_hash::<
957+
EnforcingSigner, &test_utils::TestKeysInterface,
958+
>(
959+
Some(payment_amt), None, non_default_invoice_expiry_secs, description_hash,
960+
route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet
961+
)
962+
.unwrap();
936963
assert_eq!(invoice.amount_pico_btc(), Some(200_000));
937964
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
965+
assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
938966
assert_eq!(invoice.description(), InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Description hash phantom invoice".as_bytes()))));
939967
}
940968

0 commit comments

Comments
 (0)