@@ -35,12 +35,7 @@ enum OfferStatus {
35
35
/// have a maximally fresh offer. We always want to have at least 1 offer in this state,
36
36
/// preferably a few so we can respond to user requests for new offers without returning the same
37
37
/// one multiple times. Returning a new offer each time is better for privacy.
38
- Ready {
39
- /// If this offer's invoice has been persisted for some time, it's safe to replace to ensure we
40
- /// always have the freshest possible offer available when the user goes to pull an offer from
41
- /// the cache.
42
- invoice_confirmed_persisted_at : Duration ,
43
- } ,
38
+ Ready ,
44
39
/// This offer's invoice is not yet confirmed as persisted by the static invoice server, so it is
45
40
/// not yet ready to receive payments.
46
41
Pending ,
@@ -49,6 +44,9 @@ enum OfferStatus {
49
44
#[ derive( Clone ) ]
50
45
struct AsyncReceiveOffer {
51
46
offer : Offer ,
47
+ /// The time as duration since the Unix epoch at which this offer was created. Useful when
48
+ /// refreshing unused offers.
49
+ created_at : Duration ,
52
50
/// Whether this offer is used, ready for use, or pending invoice persistence with the static
53
51
/// invoice server.
54
52
status : OfferStatus ,
@@ -63,9 +61,7 @@ struct AsyncReceiveOffer {
63
61
64
62
impl_writeable_tlv_based_enum ! ( OfferStatus ,
65
63
( 0 , Used ) => { } ,
66
- ( 1 , Ready ) => {
67
- ( 0 , invoice_confirmed_persisted_at, required) ,
68
- } ,
64
+ ( 1 , Ready ) => { } ,
69
65
( 2 , Pending ) => { } ,
70
66
) ;
71
67
@@ -74,6 +70,7 @@ impl_writeable_tlv_based!(AsyncReceiveOffer, {
74
70
( 2 , offer_nonce, required) ,
75
71
( 4 , status, required) ,
76
72
( 6 , update_static_invoice_path, required) ,
73
+ ( 8 , created_at, required) ,
77
74
} ) ;
78
75
79
76
/// If we are an often-offline recipient, we'll want to interactively build offers and static
@@ -179,9 +176,9 @@ const MAX_CACHED_OFFERS_TARGET: usize = 10;
179
176
#[ cfg( async_payments) ]
180
177
const MAX_UPDATE_ATTEMPTS : u8 = 3 ;
181
178
182
- // If we have an offer that is replaceable and its invoice was confirmed as persisted more than 2
183
- // hours ago, we can go ahead and refresh it because we always want to have the freshest offer
184
- // possible when a user goes to retrieve a cached offer.
179
+ // If we have an offer that is replaceable and is more than 2 hours old, we can go ahead and refresh
180
+ // it because we always want to have the freshest offer possible when a user goes to retrieve a
181
+ // cached offer.
185
182
//
186
183
// We avoid replacing unused offers too quickly -- this prevents the case where we send multiple
187
184
// invoices from different offers competing for the same slot to the server, messages are received
@@ -216,14 +213,11 @@ impl AsyncReceiveOfferCache {
216
213
) -> Result < ( Offer , bool ) , ( ) > {
217
214
self . prune_expired_offers ( duration_since_epoch, false ) ;
218
215
219
- // Find the freshest unused offer, where "freshness" is based on when the invoice was confirmed
220
- // persisted by the server. See `OfferStatus::Ready`.
216
+ // Find the freshest unused offer. See `OfferStatus::Ready`.
221
217
let newest_unused_offer_opt = self
222
- . unused_offers ( )
223
- . max_by ( |( _, _, persisted_at_a) , ( _, _, persisted_at_b) | {
224
- persisted_at_a. cmp ( & persisted_at_b)
225
- } )
226
- . map ( |( idx, offer, _) | ( idx, offer. offer . clone ( ) ) ) ;
218
+ . unused_ready_offers ( )
219
+ . max_by ( |( _, offer_a) , ( _, offer_b) | offer_a. created_at . cmp ( & offer_b. created_at ) )
220
+ . map ( |( idx, offer) | ( idx, offer. offer . clone ( ) ) ) ;
227
221
if let Some ( ( idx, newest_ready_offer) ) = newest_unused_offer_opt {
228
222
self . offers [ idx] . as_mut ( ) . map ( |offer| offer. status = OfferStatus :: Used ) ;
229
223
return Ok ( ( newest_ready_offer, true ) ) ;
@@ -316,6 +310,7 @@ impl AsyncReceiveOfferCache {
316
310
Some ( offer_opt) => {
317
311
* offer_opt = Some ( AsyncReceiveOffer {
318
312
offer,
313
+ created_at : duration_since_epoch,
319
314
offer_nonce,
320
315
status : OfferStatus :: Pending ,
321
316
update_static_invoice_path,
@@ -365,15 +360,11 @@ impl AsyncReceiveOfferCache {
365
360
// Filter for unused offers where longer than OFFER_REFRESH_THRESHOLD time has passed since they
366
361
// were last updated, so they are stale enough to warrant replacement.
367
362
let awhile_ago = duration_since_epoch. saturating_sub ( OFFER_REFRESH_THRESHOLD ) ;
368
- self . unused_offers ( )
369
- . filter ( |( _, _, invoice_confirmed_persisted_at) | {
370
- * invoice_confirmed_persisted_at < awhile_ago
371
- } )
363
+ self . unused_ready_offers ( )
364
+ . filter ( |( _, offer) | offer. created_at < awhile_ago)
372
365
// Get the stalest offer and return its index
373
- . min_by ( |( _, _, persisted_at_a) , ( _, _, persisted_at_b) | {
374
- persisted_at_a. cmp ( & persisted_at_b)
375
- } )
376
- . map ( |( idx, _, _) | idx)
366
+ . min_by ( |( _, offer_a) , ( _, offer_b) | offer_a. created_at . cmp ( & offer_b. created_at ) )
367
+ . map ( |( idx, _) | idx)
377
368
}
378
369
379
370
/// Returns an iterator over (offer_idx, offer)
@@ -387,13 +378,11 @@ impl AsyncReceiveOfferCache {
387
378
} )
388
379
}
389
380
390
- /// Returns an iterator over (offer_idx, offer, invoice_confirmed_persisted_at)
391
- /// where all returned offers are [`OfferStatus::Ready`]
392
- fn unused_offers ( & self ) -> impl Iterator < Item = ( usize , & AsyncReceiveOffer , Duration ) > {
381
+ /// Returns an iterator over (offer_idx, offer) where all returned offers are
382
+ /// [`OfferStatus::Ready`]
383
+ fn unused_ready_offers ( & self ) -> impl Iterator < Item = ( usize , & AsyncReceiveOffer ) > {
393
384
self . offers_with_idx ( ) . filter_map ( |( idx, offer) | match offer. status {
394
- OfferStatus :: Ready { invoice_confirmed_persisted_at } => {
395
- Some ( ( idx, offer, invoice_confirmed_persisted_at) )
396
- } ,
385
+ OfferStatus :: Ready => Some ( ( idx, offer) ) ,
397
386
_ => None ,
398
387
} )
399
388
}
@@ -464,8 +453,7 @@ impl AsyncReceiveOfferCache {
464
453
return false ;
465
454
}
466
455
467
- offer. status =
468
- OfferStatus :: Ready { invoice_confirmed_persisted_at : duration_since_epoch } ;
456
+ offer. status = OfferStatus :: Ready ;
469
457
return true ;
470
458
}
471
459
0 commit comments