|
| 1 | +{-# LANGUAGE ViewPatterns #-} |
| 2 | + |
| 3 | +-- | Specification of Pos.Chain.Lrc.OBFT (which is basically a pure |
| 4 | +-- version of 'Pos.DB.Lrc.OBFT'). |
| 5 | + |
| 6 | +module Test.Pos.Chain.Lrc.ObftRoundRobinSpec |
| 7 | + ( spec |
| 8 | + ) where |
| 9 | + |
| 10 | +import Universum hiding (sort) |
| 11 | + |
| 12 | +import Data.List (scanl1) |
| 13 | +import Data.List.NonEmpty (sort, (!!)) |
| 14 | +import qualified Data.Set as S (deleteFindMin, fromList) |
| 15 | +import Test.Hspec (Spec, describe) |
| 16 | +import Test.Hspec.QuickCheck (modifyMaxSuccess, prop) |
| 17 | +import Test.QuickCheck (Arbitrary (..), Property, choose, suchThat, |
| 18 | + (===)) |
| 19 | + |
| 20 | +import Pos.Chain.Lrc (getEpochSlotLeaderScheduleObftPure, |
| 21 | + getSlotLeaderObftPure) |
| 22 | +import Pos.Core (EpochIndex, SlotCount, SlotId, StakesList, |
| 23 | + addressHash, flattenEpochOrSlot, mkCoin, unsafeAddCoin) |
| 24 | +import Pos.Crypto (PublicKey) |
| 25 | + |
| 26 | +import Test.Pos.Core.Arbitrary (genPositiveSlotCount) |
| 27 | + |
| 28 | +spec :: Spec |
| 29 | +spec = do |
| 30 | + describe "Pos.Chain.Lrc.OBFT" $ do |
| 31 | + describe "Round-robin" $ do |
| 32 | + modifyMaxSuccess (const 10000) $ do |
| 33 | + prop description_rrListLength |
| 34 | + (rrListLength <$> genPositiveSlotCount) |
| 35 | + prop description_rrCorrectSlotLeader |
| 36 | + (rrCorrectSlotLeader <$> genPositiveSlotCount) |
| 37 | + where |
| 38 | + description_rrListLength = |
| 39 | + "the amount of stakeholders is the same as the number of slots in an epoch" |
| 40 | + description_rrCorrectSlotLeader = |
| 41 | + "the correct slot leader is chosen given any epoch and slot" |
| 42 | + |
| 43 | +-- | Type used to generate random stakes and a 'PublicKey' that |
| 44 | +-- doesn't have any stake. |
| 45 | +-- |
| 46 | +-- Two necessarily different public keys are generated, as well as a list of |
| 47 | +-- public keys who will be our other stakeholders. To guarantee a non-empty |
| 48 | +-- stakes map, one of these public keys is inserted in the list, which is |
| 49 | +-- converted to a set and then to a map. |
| 50 | +newtype StakeAndHolder = StakeAndHolder |
| 51 | + { getNoStake :: (PublicKey, StakesList) |
| 52 | + } deriving Show |
| 53 | + |
| 54 | +instance Arbitrary StakeAndHolder where |
| 55 | + arbitrary = StakeAndHolder <$> do |
| 56 | + pk1 <- arbitrary |
| 57 | + pk2 <- arbitrary `suchThat` ((/=) pk1) |
| 58 | + listPks <- do |
| 59 | + n <- choose (2, 10) |
| 60 | + replicateM n arbitrary |
| 61 | + coins <- mkCoin <$> choose (1, 1000) |
| 62 | + let setPks :: Set PublicKey |
| 63 | + setPks = S.fromList $ pk1 : pk2 : listPks |
| 64 | + (myPk, restPks) = S.deleteFindMin setPks |
| 65 | + nRest = length restPks |
| 66 | + values = scanl1 unsafeAddCoin $ replicate nRest coins |
| 67 | + stakesList = map addressHash (toList restPks) `zip` values |
| 68 | + return (myPk, stakesList) |
| 69 | + |
| 70 | +rrListLength |
| 71 | + :: SlotCount |
| 72 | + -> EpochIndex |
| 73 | + -> StakeAndHolder |
| 74 | + -> Property |
| 75 | +rrListLength epochSlotCount epochIndex (getNoStake -> (_, stakes)) = do |
| 76 | + length (getEpochSlotLeaderScheduleObftPure epochIndex epochSlotCount stakeholders) |
| 77 | + === fromIntegral epochSlotCount |
| 78 | + where |
| 79 | + stakeholders = case nonEmpty (map fst stakes) of |
| 80 | + Just s -> s |
| 81 | + Nothing -> error "rrListLength: Empty list of stakeholders" |
| 82 | + |
| 83 | +rrCorrectSlotLeader |
| 84 | + :: SlotCount |
| 85 | + -> SlotId |
| 86 | + -> StakeAndHolder |
| 87 | + -> Property |
| 88 | +rrCorrectSlotLeader epochSlotCount slotId (getNoStake -> (_, stakes)) = do |
| 89 | + actualSlotLeader === expectedSlotLeader |
| 90 | + where |
| 91 | + stakeholders = case nonEmpty (map fst stakes) of |
| 92 | + Just s -> s |
| 93 | + Nothing -> error "rrCorrectSlotLeader: Empty list of stakeholders" |
| 94 | + flatSlotId = flattenEpochOrSlot epochSlotCount slotId |
| 95 | + expectedSlotLeaderIndex = |
| 96 | + (fromIntegral flatSlotId :: Int) `mod` (length stakeholders) |
| 97 | + expectedSlotLeader = (sort stakeholders) !! expectedSlotLeaderIndex |
| 98 | + actualSlotLeader = getSlotLeaderObftPure slotId epochSlotCount stakeholders |
0 commit comments