{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

-- | Test utilities for the V1 (production) Peras implementation.
module Test.Util.Peras.V1
  ( perasVoteIsPersistent
  , perasCertContainsOnlyPersistentVotes
  , genPerasVote
  , genPerasCert
  , tabulatePerasCert
  , tabulatePerasVote
  ) where

import Cardano.Crypto.Hash (ByteString)
import Cardano.Ledger.BaseTypes (SlotNo (..))
import Control.Monad (forM)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Short as ShortByteString
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Map.NonEmpty as NEMap
import Data.Maybe (catMaybes, fromMaybe)
import Data.Proxy (Proxy (..))
import Data.String (IsString (..))
import Data.Traversable (mapAccumM)
import Data.Word (Word8)
import GHC.Word (Word16)
import Ouroboros.Consensus.Block.Abstract (WithOrigin (..))
import Ouroboros.Consensus.Block.RealPoint (Bytes32RealPoint (..))
import qualified Ouroboros.Consensus.Committee.Crypto.BLS as BLS
import qualified Ouroboros.Consensus.Peras.Cert.V1 as V1
import Ouroboros.Consensus.Peras.Crypto.BLS
  ( PerasBLSCryptoAggregateVoteSignature (..)
  , VRFOutput (..)
  , VoteSignature (..)
  )
import Ouroboros.Consensus.Peras.Types
  ( PerasBoostedBlock (..)
  , PerasSeatIndex (..)
  )
import qualified Ouroboros.Consensus.Peras.Vote.V1 as V1
import Test.QuickCheck
  ( Arbitrary (..)
  , Gen
  , Property
  , choose
  , frequency
  , sized
  , tabulate
  , vectorOf
  )
import Test.Util.Peras.Common
  ( genRoundNo
  , genSeatIndex
  , mkBucket
  )

-- | Whether a Peras vote is a persistent one
perasVoteIsPersistent :: V1.PerasVote tag -> Bool
perasVoteIsPersistent :: forall tag. PerasVote tag -> Bool
perasVoteIsPersistent PerasVote tag
vote
  | V1.PersistentPerasVoteEligibilityProof{} <- PerasVote tag -> PerasVoteEligibilityProof
forall blk. PerasVote blk -> PerasVoteEligibilityProof
V1.pvEligibilityProof PerasVote tag
vote = Bool
True
  | Bool
otherwise = Bool
False

-- | Whether a Peras certificate only contains persistent votes
perasCertContainsOnlyPersistentVotes :: V1.PerasCert tag -> Bool
perasCertContainsOnlyPersistentVotes :: forall tag. PerasCert tag -> Bool
perasCertContainsOnlyPersistentVotes PerasCert tag
cert =
  (PerasVoteEligibilityProof -> Bool)
-> NonEmpty PerasVoteEligibilityProof -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all
    ( \case
        PerasVoteEligibilityProof
V1.PersistentPerasVoteEligibilityProof -> Bool
True
        V1.NonPersistentPerasVoteEligibilityProof{} -> Bool
False
    )
    ( NEMap PerasSeatIndex PerasVoteEligibilityProof
-> NonEmpty PerasVoteEligibilityProof
forall k a. NEMap k a -> NonEmpty a
NEMap.elems
        (NEMap PerasSeatIndex PerasVoteEligibilityProof
 -> NonEmpty PerasVoteEligibilityProof)
-> (PerasCert tag
    -> NEMap PerasSeatIndex PerasVoteEligibilityProof)
-> PerasCert tag
-> NonEmpty PerasVoteEligibilityProof
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PerasCertVoters -> NEMap PerasSeatIndex PerasVoteEligibilityProof
PerasCertVoters
-> NE (Map PerasSeatIndex PerasVoteEligibilityProof)
V1.unPerasCertVoters
        (PerasCertVoters -> NEMap PerasSeatIndex PerasVoteEligibilityProof)
-> (PerasCert tag -> PerasCertVoters)
-> PerasCert tag
-> NEMap PerasSeatIndex PerasVoteEligibilityProof
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PerasCert tag -> PerasCertVoters
forall blk. PerasCert blk -> PerasCertVoters
V1.pcVoters
        (PerasCert tag -> NonEmpty PerasVoteEligibilityProof)
-> PerasCert tag -> NonEmpty PerasVoteEligibilityProof
forall a b. (a -> b) -> a -> b
$ PerasCert tag
cert
    )

genBoostedBlock :: Gen PerasBoostedBlock
genBoostedBlock :: Gen PerasBoostedBlock
genBoostedBlock =
  WithOrigin Bytes32RealPoint -> PerasBoostedBlock
PerasBoostedBlock (WithOrigin Bytes32RealPoint -> PerasBoostedBlock)
-> Gen (WithOrigin Bytes32RealPoint) -> Gen PerasBoostedBlock
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen Bytes32RealPoint -> Gen (WithOrigin Bytes32RealPoint)
forall {t}. Gen t -> Gen (WithOrigin t)
genWithOrigin Gen Bytes32RealPoint
genBytes32RealPoint
 where
  genWithOrigin :: Gen t -> Gen (WithOrigin t)
genWithOrigin Gen t
gen =
    [(Int, Gen (WithOrigin t))] -> Gen (WithOrigin t)
forall a. HasCallStack => [(Int, Gen a)] -> Gen a
frequency
      [ (Int
1, WithOrigin t -> Gen (WithOrigin t)
forall a. a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure WithOrigin t
forall t. WithOrigin t
Origin)
      , (Int
9, t -> WithOrigin t
forall t. t -> WithOrigin t
NotOrigin (t -> WithOrigin t) -> Gen t -> Gen (WithOrigin t)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen t
gen)
      ]
  genBytes32RealPoint :: Gen Bytes32RealPoint
genBytes32RealPoint = do
    slotNo <- Word64 -> SlotNo
SlotNo (Word64 -> SlotNo) -> Gen Word64 -> Gen SlotNo
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen Word64
forall a. Arbitrary a => Gen a
arbitrary
    hash <- ShortByteString.pack <$> vectorOf 32 arbitrary
    pure $ Bytes32RealPoint slotNo hash

genPrivateKey :: Proxy r -> Gen (BLS.PrivateKey r)
genPrivateKey :: forall (r :: KeyRole). Proxy r -> Gen (PrivateKey r)
genPrivateKey Proxy r
_ =
  PrivateKey r -> Maybe (PrivateKey r) -> PrivateKey r
forall a. a -> Maybe a -> a
fromMaybe ([Char] -> PrivateKey r
forall a. HasCallStack => [Char] -> a
error [Char]
"genPrivateKey: invalid key bytes")
    (Maybe (PrivateKey r) -> PrivateKey r)
-> ([Word8] -> Maybe (PrivateKey r)) -> [Word8] -> PrivateKey r
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString -> Maybe (PrivateKey r)
forall (r :: KeyRole).
ByteString -> ByteString -> Maybe (PrivateKey r)
BLS.rawDeserialisePrivateKey ByteString
"ROUNDTRIP"
    (ByteString -> Maybe (PrivateKey r))
-> ([Word8] -> ByteString) -> [Word8] -> Maybe (PrivateKey r)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> ByteString
ByteString.pack
    ([Word8] -> PrivateKey r) -> Gen [Word8] -> Gen (PrivateKey r)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Gen Word8 -> Gen [Word8]
forall a. Int -> Gen a -> Gen [a]
vectorOf Int
32 (forall a. Arbitrary a => Gen a
arbitrary @Word8)

genSignature :: forall r. BLS.HasBLSContext r => Proxy r -> Gen (BLS.Signature r)
genSignature :: forall (r :: KeyRole).
HasBLSContext r =>
Proxy r -> Gen (Signature r)
genSignature Proxy r
_ = do
  key <- Proxy r -> Gen (PrivateKey r)
forall (r :: KeyRole). Proxy r -> Gen (PrivateKey r)
genPrivateKey (forall {k} (t :: k). Proxy t
forall (t :: KeyRole). Proxy t
Proxy @r)
  msg <- fromString @ByteString <$> arbitrary
  pure $ BLS.signWithRole key msg

genVoteEligibilityProof :: Bool -> Gen V1.PerasVoteEligibilityProof
genVoteEligibilityProof :: Bool -> Gen PerasVoteEligibilityProof
genVoteEligibilityProof Bool
shouldGenNonPersistent = do
  [(Int, Gen PerasVoteEligibilityProof)]
-> Gen PerasVoteEligibilityProof
forall a. HasCallStack => [(Int, Gen a)] -> Gen a
frequency
    [
      ( Int
4
      , PerasVoteEligibilityProof -> Gen PerasVoteEligibilityProof
forall a. a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure PerasVoteEligibilityProof
V1.PersistentPerasVoteEligibilityProof
      )
    ,
      ( if Bool
shouldGenNonPersistent then Int
1 else Int
0
      , VRFOutput PerasBLSCrypto -> PerasVoteEligibilityProof
V1.NonPersistentPerasVoteEligibilityProof
          (VRFOutput PerasBLSCrypto -> PerasVoteEligibilityProof)
-> (Signature VRF -> VRFOutput PerasBLSCrypto)
-> Signature VRF
-> PerasVoteEligibilityProof
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Signature VRF -> VRFOutput PerasBLSCrypto
PerasBLSCryptoVRFOutput
          (Signature VRF -> PerasVoteEligibilityProof)
-> Gen (Signature VRF) -> Gen PerasVoteEligibilityProof
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Proxy VRF -> Gen (Signature VRF)
forall (r :: KeyRole).
HasBLSContext r =>
Proxy r -> Gen (Signature r)
genSignature (forall {k} (t :: k). Proxy t
forall (t :: KeyRole). Proxy t
Proxy @BLS.VRF)
      )
    ]

genVoters :: Bool -> Gen V1.PerasCertVoters
genVoters :: Bool -> Gen PerasCertVoters
genVoters Bool
shouldGenNonPersistent = do
  numVoters <-
    (Int -> Gen Word16) -> Gen Word16
forall a. (Int -> Gen a) -> Gen a
sized ((Int -> Gen Word16) -> Gen Word16)
-> (Int -> Gen Word16) -> Gen Word16
forall a b. (a -> b) -> a -> b
$ \Int
size ->
      (Word16 -> Word16) -> Gen Word16 -> Gen Word16
forall a b. (a -> b) -> Gen a -> Gen b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
+ Word16
1) (Gen Word16 -> Gen Word16) -> Gen Word16 -> Gen Word16
forall a b. (a -> b) -> a -> b
$
        forall a. Random a => (a, a) -> Gen a
