Skip to content

Commit 1b26ad0

Browse files
authored
Merge pull request #6346 from IntersectMBO/jordan/remove-integration-monad
Remove Integration Monad
2 parents bcba24b + 8cf34ed commit 1b26ad0

File tree

32 files changed

+769
-284
lines changed

32 files changed

+769
-284
lines changed

cardano-node/src/Cardano/Node/Protocol/Byron.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import Cardano.Tracing.OrphanInstances.Shelley ()
3636
import Ouroboros.Consensus.Cardano
3737
import qualified Ouroboros.Consensus.Cardano as Consensus
3838

39+
import Control.Exception
3940
import qualified Data.ByteString.Lazy as LB
4041
import Data.Maybe (fromMaybe)
4142
import Data.Text (Text)
@@ -167,6 +168,9 @@ data ByronProtocolInstantiationError =
167168
| SigningKeyFilepathNotSpecified
168169
deriving Show
169170

171+
instance Exception ByronProtocolInstantiationError where
172+
displayException = docToString . prettyError
173+
170174
instance Error ByronProtocolInstantiationError where
171175
prettyError (CanonicalDecodeFailure fp failure) =
172176
"Canonical decode failure in " <> pshow fp

cardano-testnet/cardano-testnet.cabal

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ library
3838

3939
build-depends: aeson
4040
, aeson-pretty
41+
, annotated-exception
4142
, ansi-terminal
4243
, bytestring
4344
, cardano-api ^>= 10.19
@@ -88,6 +89,8 @@ library
8889
, process
8990
, resourcet
9091
, retry
92+
, rio
93+
, rio-orphans
9194
, safe-exceptions
9295
, scientific
9396
, stm
@@ -99,6 +102,7 @@ library
99102
, time
100103
, transformers
101104
, transformers-except
105+
, unliftio
102106
, yaml
103107

104108
hs-source-dirs: src
@@ -111,17 +115,20 @@ library
111115
Testnet.EpochStateProcessing
112116
Testnet.Filepath
113117
Testnet.Handlers
118+
Testnet.Orphans
114119
Testnet.Ping
115120
Testnet.Process.Cli.DRep
116121
Testnet.Process.Cli.Keys
117122
Testnet.Process.Cli.SPO
118123
Testnet.Process.Cli.Transaction
124+
Testnet.Process.RunIO
119125
Testnet.Process.Run
120126
Testnet.Property.Assert
121127
Testnet.Property.Run
122128
Testnet.Property.Util
123129
Testnet.Runtime
124130
Testnet.Start.Byron
131+
Testnet.Start.Cardano
125132
Testnet.Start.Types
126133
Testnet.SubmitApi
127134
Testnet.TestQueryCmds
@@ -130,7 +137,6 @@ library
130137
other-modules: Parsers.Cardano
131138
Parsers.Help
132139
Parsers.Version
133-
Testnet.Start.Cardano
134140
Testnet.TestEnumGenerator
135141
Paths_cardano_testnet
136142

@@ -265,6 +271,7 @@ test-suite cardano-testnet-test
265271
, monad-control
266272
, mtl
267273
, process
274+
, resourcet
268275
, regex-compat
269276
, rio
270277
, tasty ^>= 1.5

cardano-testnet/src/Parsers/Run.hs

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

65
module Parsers.Run
@@ -11,13 +10,14 @@ module Parsers.Run
1110
) where
1211

1312
import Cardano.CLI.Environment
13+
import Control.Monad
1414

1515
import Data.Default.Class (def)
1616
import Data.Foldable
1717
import Options.Applicative
1818
import qualified Options.Applicative as Opt
19-
20-
import Testnet.Property.Run
19+
import RIO (runRIO)
20+
import RIO.Orphans
2121
import Testnet.Start.Cardano
2222
import Testnet.Start.Types
2323

