commit dd25247ba61d75fae3502b51e95766acf47450e4
parent d1583e150f23af9baa923f1aaa17b35b85872a5e
Author: Jared Tobin <jared@jtobin.io>
Date: Fri, 3 Jan 2025 19:29:33 -0330
lib: decoding
Diffstat:
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