X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-invoice%2Fsrc%2Flib.rs;h=94a93fe62b7f1d160c5f1dc0c89ba66b56bf34c1;hb=68149a204f9da3462f15cc108b5efe7a054cd27b;hp=17b6e2de2b6efe8582f9f78823289f306c9f4991;hpb=828c7767463116705559dee367778b4f677476c6;p=rust-lightning diff --git a/lightning-invoice/src/lib.rs b/lightning-invoice/src/lib.rs index 17b6e2de..94a93fe6 100644 --- a/lightning-invoice/src/lib.rs +++ b/lightning-invoice/src/lib.rs @@ -18,9 +18,11 @@ //! invoices and functions to create, encode and decode these. If you just want to use the standard //! en-/decoding functionality this should get you started: //! -//! * For parsing use `str::parse::(&self)` (see the docs of `impl FromStr for Invoice`) -//! * For constructing invoices use the `InvoiceBuilder` -//! * For serializing invoices use the `Display`/`ToString` traits +//! * For parsing use `str::parse::(&self)` (see [`Invoice::from_str`]) +//! * For constructing invoices use the [`InvoiceBuilder`] +//! * For serializing invoices use the [`Display`]/[`ToString`] traits +//! +//! [`Invoice::from_str`]: crate::Invoice#impl-FromStr #[cfg(not(any(feature = "std", feature = "no-std")))] compile_error!("at least one of the `std` or `no-std` features must be enabled"); @@ -45,8 +47,9 @@ extern crate serde; use std::time::SystemTime; use bech32::u5; -use bitcoin_hashes::Hash; -use bitcoin_hashes::sha256; +use bitcoin::{Address, Network, PubkeyHash, ScriptHash}; +use bitcoin::util::address::{Payload, WitnessVersion}; +use bitcoin_hashes::{Hash, sha256}; use lightning::ln::PaymentSecret; use lightning::ln::features::InvoiceFeatures; #[cfg(any(doc, test))] @@ -160,7 +163,7 @@ pub const DEFAULT_EXPIRY_TIME: u64 = 3600; /// [`MIN_FINAL_CLTV_EXPIRY_DELTA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18; -/// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures +/// Builder for [`Invoice`]s. It's the most convenient and advised way to use this library. It ensures /// that only a semantically and syntactically correct Invoice can be built using it. /// /// ``` @@ -212,11 +215,11 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18; /// # Type parameters /// The two parameters `D` and `H` signal if the builder already contains the correct amount of the /// given field: -/// * `D`: exactly one `Description` or `DescriptionHash` -/// * `H`: exactly one `PaymentHash` +/// * `D`: exactly one [`TaggedField::Description`] or [`TaggedField::DescriptionHash`] +/// * `H`: exactly one [`TaggedField::PaymentHash`] /// * `T`: the timestamp is set /// -/// (C-not exported) as we likely need to manually select one set of boolean type parameters. +/// This is not exported to bindings users as we likely need to manually select one set of boolean type parameters. #[derive(Eq, PartialEq, Debug, Clone)] pub struct InvoiceBuilder { currency: Currency, @@ -236,9 +239,11 @@ pub struct InvoiceBuilder(&str)` +/// 1. using [`InvoiceBuilder`] +/// 2. using [`Invoice::from_signed`] +/// 3. using `str::parse::(&str)` (see [`Invoice::from_str`]) +/// +/// [`Invoice::from_str`]: crate::Invoice#impl-FromStr #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct Invoice { signed_invoice: SignedRawInvoice, @@ -247,7 +252,7 @@ pub struct Invoice { /// Represents the description of an invoice which has to be either a directly included string or /// a hash of a description provided out of band. /// -/// (C-not exported) As we don't have a good way to map the reference lifetimes making this +/// This is not exported to bindings users as we don't have a good way to map the reference lifetimes making this /// practically impossible to use safely in languages like C. #[derive(Eq, PartialEq, Debug, Clone)] pub enum InvoiceDescription<'f> { @@ -258,34 +263,34 @@ pub enum InvoiceDescription<'f> { Hash(&'f Sha256), } -/// Represents a signed `RawInvoice` with cached hash. The signature is not checked and may be +/// Represents a signed [`RawInvoice`] with cached hash. The signature is not checked and may be /// invalid. /// /// # Invariants -/// The hash has to be either from the deserialized invoice or from the serialized `raw_invoice`. +/// The hash has to be either from the deserialized invoice or from the serialized [`RawInvoice`]. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct SignedRawInvoice { /// The rawInvoice that the signature belongs to raw_invoice: RawInvoice, - /// Hash of the `RawInvoice` that will be used to check the signature. + /// Hash of the [`RawInvoice`] that will be used to check the signature. /// /// * if the `SignedRawInvoice` was deserialized the hash is of from the original encoded form, /// since it's not guaranteed that encoding it again will lead to the same result since integers /// could have been encoded with leading zeroes etc. /// * if the `SignedRawInvoice` was constructed manually the hash will be the calculated hash - /// from the `RawInvoice` + /// from the [`RawInvoice`] hash: [u8; 32], /// signature of the payment request signature: InvoiceSignature, } -/// Represents an syntactically correct Invoice for a payment on the lightning network, +/// Represents an syntactically correct [`Invoice`] for a payment on the lightning network, /// but without the signature information. -/// De- and encoding should not lead to information loss but may lead to different hashes. +/// Decoding and encoding should not lead to information loss but may lead to different hashes. /// -/// For methods without docs see the corresponding methods in `Invoice`. +/// For methods without docs see the corresponding methods in [`Invoice`]. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct RawInvoice { /// human readable part @@ -295,9 +300,9 @@ pub struct RawInvoice { pub data: RawDataPart, } -/// Data of the `RawInvoice` that is encoded in the human readable part +/// Data of the [`RawInvoice`] that is encoded in the human readable part. /// -/// (C-not exported) As we don't yet support `Option` +/// This is not exported to bindings users as we don't yet support `Option` #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct RawHrp { /// The currency deferred from the 3rd and 4th character of the bech32 transaction @@ -310,7 +315,7 @@ pub struct RawHrp { pub si_prefix: Option, } -/// Data of the `RawInvoice` that is encoded in the data part +/// Data of the [`RawInvoice`] that is encoded in the data part #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct RawDataPart { /// generation time of the invoice @@ -357,7 +362,7 @@ impl SiPrefix { /// Returns all enum variants of `SiPrefix` sorted in descending order of their associated /// multiplier. /// - /// (C-not exported) As we don't yet support a slice of enums, and also because this function + /// This is not exported to bindings users as we don't yet support a slice of enums, and also because this function /// isn't the most critical to expose. pub fn values_desc() -> &'static [SiPrefix] { use crate::SiPrefix::*; @@ -385,9 +390,32 @@ pub enum Currency { Signet, } +impl From for Currency { + fn from(network: Network) -> Self { + match network { + Network::Bitcoin => Currency::Bitcoin, + Network::Testnet => Currency::BitcoinTestnet, + Network::Regtest => Currency::Regtest, + Network::Signet => Currency::Signet, + } + } +} + +impl From for Network { + fn from(currency: Currency) -> Self { + match currency { + Currency::Bitcoin => Network::Bitcoin, + Currency::BitcoinTestnet => Network::Testnet, + Currency::Regtest => Network::Regtest, + Currency::Simnet => Network::Regtest, + Currency::Signet => Network::Signet, + } + } +} + /// Tagged field which may have an unknown tag /// -/// (C-not exported) as we don't currently support TaggedField +/// This is not exported to bindings users as we don't currently support TaggedField #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub enum RawTaggedField { /// Parsed tagged field with known tag @@ -400,7 +428,7 @@ pub enum RawTaggedField { /// /// For descriptions of the enum values please refer to the enclosed type's docs. /// -/// (C-not exported) As we don't yet support enum variants with the same name the struct contained +/// This is not exported to bindings users as we don't yet support enum variants with the same name the struct contained /// in the variant. #[allow(missing_docs)] #[derive(Clone, Debug, Hash, Eq, PartialEq)] @@ -419,7 +447,7 @@ pub enum TaggedField { /// SHA-256 hash #[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct Sha256(/// (C-not exported) as the native hash types are not currently mapped +pub struct Sha256(/// This is not exported to bindings users as the native hash types are not currently mapped pub sha256::Hash); /// Description string @@ -442,17 +470,16 @@ pub struct ExpiryTime(Duration); #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct MinFinalCltvExpiryDelta(pub u64); -// TODO: better types instead onf byte arrays /// Fallback address in case no LN payment is possible #[allow(missing_docs)] #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub enum Fallback { SegWitProgram { - version: u5, + version: WitnessVersion, program: Vec, }, - PubKeyHash([u8; 20]), - ScriptHash([u8; 20]), + PubKeyHash(PubkeyHash), + ScriptHash(ScriptHash), } /// Recoverable signature @@ -564,7 +591,8 @@ impl InvoiceBui } impl InvoiceBuilder { - /// Builds a `RawInvoice` if no `CreationError` occurred while construction any of the fields. + /// Builds a [`RawInvoice`] if no [`CreationError`] occurred while construction any of the + /// fields. pub fn build_raw(self) -> Result { // If an error occurred at any time before, return it now @@ -585,13 +613,13 @@ impl InvoiceBuilder>(); let data = RawDataPart { - timestamp: timestamp, - tagged_fields: tagged_fields, + timestamp, + tagged_fields, }; Ok(RawInvoice { - hrp: hrp, - data: data, + hrp, + data, }) } } @@ -611,6 +639,18 @@ impl InvoiceBuilder InvoiceBuilder { + match description { + InvoiceDescription::Direct(desc) => { + self.description(desc.clone().into_inner()) + } + InvoiceDescription::Hash(hash) => { + self.description_hash(hash.0) + } + } + } } impl InvoiceBuilder { @@ -741,17 +781,17 @@ impl SignedRawInvoice { (self.raw_invoice, self.hash, self.signature) } - /// The `RawInvoice` which was signed. + /// The [`RawInvoice`] which was signed. pub fn raw_invoice(&self) -> &RawInvoice { &self.raw_invoice } - /// The hash of the `RawInvoice` that was signed. + /// The hash of the [`RawInvoice`] that was signed. pub fn signable_hash(&self) -> &[u8; 32] { &self.hash } - /// InvoiceSignature for the invoice. + /// Signature for the invoice. pub fn signature(&self) -> &InvoiceSignature { &self.signature } @@ -781,7 +821,7 @@ impl SignedRawInvoice { recovered_pub_key = Some(recovered); } - let pub_key = included_pub_key.or_else(|| recovered_pub_key.as_ref()) + let pub_key = included_pub_key.or(recovered_pub_key.as_ref()) .expect("One is always present"); let hash = Message::from_slice(&self.hash[..]) @@ -869,11 +909,11 @@ impl RawInvoice { ) } - /// Signs the invoice using the supplied `sign_function`. This function MAY fail with an error - /// of type `E`. Since the signature of a `SignedRawInvoice` is not required to be valid there + /// Signs the invoice using the supplied `sign_method`. This function MAY fail with an error of + /// type `E`. Since the signature of a [`SignedRawInvoice`] is not required to be valid there /// are no constraints regarding the validity of the produced signature. /// - /// (C-not exported) As we don't currently support passing function pointers into methods + /// This is not exported to bindings users as we don't currently support passing function pointers into methods /// explicitly. pub fn sign(self, sign_method: F) -> Result where F: FnOnce(&Message) -> Result @@ -892,7 +932,7 @@ impl RawInvoice { /// Returns an iterator over all tagged fields with known semantics. /// - /// (C-not exported) As there is not yet a manual mapping for a FilterMap + /// This is not exported to bindings users as there is not yet a manual mapping for a FilterMap pub fn known_tagged_fields(&self) -> FilterMap, fn(&RawTaggedField) -> Option<&TaggedField>> { @@ -941,7 +981,7 @@ impl RawInvoice { find_extract!(self.known_tagged_fields(), TaggedField::Features(ref x), x) } - /// (C-not exported) as we don't support Vec<&NonOpaqueType> + /// This is not exported to bindings users as we don't support Vec<&NonOpaqueType> pub fn fallbacks(&self) -> Vec<&Fallback> { find_all_extract!(self.known_tagged_fields(), TaggedField::Fallback(ref x), x).collect() } @@ -1014,13 +1054,18 @@ impl PositiveTimestamp { } #[cfg(feature = "std")] -impl Into for PositiveTimestamp { - fn into(self) -> SystemTime { - SystemTime::UNIX_EPOCH + self.0 +impl From for SystemTime { + fn from(val: PositiveTimestamp) -> Self { + SystemTime::UNIX_EPOCH + val.0 } } impl Invoice { + /// The hash of the [`RawInvoice`] that was signed. + pub fn signable_hash(&self) -> [u8; 32] { + self.signed_invoice.hash + } + /// Transform the `Invoice` into it's unchecked version pub fn into_signed_raw(self) -> SignedRawInvoice { self.signed_invoice @@ -1125,7 +1170,7 @@ impl Invoice { Ok(()) } - /// Constructs an `Invoice` from a `SignedRawInvoice` by checking all its invariants. + /// Constructs an `Invoice` from a [`SignedRawInvoice`] by checking all its invariants. /// ``` /// use lightning_invoice::*; /// @@ -1147,7 +1192,7 @@ impl Invoice { /// ``` pub fn from_signed(signed_invoice: SignedRawInvoice) -> Result { let invoice = Invoice { - signed_invoice: signed_invoice, + signed_invoice, }; invoice.check_field_counts()?; invoice.check_feature_bits()?; @@ -1170,7 +1215,7 @@ impl Invoice { /// Returns an iterator over all tagged fields of this Invoice. /// - /// (C-not exported) As there is not yet a manual mapping for a FilterMap + /// This is not exported to bindings users as there is not yet a manual mapping for a FilterMap pub fn tagged_fields(&self) -> FilterMap, fn(&RawTaggedField) -> Option<&TaggedField>> { self.signed_invoice.raw_invoice().known_tagged_fields() @@ -1183,11 +1228,11 @@ impl Invoice { /// Return the description or a hash of it for longer ones /// - /// (C-not exported) because we don't yet export InvoiceDescription + /// This is not exported to bindings users because we don't yet export InvoiceDescription pub fn description(&self) -> InvoiceDescription { - if let Some(ref direct) = self.signed_invoice.description() { + if let Some(direct) = self.signed_invoice.description() { return InvoiceDescription::Direct(direct); - } else if let Some(ref hash) = self.signed_invoice.description_hash() { + } else if let Some(hash) = self.signed_invoice.description_hash() { return InvoiceDescription::Hash(hash); } unreachable!("ensured by constructor"); @@ -1213,6 +1258,12 @@ impl Invoice { self.signed_invoice.recover_payee_pub_key().expect("was checked by constructor").0 } + /// Returns the Duration since the Unix epoch at which the invoice expires. + /// Returning None if overflow occurred. + pub fn expires_at(&self) -> Option { + self.duration_since_epoch().checked_add(self.expiry_time()) + } + /// Returns the invoice's expiry time, if present, otherwise [`DEFAULT_EXPIRY_TIME`]. pub fn expiry_time(&self) -> Duration { self.signed_invoice.expiry_time() @@ -1235,6 +1286,20 @@ impl Invoice { } } + /// Returns the Duration remaining until the invoice expires. + #[cfg(feature = "std")] + pub fn duration_until_expiry(&self) -> Duration { + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) + .map(|now| self.expiration_remaining_from_epoch(now)) + .unwrap_or(Duration::from_nanos(0)) + } + + /// Returns the Duration remaining until the invoice expires given the current time. + /// `time` is the timestamp as a duration since the Unix epoch. + pub fn expiration_remaining_from_epoch(&self, time: Duration) -> Duration { + self.expires_at().map(|x| x.checked_sub(time)).flatten().unwrap_or(Duration::from_nanos(0)) + } + /// Returns whether the expiry time would pass at the given point in time. /// `at_time` is the timestamp as a duration since the Unix epoch. pub fn would_expire(&self, at_time: Duration) -> bool { @@ -1253,11 +1318,30 @@ impl Invoice { /// Returns a list of all fallback addresses /// - /// (C-not exported) as we don't support Vec<&NonOpaqueType> + /// This is not exported to bindings users as we don't support Vec<&NonOpaqueType> pub fn fallbacks(&self) -> Vec<&Fallback> { self.signed_invoice.fallbacks() } + /// Returns a list of all fallback addresses as [`Address`]es + pub fn fallback_addresses(&self) -> Vec
{ + self.fallbacks().iter().map(|fallback| { + let payload = match fallback { + Fallback::SegWitProgram { version, program } => { + Payload::WitnessProgram { version: *version, program: program.to_vec() } + } + Fallback::PubKeyHash(pkh) => { + Payload::PubkeyHash(*pkh) + } + Fallback::ScriptHash(sh) => { + Payload::ScriptHash(*sh) + } + }; + + Address { payload, network: self.network() } + }).collect() + } + /// Returns a list of all routes included in the invoice pub fn private_routes(&self) -> Vec<&PrivateRoute> { self.signed_invoice.private_routes() @@ -1275,6 +1359,13 @@ impl Invoice { self.signed_invoice.currency() } + /// Returns the network for which the invoice was issued + /// + /// This is not exported to bindings users, see [`Self::currency`] instead. + pub fn network(&self) -> Network { + self.signed_invoice.currency().into() + } + /// Returns the amount if specified in the invoice as millisatoshis. pub fn amount_milli_satoshis(&self) -> Option { self.signed_invoice.amount_pico_btc().map(|v| v / 10) @@ -1315,7 +1406,7 @@ impl TaggedField { impl Description { /// Creates a new `Description` if `description` is at most 1023 __bytes__ long, - /// returns `CreationError::DescriptionTooLong` otherwise + /// returns [`CreationError::DescriptionTooLong`] otherwise /// /// Please note that single characters may use more than one byte due to UTF8 encoding. pub fn new(description: String) -> Result { @@ -1326,15 +1417,15 @@ impl Description { } } - /// Returns the underlying description `String` + /// Returns the underlying description [`String`] pub fn into_inner(self) -> String { self.0 } } -impl Into for Description { - fn into(self) -> String { - self.into_inner() +impl From for String { + fn from(val: Description) -> Self { + val.into_inner() } } @@ -1366,7 +1457,7 @@ impl ExpiryTime { ExpiryTime(Duration::from_secs(seconds)) } - /// Construct an `ExpiryTime` from a `Duration`, dropping the sub-second part. + /// Construct an `ExpiryTime` from a [`Duration`], dropping the sub-second part. pub fn from_duration(duration: Duration) -> ExpiryTime { Self::from_seconds(duration.as_secs()) } @@ -1376,7 +1467,7 @@ impl ExpiryTime { self.0.as_secs() } - /// Returns a reference to the underlying `Duration` (=expiry time) + /// Returns a reference to the underlying [`Duration`] (=expiry time) pub fn as_duration(&self) -> &Duration { &self.0 } @@ -1398,9 +1489,9 @@ impl PrivateRoute { } } -impl Into for PrivateRoute { - fn into(self) -> RouteHint { - self.into_inner() +impl From for RouteHint { + fn from(val: PrivateRoute) -> Self { + val.into_inner() } } @@ -1428,10 +1519,10 @@ impl Deref for SignedRawInvoice { } } -/// Errors that may occur when constructing a new `RawInvoice` or `Invoice` +/// Errors that may occur when constructing a new [`RawInvoice`] or [`Invoice`] #[derive(Eq, PartialEq, Debug, Clone)] pub enum CreationError { - /// The supplied description string was longer than 639 __bytes__ (see [`Description::new(…)`](./struct.Description.html#method.new)) + /// The supplied description string was longer than 639 __bytes__ (see [`Description::new`]) DescriptionTooLong, /// The specified route has too many hops and can't be encoded @@ -1472,7 +1563,7 @@ impl Display for CreationError { #[cfg(feature = "std")] impl std::error::Error for CreationError { } -/// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the +/// Errors that may occur when converting a [`RawInvoice`] to an [`Invoice`]. They relate to the /// requirements sections in BOLT #11 #[derive(Eq, PartialEq, Debug, Clone)] pub enum SemanticError { @@ -1528,7 +1619,7 @@ impl Display for SemanticError { #[cfg(feature = "std")] impl std::error::Error for SemanticError { } -/// When signing using a fallible method either an user-supplied `SignError` or a `CreationError` +/// When signing using a fallible method either an user-supplied `SignError` or a [`CreationError`] /// may occur. #[derive(Eq, PartialEq, Debug, Clone)] pub enum SignOrCreationError { @@ -1559,7 +1650,7 @@ impl<'de> Deserialize<'de> for Invoice { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { let bolt11 = String::deserialize(deserializer)? .parse::() - .map_err(|e| D::Error::custom(format!("{:?}", e)))?; + .map_err(|e| D::Error::custom(alloc::format!("{:?}", e)))?; Ok(bolt11) } @@ -1567,6 +1658,7 @@ impl<'de> Deserialize<'de> for Invoice { #[cfg(test)] mod test { + use bitcoin::Script; use bitcoin_hashes::hex::FromHex; use bitcoin_hashes::sha256; @@ -1766,7 +1858,7 @@ mod test { // Multiple payment secrets let invoice = { - let mut invoice = invoice_template.clone(); + let mut invoice = invoice_template; 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_ecdsa_recoverable(hash, &private_key))) @@ -1792,7 +1884,7 @@ mod test { assert_eq!(invoice.hrp.raw_amount, Some(15)); - let invoice = builder.clone() + let invoice = builder .amount_milli_satoshis(150) .build_raw() .unwrap(); @@ -1846,7 +1938,7 @@ mod test { .build_raw(); assert_eq!(long_route_res, Err(CreationError::RouteTooLong)); - let sign_error_res = builder.clone() + let sign_error_res = builder .description("Test".into()) .payment_secret(PaymentSecret([0; 32])) .try_build_signed(|_| { @@ -1876,7 +1968,7 @@ mod test { let route_1 = RouteHint(vec![ RouteHintHop { - src_node_id: public_key.clone(), + src_node_id: public_key, short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"), fees: RoutingFees { base_msat: 2, @@ -1887,7 +1979,7 @@ mod test { htlc_maximum_msat: None, }, RouteHintHop { - src_node_id: public_key.clone(), + src_node_id: public_key, short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"), fees: RoutingFees { base_msat: 3, @@ -1901,7 +1993,7 @@ mod test { let route_2 = RouteHint(vec![ RouteHintHop { - src_node_id: public_key.clone(), + src_node_id: public_key, short_channel_id: 0, fees: RoutingFees { base_msat: 4, @@ -1912,7 +2004,7 @@ mod test { htlc_maximum_msat: None, }, RouteHintHop { - src_node_id: public_key.clone(), + src_node_id: public_key, short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"), fees: RoutingFees { base_msat: 5, @@ -1927,10 +2019,10 @@ mod test { let builder = InvoiceBuilder::new(Currency::BitcoinTestnet) .amount_milli_satoshis(123) .duration_since_epoch(Duration::from_secs(1234567)) - .payee_pub_key(public_key.clone()) + .payee_pub_key(public_key) .expiry_time(Duration::from_secs(54321)) .min_final_cltv_expiry_delta(144) - .fallback(Fallback::PubKeyHash([0;20])) + .fallback(Fallback::PubKeyHash(PubkeyHash::from_slice(&[0;20]).unwrap())) .private_route(route_1.clone()) .private_route(route_2.clone()) .description_hash(sha256::Hash::from_slice(&[3;32][..]).unwrap()) @@ -1956,7 +2048,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_delta(), 144); - assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]); + assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash(PubkeyHash::from_slice(&[0;20]).unwrap())]); + let address = Address::from_script(&Script::new_p2pkh(&PubkeyHash::from_slice(&[0;20]).unwrap()), Network::Testnet).unwrap(); + assert_eq!(invoice.fallback_addresses(), vec![address]); assert_eq!(invoice.private_routes(), vec![&PrivateRoute(route_1), &PrivateRoute(route_2)]); assert_eq!( invoice.description(),