Merge pull request #1163 from TheBlueMatt/2021-11-support-insecure-counterparty
[rust-lightning] / lightning-invoice / src / lib.rs
index 5388321694d6f5d9c3fd68999629fafcc89aae03..04ba08bd8552c34379576b59047de719ef200ae3 100644 (file)
@@ -379,7 +379,8 @@ pub enum TaggedField {
 
 /// SHA-256 hash
 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
-pub struct Sha256(pub sha256::Hash);
+pub struct Sha256(/// (C-not exported) as the native hash types are not currently mapped
+       pub sha256::Hash);
 
 /// Description string
 ///
@@ -1188,6 +1189,19 @@ impl Invoice {
                        .unwrap_or(Duration::from_secs(DEFAULT_EXPIRY_TIME))
        }
 
+       /// Returns whether the invoice has expired.
+       pub fn is_expired(&self) -> bool {
+               Self::is_expired_from_epoch(self.timestamp(), self.expiry_time())
+       }
+
+       /// Returns whether the expiry time from the given epoch has passed.
+       pub(crate) fn is_expired_from_epoch(epoch: &SystemTime, expiry_time: Duration) -> bool {
+               match epoch.elapsed() {
+                       Ok(elapsed) => elapsed > expiry_time,
+                       Err(_) => false,
+               }
+       }
+
        /// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
        /// [`DEFAULT_MIN_FINAL_CLTV_EXPIRY`].
        pub fn min_final_cltv_expiry(&self) -> u64 {
@@ -1920,5 +1934,33 @@ mod test {
 
                assert_eq!(invoice.min_final_cltv_expiry(), DEFAULT_MIN_FINAL_CLTV_EXPIRY);
                assert_eq!(invoice.expiry_time(), Duration::from_secs(DEFAULT_EXPIRY_TIME));
+               assert!(!invoice.is_expired());
+       }
+
+       #[test]
+       fn test_expiration() {
+               use ::*;
+               use secp256k1::Secp256k1;
+               use secp256k1::key::SecretKey;
+
+               let timestamp = SystemTime::now()
+                       .checked_sub(Duration::from_secs(DEFAULT_EXPIRY_TIME * 2))
+                       .unwrap();
+               let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
+                       .description("Test".into())
+                       .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
+                       .payment_secret(PaymentSecret([0; 32]))
+                       .timestamp(timestamp)
+                       .build_raw()
+                       .unwrap()
+                       .sign::<_, ()>(|hash| {
+                               let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
+                               let secp_ctx = Secp256k1::new();
+                               Ok(secp_ctx.sign_recoverable(hash, &privkey))
+                       })
+                       .unwrap();
+               let invoice = Invoice::from_signed(signed_invoice).unwrap();
+
+               assert!(invoice.is_expired());
        }
 }