@@ -12,9 +12,11 @@ module Test.Consensus.Genesis.Tests.LoP
1212 , testSuite
1313 ) where
1414
15+ import Cardano.Ledger.BaseTypes.NonZero (unNonZero )
1516import Data.Functor (($>) )
1617import Data.Ratio ((%) )
17- import Ouroboros.Consensus.Block.Abstract (Header )
18+ import Ouroboros.Consensus.Block.Abstract (Header , HeaderFields (.. ), unSlotNo )
19+ import Ouroboros.Consensus.Config.SecurityParam (SecurityParam (.. ))
1820import qualified Ouroboros.Consensus.MiniProtocol.ChainSync.Client as CSClient
1921import Ouroboros.Consensus.Util.IOLike
2022 ( DiffTime
@@ -26,7 +28,7 @@ import Ouroboros.Consensus.Util.LeakyBucket
2628 )
2729import Ouroboros.Network.AnchoredFragment
2830 ( AnchoredFragment
29- , HasHeader
31+ , HasHeader ( .. )
3032 )
3133import qualified Ouroboros.Network.AnchoredFragment as AF
3234import Test.Consensus.BlockTree (BlockTree (.. ), BlockTreeBranch (.. ))
@@ -49,6 +51,7 @@ import Test.Consensus.PointSchedule.SinglePeer
4951 , scheduleHeaderPoint
5052 , scheduleTipPoint
5153 )
54+ import Test.QuickCheck.Gen (suchThat )
5255import Test.Util.Orphans.IOLike ()
5356import Test.Util.PartialAccessors
5457
@@ -216,7 +219,7 @@ testServe description mustTimeout =
216219 adjustTestCount
217220 adjustMaxSize
218221 ( do
219- gt@ GenesisTest {gtBlockTree} <- genChains (pure 0 )
222+ gt@ GenesisTest {gtBlockTree} <- genChains (pure 0 ) `suchThat` hasAtLeastOneBlockPerEpoch
220223 let lbpRate = borderlineRate (AF. length (btTrunk gtBlockTree))
221224 ps = makeSchedule (btTrunk gtBlockTree)
222225 gt' = gt{gtLoPBucketParams = LoPBucketParams {lbpCapacity, lbpRate}}
@@ -279,7 +282,7 @@ testDelayAttack description lopEnabled =
279282 adjustTestCount
280283 adjustMaxSize
281284 ( do
282- gt@ GenesisTest {gtBlockTree} <- genChains (pure 1 )
285+ gt@ GenesisTest {gtBlockTree} <- genChains (pure 1 ) `suchThat` hasAtLeastOneBlockPerEpoch
283286 let gt' = gt{gtLoPBucketParams = LoPBucketParams {lbpCapacity = 10 , lbpRate = 1 }}
284287 ps = delaySchedule gtBlockTree
285288 pure $ gt' $> ps
@@ -354,3 +357,22 @@ testDelayAttack description lopEnabled =
354357 -- Wait for LoP bucket to empty
355358 psMinEndTime = Time 11
356359 in PointSchedule {psSchedule, psStartOrder = [] , psMinEndTime}
360+
361+ -- \| Ensure that the block tree has at least one block per epoch on all branches.
362+ -- Otherwise, issues would arise when trying to update the ledger from era n to era n+2.
363+ hasAtLeastOneBlockPerEpoch :: HasHeader blk => GenesisTest blk schedule -> Bool
364+ hasAtLeastOneBlockPerEpoch GenesisTest {gtBlockTree, gtSecurityParam} =
365+ all fragmentHasEnoughBlocks (btTrunk gtBlockTree : (btbFull <$> btBranches gtBlockTree))
366+ where
367+ k = unNonZero $ maxRollbacks gtSecurityParam
368+ -- \| This value comes from `defaultErasParams`, which is way out of scope from here.
369+ -- We should find a way to avoid repetition of this value, or at least centralize it.
370+ epochSize = 10 * k
371+ slotList frag = slotNo <$> AF. toOldestFirst frag
372+ slotNo = unSlotNo . headerFieldSlot . getHeaderFields
373+
374+ fragmentHasEnoughBlocks frag =
375+ all
376+ (\ (prev, next) -> next - prev <= epochSize)
377+ -- \| Add 0 at the beginning to simulate Origin
378+ (zip (0 : slotList frag) (slotList frag))
0 commit comments