10
10
//! Data structures and encoding for `invoice` messages.
11
11
12
12
use bitcoin:: blockdata:: constants:: ChainHash ;
13
+ use bitcoin:: hash_types:: { WPubkeyHash , WScriptHash } ;
14
+ use bitcoin:: hashes:: Hash ;
13
15
use bitcoin:: network:: constants:: Network ;
14
- use bitcoin:: secp256k1:: PublicKey ;
16
+ use bitcoin:: secp256k1:: { Message , PublicKey } ;
15
17
use bitcoin:: secp256k1:: schnorr:: Signature ;
16
18
use bitcoin:: util:: address:: { Address , Payload , WitnessVersion } ;
19
+ use bitcoin:: util:: schnorr:: TweakedPublicKey ;
17
20
use core:: convert:: TryFrom ;
18
21
use core:: time:: Duration ;
19
22
use crate :: io;
20
23
use crate :: ln:: PaymentHash ;
21
24
use crate :: ln:: features:: { BlindedHopFeatures , Bolt12InvoiceFeatures } ;
22
25
use 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 } ;
26
29
use crate :: offers:: parse:: { ParseError , ParsedMessage , SemanticError } ;
27
- use crate :: offers:: payer:: PayerTlvStream ;
30
+ use crate :: offers:: payer:: { PayerTlvStream , PayerTlvStreamRef } ;
28
31
use crate :: offers:: refund:: RefundContents ;
29
32
use 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 } ;
31
34
32
35
use crate :: prelude:: * ;
33
36
@@ -38,6 +41,161 @@ const DEFAULT_RELATIVE_EXPIRY: Duration = Duration::from_secs(7200);
38
41
39
42
const SIGNATURE_TAG : & ' static str = concat ! ( "lightning" , "invoice" , "signature" ) ;
40
43
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.
105
+ pub fn fallback_v0_p2wsh ( mut self , script_hash : & WScriptHash ) -> Self {
106
+ let address = FallbackAddress {
107
+ version : WitnessVersion :: V0 ,
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.
118
+ pub fn fallback_v0_p2wpkh ( mut self , pubkey_hash : & WPubkeyHash ) -> Self {
119
+ let address = FallbackAddress {
120
+ version : WitnessVersion :: V0 ,
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.
131
+ pub fn fallback_v1_p2tr_tweaked ( mut self , output_key : & TweakedPublicKey ) -> Self {
132
+ let address = FallbackAddress {
133
+ version : WitnessVersion :: V1 ,
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
+
41
199
/// An `Invoice` is a payment request, typically corresponding to an [`Offer`] or a [`Refund`].
42
200
///
43
201
/// An invoice may be sent in response to an [`InvoiceRequest`] in the case of an offer or sent
@@ -195,6 +353,15 @@ impl Invoice {
195
353
}
196
354
197
355
impl InvoiceContents {
356
+ /// Whether the original offer or refund has expired.
357
+ #[ cfg( feature = "std" ) ]
358
+ fn is_offer_or_refund_expired ( & self ) -> bool {
359
+ match self {
360
+ InvoiceContents :: ForOffer { invoice_request, .. } => invoice_request. offer . is_expired ( ) ,
361
+ InvoiceContents :: ForRefund { refund, .. } => refund. is_expired ( ) ,
362
+ }
363
+ }
364
+
198
365
fn chain ( & self ) -> ChainHash {
199
366
match self {
200
367
InvoiceContents :: ForOffer { invoice_request, .. } => invoice_request. chain ( ) ,
@@ -208,6 +375,44 @@ impl InvoiceContents {
208
375
InvoiceContents :: ForRefund { fields, .. } => fields,
209
376
}
210
377
}
378
+
379
+ fn fields_mut ( & mut self ) -> & mut InvoiceFields {
380
+ match self {
381
+ InvoiceContents :: ForOffer { fields, .. } => fields,
382
+ InvoiceContents :: ForRefund { fields, .. } => fields,
383
+ }
384
+ }
385
+
386
+ fn as_tlv_stream ( & self ) -> PartialInvoiceTlvStreamRef {
387
+ let ( payer, offer, invoice_request) = match self {
388
+ InvoiceContents :: ForOffer { invoice_request, .. } => invoice_request. as_tlv_stream ( ) ,
389
+ InvoiceContents :: ForRefund { refund, .. } => refund. as_tlv_stream ( ) ,
390
+ } ;
391
+ let invoice = self . fields ( ) . as_tlv_stream ( ) ;
392
+
393
+ ( payer, offer, invoice_request, invoice)
394
+ }
395
+ }
396
+
397
+ impl InvoiceFields {
398
+ fn as_tlv_stream ( & self ) -> InvoiceTlvStreamRef {
399
+ let features = {
400
+ if self . features == Bolt12InvoiceFeatures :: empty ( ) { None }
401
+ else { Some ( & self . features ) }
402
+ } ;
403
+
404
+ InvoiceTlvStreamRef {
405
+ paths : Some ( Iterable ( self . payment_paths . iter ( ) . map ( |( path, _) | path) ) ) ,
406
+ blindedpay : Some ( Iterable ( self . payment_paths . iter ( ) . map ( |( _, payinfo) | payinfo) ) ) ,
407
+ created_at : Some ( self . created_at . as_secs ( ) ) ,
408
+ relative_expiry : self . relative_expiry . map ( |duration| duration. as_secs ( ) as u32 ) ,
409
+ payment_hash : Some ( & self . payment_hash ) ,
410
+ amount : Some ( self . amount_msats ) ,
411
+ fallbacks : self . fallbacks . as_ref ( ) ,
412
+ features,
413
+ node_id : Some ( & self . signing_pubkey ) ,
414
+ }
415
+ }
211
416
}
212
417
213
418
impl Writeable for Invoice {
@@ -226,8 +431,8 @@ impl TryFrom<Vec<u8>> for Invoice {
226
431
}
227
432
228
433
tlv_stream ! ( InvoiceTlvStream , InvoiceTlvStreamRef , 160 ..240 , {
229
- ( 160 , paths: ( Vec <BlindedPath >, WithoutLength ) ) ,
230
- ( 162 , blindedpay: ( Vec <BlindedPayInfo >, WithoutLength ) ) ,
434
+ ( 160 , paths: ( Vec <BlindedPath >, WithoutLength , Iterable < ' a , BlindedPathIter < ' a> , BlindedPath > ) ) ,
435
+ ( 162 , blindedpay: ( Vec <BlindedPayInfo >, WithoutLength , Iterable < ' a , BlindedPayInfoIter < ' a> , BlindedPayInfo > ) ) ,
231
436
( 164 , created_at: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
232
437
( 166 , relative_expiry: ( u32 , HighZeroBytesDroppedBigSize ) ) ,
233
438
( 168 , payment_hash: PaymentHash ) ,
@@ -237,7 +442,17 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef, 160..240, {
237
442
( 176 , node_id: PublicKey ) ,
238
443
} ) ;
239
444
240
- /// Information needed to route a payment across a [`BlindedPath`] hop.
445
+ type BlindedPathIter < ' a > = core:: iter:: Map <
446
+ core:: slice:: Iter < ' a , ( BlindedPath , BlindedPayInfo ) > ,
447
+ for <' r > fn ( & ' r ( BlindedPath , BlindedPayInfo ) ) -> & ' r BlindedPath ,
448
+ > ;
449
+
450
+ type BlindedPayInfoIter < ' a > = core:: iter:: Map <
451
+ core:: slice:: Iter < ' a , ( BlindedPath , BlindedPayInfo ) > ,
452
+ for <' r > fn ( & ' r ( BlindedPath , BlindedPayInfo ) ) -> & ' r BlindedPayInfo ,
453
+ > ;
454
+
455
+ /// Information needed to route a payment across a [`BlindedPath`].
241
456
#[ derive( Debug , PartialEq ) ]
242
457
pub struct BlindedPayInfo {
243
458
fee_base_msat : u32 ,
@@ -284,6 +499,13 @@ impl SeekReadable for FullInvoiceTlvStream {
284
499
type PartialInvoiceTlvStream =
285
500
( PayerTlvStream , OfferTlvStream , InvoiceRequestTlvStream , InvoiceTlvStream ) ;
286
501
502
+ type PartialInvoiceTlvStreamRef < ' a > = (
503
+ PayerTlvStreamRef < ' a > ,
504
+ OfferTlvStreamRef < ' a > ,
505
+ InvoiceRequestTlvStreamRef < ' a > ,
506
+ InvoiceTlvStreamRef < ' a > ,
507
+ ) ;
508
+
287
509
impl TryFrom < ParsedMessage < FullInvoiceTlvStream > > for Invoice {
288
510
type Error = ParseError ;
289
511
0 commit comments