Do not return a reference to a u64 in rust-lightning-invoices
[rust-lightning] / lightning-invoice / src / lib.rs
index 8820274c38146d26a8c146518b057652054f7a08..5a9c094c63be6d1190249df462a4bd4c3a794154 100644 (file)
@@ -24,9 +24,10 @@ extern crate secp256k1;
 use bech32::u5;
 use bitcoin_hashes::Hash;
 use bitcoin_hashes::sha256;
+use lightning::ln::features::InvoiceFeatures;
 #[cfg(any(doc, test))]
 use lightning::routing::network_graph::RoutingFees;
-use lightning::routing::router::RouteHint;
+use lightning::routing::router::RouteHintHop;
 
 use secp256k1::key::PublicKey;
 use secp256k1::{Message, Secp256k1};
@@ -327,8 +328,9 @@ pub enum TaggedField {
        ExpiryTime(ExpiryTime),
        MinFinalCltvExpiry(MinFinalCltvExpiry),
        Fallback(Fallback),
-       Route(Route),
+       Route(RouteHint),
        PaymentSecret(PaymentSecret),
+       Features(InvoiceFeatures),
 }
 
 /// SHA-256 hash
@@ -387,7 +389,7 @@ pub struct Signature(pub RecoverableSignature);
 /// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
 ///
 #[derive(Eq, PartialEq, Debug, Clone)]
-pub struct Route(Vec<RouteHint>);
+pub struct RouteHint(Vec<RouteHintHop>);
 
 /// Tag constants as specified in BOLT11
 #[allow(missing_docs)]
