{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

module Test.Cardano.Ledger.Shelley.SafeHash (safeHashTest) where

import Cardano.Ledger.SafeHash

-- ByteString imports

import Data.ByteString (ByteString)
import Data.ByteString.Short (ShortByteString, toShort)

-- Testing imports

import Data.Proxy
import Data.String (fromString)
import Test.Cardano.Ledger.Shelley.ConcreteCryptoTypes
import Test.Tasty
import Test.Tasty.HUnit

-- =========================
-- Some examples

data FooI -- HashAnnotated indexes which are analogs of our EraIndependentXXX

long :: ByteString
long :: ByteString
long = (forall a. IsString a => String -> a
fromString String
"abc")

short :: ShortByteString
short :: ShortByteString
short = ByteString -> ShortByteString
toShort ByteString
long

-- Any newtype over some type that is SafeToHash can easily
-- derive SafeToHash and also assign its HashAnnotated type, and
-- thus become a client of 'hashAnnotated'

newtype Foo c = Foo ShortByteString
  deriving (Int -> Foo c -> ShowS
forall c. Int -> Foo c -> ShowS
forall c. [Foo c] -> ShowS
forall c. Foo c -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Foo c] -> ShowS
$cshowList :: forall c. [Foo c] -> ShowS
show :: Foo c -> String
$cshow :: forall c. Foo c -> String
showsPrec :: Int -> Foo c -> ShowS
$cshowsPrec :: forall c. Int -> Foo c -> ShowS
Show, Foo c -> Int
Foo c -> ByteString
forall c. Foo c -> Int
forall c. Foo c -> ByteString
forall t.
(t -> ByteString)
-> (t -> Int)
-> (forall c index.
    HashAlgorithm (HASH c) =>
    Proxy c -> Proxy index -> t -> SafeHash c index)
-> SafeToHash t
forall c index.
HashAlgorithm (HASH c) =>
Proxy c -> Proxy index -> Foo c -> SafeHash c index
forall c c index.
HashAlgorithm (HASH c) =>
Proxy c -> Proxy index -> Foo c -> SafeHash c index
makeHashWithExplicitProxys :: forall c index.
HashAlgorithm (HASH c) =>
Proxy c -> Proxy index -> Foo c -> SafeHash c index
$cmakeHashWithExplicitProxys :: forall c c index.
HashAlgorithm (HASH c) =>
Proxy c -> Proxy index -> Foo c -> SafeHash c index
originalBytesSize :: Foo c -> Int
$coriginalBytesSize :: forall c. Foo c -> Int
originalBytes :: Foo c -> ByteString
$coriginalBytes :: forall c. Foo c -> ByteString
SafeToHash)

instance HashAnnotated (Foo c) FooI c

foo :: Foo c
foo :: forall c. Foo c
foo = forall c. ShortByteString -> Foo c
Foo ShortByteString
short

-- ===================================
-- Lets run some examples, we'll need some concrete Crypto

foohash :: SafeHash StandardCrypto FooI
foohash :: SafeHash StandardCrypto FooI
foohash = forall x index c.
(HashAnnotated x index c, HashAlgorithm (HASH c)) =>
x -> SafeHash c index
hashAnnotated forall c. Foo c
foo

shorthash :: SafeHash StandardCrypto ShortByteString
shorthash :: SafeHash StandardCrypto ShortByteString
shorthash = forall t c index.
(SafeToHash t, HashAlgorithm (HASH c)) =>
Proxy c -> Proxy index -> t -> SafeHash c index
makeHashWithExplicitProxys (forall {k} (t :: k). Proxy t
Proxy @StandardCrypto) (forall {k} (t :: k). Proxy t
Proxy @ShortByteString) ShortByteString
short

