Skip to content

Commit 48108cd

Browse files
committed
Refactoring: Split testnet creation/startup/runtime options and remove dummy flags and variables
1 parent 60af1c2 commit 48108cd

51 files changed

Lines changed: 509 additions & 479 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cardano-node-chairman/test/Spec/Chairman/Cardano.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ hprop_chairman :: H.Property
2020
hprop_chairman = integrationRetryWorkspace 2 "cardano-chairman" $ \tempAbsPath' -> H.runWithDefaultWatchdog_ $ do
2121
conf <- mkConf tempAbsPath'
2222

23-
let testnetOptions = def{ cardanoNodes = SpoNodeOptions [] :| [RelayNodeOptions [], RelayNodeOptions []] }
24-
allNodes <- testnetNodes <$> createAndRunTestnet testnetOptions def conf
23+
let creationOptions = def{ creationNodes = SpoNodeOptions [] :| [RelayNodeOptions [], RelayNodeOptions []] }
24+
allNodes <- testnetNodes <$> createAndRunTestnet creationOptions def conf
2525

2626
chairmanOver 120 50 conf allNodes
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
### Removed
3+
4+
- Removed `CardanoTestnetOptions` type and `CreateEnvOptions` wrapper (replaced by purpose-specific types).
5+
- Removed dead fields `cardanoNodeLoggingFormat` and `cardanoOutputDir`.
6+
7+
### Changed
8+
9+
- Split `CardanoTestnetOptions` into `TestnetCreationOptions` and `TestnetRuntimeOptions` so each function receives only the fields it uses.
10+
- `CardanoTestnetCliOptions` is now a sum type (`StartFromScratch | StartFromEnv`), making `--node-env` and `--num-pool-nodes` structurally mutually exclusive in the CLI parser.
11+
- Simplified `CardanoTestnetCreateEnvOptions` and `createTestnetEnv` signatures (fewer arguments, genesis options and on-chain params folded into `TestnetCreationOptions`).
12+
13+
### Added
14+
15+
- `readNodeOptionsFromEnv`: scans an existing testnet environment directory to classify nodes as SPO or relay.
16+

cardano-testnet/src/Cardano/Testnet.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ module Cardano.Testnet (
1010
retryOnAddressInUseError,
1111

1212
-- ** Testnet options
13-
CardanoTestnetOptions(..),
13+
TestnetCreationOptions(..),
14+
TestnetRuntimeOptions(..),
15+
TestnetEnvOptions(..),
1416
RpcSupport(..),
1517
NodeOption(..),
1618
cardanoDefaultTestnetNodeOptions,

cardano-testnet/src/Parsers/Cardano.hs

Lines changed: 69 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ module Parsers.Cardano
66
) where
77

88
import Cardano.Api (AnyShelleyBasedEra (..))
9-
import Cardano.Api.Pretty
109

1110
import Cardano.CLI.EraBased.Common.Option hiding (pNetworkId)
1211
import Cardano.Prelude (readMaybe)
@@ -26,68 +25,81 @@ import Options.Applicative.Types (readerAsk)
2625
import Testnet.Defaults (defaultEra)
2726
import Testnet.Start.Cardano
2827
import Testnet.Start.Types
29-
import Testnet.Types (readNodeLoggingFormat)
3028

3129
optsTestnet :: Parser CardanoTestnetCliOptions
32-
optsTestnet = CardanoTestnetCliOptions
33-
<$> pCardanoTestnetCliOptions
34-
<*> pGenesisOptions
35-
<*> pNodeEnvironment
36-
<*> pUpdateTimestamps
37-
<*> pOnChainParams
30+
optsTestnet = mkCliOptions <$> pModeOptions <*> pRuntimeOptions
31+
where
32+
pModeOptions =
33+
Left <$> pFromEnv
34+
<|> Right <$> ((,) <$> pCreationOptions <*> pScratchOutputDir)
35+
mkCliOptions (Left envOpts) rt = StartFromEnv (StartFromEnvOptions envOpts rt)
36+
mkCliOptions (Right (creation, outDir)) rt = StartFromScratch (StartFromScratchOptions creation outDir rt)
3837

3938
optsCreateTestnet :: Parser CardanoTestnetCreateEnvOptions
4039
optsCreateTestnet = CardanoTestnetCreateEnvOptions
41-
<$> pCardanoTestnetCliOptions
42-
<*> pGenesisOptions
40+
<$> pCreationOptions
4341
<*> pEnvOutputDir
44-
<*> pCreateEnvOptions
4542

