module Data.Blockchain.Crypto.Hash
( Hash
, ToHash(..)
, hashToHex
, fromByteString
, unsafeFromByteString
) where
import qualified Crypto.Hash as Crypto
import qualified Data.Aeson as Aeson
import qualified Data.ByteArray.Encoding as Byte
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as Lazy
import qualified Data.Hashable as H
import qualified Data.Maybe as Maybe
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import qualified Data.Blockchain.Types.Hex as Hex
newtype Hash a = Hash { unHash :: Crypto.Digest Crypto.SHA256 }
deriving (Eq, Ord)
instance H.Hashable (Hash a) where
hashWithSalt _ = H.hash . show
instance Show (Hash a) where
show = show . unHash
instance Aeson.ToJSON (Hash a) where
toJSON = Aeson.String . Text.pack . show . unHash
instance Aeson.FromJSON (Hash a) where
parseJSON = Aeson.withText "Hash" $
maybe (fail "Invalid bytestring hash") return . fromByteString . Text.encodeUtf8
instance Monoid (Hash a) where
mempty = Hash $ Crypto.hash (mempty :: BS.ByteString)
mappend (Hash h1) (Hash h2) = Hash $ Crypto.hashFinalize $ Crypto.hashUpdates Crypto.hashInit [h1, h2]
class ToHash a where
hash :: a -> Hash a
default hash :: Aeson.ToJSON a => a -> Hash a
hash = Hash . Crypto.hash . Lazy.toStrict . Aeson.encode
instance ToHash BS.ByteString where
hash = Hash . Crypto.hash
hashToHex :: Hash a -> Hex.Hex256
hashToHex = Maybe.fromMaybe (error "Unexpected hex conversion failure") . Hex.hex256 . show . unHash
fromByteString :: BS.ByteString -> Maybe (Hash a)
fromByteString bs = case Byte.convertFromBase Byte.Base16 bs of
Left _ -> Nothing
Right bs' -> Hash <$> Crypto.digestFromByteString (bs' :: BS.ByteString)
unsafeFromByteString :: BS.ByteString -> Hash a
unsafeFromByteString = Maybe.fromMaybe (error "Invalid hash string") . fromByteString