X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-invoice%2Fsrc%2Flib.rs;h=8ccd9d979ab139899731d3724566b0b12e27069d;hb=288fe0298ae0dd3550c4618e4f10d67d810aa997;hp=0c73ebc9d7df81f43e13bfeab293306d0923357c;hpb=854d650b5b67821b006a780b2f770be5bbb3ef0a;p=rust-lightning diff --git a/lightning-invoice/src/lib.rs b/lightning-invoice/src/lib.rs index 0c73ebc9..8ccd9d97 100644 --- a/lightning-invoice/src/lib.rs +++ b/lightning-invoice/src/lib.rs @@ -47,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))] @@ -60,6 +61,7 @@ use secp256k1::PublicKey; use secp256k1::{Message, Secp256k1}; use secp256k1::ecdsa::RecoverableSignature; +use core::cmp::Ordering; use core::fmt::{Display, Formatter, self}; use core::iter::FilterMap; use core::num::ParseIntError; @@ -217,10 +219,13 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18; /// * `D`: exactly one [`TaggedField::Description`] or [`TaggedField::DescriptionHash`] /// * `H`: exactly one [`TaggedField::PaymentHash`] /// * `T`: the timestamp is set +/// * `C`: the CLTV expiry is set +/// * `S`: the payment secret is set +/// * `M`: payment metadata 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 { +pub struct InvoiceBuilder { currency: Currency, amount: Option, si_prefix: Option, @@ -233,6 +238,7 @@ pub struct InvoiceBuilder, phantom_c: core::marker::PhantomData, phantom_s: core::marker::PhantomData, + phantom_m: core::marker::PhantomData, } /// Represents a syntactically and semantically correct lightning BOLT11 invoice. @@ -243,7 +249,7 @@ pub struct InvoiceBuilder(&str)` (see [`Invoice::from_str`]) /// /// [`Invoice::from_str`]: crate::Invoice#impl-FromStr -#[derive(Eq, PartialEq, Debug, Clone, Hash)] +#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)] pub struct Invoice { signed_invoice: SignedRawInvoice, } @@ -251,9 +257,9 @@ 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)] +#[derive(Eq, PartialEq, Debug, Clone, Ord, PartialOrd)] pub enum InvoiceDescription<'f> { /// Reference to the directly supplied description in the invoice Direct(&'f Description), @@ -267,7 +273,7 @@ pub enum InvoiceDescription<'f> { /// /// # Invariants /// The hash has to be either from the deserialized invoice or from the serialized [`RawInvoice`]. -#[derive(Eq, PartialEq, Debug, Clone, Hash)] +#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)] pub struct SignedRawInvoice { /// The rawInvoice that the signature belongs to raw_invoice: RawInvoice, @@ -290,7 +296,7 @@ pub struct SignedRawInvoice { /// 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`]. -#[derive(Eq, PartialEq, Debug, Clone, Hash)] +#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)] pub struct RawInvoice { /// human readable part pub hrp: RawHrp, @@ -301,8 +307,8 @@ pub struct RawInvoice { /// Data of the [`RawInvoice`] that is encoded in the human readable part. /// -/// (C-not exported) As we don't yet support `Option` -#[derive(Eq, PartialEq, Debug, Clone, Hash)] +/// This is not exported to bindings users as we don't yet support `Option` +#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)] pub struct RawHrp { /// The currency deferred from the 3rd and 4th character of the bech32 transaction pub currency: Currency, @@ -315,7 +321,7 @@ pub struct RawHrp { } /// Data of the [`RawInvoice`] that is encoded in the data part -#[derive(Eq, PartialEq, Debug, Clone, Hash)] +#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)] pub struct RawDataPart { /// generation time of the invoice pub timestamp: PositiveTimestamp, @@ -330,11 +336,11 @@ pub struct RawDataPart { /// /// The Unix timestamp representing the stored time has to be positive and no greater than /// [`MAX_TIMESTAMP`]. -#[derive(Eq, PartialEq, Debug, Clone, Hash)] +#[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)] pub struct PositiveTimestamp(Duration); /// SI prefixes for the human readable part -#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] +#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Ord, PartialOrd)] pub enum SiPrefix { /// 10^-3 Milli, @@ -361,7 +367,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::*; @@ -371,7 +377,7 @@ impl SiPrefix { } /// Enum representing the crypto currencies (or networks) supported by this library -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub enum Currency { /// Bitcoin mainnet Bitcoin, @@ -389,10 +395,33 @@ 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 -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +/// This is not exported to bindings users as we don't currently support TaggedField +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub enum RawTaggedField { /// Parsed tagged field with known tag KnownSemantics(TaggedField), @@ -404,10 +433,10 @@ 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)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub enum TaggedField { PaymentHash(Sha256), Description(Description), @@ -418,57 +447,78 @@ pub enum TaggedField { Fallback(Fallback), PrivateRoute(PrivateRoute), PaymentSecret(PaymentSecret), + PaymentMetadata(Vec), Features(InvoiceFeatures), } /// SHA-256 hash -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct Sha256(/// (C-not exported) as the native hash types are not currently mapped +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub struct Sha256(/// This is not exported to bindings users as the native hash types are not currently mapped pub sha256::Hash); +impl Sha256 { + /// Constructs a new [`Sha256`] from the given bytes, which are assumed to be the output of a + /// single sha256 hash. + #[cfg(c_bindings)] + pub fn from_bytes(bytes: &[u8; 32]) -> Self { + Self(sha256::Hash::from_slice(bytes).expect("from_slice only fails if len is not 32")) + } +} + /// Description string /// /// # Invariants /// The description can be at most 639 __bytes__ long -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct Description(String); /// Payee public key -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct PayeePubKey(pub PublicKey); /// Positive duration that defines when (relatively to the timestamp) in the future the invoice /// expires -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct ExpiryTime(Duration); /// `min_final_cltv_expiry_delta` to use for the last HTLC in the route -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] 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)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub enum Fallback { SegWitProgram { - version: u5, + version: WitnessVersion, program: Vec, }, - PubKeyHash([u8; 20]), - ScriptHash([u8; 20]), + PubKeyHash(PubkeyHash), + ScriptHash(ScriptHash), } /// Recoverable signature #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct InvoiceSignature(pub RecoverableSignature); +impl PartialOrd for InvoiceSignature { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.serialize_compact().1.partial_cmp(&other.0.serialize_compact().1) + } +} + +impl Ord for InvoiceSignature { + fn cmp(&self, other: &Self) -> Ordering { + self.0.serialize_compact().1.cmp(&other.0.serialize_compact().1) + } +} + /// Private routing information /// /// # Invariants /// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops) /// -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct PrivateRoute(RouteHint); /// Tag constants as specified in BOLT11 @@ -483,15 +533,16 @@ pub mod constants { pub const TAG_FALLBACK: u8 = 9; pub const TAG_PRIVATE_ROUTE: u8 = 3; pub const TAG_PAYMENT_SECRET: u8 = 16; + pub const TAG_PAYMENT_METADATA: u8 = 27; 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 { + pub fn new(currency: Currency) -> Self { InvoiceBuilder { - currency: currrency, + currency, amount: None, si_prefix: None, timestamp: None, @@ -503,14 +554,15 @@ impl InvoiceBuilder { phantom_t: core::marker::PhantomData, phantom_c: core::marker::PhantomData, phantom_s: core::marker::PhantomData, + phantom_m: core::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, @@ -523,6 +575,7 @@ impl InvoiceBui phantom_t: core::marker::PhantomData, phantom_c: core::marker::PhantomData, phantom_s: core::marker::PhantomData, + phantom_m: core::marker::PhantomData, } } @@ -567,7 +620,7 @@ impl InvoiceBui } } -impl InvoiceBuilder { +impl InvoiceBuilder { /// Builds a [`RawInvoice`] if no [`CreationError`] occurred while construction any of the /// fields. pub fn build_raw(self) -> Result { @@ -601,9 +654,9 @@ impl InvoiceBuilder 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), @@ -612,24 +665,36 @@ impl InvoiceBuilder InvoiceBuilder { + pub fn description_hash(mut self, description_hash: sha256::Hash) -> InvoiceBuilder { self.tagged_fields.push(TaggedField::DescriptionHash(Sha256(description_hash))); self.set_flags() } + + /// Set the description or description hash. This function is only available if no description (hash) was set. + pub fn invoice_description(self, description: InvoiceDescription) -> InvoiceBuilder { + match description { + InvoiceDescription::Direct(desc) => { + self.description(desc.clone().into_inner()) + } + InvoiceDescription::Hash(hash) => { + self.description_hash(hash.0) + } + } + } } -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 to a specific [`SystemTime`]. #[cfg(feature = "std")] - 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), @@ -640,7 +705,7 @@ impl InvoiceBuilder InvoiceBuilder { + pub fn duration_since_epoch(mut self, time: Duration) -> InvoiceBuilder { match PositiveTimestamp::from_duration_since_epoch(time) { Ok(t) => self.timestamp = Some(t), Err(e) => self.error = Some(e), @@ -651,34 +716,82 @@ impl InvoiceBuilder 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_delta`. - pub fn min_final_cltv_expiry_delta(mut self, min_final_cltv_expiry_delta: u64) -> InvoiceBuilder { + pub fn min_final_cltv_expiry_delta(mut self, min_final_cltv_expiry_delta: u64) -> InvoiceBuilder { self.tagged_fields.push(TaggedField::MinFinalCltvExpiryDelta(MinFinalCltvExpiryDelta(min_final_cltv_expiry_delta))); self.set_flags() } } -impl InvoiceBuilder { +impl InvoiceBuilder { /// Sets the payment secret and relevant features. - pub fn payment_secret(mut self, payment_secret: PaymentSecret) -> InvoiceBuilder { - let mut features = InvoiceFeatures::empty(); - features.set_variable_length_onion_required(); - features.set_payment_secret_required(); + pub fn payment_secret(mut self, payment_secret: PaymentSecret) -> InvoiceBuilder { + let mut found_features = false; + for field in self.tagged_fields.iter_mut() { + if let TaggedField::Features(f) = field { + found_features = true; + f.set_variable_length_onion_required(); + f.set_payment_secret_required(); + } + } self.tagged_fields.push(TaggedField::PaymentSecret(payment_secret)); - self.tagged_fields.push(TaggedField::Features(features)); + if !found_features { + let mut features = InvoiceFeatures::empty(); + features.set_variable_length_onion_required(); + features.set_payment_secret_required(); + self.tagged_fields.push(TaggedField::Features(features)); + } self.set_flags() } } -impl InvoiceBuilder { +impl InvoiceBuilder { + /// Sets the payment metadata. + /// + /// By default features are set to *optionally* allow the sender to include the payment metadata. + /// If you wish to require that the sender include the metadata (and fail to parse the invoice if + /// they don't support payment metadata fields), you need to call + /// [`InvoiceBuilder::require_payment_metadata`] after this. + pub fn payment_metadata(mut self, payment_metadata: Vec) -> InvoiceBuilder { + self.tagged_fields.push(TaggedField::PaymentMetadata(payment_metadata)); + let mut found_features = false; + for field in self.tagged_fields.iter_mut() { + if let TaggedField::Features(f) = field { + found_features = true; + f.set_payment_metadata_optional(); + } + } + if !found_features { + let mut features = InvoiceFeatures::empty(); + features.set_payment_metadata_optional(); + self.tagged_fields.push(TaggedField::Features(features)); + } + self.set_flags() + } +} + +impl InvoiceBuilder { + /// Sets forwarding of payment metadata as required. A reader of the invoice which does not + /// support sending payment metadata will fail to read the invoice. + pub fn require_payment_metadata(mut self) -> InvoiceBuilder { + for field in self.tagged_fields.iter_mut() { + if let TaggedField::Features(f) = field { + f.set_payment_metadata_required(); + } + } + self + } +} + +impl InvoiceBuilder { /// Sets the `basic_mpp` feature as optional. pub fn basic_mpp(mut self) -> Self { for field in self.tagged_fields.iter_mut() { @@ -690,7 +803,7 @@ impl InvoiceBuilder { +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. @@ -878,7 +991,7 @@ impl RawInvoice { /// 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 @@ -897,7 +1010,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>> { @@ -942,11 +1055,15 @@ impl RawInvoice { find_extract!(self.known_tagged_fields(), TaggedField::PaymentSecret(ref x), x) } + pub fn payment_metadata(&self) -> Option<&Vec> { + find_extract!(self.known_tagged_fields(), TaggedField::PaymentMetadata(ref x), x) + } + pub fn features(&self) -> Option<&InvoiceFeatures> { 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() } @@ -1026,6 +1143,11 @@ impl From for SystemTime { } 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 @@ -1175,7 +1297,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() @@ -1188,7 +1310,7 @@ 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(direct) = self.signed_invoice.description() { return InvoiceDescription::Direct(direct); @@ -1208,6 +1330,11 @@ impl Invoice { self.signed_invoice.payment_secret().expect("was checked by constructor") } + /// Get the payment metadata blob if one was included in the invoice + pub fn payment_metadata(&self) -> Option<&Vec> { + self.signed_invoice.payment_metadata() + } + /// Get the invoice features if they were included in the invoice pub fn features(&self) -> Option<&InvoiceFeatures> { self.signed_invoice.features() @@ -1218,6 +1345,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() @@ -1240,6 +1373,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 { @@ -1258,11 +1405,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() @@ -1280,6 +1446,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) @@ -1310,6 +1483,7 @@ impl TaggedField { TaggedField::Fallback(_) => constants::TAG_FALLBACK, TaggedField::PrivateRoute(_) => constants::TAG_PRIVATE_ROUTE, TaggedField::PaymentSecret(_) => constants::TAG_PAYMENT_SECRET, + TaggedField::PaymentMetadata(_) => constants::TAG_PAYMENT_METADATA, TaggedField::Features(_) => constants::TAG_FEATURES, }; @@ -1564,7 +1738,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(format_args!("{:?}", e)))?; Ok(bolt11) } @@ -1572,6 +1746,7 @@ impl<'de> Deserialize<'de> for Invoice { #[cfg(test)] mod test { + use bitcoin::Script; use bitcoin_hashes::hex::FromHex; use bitcoin_hashes::sha256; @@ -1935,7 +2110,7 @@ mod test { .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()) @@ -1961,7 +2136,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(),