1+ {-# LANGUAGE DataKinds #-}
12{-# LANGUAGE DeriveAnyClass #-}
23{-# LANGUAGE DeriveGeneric #-}
34{-# LANGUAGE DerivingVia #-}
@@ -16,19 +17,30 @@ module Ouroboros.Consensus.Block.SupportsPeras
1617 , onPerasRoundNo
1718 , BlockSupportsPeras (.. )
1819 , PerasCert (.. )
20+ , PerasVote (.. )
1921 , ValidatedPerasCert (.. )
22+ , ValidatedPerasVote (.. )
2023 , HasPerasCertRound (.. )
2124 , HasPerasCertBoostedBlock (.. )
2225 , HasPerasCertBoost (.. )
26+ , HasPerasVoteRound (.. )
27+ , HasPerasVoteBlock (.. )
28+ , HasPerasVoteVoterId (.. )
2329
2430 -- * Convenience re-exports
2531 , module Ouroboros.Consensus.Peras.Params
2632 ) where
2733
34+ import qualified Cardano.Binary as KeyHash
35+ import Cardano.Ledger.Hashes (KeyHash , KeyRole (.. ))
36+ import Cardano.Ledger.State (IndividualPoolStake )
37+ import qualified Cardano.Ledger.State as SL
2838import Codec.Serialise (Serialise (.. ))
2939import Codec.Serialise.Decoding (decodeListLenOf )
3040import Codec.Serialise.Encoding (encodeListLen )
3141import Data.Coerce (coerce )
42+ import Data.Function (on )
43+ import qualified Data.Map as Map
3244import Data.Proxy (Proxy (.. ))
3345import Data.Word (Word64 )
3446import GHC.Generics (Generic )
@@ -40,6 +52,12 @@ import Ouroboros.Consensus.Util
4052import Ouroboros.Consensus.Util.Condense
4153import Quiet (Quiet (.. ))
4254
55+ {- ------------------------------------------------------------------------------
56+ -- * Peras types
57+ -------------------------------------------------------------------------------}
58+
59+ -- ** Round numbers
60+
4361newtype PerasRoundNo = PerasRoundNo { unPerasRoundNo :: Word64 }
4462 deriving Show via Quiet PerasRoundNo
4563 deriving stock Generic
@@ -57,14 +75,56 @@ onPerasRoundNo ::
5775 (PerasRoundNo -> PerasRoundNo -> PerasRoundNo )
5876onPerasRoundNo = coerce
5977
60- -- TODO using 'Validated' for extra safety? Or some @.Unsafe@ module?
78+ -- ** Stake pool distributions
79+
80+ newtype PerasVoterId = PerasVoterId
81+ { unPerasVoterId :: KeyHash StakePool
82+ }
83+ deriving stock (Show , Eq , Ord , Generic )
84+ deriving newtype NoThunks
85+
86+ newtype PerasIndividualVoterStake = PerasIndividualVoterStake
87+ { perasIndividualVoterStake :: IndividualPoolStake
88+ }
89+ deriving stock (Show , Eq , Generic )
90+ deriving newtype NoThunks
91+
92+ instance Ord PerasIndividualVoterStake where
93+ compare = compare `on` (SL. individualPoolStakeVrf . perasIndividualVoterStake)
94+
95+ newtype PerasVoterStakeDistr = PerasVotingStakeDistr
96+ { unPerasVotingStakeDistr :: SL. PoolDistr
97+ }
98+ deriving stock (Show , Eq , Generic )
99+ deriving newtype NoThunks
100+
101+ -- | Lookup the stake of a vote's stake pool in a given stake distribution.
102+ lookupPerasVoterStake ::
103+ PerasVote blk ->
104+ PerasVoterStakeDistr ->
105+ Maybe PerasIndividualVoterStake
106+ lookupPerasVoterStake vote (PerasVotingStakeDistr distr) =
107+ PerasIndividualVoterStake
108+ <$> Map. lookup
109+ (unPerasVoterId (pvVoteVoterId vote))
110+ (SL. unPoolDistr distr)
111+
112+ -- ** Validated types
113+
61114data ValidatedPerasCert blk = ValidatedPerasCert
62115 { vpcCert :: ! (PerasCert blk )
63116 , vpcCertBoost :: ! PerasWeight
64117 }
65118 deriving stock (Show , Eq , Ord , Generic )
66119 deriving anyclass NoThunks
67120
121+ data ValidatedPerasVote blk = ValidatedPerasVote
122+ { vpvVote :: ! (PerasVote blk )
123+ , vpvVoteStake :: ! PerasIndividualVoterStake
124+ }
125+ deriving stock (Show , Eq , Ord , Generic )
126+ deriving anyclass NoThunks
127+
68128{- ------------------------------------------------------------------------------
69129-- * BlockSupportsPeras class
70130-------------------------------------------------------------------------------}
@@ -79,13 +139,21 @@ class
79139
80140 data PerasCert blk
81141
142+ data PerasVote blk
143+
82144 data PerasValidationErr blk
83145
84146 validatePerasCert ::
85147 PerasCfg blk ->
86148 PerasCert blk ->
87149 Either (PerasValidationErr blk ) (ValidatedPerasCert blk )
88150
151+ validatePerasVote ::
152+ PerasCfg blk ->
153+ PerasVote blk ->
154+ PerasVoterStakeDistr ->
155+ Either (PerasValidationErr blk ) (ValidatedPerasVote blk )
156+
89157-- TODO: degenerate instance for all blks to get things to compile
90158-- see https://github.com/tweag/cardano-peras/issues/73
91159instance StandardHash blk => BlockSupportsPeras blk where
@@ -98,6 +166,14 @@ instance StandardHash blk => BlockSupportsPeras blk where
98166 deriving stock (Generic , Eq , Ord , Show )
99167 deriving anyclass NoThunks
100168
169+ data PerasVote blk = PerasVote
170+ { pvVoteRound :: PerasRoundNo
171+ , pvVoteBlock :: Point blk
172+ , pvVoteVoterId :: PerasVoterId
173+ }
174+ deriving stock (Generic , Eq , Ord , Show )
175+ deriving anyclass NoThunks
176+
101177 -- TODO: enrich with actual error types
102178 -- see https://github.com/tweag/cardano-peras/issues/120
103179 data PerasValidationErr blk
@@ -114,9 +190,25 @@ instance StandardHash blk => BlockSupportsPeras blk where
114190 , vpcCertBoost = perasWeight params
115191 }
116192
193+ -- TODO: perform actual validation against all
194+ -- possible 'PerasValidationErr' variants
195+ -- see https://github.com/tweag/cardano-peras/issues/120
196+ validatePerasVote _params vote stakeDistr
197+ | Just stake <- lookupPerasVoterStake vote stakeDistr =
198+ Right
199+ ValidatedPerasVote
200+ { vpvVote = vote
201+ , vpvVoteStake = stake
202+ }
203+ | otherwise =
204+ Left PerasValidationErr
205+
117206instance ShowProxy blk => ShowProxy (PerasCert blk ) where
118207 showProxy _ = " PerasCert " <> showProxy (Proxy @ blk )
119208
209+ instance ShowProxy blk => ShowProxy (PerasVote blk ) where
210+ showProxy _ = " PerasVote " <> showProxy (Proxy @ blk )
211+
120212instance Serialise (HeaderHash blk ) => Serialise (PerasCert blk ) where
121213 encode PerasCert {pcCertRound, pcCertBoostedBlock} =
122214 encodeListLen 2
@@ -128,6 +220,19 @@ instance Serialise (HeaderHash blk) => Serialise (PerasCert blk) where
128220 pcCertBoostedBlock <- decode
129221 pure $ PerasCert {pcCertRound, pcCertBoostedBlock}
130222
223+ instance Serialise (HeaderHash blk ) => Serialise (PerasVote blk ) where
224+ encode PerasVote {pvVoteRound, pvVoteBlock, pvVoteVoterId} =
225+ encodeListLen 3
226+ <> encode pvVoteRound
227+ <> encode pvVoteBlock
228+ <> KeyHash. toCBOR (unPerasVoterId pvVoteVoterId)
229+ decode = do
230+ decodeListLenOf 3
231+ pvVoteRound <- decode
232+ pvVoteBlock <- decode
233+ pvVoteVoterId <- PerasVoterId <$> KeyHash. fromCBOR
234+ pure $ PerasVote {pvVoteRound, pvVoteBlock, pvVoteVoterId}
235+
131236-- | Extract the certificate round from a Peras certificate container
132237class HasPerasCertRound cert where
133238 getPerasCertRound :: cert -> PerasRoundNo
@@ -172,3 +277,51 @@ instance
172277 HasPerasCertBoost (WithArrivalTime cert )
173278 where
174279 getPerasCertBoost = getPerasCertBoost . forgetArrivalTime
280+
281+ -- | Extract the vote round from a Peras vote container
282+ class HasPerasVoteRound vote where
283+ getPerasVoteRound :: vote -> PerasRoundNo
284+
285+ instance HasPerasVoteRound (PerasVote blk ) where
286+ getPerasVoteRound = pvVoteRound
287+
288+ instance HasPerasVoteRound (ValidatedPerasVote blk ) where
289+ getPerasVoteRound = getPerasVoteRound . vpvVote
290+
291+ instance
292+ HasPerasVoteRound vote =>
293+ HasPerasVoteRound (WithArrivalTime vote )
294+ where
295+ getPerasVoteRound = getPerasVoteRound . forgetArrivalTime
296+
297+ -- | Extract the vote block point from a Peras vote container
298+ class HasPerasVoteBlock vote blk | vote -> blk where
299+ getPerasVoteBlock :: vote -> Point blk
300+
301+ instance HasPerasVoteBlock (PerasVote blk ) blk where
302+ getPerasVoteBlock = pvVoteBlock
303+
304+ instance HasPerasVoteBlock (ValidatedPerasVote blk ) blk where
305+ getPerasVoteBlock = getPerasVoteBlock . vpvVote
306+
307+ instance
308+ HasPerasVoteBlock vote blk =>
309+ HasPerasVoteBlock (WithArrivalTime vote ) blk
310+ where
311+ getPerasVoteBlock = getPerasVoteBlock . forgetArrivalTime
312+
313+ -- | Extract the stake pool ID from a Peras vote container
314+ class HasPerasVoteVoterId vote where
315+ getPerasVoteVoterId :: vote -> PerasVoterId
316+
317+ instance HasPerasVoteVoterId (PerasVote blk ) where
318+ getPerasVoteVoterId = pvVoteVoterId
319+
320+ instance HasPerasVoteVoterId (ValidatedPerasVote blk ) where
321+ getPerasVoteVoterId = getPerasVoteVoterId . vpvVote
322+
323+ instance
324+ HasPerasVoteVoterId vote =>
325+ HasPerasVoteVoterId (WithArrivalTime vote )
326+ where
327+ getPerasVoteVoterId = getPerasVoteVoterId . forgetArrivalTime
0 commit comments