Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cardano-testnet: allow to pass genesis files #5645

Merged
merged 1 commit into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cardano-node-chairman/test/Spec/Chairman/Cardano.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ hprop_chairman :: H.Property
hprop_chairman = H.integrationRetryWorkspace 2 "cardano-chairman" $ \tempAbsPath' -> do
conf <- H.mkConf tempAbsPath'

allNodes <- fmap H.nodeName . H.allNodes <$> H.cardanoTestnet H.cardanoDefaultTestnetOptions conf
allNodes <- fmap H.nodeName . H.allNodes <$> H.cardanoTestnetDefault H.cardanoDefaultTestnetOptions conf

chairmanOver 120 50 conf allNodes
1 change: 1 addition & 0 deletions cardano-testnet/src/Cardano/Testnet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Cardano.Testnet (

-- ** Start a testnet
cardanoTestnet,
cardanoTestnetDefault,

-- ** Testnet options
CardanoTestnetOptions(..),
Expand Down
2 changes: 1 addition & 1 deletion cardano-testnet/src/Parsers/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ runTestnetCmd = \case

runCardanoOptions :: CardanoTestnetOptions -> IO ()
runCardanoOptions options =
runTestnet $ cardanoTestnet options
runTestnet $ cardanoTestnetDefault options
38 changes: 21 additions & 17 deletions cardano-testnet/src/Testnet/Components/Configuration.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

Expand All @@ -8,6 +9,8 @@ module Testnet.Components.Configuration
, createSPOGenesisAndFiles
, mkTopologyConfig
, numSeededUTxOKeys
, NumPools
, numPools
) where

import Cardano.Api.Pretty
Expand All @@ -30,7 +33,6 @@ import qualified Data.ByteString.Lazy as LBS
import Data.Char (toLower)
import qualified Data.List as List
import Data.String
import Data.Time
import GHC.Stack (HasCallStack)
import qualified GHC.Stack as GHC
import Lens.Micro
Expand All @@ -42,11 +44,13 @@ import qualified Hedgehog.Extras.Stock.Time as DTC
import qualified Hedgehog.Extras.Test.Base as H
import qualified Hedgehog.Extras.Test.File as H

import Cardano.Api.Ledger (StandardCrypto)
import Data.Word (Word32)
import Testnet.Defaults
import Testnet.Filepath
import Testnet.Process.Run (execCli_)
import Testnet.Property.Utils
import Testnet.Start.Types
import Testnet.Start.Types (CardanoTestnetOptions (..))