46-
-- We can't fill in the optional Genesis files at parse time, because we want to be in a monad
47-
-- to properly parse JSON. We delegate this task to the caller.
48-
pCreateEnvOptions :: Parser CreateEnvOptions
49-
pCreateEnvOptions = CreateEnvOptions
50-
<$> pOnChainParams
43+
pFromEnv :: Parser TestnetEnvOptions
44+
pFromEnv = TestnetEnvOptions
45+
<$> OA.strOption
46+
( OA.long "node-env"
47+
<> OA.metavar "FILEPATH"
48+
<> OA.help "Path to the node's environment (which is generated otherwise). You can generate a default environment with the 'create-env' command, then modify it and pass it with this argument."
49+
)
50+
<*> pUpdateTimestamps
5151

52-
pCardanoTestnetCliOptions :: Parser CardanoTestnetOptions
53-
pCardanoTestnetCliOptions = CardanoTestnetOptions
52+
pCreationOptions :: Parser TestnetCreationOptions
53+
pCreationOptions = TestnetCreationOptions
5454
<$> pTestnetNodeOptions
5555
<*> pure (AnyShelleyBasedEra defaultEra)
5656
<*> pMaxLovelaceSupply
57-
<*> OA.option (OA.eitherReader readNodeLoggingFormat)
58-
( OA.long "node-logging-format"
59-
<> OA.help "Node logging format (json|text)"
60-
<> OA.metavar "LOGGING_FORMAT"
61-
<> OA.showDefaultWith prettyShow
62-
<> OA.value (cardanoNodeLoggingFormat def)
63-
)
64-
<*> OA.option OA.auto
65-
( OA.long "num-dreps"
66-
<> OA.help "Number of delegate representatives (DReps) to generate. Ignored if a node environment is passed."
67-
<> OA.metavar "NUMBER"
68-
<> OA.showDefault
69-
<> OA.value 3
70-
)
71-
<*> OA.switch
72-
( OA.long "enable-new-epoch-state-logging"
73-
<> OA.help "Enable new epoch state logging to logs/ledger-epoch-state.log"
74-
<> OA.showDefault
75-
)
76-
<*> (maybe NoUserProvidedEnv UserProvidedEnv <$> optional (OA.strOption
77-
( OA.long "output-dir"
78-
<> OA.help "Directory where to store files, sockets, and so on. It is created if it doesn't exist. If unset, a temporary directory is used."
79-
<> OA.metavar "DIRECTORY"
80-
)))
81-
<*> OA.flag RpcDisabled RpcEnabled
82-
( OA.long "enable-grpc"
83-
<> OA.help "[EXPERIMENTAL] Enable gRPC endpoint on all of testnet nodes. The listening socket file will be the same directory as node's N2C socket."
84-
<> OA.showDefault
85-
)
86-
<*> OA.flag UseKesKeyFile UseKesSocket
87-
( OA.long "use-kes-agent"
88-
<> OA.help "Get Praos block forging credentials from kes-agent via the default socket path"
89-
<> OA.showDefault
90-
)
57+
<*> pNumDReps
58+
<*> pGenesisOptions
59+
<*> pOnChainParams
60+
61+
pRuntimeOptions :: Parser TestnetRuntimeOptions
62+
pRuntimeOptions = TestnetRuntimeOptions
63+
<$> pEnableNewEpochStateLogging
64+
<*> pEnableRpc
65+
<*> pKesSource
66+
67+
pScratchOutputDir :: Parser (Maybe FilePath)
68+
pScratchOutputDir = optional $ OA.strOption
69+
( OA.long "output-dir"
70+
<> OA.help "Directory where to store files, sockets, and so on. It is created if it doesn't exist. If unset, a temporary directory is used."
71+
<> OA.metavar "DIRECTORY"
72+
)
73+
74+
pNumDReps :: Parser NumDReps
75+
pNumDReps = OA.option OA.auto
76+
( OA.long "num-dreps"
77+
<> OA.help "Number of delegate representatives (DReps) to generate."
78+
<> OA.metavar "NUMBER"
79+
<> OA.showDefault
80+
<> OA.value 3
81+
)
82+
83+
pEnableNewEpochStateLogging :: Parser Bool
84+
pEnableNewEpochStateLogging = OA.switch
85+
( OA.long "enable-new-epoch-state-logging"
86+
<> OA.help "Enable new epoch state logging to logs/ledger-epoch-state.log"
87+
<> OA.showDefault
88+
)
89+
90+
pEnableRpc :: Parser RpcSupport
91+
pEnableRpc = OA.flag RpcDisabled RpcEnabled
92+
( OA.long "enable-grpc"
93+
<> OA.help "[EXPERIMENTAL] Enable gRPC endpoint on all of testnet nodes. The listening socket file will be the same directory as node's N2C socket."
94+
<> OA.showDefault
95+
)
96+
97+
pKesSource :: Parser PraosCredentialsSource
98+
pKesSource = OA.flag UseKesKeyFile UseKesSocket
99+
( OA.long "use-kes-agent"
100+
<> OA.help "Get Praos block forging credentials from kes-agent via the default socket path"
101+
<> OA.showDefault
102+
)
91103

