Skip to content
Draft
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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,5 @@ cardano-tracer/cardano-tracer-test
.codex

.serena/

cardano-node/test/db-synthesizer/disk/chaindb
12 changes: 8 additions & 4 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,17 @@ source-repository-package
kes-agent
kes-agent-crypto

source-repository-package
type: git
location: https://github.com/IntersectMBO/cardano-config
tag: 146fae3b13ad13a72a72ca9792a8a44ef5076b52
--sha256: sha256-M+LYeNr30ng261MnjwOURcWe1Gajn8T4YArAUf3XyRY=

source-repository-package
type: git
location: https://github.com/IntersectMBO/ouroboros-consensus.git
tag: e468a936006a890d4469d1cbfaa3cfbe6867e29c
--sha256: sha256-X1Yd6TMYhhxbm8qiD3y8Ad3nY2D5wieGWf9kwoRCWxc=
subdir:
.
tag: 97dac2a1f85f9dd8c5be14200b6ea4e756a3f07e
--sha256: sha256-47Po3W1Ut2hFA/LyZweg3orL2ZM3WVZEH65lOQziYRs=

source-repository-package
type: git
Expand Down
161 changes: 161 additions & 0 deletions cardano-node/app/DBSynthesizer/Parsers.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
module DBSynthesizer.Parsers (parseCommandLine) where

import Cardano.Node.Types (KESSource (..), ProtocolFilepaths (..))
import Cardano.Tools.DBSynthesizer.Types
import Data.Word (Word64)
import Options.Applicative as Opt
import Ouroboros.Consensus.Block.Abstract (SlotNo (..))

parseCommandLine :: IO (FilePath, FilePath, ProtocolFilepaths, DBSynthesizerOptions)
parseCommandLine =
Opt.customExecParser p opts
where
p = Opt.prefs Opt.showHelpOnEmpty
opts = Opt.info parserCommandLine mempty

parserCommandLine :: Parser (FilePath, FilePath, ProtocolFilepaths, DBSynthesizerOptions)
parserCommandLine =
(,,,)
<$> parseNodeConfigFilePath
<*> parseChainDBFilePath
<*> parseProtocolFilepaths
<*> parseDBSynthesizerOptions

-- | The forging credentials, as file paths. Byron delegation credentials are
-- not wired up (the synthesizer forges Shelley-based blocks); the KES key path,
-- when given, is interpreted as a key file (not a KES agent socket).
parseProtocolFilepaths :: Parser ProtocolFilepaths
parseProtocolFilepaths =
mkFilepaths
<$> optional parseKesKeyFilePath
<*> optional parseVrfKeyFilePath
<*> optional parseOperationalCertFilePath
<*> optional parseBulkFilePath
where
mkFilepaths mKes mVrf mCert mBulk =
ProtocolFilepaths
{ byronCertFile = Nothing
, byronKeyFile = Nothing
, shelleyKESSource = KESKeyFilePath <$> mKes
, shelleyVRFFile = mVrf
, shelleyCertFile = mCert
, shelleyBulkCredsFile = mBulk
}

parseDBSynthesizerOptions :: Parser DBSynthesizerOptions
parseDBSynthesizerOptions =
DBSynthesizerOptions
<$> parseForgeOptions
<*> parseOpenMode

parseForgeOptions :: Parser ForgeLimit
parseForgeOptions =
ForgeLimitSlot <$> parseSlotLimit
<|> ForgeLimitBlock <$> parseBlockLimit
<|> ForgeLimitEpoch <$> parseEpochLimit

parseChainDBFilePath :: Parser FilePath
parseChainDBFilePath =
strOption
( long "db"
<> metavar "PATH"
<> help "Path to the Chain DB"
<> completer (bashCompleter "directory")
)

parseNodeConfigFilePath :: Parser FilePath
parseNodeConfigFilePath =
strOption
( long "config"
<> metavar "FILE"
<> help "Path to the node's config.json"
<> completer (bashCompleter "file")
)

parseOperationalCertFilePath :: Parser FilePath
parseOperationalCertFilePath =
strOption
( long "shelley-operational-certificate"
<> metavar "FILE"
<> help "Path to the delegation certificate (in JSON TextEnvelope format)"
<> completer (bashCompleter "file")
)

parseKesKeyFilePath :: Parser FilePath
parseKesKeyFilePath =
strOption
( long "shelley-kes-key"
<> metavar "FILE"
<> help "Path to the KES signing key (in JSON TextEnvelope format)"
<> completer (bashCompleter "file")
)

parseVrfKeyFilePath :: Parser FilePath
parseVrfKeyFilePath =
strOption
( long "shelley-vrf-key"
<> metavar "FILE"
<> help "Path to the VRF signing key (in JSON TextEnvelope format)"
<> completer (bashCompleter "file")
)

