Skip to content

Commit 5e226c8

Browse files
authored
Merge pull request #6239 from IntersectMBO/nbacquey/dump_config
Add an option to dump/load configuration sandbox in `cardano-testnet`
2 parents 781646a + ad83022 commit 5e226c8

48 files changed

Lines changed: 511 additions & 286 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
@@ -4,7 +4,7 @@
44

55
module Spec.Chairman.Cardano where
66

7-
import Cardano.Testnet (cardanoTestnetDefault, mkConf, testnetNodes)
7+
import Cardano.Testnet (createAndRunTestnet, mkConf, testnetNodes)
88

99
import Data.Default.Class
1010

@@ -19,6 +19,6 @@ hprop_chairman :: H.Property
1919
hprop_chairman = integrationRetryWorkspace 2 "cardano-chairman" $ \tempAbsPath' -> H.runWithDefaultWatchdog_ $ do
2020
conf <- mkConf tempAbsPath'
2121

22-
allNodes <- testnetNodes <$> cardanoTestnetDefault def def conf
22+
allNodes <- testnetNodes <$> createAndRunTestnet def def conf
2323

2424
chairmanOver 120 50 conf allNodes

cardano-testnet/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Next version
44

55
* [Fix discrepancy in security parameter between Byron and Shelley genesis files](https://github.com/IntersectMBO/cardano-node/pull/6188)
6+
* [Add an option to dump/load configuration sandbox](https://github.com/IntersectMBO/cardano-node/pull/6239)
67

78
## 10.0.0
89

cardano-testnet/cardano-testnet.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ test-suite cardano-testnet-test
202202
Cardano.Testnet.Test.Cli.StakeSnapshot
203203
Cardano.Testnet.Test.Cli.Transaction
204204
Cardano.Testnet.Test.Cli.Transaction.RegisterDeregisterStakeAddress
205+
Cardano.Testnet.Test.DumpConfig
205206
Cardano.Testnet.Test.FoldEpochState
206207
Cardano.Testnet.Test.Gov.CommitteeAddNew
207208
Cardano.Testnet.Test.Gov.DRepActivity
@@ -238,6 +239,7 @@ test-suite cardano-testnet-test
238239
, cardano-ledger-core
239240
, cardano-ledger-shelley
240241
, cardano-node
242+
, cardano-prelude
241243
, cardano-slotting
242244
, cardano-strict-containers ^>= 0.1
243245
, cardano-testnet

cardano-testnet/src/Cardano/Testnet.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ module Cardano.Testnet (
55

66
-- ** Start a testnet
77
cardanoTestnet,
8-
cardanoTestnetDefault,
8+
createAndRunTestnet,
9+
createTestnetEnv,
910
retryOnAddressInUseError,
1011

1112
-- ** Testnet options
1213
CardanoTestnetOptions(..),
13-
TestnetNodeOptions(..),
14-
AutomaticNodeOption(..),
14+
NodeOption(..),
1515
cardanoDefaultTestnetNodeOptions,
1616
getDefaultAlonzoGenesis,
1717
getDefaultShelleyGenesis,

cardano-testnet/src/Parsers/Cardano.hs

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
module Parsers.Cardano
44
( cmdCardano
5+
, cmdCreateEnv
56
) where
67

78
import Cardano.Api (AnyShelleyBasedEra (AnyShelleyBasedEra), EraInEon (..))
@@ -16,19 +17,24 @@ import Data.Default.Class
1617
import Data.Functor
1718
import qualified Data.List as L
1819
import Data.Word (Word64)
19-
import Options.Applicative
20+
import Options.Applicative (CommandFields, Mod, Parser)
2021
import qualified Options.Applicative as OA
2122

2223
import Testnet.Start.Cardano
2324
import Testnet.Start.Types
2425
import Testnet.Types (readNodeLoggingFormat)
2526

26-
{- HLINT ignore "Use asum" -}
27-
2827
optsTestnet :: EnvCli -> Parser CardanoTestnetCliOptions
2928
optsTestnet envCli = CardanoTestnetCliOptions
3029
<$> pCardanoTestnetCliOptions envCli
3130
<*> pGenesisOptions
31+
<*> pNodeEnvironment
32+
33+
optsCreateTestnet :: EnvCli -> Parser CardanoTestnetCreateEnvOptions
34+
optsCreateTestnet envCli = CardanoTestnetCreateEnvOptions
35+
<$> pCardanoTestnetCliOptions envCli
36+
<*> pGenesisOptions
37+
<*> pEnvOutputDir
3238

3339
pCardanoTestnetCliOptions :: EnvCli -> Parser CardanoTestnetOptions
3440
pCardanoTestnetCliOptions envCli = CardanoTestnetOptions
@@ -42,7 +48,7 @@ pCardanoTestnetCliOptions envCli = CardanoTestnetOptions
4248
<> OA.showDefault
4349
<> OA.value (cardanoNodeLoggingFormat def)
4450
)
45-
<*> OA.option auto
51+
<*> OA.option OA.auto
4652
( OA.long "num-dreps"
4753
<> OA.help "Number of delegate representatives (DReps) to generate. Ignored if a custom Conway genesis file is passed."
4854
<> OA.metavar "NUMBER"
@@ -54,37 +60,43 @@ pCardanoTestnetCliOptions envCli = CardanoTestnetOptions
5460
<> OA.help "Enable new epoch state logging to logs/ledger-epoch-state.log"
5561
<> OA.showDefault
5662
)
57-
<*> optional (OA.strOption
63+
<*> (maybe NoUserProvidedEnv UserProvidedEnv <$> optional (OA.strOption
5864
( OA.long "output-dir"
5965
<> 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."
6066
<> OA.metavar "DIRECTORY"
61-
))
67+
)))
6268
where
6369
pAnyShelleyBasedEra' :: Parser AnyShelleyBasedEra
6470
pAnyShelleyBasedEra' =
6571
pAnyShelleyBasedEra envCli <&> (\(EraInEon x) -> AnyShelleyBasedEra x)
6672

67-
pTestnetNodeOptions :: Parser TestnetNodeOptions
73+
pTestnetNodeOptions :: Parser [NodeOption]
6874
pTestnetNodeOptions =
69-
asum' [
70-
AutomaticNodeOptions . (`L.replicate` defaultSpoOptions) <$>
71-
OA.option auto
72-
( OA.long "num-pool-nodes"
73-
<> OA.help "Number of pool nodes. Note this uses a default node configuration for all nodes."
74-
<> OA.metavar "COUNT"
75-
<> OA.showDefault
76-
<> OA.value 1)
77-
, UserProvidedNodeOptions
78-
<$> strOption ( long "node-config"
79-
<> metavar "FILEPATH"
80-
<> help "Path to the node's configuration file (which is generated otherwise). If you use this option, you should also pass all the genesis files (files pointed to by the fields \"AlonzoGenesisFile\", \"ShelleyGenesisFile\", etc.).")
81-
]
75+
-- If `--num-pool-nodes N` is present, return N nodes with option `SpoNodeOptions []`.
76+
-- Otherwise, return `cardanoDefaultTestnetNodeOptions`
77+
fmap (maybe cardanoDefaultTestnetNodeOptions (`L.replicate` defaultSpoOptions)) <$>
78+
optional $ OA.option OA.auto
79+
( OA.long "num-pool-nodes"
80+
<> OA.help "Number of pool nodes. Note this uses a default node configuration for all nodes."
81+
<> OA.metavar "COUNT"
82+
)
8283
where
8384
defaultSpoOptions = SpoNodeOptions []
84-
-- \| Because asum is not available GHC 8.10.7's base (4.14.3.0). This can be removed
85-
-- when oldest version of GHC we use is >= 9.0 (base >= 4.15)
86-
asum' :: (Foldable t, Alternative f) => t (f a) -> f a
87-
asum' = foldr (<|>) empty
85+
86+
pNodeEnvironment :: Parser UserProvidedEnv
87+
pNodeEnvironment = fmap (maybe NoUserProvidedEnv UserProvidedEnv) <$>
88+
optional $ OA.strOption
89+
( OA.long "node-env"
90+
<> OA.metavar "FILEPATH"
91+
<> 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."
92+
)
93+
94+
pEnvOutputDir :: Parser FilePath
95+
pEnvOutputDir = OA.strOption
96+
( OA.long "output"
97+
<> OA.help "Directory where to create the sandbox environment."
98+
<> OA.metavar "DIRECTORY"
99+
)
88100

