{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
{-# OPTIONS_GHC -Wno-unused-top-binds #-}
module Language.Nominal.Properties.Examples.IdealisedEUTxOSpec
where
import Control.Monad (replicateM)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe (fromJust, isJust)
import Data.Proxy (Proxy (..))
import qualified Data.Set as S
import GHC.Generics
import Numeric.Partial.Semigroup
import Numeric.Partial.Monoid
import Test.QuickCheck
import Type.Reflection
import Language.Nominal.Examples.IdealisedEUTxO
import Language.Nominal.NameSet
import Language.Nominal.Properties.SpecUtilities (genEvFinMap)
import Language.Nominal.Properties.UnifySpec ()
import Language.Nominal.Unify
import Language.Nominal.Equivar
import Language.Nominal.Nom
import Language.Nominal.Binder
instance Arbitrary r => Arbitrary (Input r) where
arbitrary = Input <$> arbitrary <*> arbitrary
shrink (Input p r) = Input p <$> shrink r
instance Arbitrary (ValTriv r d) where
arbitrary = return ValTriv
shrink = const []
instance (Eq r, UnifyPerm r, Arbitrary r, Eq d, UnifyPerm d, Arbitrary d) => Arbitrary (ValFin r d) where
arbitrary = ValFin <$> genEvFinMap (scale (`div` 3) arbitrary) arbitrary
shrink (ValFin f) = ValFin <$> shrink f
instance (Arbitrary d, Arbitrary v) => Arbitrary (Output d v) where
arbitrary = Output <$> arbitrary <*> arbitrary <*> arbitrary
shrink (Output p d v) =
(Output p d <$> shrink v)
++ ((\d' -> Output p d' v) <$> shrink d)
instance (Arbitrary r, Arbitrary d, Arbitrary v) => Arbitrary (Transaction r d v) where
arbitrary = Transaction <$> arbitrary <*> arbitrary
shrink (Transaction is os) =
(Transaction is <$> shrink os)
++ (flip Transaction os <$> shrink is)
instance (Arbitrary r, Arbitrary d, Arbitrary v) => Arbitrary (Context r d v) where
arbitrary = Transaction <$> arbitrary <*> arbitrary
newtype ValidTx r d v = ValidTx (Transaction r d v) deriving (Show, Generic)
fromValidTx :: ValidTx r d v -> Transaction r d v
fromValidTx (ValidTx tx) = tx
instance (Arbitrary r, Arbitrary d, Arbitrary v) => Arbitrary (ValidTx r d v) where
arbitrary = do
tx <- arbitrary
return $ ValidTx $ go [tx]
where
go [] = Transaction [] []
go (tx : txs)
| transactionValid tx = tx
| otherwise = go $ take 10 $ txs ++ shrink tx
shrink (ValidTx tx) = ValidTx <$> shrink tx
instance ( Arbitrary r
, Support r
, Arbitrary d
, Support d
, Arbitrary v
, Validator r d v
) => Arbitrary (Chunk r d v) where
arbitrary = sized $ \size -> do
l <- choose (0, min limit size)
scale (min limit) $ go l
where
go :: Int -> Gen (Chunk r d v)
go 0 = return pzero
go l = do
valids <- replicateM 5 $ scale (min limit) arbitrary
chunk <- go $ pred l
go' valids chunk
go' [] chunk = return chunk
go' (ValidTx tx : txs) chunk = do
let c = unsafeSingletonChunk tx
let c1 = padd c chunk
c2 = padd chunk c
case (c1, c2) of
(Just chunk', _) -> return chunk'
(Nothing , Just chunk') -> return chunk'
(Nothing , Nothing) -> go' txs chunk
limit :: Int
limit = 15
shrink = const []
newtype SmallChunk r d v = SmallChunk (Chunk r d v)
deriving Show
instance Arbitrary (Chunk r d v) => Arbitrary (SmallChunk r d v) where
arbitrary = SmallChunk <$> scale (min 7) arbitrary
shrink (SmallChunk ch) = SmallChunk <$> shrink ch
class Validator r d v => Fixable r d v | v -> r d where
fixOutput :: Context r d v -> Output d v -> Output d v
instance (Support r, Support d) => Fixable r d (Val r d) where
fixOutput (Transaction (Input p _ :| _) _) (Output _ d _) =
Output p d $ Val $ EvFun $ const True
instance (Eq r, UnifyPerm r, Eq d, UnifyPerm d) => Fixable r d (ValFin r d) where
fixOutput c@(Transaction (Input p _ :| _) _) (Output _ d (ValFin f)) =
Output p d (ValFin $ extEvFinMap (d, c) True f)
instance (Support r, Support d) => Fixable r d (ValTriv r d) where
fixOutput (Transaction (Input p _ :| _) _) (Output _ d v) = Output p d v
fixChunkToBlockchain :: (Arbitrary d, Arbitrary v, Fixable r d v)
=> Chunk r d v -> Gen (Blockchain r d v)
fixChunkToBlockchain chunk
| isBlockchain chunk = return $ blockchain chunk
| otherwise = do
let utxcsCh' = utxcsOfChunk chunk
os' <- replicateM (resAppC length utxcsCh') arbitrary
let cs = exit $ utxcsCh'
let os = map (uncurry fixOutput) (zip cs os')
let genesis = unsafeSingletonChunk (Transaction [] os)
return . blockchain . fromJust $ chunk `padd` genesis
instance ( Arbitrary r
, Support r
, Arbitrary d
, Support d
, Arbitrary v
, Fixable r d v
) => Arbitrary (Blockchain r d v) where
arbitrary = arbitrary >>= fixChunkToBlockchain
shrink = const []
type TR = Int
type TD = Int
type TV = ValFin TR TD
type TC = Chunk TR TD TV
type SmallTC = SmallChunk TR TD TV
type TB = Blockchain TR TD TV
type TX = ValidTx TR TD TV
type TC' v = Chunk TR TD v
atValTriv :: Proxy (ValTriv TR TD)
atValTriv = Proxy
atValFin :: Proxy (ValFin TR TD)
atValFin = Proxy
type CVal v = (Typeable v, Show v, Arbitrary v, UnifyPerm v, Validator TR TD v)
data ValProxy where
ValProxy :: CVal v => Proxy v -> ValProxy
instance Show ValProxy where
show (ValProxy (Proxy :: Proxy v)) = "{" ++ show (typeRep :: TypeRep v) ++ "}"
instance Arbitrary ValProxy where
arbitrary = elements [ValProxy atValTriv, ValProxy atValFin]
shrink = const []
valTriv, valFin :: ValProxy
valTriv = ValProxy atValTriv
valFin = ValProxy atValFin
withVal :: (forall v proxy. CVal v => proxy v -> a) -> ValProxy -> a
withVal f (ValProxy p) = f p
prop_arbitraryChunkIsValid :: TC -> Bool
prop_arbitraryChunkIsValid = isChunk
prop_arbitraryChunkIsValid' :: TC -> Bool
prop_arbitraryChunkIsValid' = isChunk'
prop_arbitraryChunkEqCheck :: TC -> Bool
prop_arbitraryChunkEqCheck ch = ch == ch
prop_notEveryChunkBlockchain :: TC -> Property
prop_notEveryChunkBlockchain c = expectFailure $ isBlockchain c
prop_arbitraryTxIsValid :: TX -> Bool
prop_arbitraryTxIsValid = transactionValid . fromValidTx
prop_arbitraryBlockchainIsValid :: TB -> Bool
prop_arbitraryBlockchainIsValid = isBlockchain . getBlockchain
prop_blockchainToChunkAndBack :: TB -> Bool
prop_blockchainToChunkAndBack b = (blockchain . getBlockchain $ b) `unifiablePerm` b
prop_blockchainHasNoUTxIs :: TB -> Bool
prop_blockchainHasNoUTxIs b = null . utxisOfChunk . getBlockchain $ b
prop_chunkrefl :: CVal v => proxy v -> TC' v -> Bool
prop_chunkrefl _ ch = ch == ch
prop_chunkneq :: TC -> TC -> Property
prop_chunkneq ch ch' = expectFailure $ ch == ch'
prop_emptyIsPrefix :: TC -> Bool
prop_emptyIsPrefix ch = isPrefixChunk (return pzero) ch
prop_chunkTail_is_chunk :: TC -> Property
prop_chunkTail_is_chunk ch = chunkLength ch > 0 ==> isJust $ chunkTail ch
prop_chunkTail_is_prefix :: TC -> Property
prop_chunkTail_is_prefix ch = chunkLength ch > 0 ==> isPrefixChunk (fromJust $ chunkTail ch) ch
genChunkTail :: (UnifyPerm r, UnifyPerm d, UnifyPerm v, Validator r d v) => Chunk r d v -> Maybe (Chunk r d v)
genChunkTail = fmap exit . chunkTail
prop_chunkTail_is_prefix_gotcha :: TC -> Property
prop_chunkTail_is_prefix_gotcha ch = expectFailure $ chunkLength ch > 0 ==> isPrefixChunk (return . fromJust $ genChunkTail ch) ch
prop_warningNotChunkTail_is_not_chunk :: TC -> Property
prop_warningNotChunkTail_is_not_chunk ch = expectFailure $ chunkLength ch > 0 ==> fromJust $ isChunk <$> warningNotChunkTail ch
prop_underbinding :: TC -> Property
prop_underbinding ch = expectFailure $ isChunk $ ch @@! \_ txs -> Chunk (return txs)
prop_overbinding :: TC -> Property
prop_overbinding ch = expectFailure $ isChunk $ restrict (S.toList $ supp ch) ch
prop_chunkHead_chunkTail_recombine :: TC -> Property
prop_chunkHead_chunkTail_recombine ch = expectFailure $ chunkLength ch > 0 ==> unNom $ do
h <- fromJust $ chunkHead ch
t <- fromJust $ chunkTail ch
return $ Just ch == appendTxChunk h t
prop_chunkHdTl_recombine :: TC -> Property
prop_chunkHdTl_recombine ch = chunkLength ch > 0 ==> unNom $ do
(tx,ch') <- fromJust $ chunkToHdTl ch
return $ Just ch == appendTxChunk tx ch'
prop_reverseIsNotValid :: TC -> Property
prop_reverseIsNotValid ch = expectFailure $ isJust (reverseTxsOf ch)
prop_subchunksValid :: SmallChunk TR TD TV -> Bool
prop_subchunksValid (SmallChunk ch) = and . unNom $ map (isJust . txListToChunk) <$> (subTxListOf ch)
prop_apart_is_valid_tx :: TX -> TX -> Bool
prop_apart_is_valid_tx vtx1 vtx2 = unNom $ do
let [tx1, tx2] = fromValidTx <$> [vtx1, vtx2]
tx2' <- freshen tx2
return . isJust $ txListToChunk [tx1, tx2']
prop_apart_is_valid_ch :: TC -> TC -> Bool
prop_apart_is_valid_ch ch1 ch2 = unNom $ do
ch2' <- freshen ch2
return . isJust $ (Just ch1) <> (Just ch2')
observe :: Maybe TC -> Maybe TC -> Maybe TC -> Bool
observe mch1 mch2 mch3 =
isJust (mch3 <> mch1) == isJust (mch3 <> mch2)
&&
isJust (mch1 <> mch3) == isJust (mch2 <> mch3)
prop_chunk_apart_commutes :: SmallTC -> SmallTC -> SmallTC -> Bool
prop_chunk_apart_commutes (SmallChunk ch1) (SmallChunk ch2) (SmallChunk ch3) = unNom $ do
ch2' <- freshen ch2
return $ observe ((Just ch1) <> (Just ch2'))
((Just ch2') <> (Just ch1))
(Just ch3)
prop_validity_fresh :: TC -> Property
prop_validity_fresh ch = chunkLength ch > 1 ==> unNom $ do
(tx1, tx2, ch') <- fromJust $ chunkToHdHdTl ch
let mch2 = appendTxChunk tx1 ch'
return $ (isBlockchain ch && isBlockchain' mch2) <= (tx1 `apart` tx2)