Skip to content

Commit b9e77bc

Browse files
EnableP2P and Improve message when running the cluster
Enhances the `cardano-testnet` user experience by refining startup output. Improve messages when running the testnet The HTTP call fetching mainnet protocol parameters from https://github.com/input-output-hk/cardano-parameters/blob/main/mainnet/parameters.json had no retry logic, so a transient network or TLS error would kill the testnet setup with an opaque exception. Retry up to 3 times with a 2-second delay, logging each attempt to stderr, and wrap the final failure in a descriptive error. Update node_default_config.json
1 parent 0d697f1 commit b9e77bc

5 files changed

Lines changed: 82 additions & 26 deletions

File tree

cardano-testnet/src/Parsers/Run.hs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ module Parsers.Run
1212
) where
1313
import Cardano.CLI.Environment
1414

15-
import Control.Monad (void)
15+
import Control.Monad (forM_)
1616
import Data.Default.Class (def)
17+
import qualified Data.Text as Text
1718
import Options.Applicative
1819
import qualified Options.Applicative as Opt
1920

2021
import Testnet.Start.Cardano
2122
import Testnet.Start.Types
23+
import Testnet.Types (TestnetNode (..))
24+
25+
import qualified Hedgehog.Extras.Stock.IO.Network.Sprocket as H
2226

