@@ -78,6 +78,92 @@ impl AsyncReceiveOfferCache {
7878 }
7979}
8080
81+ #[ cfg( async_payments) ]
82+ impl AsyncReceiveOfferCache {
83+ // The target number of offers we want to have cached at any given time, to mitigate too much
84+ // reuse of the same offer.
85+ const NUM_CACHED_OFFERS_TARGET : usize = 3 ;
86+
87+ // The max number of times we'll attempt to request offer paths or attempt to refresh a static
88+ // invoice before giving up.
89+ const MAX_UPDATE_ATTEMPTS : u8 = 3 ;
90+
91+ /// Remove expired offers from the cache.
92+ pub ( super ) fn prune_expired_offers ( & mut self , duration_since_epoch : Duration ) {
93+ // Remove expired offers from the cache.
94+ let mut offer_was_removed = false ;
95+ self . offers . retain ( |offer| {
96+ if offer. offer . is_expired_no_std ( duration_since_epoch) {
97+ offer_was_removed = true ;
98+ return false ;
99+ }
100+ true
101+ } ) ;
102+
103+ // If we just removed a newly expired offer, force allowing more paths request attempts.
104+ if offer_was_removed {
105+ self . reset_offer_paths_request_attempts ( ) ;
106+ }
107+
108+ // If we haven't attempted to request new paths in a long time, allow more requests to go out
109+ // if/when needed.
110+ self . check_reset_offer_paths_request_attempts ( duration_since_epoch) ;
111+ }
112+
113+ /// Checks whether we should request new offer paths from the always-online static invoice server.
114+ pub ( super ) fn should_request_offer_paths ( & self , duration_since_epoch : Duration ) -> bool {
115+ self . needs_new_offers ( duration_since_epoch)
116+ && self . offer_paths_request_attempts < Self :: MAX_UPDATE_ATTEMPTS
117+ }
118+
119+ /// Returns a bool indicating whether new offers are needed in the cache.
120+ fn needs_new_offers ( & self , duration_since_epoch : Duration ) -> bool {
121+ // If we have fewer than NUM_CACHED_OFFERS_TARGET offers that aren't expiring soon, indicate
122+ // that new offers should be interactively built.
123+ let num_unexpiring_offers = self
124+ . offers
125+ . iter ( )
126+ . filter ( |offer| {
127+ let offer_absolute_expiry = offer. offer . absolute_expiry ( ) . unwrap_or ( Duration :: MAX ) ;
128+ let offer_created_at = offer. offer_created_at ;
129+ let offer_lifespan =
130+ offer_absolute_expiry. saturating_sub ( offer_created_at) . as_secs ( ) ;
131+ let elapsed = duration_since_epoch. saturating_sub ( offer_created_at) . as_secs ( ) ;
132+
133+ // If an offer is in the last 10% of its lifespan, it's expiring soon.
134+ elapsed. saturating_mul ( 10 ) >= offer_lifespan. saturating_mul ( 9 )
135+ } )
136+ . count ( ) ;
137+
138+ num_unexpiring_offers < Self :: NUM_CACHED_OFFERS_TARGET
139+ }
140+
141+ // Indicates that onion messages requesting new offer paths have been sent to the static invoice
142+ // server. Calling this method allows the cache to self-limit how many requests are sent, in case
143+ // the server goes unresponsive.
144+ pub ( super ) fn new_offers_requested ( & mut self , duration_since_epoch : Duration ) {
145+ self . offer_paths_request_attempts += 1 ;
146+ self . last_offer_paths_request_timestamp = duration_since_epoch;
147+ }
148+
149+ /// If we haven't sent an offer paths request in a long time, reset the limit to allow more
150+ /// requests to be sent out if/when needed.
151+ fn check_reset_offer_paths_request_attempts ( & mut self , duration_since_epoch : Duration ) {
152+ const REQUESTS_TIME_BUFFER : Duration = Duration :: from_secs ( 3 * 60 * 60 ) ;
153+ let should_reset =
154+ self . last_offer_paths_request_timestamp . saturating_add ( REQUESTS_TIME_BUFFER )
155+ < duration_since_epoch;
156+ if should_reset {
157+ self . reset_offer_paths_request_attempts ( ) ;
158+ }
159+ }
160+
161+ fn reset_offer_paths_request_attempts ( & mut self ) {
162+ self . offer_paths_request_attempts = 0 ;
163+ self . last_offer_paths_request_timestamp = Duration :: from_secs ( 0 ) ;
164+ }
165+ }
166+
81167impl Writeable for AsyncReceiveOfferCache {
82168 fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
83169 write_tlv_fields ! ( w, {
0 commit comments