{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | Test that the Peras certificate inclusion rules can correctly decide when
-- to include a certificate
--
-- NOTE: in this file, we use uncommon variable names such as `_A` because that
-- is their name in the CIP-0140, and we can't have variable names starting
-- with capital letters. Contrary to typical Haskell conventions, those do not
-- denote ignored variables.
module Test.Consensus.Peras.Cert.Inclusion (tests) where

import GHC.Generics (Generic)
import Ouroboros.Consensus.Block (WithOrigin (..))
import Ouroboros.Consensus.Block.SupportsPeras
  ( HasPerasCertRound (..)
  , PerasRoundNo (..)
  , getPerasCertRound
  )
import Ouroboros.Consensus.Peras.Cert.Inclusion
  ( LatestCertOnChainView (..)
  , LatestCertSeenView (..)
  , PerasCertInclusionRulesDecision (..)
  , PerasCertInclusionView (..)
  , needCert
  )
import Ouroboros.Consensus.Peras.Params
  ( PerasCertMaxRounds (..)
  , PerasParams (..)
  , mkPerasParams
  )
import Ouroboros.Consensus.Storage.PerasCertDB.API (PerasCertSnapshot (..))
import Ouroboros.Consensus.Util.Pred (Evidence (..))
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.QuickCheck
  ( Arbitrary (..)
  , Gen
  , Property
  , Testable (..)
  , choose
  , counterexample
  , forAll
  , frequency
  , tabulate
  , testProperty
  )
import Test.Util.QuickCheck (geometric)
import Test.Util.TestEnv (adjustQuickCheckTests)

{-------------------------------------------------------------------------------
  Tests
-------------------------------------------------------------------------------}

tests :: TestTree
tests :: TestTree
tests =
  (Int -> Int) -> TestTree -> TestTree
adjustQuickCheckTests (Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1000) (TestTree -> TestTree) -> TestTree -> TestTree
forall a b. (a -> b) -> a -> b
$
    TestName -> [TestTree] -> TestTree
testGroup
      TestName
"Peras certificate inclusion rules"
      [ TestName -> Property -> TestTree
forall a. Testable a => TestName -> a -> TestTree
testProperty TestName
"needCert" Property
prop_needCert
      ]

{-------------------------------------------------------------------------------
  Model conformance test property
-------------------------------------------------------------------------------}

data PerasCertInclusionRulesDecisionModel
  = PerasCertInclusionDecisionModel
  { PerasCertInclusionRulesDecisionModel -> Bool
shouldIncludeCert :: Bool
  , PerasCertInclusionRulesDecisionModel -> Bool
noCertsFromTwoRoundsAgo :: Bool
  , PerasCertInclusionRulesDecisionModel -> Bool
latestCertSeenIsNotExpired :: Bool
  , PerasCertInclusionRulesDecisionModel -> Bool
latestCertSeenIsNewerThanLatestCertOnChain :: Bool
  }

-- | A simplified model of the Peras certificate inclusion rules, used to compare
-- against the real implementation. The main difference is that this model computes
-- the result of the predicate directly over the inputs, rather than using the
-- 'Pred' combinators to produce evidence in either direction.
--
-- NOTE: this predicate could be lifted directly from the agda specification.
needCertModel ::
  PerasCertInclusionView TestCert TestBlk ->
  PerasCertInclusionRulesDecisionModel
needCertModel :: PerasCertInclusionView TestCert TestBlk
-> PerasCertInclusionRulesDecisionModel
needCertModel
  PerasCertInclusionView
    { PerasParams
perasParams :: PerasParams
perasParams :: forall cert blk. PerasCertInclusionView cert blk -> PerasParams
perasParams
    , PerasRoundNo
currRoundNo :: PerasRoundNo
currRoundNo :: forall cert blk. PerasCertInclusionView cert blk -> PerasRoundNo
currRoundNo
    , LatestCertSeenView TestCert
latestCertSeen :: LatestCertSeenView TestCert
latestCertSeen :: forall cert blk.
PerasCertInclusionView cert blk -> LatestCertSeenView cert
latestCertSeen
    , WithOrigin (LatestCertOnChainView TestCert)
latestCertOnChain :: WithOrigin (LatestCertOnChainView TestCert)
latestCertOnChain :: forall cert blk.
PerasCertInclusionView cert blk
-> WithOrigin (LatestCertOnChainView cert)
latestCertOnChain
    , PerasCertSnapshot TestBlk
certSnapshot :: PerasCertSnapshot TestBlk
certSnapshot :: forall cert blk.
PerasCertInclusionView cert blk -> PerasCertSnapshot blk
certSnapshot
    } =
    PerasCertInclusionDecisionModel
      { shouldIncludeCert :: Bool
shouldIncludeCert =
          Bool
noCertsFromTwoRoundsAgo
            Bool -> Bool -> Bool
&& Bool
latestCertSeenIsNotExpired
            Bool -> Bool -> Bool
&& Bool
latestCertSeenIsNewerThanLatestCertOnChain
      , noCertsFromTwoRoundsAgo :: Bool
noCertsFromTwoRoundsAgo =
          Bool
noCertsFromTwoRoundsAgo
      , latestCertSeenIsNotExpired :: Bool
latestCertSeenIsNotExpired =
          Bool
latestCertSeenIsNotExpired
      , latestCertSeenIsNewerThanLatestCertOnChain :: Bool
latestCertSeenIsNewerThanLatestCertOnChain =
          Bool
latestCertSeenIsNewerThanLatestCertOnChain
      }
   where
    noCertsFromTwoRoundsAgo :: Bool
noCertsFromTwoRoundsAgo =
      if PerasRoundNo
currRoundNo PerasRoundNo -> PerasRoundNo -> Bool
forall a. Ord a => a -> a -> Bool
< PerasRoundNo
2
        then Bool
False
        else Bool -> Bool
not (PerasCertSnapshot TestBlk -> PerasRoundNo -> Bool
forall blk. PerasCertSnapshot blk -> PerasRoundNo -> Bool
containsCert PerasCertSnapshot TestBlk
certSnapshot (PerasRoundNo
currRoundNo PerasRoundNo -> PerasRoundNo -> PerasRoundNo
forall a. Num a => a -> a -> a
- PerasRoundNo
2))

    latestCertSeenIsNotExpired :: Bool
latestCertSeenIsNotExpired =
      PerasRoundNo
currRoundNo
        PerasRoundNo -> PerasRoundNo -> Bool
forall a. Ord a => a -> a -> Bool
<= PerasRoundNo
_A PerasRoundNo -> PerasRoundNo -> PerasRoundNo
forall a. Num a => a -> a -> a
+ TestCert -> PerasRoundNo
forall cert. HasPerasCertRound cert => cert -> PerasRoundNo
getPerasCertRound (LatestCertSeenView TestCert -> TestCert
forall cert. LatestCertSeenView cert -> cert
lcsCert LatestCertSeenView TestCert
latestCertSeen)

    latestCertSeenIsNewerThanLatestCertOnChain :: Bool
latestCertSeenIsNewerThanLatestCertOnChain =
      case WithOrigin (LatestCertOnChainView TestCert)
latestCertOnChain of
        WithOrigin (LatestCertOnChainView TestCert)
Origin -> Bool
True
        NotOrigin LatestCertOnChainView TestCert
lcoc ->
          TestCert -> PerasRoundNo
forall cert. HasPerasCertRound cert => cert -> PerasRoundNo
getPerasCertRound (LatestCertSeenView TestCert -> TestCert
forall cert. LatestCertSeenView cert -> cert
lcsCert LatestCertSeenView TestCert
latestCertSeen) PerasRoundNo -> PerasRoundNo -> Bool
forall a. Ord a => a -> a -> Bool
> LatestCertOnChainView TestCert -> PerasRoundNo
forall cert. LatestCertOnChainView cert -> PerasRoundNo
lcocRoundNo LatestCertOnChainView TestCert
lcoc

    _A :: PerasRoundNo
_A =
      Word64 -> PerasRoundNo
PerasRoundNo (Word64 -> PerasRoundNo) -> Word64 -> PerasRoundNo
forall a b. (a -> b) -> a -> b
$
        PerasCertMaxRounds -> Word64
unPerasCertMaxRounds (PerasCertMaxRounds -> Word64) -> PerasCertMaxRounds -> Word64
forall a b. (a -> b) -> a -> b
$
          PerasParams -> PerasCertMaxRounds
perasCertMaxRounds (PerasParams -> PerasCertMaxRounds)
-> PerasParams -> PerasCertMaxRounds
forall a b. (a -> b) -> a -> b
$
            PerasParams
perasParams

-- | Test that the Peras certificate inclusion rules can correctly decide when
-- to include a certificate based on a simplified model that doesn't use anything
-- fancy to evaluate the rules.
prop_needCert :: Property
prop_needCert :: Property
prop_needCert = Gen (PerasCertInclusionView TestCert TestBlk)
-> (PerasCertInclusionView TestCert TestBlk -> Property)
-> Property
forall a prop.
(Show a, Testable prop) =>
Gen a -> (a -> prop) -> Property
forAll Gen (PerasCertInclusionView TestCert TestBlk)
genPerasCertInclusionView ((PerasCertInclusionView TestCert TestBlk -> Property) -> Property)
-> (PerasCertInclusionView TestCert TestBlk -> Property)
-> Property
forall a b. (a -> b) -> a -> b
$ \PerasCertInclusionView TestCert TestBlk
pciv -> do
  -- Determine whether we should include a cert according to the model
  let PerasCertInclusionDecisionModel
        { Bool
shouldIncludeCert :: PerasCertInclusionRulesDecisionModel -> Bool
shouldIncludeCert :: Bool
shouldIncludeCert
        , Bool
noCertsFromTwoRoundsAgo :: PerasCertInclusionRulesDecisionModel -> Bool
noCertsFromTwoRoundsAgo :: Bool
noCertsFromTwoRoundsAgo
        , Bool
latestCertSeenIsNotExpired :: PerasCertInclusionRulesDecisionModel -> Bool
latestCertSeenIsNotExpired :: Bool
latestCertSeenIsNotExpired
        , Bool
latestCertSeenIsNewerThanLatestCertOnChain :: PerasCertInclusionRulesDecisionModel -> Bool
latestCertSeenIsNewerThanLatestCertOnChain :: Bool
latestCertSeenIsNewerThanLatestCertOnChain
        } =
          PerasCertInclusionView TestCert TestBlk
-> PerasCertInclusionRulesDecisionModel
needCertModel PerasCertInclusionView TestCert TestBlk
pciv
  -- Some helper functions to report success/failure
  let chain :: [c -> c] -> c -> c
chain = (c -> [c -> c] -> c) -> [c -> c] -> c -> c
forall a b c. (a -> b -> c) -> b -> a -> c
flip (((c -> c) -> c -> c) -> c -> [c -> c] -> c
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (c -> c) -> c -> c
forall a b. (a -> b) -> a -> b
($)) ([c -> c] -> c -> c)
-> ([c -> c] -> [c -> c]) -> [c -> c] -> c -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [c -> c] -> [c -> c]
forall a. [a] -> [a]
reverse
  let ok :: TestName -> Property
ok TestName
desc =
        [Property -> Property] -> Property -> Property
forall {c}. [c -> c] -> c -> c
chain
          [ TestName -> [TestName] -> Property -> Property
forall prop.
Testable prop =>
TestName -> [TestName] -> prop -> Property
tabulate TestName
"NoCertsFromTwoRoundsAgo" [Bool -> TestName
forall a. Show a => a -> TestName
show Bool
noCertsFromTwoRoundsAgo]
          , TestName -> [TestName] -> Property -> Property
forall prop.
Testable prop =>
TestName -> [TestName] -> prop -> Property
tabulate TestName
"LatestCertSeenIsNotExpired" [Bool -> TestName
forall a. Show a => a -> TestName
show Bool
latestCertSeenIsNotExpired]
          , TestName -> [TestName] -> Property -> Property
forall prop.
Testable prop =>
TestName -> [TestName] -> prop -> Property
tabulate
              TestName
"LatestCertSeenIsNewerThanLatestCertOnChain"
              [Bool -> TestName
forall a. Show a => a -> TestName
show Bool
latestCertSeenIsNewerThanLatestCertOnChain]
          , TestName -> [TestName] -> Property -> Property
forall prop.
Testable prop =>
TestName -> [TestName] -> prop -> Property
tabulate
              TestName
"NoCertsFromTwoRoundsAgo|LatestCertSeenIsNotExpired|LatestCertSeenIsNewerThanLatestCertOnChain"
              [ (Bool, Bool, Bool) -> TestName
forall a. Show a => a -> TestName
show
                  ( Bool
noCertsFromTwoRoundsAgo
                  , Bool
latestCertSeenIsNotExpired
                  , Bool
latestCertSeenIsNewerThanLatestCertOnChain
                  )
              ]
          , TestName -> [TestName] -> Property -> Property
forall prop.
Testable prop =>
TestName -> [TestName] -> prop -> Property
tabulate TestName
"Should include cert according to model" [Bool -> TestName
forall a. Show a => a -> TestName
show Bool
shouldIncludeCert]
          , TestName -> [TestName] -> Property -> Property
forall prop.
Testable prop =>
TestName -> [TestName] -> prop -> Property
tabulate TestName
"Actual result" [TestName
desc]
          ]
          (Property -> Property) -> Property -> Property
forall a b. (a -> b) -> a -> b
$ Bool -> Property
forall prop. Testable prop => prop -> Property
property Bool
True
  let failure :: TestName -> Property
failure TestName
desc =
        TestName -> Property -> Property
forall prop. Testable prop => TestName -> prop -> Property
counterexample TestName
desc (Property -> Property) -> Property -> Property
forall a b. (a -> b) -> a -> b
$
          Bool -> Property
forall prop. Testable prop => prop -> Property
property Bool
False
  -- Now check that the real implementation agrees with the model
  let certInclusionDecision :: PerasCertInclusionRulesDecision TestCert
certInclusionDecision = PerasCertInclusionView TestCert TestBlk
-> PerasCertInclusionRulesDecision TestCert
forall cert blk.
PerasCertInclusionView cert blk
-> PerasCertInclusionRulesDecision cert
needCert PerasCertInclusionView TestCert TestBlk
pciv
  case PerasCertInclusionRulesDecision TestCert
certInclusionDecision of
    IncludeCert (ETrue Pred PerasCertInclusionRule
_includeCertReason) TestCert
_cert
      | Bool
shouldIncludeCert ->
          TestName -> Property
ok (TestName -> Property) -> TestName -> Property
forall a b. (a -> b) -> a -> b
$ PerasCertInclusionRulesDecision TestCert -> TestName
forall cert. PerasCertInclusionRulesDecision cert -> TestName
certInclusionDecisionTag PerasCertInclusionRulesDecision TestCert
certInclusionDecision
      | Bool
otherwise ->
          TestName -> Property
failure (TestName -> Property) -> TestName -> Property
forall a b. (a -> b) -> a -> b
$ TestName
"Expected not to include cert, but got: " TestName -> TestName -> TestName
forall a. Semigroup a => a -> a -> a
<> PerasCertInclusionRulesDecision TestCert -> TestName
forall a. Show a => a -> TestName
show PerasCertInclusionRulesDecision TestCert
certInclusionDecision
    DoNotIncludeCert (EFalse Pred PerasCertInclusionRule
_doNotIncludeCertReason)
      | Bool -> Bool
not Bool
shouldIncludeCert ->
          TestName -> Property
ok (TestName -> Property) -> TestName -> Property
forall a b. (a -> b) -> a -> b
$ PerasCertInclusionRulesDecision TestCert -> TestName
forall cert. PerasCertInclusionRulesDecision cert -> TestName
certInclusionDecisionTag PerasCertInclusionRulesDecision TestCert
certInclusionDecision
      | Bool
otherwise ->
          TestName -> Property
failure (TestName -> Property) -> TestName -> Property
forall a b. (a -> b) -> a -> b
$ TestName
"Expected to include cert, but got: " TestName -> TestName -> TestName
forall a. Semigroup a => a -> a -> a
<> PerasCertInclusionRulesDecision TestCert -> TestName
forall a. Show a => a -> TestName
show PerasCertInclusionRulesDecision TestCert
certInclusionDecision

-- | Tag for the certificate inclusion decision result, used for tabulation.
--
-- We use this instead of 'explainShallow' to avoid creating too many unique
-- strings for tabulation (explainShallow includes round numbers which would
-- create millions of distinct cases, making the tabulation output useless).
certInclusionDecisionTag :: PerasCertInclusionRulesDecision cert -> String
certInclusionDecisionTag :: forall cert. PerasCertInclusionRulesDecision cert -> TestName
certInclusionDecisionTag = \case
  IncludeCert{} -> TestName
"IncludeCert"
  DoNotIncludeCert{} -> TestName
"DoNotIncludeCert"

{-------------------------------------------------------------------------------
  Arbitrary helpers
-------------------------------------------------------------------------------}

-- * Peras parameters

-- NOTE: we use a geometric distribution to bias towards smaller values.
-- This increases the chance of covering all the inclusion rules more evenly,
-- while still allowing for larger values to be generated occasionally.
--
-- Moreover, geometric(0.5) + 1 means that:
--  - 50% chance of being 1
--  - 25% chance of being 2
--  - 12.5% chance of being 3
--  ... and so on
genPerasParams :: Gen PerasParams
genPerasParams :: Gen PerasParams
genPerasParams = do
  _A <- Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word64) -> (Int -> Int) -> Int -> Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int -> Word64) -> Gen Int -> Gen Word64
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Double -> Gen Int
geometric Double
0.5
  pure
    mkPerasParams
      { perasCertMaxRounds = PerasCertMaxRounds _A
      }

