88{-# LANGUAGE GeneralizedNewtypeDeriving #-}
99{-# LANGUAGE NamedFieldPuns #-}
1010{-# LANGUAGE ScopedTypeVariables #-}
11- {-# LANGUAGE StandaloneDeriving #-}
1211{-# LANGUAGE TypeApplications #-}
1312{-# LANGUAGE TypeFamilies #-}
1413{-# LANGUAGE UndecidableInstances #-}
1716module Ouroboros.Consensus.Block.SupportsPeras
1817 ( PerasRoundNo (.. )
1918 , onPerasRoundNo
19+ , PerasVoteStake (.. )
2020 , PerasWeight (.. )
2121 , BlockSupportsPeras (.. )
2222 , PerasCert (.. )
2323 , PerasVote (.. )
24+ , PerasVoteTarget
2425 , PerasCfg (.. )
2526 , ValidatedPerasCert (.. )
2627 , ValidatedPerasVote (.. )
28+ , PerasVoteAggregate (.. )
29+ , emptyPerasVoteAggregate
30+ , updatePerasVoteAggregate
31+ , UpdatePerasVoteAggregateResult (.. )
2732 , makePerasCfg
33+ , HasId (.. )
2834 , HasPerasCertRound (.. )
2935 , HasPerasCertBoostedBlock (.. )
3036 , HasPerasCertBoost (.. )
3137 , HasPerasVoteRound (.. )
3238 , HasPerasVoteVotedBlock (.. )
33- , HasStakePoolId (.. )
39+ , HasPerasVoteVoterId (.. )
40+ , HasPerasVoteTarget (.. )
3441
3542 -- * Ouroboros Peras round length
3643 , PerasRoundLength (.. )
@@ -39,14 +46,16 @@ module Ouroboros.Consensus.Block.SupportsPeras
3946
4047import qualified Cardano.Binary as KeyHash
4148import Cardano.Ledger.Core (KeyHash , KeyRole (StakePool ))
42- import Cardano.Ledger.State (IndividualPoolStake (.. ), PoolDistr (PoolDistr , unPoolDistr ))
4349import Codec.Serialise (Serialise (.. ))
4450import Codec.Serialise.Decoding (decodeListLenOf )
4551import Codec.Serialise.Encoding (encodeListLen )
52+ import Control.Applicative ((<|>) )
4653import Data.Coerce (coerce )
47- import qualified Data.Map as Map
54+ import Data.Maybe ( isNothing )
4855import Data.Monoid (Sum (.. ))
4956import Data.Proxy (Proxy (.. ))
57+ import Data.Set (Set )
58+ import qualified Data.Set as Set
5059import Data.Word (Word64 )
5160import GHC.Generics (Generic )
5261import NoThunks.Class
@@ -56,13 +65,40 @@ import Ouroboros.Consensus.Util
5665import Ouroboros.Consensus.Util.Condense
5766import Quiet (Quiet (.. ))
5867
68+ class
69+ ( Ord (IdOf a )
70+ , Eq (IdOf a )
71+ , Show (IdOf a )
72+ , NoThunks (IdOf a )
73+ , Serialise (IdOf a )
74+ ) =>
75+ HasId a
76+ where
77+ type IdOf a
78+ getId :: a -> IdOf a
79+
80+ instance HasId perasObj => HasId (WithArrivalTime perasObj ) where
81+ type IdOf (WithArrivalTime perasObj ) = IdOf perasObj
82+ getId = getId . forgetArrivalTime
83+
5984newtype PerasRoundNo = PerasRoundNo { unPerasRoundNo :: Word64 }
6085 deriving Show via Quiet PerasRoundNo
6186 deriving stock Generic
6287 deriving newtype (Enum , Eq , Ord , Num , Bounded , NoThunks , Serialise )
6388
89+ newtype PerasVoteStake = PerasVoteStake { unPerasVoteStake :: Rational }
90+ deriving Show via Quiet PerasVoteStake
91+ deriving stock Generic
92+ deriving newtype (Enum , Eq , Ord , Num , Fractional , NoThunks , Serialise )
93+ deriving Semigroup via Sum Rational
94+ deriving Monoid via Sum Rational
95+
96+ data PerasVoteStakeDistr
97+ getPerasVoteStakeOf :: PerasVoteStakeDistr -> VoterId -> PerasVoteStake
98+ getPerasVoteStakeOf = undefined
99+
64100-- | TODO: what is the proper underlying type?
65- type StakePoolId = KeyHash 'StakePool
101+ type VoterId = KeyHash 'StakePool
66102
67103instance Condense PerasRoundNo where
68104 condense = show . unPerasRoundNo
@@ -98,15 +134,39 @@ data ValidatedPerasCert blk = ValidatedPerasCert
98134 deriving stock (Show , Eq , Ord , Generic )
99135 deriving anyclass NoThunks
100136
101- deriving instance Ord IndividualPoolStake
137+ instance
138+ ( HasId (PerasCert blk )
139+ , Ord (IdOf (PerasCert blk ))
140+ , Eq (IdOf (PerasCert blk ))
141+ , Show (IdOf (PerasCert blk ))
142+ , NoThunks (IdOf (PerasCert blk ))
143+ , Serialise (IdOf (PerasCert blk ))
144+ ) =>
145+ HasId (ValidatedPerasCert blk )
146+ where
147+ type IdOf (ValidatedPerasCert blk ) = IdOf (PerasCert blk )
148+ getId = getId . vpcCert
102149
103150data ValidatedPerasVote blk = ValidatedPerasVote
104151 { vpvVote :: ! (PerasVote blk )
105- , vpvVoteStake :: ! IndividualPoolStake
152+ , vpvVoteStake :: ! PerasVoteStake
106153 }
107154 deriving stock (Show , Eq , Ord , Generic )
108155 deriving anyclass NoThunks
109156
157+ instance
158+ ( HasId (PerasVote blk )
159+ , Ord (IdOf (PerasVote blk ))
160+ , Eq (IdOf (PerasVote blk ))
161+ , Show (IdOf (PerasVote blk ))
162+ , NoThunks (IdOf (PerasVote blk ))
163+ , Serialise (IdOf (PerasVote blk ))
164+ ) =>
165+ HasId (ValidatedPerasVote blk )
166+ where
167+ type IdOf (ValidatedPerasVote blk ) = IdOf (PerasVote blk )
168+ getId = getId . vpvVote
169+
110170{- ------------------------------------------------------------------------------
111171 Ouroboros Peras round length
112172-------------------------------------------------------------------------------}
@@ -144,9 +204,80 @@ class
144204 validatePerasVote ::
145205 PerasCfg blk ->
146206 PerasVote blk ->
147- PoolDistr ->
207+ PerasVoteStakeDistr ->
148208 Either (PerasValidationErr blk ) (ValidatedPerasVote blk )
149209
210+ const_PERAS_QUORUM_THRESHOLD :: PerasVoteStake
211+ const_PERAS_QUORUM_THRESHOLD = PerasVoteStake 0.75
212+
213+ data PerasVoteAggregate blk = PerasVoteAggregate
214+ { pvaTarget :: ! (PerasVoteTarget blk )
215+ , pvaVotes :: ! (Set (WithArrivalTime (ValidatedPerasVote blk )))
216+ , pvaTotalStake :: ! PerasVoteStake
217+ , pvaMaybeCert :: ! (Maybe (ValidatedPerasCert blk ))
218+ }
219+ deriving stock (Generic , Eq , Ord , Show )
220+ deriving anyclass NoThunks
221+
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 )
235+ deriving stock (Generic , Eq , Ord , Show )
236+ deriving anyclass NoThunks
237+
238+ updatePerasVoteAggregate ::
239+ StandardHash blk =>
240+ PerasVoteAggregate blk ->
241+ WithArrivalTime (ValidatedPerasVote blk ) ->
242+ UpdatePerasVoteAggregateResult blk
243+ updatePerasVoteAggregate
244+ pva@ PerasVoteAggregate
245+ { pvaTarget = (roundNo, point)
246+ , pvaVotes = existingVotes
247+ , pvaTotalStake = initialStake
248+ , pvaMaybeCert = mExistingCert
249+ }
250+ 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
278+
279+ type PerasVoteTarget blk = (PerasRoundNo , Point blk )
280+
150281-- TODO: degenerate instance for all blks to get things to compile
151282-- see https://github.com/tweag/cardano-peras/issues/73
152283instance StandardHash blk => BlockSupportsPeras blk where
@@ -168,7 +299,7 @@ instance StandardHash blk => BlockSupportsPeras blk where
168299 data PerasVote blk = PerasVote
169300 { pvVoteRound :: PerasRoundNo
170301 , pvVotedBlock :: Point blk
171- , pvVoteStakePoolId :: StakePoolId
302+ , pvVoteVoterId :: VoterId
172303 }
173304 deriving stock (Generic , Eq , Ord , Show )
174305 deriving anyclass NoThunks
@@ -189,10 +320,23 @@ instance StandardHash blk => BlockSupportsPeras blk where
189320 , vpcCertBoost = perasCfgWeightBoost cfg
190321 }
191322
192- validatePerasVote _cfg vote PoolDistr {unPoolDistr} =
193- let stake = unPoolDistr Map. ! (pvVoteStakePoolId vote)
323+ validatePerasVote _cfg vote stakeDistr =
324+ let stake = getPerasVoteStakeOf stakeDistr (pvVoteVoterId vote)
194325 in Right (ValidatedPerasVote {vpvVote = vote, vpvVoteStake = stake})
195326
327+ instance HasId (PerasCert blk ) where
328+ type IdOf (PerasCert blk ) = PerasRoundNo
329+ getId = pcCertRound
330+
331+ -- TODO: Orphan instance
332+ instance Serialise (KeyHash 'StakePool) where
333+ encode = KeyHash. toCBOR
334+ decode = KeyHash. fromCBOR
335+
336+ instance HasId (PerasVote blk ) where
337+ type IdOf (PerasVote blk ) = (PerasRoundNo , VoterId )
338+ getId vote = (pvVoteRound vote, pvVoteVoterId vote)
339+
196340instance ShowProxy blk => ShowProxy (PerasCert blk ) where
197341 showProxy _ = " PerasCert " <> showProxy (Proxy @ blk )
198342
@@ -211,17 +355,17 @@ instance Serialise (HeaderHash blk) => Serialise (PerasCert blk) where
211355 pure $ PerasCert {pcCertRound, pcCertBoostedBlock}
212356
213357instance Serialise (HeaderHash blk ) => Serialise (PerasVote blk ) where
214- encode PerasVote {pvVoteRound, pvVotedBlock, pvVoteStakePoolId } =
358+ encode PerasVote {pvVoteRound, pvVotedBlock, pvVoteVoterId } =
215359 encodeListLen 3
216360 <> encode pvVoteRound
217361 <> encode pvVotedBlock
218- <> KeyHash. toCBOR pvVoteStakePoolId
362+ <> KeyHash. toCBOR pvVoteVoterId
219363 decode = do
220364 decodeListLenOf 3
221365 pvVoteRound <- decode
222366 pvVotedBlock <- decode
223- pvVoteStakePoolId <- KeyHash. fromCBOR
224- pure $ PerasVote {pvVoteRound, pvVotedBlock, pvVoteStakePoolId }
367+ pvVoteVoterId <- KeyHash. fromCBOR
368+ pure $ PerasVote {pvVoteRound, pvVotedBlock, pvVoteVoterId }
225369
226370-- | Derive a 'PerasCfg' from a 'BlockConfig'
227371--
@@ -303,14 +447,26 @@ instance
303447 where
304448 getPerasVoteVotedBlock = getPerasVoteVotedBlock . forgetArrivalTime
305449
306- class HasStakePoolId vote where
307- getStakePoolId :: vote -> StakePoolId
308- instance HasStakePoolId (PerasVote blk ) where
309- getStakePoolId = pvVoteStakePoolId
310- instance HasStakePoolId (ValidatedPerasVote blk ) where
311- getStakePoolId = getStakePoolId . vpvVote
450+ class HasPerasVoteVoterId vote where
451+ getPerasVoteVoterId :: vote -> VoterId
452+ instance HasPerasVoteVoterId (PerasVote blk ) where
453+ getPerasVoteVoterId = pvVoteVoterId
454+ instance HasPerasVoteVoterId (ValidatedPerasVote blk ) where
455+ getPerasVoteVoterId = getPerasVoteVoterId . vpvVote
456+ instance
457+ HasPerasVoteVoterId vote =>
458+ HasPerasVoteVoterId (WithArrivalTime vote )
459+ where
460+ getPerasVoteVoterId = getPerasVoteVoterId . forgetArrivalTime
461+
462+ class HasPerasVoteTarget vote blk | vote -> blk where
463+ getPerasVoteTarget :: vote -> PerasVoteTarget blk
464+ instance HasPerasVoteTarget (PerasVote blk ) blk where
465+ getPerasVoteTarget vote = (pvVoteRound vote, pvVotedBlock vote)
466+ instance HasPerasVoteTarget (ValidatedPerasVote blk ) blk where
467+ getPerasVoteTarget vote = getPerasVoteTarget (vpvVote vote)
312468instance
313- HasStakePoolId vote =>
314- HasStakePoolId (WithArrivalTime vote )
469+ HasPerasVoteTarget vote blk =>
470+ HasPerasVoteTarget (WithArrivalTime vote ) blk
315471 where
316- getStakePoolId = getStakePoolId . forgetArrivalTime
472+ getPerasVoteTarget = getPerasVoteTarget . forgetArrivalTime
0 commit comments