1
1
{-# LANGUAGE BangPatterns #-}
2
2
{-# LANGUAGE DerivingVia #-}
3
- {-# LANGUAGE LambdaCase #-}
4
3
{-# LANGUAGE NamedFieldPuns #-}
5
4
{-# LANGUAGE OverloadedStrings #-}
6
5
{-# LANGUAGE RankNTypes #-}
@@ -41,6 +40,8 @@ import Network.DNS (Domain)
41
40
import Cardano.Binary
42
41
import Cardano.Slotting.Slot (SlotNo (.. ), WithOrigin (.. ))
43
42
import Ouroboros.Network.PeerSelection.LedgerPeers
43
+ import Ouroboros.Network.PeerSelection.LedgerPeers.Utils
44
+ (recomputeRelativeStake )
44
45
import Ouroboros.Network.PeerSelection.RelayAccessPoint
45
46
import Ouroboros.Network.PeerSelection.RootPeersDNS
46
47
import Ouroboros.Network.Testing.Data.Script
@@ -50,11 +51,13 @@ import Test.Tasty
50
51
import Test.Tasty.QuickCheck
51
52
import Text.Printf
52
53
54
+
53
55
tests :: TestTree
54
56
tests = testGroup " Ouroboros.Network.LedgerPeers"
55
57
[ testProperty " Pick 100%" prop_pick100
56
58
, testProperty " Pick" prop_pick
57
- , testProperty " accBigPoolStake" prop_accBigPoolStake
59
+ , testProperty " accumulateBigLedgerStake" prop_accumulateBigLedgerStake
60
+ , testProperty " recomputeRelativeStake" prop_recomputeRelativeStake
58
61
, testProperty " getLedgerPeers invariants" prop_getLedgerPeers
59
62
, testProperty " LedgerPeerSnapshot CBOR version 1" prop_ledgerPeerSnapshotCBORV1
60
63
, testProperty " LedgerPeerSnapshot JSON version 1" prop_ledgerPeerSnapshotJSONV1
@@ -105,7 +108,7 @@ newtype ArbitrarySlotNo =
105
108
-- of the tests we run.
106
109
instance Arbitrary ArbitrarySlotNo where
107
110
arbitrary =
108
- ArbitrarySlotNo . SlotNo <$> arbitrarySizedBoundedIntegral
111
+ ArbitrarySlotNo . SlotNo <$> arbitrary
109
112
110
113
data StakePool = StakePool {
111
114
spStake :: ! Word64
@@ -163,6 +166,93 @@ instance Arbitrary ArbLedgerPeersKind where
163
166
shrink (ArbLedgerPeersKind AllLedgerPeers ) = [ArbLedgerPeersKind BigLedgerPeers ]
164
167
shrink (ArbLedgerPeersKind BigLedgerPeers ) = []
165
168
169
+ newtype ArbStakeMapOverSource = ArbStakeMapOverSource { getArbStakeMapOverSource :: StakeMapOverSource }
170
+ deriving Show
171
+
172
+ instance Arbitrary ArbStakeMapOverSource where
173
+ arbitrary = do
174
+ peerSnapshot <-
175
+ oneof [ pure Nothing , Just <$> genPeerSnapshot ]
176
+ ledgerWithOrigin <- genWithOrigin
177
+ ula <- arbitrary
178
+ ledgerPeers <-
179
+ case (ula, ledgerWithOrigin) of
180
+ (Always , _) -> LedgerPeers TooOld . getLedgerPools <$> arbitrary
181
+ (After slotNo, Origin ) | slotNo > 0 -> return BeforeSlot
182
+ (After afterSlotNo, At atSlotNo)
183
+ | afterSlotNo <= atSlotNo -> LedgerPeers TooOld . getLedgerPools <$> arbitrary
184
+ _otherwise -> return BeforeSlot
185
+ (peerMap, bigPeerMap, cachedSlot) <-
186
+ return $ case peerSnapshot of
187
+ Nothing -> (Map. empty, Map. empty, Nothing )
188
+ Just (LedgerPeerSnapshotV1 (At slot, accPools))
189
+ -> (Map. fromList accPools, Map. fromList accPools, Just slot)
190
+ _otherwise -> error " impossible!"
191
+ return $ ArbStakeMapOverSource StakeMapOverSource {
192
+ ledgerWithOrigin,
193
+ ledgerPeers,
194
+ peerSnapshot,
195
+ peerMap,
196
+ bigPeerMap,
197
+ ula,
198
+ cachedSlot }
199
+ where
200
+ genWithOrigin = do
201
+ ArbitrarySlotNo slotNo <- arbitrary
202
+ return $ if slotNo == 0 then Origin else At slotNo
203
+ genPeerSnapshot = do
204
+ slotNo <- At . getPositive <$> arbitrary
205
+ pools <- accumulateBigLedgerStake . getLedgerPools <$> arbitrary
206
+ return $ LedgerPeerSnapshotV1 (slotNo, pools)
207
+
208
+ -- | This test checks whether requesting ledger peers works as intended
209
+ -- when snapshot data is available. For each request, peers must be returned from the right
210
+ -- source - either the ledger or snapshot, depending on whether which source is fresher.
211
+ --
212
+ prop_ledgerPeerSnapshot_requests :: ArbStakeMapOverSource
213
+ -> Property
214
+ prop_ledgerPeerSnapshot_requests ArbStakeMapOverSource {
215
+ getArbStakeMapOverSource = params@ StakeMapOverSource {
216
+ ledgerWithOrigin,
217
+ ledgerPeers,
218
+ peerSnapshot,
219
+ ula } } =
220
+ counterexample (unlines
221
+ [" Counterexample:" , " Ledger slot " ++ show ledgerWithOrigin,
222
+ " Ledger pools: " ++ show ledgerPeers,
223
+ " Snapshot? :" ++ show peerSnapshot,
224
+ " UseLedgerAfter: " ++ show ula]) $
225
+ let (poolMap, bigPoolMap, _slot) = stakeMapWithSlotOverSource params
226
+ bigPoolRelays = fmap (snd . snd ) . Map. toList $ bigPoolMap
227
+ poolRelays = fmap (snd . snd ) . Map. toList $ poolMap
228
+ in case (ledgerWithOrigin, ledgerPeers, peerSnapshot) of
229
+ (At t, LedgerPeers _ ledgerPools, Just (LedgerPeerSnapshot (At t', snapshotAccStake)))
230
+ | t' >= t ->
231
+ snapshotRelays === bigPoolRelays .&&. bigPoolRelays === poolRelays
232
+ | otherwise ->
233
+ bigPoolRelays === ledgerBigPoolRelays
234
+ .&&. poolRelays === ledgerRelays
235
+ where
236
+ snapshotRelays = fmap (snd . snd ) snapshotAccStake
237
+ ledgerBigPoolRelays = fmap (snd . snd ) (accumulateBigLedgerStake ledgerPools)
238
+ ledgerRelays = fmap (snd . snd ) . Map. toList $ accPoolStake ledgerPools
239
+
240
+ (_, LedgerPeers _ ledgerPools, Nothing ) ->
241
+ bigPoolRelays === ledgerBigPoolRelays
242
+ .&&. poolRelays === ledgerRelays
243
+ where
244
+ ledgerBigPoolRelays = fmap (snd . snd ) (accumulateBigLedgerStake ledgerPools)
245
+ ledgerRelays = fmap (snd . snd ) . Map. toList $ accPoolStake ledgerPools
246
+
247
+ (_, _, Just (LedgerPeerSnapshot (At t', snapshotAccStake)))
248
+ | After slot <- ula, t' >= slot ->
249
+ snapshotRelays === bigPoolRelays .&&. bigPoolRelays === poolRelays
250
+ where
251
+ snapshotRelays = fmap (snd . snd ) snapshotAccStake
252
+
253
+ _otherwise -> bigPoolRelays === [] .&&. poolRelays === []
254
+
255
+
166
256
-- | A pool with 100% stake should always be picked.
167
257
prop_pick100 :: Word16
168
258
-> NonNegative Int -- ^ number of pools with 0 stake
@@ -317,10 +407,9 @@ prop_pick (LedgerPools lps) (ArbLedgerPeersKind ledgerPeersKind) count seed (Moc
317
407
=== fromIntegral count `min` numOfPeers)
318
408
319
409
320
- prop_accBigPoolStake :: LedgerPools -> Property
321
- prop_accBigPoolStake (LedgerPools [] ) = property True
322
- prop_accBigPoolStake (LedgerPools lps@ (_: _)) =
323
-
410
+ prop_accumulateBigLedgerStake :: LedgerPools -> Property
411
+ prop_accumulateBigLedgerStake (LedgerPools [] ) = property True
412
+ prop_accumulateBigLedgerStake (LedgerPools lps@ (_: _)) =
324
413
-- the accumulated map is non empty, whenever ledger peers set is non
325
414
-- empty
326
415
not (Map. null accumulatedStakeMap)
@@ -334,7 +423,7 @@ prop_accBigPoolStake (LedgerPools lps@(_:_)) =
334
423
>= unAccPoolStake bigLedgerPeerQuota)
335
424
336
425
-- This property checks that elements of
337
- -- `accBigPoolStake ` form an initial sub-list of the ordered ledger
426
+ -- `accBigPoolStakeMap ` form an initial sub-list of the ordered ledger
338
427
-- peers by stake (from large to small).
339
428
--
340
429
-- We relay on the fact that `Map.elems` returns a list of elements
@@ -346,6 +435,37 @@ prop_accBigPoolStake (LedgerPools lps@(_:_)) =
346
435
where
347
436
accumulatedStakeMap = accBigPoolStakeMap lps
348
437
438
+ -- | This functions checks the following properties:
439
+ -- 1. The accumulated relative stake adds up to unity
440
+ -- 2. No pool relative stake can be less than 0
441
+ -- 3. The relays aren't mangled
442
+ -- 4. Running this function multiple times always produces the same result
443
+ --
444
+ prop_recomputeRelativeStake :: LedgerPools -> Property
445
+ prop_recomputeRelativeStake (LedgerPools [] ) = property True
446
+ prop_recomputeRelativeStake (LedgerPools lps) = property $ do
447
+ lpk <- genLedgerPeersKind
448
+ let (accStake, relayAccessPointsUnchangedNonNegativeStake) = go (reStake lpk) lps (0 , True )
449
+ return $ counterexample " recomputeRelativeStake: relays modified or negative pool stake calculated"
450
+ relayAccessPointsUnchangedNonNegativeStake
451
+ .&&. accStake === 1
452
+ .&&. counterexample " violates idempotency"
453
+ ((recomputeRelativeStake BigLedgerPeers . recomputeRelativeStake BigLedgerPeers $ lps) == recomputeRelativeStake BigLedgerPeers lps)
454
+ where
455
+ genLedgerPeersKind = elements [AllLedgerPeers , BigLedgerPeers ]
456
+ reStake lpk = recomputeRelativeStake lpk lps
457
+ -- compare relay access points in both lists for equality
458
+ -- where we assume that recomputerelativestake doesn't change
459
+ -- the order, and sum up relative stake to make sure it adds up to 1
460
+ go ((normPoolStake, raps): rest) ((_, raps'): rest') (accStake, _) =
461
+ if raps == raps' && normPoolStake >= 0
462
+ then go rest rest' (accStake + normPoolStake, True )
463
+ else (accStake + normPoolStake, False )
464
+ go [] (_: _) (accStake, _) = (accStake, False )
465
+ go (_: _) [] (accStake, _) = (accStake, False )
466
+ go _ _ (accStake, relayAccessPointsUnchangedNonNegativeStake) = (accStake, relayAccessPointsUnchangedNonNegativeStake)
467
+
468
+
349
469
prop_getLedgerPeers :: ArbitrarySlotNo
350
470
-> ArbitraryLedgerStateJudgement
351
471
-> LedgerPools
0 commit comments