-- * Peras round numbers

-- | Generate a Peras round number.
--
-- We skew the distribution towards the first two rounds to cover the edge cases
-- in the certificate inclusion rules a bit more often.
genPerasRoundNo :: Gen PerasRoundNo
genPerasRoundNo :: Gen PerasRoundNo
genPerasRoundNo =
  [(Int, Gen PerasRoundNo)] -> Gen PerasRoundNo
forall a. HasCallStack => [(Int, Gen a)] -> Gen a
frequency
    [ (Int
1, PerasRoundNo -> Gen PerasRoundNo
forall a. a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word64 -> PerasRoundNo
PerasRoundNo Word64
0))
    , (Int
1, PerasRoundNo -> Gen PerasRoundNo
forall a. a -> Gen a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word64 -> PerasRoundNo
PerasRoundNo Word64
1))
    , (Int
8, Word64 -> PerasRoundNo
PerasRoundNo (Word64 -> PerasRoundNo) -> Gen Word64 -> Gen PerasRoundNo
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen Word64
forall a. Arbitrary a => Gen a
arbitrary)
    ]

-- * Mocked certificate type

-- | A mocked certificate type for testing, so we don't have to deal with
-- development changes in the real certificate type.
data TestCert
  = TestCert
  { TestCert -> PerasRoundNo
tcRoundNo :: PerasRoundNo
  }
  deriving (Int -> TestCert -> TestName -> TestName
[TestCert] -> TestName -> TestName
TestCert -> TestName
(Int -> TestCert -> TestName -> TestName)
-> (TestCert -> TestName)
-> ([TestCert] -> TestName -> TestName)
-> Show TestCert
forall a.
(Int -> a -> TestName -> TestName)
-> (a -> TestName) -> ([a] -> TestName -> TestName) -> Show a
$cshowsPrec :: Int -> TestCert -> TestName -> TestName
showsPrec :: Int -> TestCert -> TestName -> TestName
$cshow :: TestCert -> TestName
show :: TestCert -> TestName
$cshowList :: [TestCert] -> TestName -> TestName
showList :: [TestCert] -> TestName -> TestName
Show, TestCert -> TestCert -> Bool
(TestCert -> TestCert -> Bool)
-> (TestCert -> TestCert -> Bool) -> Eq TestCert
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TestCert -> TestCert -> Bool
== :: TestCert -> TestCert -> Bool
$c/= :: TestCert -> TestCert -> Bool
/= :: TestCert -> TestCert -> Bool
Eq, (forall x. TestCert -> Rep TestCert x)
-> (forall x. Rep TestCert x -> TestCert) -> Generic TestCert
forall x. Rep TestCert x -> TestCert
forall x. TestCert -> Rep TestCert x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. TestCert -> Rep TestCert x
from :: forall x. TestCert -> Rep TestCert x
$cto :: forall x. Rep TestCert x -> TestCert
to :: forall x. Rep TestCert x -> TestCert
Generic)

