|
| 1 | +{-# LANGUAGE Rank2Types #-} |
1 | 2 | {-# LANGUAGE TypeFamilies #-}
|
2 | 3 |
|
3 | 4 | -- | Type classes for Poll abstraction.
|
4 | 5 |
|
5 | 6 | module Pos.Update.Poll.Class
|
6 | 7 | ( MonadPollRead (..)
|
7 | 8 | , MonadPoll (..)
|
| 9 | + |
| 10 | + -- Roll Transformer |
| 11 | + , RollT |
| 12 | + , runRollT |
| 13 | + , execRollT |
| 14 | + |
| 15 | + -- Poll Transformer |
| 16 | + , PollT |
| 17 | + , runPollT |
| 18 | + , evalPollT |
| 19 | + , execPollT |
8 | 20 | ) where
|
9 | 21 |
|
10 |
| -import Universum |
| 22 | +import Universum hiding (id) |
11 | 23 |
|
| 24 | +import Control.Lens (uses, (%=), (.=)) |
12 | 25 | import Control.Monad.Trans (MonadTrans)
|
13 |
| -import System.Wlog (WithLogger) |
| 26 | +import Data.Default (def) |
| 27 | +import qualified Data.HashMap.Strict as HM |
| 28 | +import qualified Data.HashSet as HS |
| 29 | +import qualified Data.List as List (find) |
| 30 | +import qualified Ether |
| 31 | +import System.Wlog (WithLogger, logWarning) |
14 | 32 |
|
15 | 33 | import Pos.Core (ApplicationName, BlockVersion, BlockVersionData,
|
16 | 34 | ChainDifficulty, Coin, EpochIndex, NumSoftwareVersion,
|
17 |
| - SlotId, SoftwareVersion, StakeholderId) |
| 35 | + SlotId, SoftwareVersion, StakeholderId, addressHash) |
18 | 36 | import Pos.Core.Slotting (SlottingData)
|
19 |
| -import Pos.Core.Update (UpId) |
| 37 | +import Pos.Core.Update (SoftwareVersion (..), UpId, |
| 38 | + UpdateProposal (..)) |
| 39 | +import Pos.Crypto (hash) |
| 40 | +import Pos.Update.BlockVersion (applyBVM) |
| 41 | +import Pos.Update.Poll.Modifier (PollModifier (..), pmActivePropsL, |
| 42 | + pmAdoptedBVFullL, pmBVsL, pmConfirmedL, pmConfirmedPropsL, |
| 43 | + pmEpochProposersL, pmSlottingDataL) |
20 | 44 | import Pos.Update.Poll.Types (BlockVersionState,
|
21 |
| - ConfirmedProposalState, DecidedProposalState, |
22 |
| - ProposalState, UndecidedProposalState) |
| 45 | + BlockVersionState (..), ConfirmedProposalState, |
| 46 | + DecidedProposalState (..), PrevValue, ProposalState (..), |
| 47 | + USUndo (..), UndecidedProposalState (..), bvsIsConfirmed, |
| 48 | + cpsSoftwareVersion, maybeToPrev, psProposal, unChangedBVL, |
| 49 | + unChangedConfPropsL, unChangedPropsL, unChangedSVL, |
| 50 | + unLastAdoptedBVL, unPrevProposersL, unSlottingDataL) |
| 51 | +import qualified Pos.Util.Modifier as MM |
| 52 | +import Pos.Util.Util (ether) |
23 | 53 |
|
24 | 54 | ----------------------------------------------------------------------------
|
25 | 55 | -- Read-only
|
@@ -137,3 +167,210 @@ instance {-# OVERLAPPABLE #-}
|
137 | 167 | deactivateProposal = lift . deactivateProposal
|
138 | 168 | setSlottingData = lift . setSlottingData
|
139 | 169 | setEpochProposers = lift . setEpochProposers
|
| 170 | + |
| 171 | +---------------------------------------------------------------------------- |
| 172 | +-- Roll Transformer |
| 173 | +---------------------------------------------------------------------------- |
| 174 | + |
| 175 | +type RollT m = Ether.LazyStateT' USUndo m |
| 176 | + |
| 177 | +-- | Monad transformer which stores USUndo and implements writable |
| 178 | +-- MonadPoll. Its purpose is to collect data necessary for rollback. |
| 179 | +-- |
| 180 | +-- [WARNING] This transformer uses StateT and is intended for |
| 181 | +-- single-threaded usage only. |
| 182 | +instance (MonadPoll m) => MonadPoll (RollT m) where |
| 183 | + putBVState bv sv = ether $ do |
| 184 | + insertIfNotExist bv unChangedBVL getBVState |
| 185 | + putBVState bv sv |
| 186 | + |
| 187 | + delBVState bv = ether $ do |
| 188 | + insertIfNotExist bv unChangedBVL getBVState |
| 189 | + delBVState bv |
| 190 | + |
| 191 | + setAdoptedBV = setValueWrapper unLastAdoptedBVL getAdoptedBV setAdoptedBV |
| 192 | + |
| 193 | + setLastConfirmedSV sv@SoftwareVersion{..} = ether $ do |
| 194 | + insertIfNotExist svAppName unChangedSVL getLastConfirmedSV |
| 195 | + setLastConfirmedSV sv |
| 196 | + |
| 197 | + -- can't be called during apply |
| 198 | + delConfirmedSV = lift . delConfirmedSV |
| 199 | + |
| 200 | + addConfirmedProposal cps = ether $ do |
| 201 | + confProps <- getConfirmedProposals |
| 202 | + insertIfNotExist (cpsSoftwareVersion cps) unChangedConfPropsL (getter confProps) |
| 203 | + addConfirmedProposal cps |
| 204 | + where |
| 205 | + getter confs sv = pure $ List.find (\x -> cpsSoftwareVersion x == sv) confs |
| 206 | + |
| 207 | + -- can't be called during apply |
| 208 | + delConfirmedProposal = lift . delConfirmedProposal |
| 209 | + |
| 210 | + insertActiveProposal ps = ether $ do |
| 211 | + whenNothingM_ (use unPrevProposersL) $ do |
| 212 | + prev <- getEpochProposers |
| 213 | + unPrevProposersL .= Just prev |
| 214 | + insertIfNotExist (hash $ psProposal $ ps) unChangedPropsL getProposal |
| 215 | + insertActiveProposal ps |
| 216 | + |
| 217 | + deactivateProposal id = ether $ do |
| 218 | + -- Proposer still can't propose new updates in the current epoch |
| 219 | + -- even if his update was deactivated in the same epoch |
| 220 | + insertIfNotExist id unChangedPropsL getProposal |
| 221 | + deactivateProposal id |
| 222 | + |
| 223 | + setSlottingData = |
| 224 | + setValueWrapper unSlottingDataL getSlottingData setSlottingData |
| 225 | + setEpochProposers = |
| 226 | + setValueWrapper unPrevProposersL getEpochProposers setEpochProposers |
| 227 | + |
| 228 | +-- This is a convenient wrapper for functions which should set some |
| 229 | +-- value and this change should be recorded in USUndo. If change of |
| 230 | +-- such kind is already recorded in 'USUndo', then we don't record it |
| 231 | +-- and just propagate the new value to the underlying 'MonadPoll'. If |
| 232 | +-- it is not recorded, we put old value into 'USUndo' before |
| 233 | +-- propagating the new value. |
| 234 | +setValueWrapper :: |
| 235 | + MonadPoll m |
| 236 | + => Lens' USUndo (Maybe a) |
| 237 | + -> m a |
| 238 | + -> (a -> m ()) |
| 239 | + -> a |
| 240 | + -> RollT m () |
| 241 | +setValueWrapper lens getAction setAction value = ether $ do |
| 242 | + whenNothingM_ (use lens) $ do |
| 243 | + prev <- lift getAction |
| 244 | + lens .= Just prev |
| 245 | + lift (setAction value) |
| 246 | + |
| 247 | +insertIfNotExist |
| 248 | + :: (Eq a, Hashable a, MonadState USUndo m) |
| 249 | + => a |
| 250 | + -> Lens' USUndo (HashMap a (PrevValue b)) |
| 251 | + -> (a -> m (Maybe b)) |
| 252 | + -> m () |
| 253 | +insertIfNotExist id setter getter = do |
| 254 | + whenNothingM_ (HM.lookup id <$> use setter) $ do |
| 255 | + prev <- getter id |
| 256 | + setter %= HM.insert id (maybeToPrev prev) |
| 257 | + |
| 258 | +runRollT :: RollT m a -> m (a, USUndo) |
| 259 | +runRollT = flip Ether.runLazyStateT def |
| 260 | + |
| 261 | +execRollT :: Monad m => RollT m a -> m USUndo |
| 262 | +execRollT = flip Ether.execLazyStateT def |
| 263 | + |
| 264 | +---------------------------------------------------------------------------- |
| 265 | +-- PollT Transformer |
| 266 | +---------------------------------------------------------------------------- |
| 267 | + |
| 268 | +-- | Monad transformer which stores PollModifier and implements |
| 269 | +-- writable MonadPoll. |
| 270 | +-- |
| 271 | +-- [WARNING] This transformer uses StateT and is intended for |
| 272 | +-- single-threaded usage only. |
| 273 | +type PollT = Ether.LazyStateT' PollModifier |
| 274 | + |
| 275 | +runPollT :: PollModifier -> PollT m a -> m (a, PollModifier) |
| 276 | +runPollT = flip Ether.runLazyStateT |
| 277 | + |
| 278 | +evalPollT :: Monad m => PollModifier -> PollT m a -> m a |
| 279 | +evalPollT = flip Ether.evalLazyStateT |
| 280 | + |
| 281 | +execPollT :: Monad m => PollModifier -> PollT m a -> m PollModifier |
| 282 | +execPollT = flip Ether.execLazyStateT |
| 283 | + |
| 284 | +instance (MonadPollRead m) => |
| 285 | + MonadPollRead (PollT m) where |
| 286 | + getBVState pv = ether $ |
| 287 | + MM.lookupM getBVState pv =<< use pmBVsL |
| 288 | + getProposedBVs = ether $ |
| 289 | + MM.keysM getProposedBVs =<< use pmBVsL |
| 290 | + getEpochProposers = ether $ do |
| 291 | + new <- use pmEpochProposersL |
| 292 | + maybe getEpochProposers pure new |
| 293 | + getCompetingBVStates = ether $ |
| 294 | + filter (bvsIsConfirmed . snd) <$> |
| 295 | + (MM.toListM getCompetingBVStates =<< use pmBVsL) |
| 296 | + getAdoptedBVFull = ether $ |
| 297 | + maybe getAdoptedBVFull pure =<< use pmAdoptedBVFullL |
| 298 | + getLastConfirmedSV appName = ether $ |
| 299 | + MM.lookupM getLastConfirmedSV appName =<< use pmConfirmedL |
| 300 | + getProposal upId = ether $ |
| 301 | + MM.lookupM getProposal upId =<< use pmActivePropsL |
| 302 | + getProposalsByApp app = ether $ do |
| 303 | + let eqApp = (== app) . svAppName . upSoftwareVersion . psProposal . snd |
| 304 | + props <- uses pmActivePropsL (filter eqApp . MM.insertions) |
| 305 | + dbProps <- map (first (hash . psProposal) . join (,)) <$> getProposalsByApp app |
| 306 | + pure . toList . HM.fromList $ dbProps ++ props -- squash props with same upId |
| 307 | + getConfirmedProposals = ether $ |
| 308 | + MM.valuesM |
| 309 | + (map (first cpsSoftwareVersion . join (,)) <$> getConfirmedProposals) =<< |
| 310 | + use pmConfirmedPropsL |
| 311 | + getEpochTotalStake = lift . getEpochTotalStake |
| 312 | + getRichmanStake e = lift . getRichmanStake e |
| 313 | + getOldProposals sl = ether $ |
| 314 | + map snd <$> |
| 315 | + (MM.mapMaybeM getOldProposalPairs extractOld =<< use pmActivePropsL) |
| 316 | + where |
| 317 | + extractOld (PSUndecided ups) |
| 318 | + | upsSlot ups <= sl = Just ups |
| 319 | + | otherwise = Nothing |
| 320 | + extractOld (PSDecided _) = Nothing |
| 321 | + getOldProposalPairs = |
| 322 | + map (\ups -> (hash $ upsProposal ups, ups)) <$> getOldProposals sl |
| 323 | + getDeepProposals cd = ether $ |
| 324 | + map snd <$> |
| 325 | + (MM.mapMaybeM getDeepProposalPairs extractDeep =<< use pmActivePropsL) |
| 326 | + where |
| 327 | + extractDeep (PSDecided dps) |
| 328 | + | Just propDifficulty <- dpsDifficulty dps |
| 329 | + , propDifficulty <= cd = Just dps |
| 330 | + | otherwise = Nothing |
| 331 | + extractDeep (PSUndecided _) = Nothing |
| 332 | + getDeepProposalPairs = |
| 333 | + map (\dps -> (hash $ upsProposal $ dpsUndecided dps, dps)) <$> |
| 334 | + getDeepProposals cd |
| 335 | + getBlockIssuerStake e = lift . getBlockIssuerStake e |
| 336 | + getSlottingData = ether $ do |
| 337 | + new <- gets pmSlottingData |
| 338 | + maybe getSlottingData pure new |
| 339 | + |
| 340 | +{-# ANN module ("HLint: ignore Reduce duplication" :: Text) #-} |
| 341 | + |
| 342 | +instance (MonadPollRead m) => |
| 343 | + MonadPoll (PollT m) where |
| 344 | + putBVState bv st = ether $ pmBVsL %= MM.insert bv st |
| 345 | + delBVState bv = ether $ pmBVsL %= MM.delete bv |
| 346 | + setAdoptedBV bv = ether $ do |
| 347 | + bvs <- getBVState bv |
| 348 | + adoptedBVD <- getAdoptedBVData |
| 349 | + case bvs of |
| 350 | + Nothing -> |
| 351 | + logWarning $ "setAdoptedBV: unknown version " <> pretty bv -- can't happen actually |
| 352 | + Just (bvsModifier -> bvm) -> |
| 353 | + pmAdoptedBVFullL .= Just (bv, applyBVM bvm adoptedBVD) |
| 354 | + setLastConfirmedSV SoftwareVersion {..} = ether $ |
| 355 | + pmConfirmedL %= MM.insert svAppName svNumber |
| 356 | + delConfirmedSV appName = ether $ |
| 357 | + pmConfirmedL %= MM.delete appName |
| 358 | + addConfirmedProposal cps = ether $ |
| 359 | + pmConfirmedPropsL %= MM.insert (cpsSoftwareVersion cps) cps |
| 360 | + delConfirmedProposal sv = ether $ |
| 361 | + pmConfirmedPropsL %= MM.delete sv |
| 362 | + insertActiveProposal ps = do |
| 363 | + let up@UnsafeUpdateProposal{..} = psProposal ps |
| 364 | + upId = hash up |
| 365 | + whenNothingM_ (getProposal upId) $ |
| 366 | + setEpochProposers =<< (HS.insert (addressHash upFrom) <$> getEpochProposers) |
| 367 | + ether $ pmActivePropsL %= MM.insert upId ps |
| 368 | + -- Deactivate proposal doesn't change epoch proposers. |
| 369 | + deactivateProposal id = do |
| 370 | + prop <- getProposal id |
| 371 | + whenJust prop $ \ps -> ether $ do |
| 372 | + let up = psProposal ps |
| 373 | + upId = hash up |
| 374 | + pmActivePropsL %= MM.delete upId |
| 375 | + setSlottingData sd = ether $ pmSlottingDataL .= Just sd |
| 376 | + setEpochProposers ep = ether $ pmEpochProposersL .= Just ep |
0 commit comments