Skip to content

Commit 1086cc0

Browse files
authored
feat(simulation): initial support for shared topology files (#151)
1 parent 29f75e3 commit 1086cc0

File tree

7 files changed

+679
-464
lines changed

7 files changed

+679
-464
lines changed

.github/workflows/ci.yaml

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ name: CI
33
# TODO: If these environment variables only affect Nix, should they be moved under the `formal-spec-check` job?
44
env:
55
ALLOWED_URIS: "https://github.com https://api.github.com"
6-
TRUSTED_PUBLIC_KEYS: "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="
6+
TRUSTED_PUBLIC_KEYS:
7+
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
8+
hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="
79
SUBSTITUTERS: "https://cache.nixos.org/ https://cache.iog.io"
810

911
on:
@@ -73,23 +75,22 @@ jobs:
7375
7476
- name: ➤💾 Export Nix store cache
7577
if: "steps.nix-cache.outputs.cache-hit != 'true'"
76-
run: "nix-store --export $(find /nix/store -maxdepth 1 -name '*-*') > /tmp/nixcache"
78+
run:
79+
"nix-store --export $(find /nix/store -maxdepth 1 -name '*-*') >
80+
/tmp/nixcache"
7781

7882
################################################################################
7983
# Simulation and Prototype in Haskell - under /simulation/
8084
################################################################################
8185

8286
simulation-test:
83-
name: "simulation: Test on ${{ matrix.os }} with GHC ${{ matrix.ghc-version }}"
84-
if: |
85-
github.event_name == 'push' &&
86-
(contains(github.event.commits.*.modified, 'simulation/') ||
87-
contains(github.event.commits.*.modified, 'data/') ||
88-
contains(github.event.commits.*.modified, 'cabal.project')) ||
89-
github.event_name == 'pull_request' &&
90-
(contains(github.event.pull_request.files.*.path, 'simulation/') ||
91-
contains(github.event.pull_request.files.*.path, 'data/') ||
92-
contains(github.event.pull_request.files.*.path, 'cabal.project'))
87+
name:
88+
"simulation: Test on ${{ matrix.os }} with GHC ${{ matrix.ghc-version }}"
89+
# TODO: fix this conditional
90+
# if:
91+
# contains(github.event.commits.*.modified, 'simulation/') ||
92+
# contains(github.event.commits.*.modified, 'data/') ||
93+
# contains(github.event.commits.*.modified, 'cabal.project'))
9394
runs-on: ${{ matrix.os }}
9495
strategy:
9596
fail-fast: false
@@ -128,10 +129,14 @@ jobs:
128129
uses: actions/cache/restore@v4
129130
id: cache
130131
env:
131-
key: ${{ runner.os }}-ghc-${{ steps.setup.outputs.ghc-version }}-cabal-${{ steps.setup.outputs.cabal-version }}
132+
key:
133+
${{ runner.os }}-ghc-${{ steps.setup.outputs.ghc-version
134+
}}-cabal-${{ steps.setup.outputs.cabal-version }}
132135
with:
133136
path: ${{ steps.setup.outputs.cabal-store }}
134-
key: ${{ env.key }}-plan-${{ hashFiles('dist-newstyle/cache/plan.json') }}
137+
key:
138+
${{ env.key }}-plan-${{ hashFiles('dist-newstyle/cache/plan.json')
139+
}}
135140
restore-keys: ${{ env.key }}-
136141

137142
- name: 🛠️ Install Cabal dependencies
@@ -156,15 +161,11 @@ jobs:
156161

157162
simulation-hlint:
158163
name: "simulation: Check with HLint"
159-
if: |
160-
github.event_name == 'push' &&
161-
(contains(github.event.commits.*.modified, 'simulation/') ||
162-
contains(github.event.commits.*.modified, 'data/') ||
163-
contains(github.event.commits.*.modified, 'cabal.project')) ||
164-
github.event_name == 'pull_request' &&
165-
(contains(github.event.pull_request.files.*.path, 'simulation/') ||
166-
contains(github.event.pull_request.files.*.path, 'data/') ||
167-
contains(github.event.pull_request.files.*.path, 'cabal.project'))
164+
# TODO: fix this conditional
165+
# if:
166+
# contains(github.event.commits.*.modified, 'simulation/') ||
167+
# contains(github.event.commits.*.modified, 'data/') ||
168+
# contains(github.event.commits.*.modified, 'cabal.project'))
168169
runs-on: ubuntu-22.04
169170
steps:
170171
- name: 📥 Checkout repository
@@ -181,15 +182,11 @@ jobs:
181182

182183
simulation-fourmolu:
183184
name: "simulation: Check with fourmolu"
184-
if: |
185-
github.event_name == 'push' &&
186-
(contains(github.event.commits.*.modified, 'simulation/') ||
187-
contains(github.event.commits.*.modified, 'data/') ||
188-
contains(github.event.commits.*.modified, 'cabal.project')) ||
189-
github.event_name == 'pull_request' &&
190-
(contains(github.event.pull_request.files.*.path, 'simulation/') ||
191-
contains(github.event.pull_request.files.*.path, 'data/') ||
192-
contains(github.event.pull_request.files.*.path, 'cabal.project'))
185+
# TODO: fix this conditional
186+
# if:
187+
# contains(github.event.commits.*.modified, 'simulation/') ||
188+
# contains(github.event.commits.*.modified, 'data/') ||
189+
# contains(github.event.commits.*.modified, 'cabal.project'))
193190
runs-on: ubuntu-22.04
194191
steps:
195192
- name: 📥 Checkout repository

simulation/ouroboros-leios-sim.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ library
6464
LeiosProtocol.TaskMultiQueue
6565
LeiosProtocol.VizSimTestRelay
6666
ModelTCP
67+
JSONCompat
6768
P2P
6869
PlotTCP
6970
PraosProtocol.BlockFetch
@@ -141,6 +142,7 @@ library
141142
, pqueue
142143
, quiet
143144
, random
145+
, scientific
144146
, serialise
145147
, si-timers
146148
, singletons

simulation/src/JSONCompat.hs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{-# LANGUAGE AllowAmbiguousTypes #-}
2+
{-# LANGUAGE DataKinds #-}
3+
{-# LANGUAGE DerivingStrategies #-}
4+
{-# LANGUAGE FlexibleContexts #-}
5+
{-# LANGUAGE FlexibleInstances #-}
6+
{-# LANGUAGE RankNTypes #-}
7+
{-# LANGUAGE TypeApplications #-}
8+
9+
module JSONCompat where
10+
11+
import Data.Aeson.Key (fromString)
12+
import Data.Aeson.Types (FromJSON (..), KeyValue ((.=)), Object, Parser, ToJSON (..), (.!=), (.:), (.:?))
13+
import Data.Char (isUpper, toLower, toUpper)
14+
import Data.Default (Default (..))
15+
import GHC.Records (HasField (..))
16+
import GHC.TypeLits (KnownSymbol (..), SSymbol, fromSSymbol)
17+
18+
kebabToCamel :: String -> String
19+
kebabToCamel = go False
20+
where
21+
go _ [] = []
22+
go _ ('-' : cs) = go True cs
23+
go b (c : cs) = (if b then toUpper c else c) : go False cs
24+
25+
camelToKebab :: String -> String
26+
camelToKebab [] = []
27+
camelToKebab (c : cs)
28+
| isUpper c = '-' : toLower c : camelToKebab cs
29+
| otherwise = c : camelToKebab cs
30+
31+
newtype Getter r = Getter {unGetter :: forall f v e kv. SSymbol f -> (HasField f r v, KeyValue e kv, ToJSON v, Eq v) => r -> Maybe kv}
32+
33+
get :: forall fld obj e kv a. (KnownSymbol fld, HasField fld obj a, KeyValue e kv, ToJSON a, Eq a) => Getter obj -> obj -> Maybe kv
34+
get (Getter getter) = getter (symbolSing @fld)
35+
36+
always :: Getter r
37+
always = Getter $ \(fld :: SSymbol fld) obj ->
38+
let key = fromString (camelToKebab (fromSSymbol fld))
39+
val = getField @fld obj
40+
in Just (key .= val)
41+
42+
omitDefault :: Default r => Getter r
43+
omitDefault = Getter $ \(fld :: SSymbol fld) obj ->
44+
let key = fromString (camelToKebab (fromSSymbol fld))
45+
getFld = getField @fld
46+
val = getFld obj
47+
in if val == getFld def then Nothing else Just (key .= val)
48+
49+
parseFieldOrDefault :: forall obj fld a. (HasField fld obj a, Default obj, KnownSymbol fld, FromJSON a) => Object -> Parser a
50+
parseFieldOrDefault obj =
51+
obj .:? fromString (camelToKebab (fromSSymbol (symbolSing @fld))) .!= getField @fld (def :: obj)
52+
53+
parseField :: forall obj fld a. (HasField fld obj a, KnownSymbol fld, FromJSON a) => Object -> Parser a
54+
parseField obj = obj .: fromString (camelToKebab (fromSSymbol (symbolSing @fld)))

simulation/src/LeiosProtocol/Config.hs

Lines changed: 43 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,12 @@
1515
module LeiosProtocol.Config where
1616

1717
import Data.Aeson.Encoding (pairs)
18-
import Data.Aeson.Key (fromString)
19-
import Data.Aeson.Types (Encoding, FromJSON (..), KeyValue ((.=)), Object, Parser, ToJSON (..), Value (..), object, typeMismatch, withObject, (.!=), (.:), (.:?))
20-
import Data.Char (isUpper, toLower, toUpper)
18+
import Data.Aeson.Types (Encoding, FromJSON (..), KeyValue ((.=)), Parser, ToJSON (..), Value (..), object, typeMismatch, withObject, (.:))
2119
import Data.Default (Default (..))
2220
import Data.Maybe (catMaybes)
2321
import Data.Text (Text)
2422
import GHC.Generics (Generic)
25-
import GHC.Records (HasField (..))
26-
import GHC.TypeLits (KnownSymbol (..), SSymbol, fromSSymbol)
23+
import JSONCompat (Getter, always, get, omitDefault, parseFieldOrDefault)
2724

2825
newtype SizeBytes = SizeBytes {unSizeBytes :: Word}
2926
deriving newtype (Show, Eq, Ord, FromJSON, ToJSON, Num, Real, Enum, Integral)
@@ -38,19 +35,6 @@ data Distribution
3835
| LogNormal {mu :: Double, sigma :: Double}
3936
deriving (Show, Eq, Generic)
4037

41-
kebabToCamel :: String -> String
42-
kebabToCamel = go False
43-
where
44-
go _ [] = []
45-
go _ ('-' : cs) = go True cs
46-
go b (c : cs) = (if b then toUpper c else c) : go False cs
47-
48-
camelToKebab :: String -> String
49-
camelToKebab [] = []
50-
camelToKebab (c : cs)
51-
| isUpper c = '-' : toLower c : camelToKebab cs
52-
| otherwise = c : camelToKebab cs
53-
5438
data Config = Config
5539
{ leiosStageLengthSlots :: Word
5640
, leiosStageActiveVotingSlots :: Word
@@ -143,24 +127,6 @@ instance Default Config where
143127
, certSizeBytesPerNode = 32
144128
}
145129

146-
newtype Getter r = Getter {unGetter :: forall f v e kv. SSymbol f -> (HasField f r v, KeyValue e kv, ToJSON v, Eq v) => r -> Maybe kv}
147-
148-
get :: forall fld obj e kv a. (KnownSymbol fld, HasField fld obj a, KeyValue e kv, ToJSON a, Eq a) => Getter obj -> obj -> Maybe kv
149-
get (Getter getter) = getter (symbolSing @fld)
150-
151-
always :: Getter r
152-
always = Getter $ \(fld :: SSymbol fld) obj ->
153-
let key = fromString (camelToKebab (fromSSymbol fld))
154-
val = getField @fld obj
155-
in Just (key .= val)
156-
157-
omitDefault :: Default r => Getter r
158-
omitDefault = Getter $ \(fld :: SSymbol fld) obj ->
159-
let key = fromString (camelToKebab (fromSSymbol fld))
160-
getFld = getField @fld
161-
val = getFld obj
162-
in if val == getFld def then Nothing else Just (key .= val)
163-
164130
configToJSONWith :: Getter Config -> Config -> Value
165131
configToJSONWith getter = object . configToKVsWith getter
166132

@@ -230,53 +196,49 @@ instance ToJSON (OmitDefault Config) where
230196
toEncoding :: OmitDefault Config -> Encoding
231197
toEncoding (OmitDefault cfg) = configToEncodingWith omitDefault cfg
232198

233-
parseFieldOrDefault :: forall fld a. (HasField fld Config a, KnownSymbol fld, FromJSON a) => Object -> Parser a
234-
parseFieldOrDefault obj =
235-
obj .:? fromString (camelToKebab (fromSSymbol (symbolSing @fld))) .!= getField @fld (def :: Config)
236-
237199
instance FromJSON Config where
238200
parseJSON = withObject "Config" $ \obj -> do
239-
leiosStageLengthSlots <- parseFieldOrDefault @"leiosStageLengthSlots" obj
240-
leiosStageActiveVotingSlots <- parseFieldOrDefault @"leiosStageActiveVotingSlots" obj
241-
txGenerationDistribution <- parseFieldOrDefault @"txGenerationDistribution" obj
242-
txSizeBytesDistribution <- parseFieldOrDefault @"txSizeBytesDistribution" obj
243-
txValidationCpuTimeMs <- parseFieldOrDefault @"txValidationCpuTimeMs" obj
244-
txMaxSizeBytes <- parseFieldOrDefault @"txMaxSizeBytes" obj
245-
rbGenerationProbability <- parseFieldOrDefault @"rbGenerationProbability" obj
246-
rbGenerationCpuTimeMs <- parseFieldOrDefault @"rbGenerationCpuTimeMs" obj
247-
rbHeadValidationCpuTimeMs <- parseFieldOrDefault @"rbHeadValidationCpuTimeMs" obj
248-
rbHeadSizeBytes <- parseFieldOrDefault @"rbHeadSizeBytes" obj
249-
rbBodyMaxSizeBytes <- parseFieldOrDefault @"rbBodyMaxSizeBytes" obj
250-
rbBodyLegacyPraosPayloadValidationCpuTimeMsConstant <- parseFieldOrDefault @"rbBodyLegacyPraosPayloadValidationCpuTimeMsConstant" obj
251-
rbBodyLegacyPraosPayloadValidationCpuTimeMsPerByte <- parseFieldOrDefault @"rbBodyLegacyPraosPayloadValidationCpuTimeMsPerByte" obj
252-
rbBodyLegacyPraosPayloadAvgSizeBytes <- parseFieldOrDefault @"rbBodyLegacyPraosPayloadAvgSizeBytes" obj
253-
ibGenerationProbability <- parseFieldOrDefault @"ibGenerationProbability" obj
254-
ibGenerationCpuTimeMs <- parseFieldOrDefault @"ibGenerationCpuTimeMs" obj
255-
ibHeadSizeBytes <- parseFieldOrDefault @"ibHeadSizeBytes" obj
256-
ibHeadValidationCpuTimeMs <- parseFieldOrDefault @"ibHeadValidationCpuTimeMs" obj
257-
ibBodyValidationCpuTimeMsConstant <- parseFieldOrDefault @"ibBodyValidationCpuTimeMsConstant" obj
258-
ibBodyValidationCpuTimeMsPerByte <- parseFieldOrDefault @"ibBodyValidationCpuTimeMsPerByte" obj
259-
ibBodyMaxSizeBytes <- parseFieldOrDefault @"ibBodyMaxSizeBytes" obj
260-
ibBodyAvgSizeBytes <- parseFieldOrDefault @"ibBodyAvgSizeBytes" obj
261-
ebGenerationProbability <- parseFieldOrDefault @"ebGenerationProbability" obj
262-
ebGenerationCpuTimeMs <- parseFieldOrDefault @"ebGenerationCpuTimeMs" obj
263-
ebValidationCpuTimeMs <- parseFieldOrDefault @"ebValidationCpuTimeMs" obj
264-
ebSizeBytesConstant <- parseFieldOrDefault @"ebSizeBytesConstant" obj
265-
ebSizeBytesPerIb <- parseFieldOrDefault @"ebSizeBytesPerIb" obj
266-
voteGenerationProbability <- parseFieldOrDefault @"voteGenerationProbability" obj
267-
voteGenerationCpuTimeMsConstant <- parseFieldOrDefault @"voteGenerationCpuTimeMsConstant" obj
268-
voteGenerationCpuTimeMsPerIb <- parseFieldOrDefault @"voteGenerationCpuTimeMsPerIb" obj
269-
voteValidationCpuTimeMs <- parseFieldOrDefault @"voteValidationCpuTimeMs" obj
270-
voteThreshold <- parseFieldOrDefault @"voteThreshold" obj
271-
voteOneEbPerVrfWin <- parseFieldOrDefault @"voteOneEbPerVrfWin" obj
272-
voteSizeBytesConstant <- parseFieldOrDefault @"voteSizeBytesConstant" obj
273-
voteSizeBytesPerNode <- parseFieldOrDefault @"voteSizeBytesPerNode" obj
274-
certGenerationCpuTimeMsConstant <- parseFieldOrDefault @"certGenerationCpuTimeMsConstant" obj
275-
certGenerationCpuTimeMsPerNode <- parseFieldOrDefault @"certGenerationCpuTimeMsPerNode" obj
276-
certValidationCpuTimeMsConstant <- parseFieldOrDefault @"certValidationCpuTimeMsConstant" obj
277-
certValidationCpuTimeMsPerNode <- parseFieldOrDefault @"certValidationCpuTimeMsPerNode" obj
278-
certSizeBytesConstant <- parseFieldOrDefault @"certSizeBytesConstant" obj
279-
certSizeBytesPerNode <- parseFieldOrDefault @"certSizeBytesPerNode" obj
201+
leiosStageLengthSlots <- parseFieldOrDefault @Config @"leiosStageLengthSlots" obj
202+
leiosStageActiveVotingSlots <- parseFieldOrDefault @Config @"leiosStageActiveVotingSlots" obj
203+
txGenerationDistribution <- parseFieldOrDefault @Config @"txGenerationDistribution" obj
204+
txSizeBytesDistribution <- parseFieldOrDefault @Config @"txSizeBytesDistribution" obj
205+
txValidationCpuTimeMs <- parseFieldOrDefault @Config @"txValidationCpuTimeMs" obj
206+
txMaxSizeBytes <- parseFieldOrDefault @Config @"txMaxSizeBytes" obj
207+
rbGenerationProbability <- parseFieldOrDefault @Config @"rbGenerationProbability" obj
208+
rbGenerationCpuTimeMs <- parseFieldOrDefault @Config @"rbGenerationCpuTimeMs" obj
209+
rbHeadValidationCpuTimeMs <- parseFieldOrDefault @Config @"rbHeadValidationCpuTimeMs" obj
210+
rbHeadSizeBytes <- parseFieldOrDefault @Config @"rbHeadSizeBytes" obj
211+
rbBodyMaxSizeBytes <- parseFieldOrDefault @Config @"rbBodyMaxSizeBytes" obj
212+
rbBodyLegacyPraosPayloadValidationCpuTimeMsConstant <- parseFieldOrDefault @Config @"rbBodyLegacyPraosPayloadValidationCpuTimeMsConstant" obj
213+
rbBodyLegacyPraosPayloadValidationCpuTimeMsPerByte <- parseFieldOrDefault @Config @"rbBodyLegacyPraosPayloadValidationCpuTimeMsPerByte" obj
214+
rbBodyLegacyPraosPayloadAvgSizeBytes <- parseFieldOrDefault @Config @"rbBodyLegacyPraosPayloadAvgSizeBytes" obj
215+
ibGenerationProbability <- parseFieldOrDefault @Config @"ibGenerationProbability" obj
216+
ibGenerationCpuTimeMs <- parseFieldOrDefault @Config @"ibGenerationCpuTimeMs" obj
217+
ibHeadSizeBytes <- parseFieldOrDefault @Config @"ibHeadSizeBytes" obj
218+
ibHeadValidationCpuTimeMs <- parseFieldOrDefault @Config @"ibHeadValidationCpuTimeMs" obj
219+
ibBodyValidationCpuTimeMsConstant <- parseFieldOrDefault @Config @"ibBodyValidationCpuTimeMsConstant" obj
220+
ibBodyValidationCpuTimeMsPerByte <- parseFieldOrDefault @Config @"ibBodyValidationCpuTimeMsPerByte" obj
221+
ibBodyMaxSizeBytes <- parseFieldOrDefault @Config @"ibBodyMaxSizeBytes" obj
222+
ibBodyAvgSizeBytes <- parseFieldOrDefault @Config @"ibBodyAvgSizeBytes" obj
223+
ebGenerationProbability <- parseFieldOrDefault @Config @"ebGenerationProbability" obj
224+
ebGenerationCpuTimeMs <- parseFieldOrDefault @Config @"ebGenerationCpuTimeMs" obj
225+
ebValidationCpuTimeMs <- parseFieldOrDefault @Config @"ebValidationCpuTimeMs" obj
226+
ebSizeBytesConstant <- parseFieldOrDefault @Config @"ebSizeBytesConstant" obj
227+
ebSizeBytesPerIb <- parseFieldOrDefault @Config @"ebSizeBytesPerIb" obj
228+
voteGenerationProbability <- parseFieldOrDefault @Config @"voteGenerationProbability" obj
229+
voteGenerationCpuTimeMsConstant <- parseFieldOrDefault @Config @"voteGenerationCpuTimeMsConstant" obj
230+
voteGenerationCpuTimeMsPerIb <- parseFieldOrDefault @Config @"voteGenerationCpuTimeMsPerIb" obj
231+
voteValidationCpuTimeMs <- parseFieldOrDefault @Config @"voteValidationCpuTimeMs" obj
232+
voteThreshold <- parseFieldOrDefault @Config @"voteThreshold" obj
233+
voteOneEbPerVrfWin <- parseFieldOrDefault @Config @"voteOneEbPerVrfWin" obj
234+
voteSizeBytesConstant <- parseFieldOrDefault @Config @"voteSizeBytesConstant" obj
235+
voteSizeBytesPerNode <- parseFieldOrDefault @Config @"voteSizeBytesPerNode" obj
236+
certGenerationCpuTimeMsConstant <- parseFieldOrDefault @Config @"certGenerationCpuTimeMsConstant" obj
237+
certGenerationCpuTimeMsPerNode <- parseFieldOrDefault @Config @"certGenerationCpuTimeMsPerNode" obj
238+
certValidationCpuTimeMsConstant <- parseFieldOrDefault @Config @"certValidationCpuTimeMsConstant" obj
239+
certValidationCpuTimeMsPerNode <- parseFieldOrDefault @Config @"certValidationCpuTimeMsPerNode" obj
240+
certSizeBytesConstant <- parseFieldOrDefault @Config @"certSizeBytesConstant" obj
241+
certSizeBytesPerNode <- parseFieldOrDefault @Config @"certSizeBytesPerNode" obj
280242
pure Config{..}
281243

282244
distributionToKVs :: KeyValue e kv => Distribution -> [kv]

0 commit comments

Comments
 (0)