@@ -401,6 +403,7 @@ pub mod constants {
        pub const TAG_FALLBACK: u8 = 9;
        pub const TAG_ROUTE: u8 = 3;
        pub const TAG_PAYMENT_SECRET: u8 = 16;
+       pub const TAG_FEATURES: u8 = 5;
 }
 
 impl InvoiceBuilder<tb::False, tb::False, tb::False> {
@@ -484,13 +487,20 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool> InvoiceBuilder<D, H, T> {
        }
 
        /// Adds a private route.
-       pub fn route(mut self, route: Vec<RouteHint>) -> Self {
-               match Route::new(route) {
+       pub fn route(mut self, route: Vec<RouteHintHop>) -> Self {
+               match RouteHint::new(route) {
                        Ok(r) => self.tagged_fields.push(TaggedField::Route(r)),
                        Err(e) => self.error = Some(e),
                }
                self
        }
+
+       /// Adds a features field which indicates the set of supported protocol extensions which the
+       /// origin node supports.
+       pub fn features(mut self, features: InvoiceFeatures) -> Self {
+               self.tagged_fields.push(TaggedField::Features(features));
+               self
+       }
 }
 
 impl<D: tb::Bool, H: tb::Bool> InvoiceBuilder<D, H, tb::True> {
@@ -810,6 +820,10 @@ impl RawInvoice {
                find_extract!(self.known_tagged_fields(), TaggedField::PaymentSecret(ref x), x)
        }
 
+       pub fn features(&self) -> Option<&InvoiceFeatures> {
+               find_extract!(self.known_tagged_fields(), TaggedField::Features(ref x), x)
+       }
+
        pub fn fallbacks(&self) -> Vec<&Fallback> {
                self.known_tagged_fields().filter_map(|tf| match tf {
                        &TaggedField::Fallback(ref f) => Some(f),
@@ -817,11 +831,11 @@ impl RawInvoice {
                }).collect::<Vec<&Fallback>>()
        }
 
-       pub fn routes(&self) -> Vec<&Route> {
+       pub fn routes(&self) -> Vec<&RouteHint> {
                self.known_tagged_fields().filter_map(|tf| match tf {
                        &TaggedField::Route(ref r) => Some(r),
                        _ => None,
-               }).collect::<Vec<&Route>>()
+               }).collect::<Vec<&RouteHint>>()
        }
 
        pub fn amount_pico_btc(&self) -> Option<u64> {
@@ -992,10 +1006,15 @@ impl Invoice {
                self.signed_invoice.payee_pub_key().map(|x| &x.0)
        }
 
-    /// Get the payment secret if one was included in the invoice
-    pub fn payment_secret(&self) -> Option<&PaymentSecret> {
-        self.signed_invoice.payment_secret()
-    }
+       /// Get the payment secret if one was included in the invoice
+       pub fn payment_secret(&self) -> Option<&PaymentSecret> {
+               self.signed_invoice.payment_secret()
+       }
+
+       /// Get the invoice features if they were included in the invoice
+       pub fn features(&self) -> Option<&InvoiceFeatures> {
+               self.signed_invoice.features()
+       }
 
        /// Recover the payee's public key (only to be used if none was included in the invoice)
        pub fn recover_payee_pub_key(&self) -> PublicKey {
@@ -1010,8 +1029,8 @@ impl Invoice {
        }
 
        /// Returns the invoice's `min_cltv_expiry` time if present
-       pub fn min_final_cltv_expiry(&self) -> Option<&u64> {
-               self.signed_invoice.min_final_cltv_expiry().map(|x| &x.0)
+       pub fn min_final_cltv_expiry(&self) -> Option<u64> {
+               self.signed_invoice.min_final_cltv_expiry().map(|x| x.0)
        }
 
        /// Returns a list of all fallback addresses
@@ -1020,7 +1039,7 @@ impl Invoice {
        }
 
        /// Returns a list of all routes included in the invoice
-       pub fn routes(&self) -> Vec<&Route> {
+       pub fn routes(&self) -> Vec<&RouteHint> {
                self.signed_invoice.routes()
        }
 
@@ -1054,6 +1073,7 @@ impl TaggedField {
                        TaggedField::Fallback(_) => constants::TAG_FALLBACK,
                        TaggedField::Route(_) => constants::TAG_ROUTE,
                        TaggedField::PaymentSecret(_) => constants::TAG_PAYMENT_SECRET,
+                       TaggedField::Features(_) => constants::TAG_FEATURES,
                };
 
                u5::try_from_u8(tag).expect("all tags defined are <32")
@@ -1142,32 +1162,32 @@ impl ExpiryTime {
        }
 }
 
-impl Route {
+impl RouteHint {
        /// Create a new (partial) route from a list of hops
-       pub fn new(hops: Vec<RouteHint>) -> Result<Route, CreationError> {
+       pub fn new(hops: Vec<RouteHintHop>) -> Result<RouteHint, CreationError> {
                if hops.len() <= 12 {
-                       Ok(Route(hops))
+                       Ok(RouteHint(hops))
                } else {
                        Err(CreationError::RouteTooLong)
                }
        }
 
        /// Returrn the underlying vector of hops
-       pub fn into_inner(self) -> Vec<RouteHint> {
+       pub fn into_inner(self) -> Vec<RouteHintHop> {
                self.0
        }
 }
 
-impl Into<Vec<RouteHint>> for Route {
-       fn into(self) -> Vec<RouteHint> {
+impl Into<Vec<RouteHintHop>> for RouteHint {
+       fn into(self) -> Vec<RouteHintHop> {
                self.into_inner()
        }
 }
 
-impl Deref for Route {
-       type Target = Vec<RouteHint>;
+impl Deref for RouteHint {
+       type Target = Vec<RouteHintHop>;
 
-       fn deref(&self) -> &Vec<RouteHint> {
+       fn deref(&self) -> &Vec<RouteHintHop> {
                &self.0
        }
 }
@@ -1443,7 +1463,7 @@ mod test {
                        .build_raw();
                assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
 
-               let route_hop = RouteHint {
+               let route_hop = RouteHintHop {
                        src_node_id: PublicKey::from_slice(
                                        &[
                                                0x03, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
@@ -1494,7 +1514,7 @@ mod test {
                let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key);
 
                let route_1 = vec![
-                       RouteHint {
+                       RouteHintHop {
                                src_node_id: public_key.clone(),
                                short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"),
                                fees: RoutingFees {
@@ -1505,7 +1525,7 @@ mod test {
                                htlc_minimum_msat: None,
                                htlc_maximum_msat: None,
                        },
-                       RouteHint {
+                       RouteHintHop {
                                src_node_id: public_key.clone(),
                                short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"),
                                fees: RoutingFees {
@@ -1519,7 +1539,7 @@ mod test {
                ];
 
                let route_2 = vec![
-                       RouteHint {
+                       RouteHintHop {
                                src_node_id: public_key.clone(),
                                short_channel_id: 0,
                                fees: RoutingFees {
@@ -1530,7 +1550,7 @@ mod test {
                                htlc_minimum_msat: None,
                                htlc_maximum_msat: None,
                        },
-                       RouteHint {
+                       RouteHintHop {
                                src_node_id: public_key.clone(),
                                short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"),
                                fees: RoutingFees {
@@ -1571,9 +1591,9 @@ mod test {
                );
                assert_eq!(invoice.payee_pub_key(), Some(&public_key));
                assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
-               assert_eq!(invoice.min_final_cltv_expiry(), Some(&144));
+               assert_eq!(invoice.min_final_cltv_expiry(), Some(144));
                assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
-               assert_eq!(invoice.routes(), vec![&Route(route_1), &Route(route_2)]);
+               assert_eq!(invoice.routes(), vec![&RouteHint(route_1), &RouteHint(route_2)]);
                assert_eq!(
                        invoice.description(),
                        InvoiceDescription::Hash(&Sha256(sha256::Hash::from_slice(&[3;32][..]).unwrap()))