parseBulkFilePath :: Parser FilePath
parseBulkFilePath =
strOption
( long "bulk-credentials-file"
<> metavar "FILE"
<> help
"Path to the bulk credentials file (a JSON file containing an array of arrays containing 3 TextEnvelope objects for the opcert, VRF Signing key, KES signing key)"
<> completer (bashCompleter "file")
)

parseSlotLimit :: Parser SlotNo
parseSlotLimit =
SlotNo
<$> option
auto
( short 's'
<> long "slots"
<> metavar "NUMBER"
<> help "Amount of slots to process"
)

parseBlockLimit :: Parser Word64
parseBlockLimit =
option
auto
( short 'b'
<> long "blocks"
<> metavar "NUMBER"
<> help "Amount of blocks to forge"
)

parseEpochLimit :: Parser Word64
parseEpochLimit =
option
auto
( short 'e'
<> long "epochs"
<> metavar "NUMBER"
<> help "Amount of epochs to process"
)

parseForce :: Parser Bool
parseForce =
switch
( short 'f'
<> help "Force overwrite an existing Chain DB"
)

parseAppend :: Parser Bool
parseAppend =
switch
( short 'a'
<> help "Append to an existing Chain DB"
)

parseOpenMode :: Parser DBSynthesizerOpenMode
parseOpenMode =
(parseForce *> pure OpenCreateForce)

Check warning on line 159 in cardano-node/app/DBSynthesizer/Parsers.hs

View workflow job for this annotation

GitHub Actions / build

Suggestion in parseOpenMode in module DBSynthesizer.Parsers: Use $> ▫︎ Found: "parseForce *> pure OpenCreateForce" ▫︎ Perhaps: "parseForce Data.Functor.$> OpenCreateForce"
<|> (parseAppend *> pure OpenAppend)

Check warning on line 160 in cardano-node/app/DBSynthesizer/Parsers.hs

View workflow job for this annotation

GitHub Actions / build

Suggestion in parseOpenMode in module DBSynthesizer.Parsers: Use $> ▫︎ Found: "parseAppend *> pure OpenAppend" ▫︎ Perhaps: "parseAppend Data.Functor.$> OpenAppend"
<|> pure OpenCreate
99 changes: 97 additions & 2 deletions cardano-node/app/cardano-node.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,32 @@
{-# 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.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)

Expand All @@ -37,6 +48,7 @@ main = do
runNode args
TraceDocumentation tdc -> runTraceDocumentationCmd tdc
VersionCmd -> runVersionCommand
ConfigCmd act -> act

where
p = Opt.prefs Opt.showHelpOnEmpty
Expand All @@ -56,6 +68,7 @@ main = do
Opt.info (fmap RunCmd nodeCLIParser
<|> fmap TraceDocumentation parseTraceDocumentationCmd
<|> parseVersionCmd
<|> fmap ConfigCmd configSubcommands
<**> helper)

( Opt.fullDesc <>
Expand All @@ -66,6 +79,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
Expand Down Expand Up @@ -105,3 +119,84 @@ 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@),
-- then re-resolve the same configuration 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.
resolveDualCommand :: Mod CommandFields (IO ())
resolveDualCommand =
command "resolve"
( info
(runDualResolve <$> Cmds.resolveOptionsParser)
( progDesc
( "Resolve a cardano-node configuration (defaults + file + CLI) with both the "
<> "node and cardano-config parsers, print the result as YAML, and report any "
<> "discrepancies between the two parsers (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
-- Cross-check: resolve the same inputs with the node's POM parser and diff.
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

-- | Resolve the 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
25 changes: 25 additions & 0 deletions cardano-node/app/db-synthesizer.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- | This tool synthesizes a valid ChainDB, replicating cardano-node's UX.
--
-- Usage: db-synthesizer --config FILE --db PATH
-- [--shelley-operational-certificate FILE]
-- [--shelley-vrf-key FILE] [--shelley-kes-key FILE]
-- [--bulk-credentials-file FILE]
-- ((-s|--slots NUMBER) | (-b|--blocks NUMBER) |
-- (-e|--epochs NUMBER)) [-f | -a]
--
-- The node configuration and forging credentials are turned into a Cardano
-- 'ProtocolInfo' and block forgers using cardano-node's own protocol-instantiation
-- machinery (see "Cardano.Node.Tools.DBSynthesizer"); the actual forging is done
-- by @ouroboros-consensus@'s @synthesize@.
module Main (main) where

import Cardano.Crypto.Init (cryptoInit)
import Cardano.Node.Tools.DBSynthesizer (synthesizeFromConfig)
import DBSynthesizer.Parsers (parseCommandLine)

main :: IO ()
main = do
cryptoInit
(configFp, dbDir, protocolFiles, opts) <- parseCommandLine
result <- synthesizeFromConfig configFp protocolFiles opts dbDir
putStrLn $ "--> done; result: " ++ show result
Loading
Loading