@@ -86,6 +86,7 @@ use crate::ln::outbound_payment::{
86
86
StaleExpiration,
87
87
};
88
88
use crate::ln::types::ChannelId;
89
+ use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
89
90
use crate::offers::flow::OffersMessageFlow;
90
91
use crate::offers::invoice::{
91
92
Bolt12Invoice, DerivedSigningPubkey, InvoiceBuilder, DEFAULT_RELATIVE_EXPIRY,
@@ -98,7 +99,8 @@ use crate::offers::parse::Bolt12SemanticError;
98
99
use crate::offers::refund::Refund;
99
100
use crate::offers::signer;
100
101
use crate::onion_message::async_payments::{
101
- AsyncPaymentsMessage, AsyncPaymentsMessageHandler, HeldHtlcAvailable, ReleaseHeldHtlc,
102
+ AsyncPaymentsMessage, AsyncPaymentsMessageHandler, HeldHtlcAvailable, OfferPaths,
103
+ OfferPathsRequest, ReleaseHeldHtlc, ServeStaticInvoice, StaticInvoicePersisted,
102
104
};
103
105
use crate::onion_message::dns_resolution::HumanReadableName;
104
106
use crate::onion_message::messenger::{
@@ -5246,6 +5248,30 @@ where
5246
5248
)
5247
5249
}
5248
5250
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
+
5249
5275
#[cfg(async_payments)]
5250
5276
fn initiate_async_payment(
5251
5277
&self, invoice: &StaticInvoice, payment_id: PaymentId,
@@ -7240,6 +7266,9 @@ where
7240
7266
duration_since_epoch, &self.pending_events
7241
7267
);
7242
7268
7269
+ #[cfg(async_payments)]
7270
+ self.check_refresh_async_receive_offer_cache(true);
7271
+
7243
7272
// Technically we don't need to do this here, but if we have holding cell entries in a
7244
7273
// channel that need freeing, it's better to do that here and block a background task
7245
7274
// than block the message queueing pipeline.
@@ -10999,9 +11028,29 @@ where
10999
11028
#[cfg(c_bindings)]
11000
11029
create_refund_builder!(self, RefundMaybeWithDerivedMetadataBuilder);
11001
11030
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
+
11002
11047
/// Create an offer for receiving async payments as an often-offline recipient.
11003
11048
///
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:
11005
11054
/// 1. Provide at least 1 [`BlindedMessagePath`] terminating at an always-online node that will
11006
11055
/// serve the [`StaticInvoice`] created from this offer on our behalf.
11007
11056
/// 2. Use [`Self::create_static_invoice_builder`] to create a [`StaticInvoice`] from this
@@ -11018,6 +11067,10 @@ where
11018
11067
/// Creates a [`StaticInvoiceBuilder`] from the corresponding [`Offer`] and [`Nonce`] that were
11019
11068
/// created via [`Self::create_async_receive_offer_builder`]. If `relative_expiry` is unset, the
11020
11069
/// 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`].
11021
11074
#[cfg(async_payments)]
11022
11075
pub fn create_static_invoice_builder<'a>(
11023
11076
&self, offer: &'a Offer, offer_nonce: Nonce, relative_expiry: Option<Duration>,
@@ -11053,6 +11106,22 @@ where
11053
11106
)
11054
11107
}
11055
11108
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
+
11056
11125
/// Pays for an [`Offer`] using the given parameters by creating an [`InvoiceRequest`] and
11057
11126
/// enqueuing it to be sent via an onion message. [`ChannelManager`] will pay the actual
11058
11127
/// [`Bolt12Invoice`] once it is received.
@@ -11960,6 +12029,13 @@ where
11960
12029
return NotifyOption::SkipPersistHandleEvents;
11961
12030
//TODO: Also re-broadcast announcement_signatures
11962
12031
});
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);
11963
12039
res
11964
12040
}
11965
12041
@@ -13374,6 +13450,64 @@ where
13374
13450
MR::Target: MessageRouter,
13375
13451
L::Target: Logger,
13376
13452
{
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
+
13377
13511
fn handle_held_htlc_available(
13378
13512
&self, _message: HeldHtlcAvailable, _context: AsyncPaymentsContext,
13379
13513
_responder: Option<Responder>,
@@ -14239,6 +14373,7 @@ where
14239
14373
(15, self.inbound_payment_id_secret, required),
14240
14374
(17, in_flight_monitor_updates, option),
14241
14375
(19, peer_storage_dir, optional_vec),
14376
+ (21, self.flow.writeable_async_receive_offer_cache(), required),
14242
14377
});
14243
14378
14244
14379
Ok(())
@@ -14818,6 +14953,7 @@ where
14818
14953
let mut decode_update_add_htlcs: Option<HashMap<u64, Vec<msgs::UpdateAddHTLC>>> = None;
14819
14954
let mut inbound_payment_id_secret = None;
14820
14955
let mut peer_storage_dir: Option<Vec<(PublicKey, Vec<u8>)>> = None;
14956
+ let mut async_receive_offer_cache: AsyncReceiveOfferCache = AsyncReceiveOfferCache::new();
14821
14957
read_tlv_fields!(reader, {
14822
14958
(1, pending_outbound_payments_no_retry, option),
14823
14959
(2, pending_intercepted_htlcs, option),
@@ -14835,6 +14971,7 @@ where
14835
14971
(15, inbound_payment_id_secret, option),
14836
14972
(17, in_flight_monitor_updates, option),
14837
14973
(19, peer_storage_dir, optional_vec),
14974
+ (21, async_receive_offer_cache, (default_value, async_receive_offer_cache)),
14838
14975
});
14839
14976
let mut decode_update_add_htlcs = decode_update_add_htlcs.unwrap_or_else(|| new_hash_map());
14840
14977
let peer_storage_dir: Vec<(PublicKey, Vec<u8>)> = peer_storage_dir.unwrap_or_else(Vec::new);
@@ -15521,7 +15658,7 @@ where
15521
15658
chain_hash, best_block, our_network_pubkey,
15522
15659
highest_seen_timestamp, expanded_inbound_key,
15523
15660
secp_ctx.clone(), args.message_router
15524
- );
15661
+ ).with_async_payments_offers_cache(async_receive_offer_cache) ;
15525
15662
15526
15663
let channel_manager = ChannelManager {
15527
15664
chain_hash,
0 commit comments