createConfigYaml
Expand Down Expand Up @@ -78,35 +82,35 @@ createConfigYaml (TmpAbsolutePath tempAbsPath') anyCardanoEra' = GHC.withFrozenC
numSeededUTxOKeys :: Int
numSeededUTxOKeys = 3

newtype NumPools = NumPools Int

numPools :: CardanoTestnetOptions -> NumPools
numPools CardanoTestnetOptions { cardanoNodes } = NumPools $ length cardanoNodes

createSPOGenesisAndFiles
:: (MonadTest m, MonadCatch m, MonadIO m, HasCallStack)
=> CardanoTestnetOptions
-> UTCTime -- ^ Start time
=> NumPools -- ^ The number of pools to make
-> AnyCardanoEra -- ^ The era to use
-> ShelleyGenesis StandardCrypto -- ^ The shelley genesis to use.
-> TmpAbsolutePath
-> m FilePath -- ^ Shelley genesis directory
createSPOGenesisAndFiles testnetOptions startTime (TmpAbsolutePath tempAbsPath') = do
createSPOGenesisAndFiles (NumPools numPoolNodes) era shelleyGenesis (TmpAbsolutePath tempAbsPath') = do
let genesisShelleyFpAbs = tempAbsPath' </> defaultShelleyGenesisFp
genesisShelleyDirAbs = takeDirectory genesisShelleyFpAbs
genesisShelleyDir <- H.createDirectoryIfMissing genesisShelleyDirAbs
let testnetMagic = cardanoTestnetMagic testnetOptions
numPoolNodes = length $ cardanoNodes testnetOptions
let testnetMagic = sgNetworkMagic shelleyGenesis
numStakeDelegators = 3
era = cardanoNodeEra testnetOptions
-- TODO: Even this is cumbersome. You need to know where to put the initial
-- shelley genesis for create-testnet-data to use.
startTime = sgSystemStart shelleyGenesis

-- TODO: We need to read the genesis files into Haskell and modify them
-- based on cardano-testnet's cli parameters

-- We create the initial genesis file to avoid having to re-write the genesis file later
-- with the parameters we want. The user must provide genesis files or we will use a default.
-- We should *never* be modifying the genesis file after cardano-testnet is run because this
-- We should *never* be modifying the genesis file after @cardanoTestnet@ is run because this
-- is sure to be a source of confusion if users provide genesis files and we are mutating them
-- without their knowledge.
let shelleyGenesis :: LBS.ByteString
shelleyGenesis = encode $ defaultShelleyGenesis startTime testnetOptions

H.evalIO $ LBS.writeFile genesisShelleyFpAbs shelleyGenesis
H.evalIO $ LBS.writeFile genesisShelleyFpAbs $ encode shelleyGenesis

-- TODO: Remove this rewrite.
-- 50 second epochs
Expand All @@ -128,7 +132,7 @@ createSPOGenesisAndFiles testnetOptions startTime (TmpAbsolutePath tempAbsPath')
execCli_
[ convertToEraString era, "genesis", "create-testnet-data"
, "--spec-shelley", genesisShelleyFpAbs
, "--testnet-magic", show @Int testnetMagic
, "--testnet-magic", show @Word32 testnetMagic
, "--pools", show @Int numPoolNodes
, "--supply", "1000000000000"
, "--supply-delegated", "1000000000000"
Expand All @@ -138,7 +142,7 @@ createSPOGenesisAndFiles testnetOptions startTime (TmpAbsolutePath tempAbsPath')
, "--out-dir", tempAbsPath'
]

-- Here we move all of the keys etc generated by create-staked
-- Here we move all of the keys etc generated by create-testnet-data
-- for the nodes to use

-- Move all genesis related files
Expand Down
93 changes: 73 additions & 20 deletions cardano-testnet/src/Testnet/Start/Cardano.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ module Testnet.Start.Cardano
, PaymentKeyPair(..)

, cardanoTestnet

, cardanoTestnetDefault
) where


import Control.Monad
import qualified Control.Monad.Class.MonadTimer.SI as MT
import Control.Monad.IO.Class
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Except (runExceptT)
import Data.Aeson
Expand All @@ -43,14 +41,25 @@ import qualified Hedgehog.Extras.Stock.OS as OS
import qualified Hedgehog.Extras.Test.Base as H
import qualified Hedgehog.Extras.Test.File as H

import qualified Testnet.Defaults as Defaults

import Cardano.Api
import Cardano.Api.Ledger (StandardCrypto)
import Cardano.Ledger.Alonzo.Genesis (AlonzoGenesis)
import Cardano.Ledger.Conway.Genesis (ConwayGenesis)
import qualified Control.Monad.Class.MonadTimer.SI as MT
import Control.Monad.IO.Class
import qualified Data.Aeson as Aeson
import Data.Bifunctor (first)
import Data.Time (UTCTime)
import Data.Word (Word32)
import Testnet.Components.Configuration
import Testnet.Defaults
import Testnet.Filepath
import qualified Testnet.Process.Run as H
import Testnet.Process.Run
import qualified Testnet.Property.Assert as H
import Testnet.Property.Checks
import Testnet.Runtime
import Testnet.Runtime as TR hiding (shelleyGenesis)
import qualified Testnet.Start.Byron as Byron
import Testnet.Start.Types

Expand All @@ -63,10 +72,11 @@ import Testnet.Start.Types
-- a valid node cluster.
testnetMinimumConfigurationRequirements :: CardanoTestnetOptions -> H.Integration ()
testnetMinimumConfigurationRequirements cTestnetOpts = do
when (length (cardanoNodes cTestnetOpts) < 2) $ do
H.noteShow_ ("Need at least two nodes to run a cluster" :: String)
let actualLength = length (cardanoNodes cTestnetOpts)
when (actualLength < 2) $ do
H.noteShow_ ("Need at least two nodes to run a cluster, but got: " <> show actualLength)
H.noteShow_ cTestnetOpts
H.assert False
H.failure

data ForkPoint
= AtVersion Int
Expand All @@ -79,6 +89,19 @@ data ForkPoint
startTimeOffsetSeconds :: DTC.NominalDiffTime
startTimeOffsetSeconds = if OS.isWin32 then 90 else 15

-- | Like 'cardanoTestnet', but using defaults for all configuration files.
-- See 'cardanoTestnet' for additional documentation.
cardanoTestnetDefault :: ()
=> CardanoTestnetOptions
-> Conf
-> H.Integration TestnetRuntime
Comment on lines +94 to +97
Copy link
Contributor Author

@smelc smelc Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, we're not mixing CardanoTestnetOptions and genesis files, which is good (as I explained elsewhere). This is nice for the vast majority of callers, that don't need to be exposed to a complex API.

cardanoTestnetDefault opts conf = do
alonzoGenesis <- H.evalEither $ first prettyError Defaults.defaultAlonzoGenesis
currentTime <- H.noteShowIO DTC.getCurrentTime
startTime <- H.noteShow $ DTC.addUTCTime startTimeOffsetSeconds currentTime
cardanoTestnet
opts conf startTime
(Defaults.defaultShelleyGenesis startTime opts) alonzoGenesis Defaults.defaultConwayGenesis

-- | Setup a number of credentials and pools, like this:
--
Expand Down Expand Up @@ -122,13 +145,37 @@ startTimeOffsetSeconds = if OS.isWin32 then 90 else 15
-- > │   └── node-spo{1,2,3}
-- > └── utxo-keys
-- >    └── utxo{1,2,3}.{addr,skey,vkey}
cardanoTestnet :: CardanoTestnetOptions -> Conf -> H.Integration TestnetRuntime
cardanoTestnet testnetOptions Conf {tempAbsPath=TmpAbsolutePath tmpAbsPath} = do
testnetMinimumConfigurationRequirements testnetOptions
void $ H.note OS.os
currentTime <- H.noteShowIO DTC.getCurrentTime
let testnetMagic = cardanoTestnetMagic testnetOptions
cardanoTestnet :: ()
=> CardanoTestnetOptions -- ^ The options to use. Must be consistent with the genesis files.
-> Conf
-> UTCTime -- ^ The starting time. Must be the same as the one in the shelley genesis.
-> ShelleyGenesis StandardCrypto -- ^ The shelley genesis to use, for example 'Defaults.defaultShelleyGenesis'.
-- Some fields are overridden by the accompanying 'CardanoTestnetOptions'.
-> AlonzoGenesis -- ^ The alonzo genesis to use, for example 'Defaults.defaultAlonzoGenesis'.
-> ConwayGenesis StandardCrypto -- ^ The conway genesis to use, for example 'Defaults.defaultConwayGenesis'.
Comment on lines +149 to +155
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we mix CardanoTestnetOptions and genesis files. So callers must be careful to provide coherent data. If they don't, they will get caught by the new checks below. Ideally, in the future, we'll want to remove fields from CardanoTestnetOptions, so that the intersection with the data of genesis files becomes empty. This a bigger change though, so I chose this version, which is an improvement already, and minimizes disturbing users.

-> H.Integration TestnetRuntime
cardanoTestnet
testnetOptions Conf {tempAbsPath=TmpAbsolutePath tmpAbsPath} startTime
shelleyGenesis alonzoGenesis conwayGenesis = do
let shelleyStartTime = sgSystemStart shelleyGenesis
shelleyTestnetMagic = sgNetworkMagic shelleyGenesis
optionsMagic :: Word32 = fromIntegral $ cardanoTestnetMagic testnetOptions
testnetMagic = cardanoTestnetMagic testnetOptions
numPoolNodes = length $ cardanoNodes testnetOptions
nbPools = numPools testnetOptions
era = cardanoNodeEra testnetOptions

-- Sanity checks
testnetMinimumConfigurationRequirements testnetOptions
when (shelleyStartTime /= startTime) $ do
H.note_ $ "Expected same system start in shelley genesis and parameter, but got " <> show shelleyStartTime <> " and " <> show startTime
H.failure
when (shelleyTestnetMagic /= optionsMagic) $ do
H.note_ $ "Expected same network magic in shelley genesis and parameter, but got " <> show shelleyTestnetMagic <> " and " <> show optionsMagic
H.failure
-- Done with sanity checks

H.note_ OS.os

if all isJust [mconfig | SpoTestnetNodeOptions mconfig _ <- cardanoNodes testnetOptions]
then
Expand All @@ -138,10 +185,8 @@ cardanoTestnet testnetOptions Conf {tempAbsPath=TmpAbsolutePath tmpAbsPath} = do
-- See all of the ad hoc file creation/renaming/dir creation etc below.
H.failMessage GHC.callStack "Specifying node configuration files per node not supported yet."
else do
startTime <- H.noteShow $ DTC.addUTCTime startTimeOffsetSeconds currentTime

H.lbsWriteFile (tmpAbsPath </> "byron.genesis.spec.json")
. encode $ defaultByronProtocolParamsJsonValue
. encode $ Defaults.defaultByronProtocolParamsJsonValue

-- Because in Conway the overlay schedule and decentralization parameter
-- are deprecated, we must use the "create-staked" cli command to create
Expand All @@ -153,10 +198,18 @@ cardanoTestnet testnetOptions Conf {tempAbsPath=TmpAbsolutePath tmpAbsPath} = do
(tmpAbsPath </> "byron.genesis.spec.json")
(tmpAbsPath </> "byron-gen-command")

_ <- createSPOGenesisAndFiles testnetOptions startTime (TmpAbsolutePath tmpAbsPath)
-- Write Alonzo genesis file
alonzoGenesisJsonFile <- H.noteShow $ tmpAbsPath </> "genesis.alonzo.spec.json"
H.evalIO $ LBS.writeFile alonzoGenesisJsonFile $ Aeson.encode alonzoGenesis

-- Write Conway genesis file
conwayGenesisJsonFile <- H.noteShow $ tmpAbsPath </> "genesis.conway.spec.json"
H.evalIO $ LBS.writeFile conwayGenesisJsonFile $ Aeson.encode conwayGenesis

configurationFile <- H.noteShow $ tmpAbsPath </> "configuration.yaml"

_ <- createSPOGenesisAndFiles nbPools era shelleyGenesis (TmpAbsolutePath tmpAbsPath)

poolKeys <- H.noteShow $ flip fmap [1..numPoolNodes] $ \n ->
PoolNodeKeys
{ poolNodeKeysColdVkey = tmpAbsPath </> "pools" </> "cold" <> show n <> ".vkey"
Expand Down Expand Up @@ -213,7 +266,7 @@ cardanoTestnet testnetOptions Conf {tempAbsPath=TmpAbsolutePath tmpAbsPath} = do


-- Add Byron, Shelley and Alonzo genesis hashes to node configuration
finalYamlConfig <- createConfigYaml (TmpAbsolutePath tmpAbsPath) $ cardanoNodeEra testnetOptions
finalYamlConfig <- createConfigYaml (TmpAbsolutePath tmpAbsPath) era

H.evalIO $ LBS.writeFile configurationFile finalYamlConfig

Expand Down Expand Up @@ -337,7 +390,7 @@ cardanoTestnet testnetOptions Conf {tempAbsPath=TmpAbsolutePath tmpAbsPath} = do

let runtime = TestnetRuntime
{ configurationFile
, shelleyGenesisFile = tmpAbsPath </> defaultShelleyGenesisFp
, shelleyGenesisFile = tmpAbsPath </> Defaults.defaultShelleyGenesisFp
, testnetMagic
, poolNodes
, wallets = wallets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ hprop_leadershipSchedule = H.integrationRetryWorkspace 2 "babbage-leadership-sch
{ testnetMagic
, wallets
, configurationFile
} <- cardanoTestnet cTestnetOptions conf
} <- cardanoTestnetDefault cTestnetOptions conf

node1sprocket <- H.headM $ poolSprockets tr
execConfig <- H.mkExecConfig tempBaseAbsPath node1sprocket testnetMagic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ hprop_stakeSnapshot = H.integrationRetryWorkspace 2 "babbage-stake-snapshot" $ \
TestnetRuntime
{ testnetMagic
, poolNodes
} <- cardanoTestnet options conf
} <- cardanoTestnetDefault options conf

poolNode1 <- H.headM poolNodes
poolSprocket1 <- H.noteShow $ nodeSprocket $ poolRuntime poolNode1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ hprop_transaction = H.integrationRetryWorkspace 0 "babbage-transaction" $ \tempA
{ testnetMagic
, poolNodes
, wallets
} <- cardanoTestnet options conf
} <- cardanoTestnetDefault options conf

poolNode1 <- H.headM poolNodes
poolSprocket1 <- H.noteShow $ nodeSprocket $ poolRuntime poolNode1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ hprop_stakeSnapshot = H.integrationRetryWorkspace 2 "conway-stake-snapshot" $ \t
TestnetRuntime
{ testnetMagic
, poolNodes
} <- cardanoTestnet options conf
} <- cardanoTestnetDefault options conf

poolNode1 <- H.headM poolNodes
poolSprocket1 <- H.noteShow $ nodeSprocket $ poolRuntime poolNode1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ hprop_kes_period_info = H.integrationRetryWorkspace 2 "kes-period-info" $ \tempA
, cardanoNodeEra = AnyCardanoEra era -- TODO: We should only support the latest era and the upcoming era
}

runTime@TestnetRuntime { configurationFile, testnetMagic, wallets } <- cardanoTestnet cTestnetOptions conf
runTime@TestnetRuntime { configurationFile, testnetMagic, wallets } <- cardanoTestnetDefault cTestnetOptions conf
node1sprocket <- H.headM $ poolSprockets runTime
execConfig <- H.mkExecConfig tempBaseAbsPath node1sprocket testnetMagic

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ hprop_querySlotNumber = H.integrationRetryWorkspace 2 "query-slot-number" $ \tem
tr@TestnetRuntime
{ testnetMagic
, poolNodes
} <- cardanoTestnet options conf
} <- cardanoTestnetDefault options conf
ShelleyGenesis{sgSlotLength, sgEpochLength} <- H.noteShowM $ shelleyGenesis tr
startTime <- H.noteShowM $ getStartTime tempAbsBasePath' tr

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ prop_foldBlocks = H.integrationRetryWorkspace 2 "foldblocks" $ \tempAbsBasePath'
, cardanoNodeEra = AnyCardanoEra era -- TODO: We should only support the latest era and the upcoming era
}

