@@ -60,13 +60,14 @@ use core::convert::TryFrom;
6060use crate :: io;
6161use crate :: ln:: PaymentHash ;
6262use crate :: ln:: features:: InvoiceRequestFeatures ;
63- use crate :: ln:: inbound_payment:: ExpandedKey ;
63+ use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
6464use crate :: ln:: msgs:: DecodeError ;
6565use crate :: offers:: invoice:: { BlindedPayInfo , InvoiceBuilder } ;
6666use crate :: offers:: merkle:: { SignError , SignatureTlvStream , SignatureTlvStreamRef , TlvStream , self } ;
6767use crate :: offers:: offer:: { Offer , OfferContents , OfferTlvStream , OfferTlvStreamRef } ;
6868use crate :: offers:: parse:: { ParseError , ParsedMessage , SemanticError } ;
6969use crate :: offers:: payer:: { PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
70+ use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey } ;
7071use crate :: onion_message:: BlindedPath ;
7172use crate :: util:: ser:: { HighZeroBytesDroppedBigSize , SeekReadable , WithoutLength , Writeable , Writer } ;
7273use crate :: util:: string:: PrintableString ;
@@ -83,6 +84,7 @@ const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "sig
8384pub struct InvoiceRequestBuilder < ' a > {
8485 offer : & ' a Offer ,
8586 invoice_request : InvoiceRequestContents ,
87+ metadata_material : Option < MetadataMaterial > ,
8688}
8789
8890impl < ' a > InvoiceRequestBuilder < ' a > {
@@ -94,9 +96,45 @@ impl<'a> InvoiceRequestBuilder<'a> {
9496 amount_msats : None , features : InvoiceRequestFeatures :: empty ( ) , quantity : None ,
9597 payer_id, payer_note : None ,
9698 } ,
99+ metadata_material : None ,
97100 }
98101 }
99102
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+
100138 /// Sets the [`InvoiceRequest::chain`] of the given [`Network`] for paying an invoice. If not
101139 /// called, [`Network::Bitcoin`] is assumed. Errors if the chain for `network` is not supported
102140 /// by the offer.
@@ -153,6 +191,21 @@ impl<'a> InvoiceRequestBuilder<'a> {
153191 }
154192 }
155193
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+
156209 let chain = self . invoice_request . chain ( ) ;
157210 if !self . offer . supports_chain ( chain) {
158211 return Err ( SemanticError :: UnsupportedChain ) ;
@@ -171,7 +224,7 @@ impl<'a> InvoiceRequestBuilder<'a> {
171224 self . invoice_request . amount_msats , self . invoice_request . quantity
172225 ) ?;
173226
174- let InvoiceRequestBuilder { offer, invoice_request } = self ;
227+ let InvoiceRequestBuilder { offer, invoice_request, .. } = self ;
175228 Ok ( UnsignedInvoiceRequest { offer, invoice_request } )
176229 }
177230}
@@ -200,7 +253,7 @@ impl<'a> InvoiceRequestBuilder<'a> {
200253 }
201254
202255 pub ( super ) fn build_unchecked ( self ) -> UnsignedInvoiceRequest < ' a > {
203- let InvoiceRequestBuilder { offer, invoice_request } = self ;
256+ let InvoiceRequestBuilder { offer, invoice_request, .. } = self ;
204257 UnsignedInvoiceRequest { offer, invoice_request }
205258 }
206259}
@@ -998,7 +1051,7 @@ mod tests {
9981051 let invoice_request = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
9991052 . amount_msats ( 1000 )
10001053 . build ( ) . unwrap ( )
1001- . request_invoice ( vec ! [ 42 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
1054+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
10021055 . build ( ) . unwrap ( )
10031056 . sign ( payer_sign) . unwrap ( ) ;
10041057
0 commit comments