@@ -1516,6 +1516,11 @@ struct AsyncReceiveOffer {
15161516}
15171517
15181518impl AsyncReceiveOffer {
1519+ // If we have more than three hours before our offer expires, don't bother requesting new
1520+ // paths.
1521+ #[cfg(async_payments)]
1522+ const OFFER_RELATIVE_EXPIRY_BUFFER: Duration = Duration::from_secs(60 * 60 * 3);
1523+
15191524 /// Removes the offer from our cache if it's expired.
15201525 #[cfg(async_payments)]
15211526 fn check_expire_offer(&mut self, duration_since_epoch: Duration) {
@@ -1526,6 +1531,17 @@ impl AsyncReceiveOffer {
15261531 }
15271532 }
15281533 }
1534+
1535+ #[cfg(async_payments)]
1536+ fn should_refresh_offer(&self, duration_since_epoch: Duration) -> bool {
1537+ if let Some(ref offer) = self.offer {
1538+ let offer_expiry = offer.absolute_expiry().unwrap_or(Duration::MAX);
1539+ if offer_expiry > duration_since_epoch.saturating_add(Self::OFFER_RELATIVE_EXPIRY_BUFFER) {
1540+ return false
1541+ }
1542+ }
1543+ return true
1544+ }
15291545}
15301546
15311547impl_writeable_tlv_based!(AsyncReceiveOffer, {
@@ -4875,15 +4891,8 @@ where
48754891 {
48764892 let mut offer_cache = self.async_receive_offer_cache.lock().unwrap();
48774893 offer_cache.check_expire_offer(duration_since_epoch);
4878-
4879- if let Some(ref offer) = offer_cache.offer {
4880- // If we have more than three hours before our offer expires, don't bother requesting new
4881- // paths.
4882- const PATHS_EXPIRY_BUFFER: Duration = Duration::from_secs(60 * 60 * 3);
4883- let offer_expiry = offer.absolute_expiry().unwrap_or(Duration::MAX);
4884- if offer_expiry > duration_since_epoch.saturating_add(PATHS_EXPIRY_BUFFER) {
4885- return
4886- }
4894+ if !offer_cache.should_refresh_offer(duration_since_epoch) {
4895+ return
48874896 }
48884897
48894898 const MAX_ATTEMPTS: u8 = 3;
@@ -12647,6 +12656,101 @@ where
1264712656 fn handle_offer_paths(
1264812657 &self, _message: OfferPaths, _context: AsyncPaymentsContext, _responder: Option<Responder>,
1264912658 ) -> Option<(ServeStaticInvoice, ResponseInstruction)> {
12659+ #[cfg(async_payments)] {
12660+ let expanded_key = &self.inbound_payment_key;
12661+ let entropy = &*self.entropy_source;
12662+ let secp_ctx = &self.secp_ctx;
12663+ let duration_since_epoch = self.duration_since_epoch();
12664+
12665+ match _context {
12666+ AsyncPaymentsContext::OfferPaths { nonce, hmac, path_absolute_expiry } => {
12667+ if let Err(()) = signer::verify_offer_paths_context(nonce, hmac, expanded_key) {
12668+ return None
12669+ }
12670+ if duration_since_epoch > path_absolute_expiry { return None }
12671+ },
12672+ _ => return None
12673+ }
12674+
12675+ if !self.async_receive_offer_cache.lock().unwrap().should_refresh_offer(duration_since_epoch) {
12676+ return None
12677+ }
12678+
12679+ // Require at least two hours before we'll need to start the process of creating a new offer.
12680+ const MIN_OFFER_PATHS_RELATIVE_EXPIRY: Duration =
12681+ Duration::from_secs(2 * 60 * 60).saturating_add(AsyncReceiveOffer::OFFER_RELATIVE_EXPIRY_BUFFER);
12682+ let min_offer_paths_absolute_expiry =
12683+ duration_since_epoch.saturating_add(MIN_OFFER_PATHS_RELATIVE_EXPIRY);
12684+ let offer_paths_absolute_expiry =
12685+ _message.paths_absolute_expiry.unwrap_or(Duration::from_secs(u64::MAX));
12686+ if offer_paths_absolute_expiry < min_offer_paths_absolute_expiry {
12687+ log_error!(self.logger, "Received offer paths with too-soon absolute Unix epoch expiry: {}", offer_paths_absolute_expiry.as_secs());
12688+ return None
12689+ }
12690+
12691+ // Expire the offer at the same time as the static invoice so we automatically refresh both
12692+ // at the same time.
12693+ let offer_and_invoice_absolute_expiry = Duration::from_secs(core::cmp::min(
12694+ offer_paths_absolute_expiry.as_secs(),
12695+ duration_since_epoch.saturating_add(STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY).as_secs()
12696+ ));
12697+
12698+ let (offer, offer_nonce) = {
12699+ let (offer_builder, offer_nonce) =
12700+ match self.create_async_receive_offer_builder(_message.paths) {
12701+ Ok((builder, nonce)) => (builder, nonce),
12702+ Err(e) => {
12703+ log_error!(self.logger, "Failed to create offer builder when replying to OfferPaths message: {:?}", e);
12704+ return None
12705+ },
12706+ };
12707+ match offer_builder.absolute_expiry(offer_and_invoice_absolute_expiry).build() {
12708+ Ok(offer) => (offer, offer_nonce),
12709+ Err(e) => {
12710+ log_error!(self.logger, "Failed to build offer when replying to OfferPaths message: {:?}", e);
12711+ return None
12712+ },
12713+ }
12714+ };
12715+
12716+ let static_invoice = {
12717+ let invoice_res = self.create_static_invoice_builder(
12718+ &offer, offer_nonce, Some(offer_and_invoice_absolute_expiry)
12719+ ).and_then(|builder| builder.build_and_sign(secp_ctx));
12720+ match invoice_res {
12721+ Ok(invoice) => invoice,
12722+ Err(e) => {
12723+ log_error!(self.logger, "Failed to create static invoice when replying to OfferPaths message: {:?}", e);
12724+ return None
12725+ },
12726+ }
12727+ };
12728+
12729+ let invoice_persisted_paths = {
12730+ // We expect the static invoice server to respond quickly, but add some buffer for no-std
12731+ // users that rely on block timestamps.
12732+ const PATH_RELATIVE_EXPIRY: Duration = Duration::from_secs(2 * 60 * 60);
12733+
12734+ let nonce = Nonce::from_entropy_source(entropy);
12735+ let hmac = signer::hmac_for_static_invoice_persisted_context(nonce, expanded_key);
12736+ let context = MessageContext::AsyncPayments(AsyncPaymentsContext::StaticInvoicePersisted {
12737+ offer, nonce, hmac,
12738+ path_absolute_expiry: duration_since_epoch.saturating_add(PATH_RELATIVE_EXPIRY)
12739+ });
12740+ match self.create_blinded_paths(context) {
12741+ Ok(paths) => paths,
12742+ Err(()) => {
12743+ log_error!(self.logger, "Failed to create blinded paths when replying to OfferPaths message");
12744+ return None
12745+ },
12746+ }
12747+ };
12748+
12749+ let reply = ServeStaticInvoice { invoice: static_invoice, invoice_persisted_paths };
12750+ return _responder.map(|responder| (reply, responder.respond()))
12751+ }
12752+
12753+ #[cfg(not(async_payments))]
1265012754 None
1265112755 }
1265212756
0 commit comments