choose @Word16 (Word16
0, Int -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
* Word16
10)
  numPersistentVoters <-
    case shouldGenNonPersistent of
      Bool
True -> (Word16, Word16) -> Gen Word16
forall a. Random a => (a, a) -> Gen a
choose (Word16
0, Word16
numVoters)
      Bool
False -> Word16 -> Gen Word16
forall a. a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Word16
numVoters
  persistentVoters <-
    if numPersistentVoters == 0
      then pure []
      else forM [0 .. numPersistentVoters - 1] $ \Word16
i -> do
        let proof :: PerasVoteEligibilityProof
proof = PerasVoteEligibilityProof
V1.PersistentPerasVoteEligibilityProof
        (PerasSeatIndex, PerasVoteEligibilityProof)
-> Gen (PerasSeatIndex, PerasVoteEligibilityProof)
forall a. a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word16 -> PerasSeatIndex
PerasSeatIndex Word16
i, PerasVoteEligibilityProof
proof)
  nonPersistentVoters <-
    if numPersistentVoters == numVoters
      then pure []
      else forM [numPersistentVoters .. numVoters - 1] $ \Word16
i -> do
        proof <-
          VRFOutput PerasBLSCrypto -> PerasVoteEligibilityProof
V1.NonPersistentPerasVoteEligibilityProof
            (VRFOutput PerasBLSCrypto -> PerasVoteEligibilityProof)