@@ -60,8 +60,8 @@ createEnvOptions CardanoTestnetCreateEnvOptions
6060
, createEnvGenesisOptions=genesisOptions
6161
, createEnvOutputDir=outputDir
6262
, createEnvCreateEnvOptions=ceOptions
63-
} =
64-
testnetRoutine (UserProvidedEnv outputDir) $ \conf -> do
63+
} = do
64+
conf <- mkConfigAbs outputDir
6565
createTestnetEnv
6666
testnetOptions genesisOptions ceOptions
6767
-- Do not add hashes to the main config file, so that genesis files
@@ -70,25 +70,25 @@ createEnvOptions CardanoTestnetCreateEnvOptions
7070

7171
runCardanoOptions :: CardanoTestnetCliOptions -> IO ()
7272
runCardanoOptions CardanoTestnetCliOptions
73-
{ cliTestnetOptions=testnetOptions@CardanoTestnetOptions{cardanoOutputDir}
73+
{ cliTestnetOptions=testnetOptions
7474
, cliGenesisOptions=genesisOptions
7575
, cliNodeEnvironment=env
76-
, cliUpdateTimestamps=updateTimestamps
77-
} =
76+
, cliUpdateTimestamps=updateTimestamps'
77+
} = do
7878
case env of
79-
NoUserProvidedEnv ->
79+
NoUserProvidedEnv -> do
8080
-- Create the sandbox, then run cardano-testnet.
8181
-- It is not necessary to honor `cliUpdateTimestamps` here, because
8282
-- the genesis files will be created with up-to-date stamps already.
83-
runTestnet cardanoOutputDir $ \conf -> do
84-
createTestnetEnv
85-
testnetOptions genesisOptions def
86-
conf
87-
cardanoTestnet testnetOptions conf
88-
UserProvidedEnv nodeEnvPath ->
83+
conf <- mkConfigAbs "testnet"
84+
runRIO () $ createTestnetEnv
85+
testnetOptions genesisOptions def
86+
conf
87+
withResourceMap (\rm -> void . runRIO rm $ cardanoTestnet testnetOptions conf)
88+
UserProvidedEnv nodeEnvPath -> do
8989
-- Run cardano-testnet in the sandbox provided by the user
9090
-- In that case, 'cardanoOutputDir' is not used
91-
runTestnet (UserProvidedEnv nodeEnvPath) $ \conf ->
92-
cardanoTestnet
91+
conf <- mkConfigAbs nodeEnvPath
92+
withResourceMap (\rm -> void . runRIO rm $ cardanoTestnet
9393
testnetOptions
94-
conf{updateTimestamps=updateTimestamps}
94+
conf{updateTimestamps=updateTimestamps'})

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

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -29,43 +29,44 @@ import qualified Cardano.Crypto.Hash.Blake2b as Crypto
2929
import qualified Cardano.Crypto.Hash.Class as Crypto
3030
import Cardano.Ledger.BaseTypes (unsafeNonZero)
3131
import Cardano.Ledger.Dijkstra.Genesis (DijkstraGenesis)
32+
import Cardano.Node.Protocol.Byron
3233

33-
import Control.Exception.Safe (MonadCatch)
34+
import Control.Exception
3435
import Control.Monad
3536
import Control.Monad.Extra
3637
import Data.Aeson
3738
import qualified Data.Aeson as Aeson
3839
import qualified Data.Aeson.Encode.Pretty as A
3940
import Data.Aeson.Key hiding (fromString)
4041
import Data.Aeson.KeyMap hiding (map)
41-
import Data.Bifunctor (first)
4242
import qualified Data.ByteString as BS
43+
import qualified Data.ByteString.Lazy as LBS
4344
import Data.Text (Text)
4445
import qualified Data.Text as Text
4546
import qualified Data.Time.Clock as DTC
4647
import Data.Word (Word64)
4748
import GHC.Stack (HasCallStack)
4849
import qualified GHC.Stack as GHC
4950
import qualified Network.HTTP.Simple as HTTP
51+
import RIO ( MonadThrow, throwM)
5052
import qualified System.Directory as System
5153
import System.FilePath.Posix (takeDirectory, (</>))
5254

55+
5356
import Testnet.Blockfrost (blockfrostToGenesis)
5457
import qualified Testnet.Defaults as Defaults
5558
import Testnet.Filepath
56-
import Testnet.Process.Run (execCli_)
59+
import Testnet.Process.RunIO (execCli_, liftIOAnnotated)
5760
import Testnet.Start.Types
5861

59-
import Hedgehog
60-
import qualified Hedgehog as H
6162
import qualified Hedgehog.Extras.Stock.OS as OS
6263
import qualified Hedgehog.Extras.Stock.Time as DTC
63-
import qualified Hedgehog.Extras.Test.Base as H
64-
import qualified Hedgehog.Extras.Test.File as H
6564

6665
-- | Returns JSON encoded hashes of the era, as well as the hard fork configuration toggle.
6766
createConfigJson :: ()
68-
=> (MonadTest m, MonadIO m, HasCallStack)
67+
=> HasCallStack
68+
=> MonadIO m
69+
=> MonadThrow m
6970
=> TmpAbsolutePath
7071
-> ShelleyBasedEra era -- ^ The era used for generating the hard fork configuration toggle
7172
-> m (KeyMap Aeson.Value)
@@ -85,7 +86,7 @@ createConfigJson (TmpAbsolutePath tempAbsPath) sbe = GHC.withFrozenCallStack $ d
8586
, Defaults.defaultYamlHardforkViaConfig sbe
8687
]
8788
where
88-
getHash :: (MonadTest m, MonadIO m) => CardanoEra a -> Text.Text -> m (KeyMap Value)
89+
getHash :: MonadIO m => CardanoEra a -> Text.Text -> m (KeyMap Value)
8990
getHash e = getShelleyGenesisHash (tempAbsPath </> Defaults.defaultGenesisFilepath e)
9091

9192
createConfigJsonNoHash :: ()
@@ -96,22 +97,25 @@ createConfigJsonNoHash = Defaults.defaultYamlHardforkViaConfig
9697
-- Generate hashes for genesis.json files
9798

9899
getByronGenesisHash
99-
:: (H.MonadTest m, MonadIO m)
100+
:: MonadIO m
101+
=> MonadThrow m
100102
=> FilePath
101103
-> m (KeyMap Aeson.Value)
102104
getByronGenesisHash path = do
103105
e <- runExceptT $ readGenesisData path
104-
(_, genesisHash) <- H.leftFail e
105-
let genesisHash' = unGenesisHash genesisHash
106-
pure . singleton "ByronGenesisHash" $ toJSON genesisHash'
106+
case e of
107+
Left err -> throwM $ GenesisReadError path err
108+
Right (_, genesisHash) -> do
109+
let genesisHash' = unGenesisHash genesisHash
110+
pure . singleton "ByronGenesisHash" $ toJSON genesisHash'
107111

108112
getShelleyGenesisHash
109-
:: (H.MonadTest m, MonadIO m)
113+
:: MonadIO m
110114
=> FilePath
111115
-> Text
112116
-> m (KeyMap Aeson.Value)
113117
getShelleyGenesisHash path key = do
114-
content <- H.evalIO $ BS.readFile path
118+
content <- liftIOAnnotated $ BS.readFile path
115119
let genesisHash = Crypto.hashWith id content :: Crypto.Hash Crypto.Blake2b_256 BS.ByteString
116120
pure . singleton (fromText key) $ toJSON genesisHash
117121

@@ -122,31 +126,34 @@ startTimeOffsetSeconds = if OS.isWin32 then 90 else 15
122126

123127
-- | A start time and 'ShelleyGenesis' value that are fit to pass to 'cardanoTestnet'
124128
getDefaultShelleyGenesis :: ()
125-
=> HasCallStack
126129
=> MonadIO m
127-
=> MonadTest m
128130
=> AnyShelleyBasedEra
129131
-> Word64 -- ^ The max supply
130132
-> GenesisOptions
131133
-> m ShelleyGenesis
132134
getDefaultShelleyGenesis asbe maxSupply opts = do
133-
currentTime <- H.noteShowIO DTC.getCurrentTime
134-
startTime <- H.noteShow $ DTC.addUTCTime startTimeOffsetSeconds currentTime
135+
currentTime <- liftIOAnnotated DTC.getCurrentTime
136+
let startTime = DTC.addUTCTime startTimeOffsetSeconds currentTime
135137
return $ Defaults.defaultShelleyGenesis asbe startTime maxSupply opts
136138

137139
-- | An 'AlonzoGenesis' value that is fit to pass to 'cardanoTestnet'
138140
getDefaultAlonzoGenesis :: ()
139141
=> HasCallStack
140-
=> MonadTest m
142+
=> MonadThrow m
141143
=> m AlonzoGenesis
142144
getDefaultAlonzoGenesis =
143-
H.evalEither $ first prettyError Defaults.defaultAlonzoGenesis
145+
case Defaults.defaultAlonzoGenesis of
146+
Right genesis -> return genesis
147+
Left err -> throwM err
148+
144149

145150
numSeededUTxOKeys :: Int
146151
numSeededUTxOKeys = 3
147152

148153
createSPOGenesisAndFiles
149-
:: (MonadTest m, MonadCatch m, MonadIO m, HasCallStack)
154+
:: MonadIO m
155+
=> HasCallStack
156+
=> MonadThrow m
150157
=> CardanoTestnetOptions -- ^ The options to use
151158
-> GenesisOptions
152159
-> TestnetOnChainParams
@@ -155,11 +162,14 @@ createSPOGenesisAndFiles
155162
createSPOGenesisAndFiles
156163
testnetOptions genesisOptions@GenesisOptions{genesisTestnetMagic}
157164
onChainParams
158-
(TmpAbsolutePath tempAbsPath) = GHC.withFrozenCallStack $ do
165+
(TmpAbsolutePath tempAbsPath) = do
159166
AnyShelleyBasedEra sbe <- pure cardanoNodeEra
160167

161-
let genesisShelleyDirAbs = takeDirectory inputGenesisShelleyFp
162-
genesisShelleyDir <- H.createDirectoryIfMissing genesisShelleyDirAbs
168+
169+
let genesisShelleyDir = takeDirectory inputGenesisShelleyFp
170+
171+
liftIOAnnotated $ System.createDirectoryIfMissing True genesisShelleyDir
172+
163173
let -- At least there should be a delegator per DRep
164174
-- otherwise some won't be representing anybody
165175
numStakeDelegators = max 3 (fromIntegral cardanoNumDReps) :: Int
@@ -176,23 +186,20 @@ createSPOGenesisAndFiles
176186
let conwayGenesis' = Defaults.defaultConwayGenesis
177187
dijkstraGenesis' = dijkstraGenesisDefaults
178188

179-
(shelleyGenesis, alonzoGenesis, conwayGenesis, dijkstraGenesis) <- resolveOnChainParams onChainParams
180-
(shelleyGenesis', alonzoGenesis', conwayGenesis', dijkstraGenesis')
189+
(shelleyGenesis, alonzoGenesis, conwayGenesis, dijkstraGenesis)
190+
<- resolveOnChainParams onChainParams
191+
(shelleyGenesis', alonzoGenesis', conwayGenesis', dijkstraGenesis')
181192

182193
-- Write Genesis files to disk, so they can be picked up by create-testnet-data
183-
H.lbsWriteFile inputGenesisAlonzoFp $ A.encodePretty alonzoGenesis
184-
H.lbsWriteFile inputGenesisConwayFp $ A.encodePretty conwayGenesis
185-
H.lbsWriteFile inputGenesisShelleyFp $ A.encodePretty shelleyGenesis
186-
H.lbsWriteFile inputGenesisDijkstraFp $ A.encodePretty dijkstraGenesis
187-
188-
H.note_ $ "Number of pools: " <> show nPoolNodes
189-
H.note_ $ "Number of stake delegators: " <> show numStakeDelegators
190-
H.note_ $ "Number of seeded UTxO keys: " <> show numSeededUTxOKeys
191-
194+
liftIOAnnotated $ do
195+
LBS.writeFile inputGenesisAlonzoFp $ A.encodePretty alonzoGenesis
196+
LBS.writeFile inputGenesisConwayFp $ A.encodePretty conwayGenesis
197+
LBS.writeFile inputGenesisShelleyFp $ A.encodePretty shelleyGenesis
198+
LBS.writeFile inputGenesisDijkstraFp $ A.encodePretty dijkstraGenesis
192199
let era = toCardanoEra sbe
193200

194-
currentTime <- H.noteShowIO DTC.getCurrentTime
195-
startTime <- H.noteShow $ DTC.addUTCTime startTimeOffsetSeconds currentTime
201+
currentTime <- liftIOAnnotated DTC.getCurrentTime
202+
let startTime = DTC.addUTCTime startTimeOffsetSeconds currentTime
196203

197204
execCli_ $
198205
[ eraToString sbe, "genesis", "create-testnet-data" ]
@@ -216,10 +223,7 @@ createSPOGenesisAndFiles
216223
[ inputGenesisShelleyFp, inputGenesisAlonzoFp, inputGenesisConwayFp
217224
, tempAbsPath </> "byron.genesis.spec.json" -- Created by create-testnet-data
218225
]
219-
(\fp -> liftIO $ whenM (System.doesFileExist fp) (System.removeFile fp))
220-
221-
files <- H.listDirectory tempAbsPath
222-
forM_ files H.note
226+
(\fp -> liftIOAnnotated $ whenM (System.doesFileExist fp) (System.removeFile fp))
223227

224228
return genesisShelleyDir
225229
where
@@ -235,23 +239,38 @@ createSPOGenesisAndFiles
235239
createTestnetDataFlag sbe =
236240
["--spec-" ++ eraToString sbe, genesisInputFilepath sbe]
237241

242+
243+
244+
data BlockfrostParamsError = BlockfrostParamsDecodeError FilePath String
245+
deriving Show
246+
247+
instance Exception BlockfrostParamsError where
248+
displayException (BlockfrostParamsDecodeError fp err) =
249+
"Failed to decode Blockfrost on-chain parameters from file "
250+
<> fp
251+
<> ": "
252+
<> err
253+
238254
-- | Resolves different kinds of user-provided on-chain parameters
239255
-- into a unified, consistent set of Genesis files
240256
resolveOnChainParams :: ()
241-
=> (MonadTest m, MonadIO m)
242257
=> HasCallStack
258+
=> MonadIO m
259+
=> MonadThrow m
243260
=> TestnetOnChainParams
244261
-> (ShelleyGenesis, AlonzoGenesis, ConwayGenesis, DijkstraGenesis)
245262
-> m (ShelleyGenesis, AlonzoGenesis, ConwayGenesis, DijkstraGenesis)
246263
resolveOnChainParams onChainParams geneses = case onChainParams of
247264

248-
DefaultParams -> pure geneses
265+
DefaultParams -> do
266+
pure geneses
249267

250268
OnChainParamsFile file -> do
251-
eParams <- H.readJsonFile file
252-
params <- H.leftFail eParams
253-
pure $ blockfrostToGenesis geneses params
269+
eParams <- eitherDecode <$> liftIOAnnotated (LBS.readFile file)
270+
case eParams of
271+
Right params -> pure $ blockfrostToGenesis geneses params
272+
Left err -> throwM $ BlockfrostParamsDecodeError file err
254273

255274
OnChainParamsMainnet -> do
256-
mainnetParams <- H.evalIO $ HTTP.getResponseBody <$> HTTP.httpJSON mainnetParamsRequest
275+
mainnetParams <- liftIOAnnotated $ HTTP.getResponseBody <$> HTTP.httpJSON mainnetParamsRequest
257276
pure $ blockfrostToGenesis geneses mainnetParams

0 commit comments

Comments
 (0)