@@ -15,6 +15,7 @@ use bitcoin::bech32::FromBase32;
15
15
use bitcoin:: hash_types:: BlockHash ;
16
16
use bitcoin:: secp256k1:: { PublicKey , XOnlyPublicKey } ;
17
17
use bitcoin:: secp256k1:: schnorr:: Signature ;
18
+ use core:: convert:: TryFrom ;
18
19
use core:: str:: FromStr ;
19
20
use core:: time:: Duration ;
20
21
use ln:: PaymentHash ;
@@ -188,6 +189,25 @@ pub enum ParseError {
188
189
Bech32 ( bech32:: Error ) ,
189
190
/// The bech32 decoded string could not be decoded as the expected message type.
190
191
Decode ( DecodeError ) ,
192
+ /// The parsed message has invalid semantics.
193
+ InvalidSemantics ( SemanticError ) ,
194
+ }
195
+
196
+ #[ derive( Debug , PartialEq ) ]
197
+ ///
198
+ pub enum SemanticError {
199
+ ///
200
+ MissingDescription ,
201
+ ///
202
+ MissingDestination ,
203
+ ///
204
+ DuplicateDestination ,
205
+ ///
206
+ MissingPaths ,
207
+ ///
208
+ InvalidQuantity ,
209
+ ///
210
+ UnexpectedRefund ,
191
211
}
192
212
193
213
impl From < bech32:: Error > for ParseError {
@@ -202,6 +222,69 @@ impl From<DecodeError> for ParseError {
202
222
}
203
223
}
204
224
225
+ impl From < SemanticError > for ParseError {
226
+ fn from ( error : SemanticError ) -> Self {
227
+ Self :: InvalidSemantics ( error)
228
+ }
229
+ }
230
+
231
+ impl FromStr for Offer {
232
+ type Err = ParseError ;
233
+
234
+ fn from_str ( s : & str ) -> Result < Self , <Self as FromStr >:: Err > {
235
+ let tlv_stream = OfferTlvStream :: from_str ( s) ?;
236
+ Ok ( Offer :: try_from ( tlv_stream) ?)
237
+ }
238
+ }
239
+
240
+ impl TryFrom < OfferTlvStream > for Offer {
241
+ type Error = SemanticError ;
242
+
243
+ fn try_from ( tlv_stream : OfferTlvStream ) -> Result < Self , Self :: Error > {
244
+ if tlv_stream. description . is_none ( ) {
245
+ return Err ( SemanticError :: MissingDescription ) ;
246
+ }
247
+
248
+ if tlv_stream. node_id . is_none ( ) && tlv_stream. paths . is_none ( ) {
249
+ return Err ( SemanticError :: MissingDestination ) ;
250
+ }
251
+
252
+ if tlv_stream. node_id . is_some ( ) && tlv_stream. paths . is_some ( ) {
253
+ return Err ( SemanticError :: DuplicateDestination ) ;
254
+ }
255
+
256
+ if let Some ( WithoutLength ( ref paths) ) = tlv_stream. paths {
257
+ if paths. is_empty ( ) {
258
+ return Err ( SemanticError :: MissingPaths ) ;
259
+ }
260
+ }
261
+
262
+ if let Some ( HighZeroBytesDroppedVarInt ( quantity_min) ) = tlv_stream. quantity_min {
263
+ if quantity_min < 1 {
264
+ return Err ( SemanticError :: InvalidQuantity ) ;
265
+ }
266
+
267
+ if let Some ( HighZeroBytesDroppedVarInt ( quantity_max) ) = tlv_stream. quantity_max {
268
+ if quantity_min > quantity_max {
269
+ return Err ( SemanticError :: InvalidQuantity ) ;
270
+ }
271
+ }
272
+ }
273
+
274
+ if let Some ( HighZeroBytesDroppedVarInt ( quantity_max) ) = tlv_stream. quantity_max {
275
+ if quantity_max < 1 {
276
+ return Err ( SemanticError :: InvalidQuantity ) ;
277
+ }
278
+ }
279
+
280
+ if tlv_stream. refund_for . is_some ( ) && tlv_stream. send_invoice . is_none ( ) {
281
+ return Err ( SemanticError :: UnexpectedRefund ) ;
282
+ }
283
+
284
+ Ok ( Offer { tlv_stream } )
285
+ }
286
+ }
287
+
205
288
const OFFER_BECH32_HRP : & str = "lno" ;
206
289
207
290
impl FromStr for OfferTlvStream {
0 commit comments