longhash :: SafeHash StandardCrypto ByteString
longhash :: SafeHash StandardCrypto ByteString
longhash = forall t c index.
(SafeToHash t, HashAlgorithm (HASH c)) =>
Proxy c -> Proxy index -> t -> SafeHash c index
makeHashWithExplicitProxys (forall {k} (t :: k). Proxy t
Proxy @StandardCrypto) (forall {k} (t :: k). Proxy t
Proxy @ByteString) ByteString
long

foohashT :: SafeHash TestCrypto FooI
foohashT :: SafeHash TestCrypto FooI
foohashT = forall x index c.
(HashAnnotated x index c, HashAlgorithm (HASH c)) =>
x -> SafeHash c index
hashAnnotated forall c. Foo c
foo

shorthashT :: SafeHash TestCrypto ShortByteString
shorthashT :: SafeHash TestCrypto ShortByteString
shorthashT = forall t c index.
(SafeToHash t, HashAlgorithm (HASH c)) =>
Proxy c -> Proxy index -> t -> SafeHash c index
makeHashWithExplicitProxys (forall {k} (t :: k). Proxy t
Proxy @TestCrypto) (forall {k} (t :: k). Proxy t
Proxy @ShortByteString) ShortByteString
short

longhashT :: SafeHash TestCrypto ByteString
longhashT :: SafeHash TestCrypto ByteString
longhashT = forall t c index.
(SafeToHash t, HashAlgorithm (HASH c)) =>
Proxy c -> Proxy index -> t -> SafeHash c index
makeHashWithExplicitProxys (forall {k} (t :: k). Proxy t
Proxy @TestCrypto) (forall {k} (t :: k). Proxy t
Proxy @ByteString) ByteString
long

test1, test2, test3, test4 :: TestTree
test1 :: TestTree
test1 =
  String -> Assertion -> TestTree
testCase
    String
"short==long"
    (forall a.
(Eq a, Show a, HasCallStack) =>
String -> a -> a -> Assertion
assertEqual String
"ShortByteString and ByteString don't hash the same" SafeHash StandardCrypto ShortByteString
shorthash (forall i j c. SafeHash c i -> SafeHash c j
castSafeHash SafeHash StandardCrypto ByteString
longhash))
test2 :: TestTree
test2 =
  String -> Assertion -> TestTree
testCase
    String
"newtype==underlyingtype"
    (forall a.
(Eq a, Show a, HasCallStack) =>
String -> a -> a -> Assertion
assertEqual String
"A newtype and its underlying type dont hash the same" SafeHash StandardCrypto ShortByteString
shorthash (forall i j c. SafeHash c i -> SafeHash c j
castSafeHash SafeHash StandardCrypto FooI
foohash))
test3 :: TestTree
test3 =
  String -> Assertion -> TestTree
testCase
    String
"short==long"
    (forall a.
(Eq a, Show a, HasCallStack) =>
String -> a -> a -> Assertion
assertEqual String
"ShortByteString and ByteString don't hash the same" SafeHash TestCrypto ShortByteString
shorthashT (forall i j c. SafeHash c i -> SafeHash c j
castSafeHash SafeHash TestCrypto ByteString
longhashT))
test4 :: TestTree
test4 =
  String -> Assertion -> TestTree
testCase
    String
"newtype==underlyingtype"
    ( forall a.
(Eq a, Show a, HasCallStack) =>
String -> a -> a -> Assertion
assertEqual
        String
"A newtype and its underlying type dont hash the same"
        SafeHash TestCrypto ShortByteString
shorthashT
        (forall i j c. SafeHash c i -> SafeHash c j
castSafeHash SafeHash TestCrypto FooI
foohashT)
    )

safeHashTest :: TestTree
safeHashTest :: TestTree
safeHashTest =
  String -> [TestTree] -> TestTree
testGroup
    String
"SafeHash"
    [ String -> [TestTree] -> TestTree
testGroup String
"StandardCrypto" [TestTree
test1, TestTree
test2]
    , String -> [TestTree] -> TestTree
testGroup String
"TestCrypto" [TestTree
test3, TestTree
test4]
    ]