@@ -86,6 +86,7 @@ use crate::ln::outbound_payment::{
8686 StaleExpiration,
8787};
8888use crate::ln::types::ChannelId;
89+ use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
8990use crate::offers::flow::OffersMessageFlow;
9091use crate::offers::invoice::{
9192 Bolt12Invoice, DerivedSigningPubkey, InvoiceBuilder, DEFAULT_RELATIVE_EXPIRY,
@@ -98,7 +99,8 @@ use crate::offers::parse::Bolt12SemanticError;
9899use crate::offers::refund::Refund;
99100use crate::offers::signer;
100101use crate::onion_message::async_payments::{
101- AsyncPaymentsMessage, AsyncPaymentsMessageHandler, HeldHtlcAvailable, ReleaseHeldHtlc,
102+ AsyncPaymentsMessage, AsyncPaymentsMessageHandler, HeldHtlcAvailable, OfferPaths,
103+ OfferPathsRequest, ReleaseHeldHtlc, ServeStaticInvoice, StaticInvoicePersisted,
102104};
103105use crate::onion_message::dns_resolution::HumanReadableName;
104106use crate::onion_message::messenger::{
@@ -5246,6 +5248,30 @@ where
52465248 )
52475249 }
52485250
5251+ #[cfg(async_payments)]
5252+ fn check_refresh_async_receive_offer_cache(&self, timer_tick_occurred: bool) {
5253+ let peers = self.get_peers_for_blinded_path();
5254+ let channels = self.list_usable_channels();
5255+ let entropy = &*self.entropy_source;
5256+ let router = &*self.router;
5257+ let refresh_res = self.flow.check_refresh_async_receive_offer_cache(
5258+ peers,
5259+ channels,
5260+ entropy,
5261+ router,
5262+ timer_tick_occurred,
5263+ );
5264+ match refresh_res {
5265+ Err(()) => {
5266+ log_error!(
5267+ self.logger,
5268+ "Failed to create blinded paths when requesting async receive offer paths"
5269+ );
5270+ },
5271+ Ok(()) => {},
5272+ }
5273+ }
5274+
52495275 #[cfg(async_payments)]
52505276 fn initiate_async_payment(
52515277 &self, invoice: &StaticInvoice, payment_id: PaymentId,
@@ -7240,6 +7266,9 @@ where
72407266 duration_since_epoch, &self.pending_events
72417267 );
72427268
7269+ #[cfg(async_payments)]
7270+ self.check_refresh_async_receive_offer_cache(true);
7271+
72437272 // Technically we don't need to do this here, but if we have holding cell entries in a
72447273 // channel that need freeing, it's better to do that here and block a background task
72457274 // than block the message queueing pipeline.
@@ -10999,9 +11028,29 @@ where
1099911028 #[cfg(c_bindings)]
1100011029 create_refund_builder!(self, RefundMaybeWithDerivedMetadataBuilder);
1100111030
11031+ /// Retrieve an [`Offer`] for receiving async payments as an often-offline recipient. Will only
11032+ /// return an offer if [`Self::set_paths_to_static_invoice_server`] was called and we succeeded in
11033+ /// interactively building a [`StaticInvoice`] with the static invoice server.
11034+ ///
11035+ /// Useful for posting offers to receive payments later, such as posting an offer on a website.
11036+ #[cfg(async_payments)]
11037+ pub fn get_async_receive_offer(&self) -> Result<Offer, ()> {
11038+ let (offer, needs_persist) = self.flow.get_async_receive_offer()?;
11039+ if needs_persist {
11040+ // We need to re-persist the cache if a fresh offer was just marked as used to ensure we
11041+ // continue to keep this offer's invoice updated and don't replace it with the server.
11042+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
11043+ }
11044+ Ok(offer)
11045+ }
11046+
1100211047 /// Create an offer for receiving async payments as an often-offline recipient.
1100311048 ///
11004- /// Because we may be offline when the payer attempts to request an invoice, you MUST:
11049+ /// Instead of using this method, it is preferable to call
11050+ /// [`Self::set_paths_to_static_invoice_server`] and retrieve the automatically built offer via
11051+ /// [`Self::get_async_receive_offer`].
11052+ ///
11053+ /// If you want to build the [`StaticInvoice`] manually using this method instead, you MUST:
1100511054 /// 1. Provide at least 1 [`BlindedMessagePath`] terminating at an always-online node that will
1100611055 /// serve the [`StaticInvoice`] created from this offer on our behalf.
1100711056 /// 2. Use [`Self::create_static_invoice_builder`] to create a [`StaticInvoice`] from this
@@ -11018,6 +11067,10 @@ where
1101811067 /// Creates a [`StaticInvoiceBuilder`] from the corresponding [`Offer`] and [`Nonce`] that were
1101911068 /// created via [`Self::create_async_receive_offer_builder`]. If `relative_expiry` is unset, the
1102011069 /// invoice's expiry will default to [`STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY`].
11070+ ///
11071+ /// Instead of using this method to manually build the invoice, it is preferable to set
11072+ /// [`Self::set_paths_to_static_invoice_server`] and retrieve the automatically built offer via
11073+ /// [`Self::get_async_receive_offer`].
1102111074 #[cfg(async_payments)]
1102211075 pub fn create_static_invoice_builder<'a>(
1102311076 &self, offer: &'a Offer, offer_nonce: Nonce, relative_expiry: Option<Duration>,
@@ -11053,6 +11106,22 @@ where
1105311106 )
1105411107 }
1105511108
11109+ /// Sets the [`BlindedMessagePath`]s that we will use as an async recipient to interactively build
11110+ /// [`Offer`]s with a static invoice server, so the server can serve [`StaticInvoice`]s to payers
11111+ /// on our behalf when we're offline.
11112+ ///
11113+ /// This method only needs to be called once when the server first takes on the recipient as a
11114+ /// client, or when the paths change, e.g. if the paths are set to expire at a particular time.
11115+ #[cfg(async_payments)]
11116+ pub fn set_paths_to_static_invoice_server(
11117+ &self, paths_to_static_invoice_server: Vec<BlindedMessagePath>,
11118+ ) -> Result<(), ()> {
11119+ self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server)?;
11120+
11121+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
11122+ Ok(())
11123+ }
11124+
1105611125 /// Pays for an [`Offer`] using the given parameters by creating an [`InvoiceRequest`] and
1105711126 /// enqueuing it to be sent via an onion message. [`ChannelManager`] will pay the actual
1105811127 /// [`Bolt12Invoice`] once it is received.
@@ -11960,6 +12029,13 @@ where
1196012029 return NotifyOption::SkipPersistHandleEvents;
1196112030 //TODO: Also re-broadcast announcement_signatures
1196212031 });
12032+
12033+ // While we usually refresh the AsyncReceiveOfferCache on a timer, we also want to start
12034+ // interactively building offers as soon as we can after startup. We can't start building offers
12035+ // until we have some peer connection(s) to send onion messages over, so as a minor optimization
12036+ // refresh the cache when a peer connects.
12037+ #[cfg(async_payments)]
12038+ self.check_refresh_async_receive_offer_cache(false);
1196312039 res
1196412040 }
1196512041
@@ -13374,6 +13450,64 @@ where
1337413450 MR::Target: MessageRouter,
1337513451 L::Target: Logger,
1337613452{
13453+ fn handle_offer_paths_request(
13454+ &self, _message: OfferPathsRequest, _context: AsyncPaymentsContext,
13455+ _responder: Option<Responder>,
13456+ ) -> Option<(OfferPaths, ResponseInstruction)> {
13457+ None
13458+ }
13459+
13460+ fn handle_offer_paths(
13461+ &self, _message: OfferPaths, _context: AsyncPaymentsContext, _responder: Option<Responder>,
13462+ ) -> Option<(ServeStaticInvoice, ResponseInstruction)> {
13463+ #[cfg(async_payments)]
13464+ {
13465+ let responder = match _responder {
13466+ Some(responder) => responder,
13467+ None => return None,
13468+ };
13469+ let (serve_static_invoice, reply_context) = match self.flow.handle_offer_paths(
13470+ _message,
13471+ _context,
13472+ responder.clone(),
13473+ self.get_peers_for_blinded_path(),
13474+ self.list_usable_channels(),
13475+ &*self.entropy_source,
13476+ &*self.router,
13477+ ) {
13478+ Some((msg, ctx)) => (msg, ctx),
13479+ None => return None,
13480+ };
13481+
13482+ // We cached a new pending offer, so persist the cache.
13483+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
13484+
13485+ let response_instructions = responder.respond_with_reply_path(reply_context);
13486+ return Some((serve_static_invoice, response_instructions));
13487+ }
13488+
13489+ #[cfg(not(async_payments))]
13490+ return None;
13491+ }
13492+
13493+ fn handle_serve_static_invoice(
13494+ &self, _message: ServeStaticInvoice, _context: AsyncPaymentsContext,
13495+ _responder: Option<Responder>,
13496+ ) {
13497+ }
13498+
13499+ fn handle_static_invoice_persisted(
13500+ &self, _message: StaticInvoicePersisted, _context: AsyncPaymentsContext,
13501+ ) {
13502+ #[cfg(async_payments)]
13503+ {
13504+ let should_persist = self.flow.handle_static_invoice_persisted(_context);
13505+ if should_persist {
13506+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
13507+ }
13508+ }
13509+ }
13510+
1337713511 fn handle_held_htlc_available(
1337813512 &self, _message: HeldHtlcAvailable, _context: AsyncPaymentsContext,
1337913513 _responder: Option<Responder>,
@@ -14239,6 +14373,7 @@ where
1423914373 (15, self.inbound_payment_id_secret, required),
1424014374 (17, in_flight_monitor_updates, option),
1424114375 (19, peer_storage_dir, optional_vec),
14376+ (21, self.flow.writeable_async_receive_offer_cache(), required),
1424214377 });
1424314378
1424414379 Ok(())
@@ -14818,6 +14953,7 @@ where
1481814953 let mut decode_update_add_htlcs: Option<HashMap<u64, Vec<msgs::UpdateAddHTLC>>> = None;
1481914954 let mut inbound_payment_id_secret = None;
1482014955 let mut peer_storage_dir: Option<Vec<(PublicKey, Vec<u8>)>> = None;
14956+ let mut async_receive_offer_cache: AsyncReceiveOfferCache = AsyncReceiveOfferCache::new();
1482114957 read_tlv_fields!(reader, {
1482214958 (1, pending_outbound_payments_no_retry, option),
1482314959 (2, pending_intercepted_htlcs, option),
@@ -14835,6 +14971,7 @@ where
1483514971 (15, inbound_payment_id_secret, option),
1483614972 (17, in_flight_monitor_updates, option),
1483714973 (19, peer_storage_dir, optional_vec),
14974+ (21, async_receive_offer_cache, (default_value, async_receive_offer_cache)),
1483814975 });
1483914976 let mut decode_update_add_htlcs = decode_update_add_htlcs.unwrap_or_else(|| new_hash_map());
1484014977 let peer_storage_dir: Vec<(PublicKey, Vec<u8>)> = peer_storage_dir.unwrap_or_else(Vec::new);
@@ -15521,7 +15658,7 @@ where
1552115658 chain_hash, best_block, our_network_pubkey,
1552215659 highest_seen_timestamp, expanded_inbound_key,
1552315660 secp_ctx.clone(), args.message_router
15524- );
15661+ ).with_async_payments_offers_cache(async_receive_offer_cache) ;
1552515662
1552615663 let channel_manager = ChannelManager {
1552715664 chain_hash,
0 commit comments