@@ -79,7 +79,7 @@ use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
7979use crate :: ln:: msgs:: DecodeError ;
8080use crate :: offers:: merkle:: { SignError , SignFn , SignatureTlvStream , SignatureTlvStreamRef , TaggedHash , TlvStream , self , SIGNATURE_TLV_RECORD_SIZE } ;
8181use crate :: offers:: nonce:: Nonce ;
82- use crate :: offers:: offer:: { EXPERIMENTAL_OFFER_TYPES , ExperimentalOfferTlvStream , ExperimentalOfferTlvStreamRef , OFFER_TYPES , Offer , OfferContents , OfferId , OfferTlvStream , OfferTlvStreamRef } ;
82+ use crate :: offers:: offer:: { Amount , EXPERIMENTAL_OFFER_TYPES , ExperimentalOfferTlvStream , ExperimentalOfferTlvStreamRef , OFFER_TYPES , Offer , OfferContents , OfferId , OfferTlvStream , OfferTlvStreamRef } ;
8383use crate :: offers:: parse:: { Bolt12ParseError , ParsedMessage , Bolt12SemanticError } ;
8484use crate :: offers:: payer:: { PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
8585use crate :: offers:: signer:: { Metadata , MetadataMaterial } ;
@@ -665,6 +665,15 @@ macro_rules! invoice_request_accessors { ($self: ident, $contents: expr) => {
665665 $contents. amount_msats( )
666666 }
667667
668+ /// Returns whether an amount was set in the request; otherwise, if [`amount_msats`] is `Some`
669+ /// then it was inferred from the [`Offer::amount`] and [`quantity`].
670+ ///
671+ /// [`amount_msats`]: Self::amount_msats
672+ /// [`quantity`]: Self::quantity
673+ pub fn has_amount_msats( & $self) -> bool {
674+ $contents. has_amount_msats( )
675+ }
676+
668677 /// Features pertaining to requesting an invoice.
669678 pub fn invoice_request_features( & $self) -> & InvoiceRequestFeatures {
670679 & $contents. features( )
@@ -974,7 +983,19 @@ impl InvoiceRequestContents {
974983 }
975984
976985 pub ( super ) fn amount_msats ( & self ) -> Option < u64 > {
977- self . inner . amount_msats
986+ self . inner
987+ . amount_msats ( )
988+ . or_else ( || match self . inner . offer . amount ( ) {
989+ Some ( Amount :: Bitcoin { amount_msats } ) => {
990+ Some ( amount_msats. saturating_mul ( self . quantity ( ) . unwrap_or ( 1 ) ) )
991+ } ,
992+ Some ( Amount :: Currency { .. } ) => None ,
993+ None => { debug_assert ! ( false ) ; None } ,
994+ } )
995+ }
996+
997+ pub ( super ) fn has_amount_msats ( & self ) -> bool {
998+ self . inner . amount_msats ( ) . is_some ( )
978999 }
9791000
9801001 pub ( super ) fn features ( & self ) -> & InvoiceRequestFeatures {
@@ -1015,6 +1036,10 @@ impl InvoiceRequestContentsWithoutPayerSigningPubkey {
10151036 self . chain . unwrap_or_else ( || self . offer . implied_chain ( ) )
10161037 }
10171038
1039+ pub ( super ) fn amount_msats ( & self ) -> Option < u64 > {
1040+ self . amount_msats
1041+ }
1042+
10181043 pub ( super ) fn as_tlv_stream ( & self ) -> PartialInvoiceRequestTlvStreamRef {
10191044 let payer = PayerTlvStreamRef {
10201045 metadata : self . payer . 0 . as_bytes ( ) ,
@@ -1381,7 +1406,7 @@ mod tests {
13811406 assert_eq ! ( invoice_request. supported_quantity( ) , Quantity :: One ) ;
13821407 assert_eq ! ( invoice_request. issuer_signing_pubkey( ) , Some ( recipient_pubkey( ) ) ) ;
13831408 assert_eq ! ( invoice_request. chain( ) , ChainHash :: using_genesis_block( Network :: Bitcoin ) ) ;
1384- assert_eq ! ( invoice_request. amount_msats( ) , None ) ;
1409+ assert_eq ! ( invoice_request. amount_msats( ) , Some ( 1000 ) ) ;
13851410 assert_eq ! ( invoice_request. invoice_request_features( ) , & InvoiceRequestFeatures :: empty( ) ) ;
13861411 assert_eq ! ( invoice_request. quantity( ) , None ) ;
13871412 assert_eq ! ( invoice_request. payer_note( ) , None ) ;
@@ -1657,6 +1682,7 @@ mod tests {
16571682 . amount_msats ( 1000 ) . unwrap ( )
16581683 . build_and_sign ( ) . unwrap ( ) ;
16591684 let ( _, _, tlv_stream, _, _, _) = invoice_request. as_tlv_stream ( ) ;
1685+ assert ! ( invoice_request. has_amount_msats( ) ) ;
16601686 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 1000 ) ) ;
16611687 assert_eq ! ( tlv_stream. amount, Some ( 1000 ) ) ;
16621688
@@ -1668,6 +1694,7 @@ mod tests {
16681694 . amount_msats ( 1000 ) . unwrap ( )
16691695 . build_and_sign ( ) . unwrap ( ) ;
16701696 let ( _, _, tlv_stream, _, _, _) = invoice_request. as_tlv_stream ( ) ;
1697+ assert ! ( invoice_request. has_amount_msats( ) ) ;
16711698 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 1000 ) ) ;
16721699 assert_eq ! ( tlv_stream. amount, Some ( 1000 ) ) ;
16731700
@@ -1678,6 +1705,7 @@ mod tests {
16781705 . amount_msats ( 1001 ) . unwrap ( )
16791706 . build_and_sign ( ) . unwrap ( ) ;
16801707 let ( _, _, tlv_stream, _, _, _) = invoice_request. as_tlv_stream ( ) ;
1708+ assert ! ( invoice_request. has_amount_msats( ) ) ;
16811709 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 1001 ) ) ;
16821710 assert_eq ! ( tlv_stream. amount, Some ( 1001 ) ) ;
16831711
@@ -1748,6 +1776,47 @@ mod tests {
17481776 }
17491777 }
17501778
1779+ #[ test]
1780+ fn builds_invoice_request_without_amount ( ) {
1781+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
1782+ let entropy = FixedEntropy { } ;
1783+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
1784+ let secp_ctx = Secp256k1 :: new ( ) ;
1785+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1786+
1787+ let invoice_request = OfferBuilder :: new ( recipient_pubkey ( ) )
1788+ . amount_msats ( 1000 )
1789+ . build ( ) . unwrap ( )
1790+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id) . unwrap ( )
1791+ . build_and_sign ( ) . unwrap ( ) ;
1792+ let ( _, _, tlv_stream, _, _, _) = invoice_request. as_tlv_stream ( ) ;
1793+ assert ! ( !invoice_request. has_amount_msats( ) ) ;
1794+ assert_eq ! ( invoice_request. amount_msats( ) , Some ( 1000 ) ) ;
1795+ assert_eq ! ( tlv_stream. amount, None ) ;
1796+
1797+ let invoice_request = OfferBuilder :: new ( recipient_pubkey ( ) )
1798+ . amount_msats ( 1000 )
1799+ . supported_quantity ( Quantity :: Unbounded )
1800+ . build ( ) . unwrap ( )
1801+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id) . unwrap ( )
1802+ . quantity ( 2 ) . unwrap ( )
1803+ . build_and_sign ( ) . unwrap ( ) ;
1804+ let ( _, _, tlv_stream, _, _, _) = invoice_request. as_tlv_stream ( ) ;
1805+ assert ! ( !invoice_request. has_amount_msats( ) ) ;
1806+ assert_eq ! ( invoice_request. amount_msats( ) , Some ( 2000 ) ) ;
1807+ assert_eq ! ( tlv_stream. amount, None ) ;
1808+
1809+ let invoice_request = OfferBuilder :: new ( recipient_pubkey ( ) )
1810+ . amount ( Amount :: Currency { iso4217_code : * b"USD" , amount : 10 } )
1811+ . build_unchecked ( )
1812+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id) . unwrap ( )
1813+ . build_unchecked_and_sign ( ) ;
1814+ let ( _, _, tlv_stream, _, _, _) = invoice_request. as_tlv_stream ( ) ;
1815+ assert ! ( !invoice_request. has_amount_msats( ) ) ;
1816+ assert_eq ! ( invoice_request. amount_msats( ) , None ) ;
1817+ assert_eq ! ( tlv_stream. amount, None ) ;
1818+ }
1819+
17511820 #[ test]
17521821 fn builds_invoice_request_with_features ( ) {
17531822 let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
0 commit comments