bech32

Pure Haskell bech32, bech32m encodings (docs.ppad.tech/bech32).
git clone git://git.ppad.tech/bech32.git
Log | Files | Refs | README | LICENSE

commit dd25247ba61d75fae3502b51e95766acf47450e4
parent d1583e150f23af9baa923f1aaa17b35b85872a5e
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri,  3 Jan 2025 19:29:33 -0330

lib: decoding

Diffstat:
MREADME.md | 6+++++-
Mflake.nix | 2+-
Mlib/Data/ByteString/Bech32.hs | 28++++++++++++++++++++++++++--
Mlib/Data/ByteString/Bech32m.hs | 29+++++++++++++++++++++++++++--
Mppad-bech32.cabal | 6+++---
5 files changed, 62 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md @@ -1,6 +1,6 @@ # ppad-bech32 -A pure Haskell implementation of the bech32m and bech32 encodings on +A pure Haskell implementation of bech32m and bech32 encoding/decoding on strict ByteStrings, as specified by [BIP350][bi350] and [BIP173][bi173]. ## Usage @@ -26,6 +26,10 @@ A sample GHCi session: > -- tweaked stuff will obviously fail to verify (s/m/w below) > Bech32m.verify "bc1vys8xarpdejxzunyypwk7uny8qsxy7t5v4ehgunfdenswyuz0e" False + > + > -- decode bech32m-encoded input + > Bech32m.decode bech32m + Just ("bc","a standard word8 bytestring") ``` ## Documentation diff --git a/flake.nix b/flake.nix @@ -1,5 +1,5 @@ { - description = "Pure Haskell bech32m/bech32 encodings."; + description = "Pure Haskell bech32m and bech32 encoding/decoding."; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; diff --git a/lib/Data/ByteString/Bech32.hs b/lib/Data/ByteString/Bech32.hs @@ -9,11 +9,12 @@ -- -- The -- [BIP0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) --- bech32 checksummed base32 encoding, with checksum verification. +-- bech32 checksummed base32 encoding, with decoding and checksum verification. module Data.ByteString.Bech32 ( - -- * Encoding + -- * Encoding and Decoding encode + , decode -- * Checksum , verify @@ -26,6 +27,7 @@ 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) -- realization for small builders @@ -57,6 +59,28 @@ encode hrp (B32.encode -> dat) = do guard (BS.length res < 91) pure res +-- | Decode a bech32-encoded 'ByteString' into its human-readable and data +-- parts. +-- +-- >>> decode "hi1df6x7cnfdcs8wctnyp5x2un9wed5st" +-- Just ("hi","jtobin was here") +-- >>> decode "hey1df6x7cnfdcs8wctnyp5x2un9wed5st" -- s/hi/hey +-- Nothing +decode + :: BS.ByteString -- ^ bech23-encoded bytestring + -> Maybe (BS.ByteString, BS.ByteString) +decode bs@(BSI.PS _ _ l) = do + guard (l <= 90) + guard (verify bs) + sep <- BS.elemIndexEnd 0x31 bs + case BS.splitAt sep bs of + (hrp, raw) -> do + guard (BI.valid_hrp hrp) + guard (BS.length raw >= 6) + (_, BS.dropEnd 6 -> bech32dat) <- BS.uncons raw + dat <- B32.decode bech32dat + pure (hrp, dat) + -- | Verify that a bech32 string has a valid checksum. -- -- >>> verify "bc1d4ujqum5wf5kuecmu02w2" diff --git a/lib/Data/ByteString/Bech32m.hs b/lib/Data/ByteString/Bech32m.hs @@ -9,11 +9,13 @@ -- -- The -- [BIP350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki) --- bech32m checksummed base32 encoding, with checksum verification. +-- bech32m checksummed base32 encoding, with decoding and checksum +-- verification. module Data.ByteString.Bech32m ( - -- * Encoding + -- * Encoding and Decoding encode + , decode -- * Checksum , verify @@ -26,6 +28,7 @@ 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) -- realization for small builders @@ -57,6 +60,28 @@ encode hrp (B32.encode -> dat) = do guard (BS.length res < 91) pure res +-- | Decode a bech32m-encoded 'ByteString' into its human-readable and data +-- parts. +-- +-- >>> decode "hi1df6x7cnfdcs8wctnyp5x2un9m9ac4f" +-- Just ("hi","jtobin was here") +-- >>> decode "hey1df6x7cnfdcs8wctnyp5x2un9m9ac4f" -- s/hi/hey +-- Nothing +decode + :: BS.ByteString -- ^ bech23-encoded bytestring + -> Maybe (BS.ByteString, BS.ByteString) +decode bs@(BSI.PS _ _ l) = do + guard (l <= 90) + guard (verify bs) + sep <- BS.elemIndexEnd 0x31 bs + case BS.splitAt sep bs of + (hrp, raw) -> do + guard (BI.valid_hrp hrp) + guard (BS.length raw >= 6) + (_, BS.dropEnd 6 -> bech32dat) <- BS.uncons raw + dat <- B32.decode bech32dat + pure (hrp, dat) + -- | Verify that a bech32m string has a valid checksum. -- -- >>> verify "bc1d4ujqum5wf5kuecwqlxtg" diff --git a/ppad-bech32.cabal b/ppad-bech32.cabal @@ -1,7 +1,7 @@ cabal-version: 3.0 name: ppad-bech32 version: 0.1.2 -synopsis: The bech32 and bech32m encodings, per BIPs 173 & 350. +synopsis: bech32 and bech32m encoding/decoding, per BIPs 173 & 350. license: MIT license-file: LICENSE author: Jared Tobin @@ -11,8 +11,8 @@ build-type: Simple tested-with: GHC == 9.8.1 extra-doc-files: CHANGELOG description: - The bech32 and bech32m encodings on strict bytestrings, per BIPs 173 & - 350. + bech32 and bech32m encoding/decoding on strict bytestrings, per BIPs + 173 & 350. source-repository head type: git