-> (Signature VRF -> VRFOutput PerasBLSCrypto)
-> Signature VRF
-> PerasVoteEligibilityProof
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Signature VRF -> VRFOutput PerasBLSCrypto
PerasBLSCryptoVRFOutput
            (Signature VRF -> PerasVoteEligibilityProof)
-> Gen (Signature VRF) -> Gen PerasVoteEligibilityProof
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Proxy VRF -> Gen (Signature VRF)
forall (r :: KeyRole).
HasBLSContext r =>
Proxy r -> Gen (Signature r)
genSignature (forall {k} (t :: k). Proxy t
forall (t :: KeyRole). Proxy t
Proxy @BLS.VRF)
        pure (PerasSeatIndex i, proof)
  voters <-
    fmap (snd . fmap catMaybes)
      . mapAccumM
        ( \Bool
canDrop (PerasSeatIndex
i, PerasVoteEligibilityProof
proof) -> do
            voter <-
              [(Int, Gen (Maybe (PerasSeatIndex, PerasVoteEligibilityProof)))]
-> Gen (Maybe (PerasSeatIndex, PerasVoteEligibilityProof))
forall a. HasCallStack => [(Int, Gen a)] -> Gen a
frequency
                [ (Int
75, Maybe (PerasSeatIndex, PerasVoteEligibilityProof)
-> Gen (Maybe (PerasSeatIndex, PerasVoteEligibilityProof))
forall a. a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((PerasSeatIndex, PerasVoteEligibilityProof)
-> Maybe (PerasSeatIndex, PerasVoteEligibilityProof)
forall a. a -> Maybe a
Just (PerasSeatIndex
i, PerasVoteEligibilityProof
proof)))
                , (if Bool
canDrop then Int
25 else Int
0, Maybe (PerasSeatIndex, PerasVoteEligibilityProof)
-> Gen (Maybe (PerasSeatIndex, PerasVoteEligibilityProof))
forall a. a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (PerasSeatIndex, PerasVoteEligibilityProof)
forall a. Maybe a
Nothing)
                ]
            pure
              ( canDrop || voter == Nothing
              , voter
              )
        )
        False
      $ persistentVoters <> nonPersistentVoters
  pure $
    V1.PerasCertVoters (NEMap.fromList (NonEmpty.fromList voters))

