From 383ebc040642e34f90776aa66ac53d0b694d9e9d Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Mon, 26 Apr 2021 13:08:19 -0700 Subject: [PATCH] Require min_final_cltv_expiry in invoice --- lightning-invoice/src/lib.rs | 54 +++++++++++++++++-------------- lightning-invoice/tests/ser_de.rs | 9 +++--- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lightning-invoice/src/lib.rs b/lightning-invoice/src/lib.rs index 9c676e434..0b112a72a 100644 --- a/lightning-invoice/src/lib.rs +++ b/lightning-invoice/src/lib.rs @@ -146,6 +146,7 @@ pub fn check_platform() { /// .description("Coins pls!".into()) /// .payment_hash(payment_hash) /// .current_timestamp() +/// .min_final_cltv_expiry(144) /// .build_signed(|hash| { /// Secp256k1::new().sign_recoverable(hash, &private_key) /// }) @@ -164,7 +165,7 @@ pub fn check_platform() { /// /// (C-not exported) as we likely need to manually select one set of boolean type parameters. #[derive(Eq, PartialEq, Debug, Clone)] -pub struct InvoiceBuilder { +pub struct InvoiceBuilder { currency: Currency, amount: Option, si_prefix: Option, @@ -175,6 +176,7 @@ pub struct InvoiceBuilder { phantom_d: std::marker::PhantomData, phantom_h: std::marker::PhantomData, phantom_t: std::marker::PhantomData, + phantom_c: std::marker::PhantomData, } /// Represents a syntactically and semantically correct lightning BOLT11 invoice. @@ -426,7 +428,7 @@ pub mod constants { pub const TAG_FEATURES: u8 = 5; } -impl InvoiceBuilder { +impl InvoiceBuilder { /// Construct new, empty `InvoiceBuilder`. All necessary fields have to be filled first before /// `InvoiceBuilder::build(self)` becomes available. pub fn new(currrency: Currency) -> Self { @@ -441,14 +443,15 @@ impl InvoiceBuilder { phantom_d: std::marker::PhantomData, phantom_h: std::marker::PhantomData, phantom_t: std::marker::PhantomData, + phantom_c: std::marker::PhantomData, } } } -impl InvoiceBuilder { +impl InvoiceBuilder { /// Helper function to set the completeness flags. - fn set_flags(self) -> InvoiceBuilder { - InvoiceBuilder:: { + fn set_flags(self) -> InvoiceBuilder { + InvoiceBuilder:: { currency: self.currency, amount: self.amount, si_prefix: self.si_prefix, @@ -459,6 +462,7 @@ impl InvoiceBuilder { phantom_d: std::marker::PhantomData, phantom_h: std::marker::PhantomData, phantom_t: std::marker::PhantomData, + phantom_c: std::marker::PhantomData, } } @@ -494,12 +498,6 @@ impl InvoiceBuilder { self } - /// Sets `min_final_cltv_expiry`. - pub fn min_final_cltv_expiry(mut self, min_final_cltv_expiry: u64) -> Self { - self.tagged_fields.push(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry(min_final_cltv_expiry))); - self - } - /// Adds a fallback address. pub fn fallback(mut self, fallback: Fallback) -> Self { self.tagged_fields.push(TaggedField::Fallback(fallback)); @@ -523,7 +521,7 @@ impl InvoiceBuilder { } } -impl InvoiceBuilder { +impl InvoiceBuilder { /// Builds a `RawInvoice` if no `CreationError` occurred while construction any of the fields. pub fn build_raw(self) -> Result { @@ -556,9 +554,9 @@ impl InvoiceBuilder { } } -impl InvoiceBuilder { +impl InvoiceBuilder { /// Set the description. This function is only available if no description (hash) was set. - pub fn description(mut self, description: String) -> InvoiceBuilder { + pub fn description(mut self, description: String) -> InvoiceBuilder { match Description::new(description) { Ok(d) => self.tagged_fields.push(TaggedField::Description(d)), Err(e) => self.error = Some(e), @@ -567,23 +565,23 @@ impl InvoiceBuilder { } /// Set the description hash. This function is only available if no description (hash) was set. - pub fn description_hash(mut self, description_hash: sha256::Hash) -> InvoiceBuilder { + pub fn description_hash(mut self, description_hash: sha256::Hash) -> InvoiceBuilder { self.tagged_fields.push(TaggedField::DescriptionHash(Sha256(description_hash))); self.set_flags() } } -impl InvoiceBuilder { +impl InvoiceBuilder { /// Set the payment hash. This function is only available if no payment hash was set. - pub fn payment_hash(mut self, hash: sha256::Hash) -> InvoiceBuilder { + pub fn payment_hash(mut self, hash: sha256::Hash) -> InvoiceBuilder { self.tagged_fields.push(TaggedField::PaymentHash(Sha256(hash))); self.set_flags() } } -impl InvoiceBuilder { +impl InvoiceBuilder { /// Sets the timestamp. - pub fn timestamp(mut self, time: SystemTime) -> InvoiceBuilder { + pub fn timestamp(mut self, time: SystemTime) -> InvoiceBuilder { match PositiveTimestamp::from_system_time(time) { Ok(t) => self.timestamp = Some(t), Err(e) => self.error = Some(e), @@ -593,14 +591,22 @@ impl InvoiceBuilder { } /// Sets the timestamp to the current UNIX timestamp. - pub fn current_timestamp(mut self) -> InvoiceBuilder { + pub fn current_timestamp(mut self) -> InvoiceBuilder { let now = PositiveTimestamp::from_system_time(SystemTime::now()); self.timestamp = Some(now.expect("for the foreseeable future this shouldn't happen")); self.set_flags() } } -impl InvoiceBuilder { +impl InvoiceBuilder { + /// Sets `min_final_cltv_expiry`. + pub fn min_final_cltv_expiry(mut self, min_final_cltv_expiry: u64) -> InvoiceBuilder { + self.tagged_fields.push(TaggedField::MinFinalCltvExpiry(MinFinalCltvExpiry(min_final_cltv_expiry))); + self.set_flags() + } +} + +impl InvoiceBuilder { /// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail /// and MUST produce a recoverable signature valid for the given hash and if applicable also for /// the included payee public key. @@ -1489,7 +1495,8 @@ mod test { let builder = InvoiceBuilder::new(Currency::Bitcoin) .payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap()) - .current_timestamp(); + .current_timestamp() + .min_final_cltv_expiry(144); let too_long_string = String::from_iter( (0..1024).map(|_| '?') @@ -1606,7 +1613,6 @@ mod test { .payee_pub_key(public_key.clone()) .expiry_time(Duration::from_secs(54321)) .min_final_cltv_expiry(144) - .min_final_cltv_expiry(143) .fallback(Fallback::PubKeyHash([0;20])) .route(route_1.clone()) .route(route_2.clone()) @@ -1618,7 +1624,7 @@ mod test { }).unwrap(); assert!(invoice.check_signature().is_ok()); - assert_eq!(invoice.tagged_fields().count(), 9); + assert_eq!(invoice.tagged_fields().count(), 8); assert_eq!(invoice.amount_pico_btc(), Some(123)); assert_eq!(invoice.currency(), Currency::BitcoinTestnet); diff --git a/lightning-invoice/tests/ser_de.rs b/lightning-invoice/tests/ser_de.rs index 403f8f1f0..ba3836940 100644 --- a/lightning-invoice/tests/ser_de.rs +++ b/lightning-invoice/tests/ser_de.rs @@ -110,13 +110,14 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, Option)> { .amount_pico_btc(20000000000) .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) .payment_secret(PaymentSecret([42; 32])) - .build_signed(|msg_hash| { + .build_raw() + .unwrap() + .sign::<_, ()>(|msg_hash| { let privkey = SecretKey::from_slice(&[41; 32]).unwrap(); let secp_ctx = Secp256k1::new(); - secp_ctx.sign_recoverable(msg_hash, &privkey) + Ok(secp_ctx.sign_recoverable(msg_hash, &privkey)) }) - .unwrap() - .into_signed_raw(), + .unwrap(), None ) ] -- 2.39.5