Skip to content

Commit 1815d9b

Browse files
Track cached async receive offers in offers::Flow
In future commits, as part of being an async recipient, we will interactively build offers and static invoices with an always-online node that will serve static invoices on our behalf. Once an offer is built and the static invoice is confirmed as persisted by the server, we will use the new offer cache added here to save the invoice metadata and the offer in ChannelManager, though the OffersMessageFlow is responsible for keeping the cache updated.
1 parent 0384462 commit 1815d9b

File tree

4 files changed

+136
-3
lines changed

4 files changed

+136
-3
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ use crate::ln::msgs::{BaseMessageHandler, ChannelMessageHandler, CommitmentUpdat
6666
#[cfg(test)]
6767
use crate::ln::outbound_payment;
6868
use crate::ln::outbound_payment::{Bolt11PaymentError, OutboundPayments, PendingOutboundPayment, RetryableInvoiceRequest, SendAlongPathArgs, StaleExpiration};
69+
use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
6970
use crate::offers::invoice::{Bolt12Invoice, DerivedSigningPubkey, InvoiceBuilder, DEFAULT_RELATIVE_EXPIRY};
7071
use crate::offers::invoice_error::InvoiceError;
7172
use crate::offers::invoice_request::InvoiceRequest;
@@ -13759,6 +13760,7 @@ where
1375913760
let mut decode_update_add_htlcs: Option<HashMap<u64, Vec<msgs::UpdateAddHTLC>>> = None;
1376013761
let mut inbound_payment_id_secret = None;
1376113762
let mut peer_storage_dir: Option<Vec<(PublicKey, Vec<u8>)>> = None;
13763+
let mut async_receive_offer_cache: AsyncReceiveOfferCache = AsyncReceiveOfferCache::new();
1376213764
read_tlv_fields!(reader, {
1376313765
(1, pending_outbound_payments_no_retry, option),
1376413766
(2, pending_intercepted_htlcs, option),
@@ -13776,6 +13778,7 @@ where
1377613778
(15, inbound_payment_id_secret, option),
1377713779
(17, in_flight_monitor_updates, required),
1377813780
(19, peer_storage_dir, optional_vec),
13781+
(21, async_receive_offer_cache, (default_value, async_receive_offer_cache)),
1377913782
});
1378013783
let mut decode_update_add_htlcs = decode_update_add_htlcs.unwrap_or_else(|| new_hash_map());
1378113784
let peer_storage_dir: Vec<(PublicKey, Vec<u8>)> = peer_storage_dir.unwrap_or_else(Vec::new);
@@ -14430,7 +14433,12 @@ where
1443014433
}
1443114434

1443214435
let best_block = BestBlock::new(best_block_hash, best_block_height);
14433-
let flow = OffersMessageFlow::new(chain_hash, best_block, our_network_pubkey, highest_seen_timestamp, expanded_inbound_key, secp_ctx.clone(), args.message_router);
14436+
let flow = OffersMessageFlow::new(
14437+
chain_hash, best_block, our_network_pubkey, highest_seen_timestamp, expanded_inbound_key,
14438+
secp_ctx.clone(), args.message_router
14439+
).with_async_payments_offers_cache(
14440+
async_receive_offer_cache, &args.default_config.paths_to_static_invoice_server[..]
14441+
);
1443414442

1443514443
let channel_manager = ChannelManager {
1443614444
chain_hash,
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! Data structures and methods for caching offers that we interactively build with a static invoice
11+
//! server as an async recipient. The static invoice server will serve the resulting invoices to
12+
//! payers on our behalf when we're offline.
13+
14+
use crate::io;
15+
use crate::io::Read;
16+
use crate::ln::msgs::DecodeError;
17+
use crate::offers::nonce::Nonce;
18+
use crate::offers::offer::Offer;
19+
use crate::onion_message::messenger::Responder;
20+
use crate::prelude::*;
21+
use crate::util::ser::{Readable, Writeable, Writer};
22+
use core::time::Duration;
23+
24+
struct AsyncReceiveOffer {
25+
offer: Offer,
26+
/// We determine whether an offer is expiring "soon" based on how far the offer is into its total
27+
/// lifespan, using this field.
28+
offer_created_at: Duration,
29+
30+
/// The below fields are used to generate and persist a new static invoice with the invoice
31+
/// server, if the invoice is expiring prior to the corresponding offer.
32+
offer_nonce: Nonce,
33+
update_static_invoice_path: Responder,
34+
static_invoice_absolute_expiry: Duration,
35+
invoice_update_attempts: u8,
36+
}
37+
38+
impl_writeable_tlv_based!(AsyncReceiveOffer, {
39+
(0, offer, required),
40+
(2, offer_nonce, required),
41+
(4, offer_created_at, required),
42+
(6, update_static_invoice_path, required),
43+
(8, static_invoice_absolute_expiry, required),
44+
(10, invoice_update_attempts, (static_value, 0)),
45+
});
46+
47+
/// If we are an often-offline recipient, we'll want to interactively build offers and static
48+
/// invoices with an always-online node that will serve those static invoices to payers on our
49+
/// behalf when we are offline.
50+
///
51+
/// This struct is used to cache those interactively built offers, and should be passed into
52+
/// [`OffersMessageFlow`] on startup as well as persisted whenever an offer or invoice is updated
53+
/// with the static invoice server.
54+
///
55+
/// [`OffersMessageFlow`]: crate::offers::flow::OffersMessageFlow
56+
pub struct AsyncReceiveOfferCache {
57+
offers: Vec<AsyncReceiveOffer>,
58+
/// Used to limit the number of times we request paths for our offer from the static invoice
59+
/// server.
60+
#[allow(unused)] // TODO: remove when we get rid of async payments cfg flag
61+
offer_paths_request_attempts: u8,
62+
/// Used to determine whether enough time has passed since our last request for offer paths that
63+
/// more requests should be allowed to go out.
64+
#[allow(unused)] // TODO: remove when we get rid of async payments cfg flag
65+
last_offer_paths_request_timestamp: Duration,
66+
}
67+
68+
impl AsyncReceiveOfferCache {
69+
/// Creates an empty [`AsyncReceiveOfferCache`] to be passed into [`OffersMessageFlow`].
70+
///
71+
/// [`OffersMessageFlow`]: crate::offers::flow::OffersMessageFlow
72+
pub fn new() -> Self {
73+
Self {
74+
offers: Vec::new(),
75+
offer_paths_request_attempts: 0,
76+
last_offer_paths_request_timestamp: Duration::from_secs(0),
77+
}
78+
}
79+
}
80+
81+
impl Writeable for AsyncReceiveOfferCache {
82+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
83+
write_tlv_fields!(w, {
84+
(0, self.offers, required_vec),
85+
// offer paths request retry info always resets on restart
86+
});
87+
Ok(())
88+
}
89+
}
90+
91+
impl Readable for AsyncReceiveOfferCache {
92+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
93+
_init_and_read_len_prefixed_tlv_fields!(r, {
94+
(0, offers, required_vec),
95+
});
96+
let offers: Vec<AsyncReceiveOffer> = offers;
97+
Ok(Self {
98+
offers,
99+
offer_paths_request_attempts: 0,
100+
last_offer_paths_request_timestamp: Duration::from_secs(0),
101+
})
102+
}
103+
}

