Skip to content

Conversation

@vincenzopalazzo
Copy link
Contributor

@vincenzopalazzo vincenzopalazzo commented Jan 20, 2026

Summary

  • Normalize offer_amount=0 to None in both the builder and parser for consistent internal representation
  • An amount of zero has no practical meaning (you cannot pay zero), so treating it as "no amount specified" simplifies handling
  • When parsing offers with amount=0 (with or without currency), the amount is normalized to None

Motivation

Per BOLT12, if no specific minimum amount is required, the offer_amount field should be omitted rather than set to zero. This change ensures offers with amount=0 are handled gracefully rather than causing unexpected behavior downstream.

Spec clarification pending: lightning/bolts#1314

Test plan

  • Builder test: amount_msats(0) results in amount() == None and as_tlv_stream().0.amount == None
  • Roundtrip test: serialize and re-parse maintains None state
  • Parser tests: TLV with amount=0 (with and without currency) results in amount() == None
  • Invoice request tests: offer with amount=0 requires invoice request to provide amount

Per BOLT12, if no specific minimum amount is required, the offer_amount
field should be omitted rather than set to zero. Since an amount of zero
has no practical meaning (you cannot pay zero), this change normalizes
amount=0 to None in both the builder and parser, ensuring consistent
internal representation.

This normalization allows offers with amount=0 to be handled gracefully
rather than being rejected or causing unexpected behavior downstream.

Spec clarification pending: lightning/bolts#1314
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Jan 20, 2026

👋 Thanks for assigning @jkczyz as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@jkczyz jkczyz requested review from jkczyz and removed request for wpaulino January 20, 2026 15:16
@codecov
Copy link

codecov bot commented Jan 20, 2026

Codecov Report

❌ Patch coverage is 92.30769% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.64%. Comparing base (09b3bef) to head (14b7f66).

Files with missing lines Patch % Lines
lightning/src/offers/offer.rs 90.62% 3 Missing ⚠️
lightning/src/offers/invoice_request.rs 95.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4324      +/-   ##
==========================================
+ Coverage   86.61%   86.64%   +0.02%     
==========================================
  Files         158      158              
  Lines      102730   102782      +52     
  Branches   102730   102782      +52     
==========================================
+ Hits        88984    89052      +68     
+ Misses      11328    11315      -13     
+ Partials     2418     2415       -3     
Flag Coverage Δ
fuzzing 37.23% <40.00%> (+1.03%) ⬆️
tests 85.92% <92.30%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth waiting on feedback from other implementations. Left some concerns of mine. I'd lean more towards disallowing explicit zero amounts.

return Err(Bolt12SemanticError::InvalidAmount);
},
// An amount of 0 is equivalent to not setting an amount, so default to None.
(None, Some(0)) => None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm... I'd rather we not use the same representation for two different serializations. It may still work because when creating the InvoiceRequest we'll serialize using Offer::bytes. If we didn't use that, the issuer may fail to authenticate the offer when receiving an InvoiceRequest.

Comment on lines +1320 to +1321
// An amount of 0 with a currency is equivalent to not setting an amount.
(Some(_), Some(0)) => None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prevents the currency code check. Also, I don't think it's useful to drop information from the Offer even if we still retain it in Offer::bytes. At very least it would be surprising if viewing an Offer that is suppose to have a currency but LDK shows that it doesn't.

Comment on lines +408 to +411
// An amount of 0 is equivalent to not setting an amount, so default to None.
if amount_msats == 0 {
$self.offer.amount = None;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm somewhat unsure about this change. It could hide an error on the user's end. It might be better to fail in this case instead of allowing them to accept any amount.

@ldk-reviews-bot
Copy link

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants