Skip to content

Commit b191fd4

Browse files
committed
Check offer expiry when building invoice in no-std
Building an invoice will fail if the underlying offer or refund has already expired. The check was skipped in no-std since there is no system clock. However, the invoice creation time can be used instead. This prevents responding to an invoice request if the offer has already expired.
1 parent 8954280 commit b191fd4

File tree

3 files changed

+42
-14
lines changed

3 files changed

+42
-14
lines changed

lightning/src/offers/invoice.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> {
339339
}
340340
}
341341

342+
#[cfg(not(feature = "std"))] {
343+
if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
344+
return Err(Bolt12SemanticError::AlreadyExpired);
345+
}
346+
}
347+
342348
let InvoiceBuilder { invreq_bytes, invoice, .. } = self;
343349
Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
344350
}
@@ -355,6 +361,12 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
355361
}
356362
}
357363

364+
#[cfg(not(feature = "std"))] {
365+
if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
366+
return Err(Bolt12SemanticError::AlreadyExpired);
367+
}
368+
}
369+
358370
let InvoiceBuilder {
359371
invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
360372
} = self;
@@ -727,6 +739,16 @@ impl InvoiceContents {
727739
}
728740
}
729741

742+
#[cfg(not(feature = "std"))]
743+
fn is_offer_or_refund_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
744+
match self {
745+
InvoiceContents::ForOffer { invoice_request, .. } =>
746+
invoice_request.inner.offer.is_expired_no_std(duration_since_epoch),
747+
InvoiceContents::ForRefund { refund, .. } =>
748+
refund.is_expired_no_std(duration_since_epoch),
749+
}
750+
}
751+
730752
fn offer_chains(&self) -> Option<Vec<ChainHash>> {
731753
match self {
732754
InvoiceContents::ForOffer { invoice_request, .. } =>

lightning/src/offers/offer.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -609,13 +609,16 @@ impl OfferContents {
609609

610610
#[cfg(feature = "std")]
611611
pub(super) fn is_expired(&self) -> bool {
612-
match self.absolute_expiry {
613-
Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
614-
Ok(elapsed) => elapsed > seconds_from_epoch,
615-
Err(_) => false,
616-
},
617-
None => false,
618-
}
612+
SystemTime::UNIX_EPOCH
613+
.elapsed()
614+
.map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
615+
.unwrap_or(false)
616+
}
617+
618+
pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
619+
self.absolute_expiry
620+
.map(|absolute_expiry| duration_since_epoch > absolute_expiry)
621+
.unwrap_or(false)
619622
}
620623

621624
pub fn issuer(&self) -> Option<PrintableString> {

lightning/src/offers/refund.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -540,13 +540,16 @@ impl RefundContents {
540540

541541
#[cfg(feature = "std")]
542542
pub(super) fn is_expired(&self) -> bool {
543-
match self.absolute_expiry {
544-
Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
545-
Ok(elapsed) => elapsed > seconds_from_epoch,
546-
Err(_) => false,
547-
},
548-
None => false,
549-
}
543+
SystemTime::UNIX_EPOCH
544+
.elapsed()
545+
.map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
546+
.unwrap_or(false)
547+
}
548+
549+
pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
550+
self.absolute_expiry
551+
.map(|absolute_expiry| duration_since_epoch > absolute_expiry)
552+
.unwrap_or(false)
550553
}
551554

552555
pub fn issuer(&self) -> Option<PrintableString> {

0 commit comments

Comments
 (0)