packages feed

multihash-cryptonite (empty) → 0.1.0.0

raw patch · 10 files changed

+722/−0 lines, 10 filesdep +basedep +binarydep +binary-varintbuild-type:Customsetup-changed

Dependencies added: base, binary, binary-varint, bytestring, cryptonite, deepseq, doctest, hashable, hedgehog, memory, multihash-cryptonite

Files

+ CHANGELOG.md view
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018, Monadic GmbH++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Monadic GmbH nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,6 @@+Haskell implementation of [multihash](https://github.com/multiformats/multihash): "self-identifying hashes".++In contrast to other implementations, this library assumes that applications+mainly work with the `Crypto.Hash.Digest` type (from the [cryptonite](https://hackage.haskell.org/package/cryptonite) package),+and use multihash as a wire encoding, or treat an encoded multihash as an opaque+value i.e. destructuring into a product is not provided.
+ Setup.hs view
@@ -0,0 +1,6 @@+module Main where++import Distribution.Extra.Doctest (defaultMainWithDoctests)++main :: IO ()+main = defaultMainWithDoctests "doctests"
+ multihash-cryptonite.cabal view
@@ -0,0 +1,102 @@+cabal-version: 2.2+build-type:    Custom++name:          multihash-cryptonite+version:       0.1.0.0+synopsis:      Self-identifying hashes, implementation of <https://github.com/multiformats/multihash>+homepage:      https://github.com/oscoin/ipfs+bug-reports:   https://github.com/oscoin/ipfs/issues+license:       BSD-3-Clause+license-file:  LICENSE+author:        Kim Altintop+maintainer:    Kim Altintop <kim@monadic.xyz>, Monadic Team <team@monadic.xyz>+copyright:     2018 Monadic GmbH++category:      Data++extra-source-files:+      CHANGELOG.md+    , README.md++source-repository head+    type: git+    location: https://github.com/oscoin/ipfs++custom-setup+    setup-depends:+          base >= 4.9 && < 5+        , Cabal+        , cabal-doctest >= 1++common common+    default-language: Haskell2010+    ghc-options:+        -Wall+        -Wcompat+        -Wincomplete-uni-patterns+        -Wincomplete-record-updates+        -Wredundant-constraints+        -fprint-expanded-synonyms+        -funbox-small-strict-fields+    default-extensions:+        DeriveGeneric+        LambdaCase+        MultiWayIf+        NamedFieldPuns+        RecordWildCards+        StrictData+        TupleSections+        TypeApplications++library+    import: common+    hs-source-dirs: src+    exposed-modules:+        Data.Multihash+        Data.Multihash.Internal+        Data.Multihash.Lazy++    build-depends:+        base >= 4.9 && < 5+      , binary >= 0.8.3+      , binary-varint+      , bytestring >= 0.10.4+      , deepseq+      , cryptonite >= 0.13+      , hashable+      , memory >= 0.11++test-suite properties+    import: common+    main-is: Main.hs+    hs-source-dirs: test/properties+    type: exitcode-stdio-1.0++    build-depends:+        base+      , cryptonite+      , hedgehog+      , multihash-cryptonite++    ghc-options:+        -threaded+        -rtsopts+        -with-rtsopts=-N++test-suite doctests+    import: common+    main-is: Main.hs+    hs-source-dirs: test/doctests+    type: exitcode-stdio-1.0++    other-modules:   Build_doctests+    autogen-modules: Build_doctests++    build-depends:+        base+      , doctest++    ghc-options:+        -threaded+        -rtsopts+        -with-rtsopts=-N
+ src/Data/Multihash.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE ExistentialQuantification  #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables        #-}++-- |+-- Copyright   : 2018 Monadic GmbH+-- License     : BSD3+-- Maintainer  : kim@monadic.xyz, team@monadic.xyz+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+-- <https://github.com/multiformats/multihash multihash> encoding of 'Crypto.Hash.Digest's.+--+-- Example:+--+-- >>> :set -XOverloadedStrings+-- >>> import qualified Crypto.Hash as C+-- >>> import Data.ByteArray.Encoding (Base(..), convertToBase)+-- >>> import Data.ByteString (ByteString)+-- >>> import qualified Data.ByteString.Char8 as C8+-- >>> import System.IO (stdout)+-- >>> :{+--     let+--         input :: ByteString+--         input = "multihash"+--         atBase :: Base -> Multihash -> ByteString+--         atBase base = convertToBase base . encodedBytes+--      in+--         C8.hPutStr stdout $ C8.unlines+--             [ atBase Base16 $ multihash C.SHA1   input+--             , atBase Base32 $ multihash C.SHA1   input+--             , atBase Base16 $ multihash C.SHA256 input+--             , atBase Base32 $ multihash C.SHA256 input+--             ]+-- :}+-- 111488c2f11fb2ce392acb5b2986e640211c4690073e+-- CEKIRQXRD6ZM4OJKZNNSTBXGIAQRYRUQA47A====+-- 12209cbc07c3f991725836a3aa2a581ca2029198aa420b9d99bc0e131d9f3e2cbe47+-- CIQJZPAHYP4ZC4SYG2R2UKSYDSRAFEMYVJBAXHMZXQHBGHM7HYWL4RY=+--+module Data.Multihash+    ( Multihash+    , fromDigest+    , encodedBytes+    , multihash+    , decode+    , decodeDigest+    , getMultihash++    -- * Compact representation+    , CompactMultihash+    , compact+    , expand++    -- * Re-exports+    , HashAlgorithm+    , Multihashable+    , fromCryptonite+    , toCode+    , fromCode+    , digestSize+    )+where++import           Data.Multihash.Internal++import           Control.DeepSeq (NFData)+import qualified Crypto.Hash as C+import           Data.Bifunctor (bimap)+import qualified Data.Binary.Get as Binary+import           Data.Binary.VarInt (buildVarInt)+import           Data.ByteArray (ByteArrayAccess, convert)+import           Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Builder as Builder+import qualified Data.ByteString.Lazy as LBS+import           Data.ByteString.Short (ShortByteString, fromShort, toShort)+import           Data.Coerce (coerce)+import           Data.Hashable (Hashable)++-- | A multihash-encoded strict 'ByteString'.+newtype Multihash = Multihash ByteString+    deriving (Eq, Ord, Hashable, NFData)++-- | A 'Multihash' backed by a 'ShortByteString'.+--+-- This is useful when holding many 'Multihash'es in memory, due to lower memory+-- overhead and less heap fragmentation. See the documentation for+-- 'ShortByteString' for details.+newtype CompactMultihash = Compact ShortByteString+    deriving (Eq, Ord, Hashable, NFData)++-- | Encode a 'C.Digest' as a 'Multihash'.+fromDigest :: forall a. Multihashable a => C.Digest a -> Multihash+fromDigest dig =+    Multihash+        . LBS.toStrict . Builder.toLazyByteString+        $ code <> len <> Builder.byteString bytes+  where+    code  = buildVarInt . toCode $ fromCryptonite dig+    bytes = convert dig+    len   = buildVarInt $ BS.length bytes++-- | Extract the raw, multihash-encoded bytes of a 'Multihash'.+encodedBytes :: Multihash -> ByteString+encodedBytes = coerce+{-# INLINE encodedBytes #-}++-- | Hash a value to a 'Multihash'+multihash :: (ByteArrayAccess ba, Multihashable a) => a -> ba -> Multihash+multihash rithm = fromDigest . C.hashWith rithm++-- | Decode a 'Multihash' from a 'ByteString'.+decode :: ByteString -> Either String Multihash+decode =+    bimap _3 _3+        . Binary.runGetOrFail getMultihash+        . LBS.fromStrict++-- | Decode a 'C.Digest' from a multihash-encoded 'ByteString'.+decodeDigest+    :: forall a. Multihashable a+    => ByteString+    -> Either String (C.Digest a)+decodeDigest =+    bimap _3 _3+        . Binary.runGetOrFail getMultihashedDigest+        . LBS.fromStrict++getMultihash :: Binary.Get Multihash+getMultihash = do+    algo <- Binary.lookAhead getHashAlgorithm+    case algo of+        Blake2s_160 -> fromDigest <$> getMultihashedDigest @C.Blake2s_160+        Blake2s_224 -> fromDigest <$> getMultihashedDigest @C.Blake2s_224+        Blake2s_256 -> fromDigest <$> getMultihashedDigest @C.Blake2s_256+        Blake2b_160 -> fromDigest <$> getMultihashedDigest @C.Blake2b_160+        Blake2b_224 -> fromDigest <$> getMultihashedDigest @C.Blake2b_224+        Blake2b_256 -> fromDigest <$> getMultihashedDigest @C.Blake2b_256+        Blake2b_384 -> fromDigest <$> getMultihashedDigest @C.Blake2b_384+        Blake2b_512 -> fromDigest <$> getMultihashedDigest @C.Blake2b_512+        MD4         -> fromDigest <$> getMultihashedDigest @C.MD4+        MD5         -> fromDigest <$> getMultihashedDigest @C.MD5+        SHA1        -> fromDigest <$> getMultihashedDigest @C.SHA1+        SHA256      -> fromDigest <$> getMultihashedDigest @C.SHA256+        SHA512      -> fromDigest <$> getMultihashedDigest @C.SHA512+        Keccak_224  -> fromDigest <$> getMultihashedDigest @C.Keccak_224+        Keccak_256  -> fromDigest <$> getMultihashedDigest @C.Keccak_256+        Keccak_384  -> fromDigest <$> getMultihashedDigest @C.Keccak_384+        Keccak_512  -> fromDigest <$> getMultihashedDigest @C.Keccak_512+        SHA3_224    -> fromDigest <$> getMultihashedDigest @C.SHA3_224+        SHA3_256    -> fromDigest <$> getMultihashedDigest @C.SHA3_256+        SHA3_384    -> fromDigest <$> getMultihashedDigest @C.SHA3_384+        SHA3_512    -> fromDigest <$> getMultihashedDigest @C.SHA3_512++-- | Convert a 'Multihash' to a compact representation.+compact :: Multihash -> CompactMultihash+compact = coerce . toShort . coerce+{-# INLINE compact #-}++-- | Convert a 'CompactMultihash' to the regular representation.+expand :: CompactMultihash -> Multihash+expand = coerce . fromShort . coerce+{-# INLINE expand #-}
+ src/Data/Multihash/Internal.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE ConstraintKinds           #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE ScopedTypeVariables       #-}++-- |+-- Copyright   : 2018 Monadic GmbH+-- License     : BSD3+-- Maintainer  : kim@monadic.xyz, team@monadic.xyz+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+module Data.Multihash.Internal+    ( HashAlgorithm (..)+    , Multihashable++    , getMultihashedDigest+    , getHashAlgorithm+    , getLength++    , toCode+    , fromCode+    , fromCryptonite+    , digestSize++    , _3+    )+where++import           Control.Monad (when)+import qualified Crypto.Hash as C+import qualified Data.Binary.Get as Binary+import           Data.Binary.VarInt (getVarInt)+import           Data.Proxy (Proxy(..))+import           Data.Word (Word16)++type Multihashable a = (C.HashAlgorithm a, FromCryptonite a)++getMultihashedDigest :: forall a. Multihashable a => Binary.Get (C.Digest a)+getMultihashedDigest = do+    algo <- getHashAlgorithm+    when (fromCryptonite (Proxy @a) /= algo) $ fail "Algorithm mismatch"+    len <- getLength+    dig <- Binary.getByteString len+    maybe (fail "Invalid Digest") pure $ C.digestFromByteString dig++getHashAlgorithm :: Binary.Get HashAlgorithm+getHashAlgorithm = do+    code <- Binary.getWord8 >>= getVarInt+    case fromCode code of+        Nothing -> fail ("Unknown HashAlgorithm " <> show code)+        Just ha -> pure ha++getLength :: Binary.Get Int+getLength = Binary.getWord8 >>= getVarInt++-- | 'Crypto.Hash.HashAlgorithm's for which we know a multihash code.+--+-- Note that this currently excludes variable output-length algorithms.+data HashAlgorithm =+      Blake2s_160+    | Blake2s_224+    | Blake2s_256+    | Blake2b_160+    | Blake2b_224+    | Blake2b_256+    | Blake2b_384+    | Blake2b_512+    | MD4+    | MD5+    | SHA1+    | SHA256+    | SHA512+    | Keccak_224+    | Keccak_256+    | Keccak_384+    | Keccak_512+    | SHA3_224+    | SHA3_256+    | SHA3_384+    | SHA3_512+    deriving (Eq, Enum, Bounded)++class FromCryptonite a where+    fromCryptonite :: proxy a -> HashAlgorithm++instance FromCryptonite C.Blake2s_160 where fromCryptonite _ = Blake2s_160+instance FromCryptonite C.Blake2s_224 where fromCryptonite _ = Blake2s_224+instance FromCryptonite C.Blake2s_256 where fromCryptonite _ = Blake2s_256+instance FromCryptonite C.Blake2b_160 where fromCryptonite _ = Blake2b_160+instance FromCryptonite C.Blake2b_224 where fromCryptonite _ = Blake2b_224+instance FromCryptonite C.Blake2b_256 where fromCryptonite _ = Blake2b_256+instance FromCryptonite C.Blake2b_384 where fromCryptonite _ = Blake2b_384+instance FromCryptonite C.Blake2b_512 where fromCryptonite _ = Blake2b_512+instance FromCryptonite C.MD4         where fromCryptonite _ = MD4+instance FromCryptonite C.MD5         where fromCryptonite _ = MD5+instance FromCryptonite C.SHA1        where fromCryptonite _ = SHA1+instance FromCryptonite C.SHA256      where fromCryptonite _ = SHA256+instance FromCryptonite C.SHA512      where fromCryptonite _ = SHA512+instance FromCryptonite C.Keccak_224  where fromCryptonite _ = Keccak_224+instance FromCryptonite C.Keccak_256  where fromCryptonite _ = Keccak_256+instance FromCryptonite C.Keccak_384  where fromCryptonite _ = Keccak_384+instance FromCryptonite C.Keccak_512  where fromCryptonite _ = Keccak_512+instance FromCryptonite C.SHA3_224    where fromCryptonite _ = SHA3_224+instance FromCryptonite C.SHA3_256    where fromCryptonite _ = SHA3_256+instance FromCryptonite C.SHA3_384    where fromCryptonite _ = SHA3_384+instance FromCryptonite C.SHA3_512    where fromCryptonite _ = SHA3_512++toCode :: HashAlgorithm -> Word16+toCode Blake2s_160 = 0xb254+toCode Blake2s_224 = 0xb25c+toCode Blake2s_256 = 0xb260+toCode Blake2b_160 = 0xb214+toCode Blake2b_224 = 0xb21c+toCode Blake2b_256 = 0xb220+toCode Blake2b_384 = 0xb230+toCode Blake2b_512 = 0xb240+toCode MD4         = 0xd4+toCode MD5         = 0xd5+toCode SHA1        = 0x11+toCode SHA256      = 0x12+toCode SHA512      = 0x13+toCode Keccak_224  = 0x1A+toCode Keccak_256  = 0x1B+toCode Keccak_384  = 0x1C+toCode Keccak_512  = 0x1D+toCode SHA3_224    = 0x17+toCode SHA3_256    = 0x16+toCode SHA3_384    = 0x15+toCode SHA3_512    = 0x14++fromCode :: Word16 -> Maybe HashAlgorithm+fromCode 0xb254 = pure Blake2s_160+fromCode 0xb25c = pure Blake2s_224+fromCode 0xb260 = pure Blake2s_256+fromCode 0xb214 = pure Blake2b_160+fromCode 0xb21c = pure Blake2b_224+fromCode 0xb220 = pure Blake2b_256+fromCode 0xb230 = pure Blake2b_384+fromCode 0xb240 = pure Blake2b_512+fromCode 0xd4   = pure MD4+fromCode 0xd5   = pure MD5+fromCode 0x11   = pure SHA1+fromCode 0x12   = pure SHA256+fromCode 0x13   = pure SHA512+fromCode 0x1A   = pure Keccak_224+fromCode 0x1B   = pure Keccak_256+fromCode 0x1C   = pure Keccak_384+fromCode 0x1D   = pure Keccak_512+fromCode 0x17   = pure SHA3_224+fromCode 0x16   = pure SHA3_256+fromCode 0x15   = pure SHA3_384+fromCode 0x14   = pure SHA3_512+fromCode _      = Nothing++digestSize :: HashAlgorithm -> Int+digestSize Blake2s_160 = C.hashDigestSize C.Blake2s_160+digestSize Blake2s_224 = C.hashDigestSize C.Blake2s_224+digestSize Blake2s_256 = C.hashDigestSize C.Blake2s_256+digestSize Blake2b_160 = C.hashDigestSize C.Blake2b_160+digestSize Blake2b_224 = C.hashDigestSize C.Blake2b_224+digestSize Blake2b_256 = C.hashDigestSize C.Blake2b_256+digestSize Blake2b_384 = C.hashDigestSize C.Blake2b_384+digestSize Blake2b_512 = C.hashDigestSize C.Blake2b_512+digestSize MD4         = C.hashDigestSize C.MD4+digestSize MD5         = C.hashDigestSize C.MD5+digestSize SHA1        = C.hashDigestSize C.SHA1+digestSize SHA256      = C.hashDigestSize C.SHA256+digestSize SHA512      = C.hashDigestSize C.SHA512+digestSize Keccak_224  = C.hashDigestSize C.Keccak_224+digestSize Keccak_256  = C.hashDigestSize C.Keccak_256+digestSize Keccak_384  = C.hashDigestSize C.Keccak_384+digestSize Keccak_512  = C.hashDigestSize C.Keccak_512+digestSize SHA3_224    = C.hashDigestSize C.SHA3_224+digestSize SHA3_256    = C.hashDigestSize C.SHA3_256+digestSize SHA3_384    = C.hashDigestSize C.SHA3_384+digestSize SHA3_512    = C.hashDigestSize C.SHA3_512++--------------------------------------------------------------------------------++_3 :: (a, b, c) -> c+_3 (_, _, x) = x
+ src/Data/Multihash/Lazy.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE ExistentialQuantification  #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables        #-}++-- |+-- Copyright   : 2018 Monadic GmbH+-- License     : BSD3+-- Maintainer  : kim@monadic.xyz, team@monadic.xyz+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+-- Like "Data.Multihash", but using a lazy 'ByteString' representation.+--+-- Example:+--+-- >>> :set -XOverloadedStrings+-- >>> import qualified Crypto.Hash as C+-- >>> import Data.ByteArray.Encoding (Base(..), convertToBase)+-- >>> import qualified Data.ByteString as Strict+-- >>> import qualified Data.ByteString.Lazy as Lazy+-- >>> import Data.ByteString.Lazy (toStrict)+-- >>> import qualified Data.ByteString.Char8 as C8+-- >>> import System.IO (stdout)+-- >>> :{+--     let+--         input :: Strict.ByteString+--         input = "multihash"+--         atBase :: Base -> Multihash -> Strict.ByteString+--         atBase base = convertToBase base . toStrict . encodedBytes+--      in+--         C8.hPutStr stdout $ C8.unlines+--             [ atBase Base16 $ multihash C.SHA1   input+--             , atBase Base32 $ multihash C.SHA1   input+--             , atBase Base16 $ multihash C.SHA256 input+--             , atBase Base32 $ multihash C.SHA256 input+--             ]+-- :}+-- 111488c2f11fb2ce392acb5b2986e640211c4690073e+-- CEKIRQXRD6ZM4OJKZNNSTBXGIAQRYRUQA47A====+-- 12209cbc07c3f991725836a3aa2a581ca2029198aa420b9d99bc0e131d9f3e2cbe47+-- CIQJZPAHYP4ZC4SYG2R2UKSYDSRAFEMYVJBAXHMZXQHBGHM7HYWL4RY=+--+module Data.Multihash.Lazy+    ( Multihash+    , fromDigest+    , encodedBytes+    , multihash+    , decode+    , decodeDigest+    , getMultihash++    -- * Re-exports+    , Multihashable+    )+where++import           Data.Multihash.Internal++import           Control.DeepSeq (NFData)+import qualified Crypto.Hash as C+import           Data.Bifunctor (bimap)+import qualified Data.Binary.Get as Binary+import           Data.Binary.VarInt (buildVarInt)+import           Data.ByteArray (ByteArrayAccess, convert)+import qualified Data.ByteString as Strict+import qualified Data.ByteString.Builder as Builder+import           Data.ByteString.Lazy (ByteString)+import           Data.Coerce (coerce)+import           Data.Hashable (Hashable)++-- | A multihash-encoded lazy 'ByteString'+newtype Multihash = Multihash ByteString+    deriving (Eq, Ord, Hashable, NFData)++-- | Encode a 'C.Digest' as a 'Multihash'.+fromDigest :: forall a. Multihashable a => C.Digest a -> Multihash+fromDigest dig =+    Multihash+        . Builder.toLazyByteString+        $ code <> len <> Builder.byteString bytes+  where+    code  = buildVarInt . toCode $ fromCryptonite dig+    bytes = convert dig+    len   = buildVarInt $ Strict.length bytes++-- | Extract the raw, multihash-encoded bytes of a 'Multihash'.+encodedBytes :: Multihash -> ByteString+encodedBytes = coerce+{-# INLINE encodedBytes #-}++-- | Hash a value to a 'Multihash'.+--+-- Note that lazy 'ByteString's are /not/ an instance of 'ByteArrayAccess', ie.+-- you will have to pass a strict 'Strict.ByteString' here.+multihash :: (ByteArrayAccess ba, Multihashable a) => a -> ba -> Multihash+multihash rithm = fromDigest . C.hashWith rithm++-- | Decode a 'Multihash' from a lazy 'ByteString'.+decode :: ByteString -> Either String Multihash+decode = bimap _3 _3 . Binary.runGetOrFail getMultihash++-- | Decode a 'C.Digest' from a multihash-encoded lazy 'ByteString'.+decodeDigest+    :: forall a. Multihashable a+    => ByteString+    -> Either String (C.Digest a)+decodeDigest = bimap _3 _3 . Binary.runGetOrFail getMultihashedDigest++getMultihash :: Binary.Get Multihash+getMultihash = do+    algo <- Binary.lookAhead getHashAlgorithm+    case algo of+        Blake2s_160 -> fromDigest <$> getMultihashedDigest @C.Blake2s_160+        Blake2s_224 -> fromDigest <$> getMultihashedDigest @C.Blake2s_224+        Blake2s_256 -> fromDigest <$> getMultihashedDigest @C.Blake2s_256+        Blake2b_160 -> fromDigest <$> getMultihashedDigest @C.Blake2b_160+        Blake2b_224 -> fromDigest <$> getMultihashedDigest @C.Blake2b_224+        Blake2b_256 -> fromDigest <$> getMultihashedDigest @C.Blake2b_256+        Blake2b_384 -> fromDigest <$> getMultihashedDigest @C.Blake2b_384+        Blake2b_512 -> fromDigest <$> getMultihashedDigest @C.Blake2b_512+        MD4         -> fromDigest <$> getMultihashedDigest @C.MD4+        MD5         -> fromDigest <$> getMultihashedDigest @C.MD5+        SHA1        -> fromDigest <$> getMultihashedDigest @C.SHA1+        SHA256      -> fromDigest <$> getMultihashedDigest @C.SHA256+        SHA512      -> fromDigest <$> getMultihashedDigest @C.SHA512+        Keccak_224  -> fromDigest <$> getMultihashedDigest @C.Keccak_224+        Keccak_256  -> fromDigest <$> getMultihashedDigest @C.Keccak_256+        Keccak_384  -> fromDigest <$> getMultihashedDigest @C.Keccak_384+        Keccak_512  -> fromDigest <$> getMultihashedDigest @C.Keccak_512+        SHA3_224    -> fromDigest <$> getMultihashedDigest @C.SHA3_224+        SHA3_256    -> fromDigest <$> getMultihashedDigest @C.SHA3_256+        SHA3_384    -> fromDigest <$> getMultihashedDigest @C.SHA3_384+        SHA3_512    -> fromDigest <$> getMultihashedDigest @C.SHA3_512
+ test/doctests/Main.hs view
@@ -0,0 +1,7 @@+module Main (main) where++import           Build_doctests (flags, module_sources, pkgs)+import           Test.DocTest (doctest)++main :: IO ()+main = doctest $ flags <> pkgs <> module_sources
+ test/properties/Main.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE TemplateHaskell  #-}+{-# LANGUAGE TypeApplications #-}++module Main (main) where++import           Control.Monad (unless)+import qualified Crypto.Hash as C+import           System.Exit (exitFailure)+import           System.IO (BufferMode(..), hSetBuffering, stderr, stdout)++import qualified Data.Multihash as Strict+import qualified Data.Multihash.Lazy as Lazy++import           Hedgehog+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range++main :: IO ()+main = do+    hSetBuffering stdout LineBuffering+    hSetBuffering stderr LineBuffering++    ok <- props+    unless ok exitFailure++props :: IO Bool+props = checkParallel $$(discover)++prop_roundtripStrict :: Property+prop_roundtripStrict = property $ do+    bs <- forAll $ Gen.bytes (Range.singleton 128)+    let+        hashed  = Strict.multihash C.Blake2b_256 bs+        bytes   = Strict.encodedBytes hashed+        decoded = Strict.fromDigest @C.Blake2b_256 <$> Strict.decodeDigest bytes+     in+        assert $ decoded == Right hashed++prop_roundtripBytesStrict :: Property+prop_roundtripBytesStrict = property $ do+    bs <- forAll $ Gen.bytes (Range.singleton 128)+    let+        hashed  = Strict.multihash C.Blake2b_256 bs+        bytes   = Strict.encodedBytes hashed+        decoded = Strict.decode bytes+     in+        assert $ decoded == Right hashed++prop_roundtripLazy :: Property+prop_roundtripLazy = property $ do+    bs <- forAll $ Gen.bytes (Range.singleton 128)+    let+        hashed  = Lazy.multihash C.Blake2b_256 bs+        bytes   = Lazy.encodedBytes hashed+        decoded = Lazy.fromDigest @C.Blake2b_256 <$> Lazy.decodeDigest bytes+     in+        assert $ decoded == Right hashed++prop_roundtripBytesLazy :: Property+prop_roundtripBytesLazy = property $ do+    bs <- forAll $ Gen.bytes (Range.singleton 128)+    let+        hashed  = Lazy.multihash C.Blake2b_256 bs+        bytes   = Lazy.encodedBytes hashed+        decoded = Lazy.decode bytes+     in+        assert $ decoded == Right hashed++prop_algorithmMismatchStrict :: Property+prop_algorithmMismatchStrict = property $ do+    bs <- forAll $ Gen.bytes (Range.singleton 128)+    let+        hashed  = Strict.multihash C.Blake2b_256 bs+        bytes   = Strict.encodedBytes hashed+        decoded = Strict.fromDigest @C.SHA256 <$> Strict.decodeDigest bytes+     in do+        annotate (render decoded)+        assert $ decoded == Left "Algorithm mismatch"++prop_algorithmMismatchLazy :: Property+prop_algorithmMismatchLazy = property $ do+    bs <- forAll $ Gen.bytes (Range.singleton 128)+    let+        hashed  = Lazy.multihash C.Blake2b_256 bs+        bytes   = Lazy.encodedBytes hashed+        decoded = Lazy.fromDigest @C.SHA256 <$> Lazy.decodeDigest bytes+     in do+        annotate (render decoded)+        assert $ decoded == Left "Algorithm mismatch"++render :: Either String a -> String+render (Left  s) = "Left " <> s+render (Right _) = "Right <multihash>"