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