instance HasPerasCertRound TestCert where
  getPerasCertRound :: TestCert -> PerasRoundNo
getPerasCertRound = TestCert -> PerasRoundNo
tcRoundNo

-- | Generate a test certificate
--
-- NOTE: to improve the probabilities of covering all the paths in the code,
-- we generate certificates relative to a given Peras round (the current one).
genTestCert :: PerasRoundNo -> Gen TestCert
genTestCert :: PerasRoundNo -> Gen TestCert
genTestCert PerasRoundNo
roundNo = do
  offset <- forall a. Random a => (a, a) -> Gen a
choose @Integer (-Integer
10, Integer
3)
  -- NOTE: here we need to be careful not to underflow the round number
  let roundNo' =
        Word64 -> PerasRoundNo
PerasRoundNo (Word64 -> PerasRoundNo) -> Word64 -> PerasRoundNo
forall a b. (a -> b) -> a -> b
$
          Integer -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Word64) -> Integer -> Word64
forall a b. (a -> b) -> a -> b
$
            Integer -> Integer -> Integer
forall a. Ord a => a -> a -> a
max Integer
0 (Integer -> Integer) -> Integer -> Integer
forall a b. (a -> b) -> a -> b
$
              Word64 -> Integer
forall a. Integral a => a -> Integer
toInteger (PerasRoundNo -> Word64
unPerasRoundNo PerasRoundNo
roundNo) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
offset
  pure $
    TestCert
      { tcRoundNo = roundNo'
      }

