@@ -80,12 +80,14 @@ use core::time::Duration;
80
80
use crate :: io;
81
81
use crate :: ln:: PaymentHash ;
82
82
use crate :: ln:: features:: InvoiceRequestFeatures ;
83
+ use crate :: ln:: inbound_payment:: { ExpandedKey , Nonce } ;
83
84
use crate :: ln:: msgs:: { DecodeError , MAX_VALUE_MSAT } ;
84
85
use crate :: offers:: invoice:: { BlindedPayInfo , InvoiceBuilder } ;
85
86
use crate :: offers:: invoice_request:: { InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef } ;
86
87
use crate :: offers:: offer:: { OfferTlvStream , OfferTlvStreamRef } ;
87
88
use crate :: offers:: parse:: { Bech32Encode , ParseError , ParsedMessage , SemanticError } ;
88
89
use crate :: offers:: payer:: { PayerContents , PayerTlvStream , PayerTlvStreamRef } ;
90
+ use crate :: offers:: signer:: { MetadataMaterial , DerivedPubkey } ;
89
91
use crate :: onion_message:: BlindedPath ;
90
92
use crate :: util:: ser:: { SeekReadable , WithoutLength , Writeable , Writer } ;
91
93
use crate :: util:: string:: PrintableString ;
@@ -102,6 +104,7 @@ use std::time::SystemTime;
102
104
/// [module-level documentation]: self
103
105
pub struct RefundBuilder {
104
106
refund : RefundContents ,
107
+ metadata_material : Option < MetadataMaterial > ,
105
108
}
106
109
107
110
impl RefundBuilder {
@@ -123,7 +126,53 @@ impl RefundBuilder {
123
126
quantity : None , payer_id, payer_note : None ,
124
127
} ;
125
128
126
- Ok ( RefundBuilder { refund } )
129
+ Ok ( RefundBuilder { refund, metadata_material : None } )
130
+ }
131
+
132
+ /// Similar to [`RefundBuilder::new`] except it:
133
+ /// - derives the payer id such that a different key can be used for each refund, and
134
+ /// - sets the metadata when [`RefundBuilder::build`] is called such that it can be used by
135
+ /// [`Invoice::verify`] to determine if the refund was produced using a base [`ExpandedKey`]
136
+ /// from which the payer id was derived.
137
+ ///
138
+ /// [`Invoice::verify`]: crate::offers::invoice::Invoice::verify
139
+ /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
140
+ #[ allow( unused) ]
141
+ pub ( crate ) fn deriving_payer_id (
142
+ description : String , payer_id : DerivedPubkey , amount_msats : u64
143
+ ) -> Result < Self , SemanticError > {
144
+ if amount_msats > MAX_VALUE_MSAT {
145
+ return Err ( SemanticError :: InvalidAmount ) ;
146
+ }
147
+
148
+ let ( payer_id, metadata_material) = payer_id. into_parts ( ) ;
149
+ let refund = RefundContents {
150
+ payer : PayerContents ( vec ! [ ] ) , description, absolute_expiry : None , issuer : None ,
151
+ paths : None , chain : None , amount_msats, features : InvoiceRequestFeatures :: empty ( ) ,
152
+ quantity : None , payer_id, payer_note : None ,
153
+ } ;
154
+
155
+ Ok ( RefundBuilder { refund, metadata_material : Some ( metadata_material) } )
156
+ }
157
+
158
+ /// Sets the [`Refund::metadata`] derived from the given `key` and any fields set prior to
159
+ /// calling [`Refund::build`]. Allows for stateless verification of an [`Invoice`] when using a
160
+ /// public node id as the [`Refund::payer_id`] instead of a derived one.
161
+ ///
162
+ /// Errors if already called or if the builder was constructed with [`Self::deriving_payer_id`].
163
+ ///
164
+ /// [`Invoice`]: crate::offers::invoice::Invoice
165
+ #[ allow( unused) ]
166
+ pub ( crate ) fn metadata_derived (
167
+ mut self , key : & ExpandedKey , nonce : Nonce
168
+ ) -> Result < Self , SemanticError > {
169
+ if self . metadata_material . is_some ( ) {
170
+ return Err ( SemanticError :: UnexpectedMetadata ) ;
171
+ }
172
+
173
+ self . refund . payer = PayerContents ( vec ! [ ] ) ;
174
+ self . metadata_material = Some ( MetadataMaterial :: new ( nonce, key) ) ;
175
+ Ok ( self )
127
176
}
128
177
129
178
/// Sets the [`Refund::absolute_expiry`] as seconds since the Unix epoch. Any expiry that has
@@ -190,6 +239,17 @@ impl RefundBuilder {
190
239
self . refund . chain = None ;
191
240
}
192
241
242
+ // Create the metadata for stateless verification of an Invoice.
243
+ if let Some ( mut metadata_material) = self . metadata_material {
244
+ debug_assert ! ( self . refund. payer. 0 . is_empty( ) ) ;
245
+ let mut tlv_stream = self . refund . as_tlv_stream ( ) ;
246
+ tlv_stream. 0 . metadata = None ;
247
+ tlv_stream. 2 . payer_id = None ;
248
+ tlv_stream. write ( & mut metadata_material) . unwrap ( ) ;
249
+
250
+ self . refund . payer . 0 = metadata_material. into_metadata ( ) ;
251
+ }
252
+
193
253
let mut bytes = Vec :: new ( ) ;
194
254
self . refund . write ( & mut bytes) . unwrap ( ) ;
195
255
0 commit comments