1010//! Data structures and encoding for `invoice` messages.
1111
1212use bitcoin:: blockdata:: constants:: ChainHash ;
13+ use bitcoin:: hash_types:: { WPubkeyHash , WScriptHash } ;
14+ use bitcoin:: hashes:: Hash ;
1315use bitcoin:: network:: constants:: Network ;
14- use bitcoin:: secp256k1:: PublicKey ;
16+ use bitcoin:: secp256k1:: { Message , PublicKey } ;
1517use bitcoin:: secp256k1:: schnorr:: Signature ;
1618use bitcoin:: util:: address:: { Address , Payload , WitnessVersion } ;
19+ use bitcoin:: util:: schnorr:: TweakedPublicKey ;
1720use core:: convert:: TryFrom ;
1821use core:: time:: Duration ;
1922use crate :: io;
2023use crate :: ln:: PaymentHash ;
2124use crate :: ln:: features:: { BlindedHopFeatures , Bolt12InvoiceFeatures } ;
2225use crate :: ln:: msgs:: DecodeError ;
23- use crate :: offers:: invoice_request:: { InvoiceRequestContents , InvoiceRequestTlvStream } ;
24- use crate :: offers:: merkle:: { SignatureTlvStream , self } ;
25- use crate :: offers:: offer:: OfferTlvStream ;
26+ use crate :: offers:: invoice_request:: { InvoiceRequest , InvoiceRequestContents , InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef } ;
27+ use crate :: offers:: merkle:: { SignError , SignatureTlvStream , SignatureTlvStreamRef , WithoutSignatures , self } ;
28+ use crate :: offers:: offer:: { Amount , OfferTlvStream , OfferTlvStreamRef } ;
2629use crate :: offers:: parse:: { ParseError , ParsedMessage , SemanticError } ;
27- use crate :: offers:: payer:: PayerTlvStream ;
30+ use crate :: offers:: payer:: { PayerTlvStream , PayerTlvStreamRef } ;
2831use crate :: offers:: refund:: RefundContents ;
2932use crate :: onion_message:: BlindedPath ;
30- use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , SeekReadable , WithoutLength , Writeable , Writer } ;
33+ use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , Iterable , SeekReadable , WithoutLength , Writeable , Writer } ;
3134
3235use crate :: prelude:: * ;
3336
@@ -38,6 +41,161 @@ const DEFAULT_RELATIVE_EXPIRY: Duration = Duration::from_secs(7200);
3841
3942const SIGNATURE_TAG : & ' static str = concat ! ( "lightning" , "invoice" , "signature" ) ;
4043
44+ /// Builds an [`Invoice`] from either:
45+ /// - an [`InvoiceRequest`] for the "offer to be paid" flow or
46+ /// - a [`Refund`] for the "offer for money" flow.
47+ ///
48+ /// See [module-level documentation] for usage.
49+ ///
50+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
51+ /// [`Refund`]: crate::offers::refund::Refund
52+ /// [module-level documentation]: self
53+ pub struct InvoiceBuilder < ' a > {
54+ invreq_bytes : & ' a Vec < u8 > ,
55+ invoice : InvoiceContents ,
56+ }
57+
58+ impl < ' a > InvoiceBuilder < ' a > {
59+ pub ( super ) fn for_offer (
60+ invoice_request : & ' a InvoiceRequest , payment_paths : Vec < ( BlindedPath , BlindedPayInfo ) > ,
61+ created_at : Duration , payment_hash : PaymentHash
62+ ) -> Result < Self , SemanticError > {
63+ if payment_paths. is_empty ( ) {
64+ return Err ( SemanticError :: MissingPaths ) ;
65+ }
66+
67+ let amount_msats = match invoice_request. amount_msats ( ) {
68+ Some ( amount_msats) => amount_msats,
69+ None => match invoice_request. contents . offer . amount ( ) {
70+ Some ( Amount :: Bitcoin { amount_msats } ) => {
71+ amount_msats * invoice_request. quantity ( ) . unwrap_or ( 1 )
72+ } ,
73+ Some ( Amount :: Currency { .. } ) => return Err ( SemanticError :: UnsupportedCurrency ) ,
74+ None => return Err ( SemanticError :: MissingAmount ) ,
75+ } ,
76+ } ;
77+
78+ Ok ( Self {
79+ invreq_bytes : & invoice_request. bytes ,
80+ invoice : InvoiceContents :: ForOffer {
81+ invoice_request : invoice_request. contents . clone ( ) ,
82+ fields : InvoiceFields {
83+ payment_paths, created_at, relative_expiry : None , payment_hash, amount_msats,
84+ fallbacks : None , features : Bolt12InvoiceFeatures :: empty ( ) ,
85+ signing_pubkey : invoice_request. contents . offer . signing_pubkey ( ) ,
86+ } ,
87+ } ,
88+ } )
89+ }
90+
91+ /// Sets the [`Invoice::relative_expiry`] as seconds since [`Invoice::created_at`]. Any expiry
92+ /// that has already passed is valid and can be checked for using [`Invoice::is_expired`].
93+ ///
94+ /// Successive calls to this method will override the previous setting.
95+ pub fn relative_expiry ( mut self , relative_expiry_secs : u32 ) -> Self {
96+ let relative_expiry = Duration :: from_secs ( relative_expiry_secs as u64 ) ;
97+ self . invoice . fields_mut ( ) . relative_expiry = Some ( relative_expiry) ;
98+ self
99+ }
100+
101+ /// Adds a P2WSH address to [`Invoice::fallbacks`].
102+ ///
103+ /// Successive calls to this method will add another address. Caller is responsible for not
104+ /// adding duplicate addresses and only calling if capable of receiving to P2WSH addresses.
105+ pub fn fallback_v0_p2wsh ( mut self , script_hash : & WScriptHash ) -> Self {
106+ let address = FallbackAddress {
107+ version : WitnessVersion :: V0 . to_num ( ) ,
108+ program : Vec :: from ( & script_hash. into_inner ( ) [ ..] ) ,
109+ } ;
110+ self . invoice . fields_mut ( ) . fallbacks . get_or_insert_with ( Vec :: new) . push ( address) ;
111+ self
112+ }
113+
114+ /// Adds a P2WPKH address to [`Invoice::fallbacks`].
115+ ///
116+ /// Successive calls to this method will add another address. Caller is responsible for not
117+ /// adding duplicate addresses and only calling if capable of receiving to P2WPKH addresses.
118+ pub fn fallback_v0_p2wpkh ( mut self , pubkey_hash : & WPubkeyHash ) -> Self {
119+ let address = FallbackAddress {
120+ version : WitnessVersion :: V0 . to_num ( ) ,
121+ program : Vec :: from ( & pubkey_hash. into_inner ( ) [ ..] ) ,
122+ } ;
123+ self . invoice . fields_mut ( ) . fallbacks . get_or_insert_with ( Vec :: new) . push ( address) ;
124+ self
125+ }
126+
127+ /// Adds a P2TR address to [`Invoice::fallbacks`].
128+ ///
129+ /// Successive calls to this method will add another address. Caller is responsible for not
130+ /// adding duplicate addresses and only calling if capable of receiving to P2TR addresses.
131+ pub fn fallback_v1_p2tr_tweaked ( mut self , output_key : & TweakedPublicKey ) -> Self {
132+ let address = FallbackAddress {
133+ version : WitnessVersion :: V1 . to_num ( ) ,
134+ program : Vec :: from ( & output_key. serialize ( ) [ ..] ) ,
135+ } ;
136+ self . invoice . fields_mut ( ) . fallbacks . get_or_insert_with ( Vec :: new) . push ( address) ;
137+ self
138+ }
139+
140+ /// Sets [`Invoice::features`] to indicate MPP may be used. Otherwise, MPP is disallowed.
141+ pub fn allow_mpp ( mut self ) -> Self {
142+ self . invoice . fields_mut ( ) . features . set_basic_mpp_optional ( ) ;
143+ self
144+ }
145+
146+ /// Builds an unsigned [`Invoice`] after checking for valid semantics. It can be signed by
147+ /// [`UnsignedInvoice::sign`].
148+ pub fn build ( self ) -> Result < UnsignedInvoice < ' a > , SemanticError > {
149+ #[ cfg( feature = "std" ) ] {
150+ if self . invoice . is_offer_or_refund_expired ( ) {
151+ return Err ( SemanticError :: AlreadyExpired ) ;
152+ }
153+ }
154+
155+ let InvoiceBuilder { invreq_bytes, invoice } = self ;
156+ Ok ( UnsignedInvoice { invreq_bytes, invoice } )
157+ }
158+ }
159+
160+ /// A semantically valid [`Invoice`] that hasn't been signed.
161+ pub struct UnsignedInvoice < ' a > {
162+ invreq_bytes : & ' a Vec < u8 > ,
163+ invoice : InvoiceContents ,
164+ }
165+
166+ impl < ' a > UnsignedInvoice < ' a > {
167+ /// Signs the invoice using the given function.
168+ pub fn sign < F , E > ( self , sign : F ) -> Result < Invoice , SignError < E > >
169+ where
170+ F : FnOnce ( & Message ) -> Result < Signature , E >
171+ {
172+ // Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
173+ // have contained unknown TLV records, which are not stored in `InvoiceRequestContents` or
174+ // `RefundContents`.
175+ let ( _, _, _, invoice_tlv_stream) = self . invoice . as_tlv_stream ( ) ;
176+ let invoice_request_bytes = WithoutSignatures ( self . invreq_bytes ) ;
177+ let unsigned_tlv_stream = ( invoice_request_bytes, invoice_tlv_stream) ;
178+
179+ let mut bytes = Vec :: new ( ) ;
180+ unsigned_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
181+
182+ let pubkey = self . invoice . fields ( ) . signing_pubkey ;
183+ let signature = merkle:: sign_message ( sign, SIGNATURE_TAG , & bytes, pubkey) ?;
184+
185+ // Append the signature TLV record to the bytes.
186+ let signature_tlv_stream = SignatureTlvStreamRef {
187+ signature : Some ( & signature) ,
188+ } ;
189+ signature_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
190+
191+ Ok ( Invoice {
192+ bytes,
193+ contents : self . invoice ,
194+ signature,
195+ } )
196+ }
197+ }
198+
41199/// An `Invoice` is a payment request, typically corresponding to an [`Offer`] or a [`Refund`].
42200///
43201/// An invoice may be sent in response to an [`InvoiceRequest`] in the case of an offer or sent
@@ -199,6 +357,15 @@ impl Invoice {
199357}
200358
201359impl InvoiceContents {
360+ /// Whether the original offer or refund has expired.
361+ #[ cfg( feature = "std" ) ]
362+ fn is_offer_or_refund_expired ( & self ) -> bool {
363+ match self {
364+ InvoiceContents :: ForOffer { invoice_request, .. } => invoice_request. offer . is_expired ( ) ,
365+ InvoiceContents :: ForRefund { refund, .. } => refund. is_expired ( ) ,
366+ }
367+ }
368+
202369 fn chain ( & self ) -> ChainHash {
203370 match self {
204371 InvoiceContents :: ForOffer { invoice_request, .. } => invoice_request. chain ( ) ,
@@ -212,6 +379,44 @@ impl InvoiceContents {
212379 InvoiceContents :: ForRefund { fields, .. } => fields,
213380 }
214381 }
382+
383+ fn fields_mut ( & mut self ) -> & mut InvoiceFields {
384+ match self {
385+ InvoiceContents :: ForOffer { fields, .. } => fields,
386+ InvoiceContents :: ForRefund { fields, .. } => fields,
387+ }
388+ }
389+
390+ fn as_tlv_stream ( & self ) -> PartialInvoiceTlvStreamRef {
391+ let ( payer, offer, invoice_request) = match self {
392+ InvoiceContents :: ForOffer { invoice_request, .. } => invoice_request. as_tlv_stream ( ) ,
393+ InvoiceContents :: ForRefund { refund, .. } => refund. as_tlv_stream ( ) ,
394+ } ;
395+ let invoice = self . fields ( ) . as_tlv_stream ( ) ;
396+
397+ ( payer, offer, invoice_request, invoice)
398+ }
399+ }
400+
401+ impl InvoiceFields {
402+ fn as_tlv_stream ( & self ) -> InvoiceTlvStreamRef {
403+ let features = {
404+ if self . features == Bolt12InvoiceFeatures :: empty ( ) { None }
405+ else { Some ( & self . features ) }
406+ } ;
407+
408+ InvoiceTlvStreamRef {
409+ paths : Some ( Iterable ( self . payment_paths . iter ( ) . map ( |( path, _) | path) ) ) ,
410+ blindedpay : Some ( Iterable ( self . payment_paths . iter ( ) . map ( |( _, payinfo) | payinfo) ) ) ,
411+ created_at : Some ( self . created_at . as_secs ( ) ) ,
412+ relative_expiry : self . relative_expiry . map ( |duration| duration. as_secs ( ) as u32 ) ,
413+ payment_hash : Some ( & self . payment_hash ) ,
414+ amount : Some ( self . amount_msats ) ,
415+ fallbacks : self . fallbacks . as_ref ( ) ,
416+ features,
417+ node_id : Some ( & self . signing_pubkey ) ,
418+ }
419+ }
215420}
216421
217422impl Writeable for Invoice {
@@ -230,8 +435,8 @@ impl TryFrom<Vec<u8>> for Invoice {
230435}
231436
232437tlv_stream ! ( InvoiceTlvStream , InvoiceTlvStreamRef , 160 ..240 , {
233- ( 160 , paths: ( Vec <BlindedPath >, WithoutLength ) ) ,
234- ( 162 , blindedpay: ( Vec <BlindedPayInfo >, WithoutLength ) ) ,
438+ ( 160 , paths: ( Vec <BlindedPath >, WithoutLength , Iterable < ' a , BlindedPathIter < ' a> , BlindedPath > ) ) ,
439+ ( 162 , blindedpay: ( Vec <BlindedPayInfo >, WithoutLength , Iterable < ' a , BlindedPayInfoIter < ' a> , BlindedPayInfo > ) ) ,
235440 ( 164 , created_at: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
236441 ( 166 , relative_expiry: ( u32 , HighZeroBytesDroppedBigSize ) ) ,
237442 ( 168 , payment_hash: PaymentHash ) ,
@@ -241,7 +446,17 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef, 160..240, {
241446 ( 176 , node_id: PublicKey ) ,
242447} ) ;
243448
244- /// Information needed to route a payment across a [`BlindedPath`] hop.
449+ type BlindedPathIter < ' a > = core:: iter:: Map <
450+ core:: slice:: Iter < ' a , ( BlindedPath , BlindedPayInfo ) > ,
451+ for <' r > fn ( & ' r ( BlindedPath , BlindedPayInfo ) ) -> & ' r BlindedPath ,
452+ > ;
453+
454+ type BlindedPayInfoIter < ' a > = core:: iter:: Map <
455+ core:: slice:: Iter < ' a , ( BlindedPath , BlindedPayInfo ) > ,
456+ for <' r > fn ( & ' r ( BlindedPath , BlindedPayInfo ) ) -> & ' r BlindedPayInfo ,
457+ > ;
458+
459+ /// Information needed to route a payment across a [`BlindedPath`].
245460#[ derive( Debug , PartialEq ) ]
246461pub struct BlindedPayInfo {
247462 fee_base_msat : u32 ,
@@ -288,6 +503,13 @@ impl SeekReadable for FullInvoiceTlvStream {
288503type PartialInvoiceTlvStream =
289504 ( PayerTlvStream , OfferTlvStream , InvoiceRequestTlvStream , InvoiceTlvStream ) ;
290505
506+ type PartialInvoiceTlvStreamRef < ' a > = (
507+ PayerTlvStreamRef < ' a > ,
508+ OfferTlvStreamRef < ' a > ,
509+ InvoiceRequestTlvStreamRef < ' a > ,
510+ InvoiceTlvStreamRef < ' a > ,
511+ ) ;
512+
291513impl TryFrom < ParsedMessage < FullInvoiceTlvStream > > for Invoice {
292514 type Error = ParseError ;
293515
0 commit comments