{-# OPTIONS_HADDOCK prune #-}
{-# LANGUAGE ViewPatterns #-}
module Data.ByteString.Bech32 (
encode
, decode
, verify
) where
import Control.Monad (guard)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Base32 as B32
import qualified Data.ByteString.Bech32.Internal as BI
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Builder.Extra as BE
import qualified Data.ByteString.Internal as BSI
import qualified Data.Char as C (toLower, isLower, isAlpha)
toStrict :: BSB.Builder -> BS.ByteString
toStrict :: Builder -> ByteString
toStrict = ByteString -> ByteString
BS.toStrict
(ByteString -> ByteString)
-> (Builder -> ByteString) -> Builder -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AllocationStrategy -> ByteString -> Builder -> ByteString
BE.toLazyByteStringWith (Int -> Int -> AllocationStrategy
BE.safeStrategy Int
128 Int
BE.smallChunkSize) ByteString
forall a. Monoid a => a
mempty
{-# INLINE toStrict #-}
create_checksum :: BS.ByteString -> BS.ByteString -> BS.ByteString
create_checksum :: ByteString -> ByteString -> ByteString
create_checksum = Encoding -> ByteString -> ByteString -> ByteString
BI.create_checksum Encoding
BI.Bech32
encode
:: BS.ByteString
-> BS.ByteString
-> Maybe BS.ByteString
encode :: ByteString -> ByteString -> Maybe ByteString
encode ((Char -> Char) -> ByteString -> ByteString
B8.map Char -> Char
C.toLower -> ByteString
hrp) (ByteString -> ByteString
B32.encode -> ByteString
dat) = do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ByteString -> Bool
BI.valid_hrp ByteString
hrp)
let check :: ByteString
check = ByteString -> ByteString -> ByteString
create_checksum ByteString
hrp (ByteString -> ByteString
BI.as_word5 ByteString
dat)
res :: ByteString
res = Builder -> ByteString
toStrict (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$
ByteString -> Builder
BSB.byteString ByteString
hrp
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
49
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString ByteString
dat
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString (ByteString -> ByteString
BI.as_base32 ByteString
check)
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ByteString -> Int
BS.length ByteString
res Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
91)
ByteString -> Maybe ByteString
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ByteString
res
decode
:: BS.ByteString
-> Maybe (BS.ByteString, BS.ByteString)
decode :: ByteString -> Maybe (ByteString, ByteString)
decode bs :: ByteString
bs@(BSI.PS ForeignPtr Word8
_ Int
_ Int
l) = do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
90)
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard ((Char -> Bool) -> ByteString -> Bool
B8.all (\Char
a -> if Char -> Bool
C.isAlpha Char
a then Char -> Bool
C.isLower Char
a else Bool
True) ByteString
bs)
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ByteString -> Bool
verify ByteString
bs)
Int
sep <- Word8 -> ByteString -> Maybe Int
BS.elemIndexEnd Word8
0x31 ByteString
bs
case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt Int
sep ByteString
bs of
(ByteString
hrp, ByteString
raw) -> do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ByteString -> Bool
BI.valid_hrp ByteString
hrp)
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ByteString -> Int
BS.length ByteString
raw Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
6)
(Word8
_, Int -> ByteString -> ByteString
BS.dropEnd Int
6 -> ByteString
bech32dat) <- ByteString -> Maybe (Word8, ByteString)
BS.uncons ByteString
raw
ByteString
dat <- ByteString -> Maybe ByteString
B32.decode ByteString
bech32dat
(ByteString, ByteString) -> Maybe (ByteString, ByteString)
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString
hrp, ByteString
dat)
verify
:: BS.ByteString
-> Bool
verify :: ByteString -> Bool
verify = Encoding -> ByteString -> Bool
BI.verify Encoding
BI.Bech32