{-# LANGUAGE OverloadedStrings #-}
module Network.Haskoin.Address.Base58
( Base58
, encodeBase58
, decodeBase58
, encodeBase58Check
, decodeBase58Check
) where
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
import Data.Maybe (fromMaybe, isJust, listToMaybe)
import Data.Serialize as S
import Data.String.Conversions (cs)
import Data.Text (Text)
import qualified Data.Text as T
import Network.Haskoin.Crypto.Hash
import Network.Haskoin.Util
import Numeric (readInt, showIntAtBase)
type Base58 = Text
b58Data :: ByteString
b58Data = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
b58 :: Int -> Char
b58 = C.index b58Data
b58' :: Char -> Maybe Int
b58' = flip C.elemIndex b58Data
encodeBase58I :: Integer -> Base58
encodeBase58I i = cs $ showIntAtBase 58 b58 i ""
decodeBase58I :: Base58 -> Maybe Integer
decodeBase58I s =
case go of
Just (r,[]) -> Just r
_ -> Nothing
where
p = isJust . b58'
f = fromMaybe e . b58'
go = listToMaybe $ readInt 58 p f (cs s)
e = error "Could not decode base58"
encodeBase58 :: ByteString -> Base58
encodeBase58 bs =
l `mappend` r
where
(z, b) = BS.span (== 0) bs
l = cs $ BS.replicate (BS.length z) (BS.index b58Data 0)
r | BS.null b = T.empty
| otherwise = encodeBase58I $ bsToInteger b
decodeBase58 :: Base58 -> Maybe ByteString
decodeBase58 t =
BS.append prefix <$> r
where
(z, b) = BS.span (== BS.index b58Data 0) (cs t)
prefix = BS.replicate (BS.length z) 0
r | BS.null b = Just BS.empty
| otherwise = integerToBS <$> decodeBase58I (cs b)
encodeBase58Check :: ByteString -> Base58
encodeBase58Check bs =
encodeBase58 $ BS.append bs $ encode $ checkSum32 bs
decodeBase58Check :: Base58 -> Maybe ByteString
decodeBase58Check bs = do
rs <- decodeBase58 bs
let (res, chk) = BS.splitAt (BS.length rs - 4) rs
guard $ chk == encode (checkSum32 res)
return res