From 6f62054758ffe2e825cca435a29f35ceef37838c Mon Sep 17 00:00:00 2001 From: drsk Date: Tue, 19 Nov 2024 11:17:58 +0100 Subject: [PATCH] sketch for auto suspension --- .../src/Concordium/GlobalState/BlockState.hs | 4 + .../src/Concordium/GlobalState/DummyData.hs | 20 +++- .../GlobalState/Persistent/BlobStore.hs | 1 + .../GlobalState/Persistent/BlockState.hs | 23 ++++ .../Persistent/BlockState/Updates.hs | 38 +++++- .../GlobalState/Persistent/PoolRewards.hs | 3 +- .../src/Concordium/GlobalState/PoolRewards.hs | 13 +- .../src/Concordium/KonsensusV1/Scheduler.hs | 113 +++++++++++++++--- concordium-consensus/test-runners/app/Main.hs | 3 +- .../test-runners/catchup/Main.hs | 3 +- 10 files changed, 194 insertions(+), 27 deletions(-) diff --git a/concordium-consensus/src/Concordium/GlobalState/BlockState.hs b/concordium-consensus/src/Concordium/GlobalState/BlockState.hs index 4abf69dc47..3626609b32 100644 --- a/concordium-consensus/src/Concordium/GlobalState/BlockState.hs +++ b/concordium-consensus/src/Concordium/GlobalState/BlockState.hs @@ -1518,6 +1518,8 @@ class (BlockStateQuery m) => BlockStateOperations m where -- round did timeout. bsoUpdateMissedRounds :: (PVSupportsDelegation (MPV m), PVSupportsValidatorSuspension (MPV m)) => UpdatableBlockState m -> Map.Map BakerId Word64 -> m (UpdatableBlockState m) + bsoPrimeForSuspension :: (PVSupportsDelegation (MPV m), PVSupportsValidatorSuspension (MPV m)) => UpdatableBlockState m -> [BakerId] -> m (UpdatableBlockState m) + -- | A snapshot of the block state that can be used to roll back to a previous state. type StateSnapshot m @@ -1830,6 +1832,7 @@ instance (Monad (t m), MonadTrans t, BlockStateOperations m) => BlockStateOperat bsoSetRewardAccounts s = lift . bsoSetRewardAccounts s bsoIsProtocolUpdateEffective = lift . bsoIsProtocolUpdateEffective bsoUpdateMissedRounds s = lift . bsoUpdateMissedRounds s + bsoPrimeForSuspension s = lift . bsoPrimeForSuspension s type StateSnapshot (MGSTrans t m) = StateSnapshot m bsoSnapshotState = lift . bsoSnapshotState bsoRollback s = lift . bsoRollback s @@ -1887,6 +1890,7 @@ instance (Monad (t m), MonadTrans t, BlockStateOperations m) => BlockStateOperat {-# INLINE bsoGetCurrentEpochBakers #-} {-# INLINE bsoIsProtocolUpdateEffective #-} {-# INLINE bsoUpdateMissedRounds #-} + {-# INLINE bsoPrimeForSuspension #-} {-# INLINE bsoSnapshotState #-} {-# INLINE bsoRollback #-} diff --git a/concordium-consensus/src/Concordium/GlobalState/DummyData.hs b/concordium-consensus/src/Concordium/GlobalState/DummyData.hs index 749630516c..311bc664d5 100644 --- a/concordium-consensus/src/Concordium/GlobalState/DummyData.hs +++ b/concordium-consensus/src/Concordium/GlobalState/DummyData.hs @@ -364,6 +364,14 @@ dummyFinalizationCommitteeParameters = _fcpFinalizerRelativeStakeThreshold = PartsPerHundredThousands 10000 } +-- | Validator score parameters for the second consensus protocol. +dummyValidatorScoreParameters :: ValidatorScoreParameters +dummyValidatorScoreParameters = + ValidatorScoreParameters + { -- Maximal number of missed rounds before a validator gets suspended. + _vspMaxMissedRounds = 1 + } + dummyChainParameters :: forall cpv. (IsChainParametersVersion cpv) => ChainParameters' cpv dummyChainParameters = case chainParametersVersion @cpv of SChainParametersV0 -> @@ -382,7 +390,8 @@ dummyChainParameters = case chainParametersVersion @cpv of PoolParametersV0 { _ppBakerStakeThreshold = 300000000000 }, - _cpFinalizationCommitteeParameters = NoParam + _cpFinalizationCommitteeParameters = NoParam, + _cpValidatorScoreParameters = NoParam } SChainParametersV1 -> ChainParameters @@ -420,7 +429,8 @@ dummyChainParameters = case chainParametersVersion @cpv of _transactionCommissionRange = fullRange } }, - _cpFinalizationCommitteeParameters = NoParam + _cpFinalizationCommitteeParameters = NoParam, + _cpValidatorScoreParameters = NoParam } SChainParametersV2 -> ChainParameters @@ -458,7 +468,8 @@ dummyChainParameters = case chainParametersVersion @cpv of _transactionCommissionRange = fullRange } }, - _cpFinalizationCommitteeParameters = SomeParam dummyFinalizationCommitteeParameters + _cpFinalizationCommitteeParameters = SomeParam dummyFinalizationCommitteeParameters, + _cpValidatorScoreParameters = NoParam } SChainParametersV3 -> ChainParameters @@ -496,7 +507,8 @@ dummyChainParameters = case chainParametersVersion @cpv of _transactionCommissionRange = fullRange } }, - _cpFinalizationCommitteeParameters = SomeParam dummyFinalizationCommitteeParameters + _cpFinalizationCommitteeParameters = SomeParam dummyFinalizationCommitteeParameters, + _cpValidatorScoreParameters = SomeParam dummyValidatorScoreParameters } where fullRange = InclusiveRange (makeAmountFraction 0) (makeAmountFraction 100000) diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlobStore.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlobStore.hs index 7850fd6461..138f320a06 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlobStore.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlobStore.hs @@ -1563,6 +1563,7 @@ instance (MonadBlobStore m, IsCooldownParametersVersion cpv) => BlobStorable m ( instance (MonadBlobStore m) => BlobStorable m Parameters.TimeParameters instance (MonadBlobStore m) => BlobStorable m Parameters.TimeoutParameters instance (MonadBlobStore m) => BlobStorable m Parameters.FinalizationCommitteeParameters +instance (MonadBlobStore m) => BlobStorable m Parameters.ValidatorScoreParameters instance (MonadBlobStore m) => BlobStorable m Duration instance (MonadBlobStore m) => BlobStorable m Energy instance (MonadBlobStore m) => BlobStorable m (Map AccountAddress Timestamp) diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs index ef5b872b8c..71cbc9c4c4 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs @@ -3498,6 +3498,28 @@ doUpdateMissedRounds pbs rds = do (Map.toList rds) storePBS pbs bsp' +doPrimeForSuspension :: + ( PVSupportsDelegation pv, + SupportsPersistentState pv m + ) => + PersistentBlockState pv -> + [BakerId] -> + m (PersistentBlockState pv) +doPrimeForSuspension pbs bids = do + bsp <- loadPBS pbs + bsp' <- + foldM + ( \bsp0 bId -> + modifyBakerPoolRewardDetailsInPoolRewards + bsp0 + bId + (\bprd -> bprd{suspensionPrimed = True <$ suspensionPrimed bprd}) + ) + bsp + bids + + storePBS pbs bsp' + doProcessUpdateQueues :: forall pv m. (SupportsPersistentState pv m) => @@ -4387,6 +4409,7 @@ instance (IsProtocolVersion pv, PersistentState av pv r m) => BlockStateOperatio bsoSetRewardAccounts = doSetRewardAccounts bsoIsProtocolUpdateEffective = doIsProtocolUpdateEffective bsoUpdateMissedRounds = doUpdateMissedRounds + bsoPrimeForSuspension = doPrimeForSuspension type StateSnapshot (PersistentBlockStateMonad pv r m) = BlockStatePointers pv bsoSnapshotState = loadPBS bsoRollback = storePBS diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Updates.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Updates.hs index e824aeb0f7..8bc37c92ca 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Updates.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Updates.hs @@ -242,7 +242,9 @@ data PendingUpdates (cpv :: ChainParametersVersion) = PendingUpdates -- | Block energy limit (CPV2 onwards). pBlockEnergyLimitQueue :: !(HashedBufferedRefO 'PTBlockEnergyLimit cpv (UpdateQueue Energy)), -- | Finalization committee parameters (CPV2 onwards). - pFinalizationCommitteeParametersQueue :: !(HashedBufferedRefO 'PTFinalizationCommitteeParameters cpv (UpdateQueue FinalizationCommitteeParameters)) + pFinalizationCommitteeParametersQueue :: !(HashedBufferedRefO 'PTFinalizationCommitteeParameters cpv (UpdateQueue FinalizationCommitteeParameters)), + -- | Validators score parameters (CPV3 onwards). + pValidatorScoreParametersQueue :: !(HashedBufferedRefO 'PTValidatorScoreParameters cpv (UpdateQueue ValidatorScoreParameters)) } -- | See documentation of @migratePersistentBlockState@. @@ -422,6 +424,25 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr StateMigrationParametersP7ToP8{} -> case pFinalizationCommitteeParametersQueue of SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + newValidatorScoreParametersQueue <- case migration of + StateMigrationParametersTrivial -> case pValidatorScoreParametersQueue of + NoParam -> return NoParam + SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + StateMigrationParametersP1P2 -> case pValidatorScoreParametersQueue of + NoParam -> return NoParam + StateMigrationParametersP2P3 -> case pValidatorScoreParametersQueue of + NoParam -> return NoParam + StateMigrationParametersP3ToP4{} -> case pValidatorScoreParametersQueue of + NoParam -> return NoParam + StateMigrationParametersP4ToP5{} -> case pValidatorScoreParametersQueue of + NoParam -> return NoParam + StateMigrationParametersP5ToP6{} -> case pValidatorScoreParametersQueue of + NoParam -> return NoParam + StateMigrationParametersP6ToP7{} -> case pValidatorScoreParametersQueue of + NoParam -> return NoParam + StateMigrationParametersP7ToP8{} -> do + (!hbr, _) <- refFlush =<< refMake emptyUpdateQueue + return (SomeParam hbr) return $! PendingUpdates { pRootKeysUpdateQueue = newRootKeys, @@ -443,7 +464,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa pTimeoutParametersQueue = newTimeoutParameters, pMinBlockTimeQueue = newMinBlockTimeQueue, pBlockEnergyLimitQueue = newBlockEnergyLimitQueue, - pFinalizationCommitteeParametersQueue = newFinalizationCommitteeParametersQueue + pFinalizationCommitteeParametersQueue = newFinalizationCommitteeParametersQueue, + pValidatorScoreParametersQueue = newValidatorScoreParametersQueue } instance @@ -522,6 +544,7 @@ instance (putMinBlockTimeQueue, newMinBlockTimeQueue) <- storeUpdate pMinBlockTimeQueue (putBlockEnergyLimitQueue, newBlockEnergyLimitQueue) <- storeUpdate pBlockEnergyLimitQueue (putFinalizationCommitteeParametersQueue, newFinalizationCommitteeParametersQueue) <- storeUpdate pFinalizationCommitteeParametersQueue + (putValidatorScoreParametersQueue, newValidatorScoreParametersQueue) <- storeUpdate pValidatorScoreParametersQueue let newPU = PendingUpdates { pRootKeysUpdateQueue = rkQ, @@ -543,7 +566,8 @@ instance pTimeoutParametersQueue = newTimeoutParametersQueue, pMinBlockTimeQueue = newMinBlockTimeQueue, pBlockEnergyLimitQueue = newBlockEnergyLimitQueue, - pFinalizationCommitteeParametersQueue = newFinalizationCommitteeParametersQueue + pFinalizationCommitteeParametersQueue = newFinalizationCommitteeParametersQueue, + pValidatorScoreParametersQueue = newValidatorScoreParametersQueue } let putPU = pRKQ @@ -566,6 +590,7 @@ instance >> putMinBlockTimeQueue >> putBlockEnergyLimitQueue >> putFinalizationCommitteeParametersQueue + >> putValidatorScoreParametersQueue return (putPU, newPU) load = withCPVConstraints (chainParametersVersion @cpv) $ do mRKQ <- label "Root keys update queue" load @@ -588,6 +613,7 @@ instance mMinBlockTimeQueue <- label "Minimum block time update queue" load mBlockEnergyLimitQueue <- label "Block energy limit update queue" load mFinalizationCommitteeParametersQueue <- label "Finalization committee parameters update queue" load + mValidatorScoreParametersQueue <- label "Validator score parameters update queue" load return $! do pRootKeysUpdateQueue <- mRKQ pLevel1KeysUpdateQueue <- mL1KQ @@ -609,6 +635,7 @@ instance pMinBlockTimeQueue <- mMinBlockTimeQueue pBlockEnergyLimitQueue <- mBlockEnergyLimitQueue pFinalizationCommitteeParametersQueue <- mFinalizationCommitteeParametersQueue + pValidatorScoreParametersQueue <- mValidatorScoreParametersQueue return PendingUpdates{..} instance @@ -638,6 +665,7 @@ instance <*> cache pMinBlockTimeQueue <*> cache pBlockEnergyLimitQueue <*> cache pFinalizationCommitteeParametersQueue + <*> cache pValidatorScoreParametersQueue where cpv = chainParametersVersion @cpv @@ -646,7 +674,7 @@ emptyPendingUpdates :: forall m cpv. (MonadBlobStore m, IsChainParametersVersion cpv) => m (PendingUpdates cpv) -emptyPendingUpdates = PendingUpdates <$> e <*> e <*> e <*> e <*> whenSupportedA e <*> e <*> e <*> e <*> e <*> e <*> e <*> e <*> e <*> e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e +emptyPendingUpdates = PendingUpdates <$> e <*> e <*> e <*> e <*> whenSupportedA e <*> e <*> e <*> e <*> e <*> e <*> e <*> e <*> e <*> e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e <*> whenSupportedA e where e :: m (HashedBufferedRef (UpdateQueue a)) e = makeHashedBufferedRef emptyUpdateQueue @@ -678,6 +706,7 @@ makePersistentPendingUpdates UQ.PendingUpdates{..} = withCPVConstraints (chainPa pMinBlockTimeQueue <- mapM (refMake <=< makePersistentUpdateQueue) _pMinBlockTimeQueue pBlockEnergyLimitQueue <- mapM (refMake <=< makePersistentUpdateQueue) _pBlockEnergyLimitQueue pFinalizationCommitteeParametersQueue <- mapM (refMake <=< makePersistentUpdateQueue) _pFinalizationCommitteeParametersQueue + pValidatorScoreParametersQueue <- mapM (refMake <=< makePersistentUpdateQueue) _pValidatorScoreParametersQueue return PendingUpdates{..} -- | Convert a persistent 'PendingUpdates' to an in-memory 'UQ.PendingUpdates'. @@ -707,6 +736,7 @@ makeBasicPendingUpdates PendingUpdates{..} = withCPVConstraints (chainParameters _pMinBlockTimeQueue <- mapM (makeBasicUpdateQueue <=< refLoad) pMinBlockTimeQueue _pBlockEnergyLimitQueue <- mapM (makeBasicUpdateQueue <=< refLoad) pBlockEnergyLimitQueue _pFinalizationCommitteeParametersQueue <- mapM (makeBasicUpdateQueue <=< refLoad) pFinalizationCommitteeParametersQueue + _pValidatorScoreParametersQueue <- mapM (makeBasicUpdateQueue <=< refLoad) pValidatorScoreParametersQueue return UQ.PendingUpdates{..} -- | Current state of updatable parameters and update queues. diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/PoolRewards.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/PoolRewards.hs index add62a8cf0..80d0bddb09 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/PoolRewards.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/PoolRewards.hs @@ -147,7 +147,8 @@ migratePoolRewardsP1 curBakers nextBakers blockCounts npEpoch npMintRate = do { blockCount = Map.findWithDefault 0 bid blockCounts, transactionFeesAccrued = 0, finalizationAwake = False, - missedRounds = conditionally hasValidatorSuspension 0 + missedRounds = conditionally hasValidatorSuspension 0, + suspensionPrimed = conditionally hasValidatorSuspension False } (!newRef, _) <- refFlush =<< refMake bprd return newRef diff --git a/concordium-consensus/src/Concordium/GlobalState/PoolRewards.hs b/concordium-consensus/src/Concordium/GlobalState/PoolRewards.hs index e15d058fd0..f01d6a5d0d 100644 --- a/concordium-consensus/src/Concordium/GlobalState/PoolRewards.hs +++ b/concordium-consensus/src/Concordium/GlobalState/PoolRewards.hs @@ -25,7 +25,9 @@ data BakerPoolRewardDetails (av :: AccountVersion) = BakerPoolRewardDetails -- | Whether the pool contributed to a finalization proof in the reward period finalizationAwake :: !Bool, -- | The number of missed rounds in the reward period - missedRounds :: !(Conditionally (SupportsValidatorSuspension av) Word64) + missedRounds :: !(Conditionally (SupportsValidatorSuspension av) Word64), + -- | A flag indicating whether suspension can be triggered the next snapshot epoch. + suspensionPrimed :: !(Conditionally (SupportsValidatorSuspension av) Bool) } deriving (Eq, Show) @@ -35,6 +37,7 @@ instance forall av. (IsAccountVersion av) => Serialize (BakerPoolRewardDetails a put transactionFeesAccrued putBool finalizationAwake mapM_ putWord64be missedRounds + mapM_ putBool suspensionPrimed get = BakerPoolRewardDetails @@ -42,6 +45,7 @@ instance forall av. (IsAccountVersion av) => Serialize (BakerPoolRewardDetails a <*> get <*> getBool <*> conditionallyA (sSupportsValidatorSuspension (accountVersion @av)) get + <*> conditionallyA (sSupportsValidatorSuspension (accountVersion @av)) getBool instance forall av. (IsAccountVersion av) => HashableTo Hash.Hash (BakerPoolRewardDetails av) where getHash = Hash.hash . encode @@ -55,7 +59,8 @@ emptyBakerPoolRewardDetails = { blockCount = 0, transactionFeesAccrued = 0, finalizationAwake = False, - missedRounds = conditionally (sSupportsValidatorSuspension (accountVersion @av)) 0 + missedRounds = conditionally (sSupportsValidatorSuspension (accountVersion @av)) 0, + suspensionPrimed = conditionally (sSupportsValidatorSuspension (accountVersion @av)) False } -- | Migrate BakerPoolRewardDetails with different account versions. @@ -70,5 +75,9 @@ migrateBakerPoolRewardDetails BakerPoolRewardDetails{..} = conditionally (sSupportsValidatorSuspension (accountVersion @av1)) (fromCondDef missedRounds 0), + suspensionPrimed = + conditionally + (sSupportsValidatorSuspension (accountVersion @av1)) + (fromCondDef suspensionPrimed False), .. } diff --git a/concordium-consensus/src/Concordium/KonsensusV1/Scheduler.hs b/concordium-consensus/src/Concordium/KonsensusV1/Scheduler.hs index 477d76fa80..0974050151 100644 --- a/concordium-consensus/src/Concordium/KonsensusV1/Scheduler.hs +++ b/concordium-consensus/src/Concordium/KonsensusV1/Scheduler.hs @@ -16,13 +16,14 @@ import Lens.Micro.Platform import Concordium.Logger import Concordium.TimeMonad import Concordium.Types +import Concordium.Types.Conditionally import Concordium.Types.SeedState import Concordium.GlobalState.BakerInfo import Concordium.GlobalState.BlockState import Concordium.GlobalState.CapitalDistribution import qualified Concordium.GlobalState.Persistent.BlockState as PBS -import Concordium.GlobalState.PoolRewards (BakerPoolRewardDetails) +import Concordium.GlobalState.PoolRewards (BakerPoolRewardDetails (..)) import Concordium.GlobalState.TransactionTable import Concordium.GlobalState.Types import Concordium.KonsensusV1.LeaderElection @@ -117,7 +118,10 @@ data PrologueResult m av = PrologueResult prologueBlockState :: UpdatableBlockState m, -- | If the block should pay out for a payday, these parameters determine the pay out. -- Otherwise, they are 'Nothing'. - prologuePaydayParameters :: Maybe (PaydayParameters av) + prologuePaydayParameters :: Maybe (PaydayParameters av), + -- | If the block triggered an epoch transition and the new epoch is a + -- snapshot, this flag is true. + prologueIsSnapshot :: Bool } -- * Block prologue @@ -186,8 +190,8 @@ doEpochTransition :: Duration -> -- | State to update UpdatableBlockState m -> - m (Maybe (PaydayParameters (AccountVersionFor (MPV m))), UpdatableBlockState m) -doEpochTransition False _ theState = return (Nothing, theState) + m (Maybe (PaydayParameters (AccountVersionFor (MPV m))), Bool, UpdatableBlockState m) +doEpochTransition False _ theState = return (Nothing, False, theState) doEpochTransition True epochDuration theState0 = do chainParams <- bsoGetChainParameters theState0 oldSeedState <- bsoGetSeedState theState0 @@ -225,7 +229,7 @@ doEpochTransition True epochDuration theState0 = do newBakers <- bsoGetCurrentEpochBakers theState6 let newSeedState = updateSeedStateForEpoch newBakers epochDuration oldSeedState theState7 <- bsoSetSeedState theState6 newSeedState - theState9 <- + (theState9, isSnapshot) <- if newEpoch + 1 == newNextPayday then do -- This is the start of the last epoch of a payday, so take a baker snapshot. @@ -248,10 +252,10 @@ doEpochTransition True epochDuration theState0 = do -- From P7 onwards, we transition pre-pre-cooldowns into pre-cooldowns, so that -- at the next payday they will enter cooldown. case sSupportsFlexibleCooldown (sAccountVersionFor (protocolVersion @(MPV m))) of - STrue -> bsoProcessPrePreCooldowns theState9 - SFalse -> return theState9 - else return theState7 - return (mPaydayParams, theState9) + STrue -> (,True) <$> bsoProcessPrePreCooldowns theState9 + SFalse -> return (theState9, True) + else return (theState7, False) + return (mPaydayParams, isSnapshot, theState9) -- | Update the seed state to account for a block. -- See 'updateSeedStateForBlock' for details of what this entails. @@ -303,13 +307,14 @@ executeBlockPrologue BlockExecutionData{..} = do -- unlock the scheduled releases that have expired theState3 <- bsoProcessReleaseSchedule theState2 bedTimestamp -- transition the epoch if necessary - (mPaydayParms, theState4) <- doEpochTransition bedIsNewEpoch bedEpochDuration theState3 + (mPaydayParms, isSnapshot, theState4) <- doEpochTransition bedIsNewEpoch bedEpochDuration theState3 -- update the seed state using the block time and block nonce theState5 <- doUpdateSeedStateForBlock bedTimestamp bedBlockNonce theState4 return PrologueResult { prologueBlockState = theState5, - prologuePaydayParameters = mPaydayParms + prologuePaydayParameters = mPaydayParms, + prologueIsSnapshot = isSnapshot } -- * Block epilogue @@ -350,7 +355,9 @@ doMintingP6 mintRate foundationAddr theState0 = do -- | If a payday has elapsed, this mints and distributes rewards for the payday. processPaydayRewards :: + forall pv m. ( pv ~ MPV m, + IsProtocolVersion pv, BlockStateStorage m, IsConsensusV1 pv ) => @@ -363,7 +370,13 @@ processPaydayRewards (Just PaydayParameters{..}) theState0 = do -- in which the rewards are distributed. foundationAddr <- getAccountCanonicalAddress =<< bsoGetFoundationAccount theState0 theState1 <- doMintingP6 paydayMintRate foundationAddr theState0 - distributeRewards foundationAddr paydayCapitalDistribution paydayBakers paydayPoolRewards theState1 + theState2 <- distributeRewards foundationAddr paydayCapitalDistribution paydayBakers paydayPoolRewards theState1 + -- We prime all active validators of the payday for possible suspension the next snapshot. + case hasValidatorSuspension of + STrue -> bsoPrimeForSuspension theState2 (Map.keys paydayPoolRewards) + SFalse -> return theState2 + where + hasValidatorSuspension = sSupportsValidatorSuspension (sAccountVersionFor (protocolVersion @pv)) -- | Records that the baker baked this block (so it is eligible for baking rewards) and that the -- finalizers that signed the QC in the block are awake (and eligible for finalizer rewards). @@ -402,10 +415,69 @@ processBlockRewards ParticipatingBakers{..} TransactionRewardParameters{..} miss where hasValidatorSuspension = sSupportsValidatorSuspension (sAccountVersionFor (protocolVersion @pv)) +processSuspensions :: + forall pv m. + ( pv ~ MPV m, + BlockStateStorage m, + IsConsensusV1 pv, + PVSupportsValidatorSuspension pv + ) => + Timestamp -> + UpdatableBlockState m -> + m (UpdatableBlockState m) +processSuspensions timestamp bs0 = do + -- get chain paramers + ps <- bsoGetChainParameters bs0 + case _cpValidatorScoreParameters ps of + NoParam -> return bs0 -- Nothing to do here. + SomeParam (ValidatorScoreParameters{..}) -> do + -- get baker pool reward details + bprd <- bsoGetBakerPoolRewardDetails bs0 + -- account indexes that will be suspended + let ixs = + [ bakerAccountIndex bid + | (bid, rd) <- Map.toList bprd, + fromCondDef (suspensionPrimed rd) False, + fromCondDef (missedRounds rd) 0 > _vspMaxMissedRounds + ] + (_upds, bs1) <- + foldM + ( \(upds, bs) accIx -> do + res <- bsoUpdateValidator bs timestamp accIx updSuspend + case res of + -- TODO(drsk) how to deal with error? + Left _err -> return (upds, bs) + Right (upds', bs') -> return (upds ++ upds', bs') + ) + ([], bs0) + ixs + -- TODO(drsk) add updates to special transaction outcome + -- bsoAddSpecialTransactionOutcome + return bs1 + where + updSuspend = + ValidatorUpdate + { vuKeys = Nothing, + vuCapital = Nothing, + vuRestakeEarnings = Nothing, + vuOpenForDelegation = Nothing, + vuMetadataURL = Nothing, + vuTransactionFeeCommission = Nothing, + vuBakingRewardCommission = Nothing, + vuFinalizationRewardCommission = Nothing, + vuSuspend = Just True + } + +-- for all bakers that are primed and where missed rounds is > then the threshold, set the +-- suspended flag in the account via `setAccountValidatorSuspended`. +-- emit suspended event +-- clear missed rounds/primed? + -- | Execute the block epilogue. This mints and distributes the rewards for a payday if the block is -- in a new payday. This also accrues the rewards for the block that will be paid at the next -- payday. executeBlockEpilogue :: + forall pv m. ( pv ~ MPV m, IsProtocolVersion pv, BlockStateStorage m, @@ -416,12 +488,21 @@ executeBlockEpilogue :: Maybe (PaydayParameters (AccountVersionFor (MPV m))) -> TransactionRewardParameters -> Map.Map BakerId Word64 -> + Bool -> + Timestamp -> UpdatableBlockState m -> m (PBS.HashedPersistentBlockState pv) -executeBlockEpilogue participants paydayParams transactionRewardParams missedRounds theState0 = do +executeBlockEpilogue participants paydayParams transactionRewardParams missedRounds isSnapshot timestamp theState0 = do theState1 <- processPaydayRewards paydayParams theState0 theState2 <- processBlockRewards participants transactionRewardParams missedRounds theState1 - freezeBlockState theState2 + theState3 <- case hasValidatorSuspension of + STrue + | isSnapshot -> processSuspensions timestamp theState2 + | otherwise -> return theState2 + SFalse -> return theState2 + freezeBlockState theState3 + where + hasValidatorSuspension = sSupportsValidatorSuspension (sAccountVersionFor (protocolVersion @pv)) -- * Transactions @@ -592,6 +673,8 @@ executeBlockState execData@BlockExecutionData{..} transactions = do prologuePaydayParameters terTransactionRewardParameters bedMissedRounds + prologueIsSnapshot + bedTimestamp terBlockState return (endState, terEnergyUsed) @@ -647,6 +730,8 @@ constructBlockState runtimeParams transactionTable pendingTable execData@BlockEx prologuePaydayParameters terTransactionRewardParameters bedMissedRounds + prologueIsSnapshot + bedTimestamp terBlockState endTime <- currentTime logEvent Scheduler LLInfo $ "Constructed a block in " ++ show (diffUTCTime endTime startTime) diff --git a/concordium-consensus/test-runners/app/Main.hs b/concordium-consensus/test-runners/app/Main.hs index c4bcece813..143eff4252 100644 --- a/concordium-consensus/test-runners/app/Main.hs +++ b/concordium-consensus/test-runners/app/Main.hs @@ -342,7 +342,8 @@ main = do PoolParametersV0 { _ppBakerStakeThreshold = 300000000000 }, - _cpFinalizationCommitteeParameters = NoParam + _cpFinalizationCommitteeParameters = NoParam, + _cpValidatorScoreParameters = NoParam } let (genesisData, bakerIdentities, _) = makeGenesisDataV0 @PV diff --git a/concordium-consensus/test-runners/catchup/Main.hs b/concordium-consensus/test-runners/catchup/Main.hs index 051251a2a1..15de0952f3 100644 --- a/concordium-consensus/test-runners/catchup/Main.hs +++ b/concordium-consensus/test-runners/catchup/Main.hs @@ -371,7 +371,8 @@ main = do PoolParametersV0 { _ppBakerStakeThreshold = 300000000000 }, - _cpFinalizationCommitteeParameters = NoParam + _cpFinalizationCommitteeParameters = NoParam, + _cpValidatorScoreParameters = NoParam } let (genesisData, bakerIdentities, _) = makeGenesisDataV0 @PV