expose more granular data in TaggedHash struct
[rust-lightning] / lightning / src / offers / offer.rs
index 60621b9dc7491686fbf3207a532f0e2bc25a5aa5..66dca85cd94c97d6f5b6c594c24d07114d859860 100644 (file)
@@ -13,6 +13,8 @@
 //! published as a QR code to be scanned by a customer. The customer uses the offer to request an
 //! invoice from the merchant to be paid.
 //!
+//! # Example
+//!
 //! ```
 //! extern crate bitcoin;
 //! extern crate core;
 //! # Ok(())
 //! # }
 //! ```
+//!
+//! # Note
+//!
+//! If constructing an [`Offer`] for use with a [`ChannelManager`], use
+//! [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`].
+//!
+//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+//! [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
 
 use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::network::constants::Network;
@@ -132,6 +142,14 @@ impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
        /// while the offer is valid.
        ///
        /// Use a different pubkey per offer to avoid correlating offers.
+       ///
+       /// # Note
+       ///
+       /// If constructing an [`Offer`] for use with a [`ChannelManager`], use
+       /// [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`].
+       ///
+       /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+       /// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
        pub fn new(description: String, signing_pubkey: PublicKey) -> Self {
                OfferBuilder {
                        offer: OfferContents {
@@ -191,9 +209,18 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
        /// See [`Offer::chains`] on how this relates to the payment currency.
        ///
        /// Successive calls to this method will add another chain hash.
-       pub fn chain(mut self, network: Network) -> Self {
+       pub fn chain(self, network: Network) -> Self {
+               self.chain_hash(ChainHash::using_genesis_block(network))
+       }
+
+       /// Adds the [`ChainHash`] to [`Offer::chains`]. If not called, the chain hash of
+       /// [`Network::Bitcoin`] is assumed to be the only one supported.
+       ///
+       /// See [`Offer::chains`] on how this relates to the payment currency.
+       ///
+       /// Successive calls to this method will add another chain hash.
+       pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self {
                let chains = self.offer.chains.get_or_insert_with(Vec::new);
-               let chain = ChainHash::using_genesis_block(network);
                if !chains.contains(&chain) {
                        chains.push(chain);
                }
@@ -441,6 +468,11 @@ impl Offer {
                self.contents.is_expired()
        }
 
+       /// Whether the offer has expired given the duration since the Unix epoch.
+       pub fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
+               self.contents.is_expired_no_std(duration_since_epoch)
+       }
+
        /// Returns whether the given quantity is valid for the offer.
        pub fn is_valid_quantity(&self, quantity: u64) -> bool {
                self.contents.is_valid_quantity(quantity)
@@ -582,13 +614,16 @@ impl OfferContents {
 
        #[cfg(feature = "std")]
        pub(super) fn is_expired(&self) -> bool {
-               match self.absolute_expiry {
-                       Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
-                               Ok(elapsed) => elapsed > seconds_from_epoch,
-                               Err(_) => false,
-                       },
-                       None => false,
-               }
+               SystemTime::UNIX_EPOCH
+                       .elapsed()
+                       .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
+                       .unwrap_or(false)
+       }
+
+       pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
+               self.absolute_expiry
+                       .map(|absolute_expiry| duration_since_epoch > absolute_expiry)
+                       .unwrap_or(false)
        }
 
        pub fn issuer(&self) -> Option<PrintableString> {
@@ -1162,6 +1197,7 @@ mod tests {
        fn builds_offer_with_absolute_expiry() {
                let future_expiry = Duration::from_secs(u64::max_value());
                let past_expiry = Duration::from_secs(0);
+               let now = future_expiry - Duration::from_secs(1_000);
 
                let offer = OfferBuilder::new("foo".into(), pubkey(42))
                        .absolute_expiry(future_expiry)
@@ -1169,6 +1205,7 @@ mod tests {
                        .unwrap();
                #[cfg(feature = "std")]
                assert!(!offer.is_expired());
+               assert!(!offer.is_expired_no_std(now));
                assert_eq!(offer.absolute_expiry(), Some(future_expiry));
                assert_eq!(offer.as_tlv_stream().absolute_expiry, Some(future_expiry.as_secs()));
 
@@ -1179,6 +1216,7 @@ mod tests {
                        .unwrap();
                #[cfg(feature = "std")]
                assert!(offer.is_expired());
+               assert!(offer.is_expired_no_std(now));
                assert_eq!(offer.absolute_expiry(), Some(past_expiry));
                assert_eq!(offer.as_tlv_stream().absolute_expiry, Some(past_expiry.as_secs()));
        }