89101
pGenesisOptions :: Parser GenesisOptions
90102
pGenesisOptions =
@@ -95,7 +107,7 @@ pGenesisOptions =
95107
<*> pActiveSlotCoeffs
96108
where
97109
pEpochLength =
98-
OA.option auto
110+
OA.option OA.auto
99111
( OA.long "epoch-length"
100112
-- TODO Check that this flag is not used when a custom Shelley genesis file is passed
101113
<> OA.help "Epoch length, in number of slots. Ignored if a custom Shelley genesis file is passed."
@@ -104,7 +116,7 @@ pGenesisOptions =
104116
<> OA.value (genesisEpochLength def)
105117
)
106118
pSlotLength =
107-
OA.option auto
119+
OA.option OA.auto
108120
( OA.long "slot-length"
109121
-- TODO Check that this flag is not used when a custom Shelley genesis file is passed
110122
<> OA.help "Slot length. Ignored if a custom Shelley genesis file is passed."
@@ -113,7 +125,7 @@ pGenesisOptions =
113125
<> OA.value (genesisSlotLength def)
114126
)
115127
pActiveSlotCoeffs =
116-
OA.option auto
128+
OA.option OA.auto
117129
( OA.long "active-slots-coeff"
118130
-- TODO Check that this flag is not used when a custom Shelley genesis file is passed
119131
<> OA.help "Active slots coefficient. Ignored if a custom Shelley genesis file is passed."
@@ -125,6 +137,9 @@ pGenesisOptions =
125137
cmdCardano :: EnvCli -> Mod CommandFields CardanoTestnetCliOptions
126138
cmdCardano envCli = command' "cardano" "Start a testnet in any era" (optsTestnet envCli)
127139

140+
cmdCreateEnv :: EnvCli -> Mod CommandFields CardanoTestnetCreateEnvOptions
141+
cmdCreateEnv envCli = command' "create-env" "Create a sandbox for Cardano testnet" (optsCreateTestnet envCli)
142+
128143
pNetworkId :: Parser Int
129144
pNetworkId =
130145
OA.option (bounded "TESTNET_MAGIC") $ mconcat
@@ -135,11 +150,11 @@ pNetworkId =
135150

136151
pMaxLovelaceSupply :: Parser Word64
137152
pMaxLovelaceSupply =
138-
option auto
139-
( long "max-lovelace-supply"
153+
OA.option OA.auto
154+
( OA.long "max-lovelace-supply"
140155
-- TODO Check that this flag is not used when a custom Shelley genesis file is passed
141-
<> help "Max lovelace supply that your testnet starts with. Ignored if a custom Shelley genesis file is passed."
142-
<> metavar "WORD64"
143-
<> showDefault
144-
<> value (cardanoMaxSupply def)
156+
<> OA.help "Max lovelace supply that your testnet starts with. Ignored if a custom Shelley genesis file is passed."
157+
<> OA.metavar "WORD64"
158+
<> OA.showDefault
159+
<> OA.value (cardanoMaxSupply def)
145160
)

cardano-testnet/src/Parsers/Run.hs

Lines changed: 38 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE GADTs #-}
22
{-# LANGUAGE LambdaCase #-}
3+
{-# LANGUAGE NamedFieldPuns #-}
34
{-# LANGUAGE ScopedTypeVariables #-}
45

56
module Parsers.Run
@@ -9,22 +10,12 @@ module Parsers.Run
910
, opts
1011
) where
1112

12-
import Cardano.Api.Shelley (ShelleyGenesis)
1313

1414
import Cardano.CLI.Environment
15-
import Cardano.Ledger.Alonzo.Genesis (AlonzoGenesis)
16-
import Cardano.Ledger.Conway.Genesis (ConwayGenesis)
17-
import Cardano.Node.Configuration.POM
18-
import Cardano.Node.Types
1915

20-
import qualified Data.Aeson as Aeson
2116
import Data.Foldable
22-
import Data.Monoid
23-
import Data.Yaml (decodeFileThrow)
2417
import Options.Applicative
2518
import qualified Options.Applicative as Opt
26-
import qualified System.Directory as System
27-
import System.FilePath (takeDirectory, (</>))
2819

2920
import Testnet.Property.Run
3021
import Testnet.Start.Cardano
@@ -45,13 +36,15 @@ opts envCli = Opt.info (commands envCli <**> helper) idm
4536
-- via StartCardanoTestnet
4637
data CardanoTestnetCommands
4738
= StartCardanoTestnet CardanoTestnetCliOptions
39+
| CreateTestnetEnv CardanoTestnetCreateEnvOptions
4840
| GetVersion VersionOptions
4941
| Help ParserPrefs (ParserInfo CardanoTestnetCommands) HelpOptions
5042

5143
commands :: EnvCli -> Parser CardanoTestnetCommands
5244
commands envCli =
5345
asum
5446
[ fmap StartCardanoTestnet (subparser (cmdCardano envCli))
47+
, fmap CreateTestnetEnv (subparser (cmdCreateEnv envCli))
5548
, fmap GetVersion (subparser cmdVersion)
5649
, fmap (Help pref (opts envCli)) (subparser cmdHelp)
5750
]
@@ -60,54 +53,43 @@ commands envCli =
6053
runTestnetCmd :: CardanoTestnetCommands -> IO ()
6154
runTestnetCmd = \case
6255
StartCardanoTestnet cmdOpts -> runCardanoOptions cmdOpts
56+
CreateTestnetEnv cmdOpts -> createEnvOptions cmdOpts
6357
GetVersion cmdOpts -> runVersionOptions cmdOpts
6458
Help pPrefs pInfo cmdOpts -> runHelpOptions pPrefs pInfo cmdOpts
6559

60+
createEnvOptions :: CardanoTestnetCreateEnvOptions -> IO ()
61+
createEnvOptions CardanoTestnetCreateEnvOptions
62+
{ createEnvTestnetOptions=testnetOptions
63+
, createEnvGenesisOptions=genesisOptions
64+
, createEnvOutputDir=outputDir
65+
} =
66+
testnetRoutine (UserProvidedEnv outputDir) $ \conf ->
67+
createTestnetEnv
68+
testnetOptions genesisOptions
69+
-- The CLI does not provide a way to provide custom genesis data by design:
70+
-- If the user wants to have custom genesis data, they should manually
71+
-- modify the files created by this command before running the testnet.
72+
NoUserProvidedData NoUserProvidedData NoUserProvidedData
73+
-- Do not add hashes to the main config file, so that genesis files
74+
-- can be modified without having to recompute hashes every time.
75+
conf{genesisHashesPolicy = WithoutHashes}
6676

6777
runCardanoOptions :: CardanoTestnetCliOptions -> IO ()
68-
runCardanoOptions (CardanoTestnetCliOptions testnetOptions genesisOptions) =
69-
case cardanoNodes testnetOptions of
70-
AutomaticNodeOptions _ -> runTestnet testnetOptions $ cardanoTestnetDefault testnetOptions genesisOptions
71-
UserProvidedNodeOptions nodeInputConfigFile -> do
72-
nodeConfigFile <- readNodeConfigurationFile nodeInputConfigFile
73-
let protocolConfig :: NodeProtocolConfiguration =
74-
case getLast $ pncProtocolConfig nodeConfigFile of
75-
Nothing -> error $ "Genesis files not specified in node configuration file: " <> nodeInputConfigFile
76-
Just x -> x
77-
adjustedProtocolConfig =
78-
-- Make all the files be relative to the location of the config file.
79-
adjustFilePaths (takeDirectory nodeInputConfigFile </>) protocolConfig
80-
(shelley, alonzo, conway) = getShelleyGenesises adjustedProtocolConfig
81-
shelleyGenesis :: UserProvidedData ShelleyGenesis <-
82-
genesisFilepathToData $ npcShelleyGenesisFile shelley
83-
alonzoGenesis :: UserProvidedData AlonzoGenesis <-
84-
genesisFilepathToData $ npcAlonzoGenesisFile alonzo
85-
conwayGenesis :: UserProvidedData ConwayGenesis <-
86-
genesisFilepathToData $ npcConwayGenesisFile conway
87-
runTestnet testnetOptions $ cardanoTestnet
88-
testnetOptions
89-
genesisOptions
90-
shelleyGenesis
91-
alonzoGenesis
92-
conwayGenesis
93-
where
94-
getShelleyGenesises (NodeProtocolConfigurationCardano _byron shelley alonzo conway _hardForkConfig _checkPointConfig) =
95-
(shelley, alonzo, conway)
96-
readNodeConfigurationFile :: FilePath -> IO PartialNodeConfiguration
97-
readNodeConfigurationFile file = do
98-
errOrNodeConfig <- Aeson.eitherDecodeFileStrict' file
99-
case errOrNodeConfig of
100-
Left err -> error $ "Error reading node configuration file: " <> err
101-
Right nodeConfig -> pure nodeConfig
102-
genesisFilepathToData :: Aeson.FromJSON a => GenesisFile -> IO (UserProvidedData a)
103-
genesisFilepathToData (GenesisFile filepath) = do
104-
exists <- System.doesFileExist filepath
105-
if exists
106-
then UserProvidedData <$> decodeFileThrow filepath
107-
else
108-
-- Here we forbid mixing defaulting of genesis files with providing a user node configuration file:
109-
--
110-
-- Because of this check, either the user passes a node configuration file AND he passes all the genesis files.
111-
-- Or the user doesn't pass a node configuration file AND cardano-testnet generates all the genesis files.
112-
-- See the discussion here: https://github.com/IntersectMBO/cardano-node/pull/6103#discussion_r1995431437
113-
error $ "Genesis file specified in node configuration file does not exist: " <> filepath
78+
runCardanoOptions CardanoTestnetCliOptions
79+
{ cliTestnetOptions=testnetOptions@CardanoTestnetOptions{cardanoOutputDir}
80+
, cliGenesisOptions=genesisOptions
81+
, cliNodeEnvironment=env
82+
} =
83+
case env of
84+
NoUserProvidedEnv ->
85+
-- Create the sandbox, then run cardano-testnet
86+
runTestnet cardanoOutputDir $ \conf -> do
87+
createTestnetEnv
88+
testnetOptions genesisOptions
89+
NoUserProvidedData NoUserProvidedData NoUserProvidedData
90+
conf
91+
cardanoTestnet testnetOptions genesisOptions conf
92+
UserProvidedEnv nodeEnvPath ->
93+
-- Run cardano-testnet in the sandbox provided by the user
94+
-- In that case, 'cardanoOutputDir' is not used
95+
runTestnet (UserProvidedEnv nodeEnvPath) $ cardanoTestnet testnetOptions genesisOptions

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
module Testnet.Components.Configuration
1010
( createConfigJson
11+
, createConfigJsonNoHash
1112
, createSPOGenesisAndFiles
1213
, mkTopologyConfig
1314
, numSeededUTxOKeys
@@ -16,6 +17,7 @@ module Testnet.Components.Configuration
1617
, getShelleyGenesisHash
1718
, getDefaultAlonzoGenesis
1819
, getDefaultShelleyGenesis
20+
, startTimeOffsetSeconds
1921

2022
, anyEraToString
2123
, eraToString
@@ -94,6 +96,11 @@ createConfigJson (TmpAbsolutePath tempAbsPath) sbe = GHC.withFrozenCallStack $ d
9496
getHash :: (MonadTest m, MonadIO m) => CardanoEra a -> Text.Text -> m (KeyMap Value)
9597
getHash e = getShelleyGenesisHash (tempAbsPath </> defaultGenesisFilepath e)
9698

99+
createConfigJsonNoHash :: ()
100+
=> ShelleyBasedEra era -- ^ The era used for generating the hard fork configuration toggle
101+
-> LBS.ByteString
102+
createConfigJsonNoHash sbe =
103+
A.encodePretty . Object $ defaultYamlHardforkViaConfig sbe
97104

98105
-- Generate hashes for genesis.json files
99106

0 commit comments

Comments
 (0)