-- * Mocked block type

-- | A mocked block type for testing
data TestBlk
  = TestBlk
  deriving (Int -> TestBlk -> TestName -> TestName
[TestBlk] -> TestName -> TestName
TestBlk -> TestName
(Int -> TestBlk -> TestName -> TestName)
-> (TestBlk -> TestName)
-> ([TestBlk] -> TestName -> TestName)
-> Show TestBlk
forall a.
(Int -> a -> TestName -> TestName)
-> (a -> TestName) -> ([a] -> TestName -> TestName) -> Show a
$cshowsPrec :: Int -> TestBlk -> TestName -> TestName
showsPrec :: Int -> TestBlk -> TestName -> TestName
$cshow :: TestBlk -> TestName
show :: TestBlk -> TestName
$cshowList :: [TestBlk] -> TestName -> TestName
showList :: [TestBlk] -> TestName -> TestName
Show, TestBlk -> TestBlk -> Bool
(TestBlk -> TestBlk -> Bool)
-> (TestBlk -> TestBlk -> Bool) -> Eq TestBlk
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TestBlk -> TestBlk -> Bool
== :: TestBlk -> TestBlk -> Bool
$c/= :: TestBlk -> TestBlk -> Bool
/= :: TestBlk -> TestBlk -> Bool
Eq, (forall x. TestBlk -> Rep TestBlk x)
-> (forall x. Rep TestBlk x -> TestBlk) -> Generic TestBlk
forall x. Rep TestBlk x -> TestBlk
forall x. TestBlk -> Rep TestBlk x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. TestBlk -> Rep TestBlk x
from :: forall x. TestBlk -> Rep TestBlk x
$cto :: forall x. Rep TestBlk x -> TestBlk
to :: forall x. Rep TestBlk x -> TestBlk
Generic)

