diff --git a/.gitattributes b/.gitattributes index 5a706b986af..14fae058354 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,5 +5,9 @@ configuration/cardano/mainnet-alonzo-genesis.json text eol=lf configuration/cardano/mainnet-byron-genesis.json text eol=lf configuration/cardano/mainnet-conway-genesis.json text eol=lf configuration/cardano/mainnet-shelley-genesis.json text eol=lf +# Genesis hashes are taken over the raw file bytes, so a CRLF checkout on +# Windows would not match the hashes pinned in the fixture configurations. +cardano-node/test/cardano-config-compare/config/*.json text eol=lf + cardano-testnet/test/cardano-testnet-test/files/sample-proposal-anchor text eol=lf cardano-testnet/test/cardano-testnet-test/files/sample-constitution-anchor text eol=lf diff --git a/cabal.project b/cabal.project index b2e5f12c4d0..74487e0ac73 100644 --- a/cabal.project +++ b/cabal.project @@ -166,3 +166,11 @@ if impl(ghc >= 9.14) -- Do NOT add more source-repository-package stanzas here unless they are strictly -- temporary! Please read the section in CONTRIBUTING about updating dependencies. + +-- TEMPORARY: cardano-config is not published to CHaP yet. Remove this stanza +-- (and depend on the CHaP release) as soon as it is. +source-repository-package + type: git + location: https://github.com/IntersectMBO/cardano-config + tag: 5f17f6c07f7fcccc67b5360da4339e2c16615b41 + --sha256: sha256-IWkreOeztCXfx31MyDtC3PmdlO4vzfklQiB4L2XW5KU= diff --git a/cardano-node/app/cardano-node.hs b/cardano-node/app/cardano-node.hs index 563193bd652..a6bea346174 100644 --- a/cardano-node/app/cardano-node.hs +++ b/cardano-node/app/cardano-node.hs @@ -1,24 +1,38 @@ {-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TemplateHaskell #-} +import qualified Cardano.Configuration as Cfg +import qualified Cardano.Configuration.CliArgs as CliArgs +import qualified Cardano.Configuration.Commands as Cmds import qualified Cardano.Crypto.Init as Crypto import Cardano.Git.Rev (gitRev) -import Cardano.Node.Configuration.POM (PartialNodeConfiguration (..)) +import Cardano.Node.Configuration.CardanoConfigAdapter + (cardanoConfigToNodeConfiguration) +import Cardano.Node.Configuration.CardanoConfigCompare + (compareConfigurations) +import Cardano.Node.Configuration.CardanoConfigResolve + (ConfigurationDialect (..), classifyConfigurationFile) +import Cardano.Node.Configuration.POM (NodeConfiguration (..), + PartialNodeConfiguration (..), defaultPartialNodeConfiguration, + makeNodeConfiguration, parseNodeConfigurationFP) import Cardano.Node.Handlers.TopLevel import Cardano.Node.Parsers (nodeCLIParser) import Cardano.Node.Run (runNode) import Cardano.Node.Tracing.Documentation (TraceDocumentationCmd (..), parseTraceDocumentationCmd, runTraceDocumentationCmd) +import Cardano.Node.Types (ConfigYamlFilePath (..)) -import Data.Monoid (Last (getLast)) +import Data.Monoid (Last (..)) import qualified Data.Text as Text import qualified Data.Text.IO as Text import Data.Version (showVersion) import Options.Applicative import qualified Options.Applicative as Opt +import System.Exit (exitFailure) import System.Info (arch, compilerName, compilerVersion, os) import System.IO (hPutStrLn, stderr) @@ -37,6 +51,7 @@ main = do runNode args TraceDocumentation tdc -> runTraceDocumentationCmd tdc VersionCmd -> runVersionCommand + ConfigCmd act -> act where p = Opt.prefs Opt.showHelpOnEmpty @@ -56,6 +71,7 @@ main = do Opt.info (fmap RunCmd nodeCLIParser <|> fmap TraceDocumentation parseTraceDocumentationCmd <|> parseVersionCmd + <|> fmap ConfigCmd configSubcommands <**> helper) ( Opt.fullDesc <> @@ -66,6 +82,7 @@ main = do data Command = RunCmd PartialNodeConfiguration | TraceDocumentation TraceDocumentationCmd | VersionCmd + | ConfigCmd (IO ()) -- Yes! A --version flag or version command. Either guess is right! parseVersionCmd :: Parser Command @@ -105,3 +122,96 @@ command' c descr p = [ command c (info (p <**> helper) $ mconcat [ progDesc descr ]) , metavar c ] + +-- cardano-config subcommands -------------------------------------------------- + +-- | The @migrate@, @schema@ and @resolve@ subcommands, spliced from the shared +-- @cardano-config:commands@ sublibrary. @migrate@ and @schema@ are +-- cardano-config's own commands, unchanged; @resolve@ is a node-specific variant +-- (see 'resolveDualCommand') that additionally cross-checks the node's own parser +-- against cardano-config's. +configSubcommands :: Parser (IO ()) +configSubcommands = + Opt.hsubparser + ( Opt.commandGroup "Configuration commands:" + <> Cmds.migrateCommand + <> Cmds.schemaCommand + <> resolveDualCommand + ) + +-- | A node-specific @resolve@: resolve the configuration with cardano-config +-- (printing the result as YAML, exactly like cardano-config's own @resolve@) and, +-- for a legacy configuration, re-resolve it with the node's own POM parser and +-- report any discrepancies between the two. Exits non-zero when they disagree, so +-- it doubles as a CI parity check while the node still has two parsers. +-- +-- A cardano-config envelope configuration has nothing to cross-check against: +-- the POM parser cannot read it (which is the whole point of the envelope), so +-- @resolve@ just prints the cardano-config result and says so. +resolveDualCommand :: Mod CommandFields (IO ()) +resolveDualCommand = + command "resolve" + ( info + (runDualResolve <$> Cmds.resolveOptionsParser) + ( progDesc + ( "Resolve a cardano-node configuration (defaults + file + CLI) and print the " + <> "result as YAML. A legacy (pre-cardano-config) configuration is resolved " + <> "with both the node and cardano-config parsers and any discrepancy between " + <> "them is reported (exit non-zero if they disagree)." + ) + ) + ) + +runDualResolve :: Cmds.ResolveOptions -> IO () +runDualResolve resolveOpts@(Cmds.ResolveOptions cli _geneses) = do + -- Print the resolved configuration using cardano-config's own renderer (which + -- honours --with-geneses); this also terminates via 'die' if resolution fails. + Cmds.runResolveCommand resolveOpts + classifyConfigurationFile configFp >>= \case + CardanoConfigDialect -> + putStrLn $ + "resolve: this is a cardano-config envelope configuration; the node's own parser" + <> " cannot read it, so there is nothing to cross-check." + LegacyDialect -> do + discrepancies <- resolveDiscrepancies cli + case discrepancies of + [] -> + putStrLn "resolve: the node and cardano-config parsers agree on the resolved configuration." + ds -> do + hPutStrLn stderr $ + "resolve: " <> show (length ds) + <> " discrepancy(ies) between the node and cardano-config parsers:" + mapM_ (hPutStrLn stderr . (" - " <>)) ds + exitFailure + where + configFp = CliArgs.configFilePath cli + +-- | Resolve a legacy configuration file (+ CLI) both ways and return the +-- divergences. The node (POM) side takes its CLI-supplied, file-absent fields +-- (topology / database / protocol files / socket) from the shared cardano-config +-- resolution, so the diff reflects how the two parsers read the configuration +-- FILE (plus the documented adapter gaps) rather than an independent — and +-- necessarily asymmetric — CLI reverse-mapping. +resolveDiscrepancies :: Cfg.CliArgs -> IO [String] +resolveDiscrepancies cli = do + (fileCfg, _warns) <- Cfg.parseConfigurationFiles configFp + case Cfg.resolveConfiguration cli fileCfg of + Left err -> pure ["cardano-config failed to resolve the configuration: " <> show err] + Right (cfgNc, _) -> + case cardanoConfigToNodeConfiguration cfgNc of + Left adaptErr -> pure ["cardano-config configuration could not be adapted: " <> adaptErr] + Right adaptedNc -> do + filePartial <- parseNodeConfigurationFP (Just (ConfigYamlFilePath configFp)) + let withCli = + (defaultPartialNodeConfiguration <> filePartial) + { pncConfigFile = Last (Just (ConfigYamlFilePath configFp)) + , pncTopologyFile = Last (Just (ncTopologyFile adaptedNc)) + , pncDatabaseFile = Last (Just (ncDatabaseFile adaptedNc)) + , pncProtocolFiles = Last (Just (ncProtocolFiles adaptedNc)) + , pncSocketConfig = Last (Just (ncSocketConfig adaptedNc)) + } + case makeNodeConfiguration withCli of + Left err -> pure ["node parser (makeNodeConfiguration) failed: " <> err] + Right pomNc -> pure (compareConfigurations pomNc adaptedNc) + where + configFp = CliArgs.configFilePath cli diff --git a/cardano-node/cardano-node.cabal b/cardano-node/cardano-node.cabal index f50385340cc..dd5716101c0 100644 --- a/cardano-node/cardano-node.cabal +++ b/cardano-node/cardano-node.cabal @@ -14,6 +14,7 @@ license-files: LICENSE NOTICE build-type: Simple extra-doc-files: ChangeLog.md +extra-source-files: test/cardano-config-compare/config/*.json Flag unexpected_thunks Description: Turn on unexpected thunks checks @@ -69,7 +70,10 @@ library hs-source-dirs: src - exposed-modules: Cardano.Node.Configuration.NodeAddress + exposed-modules: Cardano.Node.Configuration.CardanoConfigAdapter + Cardano.Node.Configuration.CardanoConfigCompare + Cardano.Node.Configuration.CardanoConfigResolve + Cardano.Node.Configuration.NodeAddress Cardano.Node.Configuration.POM Cardano.Node.Configuration.LedgerDB Cardano.Node.Configuration.Socket @@ -134,6 +138,7 @@ library , base16-bytestring , bytestring , cardano-api ^>= 11.6 + , cardano-config , cardano-data , cardano-crypto-class ^>=2.5 , cardano-crypto-wrapper @@ -217,12 +222,28 @@ executable cardano-node autogen-modules: Paths_cardano_node build-depends: base + , cardano-config + , cardano-config:commands , cardano-crypto-class , cardano-git-rev , cardano-node , optparse-applicative , text +test-suite cardano-config-compare-test + import: project-config + hs-source-dirs: test/cardano-config-compare + main-is: Main.hs + type: exitcode-stdio-1.0 + + build-depends: base + , cardano-config + , cardano-node + , directory + , filepath + , tasty + , tasty-hunit + test-suite cardano-node-test import: project-config , maybe-unix diff --git a/cardano-node/src/Cardano/Node/Configuration/CardanoConfigAdapter.hs b/cardano-node/src/Cardano/Node/Configuration/CardanoConfigAdapter.hs new file mode 100644 index 00000000000..c222f902444 --- /dev/null +++ b/cardano-node/src/Cardano/Node/Configuration/CardanoConfigAdapter.hs @@ -0,0 +1,402 @@ +{-# LANGUAGE ScopedTypeVariables #-} + +-- | Adapter from @cardano-config@'s resolved configuration to the node's own +-- 'NodeConfiguration' (the POM one). +-- +-- It maps @cardano-config@'s resolved values onto a 'PartialNodeConfiguration' +-- and runs the node's own 'makeNodeConfiguration', so fields cardano-config +-- supplies come from cardano-config and the rest fall back to the node defaults. +-- +-- This is what the node runs on for a @cardano-config@ envelope configuration — +-- the only parser that can read one — and what the legacy configuration's +-- cross-check is compared against; see +-- 'Cardano.Node.Configuration.CardanoConfigResolve'. +-- +-- Fields not mapped are listed in 'adapterGaps' — that gap list is exactly what +-- must be closed before the POM parser can be dropped, and every gap also shows +-- up concretely as a divergence in +-- 'Cardano.Node.Configuration.CardanoConfigCompare.compareConfigurations'. +module Cardano.Node.Configuration.CardanoConfigAdapter + ( cardanoConfigToNodeConfiguration + , cardanoConfigToPartialNodeConfiguration + , nodeProtocolConfigurationFromCardanoConfig + , adapterGaps + ) where + +import Cardano.Api (File (..)) +import qualified Cardano.Configuration as Cfg +import qualified Cardano.Configuration.CliArgs as CliArgs +import Cardano.Crypto (RequiresNetworkMagic (..)) +import Cardano.Ledger.BaseTypes (strictMaybeToMaybe) +import Cardano.Ledger.BaseTypes.NonZero (nonZero) +import Cardano.Network.ConsensusMode (ConsensusMode (..)) +import Cardano.Network.NodeToNode (DiffusionMode (..)) +import Cardano.Network.PeerSelection (NumberOfBigLedgerPeers (..)) +import Cardano.Logging.Types (ForwarderMode (..), HowToConnect (..)) +import Cardano.Node.Configuration.LedgerDB (LedgerDbConfiguration (..), + LedgerDbSelectorFlag (..), noDeprecatedOptions) +import Cardano.Node.Configuration.NodeAddress (NodeHostIPv4Address (..), + NodeHostIPv6Address (..)) +import Cardano.Node.Configuration.POM (NodeConfiguration, + PartialNodeConfiguration (..), ResponderCoreAffinityPolicy (..), + defaultPartialNodeConfiguration, makeNodeConfiguration) +import Cardano.Node.Configuration.Socket (SocketConfig (..)) +import Cardano.Node.Handlers.Shutdown (ShutdownConfig (..), + ShutdownOn (..)) +import Cardano.Node.Types (ConfigYamlFilePath (..), GenesisFile (..), + GenesisHash (..), KESSource (..), MaxConcurrencyBulkSync (..), + MaxConcurrencyDeadline (..), + NodeAlonzoProtocolConfiguration (..), + NodeByronProtocolConfiguration (..), + NodeCheckpointsConfiguration (..), + NodeConwayProtocolConfiguration (..), + NodeDijkstraProtocolConfiguration (..), + NodeHardForkProtocolConfiguration (..), + NodeProtocolConfiguration (..), + NodeShelleyProtocolConfiguration (..), ProtocolFilepaths (..), + TopologyFile (..)) +import Cardano.Slotting.Block (BlockNo (..)) +import Cardano.Slotting.Slot (EpochNo (..), SlotNo (..)) +import Cardano.Rpc.Server.Config (RpcConfigF (..)) +import Data.Functor.Identity (runIdentity) +import Data.Monoid (Last (..)) +import Data.Time.Clock (secondsToDiffTime) +import Ouroboros.Consensus.Node (NodeDatabasePaths (..)) +import Ouroboros.Consensus.Node.Genesis (GenesisConfigFlags (..), + defaultGenesisConfigFlags) +import Ouroboros.Consensus.Ledger.SupportsMempool (ByteSize32 (..)) +import Ouroboros.Consensus.Mempool (MempoolCapacityBytesOverride (..)) +import Ouroboros.Consensus.Storage.LedgerDB.Args (QueryBatchSize (..)) +import Ouroboros.Consensus.Storage.LedgerDB.Snapshots + (NumOfDiskSnapshots (..), SnapshotDelayRange (..), + SnapshotFrequency (..), SnapshotFrequencyArgs (..), + SnapshotInterval (..), SnapshotPolicyArgs (..), + defaultSnapshotPolicyArgs, mithrilSnapshotPolicyArgs) +import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing (..)) +import Ouroboros.Network.Server.RateLimiting (AcceptedConnectionsLimit (..)) +import Ouroboros.Network.TxSubmission.Inbound.V2.Types + (TxSubmissionInitDelay (..), TxSubmissionLogicVersion (..)) +import System.FilePath (takeDirectory, ()) + +-- | Build the node's 'NodeConfiguration' from a @cardano-config@-resolved +-- configuration, reusing the node's own 'makeNodeConfiguration'. Fields +-- cardano-config does not yet supply keep the node defaults (see 'adapterGaps'). +cardanoConfigToNodeConfiguration :: Cfg.NodeConfiguration -> Either String NodeConfiguration +cardanoConfigToNodeConfiguration = + makeNodeConfiguration . cardanoConfigToPartialNodeConfiguration + +-- | Map the @cardano-config@-resolved values onto a 'PartialNodeConfiguration', +-- overriding the node defaults for every field cardano-config supplies. +cardanoConfigToPartialNodeConfiguration :: Cfg.NodeConfiguration -> PartialNodeConfiguration +cardanoConfigToPartialNodeConfiguration cfg = + defaultPartialNodeConfiguration + { pncConfigFile = Last (Just (ConfigYamlFilePath (Cfg.configFilePath cfg))) + , pncTopologyFile = Last (Just (TopologyFile (Cfg.topologyFile cfg))) + , pncValidateDB = Last (Just (Cfg.validateDatabase cfg)) + , pncStartAsNonProducingNode = Last (Just (runIdentity (Cfg.startAsNonProducingNode protoCfg))) + , pncProtocolConfig = Last (Just (nodeProtocolConfigurationFromCardanoConfig cfg)) + , pncProtocolFiles = Last (Just (credentialsToProtocolFilepaths (Cfg.credentials cfg))) + , pncExperimentalProtocolsEnabled = Last (Just (runIdentity (Cfg.experimentalProtocolsEnabled netCfg))) + , pncMempoolTimeoutSoft = Last (Just (runIdentity (Cfg.mempoolTimeoutSoft mempCfg))) + , pncMempoolTimeoutHard = Last (Just (runIdentity (Cfg.mempoolTimeoutHard mempCfg))) + , pncMempoolTimeoutCapacity = Last (Just (runIdentity (Cfg.mempoolTimeoutCapacity mempCfg))) + , pncMinBigLedgerPeersForTrustedState = + Last (Just (NumberOfBigLedgerPeers (runIdentity (Cfg.minBigLedgerPeersForTrustedState netCfg)))) + , pncDeadlineTargetOfEstablishedPeers = + Last (strictMaybeToMaybe (Cfg.deadlineTargetOfEstablishedPeers netCfg)) + , pncDeadlineTargetOfEstablishedBigLedgerPeers = + Last (strictMaybeToMaybe (Cfg.deadlineTargetOfEstablishedBigLedgerPeers netCfg)) + , pncSyncTargetOfEstablishedBigLedgerPeers = + Last (Just (runIdentity (Cfg.syncTargetOfEstablishedBigLedgerPeers netCfg))) + , pncDatabaseFile = Last (Just (fromCfgDbPaths (runIdentity (Cfg.databasePath storeCfg)))) + , pncDiffusionMode = Last (Just (fromCfgDiffusionMode (runIdentity (Cfg.diffusionMode netCfg)))) + , pncMaxConcurrencyBulkSync = + Last (Just (MaxConcurrencyBulkSync (runIdentity (Cfg.maxConcurrencyBulkSync netCfg)))) + , pncMaxConcurrencyDeadline = + Last (Just (MaxConcurrencyDeadline (runIdentity (Cfg.maxConcurrencyDeadline netCfg)))) + , pncTxSubmissionInitDelay = + Last (Just (TxSubmissionInitDelay (runIdentity (Cfg.txSubmissionInitDelay netCfg)))) + , pncAcceptedConnectionsLimit = + Last (Just (fromCfgAcceptedConnLimit (runIdentity (Cfg.acceptedConnectionsLimit netCfg)))) + , pncConsensusMode = Last (Just (fromCfgConsensusMode consensusModeVal)) + , pncPeerSharing = + Last (fmap toPeerSharing (strictMaybeToMaybe (Cfg.peerSharing netCfg))) + , pncMaybeMempoolCapacityOverride = + Last (fmap (MempoolCapacityBytesOverride . ByteSize32 . fromIntegral) + (strictMaybeToMaybe (Cfg.mempoolCapacityOverride mempCfg))) + , pncShutdownConfig = + Last (Just (ShutdownConfig + (strictMaybeToMaybe (Cfg.shutdownIPC cfg)) + (fmap toNodeShutdownOn (strictMaybeToMaybe (Cfg.shutdownOnTarget cfg))))) + , pncResponderCoreAffinityPolicy = + Last (Just (fromCfgAffinity (runIdentity (Cfg.responderCoreAffinityPolicy netCfg)))) + , pncTxSubmissionLogicVersion = + Last (Just (fromCfgTxSubmissionLogic (runIdentity (Cfg.txSubmissionLogicVersion netCfg)))) + , -- The Genesis tuning flags only feed 'ncGenesisConfig' when the node runs + -- in Genesis mode (see 'makeNodeConfiguration'); in Praos mode the node + -- ignores them, so mirror POM and keep the defaults there. + pncGenesisConfigFlags = + Last (Just (case consensusModeVal of + Cfg.GenesisMode flags -> fromCfgGenesisFlags flags + Cfg.PraosMode -> defaultGenesisConfigFlags)) + , -- The local (IPC) socket path comes from the configuration file or the + -- command line; the node-to-node IPv4/IPv6/port bindings are CLI-only in + -- both parsers, and cardano-config carries them on the resolved + -- configuration, so they are mapped here too. + pncSocketConfig = + Last (Just (SocketConfig + (Last (fmap NodeHostIPv4Address (strictMaybeToMaybe (Cfg.hostAddr cfg)))) + (Last (fmap NodeHostIPv6Address (strictMaybeToMaybe (Cfg.hostIPv6Addr cfg)))) + (Last (strictMaybeToMaybe (Cfg.port cfg))) + (Last (fmap File (strictMaybeToMaybe (Cfg.socketPath lcc)))))) + , pncTraceForwardSocket = + Last (fmap fromCfgTracerConnection (strictMaybeToMaybe (Cfg.tracerSocket cfg))) + , -- 'nodeSocketPath' (third field) is filled in by 'makeNodeConfiguration' + -- from the resolved socket config, so leave it empty here. + pncRpcConfig = + RpcConfig + (Last (Just (runIdentity (Cfg.enableGrpc lcc)))) + (Last (fmap File (strictMaybeToMaybe (Cfg.grpcSocketPath lcc)))) + mempty + , -- Backend selector, query batch size and snapshot policy are all mapped + -- from cardano-config. 'DeprecatedOptions' has no cardano-config + -- counterpart (they are the legacy top-level SnapshotInterval / + -- NumOfDiskSnapshots keys), so it keeps the node's empty default. + pncLedgerDbConfig = + Last (Just (LedgerDbConfiguration + (fromCfgSnapshotPolicy (strictMaybeToMaybe (Cfg.snapshots ledgerDbCfg))) + (maybe DefaultQueryBatchSize RequestedQueryBatchSize + (strictMaybeToMaybe (Cfg.queryBatchSize ledgerDbCfg))) + (maybe V2InMemory fromCfgBackend + (strictMaybeToMaybe (Cfg.backendSelector ledgerDbCfg))) + noDeprecatedOptions)) + } + where + protoCfg = Cfg.protocolConfiguration cfg + netCfg = Cfg.networkConfiguration cfg + mempCfg = Cfg.mempoolConfiguration cfg + storeCfg = Cfg.storageConfiguration cfg + lcc = Cfg.localConnectionsConfig cfg + ledgerDbCfg = runIdentity (Cfg.ledgerDbConfiguration storeCfg) + consensusModeVal = runIdentity (Cfg.getConsensusConfiguration (Cfg.consensusConfiguration cfg)) + + fromCfgDbPaths :: Cfg.NodeDatabasePaths -> NodeDatabasePaths + fromCfgDbPaths (Cfg.SingleDB p) = OnePathForAllDbs p + fromCfgDbPaths (Cfg.SplitDB imm vol) = MultipleDbPaths imm vol + + fromCfgDiffusionMode :: Cfg.DiffusionMode -> DiffusionMode + fromCfgDiffusionMode Cfg.InitiatorOnly = InitiatorOnlyDiffusionMode + fromCfgDiffusionMode Cfg.InitiatorAndResponder = InitiatorAndResponderDiffusionMode + + fromCfgAcceptedConnLimit :: Cfg.AcceptedConnectionsLimit -> AcceptedConnectionsLimit + fromCfgAcceptedConnLimit c = + AcceptedConnectionsLimit + { acceptedConnectionsHardLimit = Cfg.hardLimit c + , acceptedConnectionsSoftLimit = Cfg.softLimit c + , acceptedConnectionsDelay = Cfg.delayOnSoftLimit c + } + + fromCfgConsensusMode :: Cfg.ConsensusMode -> ConsensusMode + fromCfgConsensusMode Cfg.PraosMode = PraosMode + fromCfgConsensusMode (Cfg.GenesisMode _) = GenesisMode + + toPeerSharing :: Bool -> PeerSharing + toPeerSharing True = PeerSharingEnabled + toPeerSharing False = PeerSharingDisabled + + toNodeShutdownOn :: Cfg.ShutdownOn -> ShutdownOn + toNodeShutdownOn (Cfg.ShutdownAtSlot w) = ASlot (SlotNo w) + toNodeShutdownOn (Cfg.ShutdownAtBlock w) = ABlock (BlockNo w) + + fromCfgAffinity :: Cfg.ResponderCoreAffinityPolicy -> ResponderCoreAffinityPolicy + fromCfgAffinity Cfg.NoResponderCoreAffinity = NoResponderCoreAffinity + fromCfgAffinity Cfg.ResponderCoreAffinity = ResponderCoreAffinity + + -- cardano-config records the socket mode as the literal @"Accept"@ / + -- @"Connect"@ its CLI parser produces; anything else cannot occur. + fromCfgTracerConnection :: Cfg.TracerConnection -> (HowToConnect, ForwarderMode) + fromCfgTracerConnection (Cfg.TracerConnection mode method) = + ( case method of + CliArgs.TracerConnectViaPipe fp -> LocalPipe fp + CliArgs.TracerConnectViaRemote host portNo -> + RemoteSocket host (fromIntegral portNo) + , case mode of + "Accept" -> Responder + _ -> Initiator + ) + + fromCfgTxSubmissionLogic :: Cfg.TxSubmissionLogicVersion -> TxSubmissionLogicVersion + fromCfgTxSubmissionLogic Cfg.TxSubmissionLogicV1 = TxSubmissionLogicV1 + fromCfgTxSubmissionLogic Cfg.TxSubmissionLogicV2 = TxSubmissionLogicV2 + + -- Map cardano-config's snapshot policy onto the node's 'SnapshotPolicyArgs', + -- mirroring how POM's LedgerDB parser builds it: a named Mithril policy + -- selects the predefined 'mithrilSnapshotPolicyArgs', a custom policy is + -- mapped field-by-field, and absence keeps the node default. + fromCfgSnapshotPolicy :: Maybe Cfg.SnapshotPolicy -> SnapshotPolicyArgs + fromCfgSnapshotPolicy Nothing = defaultSnapshotPolicyArgs + fromCfgSnapshotPolicy (Just Cfg.MithrilSnapshotPolicy) = mithrilSnapshotPolicyArgs + fromCfgSnapshotPolicy (Just (Cfg.CustomSnapshotPolicy opts)) = + SnapshotPolicyArgs + (SnapshotFrequency SnapshotFrequencyArgs + { sfaInterval = + maybe (sfaInterval defaultFrequencyArgs) RequestedSnapshotInterval + (strictMaybeToMaybe (Cfg.snapshotInterval opts) >>= nonZero) + , sfaOffset = + maybe (sfaOffset defaultFrequencyArgs) SlotNo + (strictMaybeToMaybe (Cfg.slotOffset opts)) + , sfaRateLimit = + maybe (sfaRateLimit defaultFrequencyArgs) (secondsToDiffTime . fromIntegral) + (strictMaybeToMaybe (Cfg.snapshotRateLimit opts)) + , sfaDelaySnapshotRange = + case (strictMaybeToMaybe (Cfg.minDelay opts), strictMaybeToMaybe (Cfg.maxDelay opts)) of + (Just mn, Just mx) -> + SnapshotDelayRange (secondsToDiffTime (fromIntegral mn)) + (secondsToDiffTime (fromIntegral mx)) + _ -> sfaDelaySnapshotRange defaultFrequencyArgs + }) + (maybe (spaNum defaultSnapshotPolicyArgs) (NumOfDiskSnapshots . fromIntegral) + (strictMaybeToMaybe (Cfg.numOfDiskSnapshots opts))) + + -- A snapshot option the configuration leaves unset keeps the node's own + -- default for that field, exactly as POM's LedgerDB parser does. + defaultFrequencyArgs = case spaFrequency defaultSnapshotPolicyArgs of + SnapshotFrequency sfa -> sfa + DisableSnapshots -> + error "defaultSnapshotPolicyArgs unexpectedly disables snapshots" + + fromCfgBackend :: Cfg.LedgerDbBackendSelector -> LedgerDbSelectorFlag + fromCfgBackend Cfg.V2InMemory = V2InMemory + fromCfgBackend (Cfg.V2LSM dbPath exportPath) = + V2LSM (strictMaybeToMaybe dbPath) (strictMaybeToMaybe exportPath) + + -- cardano-config's 'GenesisConfigFlags' mirrors the node's field-for-field, + -- except 'gcfCSJJumpSize' is a raw 'Word64' there vs a 'SlotNo' here, and the + -- optional fields are 'StrictMaybe' vs 'Maybe'. + fromCfgGenesisFlags :: Cfg.GenesisConfigFlags -> GenesisConfigFlags + fromCfgGenesisFlags f = + GenesisConfigFlags + (Cfg.gcfEnableCSJ f) + (Cfg.gcfEnableLoEAndGDD f) + (Cfg.gcfEnableLoP f) + (strictMaybeToMaybe (Cfg.gcfBlockFetchGracePeriod f)) + (strictMaybeToMaybe (Cfg.gcfBucketCapacity f)) + (strictMaybeToMaybe (Cfg.gcfBucketRate f)) + (fmap SlotNo (strictMaybeToMaybe (Cfg.gcfCSJJumpSize f))) + (strictMaybeToMaybe (Cfg.gcfGDDRateLimit f)) + +-- | Map @cardano-config@ 'Cfg.Credentials' (file paths) onto the node's +-- 'ProtocolFilepaths'. +credentialsToProtocolFilepaths :: Cfg.Credentials -> ProtocolFilepaths +credentialsToProtocolFilepaths c = + ProtocolFilepaths + { byronCertFile = strictMaybeToMaybe (Cfg.byronDelegationCertificate c) + , byronKeyFile = strictMaybeToMaybe (Cfg.byronSigningKey c) + , shelleyKESSource = fmap fromCfgKES (strictMaybeToMaybe (Cfg.shelleyKES c)) + , shelleyVRFFile = strictMaybeToMaybe (Cfg.shelleyVRFKey c) + , shelleyCertFile = strictMaybeToMaybe (Cfg.shelleyOperationalCertificate c) + , shelleyBulkCredsFile = strictMaybeToMaybe (Cfg.bulkCredentialsFile c) + } + where + fromCfgKES (Cfg.KESKeyFilePath fp) = KESKeyFilePath fp + fromCfgKES (Cfg.KESAgentSocketPath fp) = KESAgentSocketPath fp + +-- | Build the node's 'NodeProtocolConfiguration' from a @cardano-config@-resolved +-- configuration. Genesis file paths are resolved relative to the configuration +-- file's directory (the way cardano-config resolves them at read time). +nodeProtocolConfigurationFromCardanoConfig :: + Cfg.NodeConfiguration -> NodeProtocolConfiguration +nodeProtocolConfigurationFromCardanoConfig cfg = + NodeProtocolConfigurationCardano + byronConfig + shelleyConfig + alonzoConfig + conwayConfig + dijkstraConfig + hardforkConfig + checkpointsConfig + where + protoCfg = Cfg.protocolConfiguration cfg + testCfg = Cfg.testingConfiguration cfg + configDir = takeDirectory (Cfg.configFilePath cfg) + + genFile :: Cfg.Hashed FilePath -> GenesisFile + genFile h = GenesisFile (configDir Cfg.hashed h) + + genHash :: Cfg.Hashed FilePath -> Maybe GenesisHash + genHash h = Just (GenesisHash (Cfg.hash h)) + + byronGen = Cfg.byronGenesis protoCfg + byronConfig = + NodeByronProtocolConfiguration + { npcByronGenesisFile = genFile (Cfg.byronGenesisFile byronGen) + , npcByronGenesisFileHash = genHash (Cfg.byronGenesisFile byronGen) + , npcByronReqNetworkMagic = + maybe RequiresNoMagic fromCfgReqNetworkMagic + (strictMaybeToMaybe (Cfg.byronReqNetworkMagic byronGen)) + , npcByronPbftSignatureThresh = Nothing + , -- cardano-config does not model the Byron software (block) version: its + -- @LastKnownBlockVersion-*@ keys are among the ones @migrate@ drops, as + -- they now come from consensus defaults rather than configuration. A + -- fixed default is used here, which surfaces as a divergence against POM + -- for any configuration that still sets them (see 'adapterGaps'). + npcByronSupportedProtocolVersionMajor = 1 + , npcByronSupportedProtocolVersionMinor = 0 + , npcByronSupportedProtocolVersionAlt = 0 + } + + shelleyConfig = + NodeShelleyProtocolConfiguration + (genFile (Cfg.shelleyGenesis protoCfg)) + (genHash (Cfg.shelleyGenesis protoCfg)) + alonzoConfig = + NodeAlonzoProtocolConfiguration + (genFile (Cfg.alonzoGenesis protoCfg)) + (genHash (Cfg.alonzoGenesis protoCfg)) + conwayConfig = + NodeConwayProtocolConfiguration + (genFile (Cfg.conwayGenesis protoCfg)) + (genHash (Cfg.conwayGenesis protoCfg)) + dijkstraConfig = + fmap + (\h -> NodeDijkstraProtocolConfiguration (genFile h) (genHash h)) + (strictMaybeToMaybe (Cfg.experimentalGenesis testCfg)) + + hardforkConfig = + NodeHardForkProtocolConfiguration + { npcExperimentalHardForksEnabled = runIdentity (Cfg.experimentalHardForksEnabled testCfg) + , npcTestShelleyHardForkAtEpoch = epochOf (Cfg.testShelleyHardForkAtEpoch testCfg) + , npcTestShelleyHardForkAtVersion = strictMaybeToMaybe (Cfg.testShelleyHardForkAtVersion testCfg) + , npcTestAllegraHardForkAtEpoch = epochOf (Cfg.testAllegraHardForkAtEpoch testCfg) + , npcTestAllegraHardForkAtVersion = strictMaybeToMaybe (Cfg.testAllegraHardForkAtVersion testCfg) + , npcTestMaryHardForkAtEpoch = epochOf (Cfg.testMaryHardForkAtEpoch testCfg) + , npcTestMaryHardForkAtVersion = strictMaybeToMaybe (Cfg.testMaryHardForkAtVersion testCfg) + , npcTestAlonzoHardForkAtEpoch = epochOf (Cfg.testAlonzoHardForkAtEpoch testCfg) + , npcTestAlonzoHardForkAtVersion = strictMaybeToMaybe (Cfg.testAlonzoHardForkAtVersion testCfg) + , npcTestBabbageHardForkAtEpoch = epochOf (Cfg.testBabbageHardForkAtEpoch testCfg) + , npcTestBabbageHardForkAtVersion = strictMaybeToMaybe (Cfg.testBabbageHardForkAtVersion testCfg) + , npcTestConwayHardForkAtEpoch = epochOf (Cfg.testConwayHardForkAtEpoch testCfg) + , npcTestConwayHardForkAtVersion = strictMaybeToMaybe (Cfg.testConwayHardForkAtVersion testCfg) + , npcTestDijkstraHardForkAtEpoch = epochOf (Cfg.testDijkstraHardForkAtEpoch testCfg) + , npcTestDijkstraHardForkAtVersion = strictMaybeToMaybe (Cfg.testDijkstraHardForkAtVersion testCfg) + } + + checkpointsConfig = NodeCheckpointsConfiguration Nothing Nothing + + epochOf = fmap EpochNo . strictMaybeToMaybe + + fromCfgReqNetworkMagic :: Cfg.RequiresNetworkMagic -> RequiresNetworkMagic + fromCfgReqNetworkMagic Cfg.RequiresNoMagic = RequiresNoMagic + fromCfgReqNetworkMagic Cfg.RequiresMagic = RequiresMagic + +-- | Node 'NodeConfiguration' fields the adapter does not yet populate from +-- @cardano-config@ (they keep the node defaults, so they show up as divergences +-- against POM). Closing these is the remaining work before POM can be dropped. +adapterGaps :: [String] +adapterGaps = + [ "ncProtocolConfig: Byron supported-protocol-version — the LastKnownBlockVersion-*" + <> " keys are deliberately not modelled by cardano-config (they now come from" + <> " consensus defaults), so a fixed 1/0/0 is used here" + , "ncProtocolConfig: checkpoints — the CheckpointsFile/CheckpointsFileHash keys have" + <> " no cardano-config counterpart, so the checkpoints configuration is always empty" + ] diff --git a/cardano-node/src/Cardano/Node/Configuration/CardanoConfigCompare.hs b/cardano-node/src/Cardano/Node/Configuration/CardanoConfigCompare.hs new file mode 100644 index 00000000000..9e6010087b0 --- /dev/null +++ b/cardano-node/src/Cardano/Node/Configuration/CardanoConfigCompare.hs @@ -0,0 +1,126 @@ +-- | Diff the node's POM-resolved 'NodeConfiguration' against the one produced by +-- the @cardano-config@ adapter ('Cardano.Node.Configuration.CardanoConfigAdapter'). +-- +-- Both sides are the node's own 'NodeConfiguration', so every field is compared +-- in the node's own representation — a reported divergence reflects a genuine +-- difference in the resolved value, with no representation mismatch and no +-- deferred set. A field the adapter cannot yet populate from cardano-config keeps +-- the node default and therefore surfaces here as a divergence (see +-- 'Cardano.Node.Configuration.CardanoConfigAdapter.adapterGaps'). +module Cardano.Node.Configuration.CardanoConfigCompare + ( compareConfigurations + , deprecatedFlagWarnings + ) where + +import Cardano.Node.Configuration.POM (NodeConfiguration (..)) +import Cardano.Node.Types (NodeProtocolConfiguration (..)) + +-- | Diagnose node CLI flags that cardano-config's own CLI parser rejects, in +-- terms the operator can act on. All of these flags are deprecated: the three +-- legacy aliases have a new spelling cardano-config accepts, and the two mempool +-- flags have been removed by design (mempool capacity is a config-file setting +-- now). Given the node's argv flag list, returns one guidance line per offending +-- flag found. A pure function so the dual-parse warning path is unit-testable. +deprecatedFlagWarnings :: [String] -> [String] +deprecatedFlagWarnings = concatMap diagnose + where + -- Deprecated alias -> new (cardano-config-accepted) spelling. + renamed = + [ ("--delegation-certificate", "--byron-delegation-certificate") + , ("--signing-key", "--byron-signing-key") + , ("--non-producing-node", "--start-as-non-producing-node") + ] + -- Deprecated and removed by design; not something cardano-config should grow. + removed = ["--mempool-capacity-override", "--no-mempool-capacity-override"] + + diagnose tok = + -- Accept both @--flag value@ and @--flag=value@ spellings. + let opt = takeWhile (/= '=') tok + in case lookup opt renamed of + Just new -> + [ "warning: deprecated CLI flag '" <> opt <> "'; use '" <> new + <> "' (required for cardano-config parsing / the upcoming config parser)" ] + Nothing + | opt `elem` removed -> + [ "warning: '" <> opt <> "' is deprecated and no longer supported; remove it" + <> " and set 'MempoolCapacityBytesOverride' in the configuration file instead" ] + | otherwise -> [] + +-- | Compare a POM-resolved configuration (first argument) against the +-- adapter-produced one (second argument), field by field. Returns one line per +-- diverging field; an empty list means they agree on everything compared. +compareConfigurations :: NodeConfiguration -> NodeConfiguration -> [String] +compareConfigurations pom adapted = + concat + [ -- Protocol config, compared per era/component for readable diffs. + compareProtocol (ncProtocolConfig pom) (ncProtocolConfig adapted) + , cmp "ValidateDB" ncValidateDB + , cmp "TopologyFile" ncTopologyFile + , cmp "DatabaseFile" ncDatabaseFile + , cmp "StartAsNonProducingNode" ncStartAsNonProducingNode + , cmp "ProtocolFiles" ncProtocolFiles + , cmp "ShutdownConfig" ncShutdownConfig + , cmp "SocketConfig" ncSocketConfig + , cmp "DiffusionMode" ncDiffusionMode + , cmp "ExperimentalProtocolsEnabled" ncExperimentalProtocolsEnabled + , cmp "MaxConcurrencyBulkSync" ncMaxConcurrencyBulkSync + , cmp "MaxConcurrencyDeadline" ncMaxConcurrencyDeadline + , cmp "TraceForwardSocket" ncTraceForwardSocket + , cmp "MaybeMempoolCapacityOverride" ncMaybeMempoolCapacityOverride + , cmp "LedgerDbConfig" ncLedgerDbConfig + , cmp "ProtocolIdleTimeout" ncProtocolIdleTimeout + , cmp "TimeWaitTimeout" ncTimeWaitTimeout + , cmp "EgressPollInterval" ncEgressPollInterval + , cmp "ChainSyncIdleTimeout" ncChainSyncIdleTimeout + , cmp "MempoolTimeoutSoft" ncMempoolTimeoutSoft + , cmp "MempoolTimeoutHard" ncMempoolTimeoutHard + , cmp "MempoolTimeoutCapacity" ncMempoolTimeoutCapacity + , cmp "AcceptedConnectionsLimit" ncAcceptedConnectionsLimit + , cmp "DeadlineTargetOfRootPeers" ncDeadlineTargetOfRootPeers + , cmp "DeadlineTargetOfKnownPeers" ncDeadlineTargetOfKnownPeers + , cmp "DeadlineTargetOfEstablishedPeers" ncDeadlineTargetOfEstablishedPeers + , cmp "DeadlineTargetOfActivePeers" ncDeadlineTargetOfActivePeers + , cmp "DeadlineTargetOfKnownBigLedgerPeers" ncDeadlineTargetOfKnownBigLedgerPeers + , cmp "DeadlineTargetOfEstablishedBigLedgerPeers" ncDeadlineTargetOfEstablishedBigLedgerPeers + , cmp "DeadlineTargetOfActiveBigLedgerPeers" ncDeadlineTargetOfActiveBigLedgerPeers + , cmp "SyncTargetOfRootPeers" ncSyncTargetOfRootPeers + , cmp "SyncTargetOfKnownPeers" ncSyncTargetOfKnownPeers + , cmp "SyncTargetOfEstablishedPeers" ncSyncTargetOfEstablishedPeers + , cmp "SyncTargetOfActivePeers" ncSyncTargetOfActivePeers + , cmp "SyncTargetOfKnownBigLedgerPeers" ncSyncTargetOfKnownBigLedgerPeers + , cmp "SyncTargetOfEstablishedBigLedgerPeers" ncSyncTargetOfEstablishedBigLedgerPeers + , cmp "SyncTargetOfActiveBigLedgerPeers" ncSyncTargetOfActiveBigLedgerPeers + , cmp "ConsensusMode" ncConsensusMode + , cmp "MinBigLedgerPeersForTrustedState" ncMinBigLedgerPeersForTrustedState + , cmp "PeerSharing" ncPeerSharing + , cmp "GenesisConfig" ncGenesisConfig + , cmp "ResponderCoreAffinityPolicy" ncResponderCoreAffinityPolicy + , cmp "RpcConfig" ncRpcConfig + , cmp "TxSubmissionLogicVersion" ncTxSubmissionLogicVersion + , cmp "TxSubmissionInitDelay" ncTxSubmissionInitDelay + ] + where + cmp :: (Eq a, Show a) => String -> (NodeConfiguration -> a) -> [String] + cmp label accessor = cmpValues label (accessor pom) (accessor adapted) + +-- | Report a divergence between two values of the same type. +cmpValues :: (Eq a, Show a) => String -> a -> a -> [String] +cmpValues label a b + | a == b = [] + | otherwise = [label <> ": node=" <> show a <> " vs cardano-config=" <> show b] + +-- | Compare the Cardano protocol configuration per component (each era's genesis +-- settings, the hard-fork triggers and the checkpoints) for readable diffs. +compareProtocol :: NodeProtocolConfiguration -> NodeProtocolConfiguration -> [String] +compareProtocol + (NodeProtocolConfigurationCardano b1 s1 a1 c1 d1 h1 k1) + (NodeProtocolConfigurationCardano b2 s2 a2 c2 d2 h2 k2) = + concat + [ cmpValues "Byron protocol config" b1 b2 + , cmpValues "Shelley protocol config" s1 s2 + , cmpValues "Alonzo protocol config" a1 a2 + , cmpValues "Conway protocol config" c1 c2 + , cmpValues "Dijkstra protocol config" d1 d2 + , cmpValues "HardFork protocol config" h1 h2 + , cmpValues "Checkpoints protocol config" k1 k2 + ] diff --git a/cardano-node/src/Cardano/Node/Configuration/CardanoConfigResolve.hs b/cardano-node/src/Cardano/Node/Configuration/CardanoConfigResolve.hs new file mode 100644 index 00000000000..0ffc2b3a536 --- /dev/null +++ b/cardano-node/src/Cardano/Node/Configuration/CardanoConfigResolve.hs @@ -0,0 +1,259 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ScopedTypeVariables #-} + +-- | Turning a node configuration file into the node's 'NodeConfiguration', +-- with whichever of the two parsers the file is written for. +-- +-- The node understands two configuration dialects while the migration to the +-- shared @cardano-config@ package is in progress: +-- +-- [Legacy] the flat, pre-@cardano-config@ configuration the node has always +-- read with its own POM parser. Both parsers can read it (@cardano-config@ +-- migrates it on the fly, see 'Cardano.Configuration.File.Migrate.migrate'), +-- so it is resolved with /both/ and the results are compared: the POM result +-- is what the node runs on, and every divergence is reported as a non-fatal +-- warning so the two can be reconciled before POM is dropped. +-- +-- [Envelope] the @cardano-config@ @{ $schema, Version, Configuration }@ +-- envelope. The POM parser cannot read it at all (every setting lives nested +-- under @Configuration@, so POM sees an empty document and fails on the first +-- required field), so it is not run: @cardano-config@ alone resolves the +-- configuration and the adapter maps it to the node's 'NodeConfiguration'. +-- +-- The dialect is decided by 'classifyConfigurationFile', on the file itself. +-- +-- Note that the legacy path pays for the second parse at every startup: +-- @cardano-config@ reads and decodes the era genesis files as part of resolving, +-- so the genesis files are parsed twice. That is the price of the cross-check and +-- it goes away with the POM parser. +module Cardano.Node.Configuration.CardanoConfigResolve + ( -- * Resolving + ResolvedNodeConfiguration (..) + , CrossCheck (..) + , buildNodeConfiguration + , NodeConfigurationError (..) + + -- * Dialects + , ConfigurationDialect (..) + , classifyConfigurationFile + ) where + +import Cardano.Logging.Types (TraceConfig) +import qualified Cardano.Configuration as Cfg +import qualified Cardano.Configuration.CliArgs as CliArgs +import Cardano.Node.Configuration.CardanoConfigAdapter + (cardanoConfigToNodeConfiguration) +import Cardano.Node.Configuration.CardanoConfigCompare + (compareConfigurations, deprecatedFlagWarnings) +import Cardano.Node.Configuration.POM (NodeConfiguration (..), + PartialNodeConfiguration (..), defaultPartialNodeConfiguration, + makeNodeConfiguration, parseNodeConfigurationFP) +import Cardano.Node.Types (ConfigYamlFilePath (..)) + +import Control.Exception (Exception (..)) +import qualified Control.Exception as Exception +import Data.Aeson (Value (..)) +import qualified Data.Aeson.KeyMap as KeyMap +import Data.List (isPrefixOf) +import Data.Monoid (Last (..)) +import qualified Data.Yaml as Yaml +import qualified Options.Applicative as Opt +import System.Environment (getArgs) + +-- | The configuration dialect a node configuration file is written in. +data ConfigurationDialect + = -- | A flat, pre-@cardano-config@ configuration: readable by both parsers. + LegacyDialect + | -- | A @cardano-config@ envelope: readable by @cardano-config@ only. + CardanoConfigDialect + deriving (Eq, Show) + +-- | Whether to cross-check a legacy configuration against @cardano-config@'s +-- parser. Worth it at startup; not worth re-reading every genesis file for on a +-- configuration reload, which only wants the new values. +data CrossCheck + = CrossCheckWithCardanoConfig + | SkipCrossCheck + deriving (Eq, Show) + +-- | The node's resolved configuration together with everything the caller needs +-- to know about how it was obtained. +data ResolvedNodeConfiguration = ResolvedNodeConfiguration + { rncConfiguration :: !NodeConfiguration + , rncTraceConfig :: !(Maybe TraceConfig) + -- ^ The tracing configuration, when it was resolved alongside the rest of the + -- configuration (envelope dialect: the tracing settings live nested under + -- @Configuration.HermodTracing@, where @trace-dispatcher@'s own file parser + -- would not find them). 'Nothing' for the legacy dialect, where + -- @trace-dispatcher@ reads the configuration file itself, as it always has. + , rncReport :: ![String] + -- ^ Non-fatal lines to trace at startup: the parser divergences found by the + -- dual parse, and any warning raised while resolving. + } + +-- | The configuration file could not be turned into a 'NodeConfiguration'. +newtype NodeConfigurationError = NodeConfigurationError String + +instance Show NodeConfigurationError where + show (NodeConfigurationError err) = "Error in creating the NodeConfiguration: " <> err + +instance Exception NodeConfigurationError + +-- | Read the node configuration from the file named by the given +-- 'PartialNodeConfiguration', using the parser its dialect calls for (see the +-- module header). +buildNodeConfiguration :: + -- | Whether to cross-check a legacy configuration against @cardano-config@. + CrossCheck -> + -- | The command-line configuration layer (also naming the configuration file). + PartialNodeConfiguration -> + IO ResolvedNodeConfiguration +buildNodeConfiguration crossCheck partialConf = do + classifyConfigurationFile configFp >>= \case + LegacyDialect -> buildFromLegacy crossCheck partialConf configFp + CardanoConfigDialect -> buildFromCardanoConfig configFp + where + -- An absent @--config@ falls back to the node's default path, exactly as + -- 'parseNodeConfigurationFP' does. + -- 'Last' is right-biased, so the command-line layer goes on the RIGHT of the + -- default to win. + configFp = + unConfigPath $ + case getLast (pncConfigFile defaultPartialNodeConfiguration <> pncConfigFile partialConf) of + Just fp -> fp + -- Unreachable: 'defaultPartialNodeConfiguration' always names a file. + Nothing -> ConfigYamlFilePath "configuration/cardano/mainnet-config.json" + +-- | Decide which dialect a configuration file is written in. +-- +-- The marker is the envelope's @Configuration@ key: that is exactly what +-- @cardano-config@ splits the document on +-- ('Cardano.Configuration.File.Merge.splitEnvelope'), and exactly what makes the +-- document unreadable to POM (which expects the settings at the top level). A +-- file that cannot even be decoded as YAML is reported as legacy so that the +-- POM path raises the syntax error, as it always did. +classifyConfigurationFile :: FilePath -> IO ConfigurationDialect +classifyConfigurationFile fp = + Yaml.decodeFileEither fp >>= \case + Right (Object o) | KeyMap.member "Configuration" o -> pure CardanoConfigDialect + _ -> pure LegacyDialect + +-- | Resolve a legacy configuration with the node's own POM parser — the result +-- the node runs on — and cross-check it against @cardano-config@'s. +buildFromLegacy :: + CrossCheck -> PartialNodeConfiguration -> FilePath -> IO ResolvedNodeConfiguration +buildFromLegacy crossCheck partialConf configFp = do + configYamlPc <- parseNodeConfigurationFP (Just (ConfigYamlFilePath configFp)) + nc <- + either (Exception.throwIO . NodeConfigurationError) pure $ + makeNodeConfiguration (defaultPartialNodeConfiguration <> configYamlPc <> partialConf) + report <- case crossCheck of + SkipCrossCheck -> pure [] + CrossCheckWithCardanoConfig -> crossCheckWithCardanoConfig configFp nc + pure + ResolvedNodeConfiguration + { rncConfiguration = nc + , rncTraceConfig = Nothing + , rncReport = report + } + +-- | Resolve an envelope configuration with @cardano-config@ alone and map it to +-- the node's 'NodeConfiguration'. Unlike the cross-check on the legacy path, +-- every failure here is fatal: there is no second parser to fall back to. +buildFromCardanoConfig :: FilePath -> IO ResolvedNodeConfiguration +buildFromCardanoConfig configFp = do + cliArgs <- + cardanoConfigCliArgs configFp + >>= either (Exception.throwIO . NodeConfigurationError) pure + (fileCfg, fileWarnings) <- Cfg.parseConfigurationFiles configFp + (cfgNc, checkWarnings) <- + either (Exception.throwIO . NodeConfigurationError . show) pure $ + Cfg.resolveConfiguration cliArgs fileCfg + nc <- + either (Exception.throwIO . NodeConfigurationError) pure $ + cardanoConfigToNodeConfiguration cfgNc + pure + ResolvedNodeConfiguration + { rncConfiguration = nc + , rncTraceConfig = Just (Cfg.tracingConfiguration cfgNc) + , rncReport = + "cardano-config: resolved the configuration (the node's own parser was not run: this" + <> " is a cardano-config envelope configuration, which it cannot read)" + : map (("cardano-config: " <>) . Cfg.renderConfigWarning) + (fileWarnings <> checkWarnings) + } + +-- | Resolve the same legacy configuration with @cardano-config@ and diff it +-- against the POM result, returning the lines to report. +-- +-- To keep the comparison fair, @cardano-config@ resolves from the SAME two +-- inputs the node used: it parses the node's own command line with its own CLI +-- parser and combines that with the configuration file, so both sides are +-- @file + CLI@. Every failure here is only ever reported, never fatal — the node +-- runs on the POM result either way. +crossCheckWithCardanoConfig :: FilePath -> NodeConfiguration -> IO [String] +crossCheckWithCardanoConfig configFp nc = do + (cliArgs, cliReport) <- + cardanoConfigCliArgs configFp >>= \case + Right cli -> pure (cli, []) + Left err -> + -- A parse failure means the operator used a node flag cardano-config + -- does not model (in practice, a deprecated one — 'deprecatedFlagWarnings' + -- names it). Fall back to a file-only resolution so the rest is still + -- checked, and say so. + pure + ( Cfg.defaultCliArgs configFp + , [ "cardano-config: could not parse the node command line; comparing the" + <> " configuration file only. " <> err + ] + ) + result <- Exception.try $ do + (fileCfg, fileWarnings) <- Cfg.parseConfigurationFiles configFp + resolved <- Exception.evaluate (Cfg.resolveConfiguration cliArgs fileCfg) + pure (resolved, fileWarnings) + pure . (cliReport <>) $ case result of + Left (e :: Exception.SomeException) -> + ["cardano-config: failed to parse the node configuration (ignored): " <> show e] + Right (Left err, _) -> + ["cardano-config: failed to resolve the node configuration (ignored): " <> show err] + Right (Right (cfgNc, checkWarnings), fileWarnings) -> + map (("cardano-config: " <>) . Cfg.renderConfigWarning) (fileWarnings <> checkWarnings) + <> case cardanoConfigToNodeConfiguration cfgNc of + Left adaptErr -> + ["cardano-config: could not adapt to the node configuration (ignored): " <> adaptErr] + Right adaptedNc -> + case compareConfigurations nc adaptedNc of + [] -> + [ "cardano-config: the resolved configuration (file + CLI) agrees with the" + <> " node's own parser." + ] + divergences -> + ( "cardano-config: WARNING - the resolved configuration (file + CLI) diverges" + <> " from the node's own parser:" + ) + : map (" - " <>) divergences + +-- | Re-parse the node's own command line with @cardano-config@'s CLI parser, so +-- both parsers resolve from the same @file + CLI@ inputs. +-- +-- The node is invoked as @cardano-node run \@ (the RTS has already +-- stripped its own arguments), so the leading non-flag subcommand token is +-- dropped to get the flag list; @cardano-config@'s 'Cfg.parseCliArgs' is a flat +-- parser with no @run@ subcommand, matching that stripped list. On failure the +-- returned message leads with actionable guidance for each offending flag. +cardanoConfigCliArgs :: FilePath -> IO (Either String Cfg.CliArgs) +cardanoConfigCliArgs configFp = do + argv <- getArgs + let flags = dropWhile (not . ("-" `isPrefixOf`)) argv + pure $ case Opt.execParserPure Opt.defaultPrefs (Opt.info Cfg.parseCliArgs mempty) flags of + Opt.Success cli -> Right (onConfigFile cli) + Opt.CompletionInvoked _ -> Right (Cfg.defaultCliArgs configFp) + Opt.Failure failure -> + let (msg, _exitCode) = Opt.renderFailure failure "cardano-node" + in Left (unlines (deprecatedFlagWarnings flags <> [msg])) + where + -- The file being resolved is already settled (it is what was classified); pin + -- it rather than take cardano-config's own @--config@ default, so a caller that + -- named the file some other way than on the command line still resolves the + -- genesis paths (which hang off the configuration file's directory) correctly. + onConfigFile cli = cli{CliArgs.configFilePath = configFp} diff --git a/cardano-node/src/Cardano/Node/Run.hs b/cardano-node/src/Cardano/Node/Run.hs index b75cd5f0b43..89477bdbb56 100644 --- a/cardano-node/src/Cardano/Node/Run.hs +++ b/cardano-node/src/Cardano/Node/Run.hs @@ -29,12 +29,14 @@ import qualified Cardano.Api as Api import System.Random (randomIO) import qualified Cardano.Crypto.Init as Crypto +import Cardano.Node.Configuration.CardanoConfigResolve + (CrossCheck (..), ResolvedNodeConfiguration (..), + buildNodeConfiguration) import Cardano.Node.Configuration.LedgerDB import Cardano.Node.Configuration.NodeAddress import Cardano.Node.Configuration.POM (NodeConfiguration (..), PartialNodeConfiguration (..), TimeoutOverride (..), - defaultPartialNodeConfiguration, makeNodeConfiguration, - parseNodeConfigurationFP, getForkPolicy) + getForkPolicy) import Cardano.Node.Configuration.Socket (LocalSocketOrSocketInfo, SocketOrSocketInfo, SocketOrSocketInfo' (..), gatherConfiguredSockets, getSocketOrSocketInfoAddr) @@ -62,7 +64,7 @@ import Cardano.Node.Tracing.Tracers.Startup (getStartupInfo) import Cardano.Node.Types import Cardano.Prelude (FatalError (..), bool, (:~:) (..)) import Cardano.Slotting.Slot (WithOrigin (..)) -import Cardano.Logging.Types (LogFormatting) +import Cardano.Logging.Types (LogFormatting, TraceConfig) import Cardano.Logging.Utils (showT) import Ouroboros.Consensus.Block.Forging (MkBlockForging) @@ -181,12 +183,20 @@ runNode cmdPc = do Crypto.cryptoInit - nc@NodeConfiguration - { ncProtocolConfig - , ncProtocolFiles=ncProtocolFiles@ProtocolFilepaths{shelleyVRFFile=mShelleyVrfFile} - } <- buildNodeConfiguration cmdPc - let earlyTracer = stdoutTracer + + ResolvedNodeConfiguration + { rncConfiguration = nc@NodeConfiguration + { ncProtocolConfig + , ncProtocolFiles=ncProtocolFiles@ProtocolFilepaths{shelleyVRFFile=mShelleyVrfFile} + } + , rncTraceConfig + , rncReport + } <- buildNodeConfiguration CrossCheckWithCardanoConfig cmdPc + + -- What the configuration parsers had to say: which one read the file, and, for + -- a legacy configuration, where the two of them disagree. All non-fatal. + mapM_ (traceWith earlyTracer) rncReport traceWith earlyTracer $ "Node configuration: " <> show nc forM_ mShelleyVrfFile $ @@ -200,29 +210,21 @@ runNode cmdPc = do -- don't need these. (Just ncProtocolFiles) - handleNodeWithTracers cmdPc nc consensusProtocol shelleyGenesisHash + handleNodeWithTracers cmdPc nc rncTraceConfig consensusProtocol shelleyGenesisHash runThrowExceptT :: Exception e => ExceptT e IO a -> IO a runThrowExceptT act = runExceptT act >>= either Exception.throwIO pure --- | Read node configuration from a file specified in 'PartialNodeConfiguration' -buildNodeConfiguration :: HasCallStack - => PartialNodeConfiguration -- ^ defaults - -> IO NodeConfiguration -buildNodeConfiguration partialConf = do - configYamlPc <- parseNodeConfigurationFP . getLast $ pncConfigFile partialConf - either - (\err -> error $ "Error in creating the NodeConfiguration: " <> err) - pure - $ makeNodeConfiguration (defaultPartialNodeConfiguration <> configYamlPc <> partialConf) - handleNodeWithTracers :: PartialNodeConfiguration -> NodeConfiguration + -> Maybe TraceConfig + -- ^ The tracing configuration, when the configuration parser resolved it + -- too; 'Nothing' leaves it to trace-dispatcher to read the file. -> SomeConsensusProtocol -> Api.GenesisHashShelley -> IO () -handleNodeWithTracers cmdPc nc (SomeConsensusProtocol blockType runP) shelleyGenesisHash = do +handleNodeWithTracers cmdPc nc mTrConfig (SomeConsensusProtocol blockType runP) shelleyGenesisHash = do (pInfo@ProtocolInfo{pInfoConfig}, mkBlockForging) <- Api.protocolInfo @IO runP let networkMagic :: Api.NetworkMagic = getNetworkMagic $ Consensus.configBlock pInfoConfig -- This IORef contains node kernel structure which holds node kernel. @@ -235,6 +237,7 @@ handleNodeWithTracers cmdPc nc (SomeConsensusProtocol blockType runP) shelleyGen tracers <- initTraceDispatcher nc + mTrConfig blockType pInfoConfig networkMagic @@ -796,7 +799,11 @@ updateRpcConfiguration :: Tracer IO (StartupTrace blk) -- ^ tracer for configura -> StrictTVar IO RpcConfig -- ^ TVar storing RPC configuration -> IO () updateRpcConfiguration tracer cmdPc rpcConfigVar = do - result <- try @Exception.SomeException $ buildNodeConfiguration cmdPc + -- A reload only wants the new values; the parser cross-check already ran (and + -- was reported) at startup. + result <- + try @Exception.SomeException $ + rncConfiguration <$> buildNodeConfiguration SkipCrossCheck cmdPc case result of Left err -> -- reload failure, we don't do anything this time diff --git a/cardano-node/src/Cardano/Node/Tracing/API.hs b/cardano-node/src/Cardano/Node/Tracing/API.hs index 7ba32e24ee3..3f6dcc4f21d 100644 --- a/cardano-node/src/Cardano/Node/Tracing/API.hs +++ b/cardano-node/src/Cardano/Node/Tracing/API.hs @@ -64,16 +64,24 @@ initTraceDispatcher :: , LogFormatting (TraceGsmEvent (Tip blk)) ) => NodeConfiguration + -> Maybe TraceConfig + -- ^ The tracing configuration, when it was already resolved alongside the + -- rest of the node configuration (see + -- 'Cardano.Node.Configuration.CardanoConfigResolve.rncTraceConfig'). + -- 'Nothing' means read it from the configuration file, as ever. -> BlockType blk -> TopLevelConfig blk -> NetworkMagic -> NodeKernelData blk -> Bool -> IO (Tracers RemoteAddress LocalAddress blk IO) -initTraceDispatcher nc blockType cfg networkMagic nodeKernel noBlockForging = do - trConfig <- readConfigurationWithDefault - (FromFile (unConfigPath $ ncConfigFile nc)) - defaultCardanoConfig +initTraceDispatcher nc mTrConfig blockType cfg networkMagic nodeKernel noBlockForging = do + trConfig <- case mTrConfig of + Just resolved -> pure resolved + Nothing -> + readConfigurationWithDefault + (FromFile (unConfigPath $ ncConfigFile nc)) + defaultCardanoConfig (kickoffForwarder, kickoffPrometheusSimple, tracers) <- mkTracers trConfig diff --git a/cardano-node/test/cardano-config-compare/Main.hs b/cardano-node/test/cardano-config-compare/Main.hs new file mode 100644 index 00000000000..1b34accd617 --- /dev/null +++ b/cardano-node/test/cardano-config-compare/Main.hs @@ -0,0 +1,257 @@ +{-# LANGUAGE ScopedTypeVariables #-} + +-- | Tests for the two configuration dialects the node understands +-- ('Cardano.Node.Configuration.CardanoConfigResolve'): +-- +-- * a legacy (pre-@cardano-config@) configuration is read by both parsers, and +-- the divergences between them are exactly the documented residual set; and +-- * a @cardano-config@ envelope configuration is read by @cardano-config@ only +-- — the node's own POM parser genuinely cannot resolve it, which is why the +-- dual parse is skipped for it — and resolving it yields the same +-- configuration as the legacy form it was migrated from. +-- +-- Plus 'deprecatedFlagWarnings', the operator-facing guidance emitted when +-- @cardano-config@'s CLI parser rejects the node's argv. +-- +-- The two fixture configurations are the same configuration in the two dialects: +-- @config-envelope.json@ is @config.json@ put through @cardano-node migrate@. +-- They sit in the same directory, so both resolve the same (relative) genesis +-- file paths and can be compared field by field. +module Main (main) where + +import Control.Exception (SomeException, evaluate, try) +import Control.Monad (filterM) +import Data.List (isInfixOf, isPrefixOf) +import Data.Monoid (Last (..)) + +import qualified Cardano.Configuration as Cfg +import Cardano.Node.Configuration.CardanoConfigAdapter + (cardanoConfigToNodeConfiguration) +import Cardano.Node.Configuration.CardanoConfigCompare + (compareConfigurations, deprecatedFlagWarnings) +import Cardano.Node.Configuration.CardanoConfigResolve + (ConfigurationDialect (..), classifyConfigurationFile) +import Cardano.Node.Configuration.POM (NodeConfiguration (..), + PartialNodeConfiguration (..), defaultPartialNodeConfiguration, + makeNodeConfiguration, parseNodeConfigurationFP) +import Cardano.Node.Types (ConfigYamlFilePath (..)) + +import System.Directory (doesFileExist) +import System.FilePath (()) + +import Test.Tasty +import Test.Tasty.HUnit + +-- | Locate the fixture configuration directory. Depending on the runner the +-- working directory is either the package directory or the repository root, so +-- try both rather than assume one. +fixtureDir :: IO FilePath +fixtureDir = do + found <- filterM (doesFileExist . ( "config.json")) candidates + case found of + dir : _ -> pure dir + [] -> + assertFailure $ + "could not find the fixture configuration directory; looked in " <> show candidates + where + candidates = + [ "test/cardano-config-compare/config" + , "cardano-node/test/cardano-config-compare/config" + ] + +-- | The fixture's legacy (flat, pre-cardano-config) configuration file. +legacyConfigPath :: IO FilePath +legacyConfigPath = ( "config.json") <$> fixtureDir + +-- | The same configuration in the cardano-config envelope (the output of +-- @cardano-node migrate@ on 'legacyConfigPath'). +envelopeConfigPath :: IO FilePath +envelopeConfigPath = ( "config-envelope.json") <$> fixtureDir + +-- | Labels ('compareConfigurations' prefixes each divergence with one) that are +-- documented, expected divergences on this fixture. They fall into three kinds, +-- and none is an adapter defect — the adapter faithfully reflects what +-- cardano-config resolved; the check exists precisely to surface these: +-- +-- (1) adapter gaps — a field the adapter cannot populate from cardano-config +-- (see 'Cardano.Node.Configuration.CardanoConfigAdapter.adapterGaps'); +-- (2) representation differences — same meaning, different shape; +-- (3) parser default mismatches — a field the fixture does not set (or sets +-- under a key one parser ignores), for which the two parsers fall back to +-- different defaults. +-- +-- The fixture comparison must not diverge on anything OUTSIDE this set, so a +-- regression that changes a currently-agreeing field (or an adapter change that +-- breaks a mapped one) is still caught. +allowedResidualLabels :: [String] +allowedResidualLabels = + [ -- (1) adapter gap: Byron supported-protocol-version is hard-coded 1/0/0 by + -- the adapter, whereas the fixture sets LastKnownBlockVersion-Major = 3. + "Byron protocol config" + -- (1) adapter gap: the CheckpointsFile/CheckpointsFileHash keys have no + -- cardano-config counterpart. The fixture sets neither (so this label does + -- not appear on it), but a configuration that does — mainnet's, for one — + -- diverges here. + , "Checkpoints protocol config" + -- (2) representation: "MempoolCapacityBytesOverride: NoOverride" is an + -- explicit no-override value for POM but simply absent for cardano-config. + , "MaybeMempoolCapacityOverride" + -- (3) default mismatch: the fixture leaves these unset; POM defaults to + -- Nothing (no limit) while cardano-config supplies its own default. + , "MaxConcurrencyBulkSync" + , "MaxConcurrencyDeadline" + -- (3) default mismatch: cardano-config ships its own storage defaults where + -- the node uses its bare LedgerDB defaults. + , "LedgerDbConfig" + -- (3) key/default mismatch: the fixture sets TargetNumberOf{Root,Known}Peers + -- (= 100), which POM reads as the deadline targets, but cardano-config falls + -- back to its own deadline defaults. + , "DeadlineTargetOfRootPeers" + , "DeadlineTargetOfKnownPeers" + -- (3) default mismatch: the fixture leaves this unset; the two parsers + -- default it differently. + , "TxSubmissionInitDelay" + ] + +main :: IO () +main = defaultMain tests + +tests :: TestTree +tests = testGroup "cardano-config configuration dialects" + [ testGroup "deprecated CLI flag guidance" + [ testCase "deprecated CLI aliases yield migration guidance" testDeprecatedAliases + , testCase "removed mempool flags yield removal guidance" testRemovedMempoolFlags + , testCase "no guidance for accepted / unrelated flags" testNoFalsePositives + ] + , testGroup "legacy dialect (both parsers)" + [ testCase "the fixture is classified as legacy" testLegacyClassification + , testCase "the two parsers diverge only on the documented residuals" + testLegacyDualParse + ] + , testGroup "cardano-config envelope dialect (cardano-config only)" + [ testCase "the migrated fixture is classified as an envelope" + testEnvelopeClassification + , testCase "the node's own parser cannot resolve an envelope" testEnvelopeDefeatsPom + , testCase "cardano-config resolves an envelope to the same configuration" + testEnvelopeResolvesToSameConfiguration + ] + ] + +testDeprecatedAliases :: Assertion +testDeprecatedAliases = do + let warnings = + deprecatedFlagWarnings + ["--delegation-certificate", "x", "--signing-key", "y", "--non-producing-node"] + suggests new = any (new `isInfixOf`) warnings + assertBool "suggests --byron-delegation-certificate" (suggests "--byron-delegation-certificate") + assertBool "suggests --byron-signing-key" (suggests "--byron-signing-key") + assertBool "suggests --start-as-non-producing-node" (suggests "--start-as-non-producing-node") + length warnings @?= 3 + +testRemovedMempoolFlags :: Assertion +testRemovedMempoolFlags = do + let warnings = deprecatedFlagWarnings ["--mempool-capacity-override", "100"] + length warnings @?= 1 + assertBool "says no longer supported" + (any ("no longer supported" `isInfixOf`) warnings) + assertBool "points to MempoolCapacityBytesOverride in the config file" + (any ("MempoolCapacityBytesOverride" `isInfixOf`) warnings) + +testNoFalsePositives :: Assertion +testNoFalsePositives = + deprecatedFlagWarnings ["--config", "c.json", "--topology", "t.json", "--database-path", "db"] + @?= [] + +testLegacyClassification :: Assertion +testLegacyClassification = + legacyConfigPath >>= classifyConfigurationFile >>= (@?= LegacyDialect) + +-- | Resolve the fixture both ways and check that the divergences stay inside the +-- documented residual set. +testLegacyDualParse :: Assertion +testLegacyDualParse = do + configPath <- legacyConfigPath + adapted <- resolveWithCardanoConfig configPath + pomNc <- resolveWithPom configPath adapted + + let divergences = compareConfigurations pomNc adapted + isAllowed d = any (`isPrefixOf` d) allowedResidualLabels + unexpected = filter (not . isAllowed) divergences + + -- Print what the comparison reports on this real config, so the run is legible + -- even when it passes. + putStrLn $ " compareConfigurations reported " <> show (length divergences) + <> " divergence(s) on the fixture:" + mapM_ (putStrLn . (" - " <>)) divergences + + assertBool + ("divergences outside the documented residual set: " <> show unexpected) + (null unexpected) + +testEnvelopeClassification :: Assertion +testEnvelopeClassification = + envelopeConfigPath >>= classifyConfigurationFile >>= (@?= CardanoConfigDialect) + +-- | The reason the node does not run the dual parse on an envelope: POM cannot +-- read it. Every setting lives nested under @Configuration@, so POM sees a +-- document with none of the settings it requires. It may fail either while +-- decoding the file (its decoder throws on a missing required key) or in +-- 'makeNodeConfiguration'; only that it fails matters here. +testEnvelopeDefeatsPom :: Assertion +testEnvelopeDefeatsPom = do + envelope <- envelopeConfigPath + outcome <- try $ do + filePartial <- parseNodeConfigurationFP (Just (ConfigYamlFilePath envelope)) + evaluate (makeNodeConfiguration (defaultPartialNodeConfiguration <> filePartial)) + case outcome of + Left (_ :: SomeException) -> pure () + Right (Left _) -> pure () + Right (Right _) -> + assertFailure + "the node's own parser resolved an envelope configuration; the dual-parse\ + \ dispatch in CardanoConfigResolve assumes it cannot" + +-- | The envelope is a reshaping, not a change of meaning: resolving it must give +-- exactly what resolving the legacy form it was migrated from gives. +testEnvelopeResolvesToSameConfiguration :: Assertion +testEnvelopeResolvesToSameConfiguration = do + fromLegacy <- resolveWithCardanoConfig =<< legacyConfigPath + fromEnvelope <- resolveWithCardanoConfig =<< envelopeConfigPath + let divergences = compareConfigurations fromLegacy fromEnvelope + assertBool + ("the envelope resolved differently from the legacy form it was migrated from: " + <> show divergences) + (null divergences) + +-- | Resolve a configuration file with cardano-config (file only, no CLI layer) +-- and adapt it to the node's own 'NodeConfiguration'. +resolveWithCardanoConfig :: FilePath -> IO NodeConfiguration +resolveWithCardanoConfig fp = do + resolved <- Cfg.resolveConfigurationFromFile fp + (cfgNc, _warns) <- + either (assertFailure . ("cardano-config resolve failed: " <>) . show) pure resolved + either (assertFailure . ("adapter failed: " <>)) pure + (cardanoConfigToNodeConfiguration cfgNc) + +-- | Resolve a configuration file with the node's own POM parser. +-- +-- The CLI-only fields (topology / database / protocol files / socket) are not in +-- the file; mirror them from the given cardano-config result so the comparison +-- isolates the file-parse and adapter-gap differences rather than CLI-supplied +-- noise. +resolveWithPom :: FilePath -> NodeConfiguration -> IO NodeConfiguration +resolveWithPom fp adapted = do + fileYaml <- parseNodeConfigurationFP (Just (ConfigYamlFilePath fp)) + -- 'PartialNodeConfiguration' is a Semigroup but not a Monoid, so there is no + -- empty value to start from: merge defaults with the file layer (mirroring + -- 'buildNodeConfiguration'), then override the CLI-only fields on top. + let withCli = + (defaultPartialNodeConfiguration <> fileYaml) + { pncConfigFile = Last (Just (ConfigYamlFilePath fp)) + , pncTopologyFile = Last (Just (ncTopologyFile adapted)) + , pncDatabaseFile = Last (Just (ncDatabaseFile adapted)) + , pncProtocolFiles = Last (Just (ncProtocolFiles adapted)) + , pncSocketConfig = Last (Just (ncSocketConfig adapted)) + } + either (assertFailure . ("POM makeNodeConfiguration failed: " <>)) pure + (makeNodeConfiguration withCli) diff --git a/cardano-node/test/cardano-config-compare/config/alonzo-genesis.json b/cardano-node/test/cardano-config-compare/config/alonzo-genesis.json new file mode 100644 index 00000000000..093071bb398 --- /dev/null +++ b/cardano-node/test/cardano-config-compare/config/alonzo-genesis.json @@ -0,0 +1,194 @@ +{ + "lovelacePerUTxOWord": 34482, + "executionPrices": { + "prSteps": { + "numerator": 721, + "denominator": 10000000 + }, + "prMem": { + "numerator": 577, + "denominator": 10000 + } + }, + "maxTxExUnits": { + "exUnitsMem": 14000000, + "exUnitsSteps": 10000000000 + }, + "maxBlockExUnits": { + "exUnitsMem": 56000000, + "exUnitsSteps": 40000000000 + }, + "maxValueSize": 5000, + "collateralPercentage": 150, + "maxCollateralInputs": 3, + "costModels": { + "PlutusV1": { + "sha2_256-memory-arguments": 4, + "equalsString-cpu-arguments-constant": 1000, + "cekDelayCost-exBudgetMemory": 100, + "lessThanEqualsByteString-cpu-arguments-intercept": 103599, + "divideInteger-memory-arguments-minimum": 1, + "appendByteString-cpu-arguments-slope": 621, + "blake2b-cpu-arguments-slope": 29175, + "iData-cpu-arguments": 150000, + "encodeUtf8-cpu-arguments-slope": 1000, + "unBData-cpu-arguments": 150000, + "multiplyInteger-cpu-arguments-intercept": 61516, + "cekConstCost-exBudgetMemory": 100, + "nullList-cpu-arguments": 150000, + "equalsString-cpu-arguments-intercept": 150000, + "trace-cpu-arguments": 150000, + "mkNilData-memory-arguments": 32, + "lengthOfByteString-cpu-arguments": 150000, + "cekBuiltinCost-exBudgetCPU": 29773, + "bData-cpu-arguments": 150000, + "subtractInteger-cpu-arguments-slope": 0, + "unIData-cpu-arguments": 150000, + "consByteString-memory-arguments-intercept": 0, + "divideInteger-memory-arguments-slope": 1, + "divideInteger-cpu-arguments-model-arguments-slope": 118, + "listData-cpu-arguments": 150000, + "headList-cpu-arguments": 150000, + "chooseData-memory-arguments": 32, + "equalsInteger-cpu-arguments-intercept": 136542, + "sha3_256-cpu-arguments-slope": 82363, + "sliceByteString-cpu-arguments-slope": 5000, + "unMapData-cpu-arguments": 150000, + "lessThanInteger-cpu-arguments-intercept": 179690, + "mkCons-cpu-arguments": 150000, + "appendString-memory-arguments-intercept": 0, + "modInteger-cpu-arguments-model-arguments-slope": 118, + "ifThenElse-cpu-arguments": 1, + "mkNilPairData-cpu-arguments": 150000, + "lessThanEqualsInteger-cpu-arguments-intercept": 145276, + "addInteger-memory-arguments-slope": 1, + "chooseList-memory-arguments": 32, + "constrData-memory-arguments": 32, + "decodeUtf8-cpu-arguments-intercept": 150000, + "equalsData-memory-arguments": 1, + "subtractInteger-memory-arguments-slope": 1, + "appendByteString-memory-arguments-intercept": 0, + "lengthOfByteString-memory-arguments": 4, + "headList-memory-arguments": 32, + "listData-memory-arguments": 32, + "consByteString-cpu-arguments-intercept": 150000, + "unIData-memory-arguments": 32, + "remainderInteger-memory-arguments-minimum": 1, + "bData-memory-arguments": 32, + "lessThanByteString-cpu-arguments-slope": 248, + "encodeUtf8-memory-arguments-intercept": 0, + "cekStartupCost-exBudgetCPU": 100, + "multiplyInteger-memory-arguments-intercept": 0, + "unListData-memory-arguments": 32, + "remainderInteger-cpu-arguments-model-arguments-slope": 118, + "cekVarCost-exBudgetCPU": 29773, + "remainderInteger-memory-arguments-slope": 1, + "cekForceCost-exBudgetCPU": 29773, + "sha2_256-cpu-arguments-slope": 29175, + "equalsInteger-memory-arguments": 1, + "indexByteString-memory-arguments": 1, + "addInteger-memory-arguments-intercept": 1, + "chooseUnit-cpu-arguments": 150000, + "sndPair-cpu-arguments": 150000, + "cekLamCost-exBudgetCPU": 29773, + "fstPair-cpu-arguments": 150000, + "quotientInteger-memory-arguments-minimum": 1, + "decodeUtf8-cpu-arguments-slope": 1000, + "lessThanInteger-memory-arguments": 1, + "lessThanEqualsInteger-cpu-arguments-slope": 1366, + "fstPair-memory-arguments": 32, + "modInteger-memory-arguments-intercept": 0, + "unConstrData-cpu-arguments": 150000, + "lessThanEqualsInteger-memory-arguments": 1, + "chooseUnit-memory-arguments": 32, + "sndPair-memory-arguments": 32, + "addInteger-cpu-arguments-intercept": 197209, + "decodeUtf8-memory-arguments-slope": 8, + "equalsData-cpu-arguments-intercept": 150000, + "mapData-cpu-arguments": 150000, + "mkPairData-cpu-arguments": 150000, + "quotientInteger-cpu-arguments-constant": 148000, + "consByteString-memory-arguments-slope": 1, + "cekVarCost-exBudgetMemory": 100, + "indexByteString-cpu-arguments": 150000, + "unListData-cpu-arguments": 150000, + "equalsInteger-cpu-arguments-slope": 1326, + "cekStartupCost-exBudgetMemory": 100, + "subtractInteger-cpu-arguments-intercept": 197209, + "divideInteger-cpu-arguments-model-arguments-intercept": 425507, + "divideInteger-memory-arguments-intercept": 0, + "cekForceCost-exBudgetMemory": 100, + "blake2b-cpu-arguments-intercept": 2477736, + "remainderInteger-cpu-arguments-constant": 148000, + "tailList-cpu-arguments": 150000, + "encodeUtf8-cpu-arguments-intercept": 150000, + "equalsString-cpu-arguments-slope": 1000, + "lessThanByteString-memory-arguments": 1, + "multiplyInteger-cpu-arguments-slope": 11218, + "appendByteString-cpu-arguments-intercept": 396231, + "lessThanEqualsByteString-cpu-arguments-slope": 248, + "modInteger-memory-arguments-slope": 1, + "addInteger-cpu-arguments-slope": 0, + "equalsData-cpu-arguments-slope": 10000, + "decodeUtf8-memory-arguments-intercept": 0, + "chooseList-cpu-arguments": 150000, + "constrData-cpu-arguments": 150000, + "equalsByteString-memory-arguments": 1, + "cekApplyCost-exBudgetCPU": 29773, + "quotientInteger-memory-arguments-slope": 1, + "verifySignature-cpu-arguments-intercept": 3345831, + "unMapData-memory-arguments": 32, + "mkCons-memory-arguments": 32, + "sliceByteString-memory-arguments-slope": 1, + "sha3_256-memory-arguments": 4, + "ifThenElse-memory-arguments": 1, + "mkNilPairData-memory-arguments": 32, + "equalsByteString-cpu-arguments-slope": 247, + "appendString-cpu-arguments-intercept": 150000, + "quotientInteger-cpu-arguments-model-arguments-slope": 118, + "cekApplyCost-exBudgetMemory": 100, + "equalsString-memory-arguments": 1, + "multiplyInteger-memory-arguments-slope": 1, + "cekBuiltinCost-exBudgetMemory": 100, + "remainderInteger-memory-arguments-intercept": 0, + "sha2_256-cpu-arguments-intercept": 2477736, + "remainderInteger-cpu-arguments-model-arguments-intercept": 425507, + "lessThanEqualsByteString-memory-arguments": 1, + "tailList-memory-arguments": 32, + "mkNilData-cpu-arguments": 150000, + "chooseData-cpu-arguments": 150000, + "unBData-memory-arguments": 32, + "blake2b-memory-arguments": 4, + "iData-memory-arguments": 32, + "nullList-memory-arguments": 32, + "cekDelayCost-exBudgetCPU": 29773, + "subtractInteger-memory-arguments-intercept": 1, + "lessThanByteString-cpu-arguments-intercept": 103599, + "consByteString-cpu-arguments-slope": 1000, + "appendByteString-memory-arguments-slope": 1, + "trace-memory-arguments": 32, + "divideInteger-cpu-arguments-constant": 148000, + "cekConstCost-exBudgetCPU": 29773, + "encodeUtf8-memory-arguments-slope": 8, + "quotientInteger-cpu-arguments-model-arguments-intercept": 425507, + "mapData-memory-arguments": 32, + "appendString-cpu-arguments-slope": 1000, + "modInteger-cpu-arguments-constant": 148000, + "verifySignature-cpu-arguments-slope": 1, + "unConstrData-memory-arguments": 32, + "quotientInteger-memory-arguments-intercept": 0, + "equalsByteString-cpu-arguments-constant": 150000, + "sliceByteString-memory-arguments-intercept": 0, + "mkPairData-memory-arguments": 32, + "equalsByteString-cpu-arguments-intercept": 112536, + "appendString-memory-arguments-slope": 1, + "lessThanInteger-cpu-arguments-slope": 497, + "modInteger-cpu-arguments-model-arguments-intercept": 425507, + "modInteger-memory-arguments-minimum": 1, + "sha3_256-cpu-arguments-intercept": 0, + "verifySignature-memory-arguments": 1, + "cekLamCost-exBudgetMemory": 100, + "sliceByteString-cpu-arguments-intercept": 150000 + } + } +} diff --git a/cardano-node/test/cardano-config-compare/config/byron-genesis.json b/cardano-node/test/cardano-config-compare/config/byron-genesis.json new file mode 100644 index 00000000000..aec652492ff --- /dev/null +++ b/cardano-node/test/cardano-config-compare/config/byron-genesis.json @@ -0,0 +1,42 @@ +{ "bootStakeholders": + { "ce2950ee9b35c74336371a7393b2f1fa64d4af1831180076b106208d": 1 } +, "heavyDelegation": + { "ce2950ee9b35c74336371a7393b2f1fa64d4af1831180076b106208d": + { "omega": 0 + , "issuerPk": + "0Te/2OpdrE4IFuj6ZCSky8a/oeM9xE0phU0rQJE7v3Zg0wZop+bcZaKVe8qRk1zl0DM5vGIk5+XHZGhv7xh3rQ==" + , "delegatePk": + "ojky67+tV35+CmAFX7hkCCpPgz7EwrU8HCMDk7qEgoynuLaByN8S4ek4HjQXPq/b3vZFd08+Ip2BN/nC+e6Alg==" + , "cert": + "a8e1514662b6edd544f9e22d3bc8a961e6cfe5b1db35378188bb4fcd848e27c977952c8e73c29757b8b5b6b6c0b52254357383272c0b83e24cb91558907e8d04" + } } +, "startTime": 1655366659 +, "nonAvvmBalances": + { "2657WMsDfac5TVJguqJE11Z1tdx9HP72E9Roz32GVecUrX7oScFb1sXPzC43EnLUx": + "30000" + , "2657WMsDfac5V9qqEUfJm252BN5L81ni6CZyDS31cN7XZrAtsyqbz4yGr42bKG5B7": + "270000" + } +, "blockVersionData": + { "scriptVersion": 0 + , "slotDuration": "20000" + , "maxBlockSize": "641000" + , "maxHeaderSize": "200000" + , "maxTxSize": "4096" + , "maxProposalSize": "700" + , "mpcThd": "200000" + , "heavyDelThd": "300000" + , "updateVoteThd": "100000" + , "updateProposalThd": "100000" + , "updateImplicit": "10000" + , "softforkRule": + { "initThd": "900000" + , "minThd": "600000" + , "thdDecrement": "100000" + } + , "txFeePolicy": { "summand": "0" , "multiplier": "439460" } + , "unlockStakeEpoch": "184467" + } +, "protocolConsts": { "k": 2160 , "protocolMagic": 42 } +, "avvmDistr": {} +} \ No newline at end of file diff --git a/cardano-node/test/cardano-config-compare/config/config-envelope.json b/cardano-node/test/cardano-config-compare/config/config-envelope.json new file mode 100644 index 00000000000..bd815e7be71 --- /dev/null +++ b/cardano-node/test/cardano-config-compare/config/config-envelope.json @@ -0,0 +1,127 @@ +{ + "$schema": "https://raw.githubusercontent.com/IntersectMBO/cardano-config/main/schemas/config.schema.json", + "Configuration": { + "ApplicationName": "cardano-sl", + "ApplicationVersion": 0, + "EnableP2P": false, + "HermodTracing": { + "TraceOptions": { + "": { + "backends": [ + "Stdout MachineFormat", + "EKGBackend", + "Forwarder" + ], + "severity": "Notice" + }, + "AcceptPolicy": { + "severity": "Info" + }, + "BlockFetchClient": { + "detail": "DMinimal", + "severity": "Info" + }, + "BlockFetchClient.CompletedBlockFetch": { + "maxFrequency": 2 + }, + "BlockFetchServer": { + "severity": "Info" + }, + "ChainDB": { + "severity": "Info" + }, + "ChainDB.AddBlockEvent.AddBlockValidation.ValidCandidate": { + "maxFrequency": 2 + }, + "ChainDB.AddBlockEvent.AddedBlockToQueue": { + "maxFrequency": 2 + }, + "ChainDB.AddBlockEvent.AddedBlockToVolatileDB": { + "maxFrequency": 2 + }, + "ChainDB.CopyToImmutableDBEvent.CopiedBlockToImmutableDB": { + "maxFrequency": 2 + }, + "ChainSyncClient": { + "detail": "DMinimal", + "severity": "Info" + }, + "ChainSyncServerBlock": { + "severity": "Info" + }, + "ChainSyncServerHeader": { + "severity": "Info" + }, + "DNSResolver": { + "severity": "Info" + }, + "DNSSubscription": { + "severity": "Info" + }, + "DiffusionInit": { + "severity": "Info" + }, + "ErrorPolicy": { + "severity": "Info" + }, + "Forge": { + "severity": "Info" + }, + "IpSubscription": { + "severity": "Info" + }, + "LocalErrorPolicy": { + "severity": "Info" + }, + "Mempool": { + "severity": "Info" + }, + "Resources": { + "severity": "Info" + }, + "TxSubmission2": { + "detail": "DMinimal" + } + } + }, + "MempoolConfig": { + "MempoolCapacityBytesOverride": "NoOverride" + }, + "NetworkConfig": { + "AcceptedConnectionsLimit": { + "Delay": 5, + "HardLimit": 512, + "SoftLimit": 384 + }, + "DeadlineTargetNumberOfActivePeers": 20, + "DeadlineTargetNumberOfEstablishedPeers": 50, + "DeadlineTargetNumberOfKnownPeers": 100, + "DeadlineTargetNumberOfRootPeers": 100, + "ExperimentalProtocolsEnabled": true, + "ProtocolIdleTimeout": 5, + "TimeWaitTimeout": 60 + }, + "ProtocolConfig": { + "AlonzoGenesisFile": "alonzo-genesis.json", + "AlonzoGenesisHash": "edb92321b614cfd7dcee3f49eedcde4f559ea9526194ff3cf42cd28a4bf0ad7b", + "ByronGenesisFile": "byron-genesis.json", + "ByronGenesisHash": "6836b5d0ae3bb7250c318e8906ab2bf8e42e6acfd4483526be948752707ad435", + "ConwayGenesisFile": "conway-genesis.json", + "ConwayGenesisHash": "e4cbda3b0a0db8ea984330d0951d280ca490540c97cc6407ecd1011b174d983d", + "RequiresNetworkMagic": "RequiresMagic", + "ShelleyGenesisFile": "shelley-genesis.json", + "ShelleyGenesisHash": "f6bb6e9d9b217681180754232470aa936716d62f8e11e944520b97490b100b7c" + }, + "TestingConfig": { + "DijkstraGenesisFile": "dijkstra-genesis.json", + "DijkstraGenesisHash": "56c06ff0f668c584fc54fa3cee92dd5e121b67696924ac3b01b5aec9ecf95b78", + "ExperimentalHardForksEnabled": true, + "TestAllegraHardForkAtEpoch": 0, + "TestAlonzoHardForkAtEpoch": 0, + "TestBabbageHardForkAtEpoch": 0, + "TestMaryHardForkAtEpoch": 0, + "TestShelleyHardForkAtEpoch": 0 + } + }, + "Version": 1 +} diff --git a/cardano-node/test/cardano-config-compare/config/config.json b/cardano-node/test/cardano-config-compare/config/config.json new file mode 100644 index 00000000000..70836962f78 --- /dev/null +++ b/cardano-node/test/cardano-config-compare/config/config.json @@ -0,0 +1,121 @@ +{ + "AcceptedConnectionsLimit": { + "delay": 5, + "hardLimit": 512, + "softLimit": 384 + }, + "AlonzoGenesisFile": "alonzo-genesis.json", + "AlonzoGenesisHash": "edb92321b614cfd7dcee3f49eedcde4f559ea9526194ff3cf42cd28a4bf0ad7b", + "ApplicationName": "cardano-sl", + "ApplicationVersion": 0, + "ByronGenesisFile": "byron-genesis.json", + "ByronGenesisHash": "6836b5d0ae3bb7250c318e8906ab2bf8e42e6acfd4483526be948752707ad435", + "ConwayGenesisFile": "conway-genesis.json", + "ConwayGenesisHash": "e4cbda3b0a0db8ea984330d0951d280ca490540c97cc6407ecd1011b174d983d", + "DijkstraGenesisFile": "dijkstra-genesis.json", + "DijkstraGenesisHash": "56c06ff0f668c584fc54fa3cee92dd5e121b67696924ac3b01b5aec9ecf95b78", + "EnableP2P": false, + "LastKnownBlockVersion-Alt": 0, + "LastKnownBlockVersion-Major": 3, + "LastKnownBlockVersion-Minor": 0, + "MaxKnownMajorProtocolVersion": 2, + "MempoolCapacityBytesOverride": "NoOverride", + "Protocol": "Cardano", + "ProtocolIdleTimeout": 5, + "RequiresNetworkMagic": "RequiresMagic", + "ShelleyGenesisFile": "shelley-genesis.json", + "ShelleyGenesisHash": "f6bb6e9d9b217681180754232470aa936716d62f8e11e944520b97490b100b7c", + "TargetNumberOfActivePeers": 20, + "TargetNumberOfEstablishedPeers": 50, + "TargetNumberOfKnownPeers": 100, + "TargetNumberOfRootPeers": 100, + "TestAllegraHardForkAtEpoch": 0, + "TestAlonzoHardForkAtEpoch": 0, + "TestBabbageHardForkAtEpoch": 0, + "ExperimentalHardForksEnabled": true, + "ExperimentalProtocolsEnabled": true, + "TestMaryHardForkAtEpoch": 0, + "TestShelleyHardForkAtEpoch": 0, + "TimeWaitTimeout": 60, + "TraceOptions": { + "": { + "backends": [ + "Stdout MachineFormat", + "EKGBackend", + "Forwarder" + ], + "severity": "Notice" + }, + "AcceptPolicy": { + "severity": "Info" + }, + "BlockFetchClient": { + "detail": "DMinimal", + "severity": "Info" + }, + "BlockFetchClient.CompletedBlockFetch": { + "maxFrequency": 2 + }, + "BlockFetchServer": { + "severity": "Info" + }, + "ChainDB": { + "severity": "Info" + }, + "ChainDB.AddBlockEvent.AddBlockValidation.ValidCandidate": { + "maxFrequency": 2 + }, + "ChainDB.AddBlockEvent.AddedBlockToQueue": { + "maxFrequency": 2 + }, + "ChainDB.AddBlockEvent.AddedBlockToVolatileDB": { + "maxFrequency": 2 + }, + "ChainDB.CopyToImmutableDBEvent.CopiedBlockToImmutableDB": { + "maxFrequency": 2 + }, + "ChainSyncClient": { + "detail": "DMinimal", + "severity": "Info" + }, + "ChainSyncServerBlock": { + "severity": "Info" + }, + "ChainSyncServerHeader": { + "severity": "Info" + }, + "DNSResolver": { + "severity": "Info" + }, + "DNSSubscription": { + "severity": "Info" + }, + "DiffusionInit": { + "severity": "Info" + }, + "ErrorPolicy": { + "severity": "Info" + }, + "Forge": { + "severity": "Info" + }, + "IpSubscription": { + "severity": "Info" + }, + "LocalErrorPolicy": { + "severity": "Info" + }, + "Mempool": { + "severity": "Info" + }, + "Resources": { + "severity": "Info" + }, + "TxSubmission2": { + "detail": "DMinimal" + } + }, + "TurnOnLogMetrics": true, + "TurnOnLogging": true, + "UseTraceDispatcher": true +} diff --git a/cardano-node/test/cardano-config-compare/config/conway-genesis.json b/cardano-node/test/cardano-config-compare/config/conway-genesis.json new file mode 100644 index 00000000000..08e1aed42a3 --- /dev/null +++ b/cardano-node/test/cardano-config-compare/config/conway-genesis.json @@ -0,0 +1,77 @@ +{ + "poolVotingThresholds": { + "committeeNormal": 0, + "committeeNoConfidence": 0, + "hardForkInitiation": 0, + "motionNoConfidence": 0, + "ppSecurityGroup": 0 + }, + "dRepVotingThresholds": { + "motionNoConfidence": 0, + "committeeNormal": 0, + "committeeNoConfidence": 0, + "updateToConstitution": 0, + "hardForkInitiation": 0, + "ppNetworkGroup": 0, + "ppEconomicGroup": 0, + "ppTechnicalGroup": 0, + "ppGovGroup": 0, + "treasuryWithdrawal": 0 + }, + "committeeMinSize": 0, + "committeeMaxTermLength": 0, + "govActionLifetime": 0, + "govActionDeposit": 0, + "dRepDeposit": 0, + "dRepActivity": 0, + "minFeeRefScriptCostPerByte": 0, + "plutusV3CostModel": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + "constitution": { + "anchor": { + "url": "", + "dataHash": "0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "committee": { + "members": { + "keyHash-4e88cc2d27c364aaf90648a87dfb95f8ee103ba67fa1f12f5e86c42a": 1, + "scriptHash-4e88cc2d27c364aaf90648a87dfb95f8ee103ba67fa1f12f5e86c42a": 2 + }, + "threshold": 0.5 + }, + "delegs": { + "keyHash-4e88cc2d27c364aaf90648a87dfb95f8ee103ba67fa1f12f5e86c42a": { + "dRep": "drep-alwaysAbstain" + }, + "keyHash-35bc5e86c42afbc593ab4cdd78301005df84ba67fa1f12f95f8ee103": { + "dRep": "drep-alwaysNoConfidence" + }, + "scriptHash-afbc5005df84ba5f8ee93ab435bc5e83067fa1f12f9c42cdd7110386": { + "dRep": "drep-keyHash-78301005df84ba67fa1f12f95f8ee10335bc5e86c42afbc593ab4cdd" + }, + "keyHash-df93ab435bc5eafbc500583067fa1f12f9110386c42cdd784ba5f8ee": { + "dRep": "drep-scriptHash-01305df84b078ac5e86c42afbc593ab4cdd67fa1f12f95f8ee10335b" + }, + "keyHash-5df84bcdd7a5f8ee93aafbc500b435bc5e83067fa1f12f9110386c42": { + "poolId": "0335bc5e86c42afbc578301005df84ba67fa1f12f95f8ee193ab4cdd" + }, + "keyHash-8ee93a5df84bc42cdd7a5fafbc500b435bc5e83067fa1f12f9110386": { + "poolId": "086c42afbc578301005df84ba67fa1f12f95f8ee193ab4cdd335bc5e", + "dRep": "drep-alwaysAbstain" + } + }, + "initialDReps": { + "keyHash-78301005df84ba67fa1f12f95f8ee10335bc5e86c42afbc593ab4cdd": { + "expiry": 1000, + "deposit": 5000 + }, + "scriptHash-01305df84b078ac5e86c42afbc593ab4cdd67fa1f12f95f8ee10335b": { + "expiry": 300, + "deposit": 6000, + "anchor": { + "url": "example.com", + "dataHash": "0000000000000000000000000000000000000000000000000000000000000000" + } + } + } +} diff --git a/cardano-node/test/cardano-config-compare/config/dijkstra-genesis.json b/cardano-node/test/cardano-config-compare/config/dijkstra-genesis.json new file mode 100644 index 00000000000..c33c6755721 --- /dev/null +++ b/cardano-node/test/cardano-config-compare/config/dijkstra-genesis.json @@ -0,0 +1,6 @@ +{ + "maxRefScriptSizePerBlock": 1048576, + "maxRefScriptSizePerTx": 204800, + "refScriptCostStride": 25600, + "refScriptCostMultiplier": 1.2 +} diff --git a/cardano-node/test/cardano-config-compare/config/shelley-genesis.json b/cardano-node/test/cardano-config-compare/config/shelley-genesis.json new file mode 100644 index 00000000000..7755bcd2242 --- /dev/null +++ b/cardano-node/test/cardano-config-compare/config/shelley-genesis.json @@ -0,0 +1,83 @@ +{ + "activeSlotsCoeff": 0.05, + "epochLength": 432000, + "genDelegs": {}, + "initialFunds": { + "0032635dc627da054f2a9e99559c56e16b02cf5f9237ba586ee1e648336b47a4e6e19ac5257fc97bd220bd9ae368aa2d775d86315ab7ef058f": 999500000000000, + "0064f4987ff07483636803f71f5f8442dad7f7fc46d83e2242d1548ad5d1f6f71a04ca856cc569fd068b069a555999c4776ad50b4cdd049d13": 999500000000000, + "602b43cb2b891e2dc9f5b07e051fb8d221a4a88ca161d95e859aa9ad8a": 9000000000000 + }, + "maxKESEvolutions": 60, + "maxLovelaceSupply": 2010000000000000, + "networkId": "Testnet", + "networkMagic": 42, + "protocolParams": { + "a0": 0.3, + "decentralisationParam": 0, + "eMax": 18, + "extraEntropy": { + "tag": "NeutralNonce" + }, + "keyDeposit": 400000, + "maxBlockBodySize": 81920, + "maxBlockHeaderSize": 1100, + "maxTxSize": 16384, + "minFeeA": 0, + "minFeeB": 0, + "minPoolCost": 0, + "minUTxOValue": 0, + "nOpt": 50, + "poolDeposit": 500000000, + "protocolVersion": { + "major": 5, + "minor": 0 + }, + "rho": 0.0022, + "tau": 0.05 + }, + "securityParam": 2160, + "slotLength": 1, + "slotsPerKESPeriod": 129600, + "staking": { + "pools": { + "1dc0a846ec816bdcc5288c0b57871a0400e86728ad0851132e43883f": { + "cost": 0, + "margin": 0, + "metadata": null, + "owners": [], + "pledge": 0, + "publicKey": "1dc0a846ec816bdcc5288c0b57871a0400e86728ad0851132e43883f", + "relays": [], + "rewardAccount": { + "credential": { + "key hash": "22c700325ec932f59048a7258e89b5f166604f184e0809d16a495550" + }, + "network": "Testnet" + }, + "vrf": "ee8fdadab21abed48fadee52492596841c561640920d1c022fa8ae51d206c714" + }, + "d3e16257b8c608ec4b2cb89621d7ddb60fc08839c1b491a598615fb5": { + "cost": 0, + "margin": 0, + "metadata": null, + "owners": [], + "pledge": 0, + "publicKey": "d3e16257b8c608ec4b2cb89621d7ddb60fc08839c1b491a598615fb5", + "relays": [], + "rewardAccount": { + "credential": { + "key hash": "3832f1051268ab7a7765142f21d695b7aaf40c576bf2d1a71894f0a4" + }, + "network": "Testnet" + }, + "vrf": "bbc57641002e501cf8bf98b776ba082c604e6af7c482f716934ed08d19c22733" + } + }, + "stake": { + "6b47a4e6e19ac5257fc97bd220bd9ae368aa2d775d86315ab7ef058f": "d3e16257b8c608ec4b2cb89621d7ddb60fc08839c1b491a598615fb5", + "d1f6f71a04ca856cc569fd068b069a555999c4776ad50b4cdd049d13": "1dc0a846ec816bdcc5288c0b57871a0400e86728ad0851132e43883f" + } + }, + "systemStart": "2022-06-16T08:04:19Z", + "updateQuorum": 5 +}