genPerasVote :: Bool -> Gen (V1.PerasVote tag)
genPerasVote :: forall tag. Bool -> Gen (PerasVote tag)
genPerasVote Bool
shouldGenNonPersistent = do
  pvRoundNo <- Gen PerasRoundNo
genRoundNo
  pvBoostedBlock <- genBoostedBlock
  pvSeatIndex <- genSeatIndex
  pvEligibilityProof <- genVoteEligibilityProof shouldGenNonPersistent
  pvSignature <-
    PerasBLSCryptoVoteSignature
      <$> genSignature (Proxy @BLS.SIGN)
  pure
    V1.PerasVote
      { V1.pvRoundNo
      , V1.pvBoostedBlock
      , V1.pvSeatIndex
      , V1.pvEligibilityProof
      , V1.pvSignature
      }

genPerasCert :: Bool -> Gen (V1.PerasCert tag)
genPerasCert :: forall tag. Bool -> Gen (PerasCert tag)
genPerasCert Bool
shouldGenNonPersistent = do
  pcRoundNo <- Gen PerasRoundNo
genRoundNo
  pcBoostedBlock <- genBoostedBlock
  pcVoters <- genVoters shouldGenNonPersistent
  pcSignature <-
    PerasBLSCryptoAggregateVoteSignature
      <$> genSignature (Proxy @BLS.SIGN)
  pure
    V1.PerasCert
      { V1.pcRoundNo
      , V1.pcBoostedBlock
      , V1.pcVoters
      , V1.pcSignature
      }

tabulatePerasCert :: V1.PerasCert tag -> Property -> Property
tabulatePerasCert :: forall tag. PerasCert tag -> Property -> Property
tabulatePerasCert PerasCert tag
cert =
  ((Property -> Property)
 -> (Property -> Property) -> Property -> Property)
-> (Property -> Property)
-> [Property -> Property]
-> Property
-> Property
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (((Property -> Property)
 -> (Property -> Property) -> Property -> Property)
-> (Property -> Property)
-> (Property -> Property)
-> Property
-> Property
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Property -> Property)
-> (Property -> Property) -> Property -> Property
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.)) Property -> Property
forall a. a -> a
id ([Property -> Property] -> Property -> Property)
-> [Property -> Property] -> Property -> Property
forall a b. (a -> b) -> a -> b
$
    [ [Char] -> [[Char]] -> Property -> Property
forall prop.
Testable prop =>
[Char] -> [[Char]] -> prop -> Property
tabulate
        [Char]
"Number of voters"
        [Int -> Int -> [Char] -> [Char]
mkBucket Int
100 Int
numVoters [Char]
" voters"]
    , [Char] -> [[Char]] -> Property -> Property
forall prop.
Testable prop =>
[Char] -> [[Char]] -> prop -> Property
tabulate
        [Char]
"Proportion of persistent voters"
        [Int -> Int -> [Char] -> [Char]
mkBucket Int
10 Int
persistentVotersRatio [Char]
"%"]
    ]
 where
  numVoters :: Int
numVoters =
    NEMap PerasSeatIndex PerasVoteEligibilityProof -> Int
forall a. NEMap PerasSeatIndex a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length
      (NEMap PerasSeatIndex PerasVoteEligibilityProof -> Int)
-> (PerasCert tag
    -> NEMap PerasSeatIndex PerasVoteEligibilityProof)
-> PerasCert tag
-> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PerasCertVoters -> NEMap PerasSeatIndex PerasVoteEligibilityProof
PerasCertVoters
-> NE (Map PerasSeatIndex PerasVoteEligibilityProof)
V1.unPerasCertVoters
      (PerasCertVoters -> NEMap PerasSeatIndex PerasVoteEligibilityProof)
-> (PerasCert tag -> PerasCertVoters)
-> PerasCert tag
-> NEMap PerasSeatIndex PerasVoteEligibilityProof
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PerasCert tag -> PerasCertVoters
forall blk. PerasCert blk -> PerasCertVoters
V1.pcVoters
      (PerasCert tag -> Int) -> PerasCert tag -> Int