-- * Certificate and inclusion views

genLatestCertSeen :: PerasRoundNo -> Gen (LatestCertSeenView TestCert)
genLatestCertSeen :: PerasRoundNo -> Gen (LatestCertSeenView TestCert)
genLatestCertSeen PerasRoundNo
roundNo = do
  cert <- PerasRoundNo -> Gen TestCert
genTestCert PerasRoundNo
roundNo
  pure
    LatestCertSeenView
      { lcsCert = cert
      , lcsCertRound = getPerasCertRound cert
      }

genLatestCertOnChain :: PerasRoundNo -> Gen (LatestCertOnChainView TestCert)
genLatestCertOnChain :: PerasRoundNo -> Gen (LatestCertOnChainView TestCert)
genLatestCertOnChain PerasRoundNo
roundNo = do
  cert <- PerasRoundNo -> Gen TestCert
genTestCert PerasRoundNo
roundNo
  let certRoundNo = TestCert -> PerasRoundNo
forall cert. HasPerasCertRound cert => cert -> PerasRoundNo
getPerasCertRound TestCert
cert
  pure $
    LatestCertOnChainView
      { lcocRoundNo = certRoundNo
      }

genPerasCertSnapshot :: PerasRoundNo -> Gen (PerasCertSnapshot TestBlk)
genPerasCertSnapshot :: PerasRoundNo -> Gen (PerasCertSnapshot TestBlk)
genPerasCertSnapshot PerasRoundNo
currRoundNo = do
  -- Decide whether to include a cert from two rounds ago
  containsCertFromTwoRoundsAgo <- Gen Bool
