Skip to content

Commit bdf5dcb

Browse files
slanesukejkczyz
andcommitted
Validate amount_msats against invoice and refund amounts
Add a check to ensure that the amount_msats in an invoice matches the amount_msats specified in the invoice_request or refund. Reject the invoice as invalid if there is a mismatch between these amounts. Otherwise, an invoice may be paid with an amount greater than the requested amount. Co-authored-by: Ian Slane <[email protected]> Co-authored-by: Jeffrey Czyz <[email protected]>
1 parent 020be44 commit bdf5dcb

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

lightning/src/offers/invoice.rs

+40
Original file line numberDiff line numberDiff line change
@@ -1531,6 +1531,11 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
15311531
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
15321532
)
15331533
)?;
1534+
1535+
if amount_msats != refund.amount_msats() {
1536+
return Err(Bolt12SemanticError::InvalidAmount);
1537+
}
1538+
15341539
Ok(InvoiceContents::ForRefund { refund, fields })
15351540
} else {
15361541
let invoice_request = InvoiceRequestContents::try_from(
@@ -1539,6 +1544,13 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
15391544
experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
15401545
)
15411546
)?;
1547+
1548+
if let Some(requested_amount_msats) = invoice_request.amount_msats() {
1549+
if amount_msats != requested_amount_msats {
1550+
return Err(Bolt12SemanticError::InvalidAmount);
1551+
}
1552+
}
1553+
15421554
Ok(InvoiceContents::ForOffer { invoice_request, fields })
15431555
}
15441556
}
@@ -2469,6 +2481,7 @@ mod tests {
24692481
.amount_msats(1000)
24702482
.build().unwrap()
24712483
.request_invoice(&expanded_key, nonce, &secp_ctx, payment_id).unwrap()
2484+
.amount_msats(1000).unwrap()
24722485
.build_and_sign().unwrap()
24732486
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
24742487
.build().unwrap()
@@ -2488,6 +2501,33 @@ mod tests {
24882501
Ok(_) => panic!("expected error"),
24892502
Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingAmount)),
24902503
}
2504+
2505+
let mut tlv_stream = invoice.as_tlv_stream();
2506+
tlv_stream.3.amount = Some(2000);
2507+
2508+
match Bolt12Invoice::try_from(tlv_stream.to_bytes()) {
2509+
Ok(_) => panic!("expected error"),
2510+
Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)),
2511+
}
2512+
2513+
let invoice = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
2514+
.build().unwrap()
2515+
.respond_using_derived_keys_no_std(
2516+
payment_paths(), payment_hash(), now(), &expanded_key, &entropy
2517+
)
2518+
.unwrap()
2519+
.build_and_sign(&secp_ctx).unwrap();
2520+
2521+
let mut buffer = Vec::new();
2522+
invoice.write(&mut buffer).unwrap();
2523+
2524+
let mut tlv_stream = invoice.as_tlv_stream();
2525+
tlv_stream.3.amount = Some(2000);
2526+
2527+
match Bolt12Invoice::try_from(tlv_stream.to_bytes()) {
2528+
Ok(_) => panic!("expected error"),
2529+
Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidAmount)),
2530+
}
24912531
}
24922532

24932533
#[test]

lightning/src/offers/parse.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ pub enum Bolt12SemanticError {
146146
UnexpectedChain,
147147
/// An amount was expected but was missing.
148148
MissingAmount,
149-
/// The amount exceeded the total bitcoin supply.
149+
/// The amount exceeded the total bitcoin supply or didn't match an expected amount.
150150
InvalidAmount,
151151
/// An amount was provided but was not sufficient in value.
152152
InsufficientAmount,

0 commit comments

Comments
 (0)