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 +0/−0
- LICENSE +30/−0
- README.md +6/−0
- Setup.hs +6/−0
- multihash-cryptonite.cabal +102/−0
- src/Data/Multihash.hs +164/−0
- src/Data/Multihash/Internal.hs +181/−0
- src/Data/Multihash/Lazy.hs +133/−0
- test/doctests/Main.hs +7/−0
- test/properties/Main.hs +93/−0
+ 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>"