Merge pull request #1331 from TheBlueMatt/2022-02-no-copy-invoice-fields
[rust-lightning] / lightning-invoice / src / lib.rs
index 0e05bf6d4aa74b96f15a9ea0f71141a6985b0967..1dd7123a486d1077f1295b7655b1fac497cfb57f 100644 (file)
@@ -5,6 +5,8 @@
 #![deny(unused_mut)]
 #![deny(broken_intra_doc_links)]
 
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+
 #![cfg_attr(feature = "strict", deny(warnings))]
 #![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
 
@@ -43,6 +45,7 @@ use lightning::ln::features::InvoiceFeatures;
 #[cfg(any(doc, test))]
 use lightning::routing::network_graph::RoutingFees;
 use lightning::routing::router::RouteHint;
+use lightning::util::invoice::construct_invoice_preimage;
 
 use secp256k1::key::PublicKey;
 use secp256k1::{Message, Secp256k1};
@@ -609,9 +612,9 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, T,
 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C, tb::False> {
        /// Sets the payment secret and relevant features.
        pub fn payment_secret(mut self, payment_secret: PaymentSecret) -> InvoiceBuilder<D, H, T, C, tb::True> {
-               let features = InvoiceFeatures::empty()
-                       .set_variable_length_onion_required()
-                       .set_payment_secret_required();
+               let mut features = InvoiceFeatures::empty();
+               features.set_variable_length_onion_required();
+               features.set_payment_secret_required();
                self.tagged_fields.push(TaggedField::PaymentSecret(payment_secret));
                self.tagged_fields.push(TaggedField::Features(features));
                self.set_flags()
@@ -621,13 +624,11 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T,
 impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C, tb::True> {
        /// Sets the `basic_mpp` feature as optional.
        pub fn basic_mpp(mut self) -> Self {
-               self.tagged_fields = self.tagged_fields
-                       .drain(..)
-                       .map(|field| match field {
-                               TaggedField::Features(f) => TaggedField::Features(f.set_basic_mpp_optional()),
-                               _ => field,
-                       })
-                       .collect();
+               for field in self.tagged_fields.iter_mut() {
+                       if let TaggedField::Features(f) = field {
+                               f.set_basic_mpp_optional();
+                       }
+               }
                self
        }
 }
@@ -799,32 +800,9 @@ macro_rules! find_all_extract {
 
 #[allow(missing_docs)]
 impl RawInvoice {
-       /// Construct the invoice's HRP and signatureless data into a preimage to be hashed.
-       pub(crate) fn construct_invoice_preimage(hrp_bytes: &[u8], data_without_signature: &[u5]) -> Vec<u8> {
-               use bech32::FromBase32;
-
-               let mut preimage = Vec::<u8>::from(hrp_bytes);
-
-               let mut data_part = Vec::from(data_without_signature);
-               let overhang = (data_part.len() * 5) % 8;
-               if overhang > 0 {
-                       // add padding if data does not end at a byte boundary
-                       data_part.push(u5::try_from_u8(0).unwrap());
-
-                       // if overhang is in (1..3) we need to add u5(0) padding two times
-                       if overhang < 3 {
-                               data_part.push(u5::try_from_u8(0).unwrap());
-                       }
-               }
-
-               preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
-                       .expect("No padding error may occur due to appended zero above."));
-               preimage
-       }
-
        /// Hash the HRP as bytes and signatureless data part.
        fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] {
-               let preimage = RawInvoice::construct_invoice_preimage(hrp_bytes, data_without_signature);
+               let preimage = construct_invoice_preimage(hrp_bytes, data_without_signature);
                let mut hash: [u8; 32] = Default::default();
                hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]);
                hash
@@ -1409,6 +1387,12 @@ pub enum CreationError {
 
        /// The supplied millisatoshi amount was greater than the total bitcoin supply.
        InvalidAmount,
+
+       /// Route hints were required for this invoice and were missing. Applies to
+       /// [phantom invoices].
+       ///
+       /// [phantom invoices]: crate::utils::create_phantom_invoice
+       MissingRouteHints,
 }
 
 impl Display for CreationError {
@@ -1418,6 +1402,7 @@ impl Display for CreationError {
                        CreationError::RouteTooLong => f.write_str("The specified route has too many hops and can't be encoded"),
                        CreationError::TimestampOutOfBounds => f.write_str("The Unix timestamp of the supplied date is less than zero or greater than 35-bits"),
                        CreationError::InvalidAmount => f.write_str("The supplied millisatoshi amount was greater than the total bitcoin supply"),
+                       CreationError::MissingRouteHints => f.write_str("The invoice required route hints and they weren't provided"),
                }
        }
 }