packages feed

cardano-addresses-4.0.2: test/Cardano/Address/Script/ParserSpec.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}

module Cardano.Address.Script.ParserSpec
    ( spec
    ) where

import Prelude

import Cardano.Address.KeyHash
    ( KeyHash (..), KeyRole (..) )
import Cardano.Address.Script
    ( Cosigner (..), Script (..) )
import Cardano.Address.Script.Parser
    ( requireAllOfParser
    , requireAnyOfParser
    , requireAtLeastOfParser
    , requireCosignerOfParser
    , requireCosignerOfParser
    , requireSignatureOfParser
    , scriptParser
    )
import Data.ByteString
    ( ByteString )
import Data.Text
    ( Text )
import Test.Hspec
    ( Spec, SpecWith, describe, it, shouldBe )
import Text.ParserCombinators.ReadP
    ( ReadP, readP_to_S )

import qualified Codec.Binary.Encoding as E
import qualified Data.Text as T
import qualified Data.Text.Encoding as T

spec :: Spec
spec = do
    requireOfParserTests @KeyHash requireSignatureOfParser
        (kh1,verKeyH1) "requireSignatureOfParser"
    requireOfParserTests @Cosigner requireCosignerOfParser
        (cosigner0,cosigner0Txt) "requireCosignerOfParser"

    requireAllOfParserTests @KeyHash requireSignatureOfParser
        [(kh1,verKeyH1), (kh2,verKeyH2), (kh3,verKeyH3)]
    requireAllOfParserTests @Cosigner requireCosignerOfParser
        [(cosigner0,cosigner0Txt), (cosigner1,cosigner1Txt), (cosigner2,cosigner2Txt)]

    requireAnyOfParserTests @KeyHash requireSignatureOfParser
        [(kh1,verKeyH1), (kh2,verKeyH2), (kh3,verKeyH3)]
    requireAnyOfParserTests @Cosigner requireCosignerOfParser
        [(cosigner0,cosigner0Txt), (cosigner1,cosigner1Txt), (cosigner2,cosigner2Txt)]

    requireAtLeastOfParserTests @KeyHash requireSignatureOfParser
        [(kh1,verKeyH1), (kh2,verKeyH2), (kh3,verKeyH3)]
    requireAtLeastOfParserTests @Cosigner requireCosignerOfParser
        [(cosigner0,cosigner0Txt), (cosigner1,cosigner1Txt), (cosigner2,cosigner2Txt)]

    timelockParserTests @KeyHash requireSignatureOfParser
        (kh1,verKeyH1)
    timelockParserTests @Cosigner requireCosignerOfParser
        (cosigner0,cosigner0Txt)
  where
    verKeyH1 = "addr_shared_vkh1zxt0uvrza94h3hv4jpv0ttddgnwkvdgeyq8jf9w30mcs6y8w3nq" :: Text
    kh1 = KeyHash PaymentShared (unBech32 verKeyH1)
    verKeyH2 = "addr_shared_vkh1y3zl4nqgm96ankt96dsdhc86vd5geny0wr7hu8cpzdfcqskq2cp" :: Text
    kh2 = KeyHash PaymentShared (unBech32 verKeyH2)
    verKeyH3 = "addr_shared_vkh175wsm9ckhm3snwcsn72543yguxeuqm7v9r6kl6gx57h8gdydcd9" :: Text
    kh3 = KeyHash PaymentShared (unBech32 verKeyH3)

    script1 txt = "all ["<>txt<>"]"
    script2 txt = " all   [ "<>txt<>"  ] "
    script3 txt1 txt2 = "all ["<>txt1<>", "<>txt2<>"]"
    script4 txt1 txt2 txt3 = "all ["<>txt1<>", "<>txt2<>","<>txt3<>"]"
    script5 txt = "any ["<>txt<>"]"
    script6 txt = " any   [ "<>txt<>"  ] "
    script7 txt1 txt2 = "any ["<>txt1<>", "<>txt2<>"]"
    script8 txt1 txt2 txt3 = "any ["<>txt1<>", "<>txt2<>","<>txt3<>"]"
    script9 txt1 txt2 txt3 = "any ["<>txt1<>", all ["<>txt2<>","<>txt3<>"]]"
    script10 txt1 txt2 txt3 = "at_least 1 ["<>txt1<>", "<>txt2<>","<>txt3<>"]"
    script11 txt1 txt2 txt3 = "at_least 1 ["<>txt1<>", all ["<>txt2<>","<>txt3<>"]]"
    script12 = "all []"
    script13 txt = "any ["<>txt<>", all [   ]]"
    script14 txt = "all ["<>txt<>", active_from 120]"
    script15 txt = "all ["<>txt<>", active_until 150]"
    script16 txt = "all ["<>txt<>", active_from 120, active_until 125]"

    cosigner0Txt = "cosigner#0" :: Text
    cosigner0 = Cosigner 0
    cosigner1Txt = "cosigner#1" :: Text
    cosigner1 = Cosigner 1
    cosigner2Txt = "cosigner#2" :: Text
    cosigner2 = Cosigner 2

    requireOfParserTests
        :: (Eq a, Show a)
        => ReadP (Script a)
        -> (a, Text)
        -> String
        -> SpecWith ()
    requireOfParserTests parser (obj, txt) descr =
        describe (descr <> " : unit tests") $ do
            valuesParserUnitTest parser txt
                (RequireSignatureOf obj)
            valuesParserUnitTest parser (txt <> " ")
                (RequireSignatureOf obj)
            valuesParserUnitTest parser (txt <>", ")
                (RequireSignatureOf obj)
            valuesParserUnitTest parser ("        " <> txt <>", ")
                (RequireSignatureOf obj)

    requireAllOfParserTests
        :: (Eq a, Show a)
        => ReadP (Script a)
        -> [(a, Text)]
        -> SpecWith ()
    requireAllOfParserTests parser objTxts = do
        let [(obj1, txt1),(obj2, txt2),(obj3, txt3)] = objTxts
        describe "requireAllOfParser : unit tests" $ do
            let expected1 = RequireAllOf
                    [ RequireSignatureOf obj1 ]
            valuesParserUnitTest (requireAllOfParser parser) (script1 txt1) expected1
            valuesParserUnitTest (scriptParser parser) (script1 txt1) expected1

            valuesParserUnitTest (requireAllOfParser parser) (script2 txt1) expected1
            valuesParserUnitTest (scriptParser parser) (script2 txt1) expected1

            let expected2 = RequireAllOf
                    [ RequireSignatureOf obj1
                    , RequireSignatureOf obj2 ]
            valuesParserUnitTest (requireAllOfParser parser) (script3 txt1 txt2) expected2
            valuesParserUnitTest (scriptParser parser) (script3 txt1 txt2) expected2

            let expected3 = RequireAllOf
                    [ RequireSignatureOf obj1
                    , RequireSignatureOf obj2
                    , RequireSignatureOf obj3 ]
            valuesParserUnitTest (requireAllOfParser parser) (script4 txt1 txt2 txt3) expected3
            valuesParserUnitTest (scriptParser parser) (script4 txt1 txt2 txt3) expected3

            let expected4 = RequireAllOf []
            valuesParserUnitTest (requireAllOfParser parser) script12 expected4
            valuesParserUnitTest (scriptParser parser) script12 expected4

    requireAnyOfParserTests
        :: (Eq a, Show a)
        => ReadP (Script a)
        -> [(a, Text)]
        -> SpecWith ()
    requireAnyOfParserTests parser objTxts = do
        let [(obj1, txt1),(obj2, txt2),(obj3, txt3)] = objTxts
        describe "requireAnyOfParser : unit tests" $ do
            let expected1 = RequireAnyOf
                    [ RequireSignatureOf obj1 ]
            valuesParserUnitTest (requireAnyOfParser parser) (script5 txt1) expected1
            valuesParserUnitTest (scriptParser parser) (script5 txt1) expected1

            valuesParserUnitTest (requireAnyOfParser parser) (script6 txt1) expected1
            valuesParserUnitTest (scriptParser parser) (script6 txt1) expected1

            let expected2 = RequireAnyOf
                    [ RequireSignatureOf obj1
                    , RequireSignatureOf obj2 ]
            valuesParserUnitTest (requireAnyOfParser parser) (script7 txt1 txt2) expected2
            valuesParserUnitTest (scriptParser parser) (script7 txt1 txt2) expected2

            let expected3 = RequireAnyOf
                    [ RequireSignatureOf obj1
                    , RequireSignatureOf obj2
                    , RequireSignatureOf obj3 ]
            valuesParserUnitTest (requireAnyOfParser parser) (script8 txt1 txt2 txt3) expected3
            valuesParserUnitTest (scriptParser parser) (script8 txt1 txt2 txt3) expected3

            let expected4 = RequireAnyOf
                    [ RequireSignatureOf obj1
                    , RequireAllOf
                      [ RequireSignatureOf obj2
                      , RequireSignatureOf obj3 ]
                    ]
            valuesParserUnitTest (requireAnyOfParser parser) (script9 txt1 txt2 txt3) expected4
            valuesParserUnitTest (scriptParser parser) (script9 txt1 txt2 txt3) expected4

            let expected5 = RequireAnyOf
                    [ RequireSignatureOf obj1
                    , RequireAllOf []
                    ]
            valuesParserUnitTest (requireAnyOfParser parser) (script13 txt1) expected5
            valuesParserUnitTest (scriptParser parser) (script13 txt1) expected5

    requireAtLeastOfParserTests
        :: (Eq a, Show a)
        => ReadP (Script a)
        -> [(a, Text)]
        -> SpecWith ()
    requireAtLeastOfParserTests parser objTxts = do
        let [(obj1, txt1),(obj2, txt2),(obj3, txt3)] = objTxts
        describe "requireAtLeastOfParser : unit tests" $ do
            let expected1 = RequireSomeOf 1
                    [ RequireSignatureOf obj1
                    , RequireSignatureOf obj2
                    , RequireSignatureOf obj3 ]
            valuesParserUnitTest (requireAtLeastOfParser parser) (script10 txt1 txt2 txt3) expected1
            valuesParserUnitTest (scriptParser parser) (script10 txt1 txt2 txt3) expected1

            let expected2 = RequireSomeOf 1
                    [ RequireSignatureOf obj1
                    , RequireAllOf
                      [ RequireSignatureOf obj2
                      , RequireSignatureOf obj3 ]
                    ]
            valuesParserUnitTest (requireAtLeastOfParser parser) (script11 txt1 txt2 txt3) expected2
            valuesParserUnitTest (scriptParser parser) (script11 txt1 txt2 txt3) expected2

    timelockParserTests
        :: (Eq a, Show a)
        => ReadP (Script a)
        -> (a, Text)
        -> SpecWith ()
    timelockParserTests parser (obj,txt) = do
       describe "validFromSlot unit test" $ do
           let expected = RequireAllOf
                   [ RequireSignatureOf obj
                   , ActiveFromSlot 120 ]
           valuesParserUnitTest (scriptParser parser) (script14 txt) expected

       describe "validUntilSlot unit test" $ do
           let expected = RequireAllOf
                   [ RequireSignatureOf obj
                   , ActiveUntilSlot 150 ]
           valuesParserUnitTest (scriptParser parser) (script15 txt) expected

       describe "validUntilSlot and validFromSlot unit test" $ do
           let expected = RequireAllOf
                   [ RequireSignatureOf obj
                   , ActiveFromSlot 120
                   , ActiveUntilSlot 125 ]
           valuesParserUnitTest (scriptParser parser) (script16 txt) expected

valuesParserUnitTest
    :: (Eq s, Show s)
    => ReadP s
    -> Text
    -> s
    -> SpecWith()
valuesParserUnitTest parser inp expected = it title $ do
    res <- case take 1 $ reverse $ readP_to_S parser (T.unpack inp) of
        [(res,_rest)] -> pure res
        err -> error $ "valuesParserUnitTest : "<>show err
    res `shouldBe` expected
    where
        title :: String
        title = mempty <> " input=" <> show inp

-- | Unsafe function to get bech32 data part from test data.
unBech32 :: Text -> ByteString
unBech32 = either (error "Incorrect bech32 in test data") snd
    . E.fromBech32 (const id)
    . T.encodeUtf8