Skip to content

Commit ccedf0a

Browse files
committed
wip
1 parent 93eec1c commit ccedf0a

File tree

1 file changed

+51
-36
lines changed

1 file changed

+51
-36
lines changed

pub/functora/src/bolt11/Functora/Bolt11.hs

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ where
2020
import Codec.Binary.Bech32 (Word5)
2121
import qualified Codec.Binary.Bech32.Internal as Bech32
2222
import Control.Applicative
23-
import Data.Attoparsec.Text
23+
import qualified Data.Attoparsec.Text as Atto
2424
import Data.Bits (shiftL, (.|.))
2525
import qualified Data.ByteString as BS
2626
import qualified Data.ByteString.Base16 as B16
@@ -29,7 +29,7 @@ import qualified Data.ByteString.Lazy.Char8 as BL
2929
import qualified Data.Text as T
3030
import qualified Data.Text.Encoding as T
3131
import Functora.Prelude hiding (error)
32-
import Prelude (Show (..), error, splitAt, take)
32+
import Prelude (Show (..), error)
3333

3434
newtype Hex = Hex
3535
{ unHex :: ByteString
@@ -70,13 +70,16 @@ data Tag
7070
= PaymentHash Hex
7171
| PaymentSecret Hex
7272
| Description Text
73+
| AdditionalMetadata [Word5]
7374
| PayeePubkey Hex
7475
| DescriptionHash Hex
7576
| Expiry Int
7677
| MinFinalCltvExpiry Int
7778
| OnchainFallback -- TODO: address type
7879
| ExtraRouteInfo
7980
| FeatureBits [Word5]
81+
| UnknownTag Int [Word5]
82+
| UnparsedTag Int [Word5] String
8083
deriving stock (Eq, Ord, Show, Generic)
8184

8285
isPaymentHash :: Tag -> Bool
@@ -167,75 +170,87 @@ data Bolt11 = Bolt11
167170
}
168171
deriving stock (Eq, Ord, Show, Generic)
169172

170-
parseNetwork :: Parser Network
173+
parseNetwork :: Atto.Parser Network
171174
parseNetwork =
172-
(string "bcrt" *> pure BitcoinRegtest)
173-
<|> (string "bc" *> pure BitcoinMainnet)
174-
<|> (string "tbs" *> pure BitcoinSignet)
175-
<|> (string "tb" *> pure BitcoinTestnet)
175+
(Atto.string "bcrt" *> pure BitcoinRegtest)
176+
<|> (Atto.string "bc" *> pure BitcoinMainnet)
177+
<|> (Atto.string "tbs" *> pure BitcoinSignet)
178+
<|> (Atto.string "tb" *> pure BitcoinTestnet)
176179

177-
parseMultiplier :: Parser Multiplier
180+
parseMultiplier :: Atto.Parser Multiplier
178181
parseMultiplier = do
179-
c <- satisfy (`elem` ("munp" :: String))
182+
c <- Atto.satisfy (`elem` ("munp" :: String))
180183
case c of
181184
'm' -> pure Milli
182185
'u' -> pure Micro
183186
'n' -> pure Nano
184187
'p' -> pure Pico
185188
_ -> fail "unhandled case in parseMultiplier"
186189

187-
parseHrpAmount :: Parser Bolt11HrpAmt
190+
parseHrpAmount :: Atto.Parser Bolt11HrpAmt
188191
parseHrpAmount = do
189-
amt <- decimal
192+
amt <- Atto.decimal
190193
mul <- parseMultiplier
191194
pure $ Bolt11HrpAmt amt mul
192195

193-
parseHrp :: Parser Bolt11Hrp
196+
parseHrp :: Atto.Parser Bolt11Hrp
194197
parseHrp = do
195-
_ <- char 'l'
196-
_ <- char 'n'
198+
_ <- Atto.char 'l'
199+
_ <- Atto.char 'n'
197200
net <- parseNetwork
198201
amt <- optional parseHrpAmount
199202
pure (Bolt11Hrp net amt)
200203

