Skip to content

Commit d3cc18c

Browse files
isovectornbloomfninioArtillero
committed
Generalize test machinery's reliance on TestBlock
Here we generalize all uses of unnecessarily-specified `TestBlock`s to instead be parametric `blk`s. For this we introduce two new type classes. The `IssueTestBlock` class abstracts the operations needed to produce blocks during chain generation. The `HasPointScheduleTestParams` class abstracts the parameters needed to run the point schedule tests. We invoke `getProtocolInfoArgs` at the beginning of a test, and use it to construct a `ProtocolInfo`, which in turn provides both a `TopLevelConfig` and an `ExtLedgerState`. The requirement of `IO` comes from needing to eventually parse cardano config files. A key point of entry for this change is found on `PeerSimulator.Run.nodeLifecycle`, where the `ProtocolInfo` is delivered, enabling the generalization of the simulation to arbitrary blocks. We provide instances for `TestBlock`. Co-authored-by: Nathan Bloomfield <nathan.bloomfield@tweag.io> Co-authored-by: Xavier Góngora <xavier.gongora@tweag.io>
1 parent 6f5dbf7 commit d3cc18c

File tree

16 files changed

+781
-397
lines changed

16 files changed

+781
-397
lines changed

ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/Genesis/Setup.hs

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE BlockArguments #-}
22
{-# LANGUAGE DerivingStrategies #-}
33
{-# LANGUAGE ExistentialQuantification #-}
4+
{-# LANGUAGE FlexibleContexts #-}
45
{-# LANGUAGE NamedFieldPuns #-}
56
{-# LANGUAGE RankNTypes #-}
67
{-# LANGUAGE ScopedTypeVariables #-}
@@ -19,14 +20,30 @@ import Control.Monad.Class.MonadAsync
1920
import Control.Monad.IOSim (IOSim, runSimStrictShutdown)
2021
import Control.Tracer (debugTracer, traceWith)
2122
import Data.Maybe (mapMaybe)
23+
import Ouroboros.Consensus.Block.Abstract (ConvertRawHash, Header)
24+
import Ouroboros.Consensus.Block.SupportsDiffusionPipelining
25+
( BlockSupportsDiffusionPipelining
26+
)
27+
import Ouroboros.Consensus.Config.SupportsNode (ConfigSupportsNode)
28+
import Ouroboros.Consensus.HardFork.Abstract
29+
import Ouroboros.Consensus.Ledger.Basics (LedgerState)
30+
import Ouroboros.Consensus.Ledger.Inspect (InspectLedger)
31+
import Ouroboros.Consensus.Ledger.SupportsProtocol
32+
( LedgerSupportsProtocol
33+
)
2234
import Ouroboros.Consensus.MiniProtocol.ChainSync.Client
2335
( ChainSyncClientException (..)
2436
)
37+
import qualified Ouroboros.Consensus.Storage.ChainDB.Impl as ChainDB
38+
import Ouroboros.Consensus.Storage.LedgerDB.API
39+
( CanUpgradeLedgerTables
40+
)
2541
import Ouroboros.Consensus.Util.Condense
2642
import Ouroboros.Consensus.Util.IOLike (Exception, fromException)
2743
import Ouroboros.Network.Driver.Limits
2844
( ProtocolLimitFailure (ExceededTimeLimit)
2945
)
46+
import Ouroboros.Network.Util.ShowProxy
3047
import Test.Consensus.Genesis.Setup.Classifiers
3148
( Classifiers (..)
3249
, ResultClassifiers (..)
@@ -36,16 +53,19 @@ import Test.Consensus.Genesis.Setup.Classifiers
3653
, scheduleClassifiers
3754
)
3855
import Test.Consensus.Genesis.Setup.GenChains
56+
import Test.Consensus.PeerSimulator.Config ()
3957
import Test.Consensus.PeerSimulator.Run
4058
import Test.Consensus.PeerSimulator.StateView
4159
import Test.Consensus.PeerSimulator.Trace
4260
( traceLinesWith
4361
, tracerTestBlock
4462
)
4563
import Test.Consensus.PointSchedule
64+
import Test.Consensus.PointSchedule.NodeState (NodeState)
4665
import Test.QuickCheck
4766
import Test.Util.Orphans.IOLike ()
4867
import Test.Util.QuickCheck (forAllGenRunShrinkCheck)
68+
import Test.Util.TersePrinting (Terse)
4969
import Test.Util.TestBlock (TestBlock)
5070
import Test.Util.Tracer (recordingTracerM)
5171
import Text.Printf (printf)
@@ -62,17 +82,37 @@ runSimStrictShutdownOrThrow action =
6282
-- | Runs the given 'GenesisTest' and 'PointSchedule' and evaluates the given
6383
-- property on the final 'StateView'.
6484
runGenesisTest ::
85+
( Condense (StateView blk)
86+
, CondenseList (NodeState blk)
87+
, ShowProxy blk
88+
, ShowProxy (Header blk)
89+
, ConfigSupportsNode blk
90+
, LedgerSupportsProtocol blk
91+
, ChainDB.SerialiseDiskConstraints blk
92+
, BlockSupportsDiffusionPipelining blk
93+
, InspectLedger blk
94+
, HasHardForkHistory blk
95+
, ConvertRawHash blk
96+
, CanUpgradeLedgerTables (LedgerState blk)
97+
, HasPointScheduleTestParams blk
98+
, Eq (Header blk)
99+
, Eq blk
100+
, Terse blk
101+
, Condense (NodeState blk)
102+
) =>
103+
ProtocolInfoArgs blk ->
65104
SchedulerConfig ->
66-
GenesisTestFull TestBlock ->
67-
RunGenesisTestResult
68-
runGenesisTest schedulerConfig genesisTest =
105+
GenesisTestFull blk ->
106+
RunGenesisTestResult blk
107+
runGenesisTest protocolInfoArgs schedulerConfig genesisTest =
69108
runSimStrictShutdownOrThrow $ do
70109
(recordingTracer, getTrace) <- recordingTracerM
71110
let tracer = if scDebug schedulerConfig then debugTracer else recordingTracer
72111

73112
traceLinesWith tracer $ prettyGenesisTest prettyPointSchedule genesisTest
74113

75-
rgtrStateView <- runPointSchedule schedulerConfig genesisTest =<< tracerTestBlock tracer
114+
rgtrStateView <-
115+
runPointSchedule protocolInfoArgs schedulerConfig genesisTest =<< tracerTestBlock tracer
76116
traceWith tracer (condense rgtrStateView)
77117
rgtrTrace <- unlines <$> getTrace
78118

@@ -87,24 +127,44 @@ runGenesisTest' ::
87127
GenesisTestFull TestBlock ->
88128
(StateView TestBlock -> prop) ->
89129
Property
90-
runGenesisTest' schedulerConfig genesisTest makeProperty =
91-
counterexample rgtrTrace $ makeProperty rgtrStateView
92-
where
93-
RunGenesisTestResult{rgtrTrace, rgtrStateView} =
94-
runGenesisTest schedulerConfig genesisTest
130+
runGenesisTest' schedulerConfig genesisTest makeProperty = idempotentIOProperty $ do
131+
protocolInfoArgs <- getProtocolInfoArgs
132+
let RunGenesisTestResult{rgtrTrace, rgtrStateView} =
133+
runGenesisTest protocolInfoArgs schedulerConfig genesisTest
134+
pure $ counterexample rgtrTrace $ makeProperty rgtrStateView
95135

96136
-- | All-in-one helper that generates a 'GenesisTest' and a 'Peers
97137
-- PeerSchedule', runs them with 'runGenesisTest', check whether the given
98138
-- property holds on the resulting 'StateView'.
99139
forAllGenesisTest ::
100-
Testable prop =>
101-
Gen (GenesisTestFull TestBlock) ->
140+
forall blk prop.
141+
( Testable prop
142+
, Condense (StateView blk)
143+
, CondenseList (NodeState blk)
144+
, ShowProxy blk
145+
, ShowProxy (Header blk)
146+
, ConfigSupportsNode blk
147+
, LedgerSupportsProtocol blk
148+
, ChainDB.SerialiseDiskConstraints blk
149+
, BlockSupportsDiffusionPipelining blk
150+
, InspectLedger blk
151+
, HasHardForkHistory blk
152+
, ConvertRawHash blk
153+
, CanUpgradeLedgerTables (LedgerState blk)
154+
, HasPointScheduleTestParams blk
155+
, Eq (Header blk)
156+
, Eq blk
157+
, Terse blk
158+
, Condense (NodeState blk)
159+
) =>
160+
Gen (GenesisTestFull blk) ->
102161
SchedulerConfig ->
103-
(GenesisTestFull TestBlock -> StateView TestBlock -> [GenesisTestFull TestBlock]) ->
104-
(GenesisTestFull TestBlock -> StateView TestBlock -> prop) ->
162+
(GenesisTestFull blk -> StateView blk -> [GenesisTestFull blk]) ->
163+
(GenesisTestFull blk -> StateView blk -> prop) ->
105164
Property
106-
forAllGenesisTest generator schedulerConfig shrinker mkProperty =
107-
forAllGenRunShrinkCheck generator runner shrinker' $ \genesisTest result ->
165+
forAllGenesisTest generator schedulerConfig shrinker mkProperty = idempotentIOProperty $ do
166+
protocolInfoArgs <- getProtocolInfoArgs
167+
pure $ forAllGenRunShrinkCheck generator (runGenesisTest protocolInfoArgs schedulerConfig) shrinker' $ \genesisTest result ->
108168
let cls = classifiers genesisTest
109169
resCls = resultClassifiers genesisTest result
110170
schCls = scheduleClassifiers genesisTest
@@ -128,7 +188,6 @@ forAllGenesisTest generator schedulerConfig shrinker mkProperty =
128188
$ counterexample (rgtrTrace result)
129189
$ mkProperty genesisTest stateView .&&. hasOnlyExpectedExceptions stateView
130190
where
131-
runner = runGenesisTest schedulerConfig
132191
shrinker' gt = shrinker gt . rgtrStateView
133192
hasOnlyExpectedExceptions StateView{svPeerSimulatorResults} =
134193
conjoin $

ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/Genesis/Setup/Classifiers.hs

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE LambdaCase #-}
22
{-# LANGUAGE NamedFieldPuns #-}
3+
{-# LANGUAGE ScopedTypeVariables #-}
34
{-# LANGUAGE TupleSections #-}
45
{-# LANGUAGE TypeFamilies #-}
56
{-# LANGUAGE TypeOperators #-}
@@ -21,14 +22,13 @@ import qualified Data.List.NonEmpty as NonEmpty
2122
import qualified Data.Map as Map
2223
import Data.Maybe (mapMaybe)
2324
import Data.Word (Word64)
24-
import Ouroboros.Consensus.Block
25+
import Ouroboros.Consensus.Block.Abstract
2526
( ChainHash (..)
27+
, HasHeader
2628
, HeaderHash
29+
, SlotNo (SlotNo)
2730
, blockSlot
2831
, succWithOrigin
29-
)
30-
import Ouroboros.Consensus.Block.Abstract
31-
( SlotNo (SlotNo)
3232
, withOrigin
3333
)
3434
import Ouroboros.Consensus.Config
@@ -45,7 +45,11 @@ import qualified Ouroboros.Network.AnchoredFragment as AF
4545
import Ouroboros.Network.Driver.Limits
4646
( ProtocolLimitFailure (ExceededTimeLimit)
4747
)
48-
import Test.Consensus.BlockTree (BlockTree (..), BlockTreeBranch (..))
48+
import Test.Consensus.BlockTree
49+
( BlockTree (..)
50+
, BlockTreeBranch (..)
51+
, isAncestorOf
52+
)
4953
import Test.Consensus.Network.AnchoredFragment.Extras (slotLength)
5054
import Test.Consensus.PeerSimulator.StateView
5155
( PeerSimulatorResult (..)
@@ -56,11 +60,7 @@ import Test.Consensus.PointSchedule
5660
import Test.Consensus.PointSchedule.Peers (PeerId (..), Peers (..))
5761
import Test.Consensus.PointSchedule.SinglePeer (SchedulePoint (..))
5862
import Test.Util.Orphans.IOLike ()
59-
import Test.Util.TestBlock
60-
( TestBlock
61-
, TestHash (TestHash)
62-
, isAncestorOf
63-
)
63+
import Test.Util.TestBlock (TestHash (TestHash))
6464

6565
-- | Interesting categories to classify test inputs
6666
data Classifiers
@@ -91,7 +91,7 @@ data Classifiers
9191
-- ^ The honest chain's slot count is greater than or equal to the Genesis window size.
9292
}
9393

94-
classifiers :: AF.HasHeader blk => GenesisTest blk schedule -> Classifiers
94+
classifiers :: HasHeader blk => GenesisTest blk schedule -> Classifiers
9595
classifiers GenesisTest{gtBlockTree, gtSecurityParam = SecurityParam k, gtGenesisWindow = GenesisWindow scg} =
9696
Classifiers
9797
{ existsSelectableAdversary
@@ -102,7 +102,7 @@ classifiers GenesisTest{gtBlockTree, gtSecurityParam = SecurityParam k, gtGenesi
102102
, longerThanGenesisWindow
103103
}
104104
where
105-
longerThanGenesisWindow = AF.headSlot goodChain >= At (fromIntegral scg)
105+
longerThanGenesisWindow = headSlot goodChain >= At (fromIntegral scg)
106106

107107
genesisWindowAfterIntersection =
108108
any fragmentHasGenesis branches
@@ -169,7 +169,7 @@ data ResultClassifiers
169169
nullResultClassifier :: ResultClassifiers
170170
nullResultClassifier = ResultClassifiers 0 0 0 0
171171

172-
resultClassifiers :: GenesisTestFull blk -> RunGenesisTestResult -> ResultClassifiers
172+
resultClassifiers :: GenesisTestFull blk -> RunGenesisTestResult blk -> ResultClassifiers
173173
resultClassifiers GenesisTest{gtSchedule} RunGenesisTestResult{rgtrStateView} =
174174
if adversariesCount > 0
175175
then
@@ -242,44 +242,31 @@ data ScheduleClassifiers
242242
-- do nothing afterwards.
243243
}
244244

245-
scheduleClassifiers :: GenesisTestFull TestBlock -> ScheduleClassifiers
246-
scheduleClassifiers GenesisTest{gtSchedule = schedule} =
245+
scheduleClassifiers ::
246+
forall blk. (HasHeader blk, Eq blk) => GenesisTestFull blk -> ScheduleClassifiers
247+
scheduleClassifiers GenesisTest{gtSchedule = schedule, gtBlockTree} =
247248
ScheduleClassifiers
248249
{ adversaryRollback
249250
, honestRollback
250251
, allAdversariesEmpty
251252
, allAdversariesTrivial
252253
}
253254
where
254-
hasRollback :: PeerSchedule TestBlock -> Bool
255+
hasRollback :: PeerSchedule blk -> Bool
255256
hasRollback peerSch' =
256257
any (not . isSorted) [tips, headers, blocks]
257258
where
258259
peerSch = sortOn fst peerSch'
260+
isSorted :: [WithOrigin blk] -> Bool
259261
isSorted l = and [x `ancestor` y | (x : y : _) <- tails l]
260-
ancestor Origin Origin = True
261-
ancestor Origin (At _) = True
262-
ancestor (At _) Origin = False
263-
ancestor (At p1) (At p2) = p1 `isAncestorOf` p2
264-
tips =
265-
mapMaybe
266-
( \(_, point) -> case point of
267-
ScheduleTipPoint blk -> Just blk
268-
_ -> Nothing
269-
)
270-
peerSch
271-
headers =
272-
mapMaybe
273-
( \(_, point) -> case point of
274-
ScheduleHeaderPoint blk -> Just blk
275-
_ -> Nothing
276-
)
277-
peerSch
278-
blocks =
279-
mapMaybe
262+
ancestor = isAncestorOf gtBlockTree
263+
tips, headers, blocks :: [WithOrigin blk]
264+
(tips, headers, blocks) =
265+
foldMap
280266
( \(_, point) -> case point of
281-
ScheduleBlockPoint blk -> Just blk
282-
_ -> Nothing
267+
ScheduleTipPoint blk -> (pure blk, mempty, mempty)
268+
ScheduleHeaderPoint blk -> (mempty, pure blk, mempty)
269+
ScheduleBlockPoint blk -> (mempty, mempty, pure blk)
283270
)
284271
peerSch
285272

@@ -291,7 +278,7 @@ scheduleClassifiers GenesisTest{gtSchedule = schedule} =
291278

292279
allAdversariesEmpty = all id $ adversarialPeers $ null <$> psSchedule schedule
293280

294-
isTrivial :: PeerSchedule TestBlock -> Bool
281+
isTrivial :: PeerSchedule blk -> Bool
295282
isTrivial = \case
296283
[] -> True
297284
(t0, _) : points -> all ((== t0) . fst) points

0 commit comments

Comments
 (0)