Skip to content

Commit b5e6c96

Browse files
committed
VoteDB first implementation
1 parent 5c5be99 commit b5e6c96

File tree

5 files changed

+553
-57
lines changed

5 files changed

+553
-57
lines changed

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Block/SupportsPeras.hs

Lines changed: 180 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
99
{-# LANGUAGE NamedFieldPuns #-}
1010
{-# LANGUAGE ScopedTypeVariables #-}
11-
{-# LANGUAGE StandaloneDeriving #-}
1211
{-# LANGUAGE TypeApplications #-}
1312
{-# LANGUAGE TypeFamilies #-}
1413
{-# LANGUAGE UndecidableInstances #-}
@@ -17,20 +16,28 @@
1716
module 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

4047
import qualified Cardano.Binary as KeyHash
4148
import Cardano.Ledger.Core (KeyHash, KeyRole (StakePool))
42-
import Cardano.Ledger.State (IndividualPoolStake (..), PoolDistr (PoolDistr, unPoolDistr))
4349
import Codec.Serialise (Serialise (..))
4450
import Codec.Serialise.Decoding (decodeListLenOf)
4551
import Codec.Serialise.Encoding (encodeListLen)
52+
import Control.Applicative ((<|>))
4653
import Data.Coerce (coerce)
47-
import qualified Data.Map as Map
54+
import Data.Maybe (isNothing)
4855
import Data.Monoid (Sum (..))
4956
import Data.Proxy (Proxy (..))
57+
import Data.Set (Set)
58+
import qualified Data.Set as Set
5059
import Data.Word (Word64)
5160
import GHC.Generics (Generic)
5261
import NoThunks.Class
@@ -56,13 +65,40 @@ import Ouroboros.Consensus.Util
5665
import Ouroboros.Consensus.Util.Condense
5766
import 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+
5984
newtype 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

67103
instance 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

103150
data 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
152283
instance 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+
196340
instance 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

213357
instance 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)
312468
instance
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
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
module Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.ObjectPool.PerasVote where
1+
module Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.ObjectPool.PerasVote () where
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
module Ouroboros.Consensus.Storage.PerasVoteDB where
1+
module Ouroboros.Consensus.Storage.PerasVoteDB () where

0 commit comments

Comments
 (0)