forall a. Arbitrary a => Gen a
arbitrary
  pure $
    PerasCertSnapshot
      { containsCert = \PerasRoundNo
roundNo ->
          Bool
containsCertFromTwoRoundsAgo
            Bool -> Bool -> Bool
&& PerasRoundNo
roundNo PerasRoundNo -> PerasRoundNo -> Bool
forall a. Eq a => a -> a -> Bool
== PerasRoundNo
currRoundNo PerasRoundNo -> PerasRoundNo -> PerasRoundNo
forall a. Num a => a -> a -> a
- PerasRoundNo
2
      , getCertsAfter = \PerasCertTicketNo
_ ->
          Map
  PerasCertTicketNo (WithArrivalTime (ValidatedPerasCert TestBlk))
forall a. Monoid a => a
mempty
      }

genPerasCertInclusionView :: Gen (PerasCertInclusionView TestCert TestBlk)
genPerasCertInclusionView :: Gen (PerasCertInclusionView TestCert TestBlk)
genPerasCertInclusionView = do
  perasParams <- Gen PerasParams
genPerasParams
  currRoundNo <- genPerasRoundNo
  latestCertSeen <- genLatestCertSeen currRoundNo
  latestCertOnChain <- genWithOrigin (genLatestCertOnChain currRoundNo)
  certSnapshopt <- genPerasCertSnapshot currRoundNo
  pure
    PerasCertInclusionView
      { perasParams
      , currRoundNo
      , latestCertSeen = latestCertSeen
      , latestCertOnChain = latestCertOnChain
      , certSnapshot = certSnapshopt
      }
 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)
      ]