92104
pTestnetNodeOptions :: Parser (NonEmpty NodeOption)
93105
pTestnetNodeOptions =
@@ -108,14 +120,6 @@ pTestnetNodeOptions =
108120
Just n | n >= 1 -> pure n
109121
_ -> fail "Need at least one SPO node to produce blocks, but got none."
110122

111-
pNodeEnvironment :: Parser UserProvidedEnv
112-
pNodeEnvironment = fmap (maybe NoUserProvidedEnv UserProvidedEnv) <$>
113-
optional $ OA.strOption
114-
( OA.long "node-env"
115-
<> OA.metavar "FILEPATH"
116-
<> OA.help "Path to the node's environment (which is generated otherwise). You can generate a default environment with the 'create-env' command, then modify it and pass it with this argument."
117-
)
118-
119123
pOnChainParams :: Parser TestnetOnChainParams
120124
pOnChainParams = fmap (fromMaybe DefaultParams) <$> optional $
121125
pCustomParamsFile <|> pMainnetParams
@@ -161,23 +165,23 @@ pGenesisOptions =
161165
pEpochLength =
162166
OA.option OA.auto
163167
( OA.long "epoch-length"
164-
<> OA.help "Epoch length, in number of slots. Ignored if a node environment is passed."
168+
<> OA.help "Epoch length, in number of slots."
165169
<> OA.metavar "SLOTS"
166170
<> OA.showDefault
167171
<> OA.value (genesisEpochLength def)
168172
)
169173
pSlotLength =
170174
OA.option OA.auto
171175
( OA.long "slot-length"
172-
<> OA.help "Slot length. Ignored if a node environment is passed."
176+
<> OA.help "Slot length."
173177
<> OA.metavar "SECONDS"
174178
<> OA.showDefault
175179
<> OA.value (genesisSlotLength def)
176180
)
177181
pActiveSlotCoeffs =
178182
OA.option OA.auto
179183
( OA.long "active-slots-coeff"
180-
<> OA.help "Active slots coefficient. Ignored if a node environment is passed."
184+
<> OA.help "Active slots coefficient."
181185
<> OA.metavar "DOUBLE"
182186
<> OA.showDefault
183187
<> OA.value (genesisActiveSlotsCoeff def)
@@ -203,8 +207,8 @@ pMaxLovelaceSupply :: Parser Word64
203207
pMaxLovelaceSupply =
204208
OA.option OA.auto
205209
( OA.long "max-lovelace-supply"
206-
<> OA.help "Max lovelace supply that your testnet starts with. Ignored if a node environment is passed."
210+
<> OA.help "Max lovelace supply that your testnet starts with."
207211
<> OA.metavar "WORD64"
208212
<> OA.showDefault
209-
<> OA.value (cardanoMaxSupply def)
213+
<> OA.value (creationMaxSupply def)
210214
)

cardano-testnet/src/Parsers/Run.hs

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE GADTs #-}
22
{-# LANGUAGE LambdaCase #-}
3+
{-# LANGUAGE NamedFieldPuns #-}
34
{-# LANGUAGE NumericUnderscores #-}
45
{-# LANGUAGE OverloadedStrings #-}
56
{-# LANGUAGE ScopedTypeVariables #-}
@@ -13,9 +14,11 @@ module Parsers.Run
1314
import Cardano.CLI.Environment
1415

1516
import Control.Monad (void)
17+
import Data.Maybe (fromMaybe)
1618
import Options.Applicative
1719
import qualified Options.Applicative as Opt
1820

21+
import Testnet.Filepath (unTmpAbsPath)
1922
import Testnet.Start.Cardano
2023
import Testnet.Start.Types
2124

@@ -56,50 +59,41 @@ runTestnetCmd = \case
5659

5760
createEnvOptions :: CardanoTestnetCreateEnvOptions -> IO ()
5861
createEnvOptions CardanoTestnetCreateEnvOptions
59-
{ createEnvTestnetOptions=testnetOptions
60-
, createEnvGenesisOptions=genesisOptions
62+
{ createEnvCreationOptions=creationOptions
6163
, createEnvOutputDir=outputDir
62-
, createEnvCreateEnvOptions=ceOptions
6364
} = do
6465
conf <- mkConfigAbs outputDir
6566
void $ createTestnetEnv
66-
testnetOptions genesisOptions ceOptions
67+
creationOptions
6768
-- Do not add hashes to the main config file, so that genesis files
6869
-- can be modified without having to recompute hashes every time.
6970
conf{genesisHashesPolicy = WithoutHashes}
7071

7172
runCardanoOptions :: CardanoTestnetCliOptions -> IO ()
72-
runCardanoOptions CardanoTestnetCliOptions
73-
{ cliTestnetOptions=testnetOptions
74-
, cliGenesisOptions=genesisOptions
75-
, cliNodeEnvironment=env
76-
, cliUpdateTimestamps=updateTimestamps'
77-
, cliOnChainParams=onChainParams
78-
} = do
79-
case env of
80-
NoUserProvidedEnv -> do
81-
-- Create the sandbox, then run cardano-testnet.
82-
-- It is not necessary to honor `cliUpdateTimestamps` here, because
83-
-- the genesis files will be created with up-to-date stamps already.
84-
conf <- mkConfigAbs "testnet"
85-
runSimpleApp . runResourceT $ do
86-
logInfo $ "Creating environment: " <> display (tempAbsPath conf)
87-
createTestnetEnv testnetOptions genesisOptions (CreateEnvOptions onChainParams) conf
88-
logInfo $ "Starting testnet in environment: " <> display (tempAbsPath conf)
89-
void $ cardanoTestnet testnetOptions conf
90-
logInfo "Testnet started"
91-
waitForShutdown
92-
UserProvidedEnv nodeEnvPath -> do
93-
-- Run cardano-testnet in the sandbox provided by the user
94-
-- In that case, 'cardanoOutputDir' is not used
95-
conf <- mkConfigAbs nodeEnvPath
96-
runSimpleApp . runResourceT $ do
97-
logInfo $ "Starting testnet in environment: " <> display (tempAbsPath conf)
98-
void $ cardanoTestnet
99-
testnetOptions
100-
conf{updateTimestamps=updateTimestamps'}
101-
logInfo "Testnet started"
102-
waitForShutdown
73+
runCardanoOptions = \case
74+
StartFromScratch StartFromScratchOptions{scratchCreationOptions, scratchOutputDir, scratchRuntimeOptions} -> do
75+
-- Create the sandbox, then run cardano-testnet.
76+
-- It is not necessary to update timestamps here, because
77+
-- the genesis files will be created with up-to-date stamps already.
78+
let dirName = fromMaybe "testnet" scratchOutputDir
79+
conf <- mkConfigAbs dirName
80+
runSimpleApp . runResourceT $ do
81+
logInfo $ "Creating environment: " <> display (tempAbsPath conf)
82+
createTestnetEnv scratchCreationOptions conf
83+
logInfo $ "Starting testnet in environment: " <> display (tempAbsPath conf)
84+
void $ cardanoTestnet (creationNodes scratchCreationOptions) scratchRuntimeOptions conf
85+
logInfo "Testnet started"
86+
waitForShutdown
87+
StartFromEnv StartFromEnvOptions{fromEnvOptions, fromEnvRuntimeOptions} -> do
88+
-- Run cardano-testnet in the sandbox provided by the user
89+
conf <- mkConfigAbs (envPath fromEnvOptions)
90+
nodes <- readNodeOptionsFromEnv (unTmpAbsPath (tempAbsPath conf))
91+
runSimpleApp . runResourceT $ do
92+
logInfo $ "Starting testnet in environment: " <> display (tempAbsPath conf)
93+
void $ cardanoTestnet nodes fromEnvRuntimeOptions
94+
conf{updateTimestamps = envUpdateTimestamps fromEnvOptions}
95+
logInfo "Testnet started"
96+
waitForShutdown
10397
where
10498
waitForShutdown = do
10599
logInfo "Waiting for shutdown (Ctrl+C)"

cardano-testnet/src/Testnet/Components/Configuration.hs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,16 @@ createSPOGenesisAndFiles
153153
:: MonadIO m
154154
=> HasCallStack
155155
=> MonadThrow m
156-
=> CardanoTestnetOptions -- ^ The options to use
157-
-> GenesisOptions
158-
-> TestnetOnChainParams
156+
=> TestnetCreationOptions
159157
-> TmpAbsolutePath
160158
-> m FilePath -- ^ Shelley genesis directory
161159
createSPOGenesisAndFiles
162-
testnetOptions genesisOptions@GenesisOptions{genesisTestnetMagic}
163-
onChainParams
160+
creationOptions@TestnetCreationOptions
161+
{ creationGenesisOptions=genesisOptions@GenesisOptions{genesisTestnetMagic}
162+
, creationOnChainParams=onChainParams
163+
}
164164
(TmpAbsolutePath tempAbsPath) = do
165-
AnyShelleyBasedEra sbe <- pure cardanoNodeEra
165+
AnyShelleyBasedEra sbe <- pure creationEra
166166

167167

168168
let genesisShelleyDir = takeDirectory inputGenesisShelleyFp
@@ -171,9 +171,9 @@ createSPOGenesisAndFiles
171171

172172
let -- At least there should be a delegator per DRep
173173
-- otherwise some won't be representing anybody
174-
numStakeDelegators = max 3 (fromIntegral cardanoNumDReps) :: Int
174+
numStakeDelegators = max 3 (fromIntegral creationNumDReps) :: Int
175175

176-
shelleyGenesis'' <- getDefaultShelleyGenesis cardanoNodeEra cardanoMaxSupply genesisOptions
176+
shelleyGenesis'' <- getDefaultShelleyGenesis creationEra creationMaxSupply genesisOptions
177177
-- TODO: Remove this rewrite.
178178
-- 50 second epochs
179179
-- Epoch length should be "10 * k / f" where "k = securityParam, f = activeSlotsCoeff"
@@ -209,10 +209,10 @@ createSPOGenesisAndFiles
209209
++
210210
[ "--testnet-magic", show genesisTestnetMagic
211211
, "--pools", show nPoolNodes
212-
, "--total-supply", show cardanoMaxSupply -- Half of this will be delegated, see https://github.com/IntersectMBO/cardano-cli/pull/874
212+
, "--total-supply", show creationMaxSupply -- Half of this will be delegated, see https://github.com/IntersectMBO/cardano-cli/pull/874
213213
, "--stake-delegators", show numStakeDelegators
214214
, "--utxo-keys", show numSeededUTxOKeys]
215-
<> monoidForEraInEon @ConwayEraOnwards era (const ["--drep-keys", show cardanoNumDReps])
215+
<> monoidForEraInEon @ConwayEraOnwards era (const ["--drep-keys", show creationNumDReps])
216216
<> [ "--start-time", DTC.formatIso8601 startTime
217217
, "--out-dir", tempAbsPath
218218
]
@@ -230,8 +230,8 @@ createSPOGenesisAndFiles
230230
inputGenesisAlonzoFp = genesisInputFilepath AlonzoEra
231231
inputGenesisConwayFp = genesisInputFilepath ConwayEra
232232
inputGenesisDijkstraFp = genesisInputFilepath DijkstraEra
233-
nPoolNodes = cardanoNumPools testnetOptions
234-
CardanoTestnetOptions{cardanoNodeEra, cardanoMaxSupply, cardanoNumDReps} = testnetOptions
233+
nPoolNodes = creationNumPools creationOptions
234+
TestnetCreationOptions{creationEra, creationMaxSupply, creationNumDReps} = creationOptions
235235
genesisInputFilepath :: Pretty (eon era) => eon era -> FilePath
236236
genesisInputFilepath e = tempAbsPath </> ("genesis-input." <> eraToString e <> ".json")
237237
createTestnetDataFlag :: Pretty (eon era) => eon era -> [String]

0 commit comments

Comments
 (0)