lightning/src/offers/flow.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use crate::ln::channelmanager::{
3636
Verification, {PaymentId, CLTV_FAR_FAR_AWAY, MAX_SHORT_LIVED_RELATIVE_EXPIRY},
3737
};
3838
use crate::ln::inbound_payment;
39+
use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
3940
use crate::offers::invoice::{
4041
Bolt12Invoice, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder,
4142
UnsignedBolt12Invoice, DEFAULT_RELATIVE_EXPIRY,
@@ -98,6 +99,10 @@ where
9899
pub(crate) pending_offers_messages: Mutex<Vec<(OffersMessage, MessageSendInstructions)>>,
99100

100101
pending_async_payments_messages: Mutex<Vec<(AsyncPaymentsMessage, MessageSendInstructions)>>,
102+
async_receive_offer_cache: Mutex<AsyncReceiveOfferCache>,
103+
/// Blinded paths used to request offer paths from the static invoice server, if we are an async
104+
/// recipient.
105+
paths_to_static_invoice_server: Vec<BlindedMessagePath>,
101106

102107
#[cfg(feature = "dnssec")]
103108
pub(crate) hrn_resolver: OMNameResolver,
@@ -133,9 +138,25 @@ where
133138
hrn_resolver: OMNameResolver::new(current_timestamp, best_block.height),
134139
#[cfg(feature = "dnssec")]
135140
pending_dns_onion_messages: Mutex::new(Vec::new()),
141+
142+
async_receive_offer_cache: Mutex::new(AsyncReceiveOfferCache::new()),
143+
paths_to_static_invoice_server: Vec::new(),
136144
}
137145
}
138146

147+
/// If we are an async recipient, on startup we'll interactively build offers and static invoices
148+
/// with an always-online node that will serve static invoices on our behalf. Once the offer is
149+
/// built and the static invoice is confirmed as persisted by the server, the underlying
150+
/// [`AsyncReceiveOfferCache`] should be persisted so we remember the offers we've built.
151+
pub(crate) fn with_async_payments_offers_cache(
152+
mut self, async_receive_offer_cache: AsyncReceiveOfferCache,
153+
paths_to_static_invoice_server: &[BlindedMessagePath],
154+
) -> Self {
155+
self.async_receive_offer_cache = Mutex::new(async_receive_offer_cache);
156+
self.paths_to_static_invoice_server = paths_to_static_invoice_server.to_vec();
157+
self
158+
}
159+
139160
/// Gets the node_id held by this [`OffersMessageFlow`]`
140161
pub fn get_our_node_id(&self) -> PublicKey {
141162
self.our_network_pubkey
@@ -724,14 +745,14 @@ where
724745

725746
/// Creates an [`InvoiceBuilder`] using the provided [`Refund`].
726747
///
727-
/// This method is used when a node wishes to construct an [`InvoiceBuilder`]
748+
/// This method is used when a node wishes to construct an [`InvoiceBuilder`]
728749
/// in response to a [`Refund`] request as part of a BOLT 12 flow.
729750
///
730751
/// Returns an `InvoiceBuilder` configured with:
731752
/// - Blinded paths derived from the provided `payment_secret`, which the counterparty will use to pay the invoice.
732753
/// - The given `payment_hash` and `payment_secret` to enable secure claim verification.
733754
///
734-
/// Returns an error if the refund targets a different chain or if no valid
755+
/// Returns an error if the refund targets a different chain or if no valid
735756
/// blinded path can be constructed.
736757
pub fn create_invoice_builder_from_refund<'a, ES: Deref, R: Deref>(
737758
&'a self, router: &R, entropy_source: ES, refund: &'a Refund, payment_hash: PaymentHash,

lightning/src/offers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
pub mod offer;
1717
pub mod flow;
1818

19+
pub(crate) mod async_receive_offer_cache;
1920
pub mod invoice;
2021
pub mod invoice_error;
2122
mod invoice_macros;

0 commit comments

Comments
 (0)