@@ -24,9 +24,10 @@ use core::time::Duration;
24
24
use ln:: PaymentHash ;
25
25
use ln:: features:: OfferFeatures ;
26
26
use ln:: msgs:: DecodeError ;
27
- use util:: ser:: { HighZeroBytesDroppedVarInt , Readable , WithLength , WithoutLength } ;
27
+ use util:: ser:: { HighZeroBytesDroppedVarInt , Readable , WithLength , WithoutLength , Writeable } ;
28
28
29
29
use prelude:: * ;
30
+ use io;
30
31
31
32
#[ cfg( feature = "std" ) ]
32
33
use std:: time:: SystemTime ;
@@ -123,7 +124,9 @@ impl Offer {
123
124
self . signature . as_ref ( )
124
125
}
125
126
126
- fn to_tlv_stream ( & self ) -> OfferTlvStream {
127
+ fn write ( & self , writer : & mut Vec < u8 > ) -> Result < ( ) , io:: Error > {
128
+ let chains = self . chains . clone ( ) . map ( |chains| WithoutLength ( chains) ) ;
129
+
127
130
let ( currency, amount) = match self . amount . clone ( ) {
128
131
None => ( None , None ) ,
129
132
Some ( Amount :: Bitcoin { amount_msats } ) => (
@@ -134,33 +137,43 @@ impl Offer {
134
137
) ,
135
138
} ;
136
139
140
+ let description = Some ( WithoutLength ( self . description . clone ( ) ) ) ;
141
+ let features = self . features . as_ref ( ) ;
142
+ let absolute_expiry = self . absolute_expiry . map ( |d| HighZeroBytesDroppedVarInt ( d. as_secs ( ) ) ) ;
143
+
137
144
let ( paths, node_id) = match self . destination . clone ( ) {
138
145
Destination :: NodeId ( node_id) => ( None , Some ( node_id) ) ,
139
146
Destination :: Paths ( paths) => ( Some ( WithoutLength ( paths) ) , None ) ,
140
147
} ;
141
148
142
- let ( send_invoice, refund_for) = match self . send_invoice . clone ( ) {
149
+ let issuer = self . issuer . clone ( ) . map ( |issuer| WithoutLength ( issuer) ) ;
150
+ let quantity_min = self . quantity_min . map ( |quantity| HighZeroBytesDroppedVarInt ( quantity) ) ;
151
+ let quantity_max = self . quantity_max . map ( |quantity| HighZeroBytesDroppedVarInt ( quantity) ) ;
152
+ let recurrence = self . recurrence . as_ref ( ) ;
153
+
154
+ let ( send_invoice, refund_for) = match self . send_invoice {
143
155
None => ( None , None ) ,
144
156
Some ( SendInvoice { refund_for } ) => ( Some ( ( ) ) , refund_for) ,
145
157
} ;
146
158
147
- OfferTlvStream {
148
- chains : self . chains . clone ( ) . map ( |chains| WithoutLength ( chains) ) ,
149
- currency,
150
- amount,
151
- description : Some ( WithoutLength ( self . description . clone ( ) ) ) ,
152
- features : self . features . clone ( ) ,
153
- absolute_expiry : self . absolute_expiry . map ( |d| HighZeroBytesDroppedVarInt ( d. as_secs ( ) ) ) ,
154
- paths,
155
- issuer : self . issuer . clone ( ) . map ( |issuer| WithoutLength ( issuer) ) ,
156
- quantity_min : self . quantity_min . map ( |quantity| HighZeroBytesDroppedVarInt ( quantity) ) ,
157
- quantity_max : self . quantity_max . map ( |quantity| HighZeroBytesDroppedVarInt ( quantity) ) ,
158
- recurrence : self . recurrence . clone ( ) ,
159
- node_id,
160
- send_invoice,
161
- refund_for,
162
- signature : self . signature ,
163
- }
159
+ encode_tlv_stream ! ( writer, {
160
+ ( 2 , chains, option) ,
161
+ ( 6 , currency, option) ,
162
+ ( 8 , amount, option) ,
163
+ ( 10 , description, option) ,
164
+ ( 12 , self . features, option) ,
165
+ ( 14 , absolute_expiry, option) ,
166
+ ( 16 , paths, option) ,
167
+ ( 20 , issuer, option) ,
168
+ ( 22 , quantity_min, option) ,
169
+ ( 24 , quantity_max, option) ,
170
+ ( 26 , recurrence, option) ,
171
+ ( 30 , node_id, option) ,
172
+ ( 34 , refund_for, option) ,
173
+ ( 54 , send_invoice, option) ,
174
+ ( 240 , self . signature, option) ,
175
+ } ) ;
176
+ Ok ( ( ) )
164
177
}
165
178
}
166
179
@@ -208,45 +221,6 @@ pub struct SendInvoice {
208
221
refund_for : Option < PaymentHash > ,
209
222
}
210
223
211
- /// An `offer` TLV stream without any semantic checks, apart from any checks performed when parsing
212
- /// the underlying types.
213
- #[ derive( Debug ) ]
214
- struct OfferTlvStream {
215
- chains : Option < WithoutLength < Vec < BlockHash > > > ,
216
- currency : Option < WithoutLength < String > > ,
217
- amount : Option < HighZeroBytesDroppedVarInt < u64 > > ,
218
- description : Option < WithoutLength < String > > ,
219
- features : Option < OfferFeatures > ,
220
- absolute_expiry : Option < HighZeroBytesDroppedVarInt < u64 > > ,
221
- paths : Option < WithoutLength < Vec < BlindedPath > > > ,
222
- issuer : Option < WithoutLength < String > > ,
223
- quantity_min : Option < HighZeroBytesDroppedVarInt < u64 > > ,
224
- quantity_max : Option < HighZeroBytesDroppedVarInt < u64 > > ,
225
- recurrence : Option < Recurrence > ,
226
- node_id : Option < XOnlyPublicKey > ,
227
- send_invoice : Option < ( ) > ,
228
- refund_for : Option < PaymentHash > ,
229
- signature : Option < Signature > ,
230
- }
231
-
232
- impl_writeable_tlv_stream ! ( OfferTlvStream , {
233
- ( 2 , chains, option) ,
234
- ( 6 , currency, option) ,
235
- ( 8 , amount, option) ,
236
- ( 10 , description, option) ,
237
- ( 12 , features, option) ,
238
- ( 14 , absolute_expiry, option) ,
239
- ( 16 , paths, option) ,
240
- ( 20 , issuer, option) ,
241
- ( 22 , quantity_min, option) ,
242
- ( 24 , quantity_max, option) ,
243
- ( 26 , recurrence, option) ,
244
- ( 30 , node_id, option) ,
245
- ( 34 , refund_for, option) ,
246
- ( 54 , send_invoice, option) ,
247
- ( 240 , signature, option) ,
248
- } ) ;
249
-
250
224
#[ derive( Clone , Debug ) ]
251
225
///
252
226
pub struct BlindedPath {
@@ -273,9 +247,8 @@ struct Recurrence {
273
247
274
248
impl_writeable ! ( Recurrence , { time_unit, period } ) ;
275
249
276
- /// An offer parsed from a bech32-encoded string as a TLV stream and the corresponding bytes. The
277
- /// latter is used for signature verification.
278
- struct ParsedOffer ( OfferTlvStream , Vec < u8 > ) ;
250
+ /// An offer parsed from a bech32-encoded string as serialized bytes.
251
+ struct ParsedOffer ( Vec < u8 > ) ;
279
252
280
253
/// Error when parsing a bech32 encoded message using [`str::parse`].
281
254
#[ derive( Debug , PartialEq ) ]
@@ -337,9 +310,9 @@ impl From<SemanticError> for ParseError {
337
310
}
338
311
}
339
312
340
- impl From < secp256k1:: Error > for SemanticError {
313
+ impl From < secp256k1:: Error > for ParseError {
341
314
fn from ( error : secp256k1:: Error ) -> Self {
342
- Self :: InvalidSignature ( error)
315
+ SemanticError :: InvalidSignature ( error) . into ( )
343
316
}
344
317
}
345
318
@@ -352,13 +325,41 @@ impl FromStr for Offer {
352
325
}
353
326
354
327
impl TryFrom < ParsedOffer > for Offer {
355
- type Error = SemanticError ;
328
+ type Error = ParseError ;
356
329
357
330
fn try_from ( offer : ParsedOffer ) -> Result < Self , Self :: Error > {
358
- let ParsedOffer ( OfferTlvStream {
359
- chains, currency, amount, description, features, absolute_expiry, paths, issuer,
360
- quantity_min, quantity_max, recurrence, node_id, send_invoice, refund_for, signature,
361
- } , data) = offer;
331
+ let mut chains: Option < WithoutLength < Vec < BlockHash > > > = None ;
332
+ let mut currency: Option < WithoutLength < String > > = None ;
333
+ let mut amount = None ;
334
+ let mut description: Option < WithoutLength < String > > = None ;
335
+ let mut features = None ;
336
+ let mut absolute_expiry: Option < HighZeroBytesDroppedVarInt < u64 > > = None ;
337
+ let mut paths: Option < WithoutLength < Vec < BlindedPath > > > = None ;
338
+ let mut issuer: Option < WithoutLength < String > > = None ;
339
+ let mut quantity_min = None ;
340
+ let mut quantity_max = None ;
341
+ let mut recurrence = None ;
342
+ let mut node_id = None ;
343
+ let mut send_invoice = None ;
344
+ let mut refund_for = None ;
345
+ let mut signature = None ;
346
+ decode_tlv_stream ! ( & mut & offer. 0 [ ..] , {
347
+ ( 2 , chains, option) ,
348
+ ( 6 , currency, option) ,
349
+ ( 8 , amount, option) ,
350
+ ( 10 , description, option) ,
351
+ ( 12 , features, option) ,
352
+ ( 14 , absolute_expiry, option) ,
353
+ ( 16 , paths, option) ,
354
+ ( 20 , issuer, option) ,
355
+ ( 22 , quantity_min, option) ,
356
+ ( 24 , quantity_max, option) ,
357
+ ( 26 , recurrence, option) ,
358
+ ( 30 , node_id, option) ,
359
+ ( 34 , refund_for, option) ,
360
+ ( 54 , send_invoice, option) ,
361
+ ( 240 , signature, option) ,
362
+ } ) ;
362
363
363
364
let supported_chains = [
364
365
genesis_block ( Network :: Bitcoin ) . block_hash ( ) ,
@@ -371,59 +372,59 @@ impl TryFrom<ParsedOffer> for Offer {
371
372
Some ( WithoutLength ( chains) ) => match chains. first ( ) {
372
373
None => Some ( chains) ,
373
374
Some ( chain) if supported_chains. contains ( chain) => Some ( chains) ,
374
- _ => return Err ( SemanticError :: UnsupportedChain ) ,
375
+ _ => return Err ( SemanticError :: UnsupportedChain . into ( ) ) ,
375
376
} ,
376
377
} ;
377
378
378
379
let amount = match ( currency, amount) {
379
380
( None , None ) => None ,
380
381
( None , Some ( amount_msats) ) => Some ( Amount :: Bitcoin { amount_msats : amount_msats. 0 } ) ,
381
- ( Some ( _) , None ) => return Err ( SemanticError :: UnexpectedCurrency ) ,
382
+ ( Some ( _) , None ) => return Err ( SemanticError :: UnexpectedCurrency . into ( ) ) ,
382
383
( Some ( WithoutLength ( iso4217_code) ) , Some ( HighZeroBytesDroppedVarInt ( amount) ) ) => {
383
384
if iso4217_code. len ( ) != ISO4217_CODE_LEN {
384
- return Err ( SemanticError :: InvalidCurrencyEncoding ) ;
385
+ return Err ( SemanticError :: InvalidCurrencyEncoding . into ( ) ) ;
385
386
}
386
387
Some ( Amount :: Currency { iso4217_code, amount } )
387
388
} ,
388
389
} ;
389
390
390
391
if description. is_none ( ) {
391
- return Err ( SemanticError :: MissingDescription ) ;
392
+ return Err ( SemanticError :: MissingDescription . into ( ) ) ;
392
393
}
393
394
394
395
let destination = match ( node_id, paths) {
395
- ( None , None ) => return Err ( SemanticError :: MissingDestination ) ,
396
- ( Some ( _) , Some ( _) ) => return Err ( SemanticError :: DuplicateDestination ) ,
396
+ ( None , None ) => return Err ( SemanticError :: MissingDestination . into ( ) ) ,
397
+ ( Some ( _) , Some ( _) ) => return Err ( SemanticError :: DuplicateDestination . into ( ) ) ,
397
398
( Some ( node_id) , None ) => Destination :: NodeId ( node_id) ,
398
- ( None , Some ( paths) ) if paths. 0 . is_empty ( ) => return Err ( SemanticError :: MissingPaths ) ,
399
+ ( None , Some ( paths) ) if paths. 0 . is_empty ( ) => return Err ( SemanticError :: MissingPaths . into ( ) ) ,
399
400
( None , Some ( WithoutLength ( paths) ) ) => Destination :: Paths ( paths) ,
400
401
} ;
401
402
402
403
if let Some ( HighZeroBytesDroppedVarInt ( quantity_min) ) = quantity_min {
403
404
if quantity_min < 1 {
404
- return Err ( SemanticError :: InvalidQuantity ) ;
405
+ return Err ( SemanticError :: InvalidQuantity . into ( ) ) ;
405
406
}
406
407
407
408
if let Some ( HighZeroBytesDroppedVarInt ( quantity_max) ) = quantity_max {
408
409
if quantity_min > quantity_max {
409
- return Err ( SemanticError :: InvalidQuantity ) ;
410
+ return Err ( SemanticError :: InvalidQuantity . into ( ) ) ;
410
411
}
411
412
}
412
413
}
413
414
414
415
if let Some ( HighZeroBytesDroppedVarInt ( quantity_max) ) = quantity_max {
415
416
if quantity_max < 1 {
416
- return Err ( SemanticError :: InvalidQuantity ) ;
417
+ return Err ( SemanticError :: InvalidQuantity . into ( ) ) ;
417
418
}
418
419
}
419
420
420
421
let send_invoice = match ( send_invoice, refund_for) {
421
422
( None , None ) => None ,
422
- ( None , Some ( _) ) => return Err ( SemanticError :: UnexpectedRefund ) ,
423
+ ( None , Some ( _) ) => return Err ( SemanticError :: UnexpectedRefund . into ( ) ) ,
423
424
( Some ( _) , _) => Some ( SendInvoice { refund_for } ) ,
424
425
} ;
425
426
426
- let id = merkle:: root_hash ( & data ) ;
427
+ let id = merkle:: root_hash ( & offer . 0 ) ;
427
428
if let Some ( signature) = & signature {
428
429
let tag = sha256:: Hash :: hash ( concat ! ( "lightning" , "offer" , "signature" ) . as_bytes ( ) ) ;
429
430
let message = Message :: from_slice ( & merkle:: tagged_hash ( tag, id) ) . unwrap ( ) ;
@@ -472,26 +473,18 @@ impl FromStr for ParsedOffer {
472
473
}
473
474
474
475
let data = Vec :: < u8 > :: from_base32 ( & data) ?;
475
- Ok ( ParsedOffer ( Readable :: read ( & mut & data [ .. ] ) ? , data) )
476
+ Ok ( ParsedOffer ( data) )
476
477
}
477
478
}
478
479
479
480
impl core:: fmt:: Display for Offer {
480
481
fn fmt ( & self , f : & mut core:: fmt:: Formatter ) -> Result < ( ) , core:: fmt:: Error > {
481
- self . to_tlv_stream ( ) . fmt ( f)
482
- }
483
- }
484
-
485
- impl core:: fmt:: Display for OfferTlvStream {
486
- fn fmt ( & self , f : & mut core:: fmt:: Formatter ) -> Result < ( ) , core:: fmt:: Error > {
487
- use util:: ser:: Writeable ;
488
482
let mut buffer = Vec :: new ( ) ;
489
483
self . write ( & mut buffer) . unwrap ( ) ;
490
484
491
485
use bitcoin:: bech32:: ToBase32 ;
492
486
let data = buffer. to_base32 ( ) ;
493
487
bech32:: encode_without_checksum_to_fmt ( f, OFFER_BECH32_HRP , data) . expect ( "HRP is valid" ) . unwrap ( ) ;
494
-
495
488
Ok ( ( ) )
496
489
}
497
490
}
0 commit comments