Test feature bit semantics in Invoice::from_signed
authorJeffrey Czyz <jkczyz@gmail.com>
Fri, 30 Apr 2021 21:30:58 +0000 (14:30 -0700)
committerJeffrey Czyz <jkczyz@gmail.com>
Mon, 3 May 2021 23:23:28 +0000 (16:23 -0700)
lightning-invoice/src/lib.rs

index 2aeee2c4535bcc284106fbb1ca77f1ba76859fb9..791ff37bf49fae361a29f821b991b1116b7ab126 100644 (file)
@@ -1525,6 +1525,97 @@ mod test {
                assert!(new_signed.check_signature());
        }
 
+       #[test]
+       fn test_check_feature_bits() {
+               use TaggedField::*;
+               use lightning::ln::features::InvoiceFeatures;
+               use secp256k1::Secp256k1;
+               use secp256k1::key::SecretKey;
+               use {RawInvoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp, Invoice,
+                        SemanticError};
+
+               let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
+               let payment_secret = lightning::ln::PaymentSecret([21; 32]);
+               let invoice_template = RawInvoice {
+                       hrp: RawHrp {
+                               currency: Currency::Bitcoin,
+                               raw_amount: None,
+                               si_prefix: None,
+                       },
+                       data: RawDataPart {
+                               timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
+                               tagged_fields: vec ! [
+                                       PaymentHash(Sha256(sha256::Hash::from_hex(
+                                               "0001020304050607080900010203040506070809000102030405060708090102"
+                                       ).unwrap())).into(),
+                                       Description(
+                                               ::Description::new(
+                                                       "Please consider supporting this project".to_owned()
+                                               ).unwrap()
+                                       ).into(),
+                               ],
+                       },
+               };
+
+               // Missing features
+               let invoice = {
+                       let mut invoice = invoice_template.clone();
+                       invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
+                       invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+               }.unwrap();
+               assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
+
+               // Missing feature bits
+               let invoice = {
+                       let mut invoice = invoice_template.clone();
+                       invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
+                       invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
+                       invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+               }.unwrap();
+               assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
+
+               // Including payment secret and feature bits
+               let invoice = {
+                       let mut invoice = invoice_template.clone();
+                       invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
+                       invoice.data.tagged_fields.push(Features(InvoiceFeatures::known()).into());
+                       invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+               }.unwrap();
+               assert!(Invoice::from_signed(invoice).is_ok());
+
+               // No payment secret or features
+               let invoice = {
+                       let invoice = invoice_template.clone();
+                       invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+               }.unwrap();
+               assert!(Invoice::from_signed(invoice).is_ok());
+
+               // No payment secret or feature bits
+               let invoice = {
+                       let mut invoice = invoice_template.clone();
+                       invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
+                       invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+               }.unwrap();
+               assert!(Invoice::from_signed(invoice).is_ok());
+
+               // Missing payment secret
+               let invoice = {
+                       let mut invoice = invoice_template.clone();
+                       invoice.data.tagged_fields.push(Features(InvoiceFeatures::known()).into());
+                       invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+               }.unwrap();
+               assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
+
+               // Multiple payment secrets
+               let invoice = {
+                       let mut invoice = invoice_template.clone();
+                       invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
+                       invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
+                       invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+               }.unwrap();
+               assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::MultiplePaymentSecrets));
+       }
+
        #[test]
        fn test_builder_amount() {
                use ::*;