2327
import Parsers.Cardano
2428
import Parsers.Help
@@ -85,22 +89,38 @@ runCardanoOptions CardanoTestnetCliOptions
8589
runSimpleApp . runResourceT $ do
8690
logInfo $ "Creating environment: " <> display (tempAbsPath conf)
8791
createTestnetEnv testnetOptions genesisOptions def conf
88-
logInfo $ "Starting testnet in environment: " <> display (tempAbsPath conf)
89-
void $ cardanoTestnet testnetOptions conf
90-
logInfo "Testnet started"
92+
testnetRuntime <- cardanoTestnet testnetOptions conf
93+
logTestnetInfo conf testnetRuntime
9194
waitForShutdown
9295
UserProvidedEnv nodeEnvPath -> do
9396
-- Run cardano-testnet in the sandbox provided by the user
9497
-- In that case, 'cardanoOutputDir' is not used
9598
conf <- mkConfigAbs nodeEnvPath
9699
runSimpleApp . runResourceT $ do
97-
logInfo $ "Starting testnet in environment: " <> display (tempAbsPath conf)
98-
void $ cardanoTestnet
100+
testnetRuntime <- cardanoTestnet
99101
testnetOptions
100102
conf{updateTimestamps=updateTimestamps'}
101-
logInfo "Testnet started"
103+
logTestnetInfo conf testnetRuntime
102104
waitForShutdown
103105
where
106+
logTestnetInfo conf testnetRuntime = do
107+
let nodes = testnetNodes testnetRuntime
108+
logInfo ""
109+
logInfo $ "Testnet started in " <> display (tempAbsPath conf)
110+
<> " with " <> display (length nodes) <> " nodes."
111+
logInfo ""
112+
case nodes of
113+
(firstNode:_) -> do
114+
logInfo "To interact with the testnet using cardano-cli:"
115+
logInfo $ " export CARDANO_NODE_NETWORK_ID=" <> display (testnetMagic testnetRuntime)
116+
logInfo $ " export CARDANO_NODE_SOCKET_PATH=" <> display (Text.pack (H.sprocketSystemName (nodeSprocket firstNode)))
117+
logInfo ""
118+
[] -> pure ()
119+
logInfo "Node sockets:"
120+
forM_ nodes $ \node ->
121+
logInfo $ " " <> display (Text.pack (nodeName node)) <> " " <> display (Text.pack (H.sprocketSystemName (nodeSprocket node)))
122+
logInfo ""
123+
104124
waitForShutdown = do
105-
logInfo "Waiting for shutdown (Ctrl+C)"
106-
forever (threadDelay 100_000)
125+
logInfo "Press Ctrl+C to stop all nodes."
126+
forever (threadDelay 100_000)

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

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE DerivingVia #-}
22
{-# LANGUAGE GADTs #-}
33
{-# LANGUAGE NamedFieldPuns #-}
4+
{-# LANGUAGE NumericUnderscores #-}
45
{-# LANGUAGE OverloadedStrings #-}
56
{-# LANGUAGE ScopedTypeVariables #-}
67
{-# LANGUAGE TypeApplications #-}
@@ -31,6 +32,7 @@ import Cardano.Ledger.BaseTypes (unsafeNonZero)
3132
import Cardano.Ledger.Dijkstra.Genesis (DijkstraGenesis)
3233
import Cardano.Node.Protocol.Byron
3334

35+
import Control.Concurrent (threadDelay)
3436
import Control.Exception
3537
import Control.Monad
3638
import Control.Monad.Extra
@@ -48,11 +50,12 @@ import GHC.Stack (HasCallStack)
4850
import qualified GHC.Stack as GHC
4951
import qualified Network.HTTP.Simple as HTTP
5052
import RIO ( MonadThrow, throwM)
53+
import System.IO (hPutStrLn, stderr)
5154
import qualified System.Directory as System
5255
import System.FilePath.Posix (takeDirectory, (</>))
5356

5457

55-
import Testnet.Blockfrost (blockfrostToGenesis)
58+
import Testnet.Blockfrost (BlockfrostParams, blockfrostToGenesis)
5659
import qualified Testnet.Defaults as Defaults
5760
import Testnet.Filepath
5861
import Testnet.Process.RunIO (execCli_, liftIOAnnotated)
@@ -102,7 +105,7 @@ getByronGenesisHash
102105
-> m (KeyMap Aeson.Value)
103106
getByronGenesisHash path = do
104107
e <- runExceptT $ readGenesisData path
105-
case e of
108+
case e of
106109
Left err -> throwM $ GenesisReadError path err
107110
Right (_, genesisHash) -> do
108111
let genesisHash' = unGenesisHash genesisHash
@@ -141,7 +144,7 @@ getDefaultAlonzoGenesis :: ()
141144
=> MonadThrow m
142145
=> m AlonzoGenesis
143146
getDefaultAlonzoGenesis =
144-
case Defaults.defaultAlonzoGenesis of
147+
case Defaults.defaultAlonzoGenesis of
145148
Right genesis -> return genesis
146149
Left err -> throwM err
147150

@@ -164,9 +167,9 @@ createSPOGenesisAndFiles
164167
(TmpAbsolutePath tempAbsPath) = do
165168
AnyShelleyBasedEra sbe <- pure cardanoNodeEra
166169

167-
170+
168171
let genesisShelleyDir = takeDirectory inputGenesisShelleyFp
169-
172+
170173
liftIOAnnotated $ System.createDirectoryIfMissing True genesisShelleyDir
171174

172175
let -- At least there should be a delegator per DRep
@@ -185,7 +188,7 @@ createSPOGenesisAndFiles
185188
let conwayGenesis' = Defaults.defaultConwayGenesis
186189
dijkstraGenesis' = dijkstraGenesisDefaults
187190

188-
(shelleyGenesis, alonzoGenesis, conwayGenesis, dijkstraGenesis)
191+
(shelleyGenesis, alonzoGenesis, conwayGenesis, dijkstraGenesis)
189192
<- resolveOnChainParams onChainParams
190193
(shelleyGenesis', alonzoGenesis', conwayGenesis', dijkstraGenesis')
191194

@@ -243,13 +246,21 @@ createSPOGenesisAndFiles
243246
data BlockfrostParamsError = BlockfrostParamsDecodeError FilePath String
244247
deriving Show
245248

246-
instance Exception BlockfrostParamsError where
249+
instance Exception BlockfrostParamsError where
247250
displayException (BlockfrostParamsDecodeError fp err) =
248251
"Failed to decode Blockfrost on-chain parameters from file "
249252
<> fp
250253
<> ": "
251254
<> err
252255

256+
newtype MainnetParamsFetchError = MainnetParamsFetchError SomeException
257+
deriving Show
258+
259+
instance Exception MainnetParamsFetchError where
260+
displayException (MainnetParamsFetchError exc) =
261+
"Failed to fetch mainnet on-chain parameters from GitHub after retries: "
262+
<> displayException exc
263+
253264
-- | Resolves different kinds of user-provided on-chain parameters
254265
-- into a unified, consistent set of Genesis files
255266
resolveOnChainParams :: ()
@@ -261,15 +272,35 @@ resolveOnChainParams :: ()
261272
-> m (ShelleyGenesis, AlonzoGenesis, ConwayGenesis, DijkstraGenesis)
262273
resolveOnChainParams onChainParams geneses = case onChainParams of
263274

264-
DefaultParams -> do
275+
DefaultParams -> do
265276
pure geneses
266277

267278
OnChainParamsFile file -> do
268279
eParams <- eitherDecode <$> liftIOAnnotated (LBS.readFile file)
269-
case eParams of
280+
case eParams of
270281
Right params -> pure $ blockfrostToGenesis geneses params
271282
Left err -> throwM $ BlockfrostParamsDecodeError file err
272283

273284
OnChainParamsMainnet -> do
274-
mainnetParams <- liftIOAnnotated $ HTTP.getResponseBody <$> HTTP.httpJSON mainnetParamsRequest
275-
pure $ blockfrostToGenesis geneses mainnetParams
285+
blockfrostToGenesis geneses <$> fetchMainnetParams
286+
where
287+
maxRetries = 3 :: Int
288+
retryDelaySec = 2_000_000 -- 2 seconds in microseconds
289+
fetchMainnetParams :: (MonadIO m, MonadThrow m) => m BlockfrostParams
290+
fetchMainnetParams = go maxRetries
291+
where
292+
go n = do
293+
result <- liftIO $ try @HTTP.HttpException $
294+
HTTP.getResponseBody <$> HTTP.httpJSON mainnetParamsRequest
295+
case result of
296+
Right params -> pure params
297+
Left exc
298+
| n > 0 -> do
299+
liftIO $ hPutStrLn stderr $
300+
displayException exc
301+
<> "\n\nFailed to fetch mainnet parameters (retrying, "
302+
<> show n <> " attempts left)"
303+
liftIO $ threadDelay retryDelaySec
304+
go (n - 1)
305+
| otherwise ->
306+
throwM $ MainnetParamsFetchError (toException exc)

cardano-testnet/src/Testnet/Defaults.hs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ newtype AlonzoGenesisError
108108
= AlonzoGenErrTooMuchPrecision Rational
109109
deriving Show
110110

111-
instance Exception AlonzoGenesisError where
111+
instance Exception AlonzoGenesisError where
112112
displayException = Api.docToString . Api.prettyError
113113

114114

115115
defaultAlonzoGenesis :: Either AlonzoGenesisError AlonzoGenesis
116116
defaultAlonzoGenesis = do
117-
let genesis = Api.alonzoGenesisDefaults
117+
let genesis = Api.alonzoGenesisDefaults
118118
prices = Ledger.agPrices genesis
119119

120120
-- double check that prices have correct values - they're set using unsafeBoundedRational in cardano-api
@@ -326,6 +326,9 @@ defaultYamlConfig =
326326
-- Turn logging on or off
327327
, ("EnableLogging", Aeson.Bool True)
328328

329+
-- Configuration for the node's P2P network topology
330+
, ("EnableP2P", Aeson.Bool True)
331+
329332
-- Genesis filepaths
330333
, ("ByronGenesisFile", genesisPath ByronEra)
331334
, ("ShelleyGenesisFile", genesisPath ShelleyEra)

cardano-testnet/src/Testnet/Filepath.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ newtype TmpAbsolutePath = TmpAbsolutePath
3838
instance Display TmpAbsolutePath where
3939
textDisplay = fromString . unTmpAbsPath
4040

41-
makeTmpRelPath :: TmpAbsolutePath -> FilePath
42-
makeTmpRelPath (TmpAbsolutePath fp) = makeRelative (makeTmpBaseAbsPath (TmpAbsolutePath fp)) fp
43-
4441
makeSocketDir :: TmpAbsolutePath -> FilePath
45-
makeSocketDir fp = makeTmpRelPath fp </> "socket"
42+
makeSocketDir (TmpAbsolutePath fp) =
43+
let relPath = makeRelative (takeDirectory fp) fp
44+
in if relPath == "."
45+
then "socket"
46+
else relPath </> "socket"
4647

4748
makeTmpBaseAbsPath :: TmpAbsolutePath -> FilePath
4849
makeTmpBaseAbsPath (TmpAbsolutePath fp) = addTrailingPathSeparator $ takeDirectory fp

cardano-testnet/test/cardano-testnet-golden/files/golden/node_default_config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"DijkstraGenesisFile": "dijkstra-genesis.json",
66
"EnableLogMetrics": false,
77
"EnableLogging": true,
8+
"EnableP2P": true,
89
"ExperimentalHardForksEnabled": true,
910
"ExperimentalProtocolsEnabled": true,
1011
"LastKnownBlockVersion-Alt": 0,

0 commit comments

Comments
 (0)