@@ -11,6 +11,7 @@ module Functora.Bolt11
11
11
inspectFeature ,
12
12
inspectFeatures ,
13
13
RequiredOrSupported (.. ),
14
+ Route (.. ),
14
15
isPaymentHash ,
15
16
Network (.. ),
16
17
Multiplier (.. ),
@@ -19,6 +20,7 @@ module Functora.Bolt11
19
20
Bolt11Hrp (.. ),
20
21
Bolt11Sig (.. ),
21
22
Bolt11 (.. ),
23
+ inspectW5 ,
22
24
decodeBolt11 ,
23
25
)
24
26
where
@@ -30,7 +32,9 @@ import qualified Bitcoin.Address.Settings as Btc
30
32
import Codec.Binary.Bech32 (Word5 )
31
33
import qualified Codec.Binary.Bech32.Internal as Bech32
32
34
import Control.Applicative
35
+ import qualified Data.Aeson as A
33
36
import qualified Data.Attoparsec.Text as Atto
37
+ import qualified Data.Binary.Get as BG
34
38
import Data.Bits (shiftL , (.&.) , (.|.) )
35
39
import qualified Data.ByteString as BS
36
40
import qualified Data.ByteString.Base16 as B16
@@ -86,7 +90,7 @@ data Tag
86
90
| Expiry Int
87
91
| MinFinalCltvExpiry Int
88
92
| OnchainFallback Btc. Address
89
- | ExtraRouteInfo
93
+ | ExtraRouteInfo [ Route ]
90
94
| Features [Feature ]
91
95
| UnknownTag Int [Word5 ]
92
96
| UnparsedTag Int [Word5 ] String
@@ -195,6 +199,59 @@ data RequiredOrSupported
195
199
| Supported
196
200
deriving stock (Eq , Ord , Show , Data , Generic )
197
201
202
+ data Route = Route
203
+ { routePubKey :: Hex ,
204
+ routeShortChanId :: Hex ,
205
+ routeFeeBaseMsat :: Word32 ,
206
+ routeFeePropMillionth :: Word32 ,
207
+ routeCltvExpiryDelta :: Word16
208
+ }
209
+ deriving stock (Eq , Ord , Show , Data , Generic )
210
+
211
+ instance A. ToJSON Route where
212
+ toJSON x =
213
+ A. object
214
+ [ " pubkey" A. .= inspectHex @ Text (routePubKey x),
215
+ " short_channel_id" A. .= inspectHex @ Text (routeShortChanId x),
216
+ " fee_base_msat" A. .= routeFeeBaseMsat x,
217
+ " fee_proportional_millionths" A. .= routeFeePropMillionth x,
218
+ " cltv_expiry_delta" A. .= routeCltvExpiryDelta x
219
+ ]
220
+ toEncoding x =
221
+ A. pairs
222
+ $ " pubkey"
223
+ A. .= inspectHex @ Text (routePubKey x)
224
+ <> " short_channel_id"
225
+ A. .= inspectHex @ Text (routeShortChanId x)
226
+ <> " fee_base_msat"
227
+ A. .= routeFeeBaseMsat x
228
+ <> " fee_proportional_millionths"
229
+ A. .= routeFeePropMillionth x
230
+ <> " cltv_expiry_delta"
231
+ A. .= routeCltvExpiryDelta x
232
+
233
+ parseRoutes :: ByteString -> Either String [Route ]
234
+ parseRoutes =
235
+ bimap thd3 thd3
236
+ . BG. runGetOrFail
237
+ ( many
238
+ $ do
239
+ pub <- BG. getByteString 33
240
+ chan <- BG. getByteString 8
241
+ base <- BG. getWord32be
242
+ prop <- BG. getWord32be
243
+ cltv <- BG. getWord16be
244
+ pure
245
+ Route
246
+ { routePubKey = Hex pub,
247
+ routeShortChanId = Hex chan,
248
+ routeFeeBaseMsat = base,
249
+ routeFeePropMillionth = prop,
250
+ routeCltvExpiryDelta = cltv
251
+ }
252
+ )
253
+ . BL. fromStrict
254
+
198
255
isPaymentHash :: Tag -> Bool
199
256
isPaymentHash PaymentHash {} = True
200
257
isPaymentHash _ = False
@@ -382,6 +439,13 @@ w5addr net (v0 : rest) =
382
439
where
383
440
cfg = networkSettings net
384
441
442
+ inspectW5 :: forall a . (From Text a ) => [Word5 ] -> a
443
+ inspectW5 ws =
444
+ from @ Text @ a
445
+ . either (const . inspect $ fmap fromEnum ws) id
446
+ $ w5txt ws
447
+ <|> fmap inspect (w5bs ws)
448
+
385
449
parseTag :: Network -> [Word5 ] -> (Maybe Tag , [Word5 ])
386
450
parseTag _ [] = (Nothing , [] )
387
451
parseTag _ ws@ [_] = (Nothing , ws)
@@ -410,7 +474,7 @@ parseTag net ws@(t0 : d1 : d2 : rest)
410
474
6 -> Expiry $ w5int dat
411
475
24 -> MinFinalCltvExpiry $ w5int dat
412
476
9 -> mkt OnchainFallback $ w5addr net dat
413
- 3 -> ExtraRouteInfo
477
+ 3 -> mkt ExtraRouteInfo $ parseRoutes =<< w5bs dat
414
478
5 -> Features $ parseFeatures dat
415
479
n -> UnknownTag n dat
416
480
0 commit comments