forall a b. (a -> b) -> a -> b
$ PerasCert tag
cert
  numPersistentVoters :: Int
numPersistentVoters =
    [PerasVoteEligibilityProof] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length
      ([PerasVoteEligibilityProof] -> Int)
-> (PerasCert tag -> [PerasVoteEligibilityProof])
-> PerasCert tag
-> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PerasVoteEligibilityProof -> Bool)
-> [PerasVoteEligibilityProof] -> [PerasVoteEligibilityProof]
forall a. (a -> Bool) -> [a] -> [a]
filter (PerasVoteEligibilityProof -> PerasVoteEligibilityProof -> Bool
forall a. Eq a => a -> a -> Bool
== PerasVoteEligibilityProof
V1.PersistentPerasVoteEligibilityProof)
      ([PerasVoteEligibilityProof] -> [PerasVoteEligibilityProof])
-> (PerasCert tag -> [PerasVoteEligibilityProof])
-> PerasCert tag
-> [PerasVoteEligibilityProof]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty PerasVoteEligibilityProof -> [PerasVoteEligibilityProof]
forall a. NonEmpty a -> [a]
NonEmpty.toList
      (NonEmpty PerasVoteEligibilityProof -> [PerasVoteEligibilityProof])
-> (PerasCert tag -> NonEmpty PerasVoteEligibilityProof)
-> PerasCert tag
-> [PerasVoteEligibilityProof]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NEMap PerasSeatIndex PerasVoteEligibilityProof
-> NonEmpty PerasVoteEligibilityProof
forall k a. NEMap k a -> NonEmpty a
NEMap.elems
      (NEMap PerasSeatIndex PerasVoteEligibilityProof
 -> NonEmpty PerasVoteEligibilityProof)
-> (PerasCert tag
    -> NEMap PerasSeatIndex PerasVoteEligibilityProof)
-> PerasCert tag
-> NonEmpty PerasVoteEligibilityProof
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PerasCertVoters -> NEMap PerasSeatIndex PerasVoteEligibilityProof
PerasCertVoters
-> NE (Map PerasSeatIndex PerasVoteEligibilityProof)
V1.unPerasCertVoters
      (PerasCertVoters -> NEMap PerasSeatIndex PerasVoteEligibilityProof)
-> (PerasCert tag -> PerasCertVoters)
-> PerasCert tag
-> NEMap PerasSeatIndex PerasVoteEligibilityProof
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PerasCert tag -> PerasCertVoters
forall blk. PerasCert blk -> PerasCertVoters
V1.pcVoters
      (PerasCert tag -> Int) -> PerasCert tag -> Int
forall a b. (a -> b) -> a -> b
$ PerasCert tag
cert

  persistentVotersRatio :: Int
persistentVotersRatio
    | Int
numVoters Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = Int
0
    | Bool
otherwise = Int
numPersistentVoters Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
100 Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
numVoters

tabulatePerasVote :: V1.PerasVote tag -> Property -> Property
tabulatePerasVote :: forall tag. PerasVote tag -> Property -> Property
tabulatePerasVote PerasVote tag
vote =
  ((Property -> Property)
 -> (Property -> Property) -> Property -> Property)
-> (Property -> Property)
-> [Property -> Property]
-> Property
-> Property
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (((Property -> Property)
 -> (Property -> Property) -> Property -> Property)
-> (Property -> Property)
-> (Property -> Property)
-> Property
-> Property
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Property -> Property)
-> (Property -> Property) -> Property -> Property
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.)) Property -> Property
forall a. a -> a
id ([Property -> Property] -> Property -> Property)
-> [Property -> Property] -> Property -> Property
forall a b. (a -> b) -> a -> b
$
    [ [Char] -> [[Char]] -> Property -> Property
forall prop.
Testable prop =>
[Char] -> [[Char]] -> prop -> Property
tabulate
        [Char]
"Voter type"
        [[Char]
voterType]
    ]
 where
  voterType :: [Char]
voterType =
    case PerasVote tag -> PerasVoteEligibilityProof
forall blk. PerasVote blk -> PerasVoteEligibilityProof
V1.pvEligibilityProof PerasVote tag
vote of
      PerasVoteEligibilityProof
V1.PersistentPerasVoteEligibilityProof -> [Char]
"persistent"
      V1.NonPersistentPerasVoteEligibilityProof VRFOutput PerasBLSCrypto
_ -> [Char]
"non-persistent"