Bech32m.hs (2540B)
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.Internal as BSI 30 import qualified Data.Char as C (toLower) 31 32 create_checksum :: BS.ByteString -> BS.ByteString -> BS.ByteString 33 create_checksum = BI.create_checksum BI.Bech32m 34 35 -- | Encode a base256 human-readable part and input as bech32m. 36 -- 37 -- >>> let Just bech32m = encode "bc" "my string" 38 -- >>> bech32m 39 -- "bc1d4ujqum5wf5kuecwqlxtg" 40 encode 41 :: BS.ByteString -- ^ base256-encoded human-readable part 42 -> BS.ByteString -- ^ base256-encoded data part 43 -> Maybe BS.ByteString -- ^ bech32m-encoded bytestring 44 encode (B8.map C.toLower -> hrp) (B32.encode -> dat) = do 45 guard (BI.valid_hrp hrp) 46 ws <- BI.as_word5 dat 47 let check = create_checksum hrp ws 48 res = BS.concat [hrp, BS.singleton 49, dat, BI.as_base32 check] 49 guard (BS.length res < 91) 50 pure res 51 52 -- | Decode a bech32m-encoded 'ByteString' into its human-readable and data 53 -- parts. 54 -- 55 -- >>> decode "hi1df6x7cnfdcs8wctnyp5x2un9m9ac4f" 56 -- Just ("hi","jtobin was here") 57 -- >>> decode "hey1df6x7cnfdcs8wctnyp5x2un9m9ac4f" -- s/hi/hey 58 -- Nothing 59 decode 60 :: BS.ByteString -- ^ bech23-encoded bytestring 61 -> Maybe (BS.ByteString, BS.ByteString) -- ^ (hrp, data less checksum) 62 decode bs@(BSI.PS _ _ l) = do 63 guard (l <= 90) 64 guard (verify bs) 65 sep <- BS.elemIndexEnd 0x31 bs 66 case BS.splitAt sep bs of 67 (hrp, raw) -> do 68 guard (BI.valid_hrp hrp) 69 guard (BS.length raw >= 6) 70 (_, BS.dropEnd 6 -> bech32dat) <- BS.uncons raw 71 dat <- B32.decode bech32dat 72 pure (hrp, dat) 73 74 -- | Verify that a bech32m string has a valid checksum. 75 -- 76 -- >>> verify "bc1d4ujqum5wf5kuecwqlxtg" 77 -- True 78 -- >>> verify "bc1d4ujquw5wf5kuecwqlxtg" -- s/m/w 79 -- False 80 verify 81 :: BS.ByteString -- ^ bech32m-encoded bytestring 82 -> Bool 83 verify = BI.verify BI.Bech32m 84