Skip to content

Commit 8b1dc05

Browse files
authored
Merge pull request #4324 from vincenzopalazzo/macros/bolt12-offer-amount-0
Treat offer_amount of 0 as equivalent to None
2 parents a7b4519 + a06c446 commit 8b1dc05

2 files changed

Lines changed: 68 additions & 2 deletions

File tree

lightning/src/offers/invoice_request.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,12 @@ mod tests {
20402040
Err(e) => assert_eq!(e, Bolt12SemanticError::MissingAmount),
20412041
}
20422042

2043+
// An offer with amount_msats(0) must be rejected by the builder per BOLT 12.
2044+
match OfferBuilder::new(recipient_pubkey()).amount_msats(0).build() {
2045+
Ok(_) => panic!("expected error"),
2046+
Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
2047+
}
2048+
20432049
match OfferBuilder::new(recipient_pubkey())
20442050
.amount_msats(1000)
20452051
.supported_quantity(Quantity::Unbounded)

lightning/src/offers/offer.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ macro_rules! offer_builder_methods { (
402402
pub fn build($($self_mut)* $self: $self_type) -> Result<Offer, Bolt12SemanticError> {
403403
match $self.offer.amount {
404404
Some(Amount::Bitcoin { amount_msats }) => {
405-
if amount_msats > MAX_VALUE_MSAT {
405+
if amount_msats == 0 || amount_msats > MAX_VALUE_MSAT {
406406
return Err(Bolt12SemanticError::InvalidAmount);
407407
}
408408
},
@@ -1306,11 +1306,12 @@ impl TryFrom<FullOfferTlvStream> for OfferContents {
13061306

13071307
let amount = match (currency, amount) {
13081308
(None, None) => None,
1309-
(None, Some(amount_msats)) if amount_msats > MAX_VALUE_MSAT => {
1309+
(None, Some(amount_msats)) if amount_msats == 0 || amount_msats > MAX_VALUE_MSAT => {
13101310
return Err(Bolt12SemanticError::InvalidAmount);
13111311
},
13121312
(None, Some(amount_msats)) => Some(Amount::Bitcoin { amount_msats }),
13131313
(Some(_), None) => return Err(Bolt12SemanticError::MissingAmount),
1314+
(Some(_), Some(0)) => return Err(Bolt12SemanticError::InvalidAmount),
13141315
(Some(currency_bytes), Some(amount)) => {
13151316
let iso4217_code = CurrencyCode::new(currency_bytes)
13161317
.map_err(|_| Bolt12SemanticError::InvalidCurrencyCode)?;
@@ -1702,6 +1703,12 @@ mod tests {
17021703
Ok(_) => panic!("expected error"),
17031704
Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
17041705
}
1706+
1707+
// An amount of 0 must be rejected per BOLT 12.
1708+
match OfferBuilder::new(pubkey(42)).amount_msats(0).build() {
1709+
Ok(_) => panic!("expected error"),
1710+
Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
1711+
}
17051712
}
17061713

17071714
#[test]
@@ -1974,6 +1981,59 @@ mod tests {
19741981
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidCurrencyCode)
19751982
),
19761983
}
1984+
1985+
// An offer with amount=0 must be rejected per BOLT 12.
1986+
let mut tlv_stream = offer.as_tlv_stream();
1987+
tlv_stream.0.amount = Some(0);
1988+
tlv_stream.0.currency = None;
1989+
1990+
let mut encoded_offer = Vec::new();
1991+
tlv_stream.write(&mut encoded_offer).unwrap();
1992+
1993+
match Offer::try_from(encoded_offer) {
1994+
Ok(_) => panic!("expected error"),
1995+
Err(e) => assert_eq!(
1996+
e,
1997+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)
1998+
),
1999+
}
2000+
2001+
// An offer with amount=0 and a currency must also be rejected.
2002+
let mut tlv_stream = offer.as_tlv_stream();
2003+
tlv_stream.0.amount = Some(0);
2004+
tlv_stream.0.currency = Some(b"USD");
2005+
2006+
let mut encoded_offer = Vec::new();
2007+
tlv_stream.write(&mut encoded_offer).unwrap();
2008+
2009+
match Offer::try_from(encoded_offer) {
2010+
Ok(_) => panic!("expected error"),
2011+
Err(e) => assert_eq!(
2012+
e,
2013+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)
2014+
),
2015+
}
2016+
2017+
// BOLT 12 test vectors: verify rejection of offers with amount=0 from their
2018+
// bech32 encoding (see bolt12/offers-test.json).
2019+
match "lno1pqqq5qqkyyp4he0fg7pqje62jmnq78cr0ashv4q06qql58tyd9rhp3t2wuyugtq".parse::<Offer>()
2020+
{
2021+
Ok(_) => panic!("expected error"),
2022+
Err(e) => assert_eq!(
2023+
e,
2024+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)
2025+
),
2026+
}
2027+
2028+
match "lno1qcp4256ypqqq5qqkyyp4he0fg7pqje62jmnq78cr0ashv4q06qql58tyd9rhp3t2wuyugtq"
2029+
.parse::<Offer>()
2030+
{
2031+
Ok(_) => panic!("expected error"),
2032+
Err(e) => assert_eq!(
2033+
e,
2034+
Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)
2035+
),
2036+
}
19772037
}
19782038

19792039
#[test]

0 commit comments

Comments
 (0)