Bech32m.hs (2928B)
1 {-# OPTIONS_HADDOCK prune #-} 2 {-# LANGUAGE ViewPatterns #-} 3 4 -- | 5 -- Module: Data.ByteString.Bech32m 6 -- Copyright: (c) 2024 Jared Tobin 7 -- License: MIT 8 -- Maintainer: Jared Tobin <jared@ppad.tech> 9 -- 10 -- The 11 -- [BIP350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki) 12 -- bech32m checksummed base32 encoding, with decoding and checksum 13 -- verification. 14 15 module Data.ByteString.Bech32m ( 16 -- * Encoding and Decoding 17 encode 18 , decode 19 20 -- * Checksum 21 , verify 22 ) where 23 24 import Control.Monad (guard) 25 import qualified Data.ByteString as BS 26 import qualified Data.ByteString.Char8 as B8 27 import qualified Data.ByteString.Base32 as B32 28 import qualified Data.ByteString.Bech32.Internal as BI 29 import qualified Data.ByteString.Builder as BSB 30 import qualified Data.ByteString.Builder.Extra as BE 31 import qualified Data.ByteString.Internal as BSI 32 import qualified Data.Char as C (toLower) 33 34 -- realization for small builders 35 toStrict :: BSB.Builder -> BS.ByteString 36 toStrict = BS.toStrict 37 . BE.toLazyByteStringWith (BE.safeStrategy 128 BE.smallChunkSize) mempty 38 {-# INLINE toStrict #-} 39 40 create_checksum :: BS.ByteString -> BS.ByteString -> BS.ByteString 41 create_checksum = BI.create_checksum BI.Bech32m 42 43 -- | Encode a base256 human-readable part and input as bech32m. 44 -- 45 -- >>> let Just bech32m = encode "bc" "my string" 46 -- >>> bech32m 47 -- "bc1d4ujqum5wf5kuecwqlxtg" 48 encode 49 :: BS.ByteString -- ^ base256-encoded human-readable part 50 -> BS.ByteString -- ^ base256-encoded data part 51 -> Maybe BS.ByteString -- ^ bech32m-encoded bytestring 52 encode (B8.map C.toLower -> hrp) (B32.encode -> dat) = do 53 guard (BI.valid_hrp hrp) 54 ws <- BI.as_word5 dat 55 let check = create_checksum hrp ws 56 res = toStrict $ 57 BSB.byteString hrp 58 <> BSB.word8 49 -- 1 59 <> BSB.byteString dat 60 <> BSB.byteString (BI.as_base32 check) 61 guard (BS.length res < 91) 62 pure res 63 64 -- | Decode a bech32m-encoded 'ByteString' into its human-readable and data 65 -- parts. 66 -- 67 -- >>> decode "hi1df6x7cnfdcs8wctnyp5x2un9m9ac4f" 68 -- Just ("hi","jtobin was here") 69 -- >>> decode "hey1df6x7cnfdcs8wctnyp5x2un9m9ac4f" -- s/hi/hey 70 -- Nothing 71 decode 72 :: BS.ByteString -- ^ bech23-encoded bytestring 73 -> Maybe (BS.ByteString, BS.ByteString) -- ^ (hrp, data less checksum) 74 decode bs@(BSI.PS _ _ l) = do 75 guard (l <= 90) 76 guard (verify bs) 77 sep <- BS.elemIndexEnd 0x31 bs 78 case BS.splitAt sep bs of 79 (hrp, raw) -> do 80 guard (BI.valid_hrp hrp) 81 guard (BS.length raw >= 6) 82 (_, BS.dropEnd 6 -> bech32dat) <- BS.uncons raw 83 dat <- B32.decode bech32dat 84 pure (hrp, dat) 85 86 -- | Verify that a bech32m string has a valid checksum. 87 -- 88 -- >>> verify "bc1d4ujqum5wf5kuecwqlxtg" 89 -- True 90 -- >>> verify "bc1d4ujquw5wf5kuecwqlxtg" -- s/m/w 91 -- False 92 verify 93 :: BS.ByteString -- ^ bech32m-encoded bytestring 94 -> Bool 95 verify = BI.verify BI.Bech32m 96