runtime@TestnetRuntime{configurationFile} <- cardanoTestnet options conf
runtime@TestnetRuntime{configurationFile} <- cardanoTestnetDefault options conf

-- Get socketPath
socketPathAbs <- do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ hprop_ledger_events_propose_new_constitution = H.integrationWorkspace "propose-n
, poolNodes
, wallets
}
<- cardanoTestnet fastTestnetOptions conf
<- cardanoTestnetDefault fastTestnetOptions conf

poolNode1 <- H.headM poolNodes
poolSprocket1 <- H.noteShow $ nodeSprocket $ poolRuntime poolNode1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ hprop_ledger_events_sanity_check = H.integrationWorkspace "ledger-events-sanity-
}

!testnetRuntime
<- cardanoTestnet fastTestnetOptions conf
<- cardanoTestnetDefault fastTestnetOptions conf
NodeRuntime{nodeSprocket} <- H.headM $ poolRuntime <$> poolNodes testnetRuntime
let socketName' = IO.sprocketName nodeSprocket
socketBase = IO.sprocketBase nodeSprocket -- /tmp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ hprop_shutdownOnSlotSynced = H.integrationRetryWorkspace 2 "shutdown-on-slot-syn
, SpoTestnetNodeOptions Nothing []
]
}
testnetRuntime <- cardanoTestnet fastTestnetOptions conf
testnetRuntime <- cardanoTestnetDefault fastTestnetOptions conf
let allNodes' = poolNodes testnetRuntime
H.note_ $ "All nodes: " <> show (map (nodeName . poolRuntime) allNodes')

Expand Down Expand Up @@ -238,7 +238,7 @@ hprop_shutdownOnSigint = H.integrationRetryWorkspace 2 "shutdown-on-sigint" $ \t
, cardanoSlotLength = 0.01
}
testnetRuntime
<- cardanoTestnet fastTestnetOptions conf
<- cardanoTestnetDefault fastTestnetOptions conf
node@NodeRuntime{nodeProcessHandle} <- H.headM $ poolRuntime <$> poolNodes testnetRuntime

-- send SIGINT
Expand Down
Loading
Loading