Skip to content

Commit 4da2ef6

Browse files
committed
Introduce VoteDB, VoteAggregation and various related modules
1 parent 5e1807a commit 4da2ef6

File tree

7 files changed

+1002
-0
lines changed

7 files changed

+1002
-0
lines changed

ouroboros-consensus/ouroboros-consensus.cabal

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,10 @@ library
194194
Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.Inbound
195195
Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.ObjectPool.API
196196
Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.ObjectPool.PerasCert
197+
Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.ObjectPool.PerasVote
197198
Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.Outbound
198199
Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.PerasCert
200+
Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.PerasVote
199201
Ouroboros.Consensus.Node.GsmState
200202
Ouroboros.Consensus.Node.InitStorage
201203
Ouroboros.Consensus.Node.NetworkProtocolVersion
@@ -205,6 +207,7 @@ library
205207
Ouroboros.Consensus.NodeId
206208
Ouroboros.Consensus.Peras.Params
207209
Ouroboros.Consensus.Peras.SelectView
210+
Ouroboros.Consensus.Peras.VoteAggregation
208211
Ouroboros.Consensus.Peras.Weight
209212
Ouroboros.Consensus.Protocol.Abstract
210213
Ouroboros.Consensus.Protocol.BFT
@@ -271,6 +274,9 @@ library
271274
Ouroboros.Consensus.Storage.PerasCertDB
272275
Ouroboros.Consensus.Storage.PerasCertDB.API
273276
Ouroboros.Consensus.Storage.PerasCertDB.Impl
277+
Ouroboros.Consensus.Storage.PerasVoteDB
278+
Ouroboros.Consensus.Storage.PerasVoteDB.API
279+
Ouroboros.Consensus.Storage.PerasVoteDB.Impl
274280
Ouroboros.Consensus.Storage.Serialisation
275281
Ouroboros.Consensus.Storage.VolatileDB
276282
Ouroboros.Consensus.Storage.VolatileDB.API
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
{-# LANGUAGE GADTs #-}
2+
{-# LANGUAGE ScopedTypeVariables #-}
3+
{-# LANGUAGE StandaloneDeriving #-}
4+
5+
-- | Instantiate 'ObjectPoolReader' and 'ObjectPoolWriter' using Peras
6+
-- votes from the 'PerasVoteDB' (or the 'ChainDB' which is wrapping the
7+
-- 'PerasVoteDB').
8+
module Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.ObjectPool.PerasVote
9+
( makePerasVotePoolReaderFromVoteDB
10+
, makePerasVotePoolWriterFromVoteDB
11+
) where
12+
13+
import Control.Monad ((>=>))
14+
import Data.Map (Map)
15+
import qualified Data.Map as Map
16+
import GHC.Exception (throw)
17+
import Ouroboros.Consensus.Block
18+
import Ouroboros.Consensus.BlockchainTime.WallClock.Types
19+
( SystemTime (..)
20+
, WithArrivalTime (..)
21+
, addArrivalTime
22+
)
23+
import Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.ObjectPool.API
24+
import Ouroboros.Consensus.Storage.PerasVoteDB.API
25+
( PerasVoteDB
26+
, PerasVoteSnapshot
27+
, PerasVoteTicketNo
28+
)
29+
import qualified Ouroboros.Consensus.Storage.PerasVoteDB.API as PerasVoteDB
30+
import Ouroboros.Consensus.Util.IOLike
31+
32+
-- | TODO: replace by `Data.Map.take` as soon as we move to GHC 9.8
33+
takeAscMap :: Int -> Map k v -> Map k v
34+
takeAscMap n = Map.fromDistinctAscList . take n . Map.toAscList
35+
36+
makePerasVotePoolReaderFromSnapshot ::
37+
IOLike m =>
38+
STM m (PerasVoteSnapshot blk) ->
39+
ObjectPoolReader (PerasVoteId blk) (PerasVote blk) PerasVoteTicketNo m
40+
makePerasVotePoolReaderFromSnapshot getVoteSnapshot =
41+
ObjectPoolReader
42+
{ oprObjectId = getPerasVoteId
43+
, oprZeroTicketNo = PerasVoteDB.zeroPerasVoteTicketNo
44+
, oprObjectsAfter = \lastKnown limit -> do
45+
voteSnapshot <- getVoteSnapshot
46+
let votesAfterLastKnown =
47+
PerasVoteDB.getVotesAfter voteSnapshot lastKnown
48+
let loadVotesAfterLastKnown =
49+
pure $
50+
fmap
51+
(vpvVote . forgetArrivalTime)
52+
(takeAscMap (fromIntegral limit) votesAfterLastKnown)
53+
pure $
54+
if Map.null votesAfterLastKnown
55+
then Nothing
56+
else Just loadVotesAfterLastKnown
57+
}
58+
59+
makePerasVotePoolReaderFromVoteDB ::
60+
IOLike m =>
61+
PerasVoteDB m blk -> ObjectPoolReader (PerasVoteId blk) (PerasVote blk) PerasVoteTicketNo m
62+
makePerasVotePoolReaderFromVoteDB perasVoteDB =
63+
makePerasVotePoolReaderFromSnapshot (PerasVoteDB.getVoteSnapshot perasVoteDB)
64+
65+
makePerasVotePoolWriterFromVoteDB ::
66+
(StandardHash blk, IOLike m) =>
67+
-- TODO: We probably want to be able to fetch updated stake distribution throughout
68+
-- the lifetime of the writer
69+
-- But `StrictTVar m PerasVoteStakeDistr` might not be the best choice for that.
70+
StrictTVar m PerasVoteStakeDistr ->
71+
SystemTime m ->
72+
PerasVoteDB m blk ->
73+
ObjectPoolWriter (PerasVoteId blk) (PerasVote blk) m
74+
makePerasVotePoolWriterFromVoteDB distrVar systemTime perasVoteDB =
75+
ObjectPoolWriter
76+
{ opwObjectId = getPerasVoteId
77+
, opwAddObjects = \votes -> do
78+
distr <- readTVarIO distrVar
79+
addPerasVotes distr systemTime (PerasVoteDB.addVote perasVoteDB) votes
80+
, opwHasObject = do
81+
voteSnapshot <- PerasVoteDB.getVoteSnapshot perasVoteDB
82+
pure $ PerasVoteDB.containsVote voteSnapshot
83+
}
84+
85+
data PerasVoteInboundException
86+
= forall blk. PerasVoteValidationError (PerasValidationErr blk)
87+
88+
deriving instance Show PerasVoteInboundException
89+
90+
instance Exception PerasVoteInboundException
91+
92+
-- | Validate a list of 'PerasVote's, throwing a 'PerasVoteInboundException' if
93+
-- any of them are invalid.
94+
validatePerasVotes ::
95+
(StandardHash blk, MonadThrow m) =>
96+
PerasVoteStakeDistr ->
97+
[PerasVote blk] ->
98+
m [ValidatedPerasVote blk]
99+
validatePerasVotes distr votes = do
100+
let perasParams = mkPerasParams
101+
-- TODO pass down 'BlockConfig' when all the plumbing is in place
102+
-- see https://github.com/tweag/cardano-peras/issues/73
103+
-- see https://github.com/tweag/cardano-peras/issues/120
104+
case traverse (validatePerasVote perasParams distr) votes of
105+
Left validationErr -> throw (PerasVoteValidationError validationErr)
106+
Right validatedVotes -> return validatedVotes
107+
108+
-- | Add a list of 'PerasVote's into an object pool.
109+
--
110+
-- NOTE: we first validate the votes, throwing an exception if any of
111+
-- them are invalid. We then wrap them with their arrival time, and finally add
112+
-- them to the pool using the provided adder function.
113+
--
114+
-- The order of the first two operations (i.e., validation and timestamping) are
115+
-- rather arbitrary, and the abstract Peras protocol just assumes it can happen
116+
-- "within" a slot.
117+
addPerasVotes ::
118+
(StandardHash blk, MonadThrow m) =>
119+
PerasVoteStakeDistr ->
120+
SystemTime m ->
121+
(WithArrivalTime (ValidatedPerasVote blk) -> m a) ->
122+
[PerasVote blk] ->
123+
m ()
124+
addPerasVotes distr systemTime adder = do
125+
validatePerasVotes distr
126+
>=> mapM (addArrivalTime systemTime)
127+
>=> mapM_ adder
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
-- | This module defines type aliases for the ObjectDiffusion protocol applied
2+
-- to PerasVote diffusion.
3+
module Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.PerasVote
4+
( TracePerasVoteDiffusionInbound
5+
, TracePerasVoteDiffusionOutbound
6+
, PerasVotePoolReader
7+
, PerasVotePoolWriter
8+
, PerasVoteDiffusionInboundPipelined
9+
, PerasVoteDiffusionOutbound
10+
, PerasVoteDiffusion
11+
) where
12+
13+
import Ouroboros.Consensus.Block
14+
import Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.Inbound
15+
import Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.ObjectPool.API
16+
import Ouroboros.Consensus.MiniProtocol.ObjectDiffusion.Outbound
17+
import Ouroboros.Consensus.Storage.PerasVoteDB.API
18+
import Ouroboros.Network.Protocol.ObjectDiffusion.Inbound (ObjectDiffusionInboundPipelined)
19+
import Ouroboros.Network.Protocol.ObjectDiffusion.Outbound (ObjectDiffusionOutbound)
20+
import Ouroboros.Network.Protocol.ObjectDiffusion.Type (ObjectDiffusion)
21+
22+
type TracePerasVoteDiffusionInbound blk =
23+
TraceObjectDiffusionInbound PerasRoundNo (PerasVote blk)
24+
25+
type TracePerasVoteDiffusionOutbound blk =
26+
TraceObjectDiffusionOutbound PerasRoundNo (PerasVote blk)
27+
28+
type PerasVotePoolReader blk m =
29+
ObjectPoolReader PerasRoundNo (PerasVote blk) PerasVoteTicketNo m
30+
31+
type PerasVotePoolWriter blk m =
32+
ObjectPoolWriter PerasRoundNo (PerasVote blk) m
33+
34+
type PerasVoteDiffusionInboundPipelined blk m a =
35+
ObjectDiffusionInboundPipelined PerasRoundNo (PerasVote blk) m a
36+
37+
type PerasVoteDiffusionOutbound blk m a =
38+
ObjectDiffusionOutbound PerasRoundNo (PerasVote blk) m a
39+
40+
type PerasVoteDiffusion blk =
41+
ObjectDiffusion PerasRoundNo (PerasVote blk)

0 commit comments

Comments
 (0)