204+
w5bs :: [Word5] -> Either String ByteString
205+
w5bs =
206+
maybe (Left "Non-Base256 bits") (Right . BS.pack)
207+
. Bech32.toBase256
208+
209+
w5hex :: [Word5] -> Either String Hex
210+
w5hex =
211+
second Hex . w5bs
212+
213+
w5txt :: [Word5] -> Either String Text
214+
w5txt =
215+
first displayException . decodeUtf8' <=< w5bs
216+
201217
w5int :: [Word5] -> Int
202-
w5int bytes = foldl' decodeInt 0 (zip [0 ..] (Prelude.take 7 (reverse bytes)))
218+
w5int bytes = foldl' decodeInt 0 (zip [0 ..] (take 7 (reverse bytes)))
203219
where
204220
decodeInt !n (i, byte) =
205221
n .|. fromEnum byte `shiftL` (i * 5)
206222

207-
w5bs :: [Word5] -> ByteString
208-
w5bs = BS.pack . fromMaybe (error "what") . Bech32.toBase256
209-
210-
w5txt :: [Word5] -> Text
211-
w5txt = decodeUtf8 . w5bs
212-
213223
parseTag :: [Word5] -> (Maybe Tag, [Word5])
214224
parseTag [] = (Nothing, [])
215225
parseTag ws@[_] = (Nothing, ws)
216226
parseTag ws@[_, _] = (Nothing, ws) -- appease the compiler warning gods
217227
parseTag ws
218228
| length ws < 8 = (Nothing, ws)
219-
parseTag ws@(typ : d1 : d2 : rest)
229+
parseTag ws@(t0 : d1 : d2 : rest)
220230
| length rest < 7 = (Nothing, ws)
221231
| otherwise = (Just tag, leftovers)
222232
where
223-
dataLen = w5int [d1, d2]
224-
(dat, leftovers) = Prelude.splitAt dataLen rest
225-
datBs = Hex (w5bs dat)
233+
typ = fromEnum t0
234+
len = w5int [d1, d2]
235+
(dat, leftovers) = splitAt len rest
236+
hex = w5hex dat
237+
txt = w5txt dat
238+
mkt :: (a -> Tag) -> Either String a -> Tag
239+
mkt con = either (UnparsedTag typ dat) con
226240
tag =
227-
case fromEnum typ of
228-
1 -> PaymentHash datBs
229-
16 -> PaymentSecret datBs
230-
23 -> DescriptionHash datBs -- (w5bs dat)
231-
13 -> Description (w5txt dat)
232-
19 -> PayeePubkey datBs
233-
6 -> Expiry (w5int dat)
234-
24 -> MinFinalCltvExpiry (w5int dat)
241+
case typ of
242+
1 -> mkt PaymentHash hex
243+
16 -> mkt PaymentSecret hex
244+
13 -> mkt Description txt
245+
27 -> AdditionalMetadata dat
246+
19 -> mkt PayeePubkey hex
247+
23 -> mkt DescriptionHash hex
248+
6 -> Expiry $ w5int dat
249+
24 -> MinFinalCltvExpiry $ w5int dat
235250
9 -> OnchainFallback
236251
3 -> ExtraRouteInfo
237252
5 -> FeatureBits dat
238-
n -> error ("unhandled typ " ++ show n)
253+
n -> UnknownTag n dat
239254

240255
data MSig
241256
= Sig [Word5]
@@ -255,12 +270,12 @@ parseTags ws
255270
decodeBolt11 :: Text -> Either String Bolt11
256271
decodeBolt11 raw = do
257272
(rawHrp, rawDp) <- first show $ Bech32.decodeLenient raw
258-
hrp <- parseOnly parseHrp $ Bech32.humanReadablePartToText rawHrp
273+
hrp <- Atto.parseOnly parseHrp $ Bech32.humanReadablePartToText rawHrp
259274
let (ts, rest) = splitAt 7 $ Bech32.dataPartToWords rawDp
260275
let (tags, leftover) = parseTags rest
261276
(rawSig, recFlag) <- case leftover of
262277
Sig ws -> Right $ splitAt 103 ws
263-
Unk left -> Left ("corrupt, leftover: " ++ show (Hex (w5bs left)))
278+
Unk left -> Left ("corrupt, leftover: " <> show left)
264279
sig <-
265280
maybe (Left "corrupt") Right $ Bech32.toBase256 rawSig
266281
Right

0 commit comments

Comments
 (0)