Skip to content

Commit

Permalink
simulation: progress on Leios (#81)
Browse files Browse the repository at this point in the history
* simulation: LeiosProtocol.Relay

* simulation: Commit untest DataTransfer protocol

* Fix errors after resolving compilation issues

* Scaffold data transfer protocol

* simulation: implemented relayProducer

* Progress on merge

* Finish merge

* Remove OldRelay

* Small improvements

* Tiny renaming

* Mostly finished scaffolding relay consumer

* Copy assert

* windowAdjust

* `idle !lst` in relayConsumer, for good measure

* added parameter for prioritization of bodies to relayConsumer

* simulation: implemented handleResponse for relayConsumer

* simulation: minor comments update

* relayConsumer: made max headers and bodies to req into params

* relayConsumer: added shared in-flight coordination

* SimTestRelay

* added vizualizations to test LeiosProtocol.Relay

* fix: relayProducer should start in StInit state

* fix: do not MsgRequestHeaders when both args are 0.

* fix: relayConsumer: do not submit and empty list of blocks.

* chanDriver: clarified error message

* worked around pipelined messages out of order by using ChanMux?

* simulation: comment about prioritize

* simulation: updated comment about submission order

* simulation: fix RB.empty to use `minBound + 1` as first ticket

so `takeAfterTicket buffer minBound` returns whole buffer.

* simulation: SimTestRelay: increased window size to 100

Last node in relay-test-2 was not getting blocks before expiration
otherwise.

* simulation: added relay-test-3 to viz command.

* simulation: copied over ConcreteBlock from ouroboros-network

* simulation: parametrize PraosProtocol by type of block body.

existing simulations still use BlockBody from ConcreteBlock.

* simulation: added types for leios blocks/messages

* Short Leios config and endorse/voting logic

* simulation: generation rates per slot, generator thread

* fourmolu

* simulation: implemented most of Leios.Short.Node

* simulation: refactored BlockFetch to take separate validation thread

* updated praos node to use a queue and set aside blocks until we have
validated the previous.

* updated leios node so that ranking block are now validated by the
same `processing` thread that does the others.

---------

Co-authored-by: Wen Kokke <[email protected]>
  • Loading branch information
Saizan and wenkokke authored Nov 22, 2024
1 parent 8fd48f4 commit 7b3bbf7
Show file tree
Hide file tree
Showing 28 changed files with 3,889 additions and 232 deletions.
31 changes: 26 additions & 5 deletions simulation/ouroboros-leios-sim.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ common performance-opts
-fspecialise-aggressively

library
default-extensions:
DisambiguateRecordFields
OverloadedRecordDot
ScopedTypeVariables

exposed-modules:
Chan
ChanDriver
Expand All @@ -41,6 +46,14 @@ library
ExamplesRelay
ExamplesRelayP2P
ExamplesTCP
LeiosProtocol.Common
LeiosProtocol.Relay
LeiosProtocol.RelayBuffer
LeiosProtocol.Short
LeiosProtocol.Short.Generate
LeiosProtocol.Short.Node
LeiosProtocol.SimTestRelay
LeiosProtocol.VizSimTestRelay
ModelTCP
P2P
PlotTCP
Expand All @@ -50,6 +63,7 @@ library
PraosProtocol.Common
PraosProtocol.Common.AnchoredFragment
PraosProtocol.Common.Chain
PraosProtocol.ConcreteBlock
PraosProtocol.ExamplesPraosP2P
PraosProtocol.PraosNode
PraosProtocol.SimBlockFetch
Expand Down Expand Up @@ -84,39 +98,46 @@ library
, bytestring
, cairo
, cardano-slotting
, cardano-strict-containers
, cborg
, Chart
, Chart-cairo
, colour
, containers
, contra-tracer
, deepseq
, fgl
, filepath
, fingertree
, gnuplot
, graphviz
, gtk3
, hashable
, io-classes
, io-sim
, kdt
, mtl
, nothunks
, ouroboros-network-api
, ouroboros-network-mock
, pango
, pqueue
, quiet
, random
, serialise
, si-timers
, sqlite-simple >=0.4
, singletons
, sqlite-simple >=0.4
, temporary
, text
, time
, typed-protocols >=0.3
, typed-protocols >=0.3
, vector
, zlib

hs-source-dirs: src
default-language: Haskell2010
ghc-options: -Wall
hs-source-dirs: src
default-language: Haskell2010
ghc-options: -Wall

if flag(perf)
import: performance-opts
Expand Down
2 changes: 1 addition & 1 deletion simulation/src/ChanDriver.hs
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@ chanDriver cmp ch =
case smsg of
SomeMessage @_ @st' msg -> case cmp @st @st' of
Just Refl -> pure (SomeMessage msg, ())
Nothing -> error "TODO"
Nothing -> error "recvMessage: read message state does not match expected state"
3 changes: 3 additions & 0 deletions simulation/src/ChanTCP.hs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ data LabelTcpDir e = DirClientToServer e | DirServerToClient e
class MessageSize msg where
messageSizeBytes :: msg -> Bytes

instance (MessageSize a, MessageSize b) => MessageSize (a, b) where
messageSizeBytes (a, b) = messageSizeBytes a + messageSizeBytes b

-- | Make a pair of 'Chan's, connected with a simulated bi-directional bearer
-- that emulates simple TCP performance behaviour.
--
Expand Down
228 changes: 228 additions & 0 deletions simulation/src/LeiosProtocol/Common.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE NoFieldSelectors #-}

module LeiosProtocol.Common (
module PraosProtocol.Common,
module Block,
module TimeCompat,
RankingBlockHeader,
RankingBlockBody (..),
RankingBlock,
RankingBlockId,
InputBlockId (..),
InputBlockHeader (..),
InputBlockBody (..),
InputBlock (..),
inputBlockInvariant,
EndorseBlockId (..),
EndorseBlock (..),
VoteId (..),
VoteMsg (..),
Certificate (..),
slice,
rankingBlockBodyInvariant,
NodeId,
SubSlotNo (..),
)
where

import ChanTCP
import Data.Hashable
import Data.Set (Set)
import Data.Word (Word8)
import GHC.Generics
import Ouroboros.Network.Block as Block
import PraosProtocol.Common (
ChainHash (..),
MessageSize (..),
PraosConfig (..),
ReadOnly,
SlotConfig (..),
TakeOnly,
asReadOnly,
asTakeOnly,
kilobytes,
readReadOnlyTVar,
readReadOnlyTVarIO,
slotConfigFromNow,
slotTime,
takeTakeOnlyTMVar,
tryTakeTakeOnlyTMVar,
)
import qualified PraosProtocol.Common as Praos
import SimTypes
import TimeCompat

{-
Note [size of blocks/messages]: we add a `size` field to most
entities to more easily allow size to be a runtime parameter.
Note [id fields]: we use uniquely generated `id` fields to identify
blocks instead of hashes (except for RankingBlock). They are also
used to verify the header belongs with the body, and anywhere a
reference to a block is needed. Represented as pairs `(NodeId,Int)`
to allow independent block generation from each node, but should be
considered opaque otherwise.
Note [subSlot, producer, endorseBlocksEarlierStage, endorseBlocksEarlierPipeline]:
these fields are needed to counter equivocation (subSlot, producer)
or other versions of Leios, we could remove them for now if they get
in the way, although it seems eventually we want to simulate all the
variants.
-}

type RankingBlockHeader = Praos.BlockHeader
data RankingBlockBody = RankingBlockBody
{ endorseBlocks :: ![(EndorseBlockId, Certificate)]
-- ^ at most one in short leios.
, payload :: !Bytes
-- ^ ranking blocks can also contain transactions directly, which we
-- do not model directly, but contribute to size.
, size :: !Bytes
}
deriving stock (Eq, Show, Generic)
deriving anyclass (Hashable)

rankingBlockBodyInvariant :: RankingBlockBody -> Bool
rankingBlockBodyInvariant rbb = rbb.payload <= rbb.size

type RankingBlock = Praos.Block RankingBlockBody

type RankingBlockId = HeaderHash RankingBlock

data InputBlockId = InputBlockId
{ node :: !NodeId
, num :: !Int
}
deriving stock (Eq, Ord, Show)

newtype SubSlotNo = SubSlotNo Word8
deriving stock (Show)
deriving newtype (Eq, Ord, Num, Enum, Bounded)

data InputBlockHeader = InputBlockHeader
{ id :: !InputBlockId
, slot :: !SlotNo
, subSlot :: !SubSlotNo
-- ^ for generation frequencies higher than 1 per slot.
, producer :: !NodeId
, rankingBlock :: !(ChainHash RankingBlock)
-- ^ points to ledger state for validation.
, size :: !Bytes
}
deriving stock (Eq, Show)

-- TODO: at time of writing IB are described to contain an advice field
-- Quote: `advice: advice describing which transaction scripts validated (phase 2 validation)`
-- Not represented or taken in consideration here.
data InputBlockBody = InputBlockBody
{ id :: !InputBlockId
, size :: !Bytes
-- ^ transactions not modeled, only their total size.
}
deriving stock (Eq, Show)

data InputBlock = InputBlock
{ header :: !InputBlockHeader
, body :: !InputBlockBody
}
deriving stock (Eq, Show)

inputBlockInvariant :: InputBlock -> Bool
inputBlockInvariant ib = ib.header.id == ib.body.id

data EndorseBlockId = EndorseBlockId
{ node :: !NodeId
, num :: !Int
}
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (Hashable)

data EndorseBlock = EndorseBlock
{ id :: !EndorseBlockId
, slot :: !SlotNo
, producer :: !NodeId
, inputBlocks :: ![InputBlockId]
, endorseBlocksEarlierStage :: ![EndorseBlockId]
-- ^ not used in "Short" leios.
, endorseBlocksEarlierPipeline :: ![EndorseBlockId]
-- ^ only used in "Full" leios.
, size :: !Bytes
}
deriving stock (Eq, Show)

data VoteId = VoteId
{ node :: !NodeId
, num :: !Int
}
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (Hashable)

data VoteMsg = VoteMsg
{ id :: !VoteId
, slot :: !SlotNo
, producer :: !NodeId
, endorseBlock :: !EndorseBlockId
, size :: !Bytes
}
deriving stock (Eq, Show)

data Certificate = Certificate
{ votes :: Set VoteId
}
deriving stock (Show, Eq, Generic)
deriving anyclass (Hashable)

-------------------------------------------
---- Common defs
-------------------------------------------

slice :: Int -> SlotNo -> Int -> (SlotNo, SlotNo)
slice l s x = (toEnum s', toEnum $ s' + l - 1)
where
-- taken from formal spec
s' = (fromEnum s `div` l - x) * l

-------------------------------------------
---- MessageSize instances
-------------------------------------------

instance MessageSize SubSlotNo where
messageSizeBytes _ = 1

instance MessageSize RankingBlockBody where
messageSizeBytes b = b.size

instance MessageSize InputBlockId where
messageSizeBytes _ = 32 {- hash -}

instance MessageSize InputBlockHeader where
messageSizeBytes b = b.size

instance MessageSize InputBlockBody where
messageSizeBytes b = b.size

instance MessageSize InputBlock where
messageSizeBytes b = messageSizeBytes b.header + messageSizeBytes b.body

instance MessageSize EndorseBlockId where
messageSizeBytes _ = 32 {- hash -}

instance MessageSize EndorseBlock where
messageSizeBytes b = b.size

instance MessageSize VoteId where
messageSizeBytes _ = 32 {- hash -}

instance MessageSize VoteMsg where
messageSizeBytes b = b.size
Loading

0 comments on commit 7b3bbf7

Please sign in to comment.