@@ -60,13 +60,14 @@ use core::convert::TryFrom;
60
60
use crate :: io;
61
61
use crate :: ln:: PaymentHash ;
62
62
use crate :: ln:: features:: InvoiceRequestFeatures ;
63
- use crate :: ln:: inbound_payment:: ExpandedKey ;
63
+ use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
64
64
use crate :: ln:: msgs:: DecodeError ;
65
65
use crate :: offers:: invoice:: { BlindedPayInfo , InvoiceBuilder } ;
66
66
use crate :: offers:: merkle:: { SignError , SignatureTlvStream , SignatureTlvStreamRef , TlvStream , self } ;
67
67
use crate :: offers:: offer:: { Offer , OfferContents , OfferTlvStream , OfferTlvStreamRef } ;
68
68
use crate :: offers:: parse:: { ParseError , ParsedMessage , SemanticError } ;
69
69
use crate :: offers:: payer:: { PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
70
+ use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey } ;
70
71
use crate :: onion_message:: BlindedPath ;
71
72
use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , SeekReadable , WithoutLength , Writeable , Writer } ;
72
73
use crate :: util:: string:: PrintableString ;
@@ -83,6 +84,7 @@ const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "sig
83
84
pub struct InvoiceRequestBuilder < ' a > {
84
85
offer : & ' a Offer ,
85
86
invoice_request : InvoiceRequestContents ,
87
+ metadata_material : Option < MetadataMaterial > ,
86
88
}
87
89
88
90
impl < ' a > InvoiceRequestBuilder < ' a > {
@@ -94,9 +96,45 @@ impl<'a> InvoiceRequestBuilder<'a> {
94
96
amount_msats : None , features : InvoiceRequestFeatures :: empty ( ) , quantity : None ,
95
97
payer_id, payer_note : None ,
96
98
} ,
99
+ metadata_material : None ,
97
100
}
98
101
}
99
102
103
+ #[ allow( unused) ]
104
+ pub ( super ) fn deriving_payer_id ( offer : & ' a Offer , payer_id : DerivedPubkey ) -> Self {
105
+ let ( payer_id, metadata_material) = payer_id. into_parts ( ) ;
106
+ Self {
107
+ offer,
108
+ invoice_request : InvoiceRequestContents {
109
+ payer : PayerContents ( vec ! [ ] ) , offer : offer. contents . clone ( ) , chain : None ,
110
+ amount_msats : None , features : InvoiceRequestFeatures :: empty ( ) , quantity : None ,
111
+ payer_id, payer_note : None ,
112
+ } ,
113
+ metadata_material : Some ( metadata_material) ,
114
+ }
115
+ }
116
+
117
+ /// Sets the [`InvoiceRequest::metadata`] derived from the given `key` and any fields set prior
118
+ /// to calling [`InvoiceRequestBuilder::build`]. Allows for stateless verification of an
119
+ /// [`Invoice`] when using a public node id as the [`InvoiceRequest::payer_id`] instead of a
120
+ /// derived one.
121
+ ///
122
+ /// Errors if already called or if the builder was constructed with [`Self::deriving_payer_id`].
123
+ ///
124
+ /// [`Invoice`]: crate::offers::invoice::Invoice
125
+ #[ allow( unused) ]
126
+ pub ( crate ) fn metadata_derived (
127
+ mut self , key : & ExpandedKey , nonce : Nonce
128
+ ) -> Result < Self , SemanticError > {
129
+ if self . metadata_material . is_some ( ) {
130
+ return Err ( SemanticError :: UnexpectedMetadata ) ;
131
+ }
132
+
133
+ self . invoice_request . payer = PayerContents ( vec ! [ ] ) ;
134
+ self . metadata_material = Some ( MetadataMaterial :: new ( nonce, key) ) ;
135
+ Ok ( self )
136
+ }
137
+
100
138
/// Sets the [`InvoiceRequest::chain`] of the given [`Network`] for paying an invoice. If not
101
139
/// called, [`Network::Bitcoin`] is assumed. Errors if the chain for `network` is not supported
102
140
/// by the offer.
@@ -153,6 +191,21 @@ impl<'a> InvoiceRequestBuilder<'a> {
153
191
}
154
192
}
155
193
194
+ // Create the metadata for stateless verification of an Invoice.
195
+ if let Some ( mut metadata_material) = self . metadata_material {
196
+ debug_assert ! ( self . invoice_request. payer. 0 . is_empty( ) ) ;
197
+ let mut tlv_stream = self . invoice_request . as_tlv_stream ( ) ;
198
+ tlv_stream. 0 . metadata = None ;
199
+ tlv_stream. 2 . payer_id = None ;
200
+ tlv_stream. write ( & mut metadata_material) . unwrap ( ) ;
201
+
202
+ self . invoice_request . payer . 0 = metadata_material. into_metadata ( ) ;
203
+ }
204
+
205
+ if self . invoice_request . payer . 0 . is_empty ( ) {
206
+ return Err ( SemanticError :: MissingPayerMetadata ) ;
207
+ }
208
+
156
209
let chain = self . invoice_request . chain ( ) ;
157
210
if !self . offer . supports_chain ( chain) {
158
211
return Err ( SemanticError :: UnsupportedChain ) ;
@@ -171,7 +224,7 @@ impl<'a> InvoiceRequestBuilder<'a> {
171
224
self . invoice_request . amount_msats , self . invoice_request . quantity
172
225
) ?;
173
226
174
- let InvoiceRequestBuilder { offer, invoice_request } = self ;
227
+ let InvoiceRequestBuilder { offer, invoice_request, .. } = self ;
175
228
Ok ( UnsignedInvoiceRequest { offer, invoice_request } )
176
229
}
177
230
}
@@ -200,7 +253,7 @@ impl<'a> InvoiceRequestBuilder<'a> {
200
253
}
201
254
202
255
pub ( super ) fn build_unchecked ( self ) -> UnsignedInvoiceRequest < ' a > {
203
- let InvoiceRequestBuilder { offer, invoice_request } = self ;
256
+ let InvoiceRequestBuilder { offer, invoice_request, .. } = self ;
204
257
UnsignedInvoiceRequest { offer, invoice_request }
205
258
}
206
259
}
@@ -998,7 +1051,7 @@ mod tests {
998
1051
let invoice_request = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
999
1052
. amount_msats ( 1000 )
1000
1053
. build ( ) . unwrap ( )
1001
- . request_invoice ( vec ! [ 42 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
1054
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
1002
1055
. build ( ) . unwrap ( )
1003
1056
. sign ( payer_sign) . unwrap ( ) ;
1004
1057
0 commit comments