1212{-# LANGUAGE TypeFamilies #-}
1313{-# LANGUAGE UndecidableInstances #-}
1414{-# OPTIONS_GHC -Wno-orphans #-}
15+ {-# OPTIONS_GHC -Wno-partial-fields #-}
16+ {-# OPTIONS_GHC -Wno-unused-imports #-}
1517
1618module Ouroboros.Consensus.Block.SupportsPeras
1719 ( PerasRoundNo (.. )
@@ -26,9 +28,11 @@ module Ouroboros.Consensus.Block.SupportsPeras
2628 , ValidatedPerasCert (.. )
2729 , ValidatedPerasVote (.. )
2830 , PerasVoteAggregate (.. )
29- , emptyPerasVoteAggregate
31+ , PerasVoteAggregateStatus (.. )
32+ , pvasMaybeCert
33+ , emptyPerasVoteAggregateStatus
3034 , updatePerasVoteAggregate
31- , UpdatePerasVoteAggregateResult ( .. )
35+ , updatePerasVoteAggregateStatus
3236 , makePerasCfg
3337 , HasId (.. )
3438 , HasPerasCertRound (.. )
@@ -51,6 +55,8 @@ import Codec.Serialise.Decoding (decodeListLenOf)
5155import Codec.Serialise.Encoding (encodeListLen )
5256import Control.Applicative ((<|>) )
5357import Data.Coerce (coerce )
58+ import Data.Map.Strict (Map )
59+ import qualified Data.Map.Strict as Map
5460import Data.Maybe (isNothing )
5561import Data.Monoid (Sum (.. ))
5662import Data.Proxy (Proxy (.. ))
@@ -212,69 +218,89 @@ const_PERAS_QUORUM_THRESHOLD = PerasVoteStake 0.75
212218
213219data PerasVoteAggregate blk = PerasVoteAggregate
214220 { pvaTarget :: ! (PerasVoteTarget blk )
215- , pvaVotes :: ! (Set (WithArrivalTime (ValidatedPerasVote blk )))
221+ , pvaVotes :: ! (Map ( IdOf ( PerasVote blk )) (WithArrivalTime (ValidatedPerasVote blk )))
216222 , pvaTotalStake :: ! PerasVoteStake
217- , pvaMaybeCert :: ! (Maybe (ValidatedPerasCert blk ))
218223 }
219224 deriving stock (Generic , Eq , Ord , Show )
220225 deriving anyclass NoThunks
221226
222- emptyPerasVoteAggregate :: PerasVoteTarget blk -> PerasVoteAggregate blk
223- emptyPerasVoteAggregate target =
224- PerasVoteAggregate
225- { pvaTotalStake = PerasVoteStake 0
226- , pvaTarget = target
227- , pvaVotes = Set. empty
228- , pvaMaybeCert = Nothing
229- }
230-
231- data UpdatePerasVoteAggregateResult blk
232- = IncorrectPerasVoteTarget
233- | AddedPerasVoteButDidntGenerateNewCert (PerasVoteAggregate blk )
234- | AddedPerasVoteAndGeneratedNewCert (PerasVoteAggregate blk ) (ValidatedPerasCert blk )
227+ data PerasVoteAggregateStatus blk
228+ = PerasVoteAggregateQuorumNotReached { pvasVoteAggregate :: ! (PerasVoteAggregate blk )}
229+ | PerasVoteAggregateQuorumReachedAlready
230+ { pvasVoteAggregate :: ! (PerasVoteAggregate blk ), pvasCert :: ValidatedPerasCert blk }
235231 deriving stock (Generic , Eq , Ord , Show )
236232 deriving anyclass NoThunks
237233
234+ pvasMaybeCert :: PerasVoteAggregateStatus blk -> Maybe (ValidatedPerasCert blk )
235+ pvasMaybeCert aggr = case aggr of
236+ PerasVoteAggregateQuorumNotReached {} -> Nothing
237+ PerasVoteAggregateQuorumReachedAlready {pvasCert} -> Just pvasCert
238+
239+ emptyPerasVoteAggregateStatus :: PerasVoteTarget blk -> PerasVoteAggregateStatus blk
240+ emptyPerasVoteAggregateStatus target =
241+ PerasVoteAggregateQuorumNotReached $
242+ PerasVoteAggregate
243+ { pvaTotalStake = PerasVoteStake 0
244+ , pvaTarget = target
245+ , pvaVotes = Map. empty
246+ }
247+
248+ -- | Add a vote to an existing aggregate if it isn't already present, and update
249+ -- the stake accordingly.
250+ -- PRECONDITION: the vote's target must match the aggregate's target.
238251updatePerasVoteAggregate ::
239252 StandardHash blk =>
240253 PerasVoteAggregate blk ->
241254 WithArrivalTime (ValidatedPerasVote blk ) ->
242- UpdatePerasVoteAggregateResult blk
255+ PerasVoteAggregate blk
243256updatePerasVoteAggregate
244257 pva@ PerasVoteAggregate
245258 { pvaTarget = (roundNo, point)
246259 , pvaVotes = existingVotes
247260 , pvaTotalStake = initialStake
248- , pvaMaybeCert = mExistingCert
249261 }
250262 vote =
251- if getPerasVoteRound vote == roundNo && getPerasVoteVotedBlock vote == point
252- then
253- let pvaTotalStake = initialStake + vpvVoteStake (forgetArrivalTime vote)
254- pvaVotes = Set. insert vote existingVotes
255- mNewCert =
256- if isNothing mExistingCert && pvaTotalStake >= const_PERAS_QUORUM_THRESHOLD
257- then
258- Just $
259- ValidatedPerasCert
260- { vpcCertBoost = boostPerCert
261- , vpcCert =
262- PerasCert
263- { pcCertRound = roundNo
264- , pcCertBoostedBlock = point
265- }
266- }
267- else Nothing
268- pva' =
269- pva
270- { pvaVotes
271- , pvaTotalStake
272- , pvaMaybeCert = mExistingCert <|> mNewCert
273- }
274- in case mNewCert of
275- Just cert -> AddedPerasVoteAndGeneratedNewCert pva' cert
276- Nothing -> AddedPerasVoteButDidntGenerateNewCert pva'
277- else IncorrectPerasVoteTarget
263+ if not (getPerasVoteRound vote == roundNo && getPerasVoteVotedBlock vote == point)
264+ then error " updatePerasVoteAggregate: vote target does not match aggregate target"
265+ else
266+ let (pvaVotes', pvaTotalStake') =
267+ case Map. insertLookupWithKey
268+ (\ _k old _new -> old)
269+ (getId vote)
270+ vote
271+ existingVotes of
272+ (Nothing , votes') ->
273+ -- key was NOT present → inserted and stake updated
274+ (votes', initialStake + vpvVoteStake (forgetArrivalTime vote))
275+ (Just _, _) ->
276+ -- key WAS already present → votes and stake unchanged
277+ (existingVotes, initialStake)
278+ in pva{pvaVotes = pvaVotes', pvaTotalStake = pvaTotalStake'}
279+
280+ updatePerasVoteAggregateStatus ::
281+ StandardHash blk =>
282+ PerasVoteAggregateStatus blk ->
283+ WithArrivalTime (ValidatedPerasVote blk ) ->
284+ PerasVoteAggregateStatus blk
285+ updatePerasVoteAggregateStatus aggr vote = case aggr of
286+ PerasVoteAggregateQuorumNotReached {pvasVoteAggregate} ->
287+ let aggr' = updatePerasVoteAggregate pvasVoteAggregate vote
288+ in if pvaTotalStake aggr' >= const_PERAS_QUORUM_THRESHOLD
289+ then
290+ PerasVoteAggregateQuorumReachedAlready
291+ { pvasVoteAggregate = aggr'
292+ , pvasCert =
293+ ValidatedPerasCert
294+ { vpcCertBoost = boostPerCert
295+ , vpcCert = uncurry PerasCert (pvaTarget aggr')
296+ }
297+ }
298+ else PerasVoteAggregateQuorumNotReached {pvasVoteAggregate = aggr'}
299+ PerasVoteAggregateQuorumReachedAlready {pvasVoteAggregate, pvasCert} ->
300+ PerasVoteAggregateQuorumReachedAlready
301+ { pvasVoteAggregate = updatePerasVoteAggregate pvasVoteAggregate vote
302+ , pvasCert
303+ }
278304
279305type PerasVoteTarget blk = (PerasRoundNo , Point blk )
280306
0 commit comments