Skip to content

Commit a9de9fb

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 bcbdc7f commit a9de9fb

File tree

4 files changed

+130
-0
lines changed

4 files changed

+130
-0
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 5 additions & 0 deletions
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;
@@ -13742,6 +13743,7 @@ where
1374213743
let mut decode_update_add_htlcs: Option<HashMap<u64, Vec<msgs::UpdateAddHTLC>>> = None;
1374313744
let mut inbound_payment_id_secret = None;
1374413745
let mut peer_storage_dir: Option<Vec<(PublicKey, Vec<u8>)>> = None;
13746+
let mut async_receive_offer_cache: AsyncReceiveOfferCache = AsyncReceiveOfferCache::new();
1374513747
read_tlv_fields!(reader, {
1374613748
(1, pending_outbound_payments_no_retry, option),
1374713749
(2, pending_intercepted_htlcs, option),
@@ -13759,6 +13761,7 @@ where
1375913761
(15, inbound_payment_id_secret, option),
1376013762
(17, in_flight_monitor_updates, option),
1376113763
(19, peer_storage_dir, optional_vec),
13764+
(21, async_receive_offer_cache, (default_value, async_receive_offer_cache)),
1376213765
});
1376313766
let mut decode_update_add_htlcs = decode_update_add_htlcs.unwrap_or_else(|| new_hash_map());
1376413767
let peer_storage_dir: Vec<(PublicKey, Vec<u8>)> = peer_storage_dir.unwrap_or_else(Vec::new);
@@ -14445,6 +14448,8 @@ where
1444514448
chain_hash, best_block, our_network_pubkey,
1444614449
highest_seen_timestamp, expanded_inbound_key,
1444714450
secp_ctx.clone(), args.message_router
14451+
).with_async_payments_offers_cache(
14452+
async_receive_offer_cache, &args.default_config.paths_to_static_invoice_server[..]
1444814453
);
1444914454

1445014455
let channel_manager = ChannelManager {
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: 21 additions & 0 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,
@@ -99,6 +100,10 @@ where
99100
pub(crate) pending_offers_messages: Mutex<Vec<(OffersMessage, MessageSendInstructions)>>,
100101

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

103108
#[cfg(feature = "dnssec")]
104109
pub(crate) hrn_resolver: OMNameResolver,
@@ -134,9 +139,25 @@ where
134139
hrn_resolver: OMNameResolver::new(current_timestamp, best_block.height),
135140
#[cfg(feature = "dnssec")]
136141
pending_dns_onion_messages: Mutex::new(Vec::new()),
142+
143+
async_receive_offer_cache: Mutex::new(AsyncReceiveOfferCache::new()),
144+
paths_to_static_invoice_server: Vec::new(),
137145
}
138146
}
139147

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

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)