From be959b24de00cb87178a1179ab7b1d9e7a022d80 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Fri, 9 Jan 2026 19:13:34 +0100 Subject: [PATCH 01/23] cardano-testnet | Wait for blocks using foldEpochState instead of filtering logs --- .../src/Testnet/Property/Assert.hs | 33 ++---------- cardano-testnet/src/Testnet/Start/Cardano.hs | 54 ++++++++++++++----- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/cardano-testnet/src/Testnet/Property/Assert.hs b/cardano-testnet/src/Testnet/Property/Assert.hs index 800eb927884..f0c68fba937 100644 --- a/cardano-testnet/src/Testnet/Property/Assert.hs +++ b/cardano-testnet/src/Testnet/Property/Assert.hs @@ -1,7 +1,6 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} @@ -10,7 +9,6 @@ module Testnet.Property.Assert ( assertByDeadlineIOCustom , readJsonLines - , assertChainExtended , getRelevantSlots , assertExpectedSposInLedgerState , assertErasEqual @@ -39,31 +37,24 @@ import qualified Data.Time.Clock as DTC import Data.Type.Equality import Data.Word (Word8) import GHC.Stack as GHC -import RIO (throwString) import Testnet.Process.RunIO import Testnet.Start.Types -import Testnet.Types import Hedgehog (MonadTest) import qualified Hedgehog as H +import qualified Hedgehog.Extras as H import Hedgehog.Extras.Internal.Test.Integration (IntegrationState) -import qualified Hedgehog.Extras.Stock.IO.File as IO -import qualified Hedgehog.Extras.Test.Base as H import Hedgehog.Extras.Test.Process (ExecConfig) +import RIO (throwString) + newlineBytes :: Word8 newlineBytes = 10 readJsonLines :: (MonadTest m, MonadIO m, HasCallStack) => FilePath -> m [Value] readJsonLines fp = withFrozenCallStack $ mapMaybe (Aeson.decode @Value) . LBS.split newlineBytes <$> H.evalIO (LBS.readFile fp) -fileJsonGrep :: FilePath -> (Value -> Bool) -> IO Bool -fileJsonGrep fp f = do - lines <- LBS.split newlineBytes <$> LBS.readFile fp - let jsons = mapMaybe (Aeson.decode @Value) lines - return $ L.any f jsons - assertByDeadlineIOCustom :: (MonadIO m, HasCallStack) => String -> DTC.UTCTime -> IO Bool -> m () @@ -94,31 +85,17 @@ assertExpectedSposInLedgerState output (NumPools numExpectedPools) execConfig = ePoolSet <- liftIOAnnotated (Aeson.eitherDecodeFileStrict' @(Set PoolId) output) case ePoolSet of - Left err -> + Left err -> throwString $ "Failed to decode stake pools from ledger state: " <> err Right poolSet -> do let numPoolsInLedgerState = Set.size poolSet unless (numPoolsInLedgerState == numExpectedPools) $ - throwString $ unlines + throwString $ unlines [ "Expected number of stake pools not found in ledger state" , "Expected: ", show numExpectedPools , "Actual: ", show numPoolsInLedgerState ] -assertChainExtended - :: HasCallStack - => MonadIO m - => DTC.UTCTime - -> NodeLoggingFormat - -> TestnetNode - -> m () -assertChainExtended deadline nodeLoggingFormat TestnetNode{nodeName, nodeStdout} = withFrozenCallStack $ - assertByDeadlineIOCustom ("Chain not extended in " <> nodeName) deadline $ do - case nodeLoggingFormat of - NodeLoggingFormatAsText -> IO.fileContains "Chain extended, new tip" nodeStdout - NodeLoggingFormatAsJson -> fileJsonGrep nodeStdout $ \v -> - Aeson.parseMaybe (Aeson.parseJSON @(LogEntry Kind)) v == Just (LogEntry (Kind "AddedToCurrentChain")) - newtype LogEntry a = LogEntry { unLogEntry :: a } deriving (Eq, Show) diff --git a/cardano-testnet/src/Testnet/Start/Cardano.hs b/cardano-testnet/src/Testnet/Start/Cardano.hs index ebc68247503..32ad32e1747 100644 --- a/cardano-testnet/src/Testnet/Start/Cardano.hs +++ b/cardano-testnet/src/Testnet/Start/Cardano.hs @@ -65,7 +65,7 @@ import Testnet.Filepath import Testnet.Handlers (interruptNodesOnSigINT) import Testnet.Orphans () import Testnet.Process.RunIO (execCli', execCli_, liftIOAnnotated, mkExecConfig) -import Testnet.Property.Assert (assertChainExtended, assertExpectedSposInLedgerState) +import Testnet.Property.Assert (assertExpectedSposInLedgerState) import Testnet.Runtime as TR import Testnet.Start.Types import Testnet.Types as TR hiding (shelleyGenesis) @@ -74,8 +74,9 @@ import qualified Hedgehog.Extras as H import qualified Hedgehog.Extras.Stock.IO.Network.Port as H import Hedgehog.Internal.Property (failException) -import RIO (MonadUnliftIO, RIO (..), runRIO, throwString) +import RIO (MonadUnliftIO, RIO (..), runRIO, throwString, timeout) import RIO.Orphans (ResourceMap) +import RIO.State (put) import UnliftIO.Async import UnliftIO.Exception (stringException) @@ -226,8 +227,7 @@ cardanoTestnet , updateTimestamps } = do let CardanoTestnetOptions - { cardanoNodeLoggingFormat=nodeLoggingFormat - , cardanoEnableNewEpochStateLogging=enableNewEpochStateLogging + { cardanoEnableNewEpochStateLogging=enableNewEpochStateLogging , cardanoNodes } = testnetOptions nPools = cardanoNumPools testnetOptions @@ -282,7 +282,7 @@ cardanoTestnet liftIOAnnotated $ writeFile (nodeDataDir "port") (show portNumber) let topologyPath = tmpAbsPath Defaults.defaultNodeDataDir i "topology.json" tBytes <- liftIOAnnotated $ LBS.readFile topologyPath - case eitherDecode tBytes of + case eitherDecode tBytes of Right (abstractTopology :: P2P.NetworkTopology NodeId) -> do topology <- mapM idToRemoteAddressP2P abstractTopology liftIOAnnotated $ LBS.writeFile topologyPath $ encode topology @@ -349,11 +349,8 @@ cardanoTestnet -- Interrupt cardano nodes when the main process is interrupted liftIOAnnotated $ interruptNodesOnSigINT testnetNodes' - -- FIXME: use foldEpochState waiting for chain extensions - now <- liftIOAnnotated DTC.getCurrentTime - let deadline = DTC.addUTCTime 45 now - forM_ testnetNodes' $ \nodeStdoutFile -> do - assertChainExtended deadline nodeLoggingFormat nodeStdoutFile + -- Make sure that all nodes are healthy by waiting for a chain extension + mapConcurrently_ (waitForBlockThrow 45 (File nodeConfigFile)) testnetNodes' let runtime = TestnetRuntime { configurationFile = File nodeConfigFile @@ -397,6 +394,37 @@ cardanoTestnet mkTestnetNodeKeyPaths :: Int -> SpoNodeKeys mkTestnetNodeKeyPaths n = makePathsAbsolute $ Defaults.defaultSpoKeys n + -- wait for new blocks or throw an exception if there are none in the timeout period + waitForBlockThrow :: MonadUnliftIO m + => MonadCatch m + => Int -- ^ timeout in seconds + -> NodeConfigFile 'In + -> TestnetNode + -> m () + waitForBlockThrow timeoutSeconds nodeConfigFile node@TestnetNode{nodeName} = do + result <- timeout (timeoutSeconds * 1_000_000) $ + runExceptT . foldEpochState + nodeConfigFile + (nodeSocketPath node) + QuickValidation + (EpochNo maxBound) + minBound + $ \_ slotNo blockNo -> do + put slotNo + pure $ if blockNo >= 1 + then ConditionMet -- we got one block + else ConditionNotMet + + case result of + Just (Right (ConditionMet, _)) -> pure () + Just (Right (ConditionNotMet, slotNo)) -> + throwString $ nodeName <> " was unable to produce any blocks. Reached slot " <> show slotNo + Just (Left err) -> + throwString $ "foldBlocks on " <> nodeName <> " encountered an error while waiting for new blocks: " <> show (prettyError err) + _ -> + throwString $ nodeName <> " was unable to produce any blocks for " <> show timeoutSeconds <> "s" + + -- | A convenience wrapper around `createTestnetEnv` and `cardanoTestnet` createAndRunTestnet :: () => HasCallStack @@ -420,8 +448,8 @@ retryOnAddressInUseError retryOnAddressInUseError act = withFrozenCallStack $ go maximumTimeout retryTimeout where go :: HasCallStack => NominalDiffTime -> NominalDiffTime -> ExceptT NodeStartFailure m a - go timeout interval - | timeout <= 0 = withFrozenCallStack $ do + go timeout' interval + | timeout' <= 0 = withFrozenCallStack $ do act | otherwise = withFrozenCallStack $ do !time <- liftIOAnnotated DTC.getCurrentTime @@ -430,7 +458,7 @@ retryOnAddressInUseError act = withFrozenCallStack $ go maximumTimeout retryTime liftIOAnnotated $ threadDelay (round $ interval * 1_000_000) !time' <- liftIOAnnotated DTC.getCurrentTime let elapsedTime = time' `diffUTCTime` time - newTimeout = timeout - elapsedTime + newTimeout = timeout' - elapsedTime go newTimeout interval e -> throwError e From 498cd5908890adc1387e504ded37bb3966c827c0 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Wed, 7 Jan 2026 19:49:31 +0100 Subject: [PATCH 02/23] Update cardano-cli-10.14 cardano-api-10.20 --- bench/plutus-scripts-bench/plutus-scripts-bench.cabal | 2 +- bench/tx-generator/tx-generator.cabal | 4 ++-- cardano-node-chairman/cardano-node-chairman.cabal | 2 +- cardano-node/cardano-node.cabal | 2 +- cardano-submit-api/cardano-submit-api.cabal | 4 ++-- cardano-testnet/cardano-testnet.cabal | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bench/plutus-scripts-bench/plutus-scripts-bench.cabal b/bench/plutus-scripts-bench/plutus-scripts-bench.cabal index 169a3126ed8..bc879350134 100644 --- a/bench/plutus-scripts-bench/plutus-scripts-bench.cabal +++ b/bench/plutus-scripts-bench/plutus-scripts-bench.cabal @@ -82,7 +82,7 @@ library -- IOG dependencies -------------------------- build-depends: - , cardano-api ^>=10.19 + , cardano-api ^>=10.20 , plutus-ledger-api ^>=1.53 , plutus-tx ^>=1.53 , plutus-tx-plugin ^>=1.53 diff --git a/bench/tx-generator/tx-generator.cabal b/bench/tx-generator/tx-generator.cabal index afaa52fd929..1fc33a7519f 100644 --- a/bench/tx-generator/tx-generator.cabal +++ b/bench/tx-generator/tx-generator.cabal @@ -111,9 +111,9 @@ library , attoparsec-aeson , base16-bytestring , bytestring - , cardano-api ^>= 10.19 + , cardano-api ^>= 10.20 , cardano-binary - , cardano-cli ^>= 10.13 + , cardano-cli ^>= 10.14 , cardano-crypto-class , cardano-crypto-wrapper , cardano-data diff --git a/cardano-node-chairman/cardano-node-chairman.cabal b/cardano-node-chairman/cardano-node-chairman.cabal index ed6faec2369..d8a96c5343b 100644 --- a/cardano-node-chairman/cardano-node-chairman.cabal +++ b/cardano-node-chairman/cardano-node-chairman.cabal @@ -88,5 +88,5 @@ test-suite chairman-tests ghc-options: -threaded -rtsopts "-with-rtsopts=-N -T" build-tool-depends: cardano-node:cardano-node - , cardano-cli:cardano-cli ^>= 10.13 + , cardano-cli:cardano-cli ^>= 10.14 , cardano-node-chairman:cardano-node-chairman diff --git a/cardano-node/cardano-node.cabal b/cardano-node/cardano-node.cabal index f6836d20bd6..4b03d1b6d60 100644 --- a/cardano-node/cardano-node.cabal +++ b/cardano-node/cardano-node.cabal @@ -138,7 +138,7 @@ library , async , base16-bytestring , bytestring - , cardano-api ^>= 10.19 + , cardano-api ^>= 10.20 , cardano-crypto-class ^>=2.2.3.2 , cardano-crypto-wrapper , cardano-git-rev ^>=0.2.2 diff --git a/cardano-submit-api/cardano-submit-api.cabal b/cardano-submit-api/cardano-submit-api.cabal index 866ca526430..e8b2068de48 100644 --- a/cardano-submit-api/cardano-submit-api.cabal +++ b/cardano-submit-api/cardano-submit-api.cabal @@ -39,9 +39,9 @@ library , aeson , async , bytestring - , cardano-api ^>= 10.19 + , cardano-api ^>= 10.20 , cardano-binary - , cardano-cli ^>= 10.13 + , cardano-cli ^>= 10.14 , cardano-crypto-class ^>=2.2.3.2 , containers , ekg-core diff --git a/cardano-testnet/cardano-testnet.cabal b/cardano-testnet/cardano-testnet.cabal index d9ad3e1840a..43641cd03bd 100644 --- a/cardano-testnet/cardano-testnet.cabal +++ b/cardano-testnet/cardano-testnet.cabal @@ -41,8 +41,8 @@ library , annotated-exception , ansi-terminal , bytestring - , cardano-api ^>= 10.19 - , cardano-cli:{cardano-cli, cardano-cli-test-lib} ^>= 10.13 + , cardano-api ^>= 10.20 + , cardano-cli:{cardano-cli, cardano-cli-test-lib} ^>= 10.14 , cardano-crypto-class ^>=2.2.3.2 , cardano-crypto-wrapper , cardano-git-rev ^>= 0.2.2 From 2c56d1df8b0f41639e453c9558c1eb10eb1a8d66 Mon Sep 17 00:00:00 2001 From: Jordan Millar Date: Fri, 25 Apr 2025 16:29:10 -0400 Subject: [PATCH 03/23] Add gRPC to cardano-node --- .github/workflows/haskell.yml | 20 +- .../src/Cardano/TxGenerator/Setup/Plutus.hs | 1 + bench/tx-generator/tx-generator.cabal | 1 + cabal.project | 26 +++ cardano-node/cardano-node.cabal | 3 + .../src/Cardano/Node/Configuration/POM.hs | 23 +++ cardano-node/src/Cardano/Node/Parsers.hs | 40 +++- cardano-node/src/Cardano/Node/Run.hs | 70 +++++-- cardano-node/src/Cardano/Node/Types.hs | 11 ++ .../test/Test/Cardano/Node/FilePermissions.hs | 5 +- cardano-node/test/Test/Cardano/Node/POM.hs | 8 + cardano-testnet/cardano-testnet.cabal | 7 + cardano-testnet/src/Cardano/Testnet.hs | 1 + cardano-testnet/src/Parsers/Cardano.hs | 21 +- cardano-testnet/src/Testnet/Runtime.hs | 2 +- cardano-testnet/src/Testnet/Start/Cardano.hs | 3 +- cardano-testnet/src/Testnet/Start/Types.hs | 14 +- cardano-testnet/src/Testnet/Types.hs | 6 + .../Cardano/Testnet/Test/Rpc/Query.hs | 182 ++++++++++++++++++ .../Cardano/Testnet/Test/Rpc/Transaction.hs | 158 +++++++++++++++ .../cardano-testnet-test.hs | 6 + nix/haskell.nix | 6 +- 22 files changed, 571 insertions(+), 43 deletions(-) create mode 100644 cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs create mode 100644 cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index 89ef0d1c449..3dda0e678a1 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -38,7 +38,7 @@ jobs: # If you edit these versions, make sure the version in the lonely macos-latest job below is updated accordingly # TODO add 9.8 again to the versions list when GHC-9.8 gets released with stm > 2.5.2, # see https://github.com/haskell/stm/issues/76 - ghc: ["9.6", "9.12"] + ghc: ["9.6", "9.10"] cabal: ["3.12"] sys: - { os: windows-latest, shell: 'C:/msys64/usr/bin/bash.exe -e {0}' } @@ -97,6 +97,18 @@ jobs: with: use-sodium-vrf: true # default is true + - name: "[Linux] Install grpc dependencies" + if: runner.os == 'Linux' + run: sudo apt install libsnappy-dev protobuf-compiler + + - name: "[Windows] Install grpc dependencies" + if: runner.os == 'Windows' + run: /usr/bin/pacman --noconfirm -S mingw-w64-x86_64-snappy mingw-w64-x86_64-protobuf + + - name: "[macOS] Install grpc dependencies" + if: runner.os == 'macOS' + run: brew install snappy protobuf + - uses: actions/checkout@v4 - name: Cabal update @@ -230,9 +242,9 @@ jobs: # and will silently fail if msys2 is not in path. See the "Run tests" step. # # - name: Setup tmate session - # if: ${{ failure() }} - # uses: mxschmitt/action-tmate@v3 - # with: + # if: ${{ failure() }} + # uses: mxschmitt/action-tmate@v3 + # with: # limit-access-to-actor: true build-complete: diff --git a/bench/tx-generator/src/Cardano/TxGenerator/Setup/Plutus.hs b/bench/tx-generator/src/Cardano/TxGenerator/Setup/Plutus.hs index 22e40dd9432..a9650cb48b7 100644 --- a/bench/tx-generator/src/Cardano/TxGenerator/Setup/Plutus.hs +++ b/bench/tx-generator/src/Cardano/TxGenerator/Setup/Plutus.hs @@ -36,6 +36,7 @@ import qualified PlutusTx.AssocMap as AssocMap (empty) import Cardano.TxGenerator.ProtocolParameters (ProtocolParameters(..)) import Cardano.TxGenerator.Types (TxGenError (..), TxGenPlutusResolvedTo (..)) import Control.Exception (SomeException (..), try, displayException) +import RIO (runRIO) import System.FilePath ((<.>), ()) #ifdef WITH_LIBRARY import Cardano.Benchmarking.PlutusScripts (findPlutusScript) diff --git a/bench/tx-generator/tx-generator.cabal b/bench/tx-generator/tx-generator.cabal index 1fc33a7519f..19ef08baeec 100644 --- a/bench/tx-generator/tx-generator.cabal +++ b/bench/tx-generator/tx-generator.cabal @@ -140,6 +140,7 @@ library , network , network-mux , optparse-applicative + , rio , ouroboros-consensus >= 0.6 , ouroboros-consensus-cardano >= 0.5 , ouroboros-consensus-diffusion >= 0.7.0 diff --git a/cabal.project b/cabal.project index 3ef2447a9db..8c6dbfbe0c2 100644 --- a/cabal.project +++ b/cabal.project @@ -61,6 +61,13 @@ package plutus-scripts-bench allow-newer: , katip:Win32 + +if impl (ghc >= 9.10) + allow-newer: + -- TODO: remove - this is for protolens + , *:base + , *:ghc-prim + if impl (ghc >= 9.12) allow-newer: -- https://github.com/kapralVV/Unique/issues/11 @@ -72,3 +79,22 @@ if impl (ghc >= 9.12) -- IMPORTANT -- Do NOT add more source-repository-package stanzas here unless they are strictly -- temporary! Please read the section in CONTRIBUTING about updating dependencies. + + +source-repository-package + type: git + location: https://github.com/intersectmbo/cardano-api + -- mgalazyn/feature/add-utxorpc-protocol-parameters-query + tag: 87b2afaaa037b78d1bed92257413fa9fdf2f156c + --sha256: sha256-VZv++ErHQZoG/OokIoQKGfqpKAcdm7dXPD8BzXKAkxU= + subdir: cardano-api + cardano-api-gen + cardano-rpc + +source-repository-package + type: git + location: https://github.com/intersectmbo/cardano-cli + -- mgalazyn/chore/remove-upstreamed-instances + tag: 386b22edd41820584857537203ca39caca61d158 + --sha256: sha256-wsP5leh9EIgmG0wVLuaPizHFylvS8LGt7D2erLZbs5s= + subdir: cardano-cli diff --git a/cardano-node/cardano-node.cabal b/cardano-node/cardano-node.cabal index 4b03d1b6d60..403f5412bdb 100644 --- a/cardano-node/cardano-node.cabal +++ b/cardano-node/cardano-node.cabal @@ -155,6 +155,7 @@ library , cardano-prelude , cardano-protocol-tpraos >= 1.4 , cardano-slotting >= 0.2 + , cardano-rpc ^>= 10.0 , cborg ^>= 0.2.4 , containers , contra-tracer @@ -217,6 +218,7 @@ library , typed-protocols:{typed-protocols, stateful} >= 1.0 , yaml + executable cardano-node import: project-config hs-source-dirs: app @@ -252,6 +254,7 @@ test-suite cardano-node-test , cardano-crypto-class , cardano-crypto-wrapper , cardano-api + , cardano-rpc , cardano-protocol-tpraos , cardano-node , cardano-slotting diff --git a/cardano-node/src/Cardano/Node/Configuration/POM.hs b/cardano-node/src/Cardano/Node/Configuration/POM.hs index 4255c77d775..4be8814c53d 100644 --- a/cardano-node/src/Cardano/Node/Configuration/POM.hs +++ b/cardano-node/src/Cardano/Node/Configuration/POM.hs @@ -10,6 +10,8 @@ {-# OPTIONS_GHC -Wno-noncanonical-monoid-instances #-} +{- HLINT ignore "Functor law" -} + module Cardano.Node.Configuration.POM ( NodeConfiguration (..) , ResponderCoreAffinityPolicy (..) @@ -34,6 +36,8 @@ import Cardano.Node.Configuration.Socket (SocketConfig (..)) import Cardano.Node.Handlers.Shutdown import Cardano.Node.Protocol.Types (Protocol (..)) import Cardano.Node.Types +import Cardano.Rpc.Server.Config (PartialRpcConfig, RpcConfig, RpcConfigF (..), + makeRpcConfig) import Cardano.Tracing.Config import Cardano.Tracing.OrphanInstances.Network () import Ouroboros.Consensus.Ledger.SupportsMempool @@ -174,6 +178,9 @@ data NodeConfiguration , ncGenesisConfig :: GenesisConfig , ncResponderCoreAffinityPolicy :: ResponderCoreAffinityPolicy + + -- gRPC + , ncRpcConfig :: RpcConfig } deriving (Eq, Show) -- | We expose the `Ouroboros.Network.Mux.ForkPolicy` as a `NodeConfiguration` field. @@ -255,6 +262,7 @@ data PartialNodeConfiguration , pncSyncTargetOfKnownBigLedgerPeers :: !(Last Int) , pncSyncTargetOfEstablishedBigLedgerPeers :: !(Last Int) , pncSyncTargetOfActiveBigLedgerPeers :: !(Last Int) + -- Minimum number of active big ledger peers we must be connected to -- in Genesis mode , pncMinBigLedgerPeersForTrustedState :: !(Last NumberOfBigLedgerPeers) @@ -269,6 +277,9 @@ data PartialNodeConfiguration , pncGenesisConfigFlags :: !(Last GenesisConfigFlags) , pncResponderCoreAffinityPolicy :: !(Last ResponderCoreAffinityPolicy) + + -- gRPC + , pncRpcConfig :: !PartialRpcConfig } deriving (Eq, Generic, Show) instance AdjustFilePaths PartialNodeConfiguration where @@ -381,6 +392,12 @@ instance FromJSON PartialNodeConfiguration where <$> v .:? "ResponderCoreAffinityPolicy" <*> v .:? "ForkPolicy" -- deprecated + pncRpcConfig <- + RpcConfig + <$> (Last <$> v .:? "EnableRpc") + <*> (Last <$> v .:? "RpcSocketPath") + <*> pure mempty + pure PartialNodeConfiguration { pncProtocolConfig , pncSocketConfig = Last . Just $ SocketConfig mempty mempty mempty pncSocketPath @@ -425,6 +442,7 @@ instance FromJSON PartialNodeConfiguration where , pncPeerSharing , pncGenesisConfigFlags , pncResponderCoreAffinityPolicy + , pncRpcConfig } where parseMempoolCapacityBytesOverride v = parseNoOverride <|> parseOverride @@ -687,6 +705,7 @@ defaultPartialNodeConfiguration = , pncGenesisConfigFlags = Last (Just defaultGenesisConfigFlags) -- https://ouroboros-consensus.cardano.intersectmbo.org/haddocks/ouroboros-consensus-diffusion/Ouroboros-Consensus-Node-Genesis.html#v:defaultGenesisConfigFlags , pncResponderCoreAffinityPolicy = Last $ Just NoResponderCoreAffinity + , pncRpcConfig = mempty } lastOption :: Parser a -> Parser (Last a) @@ -827,6 +846,9 @@ makeNodeConfiguration pnc = do experimentalProtocols <- lastToEither "Missing ExperimentalProtocolsEnabled" $ pncExperimentalProtocolsEnabled pnc + + ncRpcConfig <- makeRpcConfig $ (pncRpcConfig pnc){nodeSocketPath=ncSocketPath socketConfig} + return $ NodeConfiguration { ncConfigFile = configFile , ncTopologyFile = topologyFile @@ -872,6 +894,7 @@ makeNodeConfiguration pnc = do , ncConsensusMode , ncGenesisConfig , ncResponderCoreAffinityPolicy + , ncRpcConfig } ncProtocol :: NodeConfiguration -> Protocol diff --git a/cardano-node/src/Cardano/Node/Parsers.hs b/cardano-node/src/Cardano/Node/Parsers.hs index 077e9675b62..3f4d9da4b99 100644 --- a/cardano-node/src/Cardano/Node/Parsers.hs +++ b/cardano-node/src/Cardano/Node/Parsers.hs @@ -15,27 +15,26 @@ module Cardano.Node.Parsers import Cardano.Logging.Types import qualified Cardano.Logging.Types as Net -import Cardano.Node.Configuration.NodeAddress ( - NodeHostIPv4Address (NodeHostIPv4Address), File (..), +import Cardano.Node.Configuration.NodeAddress (File (..), + NodeHostIPv4Address (NodeHostIPv4Address), NodeHostIPv6Address (NodeHostIPv6Address), PortNumber, SocketPath) import Cardano.Node.Configuration.POM (PartialNodeConfiguration (..), lastOption) import Cardano.Node.Configuration.Socket import Cardano.Node.Handlers.Shutdown import Cardano.Node.Types import Cardano.Prelude (ConvertText (..)) +import Cardano.Rpc.Server.Config (PartialRpcConfig, RpcConfigF (..)) import Ouroboros.Consensus.Ledger.SupportsMempool import Ouroboros.Consensus.Node -import Data.Foldable import Data.Char (isDigit) +import Data.Foldable import Data.Maybe (fromMaybe) import Data.Monoid (Last (..)) import Data.Text (Text) import qualified Data.Text as Text import Data.Word (Word16, Word32) import Options.Applicative hiding (str, switch) --- Don't use switch. It will not allow to set an option in a configuration --- file. See `parseStartAsNonProducingNode` and `parseValidateDB`. import qualified Options.Applicative as Opt import qualified Options.Applicative.Help as OptI import qualified Prettyprinter.Internal as PP @@ -57,7 +56,7 @@ nodeRunParser = do topFp <- lastOption parseTopologyFile dbFp <- lastOption parseNodeDatabasePaths validate <- lastOption parseValidateDB - socketFp <- lastOption $ parseSocketPath "Path to a cardano-node socket" + socketFp <- lastOption $ parseSocketPath "socket-path" "Path to a cardano-node socket" traceForwardSocket <- lastOption parseTracerSocketMode nodeConfigFp <- lastOption parseConfigFile @@ -84,6 +83,9 @@ nodeRunParser = do -- Hidden options (to be removed eventually) maybeMempoolCapacityOverride <- lastOption parseMempoolCapacityOverride + -- gRPC + pncRpcConfig <- parseRpcConfig + pure $ PartialNodeConfiguration { pncSocketConfig = Last . Just $ SocketConfig @@ -141,12 +143,15 @@ nodeRunParser = do , pncPeerSharing = mempty , pncGenesisConfigFlags = mempty , pncResponderCoreAffinityPolicy = mempty + , pncRpcConfig } -parseSocketPath :: Text -> Parser SocketPath -parseSocketPath helpMessage = +parseSocketPath :: Text -- ^ option name + -> Text -- ^ help text + -> Parser SocketPath +parseSocketPath optionName helpMessage = fmap File $ strOption $ mconcat - [ long "socket-path" + [ long (toS optionName) , help (toS helpMessage) , completer (bashCompleter "file") , metavar "FILEPATH" @@ -420,6 +425,23 @@ parseStartAsNonProducingNode = ] ] +parseRpcConfig :: Parser PartialRpcConfig +parseRpcConfig = do + isEnabled <- lastOption parseRpcToggle + socketPath <- lastOption parseRpcSocketPath + pure $ RpcConfig isEnabled socketPath mempty + where + parseRpcToggle :: Parser Bool + parseRpcToggle = + Opt.switch ( + long "grpc-enable" + <> help "[EXPERIMENTAL] Enable node gRPC endpoint." + ) + parseRpcSocketPath :: Parser SocketPath + parseRpcSocketPath = + parseSocketPath + "gprc-socket-path" + "[EXPERIMENTAL] gRPC socket path. Defaults to rpc.sock in the same directory as node socket." -- | Produce just the brief help header for a given CLI option parser, -- without the options. diff --git a/cardano-node/src/Cardano/Node/Run.hs b/cardano-node/src/Cardano/Node/Run.hs index 2c87c34956f..80264252263 100644 --- a/cardano-node/src/Cardano/Node/Run.hs +++ b/cardano-node/src/Cardano/Node/Run.hs @@ -22,7 +22,7 @@ module Cardano.Node.Run , checkVRFFilePermissions ) where -import Cardano.Api (File (..), FileDirection (..)) +import Cardano.Api (File (..), FileDirection (..), NetworkMagic, fromNetworkMagic) import Cardano.Api.Error (displayError) import qualified Cardano.Api as Api import System.Random (randomIO) @@ -53,6 +53,8 @@ import Cardano.Node.Protocol.Shelley (PraosLeaderCredentialsError (..) ShelleyProtocolInstantiationError (PraosLeaderCredentialsError)) import Cardano.Node.Protocol.Types import Cardano.Node.Queries +import Cardano.Rpc.Server +import Cardano.Rpc.Server.Config import Cardano.Node.Startup import Cardano.Node.TraceConstraints (TraceConstraints) import Cardano.Node.Tracing.API @@ -122,6 +124,7 @@ import Ouroboros.Network.Protocol.ChainSync.Codec import Control.Applicative (empty) import Control.Concurrent (killThread, mkWeakThreadId, myThreadId, getNumCapabilities) +import Control.Concurrent.Async import Control.Concurrent.Class.MonadSTM.Strict import Control.Exception (try, Exception, IOException) import qualified Control.Exception as Exception @@ -154,6 +157,7 @@ import Network.HostName (getHostName) import Network.Socket (Socket) import System.Directory (canonicalizePath, createDirectoryIfMissing, makeAbsolute) import System.Environment (lookupEnv) +import System.FilePath (takeDirectory, ()) import System.IO (hPutStrLn) #ifdef UNIX import GHC.Weak (deRefWeak) @@ -166,6 +170,7 @@ import System.Win32.File import Paths_cardano_node (version) import Paths_cardano_node (version) +import GHC.Stack {- HLINT ignore "Fuse concatMap/map" -} {- HLINT ignore "Redundant <$>" -} @@ -175,37 +180,58 @@ runNode :: PartialNodeConfiguration -> IO () runNode cmdPc = do - installSigTermHandler + installSigTermHandler - Crypto.cryptoInit + Crypto.cryptoInit - configYamlPc <- parseNodeConfigurationFP . getLast $ pncConfigFile cmdPc + let earlyTracer = stdoutTracer + nc@NodeConfiguration + { ncProtocolConfig + , ncProtocolFiles=ncProtocolFiles@ProtocolFilepaths{shelleyVRFFile=mShelleyVrfFile} + } <- buildNodeConfiguration cmdPc - nc <- case makeNodeConfiguration $ defaultPartialNodeConfiguration <> configYamlPc <> cmdPc of - Left err -> error $ "Error in creating the NodeConfiguration: " <> err - Right nc' -> return nc' + traceWith earlyTracer $ "Node configuration: " <> show nc - putStrLn $ "Node configuration: " <> show nc + forM_ mShelleyVrfFile $ + runThrowExceptT . checkVRFFilePermissions earlyTracer . File - case ncProtocolFiles nc of - ProtocolFilepaths{shelleyVRFFile=Just vrfFp} -> - runThrowExceptT $ - checkVRFFilePermissions stdoutTracer (File vrfFp) - _ -> pure () - - consensusProtocol <- - runThrowExceptT $ - mkConsensusProtocol - (ncProtocolConfig nc) - -- TODO: Convert ncProtocolFiles to Maybe as relay nodes - -- don't need these. - (Just $ ncProtocolFiles nc) + consensusProtocol@(SomeConsensusProtocol _ runP) <- + runThrowExceptT $ + mkConsensusProtocol + ncProtocolConfig + -- TODO: Convert ncProtocolFiles to Maybe as relay nodes + -- don't need these. + (Just ncProtocolFiles) + let ProtocolInfo{pInfoConfig} = fst $ Api.protocolInfo @IO runP + networkMagic :: Api.NetworkMagic = getNetworkMagic $ Consensus.configBlock pInfoConfig + -- TODO move initialisation somewhere else, so that the correct tracer is used, instead of stdout default one + withAsync (runRpcServer earlyTracer $ buildRpcConfiguration networkMagic cmdPc) $ \_ -> handleNodeWithTracers cmdPc nc consensusProtocol 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) + +-- | Build RPC configuration. Reads the configuration file again. Allows RPC server to dynamically reload configuration from disk again. +buildRpcConfiguration :: HasCallStack + => NetworkMagic + -> PartialNodeConfiguration + -> IO (RpcConfig, NetworkMagic) +buildRpcConfiguration networkMagic partialConf = do + NodeConfiguration{ncRpcConfig} <- buildNodeConfiguration partialConf + pure (ncRpcConfig, networkMagic) + -- | Workaround to ensure that the main thread throws an async exception on -- receiving a SIGTERM signal. installSigTermHandler :: IO () @@ -631,6 +657,8 @@ handleSimpleNode blockType runP tracers nc onKernel = do -- SIGHUP Handlers -------------------------------------------------------------------------------- +-- TODO add SIGHUP handler for RPC configuration reloading + -- | The P2P SIGHUP handler can update block forging & reconfigure network topology. -- installSigHUPHandler :: Tracer IO (StartupTrace blk) diff --git a/cardano-node/src/Cardano/Node/Types.hs b/cardano-node/src/Cardano/Node/Types.hs index b3c9109cb4c..05aed33003c 100644 --- a/cardano-node/src/Cardano/Node/Types.hs +++ b/cardano-node/src/Cardano/Node/Types.hs @@ -47,6 +47,7 @@ import qualified Cardano.Crypto.Hash as Crypto import Cardano.Network.ConsensusMode (ConsensusMode (..)) import Cardano.Node.Configuration.Socket (SocketConfig (..)) import Cardano.Node.Orphans () +import Cardano.Rpc.Server.Config (RpcConfigF (..)) import Ouroboros.Network.NodeToNode (DiffusionMode (..)) import Control.Exception @@ -499,6 +500,16 @@ instance AdjustFilePaths a => AdjustFilePaths (Maybe a) where instance AdjustFilePaths a => AdjustFilePaths (Last a) where adjustFilePaths f = fmap (adjustFilePaths f) +instance AdjustFilePaths (File a b) where + adjustFilePaths f (File p) = File $ f p + +instance Functor f => AdjustFilePaths (RpcConfigF f) where + adjustFilePaths f (RpcConfig isEnabled rpcSocketPath nodeSocketPath) = + RpcConfig + isEnabled + (adjustFilePaths f <$> rpcSocketPath) + (adjustFilePaths f <$> nodeSocketPath) + data VRFPrivateKeyFilePermissionError = OtherPermissionsExist FilePath | GroupPermissionsExist FilePath diff --git a/cardano-node/test/Test/Cardano/Node/FilePermissions.hs b/cardano-node/test/Test/Cardano/Node/FilePermissions.hs index 524e1ee3593..0aa16e86453 100644 --- a/cardano-node/test/Test/Cardano/Node/FilePermissions.hs +++ b/cardano-node/test/Test/Cardano/Node/FilePermissions.hs @@ -39,6 +39,7 @@ import Hedgehog.Internal.Property (Group (..), failWith) import System.IO (FilePath, IO) import Text.Show (Show (..)) import Cardano.Node.Types (VRFPrivateKeyFilePermissionError (..)) +import Control.Exception (bracket) #ifdef UNIX @@ -47,7 +48,7 @@ import System.Posix.IO (closeFd, createFile) import System.Posix.Types (FileMode) import Hedgehog -import Hedgehog.Extras +import qualified Hedgehog.Extras as H import qualified Hedgehog.Gen as Gen #endif @@ -134,7 +135,7 @@ prop_sanityCheck_checkVRFFilePermissions = (const . liftIO . runExceptT $ checkVRFFilePermissions capturingTracer vrfPrivateKeyGroup) case groupResult of Left (GroupPermissionsExist _) -> do - note_ "Group permissions check should not fail" + H.note_ "Group permissions check should not fail" failure Left err -> failWith Nothing $ "checkVRFFilePermissions should not have failed with error: " diff --git a/cardano-node/test/Test/Cardano/Node/POM.hs b/cardano-node/test/Test/Cardano/Node/POM.hs index cfe7fd8be2a..28429ef79c4 100644 --- a/cardano-node/test/Test/Cardano/Node/POM.hs +++ b/cardano-node/test/Test/Cardano/Node/POM.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE TemplateHaskell #-} @@ -14,6 +15,7 @@ import Cardano.Node.Configuration.POM import Cardano.Node.Configuration.Socket import Cardano.Node.Handlers.Shutdown import Cardano.Node.Types +import Cardano.Rpc.Server.Config (makeRpcConfig) import Cardano.Tracing.Config (PartialTraceOptions (..), defaultPartialTraceConfiguration, partialTraceSelectionToEither) import Ouroboros.Consensus.Node (NodeDatabasePaths (..)) @@ -27,7 +29,9 @@ import Ouroboros.Network.NodeToNode (AcceptedConnectionsLimit (..), DiffusionMode (InitiatorAndResponderDiffusionMode)) import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing (..)) +import Data.Bifunctor (first) import Data.Monoid (Last (..)) +import Data.String import Data.Text (Text) import Hedgehog (Property, discover, withTests, (===)) @@ -171,6 +175,7 @@ testPartialYamlConfig = , pncResponderCoreAffinityPolicy = mempty , pncLedgerDbConfig = mempty , pncEgressPollInterval = mempty + , pncRpcConfig = mempty } -- | Example partial configuration theoretically created @@ -221,6 +226,7 @@ testPartialCliConfig = , pncResponderCoreAffinityPolicy = mempty , pncLedgerDbConfig = mempty , pncEgressPollInterval = mempty + , pncRpcConfig = mempty } -- | Expected final NodeConfiguration @@ -228,6 +234,7 @@ eExpectedConfig :: Either Text NodeConfiguration eExpectedConfig = do traceOptions <- partialTraceSelectionToEither (return $ PartialTracingOnLegacy defaultPartialTraceConfiguration) + ncRpcConfig <- first fromString $ makeRpcConfig mempty return $ NodeConfiguration { ncSocketConfig = SocketConfig mempty mempty mempty mempty , ncShutdownConfig = ShutdownConfig Nothing (Just . ASlot $ SlotNo 42) @@ -277,6 +284,7 @@ eExpectedConfig = do , ncGenesisConfig = disableGenesisConfig , ncResponderCoreAffinityPolicy = NoResponderCoreAffinity , ncLedgerDbConfig = LedgerDbConfiguration DefaultNumOfDiskSnapshots DefaultSnapshotInterval DefaultQueryBatchSize V2InMemory noDeprecatedOptions + , ncRpcConfig } -- ----------------------------------------------------------------------------- diff --git a/cardano-testnet/cardano-testnet.cabal b/cardano-testnet/cardano-testnet.cabal index 43641cd03bd..6b4454e9e91 100644 --- a/cardano-testnet/cardano-testnet.cabal +++ b/cardano-testnet/cardano-testnet.cabal @@ -59,6 +59,7 @@ library , cardano-node , cardano-ping ^>= 0.9 , cardano-prelude + , cardano-rpc , contra-tracer , containers , data-default-class @@ -229,6 +230,8 @@ test-suite cardano-testnet-test Cardano.Testnet.Test.Gov.TreasuryDonation Cardano.Testnet.Test.Gov.TreasuryGrowth Cardano.Testnet.Test.Gov.TreasuryWithdrawal + Cardano.Testnet.Test.Rpc.Query + Cardano.Testnet.Test.Rpc.Transaction Cardano.Testnet.Test.Misc Cardano.Testnet.Test.Node.Shutdown Cardano.Testnet.Test.MainnetParams @@ -247,12 +250,15 @@ test-suite cardano-testnet-test , bytestring , cardano-api , cardano-cli:{cardano-cli, cardano-cli-test-lib} + , cardano-ledger-api , cardano-crypto-class + , cardano-ledger-binary , cardano-ledger-conway , cardano-ledger-core , cardano-ledger-shelley , cardano-node , cardano-prelude + , cardano-rpc , cardano-slotting , cardano-strict-containers ^>= 0.1 , cardano-testnet @@ -272,6 +278,7 @@ test-suite cardano-testnet-test , mtl , process , resourcet + , rio , regex-compat , rio , tasty ^>= 1.5 diff --git a/cardano-testnet/src/Cardano/Testnet.hs b/cardano-testnet/src/Cardano/Testnet.hs index a5c88dee0e6..ec467f46ae9 100644 --- a/cardano-testnet/src/Cardano/Testnet.hs +++ b/cardano-testnet/src/Cardano/Testnet.hs @@ -46,6 +46,7 @@ module Cardano.Testnet ( TestnetNode(..), isTestnetNodeSpo, nodeSocketPath, + nodeRpcSocketPath, ) where import Testnet.Components.Query diff --git a/cardano-testnet/src/Parsers/Cardano.hs b/cardano-testnet/src/Parsers/Cardano.hs index 19291a95e33..0bb62a13b0c 100644 --- a/cardano-testnet/src/Parsers/Cardano.hs +++ b/cardano-testnet/src/Parsers/Cardano.hs @@ -7,6 +7,12 @@ module Parsers.Cardano import Cardano.Api (AnyShelleyBasedEra (..)) +import Cardano.Api (AnyShelleyBasedEra(..)) +import Cardano.CLI.EraBased.Common.Option (bounded, command') +import Cardano.Api (AnyShelleyBasedEra (..), EraInEon (..), prettyShow) + +import Cardano.CLI.Environment +import Cardano.CLI.EraBased.Common.Option (bounded, command') import Cardano.CLI.EraBased.Common.Option hiding (pNetworkId) import Prelude @@ -50,10 +56,10 @@ pCardanoTestnetCliOptions = CardanoTestnetOptions <*> pure (AnyShelleyBasedEra defaultEra) <*> pMaxLovelaceSupply <*> OA.option (OA.eitherReader readNodeLoggingFormat) - ( OA.long "nodeLoggingFormat" + ( OA.long "node-logging-format" <> OA.help "Node logging format (json|text)" <> OA.metavar "LOGGING_FORMAT" - <> OA.showDefault + <> OA.showDefaultWith prettyShow <> OA.value (cardanoNodeLoggingFormat def) ) <*> OA.option OA.auto @@ -63,7 +69,7 @@ pCardanoTestnetCliOptions = CardanoTestnetOptions <> OA.showDefault <> OA.value 3 ) - <*> OA.flag False True + <*> OA.switch ( OA.long "enable-new-epoch-state-logging" <> OA.help "Enable new epoch state logging to logs/ledger-epoch-state.log" <> OA.showDefault @@ -73,6 +79,15 @@ pCardanoTestnetCliOptions = CardanoTestnetOptions <> OA.help "Directory where to store files, sockets, and so on. It is created if it doesn't exist. If unset, a temporary directory is used." <> OA.metavar "DIRECTORY" ))) + <*> OA.switch + ( OA.long "enable-grpc" + <> OA.help "[EXPERIMENTAL] Enable gRPC endpoint on all of testnet nodes. The listening socket file will be the same directory as node's N2C socket." + <> OA.showDefault + ) + where + pAnyShelleyBasedEra' :: Parser AnyShelleyBasedEra + pAnyShelleyBasedEra' = + pAnyShelleyBasedEra envCli <&> (\(EraInEon x) -> AnyShelleyBasedEra x) pTestnetNodeOptions :: Parser [NodeOption] pTestnetNodeOptions = diff --git a/cardano-testnet/src/Testnet/Runtime.hs b/cardano-testnet/src/Testnet/Runtime.hs index 1ef96d658e3..8431209fa2d 100644 --- a/cardano-testnet/src/Testnet/Runtime.hs +++ b/cardano-testnet/src/Testnet/Runtime.hs @@ -146,7 +146,7 @@ startNode tp node ipv4 port _testnetMagic nodeCmd = GHC.withFrozenCallStack $ do left MaxSprocketLengthExceededError let socketAbsPath = H.sprocketSystemName sprocket - completeNodeCmd = nodeCmd ++ + completeNodeCmd = nodeCmd <> [ "--socket-path", H.sprocketArgumentName sprocket , "--port", show port , "--host-addr", showIpv4Address ipv4 diff --git a/cardano-testnet/src/Testnet/Start/Cardano.hs b/cardano-testnet/src/Testnet/Start/Cardano.hs index 32ad32e1747..e02c57ed515 100644 --- a/cardano-testnet/src/Testnet/Start/Cardano.hs +++ b/cardano-testnet/src/Testnet/Start/Cardano.hs @@ -229,6 +229,7 @@ cardanoTestnet let CardanoTestnetOptions { cardanoEnableNewEpochStateLogging=enableNewEpochStateLogging , cardanoNodes + , cardanoEnableRpc } = testnetOptions nPools = cardanoNumPools testnetOptions nodeConfigFile = tmpAbsPath "configuration.yaml" @@ -339,7 +340,7 @@ cardanoTestnet ] <> spoNodeCliArgs <> extraCliArgs nodeOptions - + <> ["--grpc-enable" | cardanoEnableRpc] pure $ eRuntime <&> \rt -> rt{poolKeys=mKeys} let (failedNodes, testnetNodes') = partitionEithers eTestnetNodes diff --git a/cardano-testnet/src/Testnet/Start/Types.hs b/cardano-testnet/src/Testnet/Start/Types.hs index aa615c453fd..82c3722d293 100644 --- a/cardano-testnet/src/Testnet/Start/Types.hs +++ b/cardano-testnet/src/Testnet/Start/Types.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingVia #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} @@ -176,6 +177,8 @@ data CardanoTestnetOptions = CardanoTestnetOptions , cardanoNumDReps :: NumDReps -- ^ The number of DReps to generate at creation , cardanoEnableNewEpochStateLogging :: Bool -- ^ if epoch state logging is enabled , cardanoOutputDir :: UserProvidedEnv -- ^ The output directory where to store files, sockets, and so on. If unset, a temporary directory is used. + , cardanoEnableRpc :: Bool + -- ^ True to enable gRPC endpoints in all testnet nodes } deriving (Eq, Show) -- | Path to the configuration file of the node, specified by the user @@ -211,6 +214,7 @@ instance Default CardanoTestnetOptions where , cardanoNumDReps = 3 , cardanoEnableNewEpochStateLogging = True , cardanoOutputDir = def + , cardanoEnableRpc = False } -- | Options that are implemented by writing fields in the Shelley genesis file. @@ -261,7 +265,15 @@ cardanoDefaultTestnetNodeOptions = , RelayNodeOptions [] ] -data NodeLoggingFormat = NodeLoggingFormatAsJson | NodeLoggingFormatAsText deriving (Eq, Show) +data NodeLoggingFormat + = NodeLoggingFormatAsJson + | NodeLoggingFormatAsText + deriving (Eq, Show) + +instance Pretty NodeLoggingFormat where + pretty = \case + NodeLoggingFormatAsJson -> "json" + NodeLoggingFormatAsText -> "text" data NodeConfiguration diff --git a/cardano-testnet/src/Testnet/Types.hs b/cardano-testnet/src/Testnet/Types.hs index 700be30a088..a5d67aa861b 100644 --- a/cardano-testnet/src/Testnet/Types.hs +++ b/cardano-testnet/src/Testnet/Types.hs @@ -21,6 +21,7 @@ module Testnet.Types , testnetSprockets , TestnetNode(..) , nodeSocketPath + , nodeRpcSocketPath , nodeConnectionInfo , isTestnetNodeSpo , SpoNodeKeys(..) @@ -52,6 +53,7 @@ import Cardano.Crypto.ProtocolMagic (RequiresNetworkMagic (..)) import Cardano.Node.Configuration.POM import qualified Cardano.Node.Protocol.Byron as Byron import Cardano.Node.Types +import Cardano.Rpc.Server.Config (nodeSocketPathToRpcSocketPath) import Prelude @@ -149,6 +151,10 @@ isTestnetNodeSpo = isJust . poolKeys nodeSocketPath :: TestnetNode -> SocketPath nodeSocketPath = File . H.sprocketSystemName . nodeSprocket +-- | Provide a default RPC socket path +nodeRpcSocketPath :: TestnetNode -> SocketPath +nodeRpcSocketPath = nodeSocketPathToRpcSocketPath . nodeSocketPath + -- | Connection data for a node in the testnet nodeConnectionInfo :: MonadTest m => TestnetRuntime diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs new file mode 100644 index 00000000000..a4d5e4930e4 --- /dev/null +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs @@ -0,0 +1,182 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + +module Cardano.Testnet.Test.Rpc.Query + ( hprop_rpc_query_pparams + ) +where + +import Cardano.Api +import qualified Cardano.Api.Ledger as L + +import Cardano.CLI.Type.Output (QueryTipLocalStateOutput (..)) +import qualified Cardano.Ledger.Api as L +import qualified Cardano.Ledger.Binary.Version as L +import qualified Cardano.Ledger.Conway.Core as L +import qualified Cardano.Ledger.Conway.PParams as L +import qualified Cardano.Ledger.Plutus as L +import qualified Cardano.Rpc.Client as Rpc +import qualified Cardano.Rpc.Proto.Api.UtxoRpc.Query as UtxoRpc +import Cardano.Rpc.Server.Internal.UtxoRpc.Query () +import Cardano.Testnet + +import Prelude + +import qualified Data.ByteString.Short as SBS +import Data.Default.Class +import qualified Data.Map.Strict as M +import Lens.Micro + +import Testnet.Components.Query +import Testnet.Process.Run +import Testnet.Property.Util (integrationRetryWorkspace) +import Testnet.Start.Types + +import Hedgehog +import qualified Hedgehog as H +import qualified Hedgehog.Extras.Test.Base as H +import qualified Hedgehog.Extras.Test.TestWatchdog as H + +hprop_rpc_query_pparams :: Property +hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tempAbsBasePath' -> H.runWithDefaultWatchdog_ $ do + conf@Conf{tempAbsPath} <- mkConf tempAbsBasePath' + let tempAbsPath' = unTmpAbsPath tempAbsPath + + let ceo = ConwayEraOnwardsConway + sbe = convert ceo + eraName = eraToString sbe + options = def{cardanoNodeEra = AnyShelleyBasedEra sbe, cardanoEnableRpc = True} + + TestnetRuntime + { testnetMagic + , configurationFile + , testnetNodes = node0@TestnetNode{nodeSprocket} : _ + } <- + createAndRunTestnet options def conf + + execConfig <- mkExecConfig tempAbsPath' nodeSprocket testnetMagic + epochStateView <- getEpochStateView configurationFile (nodeSocketPath node0) + pparams <- unLedgerProtocolParameters <$> getProtocolParams epochStateView ceo + -- H.noteShowPretty_ pparams + utxos <- findAllUtxos epochStateView sbe + H.noteShowPretty_ utxos + rpcSocket <- H.note . unFile $ nodeRpcSocketPath node0 + + ---------- + -- Get tip + ---------- + QueryTipLocalStateOutput{localStateChainTip} <- + H.noteShowM $ execCliStdoutToJson execConfig [eraName, "query", "tip"] + (slot, blockHash, blockNo) <- case localStateChainTip of + ChainTipAtGenesis -> H.failure + ChainTip (SlotNo slot) (HeaderHash hash) (BlockNo blockNo) -> pure (slot, SBS.fromShort hash, blockNo) + + -------------- + -- RPC queries + -------------- + let rpcServer = Rpc.ServerUnix rpcSocket + (pparamsResponse, utxosResponse) <- H.noteShowM . H.evalIO . Rpc.withConnection def rpcServer $ \conn -> do + pparams' <- do + let req = Rpc.defMessage + Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readParams")) req + + utxos' <- do + let req = Rpc.defMessage + Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) req + pure (pparams', utxos') + + ------------------------ + -- Test readParams response + ------------------------ + pparamsResponse ^. #ledgerTip . #slot === slot + pparamsResponse ^. #ledgerTip . #hash === blockHash + pparamsResponse ^. #ledgerTip . #height === blockNo + pparamsResponse ^. #ledgerTip . #timestamp === 0 -- not possible to implement at this moment + + -- https://docs.cardano.org/about-cardano/explore-more/parameter-guide + let chainParams = pparamsResponse ^. #values . #cardano + babbageEraOnwardsConstraints (convert ceo) $ do + pparams ^. L.ppCoinsPerUTxOByteL . to L.unCoinPerByte . to L.unCoin + === chainParams ^. #coinsPerUtxoByte . to fromIntegral + pparams ^. L.ppMaxTxSizeL === chainParams ^. #maxTxSize . to fromIntegral + pparams ^. L.ppMinFeeBL === chainParams ^. #minFeeCoefficient . to fromIntegral + pparams ^. L.ppMinFeeAL === chainParams ^. #minFeeConstant . to fromIntegral + pparams ^. L.ppMaxBBSizeL === chainParams ^. #maxBlockBodySize . to fromIntegral + pparams ^. L.ppMaxBHSizeL === chainParams ^. #maxBlockHeaderSize . to fromIntegral + pparams ^. L.ppKeyDepositL === chainParams ^. #stakeKeyDeposit . to fromIntegral + pparams ^. L.ppPoolDepositL === chainParams ^. #poolDeposit . to fromIntegral + pparams ^. L.ppEMaxL . to L.unEpochInterval === chainParams ^. #poolRetirementEpochBound . to fromIntegral + pparams ^. L.ppNOptL === chainParams ^. #desiredNumberOfPools . to fromIntegral + pparams ^. L.ppA0L . to L.unboundRational === chainParams ^. #poolInfluence . to inject + pparams ^. L.ppNOptL === chainParams ^. #desiredNumberOfPools . to fromIntegral + pparams ^. L.ppRhoL . to L.unboundRational === chainParams ^. #monetaryExpansion . to inject + pparams ^. L.ppMinPoolCostL === chainParams ^. #minPoolCost . to fromIntegral + ( pparams ^. L.ppProtocolVersionL . to L.pvMajor . to L.getVersion + , pparams ^. L.ppProtocolVersionL . to L.pvMinor + ) + === ( chainParams ^. #protocolVersion . #major + , chainParams ^. #protocolVersion . #minor . to fromIntegral + ) + pparams ^. L.ppMaxValSizeL === chainParams ^. #maxValueSize . to fromIntegral + pparams ^. L.ppCollateralPercentageL === chainParams ^. #collateralPercentage . to fromIntegral + pparams ^. L.ppMaxCollateralInputsL === chainParams ^. #maxCollateralInputs . to fromIntegral + let pparamsCostModels = L.getCostModelParams <$> pparams ^. L.ppCostModelsL . to L.costModelsValid + M.lookup L.PlutusV1 pparamsCostModels === chainParams ^. #costModels . #plutusV1 . #values . to Just + M.lookup L.PlutusV2 pparamsCostModels === chainParams ^. #costModels . #plutusV2 . #values . to Just + M.lookup L.PlutusV3 pparamsCostModels === chainParams ^. #costModels . #plutusV3 . #values . to Just + pparams ^. L.ppPricesL . to L.prSteps . to L.unboundRational === chainParams ^. #prices . #steps . to inject + pparams ^. L.ppPricesL . to L.prMem . to L.unboundRational === chainParams ^. #prices . #memory . to inject + pparams ^. L.ppMaxTxExUnitsL === chainParams ^. #maxExecutionUnitsPerTransaction . to inject + pparams ^. L.ppMaxBlockExUnitsL === chainParams ^. #maxExecutionUnitsPerBlock . to inject + pparams ^. L.ppMinFeeRefScriptCostPerByteL . to L.unboundRational + === chainParams ^. #minFeeScriptRefCostPerByte . to inject + let poolVotingThresholds :: L.PoolVotingThresholds = + conwayEraOnwardsConstraints ceo $ + pparams ^. L.ppPoolVotingThresholdsL + ( L.unboundRational + <$> [ poolVotingThresholds ^. L.pvtMotionNoConfidenceL + , poolVotingThresholds ^. L.pvtCommitteeNormalL + , poolVotingThresholds ^. L.pvtCommitteeNoConfidenceL + , poolVotingThresholds ^. L.pvtHardForkInitiationL + , poolVotingThresholds ^. L.pvtPPSecurityGroupL + ] + ) + === chainParams ^. #poolVotingThresholds . #thresholds . to (map inject) + let drepVotingThresholds :: L.DRepVotingThresholds = + conwayEraOnwardsConstraints ceo $ + pparams ^. L.ppDRepVotingThresholdsL + ( L.unboundRational + <$> [ drepVotingThresholds ^. L.dvtMotionNoConfidenceL + , drepVotingThresholds ^. L.dvtCommitteeNormalL + , drepVotingThresholds ^. L.dvtCommitteeNoConfidenceL + , drepVotingThresholds ^. L.dvtUpdateToConstitutionL + , drepVotingThresholds ^. L.dvtHardForkInitiationL + , drepVotingThresholds ^. L.dvtPPNetworkGroupL + , drepVotingThresholds ^. L.dvtPPEconomicGroupL + , drepVotingThresholds ^. L.dvtPPTechnicalGroupL + , drepVotingThresholds ^. L.dvtPPGovGroupL + , drepVotingThresholds ^. L.dvtTreasuryWithdrawalL + ] + ) + === chainParams ^. #drepVotingThresholds . #thresholds . to (map inject) + pparams ^. L.ppCommitteeMinSizeL === chainParams ^. #minCommitteeSize . to fromIntegral + pparams ^. L.ppCommitteeMaxTermLengthL . to L.unEpochInterval + === chainParams ^. #committeeTermLimit . to fromIntegral + pparams ^. L.ppGovActionLifetimeL . to L.unEpochInterval + === chainParams ^. #governanceActionValidityPeriod . to fromIntegral + pparams ^. L.ppGovActionDepositL === chainParams ^. #governanceActionDeposit . to fromIntegral + pparams ^. L.ppDRepDepositL === chainParams ^. #drepDeposit . to fromIntegral + pparams ^. L.ppDRepActivityL . to L.unEpochInterval === chainParams ^. #drepInactivityPeriod . to fromIntegral + + -------------------------- + -- Test readUtxos response + -------------------------- + + _ <- H.noteShowPretty $ utxos + _ <- H.noteShowPretty $ utxosResponse + H.failure diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs new file mode 100644 index 00000000000..eef09eb9418 --- /dev/null +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs @@ -0,0 +1,158 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} + +module Cardano.Testnet.Test.Rpc.Transaction + ( hprop_rpc_transaction + ) +where + +import Cardano.Api +import qualified Cardano.Api.Ledger as L + +import Cardano.Rpc.Client (Proto) +import qualified Cardano.Rpc.Client as Rpc +import qualified Cardano.Rpc.Proto.Api.UtxoRpc.Query as UtxoRpc +import qualified Cardano.Rpc.Proto.Api.UtxoRpc.Submit as UtxoRpc +import Cardano.Rpc.Server.Internal.UtxoRpc.Query () +import Cardano.Rpc.Server.Internal.UtxoRpc.Type +import Cardano.Testnet + +import Prelude + +import Control.Monad +import Control.Monad.Fix +import Data.Default.Class +import qualified Data.Text.Encoding as T +import GHC.Stack +import Lens.Micro + +import Testnet.Property.Util (integrationRetryWorkspace) +import Testnet.Types + +import Hedgehog +import qualified Hedgehog as H +import qualified Hedgehog.Extras.Test.Base as H +import qualified Hedgehog.Extras.Test.TestWatchdog as H + +import RIO (threadDelay) + +hprop_rpc_transaction :: Property +hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' -> H.runWithDefaultWatchdog_ $ do + conf <- mkConf tempAbsBasePath' + let (ceo, eraProxy) = + (conwayBasedEra, asType) :: era ~ ConwayEra => (ConwayEraOnwards era, AsType era) + sbe = convert ceo + options = def{cardanoNodeEra = AnyShelleyBasedEra sbe, cardanoEnableRpc = True} + + TestnetRuntime + { testnetNodes = node0 : _ + , wallets = wallet0@(PaymentKeyInfo _ addrTxt0) : (PaymentKeyInfo _ addrTxt1) : _ + } <- + createAndRunTestnet options def conf + + rpcSocket <- H.note . unFile $ nodeRpcSocketPath node0 + + -- prepare tx inputs and output address + H.noteShow_ addrTxt0 + addr0 <- H.nothingFail $ deserialiseAddress (AsAddressInEra eraProxy) addrTxt0 + + H.noteShow_ addrTxt1 + addr1 <- H.nothingFail $ deserialiseAddress (AsAddressInEra eraProxy) addrTxt1 + + -- read key witnesses + wit0 :: ShelleyWitnessSigningKey <- + H.leftFailM . H.evalIO $ + readFileTextEnvelopeAnyOf + [FromSomeType asType WitnessGenesisUTxOKey] + (signingKey $ paymentKeyInfoPair wallet0) + + -------------- + -- RPC queries + -------------- + let rpcServer = Rpc.ServerUnix rpcSocket + (pparamsResponse, utxosResponse) <- H.noteShowM . H.evalIO . Rpc.withConnection def rpcServer $ \conn -> do + pparams' <- do + let req = def + Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readParams")) req + + utxos' <- do + let req = def & #addresses . #items .~ [T.encodeUtf8 addrTxt0] + Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) req + pure (pparams', utxos') + + pparams <- H.leftFail $ utxoRpcPParamsToProtocolParams (convert ceo) $ pparamsResponse ^. #values . #cardano + + txOut0 : _ <- H.noteShow $ utxosResponse ^. #items + txIn0 <- txoRefToTxIn $ txOut0 ^. #txoRef + + let outputCoin = txOut0 ^. #cardano . #coin . to fromIntegral + amount = 200_000_000 + fee = 500 + change = outputCoin - amount - fee + txOut = TxOut addr1 (lovelaceToTxOutValue sbe $ L.Coin amount) TxOutDatumNone ReferenceScriptNone + changeTxOut = TxOut addr0 (lovelaceToTxOutValue sbe $ L.Coin change) TxOutDatumNone ReferenceScriptNone + content = + defaultTxBodyContent sbe + & setTxIns [(txIn0, pure $ KeyWitness KeyWitnessForSpending)] + & setTxFee (TxFeeExplicit sbe 500) + & setTxOuts [txOut, changeTxOut] + & setTxProtocolParams (pure . pure $ LedgerProtocolParameters pparams) + + txBody <- H.leftFail $ createTransactionBody sbe content + + let signedTx = signShelleyTransaction sbe txBody [wit0] + txId' <- H.noteShow . getTxId $ getTxBody signedTx + + H.noteShowPretty_ utxosResponse + + (utxos, submitResponse) <- H.noteShowM . H.evalIO . Rpc.withConnection def rpcServer $ \conn -> do + submitResponse <- + Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.SubmitService "submitTx")) $ + def & #tx .~ [def & #raw .~ serialiseToCBOR signedTx] + + fix $ \loop -> do + resp <- Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readParams")) def + + let previousBlockNo = pparamsResponse ^. #ledgerTip . #height + currentBlockNo = resp ^. #ledgerTip . #height + -- wait for 2 blocks + when (previousBlockNo + 1 >= currentBlockNo) $ do + threadDelay 500_000 + loop + + utxos <- + Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) $ + def & #addresses . #items .~ [T.encodeUtf8 addrTxt1] + pure (utxos, submitResponse) + + submittedTxIds <- forM (submitResponse ^. #results) $ \res -> do + let mErr = res ^. #maybe'errorMessage + mTxId = res ^. #maybe'ref + case (mErr, mTxId) of + (Just err, Nothing) -> H.noteShow_ err >> H.failure + (Nothing, Just txId'') -> + H.leftFail $ deserialiseFromRawBytes AsTxId txId'' + _ -> do + H.note_ $ "Protocol error: " <> show res + H.failure + + H.note_ "Ensure that submitted transaction ID is in the submitted transactions list" + [txId'] === submittedTxIds + + H.note_ $ "Enxure that there are 2 UTXOs in the address " <> show addrTxt1 + 2 === length (utxos ^. #items) + + let outputsAmounts = map (^. #cardano . #coin) $ utxos ^. #items + H.note_ $ "Ensure that the output sent is one of the utxos for the address " <> show addrTxt1 + H.assertWith outputsAmounts $ elem (fromIntegral amount) + +txoRefToTxIn :: (HasCallStack, MonadTest m) => Proto UtxoRpc.TxoRef -> m TxIn +txoRefToTxIn r = withFrozenCallStack $ do + txId' <- H.leftFail $ deserialiseFromRawBytes AsTxId $ r ^. #hash + pure $ TxIn txId' (TxIx . fromIntegral $ r ^. #index) diff --git a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs index a142563b4ae..e6b2f7bb838 100644 --- a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs +++ b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs @@ -29,6 +29,8 @@ import qualified Cardano.Testnet.Test.Gov.TreasuryDonation as Gov import qualified Cardano.Testnet.Test.Gov.TreasuryWithdrawal as Gov import qualified Cardano.Testnet.Test.MainnetParams import qualified Cardano.Testnet.Test.Node.Shutdown +import qualified Cardano.Testnet.Test.Rpc.Query +import qualified Cardano.Testnet.Test.Rpc.Transaction import qualified Cardano.Testnet.Test.RunTestnet import qualified Cardano.Testnet.Test.SanityCheck as LedgerEvents import qualified Cardano.Testnet.Test.SubmitApi.Transaction @@ -124,6 +126,10 @@ tests = do , T.testGroup "SubmitApi" [ ignoreOnMacAndWindows "transaction" Cardano.Testnet.Test.SubmitApi.Transaction.hprop_transaction ] + , T.testGroup "RPC" + [ ignoreOnWindows "RPC Query Protocol Params" Cardano.Testnet.Test.Rpc.Query.hprop_rpc_query_pparams + , ignoreOnWindows "RPC Transaction Submit" Cardano.Testnet.Test.Rpc.Transaction.hprop_rpc_transaction + ] ] main :: IO () diff --git a/nix/haskell.nix b/nix/haskell.nix index 15650924eff..f3ad2f91428 100644 --- a/nix/haskell.nix +++ b/nix/haskell.nix @@ -308,7 +308,11 @@ let # also needs them to be quoted) export WORKDIR=$TMP/testTracerExt ''; - }) + }) + ({pkgs, ...}: { + packages.proto-lens-protobuf-types.components.library.build-tools = [ pkgs.protobuf ]; + packages.cardano-rpc.components.library.build-tools = [ pkgs.protobuf ]; + }) ({ lib, pkgs, ... }: lib.mkIf (!pkgs.stdenv.hostPlatform.isDarwin) { # Needed for profiled builds to fix an issue loading recursion-schemes part of makeBaseFunctor # that is missing from the `_p` output. See https://gitlab.haskell.org/ghc/ghc/-/issues/18320 From c4dd06da1957717c75180b8903a5d782fe740056 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Mon, 28 Jul 2025 13:40:43 +0200 Subject: [PATCH 04/23] update deps --- cabal.project | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cabal.project b/cabal.project index 8c6dbfbe0c2..a85c56b1be1 100644 --- a/cabal.project +++ b/cabal.project @@ -84,9 +84,9 @@ if impl (ghc >= 9.12) source-repository-package type: git location: https://github.com/intersectmbo/cardano-api - -- mgalazyn/feature/add-utxorpc-protocol-parameters-query - tag: 87b2afaaa037b78d1bed92257413fa9fdf2f156c - --sha256: sha256-VZv++ErHQZoG/OokIoQKGfqpKAcdm7dXPD8BzXKAkxU= + -- master @ Fri Jul 25 18:47:11 2025 + tag: 4a6ce60b0028e3062d666980574aebf6acfee9b3 + --sha256: sha256-N+eiMtiKvNyzSa9uMZjdGGhYwEpXvI0oXH5GOvp5WyE= subdir: cardano-api cardano-api-gen cardano-rpc @@ -94,7 +94,7 @@ source-repository-package source-repository-package type: git location: https://github.com/intersectmbo/cardano-cli - -- mgalazyn/chore/remove-upstreamed-instances - tag: 386b22edd41820584857537203ca39caca61d158 - --sha256: sha256-wsP5leh9EIgmG0wVLuaPizHFylvS8LGt7D2erLZbs5s= + -- master @ Fri Jul 18 14:45:44 2025 + tag: 5dc410f1ec0b234a572e4470c3801fbac783bbeb + --sha256: sha256-f3RviGvNoDu5WX79BgAzPo9bL6PsNBqm+Qt1UlJGe5E= subdir: cardano-cli From 7cda57b525d5bc7508427c3d657fb597556e9dc1 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 30 Jul 2025 15:08:36 +0200 Subject: [PATCH 05/23] Add scripts for starting testnet and funding an address --- scripts-demo/fund.sh | 11 +++++++++++ scripts-demo/start-testnet.sh | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100755 scripts-demo/fund.sh create mode 100755 scripts-demo/start-testnet.sh diff --git a/scripts-demo/fund.sh b/scripts-demo/fund.sh new file mode 100755 index 00000000000..6fd80d16df0 --- /dev/null +++ b/scripts-demo/fund.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Example usage: +# ./scripts-demo/fund.sh addr_test1vq922scgdwrrfa3n2pzu3empkju9ekregg0tza0xnveya3gfl0ycn 1000000000 +export CARDANO_NODE_SOCKET_PATH=./testnet-data/socket/node1/sock +export CARDANO_NODE_NETWORK_ID=42 +SRC_ADDR=$(cat testnet-data/utxo-keys/utxo1/utxo.addr) +SRC_UTXO=$(cabal run cardano-cli -- latest query utxo --address $SRC_ADDR | jq -r 'keys'[0]) +cabal run cardano-cli -- latest transaction build --tx-in $SRC_UTXO --tx-out $1+$2 --change-address $SRC_ADDR --out-file funding.txbody +cabal run cardano-cli -- latest transaction sign --tx-body-file funding.txbody --signing-key-file testnet-data/utxo-keys/utxo1/utxo.skey --out-file funding.tx +cabal run cardano-cli -- latest transaction submit --tx-file funding.tx +rm -f funding.txbody funding.tx diff --git a/scripts-demo/start-testnet.sh b/scripts-demo/start-testnet.sh new file mode 100755 index 00000000000..b889902cfad --- /dev/null +++ b/scripts-demo/start-testnet.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +rm -fr ./testnet-data +rm -fr ../cardano-api/rpc.socket +cabal build cardano-node cardano-cli cardano-testnet +( + tries=0 + while [ $tries -lt 60 ]; do + if [ -S ./testnet-data/socket/node1/rpc.sock ]; then + break + fi + sleep 1 + tries=$((tries + 1)) + done + + if [ $tries -eq 60 ]; then + echo "Timeout: Socket not found in 60 seconds." >&2 + exit 1 + fi + + ln -sf ./testnet-data/socket/node1/rpc.sock ../cardano-api/rpc.socket +) & cabal run cardano-testnet -- cardano --testnet-magic 42 --enable-grpc --output-dir ./testnet-data From 9070468dbe5e89690d322865208d027051fb9d7d Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Fri, 1 Aug 2025 11:26:02 +0200 Subject: [PATCH 06/23] Fix funding script --- scripts-demo/fund.sh | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts-demo/fund.sh b/scripts-demo/fund.sh index 6fd80d16df0..e5f956e32b6 100755 --- a/scripts-demo/fund.sh +++ b/scripts-demo/fund.sh @@ -1,11 +1,20 @@ #!/usr/bin/env bash # Example usage: # ./scripts-demo/fund.sh addr_test1vq922scgdwrrfa3n2pzu3empkju9ekregg0tza0xnveya3gfl0ycn 1000000000 + +set -euo pipefail + +trap 'rm -f fundint.txbody' EXIT +rm -f funding.txbody funding.tx + +run_cardano_cli() { + cabal run -v0 cardano-cli -- "$@" +} + export CARDANO_NODE_SOCKET_PATH=./testnet-data/socket/node1/sock export CARDANO_NODE_NETWORK_ID=42 SRC_ADDR=$(cat testnet-data/utxo-keys/utxo1/utxo.addr) -SRC_UTXO=$(cabal run cardano-cli -- latest query utxo --address $SRC_ADDR | jq -r 'keys'[0]) -cabal run cardano-cli -- latest transaction build --tx-in $SRC_UTXO --tx-out $1+$2 --change-address $SRC_ADDR --out-file funding.txbody -cabal run cardano-cli -- latest transaction sign --tx-body-file funding.txbody --signing-key-file testnet-data/utxo-keys/utxo1/utxo.skey --out-file funding.tx -cabal run cardano-cli -- latest transaction submit --tx-file funding.tx -rm -f funding.txbody funding.tx +SRC_UTXO=$(run_cardano-cli latest query utxo --address "$SRC_ADDR" | jq -r 'keys[0]') +run_cardano-cli latest transaction build --tx-in "$SRC_UTXO" --tx-out "$1+$2" --change-address "$SRC_ADDR" --out-file funding.txbody +run_cardano-cli latest transaction sign --tx-body-file funding.txbody --signing-key-file testnet-data/utxo-keys/utxo1/utxo.skey --out-file funding.tx +run_cardano-cli latest transaction submit --tx-file funding.tx From c237c61b541d5938e4c8acb6c087ab685c07de63 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Sun, 3 Aug 2025 18:12:37 +0200 Subject: [PATCH 07/23] Update deps and fix fund script --- cabal.project | 4 ++-- scripts-demo/fund.sh | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cabal.project b/cabal.project index a85c56b1be1..2e3f2153d89 100644 --- a/cabal.project +++ b/cabal.project @@ -85,8 +85,8 @@ source-repository-package type: git location: https://github.com/intersectmbo/cardano-api -- master @ Fri Jul 25 18:47:11 2025 - tag: 4a6ce60b0028e3062d666980574aebf6acfee9b3 - --sha256: sha256-N+eiMtiKvNyzSa9uMZjdGGhYwEpXvI0oXH5GOvp5WyE= + tag: 1765e37a04a8ed3dd7455e0434c1b2b09fa609a5 + --sha256: sha256-XkoP1eBoyQGjmq1n3Oma0OJgxRos5oW4QCQ8HSfAexo= subdir: cardano-api cardano-api-gen cardano-rpc diff --git a/scripts-demo/fund.sh b/scripts-demo/fund.sh index e5f956e32b6..733a52ec0bb 100755 --- a/scripts-demo/fund.sh +++ b/scripts-demo/fund.sh @@ -14,7 +14,7 @@ run_cardano_cli() { export CARDANO_NODE_SOCKET_PATH=./testnet-data/socket/node1/sock export CARDANO_NODE_NETWORK_ID=42 SRC_ADDR=$(cat testnet-data/utxo-keys/utxo1/utxo.addr) -SRC_UTXO=$(run_cardano-cli latest query utxo --address "$SRC_ADDR" | jq -r 'keys[0]') -run_cardano-cli latest transaction build --tx-in "$SRC_UTXO" --tx-out "$1+$2" --change-address "$SRC_ADDR" --out-file funding.txbody -run_cardano-cli latest transaction sign --tx-body-file funding.txbody --signing-key-file testnet-data/utxo-keys/utxo1/utxo.skey --out-file funding.tx -run_cardano-cli latest transaction submit --tx-file funding.tx +SRC_UTXO=$(run_cardano_cli latest query utxo --address "$SRC_ADDR" | jq -r 'keys[0]') +run_cardano_cli latest transaction build --tx-in "$SRC_UTXO" --tx-out "$1+$2" --change-address "$SRC_ADDR" --out-file funding.txbody +run_cardano_cli latest transaction sign --tx-body-file funding.txbody --signing-key-file testnet-data/utxo-keys/utxo1/utxo.skey --out-file funding.tx +run_cardano_cli latest transaction submit --tx-file funding.tx From ea5e231dcc63d3e9b4c0c96909a1aacf28ab4679 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Mon, 4 Aug 2025 01:43:23 +0200 Subject: [PATCH 08/23] Ensure files are deleted after funding --- scripts-demo/fund.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts-demo/fund.sh b/scripts-demo/fund.sh index 733a52ec0bb..cf06e62bcab 100755 --- a/scripts-demo/fund.sh +++ b/scripts-demo/fund.sh @@ -4,7 +4,7 @@ set -euo pipefail -trap 'rm -f fundint.txbody' EXIT +trap 'rm -f funding.txbody' EXIT rm -f funding.txbody funding.tx run_cardano_cli() { @@ -18,3 +18,4 @@ SRC_UTXO=$(run_cardano_cli latest query utxo --address "$SRC_ADDR" | jq -r 'keys run_cardano_cli latest transaction build --tx-in "$SRC_UTXO" --tx-out "$1+$2" --change-address "$SRC_ADDR" --out-file funding.txbody run_cardano_cli latest transaction sign --tx-body-file funding.txbody --signing-key-file testnet-data/utxo-keys/utxo1/utxo.skey --out-file funding.tx run_cardano_cli latest transaction submit --tx-file funding.tx +rm -f funding.txbody funding.tx From 4016bdef396d84f2f790f6bb4213478f1b1a0a21 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Fri, 5 Sep 2025 18:21:18 +0200 Subject: [PATCH 09/23] Update for cardano-api#945 --- cardano-testnet/src/Parsers/Cardano.hs | 8 ++------ .../Cardano/Testnet/Test/Rpc/Query.hs | 1 + .../Cardano/Testnet/Test/Rpc/Transaction.hs | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/cardano-testnet/src/Parsers/Cardano.hs b/cardano-testnet/src/Parsers/Cardano.hs index 0bb62a13b0c..24bb845a8a2 100644 --- a/cardano-testnet/src/Parsers/Cardano.hs +++ b/cardano-testnet/src/Parsers/Cardano.hs @@ -10,9 +10,9 @@ import Cardano.Api (AnyShelleyBasedEra (..)) import Cardano.Api (AnyShelleyBasedEra(..)) import Cardano.CLI.EraBased.Common.Option (bounded, command') import Cardano.Api (AnyShelleyBasedEra (..), EraInEon (..), prettyShow) +import Cardano.Api.Era (AnyShelleyBasedEra (..)) +import Cardano.Api.Pretty -import Cardano.CLI.Environment -import Cardano.CLI.EraBased.Common.Option (bounded, command') import Cardano.CLI.EraBased.Common.Option hiding (pNetworkId) import Prelude @@ -84,10 +84,6 @@ pCardanoTestnetCliOptions = CardanoTestnetOptions <> OA.help "[EXPERIMENTAL] Enable gRPC endpoint on all of testnet nodes. The listening socket file will be the same directory as node's N2C socket." <> OA.showDefault ) - where - pAnyShelleyBasedEra' :: Parser AnyShelleyBasedEra - pAnyShelleyBasedEra' = - pAnyShelleyBasedEra envCli <&> (\(EraInEon x) -> AnyShelleyBasedEra x) pTestnetNodeOptions :: Parser [NodeOption] pTestnetNodeOptions = diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs index a4d5e4930e4..588a93b13fb 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs @@ -129,6 +129,7 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem M.lookup L.PlutusV1 pparamsCostModels === chainParams ^. #costModels . #plutusV1 . #values . to Just M.lookup L.PlutusV2 pparamsCostModels === chainParams ^. #costModels . #plutusV2 . #values . to Just M.lookup L.PlutusV3 pparamsCostModels === chainParams ^. #costModels . #plutusV3 . #values . to Just + M.lookup L.PlutusV4 pparamsCostModels === chainParams ^. #costModels . #plutusV4 . #values . to Just pparams ^. L.ppPricesL . to L.prSteps . to L.unboundRational === chainParams ^. #prices . #steps . to inject pparams ^. L.ppPricesL . to L.prMem . to L.unboundRational === chainParams ^. #prices . #memory . to inject pparams ^. L.ppMaxTxExUnitsL === chainParams ^. #maxExecutionUnitsPerTransaction . to inject diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs index eef09eb9418..61fe119418c 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs @@ -82,7 +82,7 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readParams")) req utxos' <- do - let req = def & #addresses . #items .~ [T.encodeUtf8 addrTxt0] + let req = def & #cardanoAddresses . #items .~ [T.encodeUtf8 addrTxt0] Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) req pure (pparams', utxos') @@ -128,7 +128,7 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' utxos <- Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) $ - def & #addresses . #items .~ [T.encodeUtf8 addrTxt1] + def & #cardanoAddresses . #items .~ [T.encodeUtf8 addrTxt1] pure (utxos, submitResponse) submittedTxIds <- forM (submitResponse ^. #results) $ \res -> do From eecf6e725732422f7eae702928df540c30843d02 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Wed, 19 Nov 2025 16:47:41 +0100 Subject: [PATCH 10/23] wip --- .../Cardano/Testnet/Test/Rpc/Query.hs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs index 588a93b13fb..337cb261f85 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs @@ -23,6 +23,7 @@ import qualified Cardano.Ledger.Plutus as L import qualified Cardano.Rpc.Client as Rpc import qualified Cardano.Rpc.Proto.Api.UtxoRpc.Query as UtxoRpc import Cardano.Rpc.Server.Internal.UtxoRpc.Query () +import Cardano.Rpc.Server.Internal.UtxoRpc.Type (anyUtxoDataUtxoRpcToUtxo) import Cardano.Testnet import Prelude @@ -126,10 +127,11 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem pparams ^. L.ppCollateralPercentageL === chainParams ^. #collateralPercentage . to fromIntegral pparams ^. L.ppMaxCollateralInputsL === chainParams ^. #maxCollateralInputs . to fromIntegral let pparamsCostModels = L.getCostModelParams <$> pparams ^. L.ppCostModelsL . to L.costModelsValid - M.lookup L.PlutusV1 pparamsCostModels === chainParams ^. #costModels . #plutusV1 . #values . to Just - M.lookup L.PlutusV2 pparamsCostModels === chainParams ^. #costModels . #plutusV2 . #values . to Just - M.lookup L.PlutusV3 pparamsCostModels === chainParams ^. #costModels . #plutusV3 . #values . to Just - M.lookup L.PlutusV4 pparamsCostModels === chainParams ^. #costModels . #plutusV4 . #values . to Just + wrapInMaybe v = if v == mempty then Nothing else Just v + M.lookup L.PlutusV1 pparamsCostModels === chainParams ^. #costModels . #plutusV1 . #values . to wrapInMaybe + M.lookup L.PlutusV2 pparamsCostModels === chainParams ^. #costModels . #plutusV2 . #values . to wrapInMaybe + M.lookup L.PlutusV3 pparamsCostModels === chainParams ^. #costModels . #plutusV3 . #values . to wrapInMaybe + M.lookup L.PlutusV4 pparamsCostModels === chainParams ^. #costModels . #plutusV4 . #values . to wrapInMaybe pparams ^. L.ppPricesL . to L.prSteps . to L.unboundRational === chainParams ^. #prices . #steps . to inject pparams ^. L.ppPricesL . to L.prMem . to L.unboundRational === chainParams ^. #prices . #memory . to inject pparams ^. L.ppMaxTxExUnitsL === chainParams ^. #maxExecutionUnitsPerTransaction . to inject @@ -178,6 +180,7 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem -- Test readUtxos response -------------------------- - _ <- H.noteShowPretty $ utxos - _ <- H.noteShowPretty $ utxosResponse + utxoFromUtxoRpc <- H.leftFail $ utxosResponse ^. #items . to (anyUtxoDataUtxoRpcToUtxo $ convert ceo) + utxos === utxoFromUtxoRpc + H.failure From 2681e3a8b8890edb3b23dc9a565650ee240aa8cf Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Thu, 27 Nov 2025 19:29:18 +0100 Subject: [PATCH 11/23] expose prometheus metrics in testnet --- cardano-testnet/src/Testnet/Defaults.hs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/cardano-testnet/src/Testnet/Defaults.hs b/cardano-testnet/src/Testnet/Defaults.hs index b84c3508ead..b50c27d238d 100644 --- a/cardano-testnet/src/Testnet/Defaults.hs +++ b/cardano-testnet/src/Testnet/Defaults.hs @@ -85,9 +85,11 @@ import qualified Data.Aeson.Key as Aeson import qualified Data.Aeson.KeyMap as Aeson import Data.Bifunctor (bimap) import qualified Data.Default.Class as DefaultClass +import Data.IORef import Data.Proxy import Data.Ratio import Data.Scientific +import Data.String import Data.Text (Text) import qualified Data.Text as Text import Data.Time (UTCTime) @@ -95,6 +97,7 @@ import Data.Word (Word64) import Lens.Micro import Numeric.Natural import System.FilePath (()) +import System.IO.Unsafe import Test.Cardano.Ledger.Core.Rational import Testnet.Start.Types @@ -174,6 +177,11 @@ defaultConwayGenesis = do , cgInitialDReps = mempty } +-- TODO REMOVE +nCounter :: IORef Int +nCounter = unsafePerformIO $ newIORef 0 +{-# NOINLINE nCounter #-} + -- | The only era supported by cardano-testnet for the moment. -- It's important to keep the era parameterization everywhere, for ease of development -- when new eras roll out. @@ -186,7 +194,7 @@ defaultYamlHardforkViaConfig :: ShelleyBasedEra era -> Aeson.KeyMap Aeson.Value defaultYamlHardforkViaConfig sbe = defaultYamlConfig <> tracers - <> [("TraceOptions", Aeson.Object mempty)] + <> [("TraceOptions", traceOptions)] <> protocolVersions sbe <> hardforkViaConfig sbe where @@ -300,6 +308,19 @@ defaultYamlHardforkViaConfig sbe = , (proxyName (Proxy @TraceTxSubmissionProtocol), False) ] + traceOptions = do + let n = unsafePerformIO . atomicModifyIORef nCounter $ \n' -> (n'+1, n') + Aeson.object + [ "" .= Aeson.object + [ "backends" .= Aeson.Array + [ "EKGBackend" + , "Forwarder" + , fromString $ "PrometheusSimple suffix 0.0.0.0 " <> show (12798 + n - 1) + , "Stdout HumanFormatColoured" + ] + ] + ] + defaultYamlConfig :: Aeson.KeyMap Aeson.Value defaultYamlConfig = Aeson.fromList From a7bb2938cdbee68bd668c32432c27966a39bf3d0 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Fri, 28 Nov 2025 21:39:12 +0100 Subject: [PATCH 12/23] undo ioref bullshit --- cardano-testnet/src/Testnet/Defaults.hs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cardano-testnet/src/Testnet/Defaults.hs b/cardano-testnet/src/Testnet/Defaults.hs index b50c27d238d..403e7d39fcd 100644 --- a/cardano-testnet/src/Testnet/Defaults.hs +++ b/cardano-testnet/src/Testnet/Defaults.hs @@ -177,11 +177,6 @@ defaultConwayGenesis = do , cgInitialDReps = mempty } --- TODO REMOVE -nCounter :: IORef Int -nCounter = unsafePerformIO $ newIORef 0 -{-# NOINLINE nCounter #-} - -- | The only era supported by cardano-testnet for the moment. -- It's important to keep the era parameterization everywhere, for ease of development -- when new eras roll out. @@ -309,13 +304,12 @@ defaultYamlHardforkViaConfig sbe = ] traceOptions = do - let n = unsafePerformIO . atomicModifyIORef nCounter $ \n' -> (n'+1, n') Aeson.object [ "" .= Aeson.object [ "backends" .= Aeson.Array [ "EKGBackend" , "Forwarder" - , fromString $ "PrometheusSimple suffix 0.0.0.0 " <> show (12798 + n - 1) + , fromString $ "PrometheusSimple suffix 0.0.0.0 12798" , "Stdout HumanFormatColoured" ] ] From d682e0bc9c8be7013d830cabdc1f39eac5d75c49 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Thu, 8 Jan 2026 13:39:29 +0100 Subject: [PATCH 13/23] REMOVEME: cardano-testnet: disabled 2 relays and inducing failure in rpc query test --- cardano-testnet/src/Testnet/Defaults.hs | 6 +++--- cardano-testnet/src/Testnet/Start/Types.hs | 13 +++++++------ .../Cardano/Testnet/Test/Rpc/Query.hs | 2 ++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/cardano-testnet/src/Testnet/Defaults.hs b/cardano-testnet/src/Testnet/Defaults.hs index 403e7d39fcd..966e6c8d947 100644 --- a/cardano-testnet/src/Testnet/Defaults.hs +++ b/cardano-testnet/src/Testnet/Defaults.hs @@ -111,13 +111,13 @@ newtype AlonzoGenesisError = AlonzoGenErrTooMuchPrecision Rational deriving Show -instance Exception AlonzoGenesisError where +instance Exception AlonzoGenesisError where displayException = Api.docToString . Api.prettyError defaultAlonzoGenesis :: Either AlonzoGenesisError AlonzoGenesis defaultAlonzoGenesis = do - let genesis = Api.alonzoGenesisDefaults + let genesis = Api.alonzoGenesisDefaults prices = Ledger.agPrices genesis -- double check that prices have correct values - they're set using unsafeBoundedRational in cardano-api @@ -309,7 +309,7 @@ defaultYamlHardforkViaConfig sbe = [ "backends" .= Aeson.Array [ "EKGBackend" , "Forwarder" - , fromString $ "PrometheusSimple suffix 0.0.0.0 12798" + , fromString $ "PrometheusSimple suffix 127.0.0.1 12798" , "Stdout HumanFormatColoured" ] ] diff --git a/cardano-testnet/src/Testnet/Start/Types.hs b/cardano-testnet/src/Testnet/Start/Types.hs index 82c3722d293..f3cf30085e6 100644 --- a/cardano-testnet/src/Testnet/Start/Types.hs +++ b/cardano-testnet/src/Testnet/Start/Types.hs @@ -261,8 +261,9 @@ isRelayNodeOptions RelayNodeOptions{} = True cardanoDefaultTestnetNodeOptions :: [NodeOption] cardanoDefaultTestnetNodeOptions = [ SpoNodeOptions [] - , RelayNodeOptions [] - , RelayNodeOptions [] + -- TODO: uncomment relays, because they were causing conflicts for prometheus ports + -- , RelayNodeOptions [] + -- , RelayNodeOptions [] ] data NodeLoggingFormat @@ -288,7 +289,7 @@ data Conf = Conf , updateTimestamps :: UpdateTimestamps } deriving (Eq, Show) --- | Same as mkConfig except that it renders the path +-- | Same as mkConfig except that it renders the path -- when failing in a property test. mkConf :: (HasCallStack, MonadTest m) => FilePath -> m Conf mkConf tempAbsPath' = withFrozenCallStack $ do @@ -298,7 +299,7 @@ mkConf tempAbsPath' = withFrozenCallStack $ do -- | Create a 'Conf' from a temporary absolute path, with Genesis Hashes enabled -- and updating time stamps disabled. mkConfig :: FilePath -> Conf -mkConfig tempAbsPath' = +mkConfig tempAbsPath' = Conf { genesisHashesPolicy = WithHashes , tempAbsPath = TmpAbsolutePath (addTrailingPathSeparator tempAbsPath') @@ -308,10 +309,10 @@ mkConfig tempAbsPath' = -- | Create a 'Conf' from an absolute path, with Genesis Hashes enabled -- and updating time stamps disabled. mkConfigAbs :: FilePath -> IO Conf -mkConfigAbs userOutputDir = do +mkConfigAbs userOutputDir = do absUserOutputDir <- makeAbsolute userOutputDir dirExists <- doesDirectoryExist absUserOutputDir - let conf = mkConfig absUserOutputDir + let conf = mkConfig absUserOutputDir unless dirExists $ createDirectory absUserOutputDir pure conf diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs index 337cb261f85..19ab129d012 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs @@ -60,6 +60,8 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem } <- createAndRunTestnet options def conf + H.failure + execConfig <- mkExecConfig tempAbsPath' nodeSprocket testnetMagic epochStateView <- getEpochStateView configurationFile (nodeSocketPath node0) pparams <- unLedgerProtocolParameters <$> getProtocolParams epochStateView ceo From 5d1fc772c4f9acd5162cda166f207b42816f598c Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Fri, 9 Jan 2026 14:00:49 +0100 Subject: [PATCH 14/23] changs --- cardano-testnet/src/Testnet/Defaults.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cardano-testnet/src/Testnet/Defaults.hs b/cardano-testnet/src/Testnet/Defaults.hs index 966e6c8d947..06286cb41aa 100644 --- a/cardano-testnet/src/Testnet/Defaults.hs +++ b/cardano-testnet/src/Testnet/Defaults.hs @@ -190,6 +190,7 @@ defaultYamlHardforkViaConfig sbe = defaultYamlConfig <> tracers <> [("TraceOptions", traceOptions)] + -- <> [("TraceOptions", Aeson.Object mempty)] <> protocolVersions sbe <> hardforkViaConfig sbe where @@ -308,10 +309,11 @@ defaultYamlHardforkViaConfig sbe = [ "" .= Aeson.object [ "backends" .= Aeson.Array [ "EKGBackend" - , "Forwarder" - , fromString $ "PrometheusSimple suffix 127.0.0.1 12798" + , "PrometheusSimple suffix 0.0.0.0 12798" , "Stdout HumanFormatColoured" ] + , "detail" .= ("DNormal" :: Aeson.Value) + , "severity" .= ("Notice" :: Aeson.Value) ] ] From 55e1df464da6d163ac31672cc1ca19aa65f4a3f5 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Mon, 12 Jan 2026 22:30:29 +0100 Subject: [PATCH 15/23] move grpc initialisation --- cardano-node/src/Cardano/Node/Run.hs | 129 ++++++++---------- cardano-node/src/Cardano/Node/Tracing.hs | 5 +- .../src/Cardano/Node/Tracing/Tracers.hs | 4 + cardano-node/src/Cardano/Tracing/Tracers.hs | 17 ++- .../files/calculatePlutusScriptCost.json | 2 +- 5 files changed, 75 insertions(+), 82 deletions(-) diff --git a/cardano-node/src/Cardano/Node/Run.hs b/cardano-node/src/Cardano/Node/Run.hs index 80264252263..160dcea8286 100644 --- a/cardano-node/src/Cardano/Node/Run.hs +++ b/cardano-node/src/Cardano/Node/Run.hs @@ -184,12 +184,12 @@ runNode cmdPc = do Crypto.cryptoInit - let earlyTracer = stdoutTracer nc@NodeConfiguration { ncProtocolConfig , ncProtocolFiles=ncProtocolFiles@ProtocolFilepaths{shelleyVRFFile=mShelleyVrfFile} } <- buildNodeConfiguration cmdPc + let earlyTracer = stdoutTracer traceWith earlyTracer $ "Node configuration: " <> show nc forM_ mShelleyVrfFile $ @@ -203,11 +203,7 @@ runNode cmdPc = do -- don't need these. (Just ncProtocolFiles) - let ProtocolInfo{pInfoConfig} = fst $ Api.protocolInfo @IO runP - networkMagic :: Api.NetworkMagic = getNetworkMagic $ Consensus.configBlock pInfoConfig - -- TODO move initialisation somewhere else, so that the correct tracer is used, instead of stdout default one - withAsync (runRpcServer earlyTracer $ buildRpcConfiguration networkMagic cmdPc) $ \_ -> - handleNodeWithTracers cmdPc nc consensusProtocol + handleNodeWithTracers cmdPc nc consensusProtocol runThrowExceptT :: Exception e => ExceptT e IO a -> IO a runThrowExceptT act = runExceptT act >>= either Exception.throwIO pure @@ -223,15 +219,6 @@ buildNodeConfiguration partialConf = do pure $ makeNodeConfiguration (defaultPartialNodeConfiguration <> configYamlPc <> partialConf) --- | Build RPC configuration. Reads the configuration file again. Allows RPC server to dynamically reload configuration from disk again. -buildRpcConfiguration :: HasCallStack - => NetworkMagic - -> PartialNodeConfiguration - -> IO (RpcConfig, NetworkMagic) -buildRpcConfiguration networkMagic partialConf = do - NodeConfiguration{ncRpcConfig} <- buildNodeConfiguration partialConf - pure (ncRpcConfig, networkMagic) - -- | Workaround to ensure that the main thread throws an async exception on -- receiving a SIGTERM signal. installSigTermHandler :: IO () @@ -531,63 +518,65 @@ handleSimpleNode blockType runP tracers nc onKernel = do #endif nForkPolicy <- getForkPolicy $ ncResponderCoreAffinityPolicy nc cForkPolicy <- getForkPolicy $ ncResponderCoreAffinityPolicy nc - void $ - let diffusionNodeArguments :: Cardano.Diffusion.CardanoNodeArguments IO - diffusionNodeArguments = Cardano.Diffusion.CardanoNodeArguments { - Cardano.Diffusion.consensusMode = ncConsensusMode nc, - Cardano.Diffusion.genesisPeerTargets = - PeerSelectionTargets { - targetNumberOfRootPeers = ncSyncTargetOfRootPeers nc, - targetNumberOfKnownPeers = ncSyncTargetOfKnownPeers nc, - targetNumberOfEstablishedPeers = ncSyncTargetOfEstablishedPeers nc, - targetNumberOfActivePeers = ncSyncTargetOfActivePeers nc, - targetNumberOfKnownBigLedgerPeers = ncSyncTargetOfKnownBigLedgerPeers nc, - targetNumberOfEstablishedBigLedgerPeers = ncSyncTargetOfEstablishedBigLedgerPeers nc, - targetNumberOfActiveBigLedgerPeers = ncSyncTargetOfActiveBigLedgerPeers nc - }, - Cardano.Diffusion.minNumOfBigLedgerPeers = ncMinBigLedgerPeersForTrustedState nc, - Cardano.Diffusion.tracerChurnMode = churnModeTracer tracers - } + let diffusionNodeArguments :: Cardano.Diffusion.CardanoNodeArguments IO + diffusionNodeArguments = Cardano.Diffusion.CardanoNodeArguments { + Cardano.Diffusion.consensusMode = ncConsensusMode nc, + Cardano.Diffusion.genesisPeerTargets = + PeerSelectionTargets { + targetNumberOfRootPeers = ncSyncTargetOfRootPeers nc, + targetNumberOfKnownPeers = ncSyncTargetOfKnownPeers nc, + targetNumberOfEstablishedPeers = ncSyncTargetOfEstablishedPeers nc, + targetNumberOfActivePeers = ncSyncTargetOfActivePeers nc, + targetNumberOfKnownBigLedgerPeers = ncSyncTargetOfKnownBigLedgerPeers nc, + targetNumberOfEstablishedBigLedgerPeers = ncSyncTargetOfEstablishedBigLedgerPeers nc, + targetNumberOfActiveBigLedgerPeers = ncSyncTargetOfActiveBigLedgerPeers nc + }, + Cardano.Diffusion.minNumOfBigLedgerPeers = ncMinBigLedgerPeersForTrustedState nc, + Cardano.Diffusion.tracerChurnMode = churnModeTracer tracers + } - diffusionConfiguration :: Cardano.Diffusion.CardanoConfiguration IO - diffusionConfiguration = - mkDiffusionConfiguration - publicIPv4SocketOrAddr - publicIPv6SocketOrAddr - localSocketOrPath - publicPeerSelectionVar - nForkPolicy cForkPolicy - (readTVar localRootsVar) - (readTVar publicRootsVar) - (readTVar useLedgerVar) - (readTVar ledgerPeerSnapshotVar) - nc - in - Node.run - nodeArgs { - rnNodeKernelHook = \registry nodeKernel -> do - -- reinstall `SIGHUP` handler - installSigHUPHandler (startupTracer tracers) (Consensus.kesAgentTracer $ consensusTracers tracers) blockType nc nodeKernel - localRootsVar publicRootsVar useLedgerVar useBootstrapVar - ledgerPeerSnapshotPathVar ledgerPeerSnapshotVar - rnNodeKernelHook nodeArgs registry nodeKernel - } - StdRunNodeArgs - { srnBfcMaxConcurrencyBulkSync = unMaxConcurrencyBulkSync <$> ncMaxConcurrencyBulkSync nc - , srnBfcMaxConcurrencyDeadline = unMaxConcurrencyDeadline <$> ncMaxConcurrencyDeadline nc - , srnChainDbValidateOverride = ncValidateDB nc - , srnDatabasePath = dbPath - , srnDiffusionConfiguration = diffusionConfiguration - , srnDiffusionArguments = diffusionNodeArguments - , srnDiffusionTracers = diffusionTracers tracers - , srnEnableInDevelopmentVersions = ncExperimentalProtocolsEnabled nc - , srnTraceChainDB = chainDBTracer tracers - , srnMaybeMempoolCapacityOverride = ncMaybeMempoolCapacityOverride nc - , srnChainSyncIdleTimeout = customizeChainSyncTimeout - , srnSnapshotPolicyArgs = snapshotPolicyArgs - , srnQueryBatchSize = queryBatchSize - , srnLdbFlavorArgs = selectorToArgs ldbBackend + diffusionConfiguration :: Cardano.Diffusion.CardanoConfiguration IO + diffusionConfiguration = + mkDiffusionConfiguration + publicIPv4SocketOrAddr + publicIPv6SocketOrAddr + localSocketOrPath + publicPeerSelectionVar + nForkPolicy cForkPolicy + (readTVar localRootsVar) + (readTVar publicRootsVar) + (readTVar useLedgerVar) + (readTVar ledgerPeerSnapshotVar) + nc + + ProtocolInfo{pInfoConfig} = fst $ Api.protocolInfo @IO runP + networkMagic :: Api.NetworkMagic = getNetworkMagic $ Consensus.configBlock pInfoConfig + withAsync (runRpcServer earlyTracer (pure $ (ncRpcConfig, networkMagic))) $ \_ -> + Node.run + nodeArgs { + rnNodeKernelHook = \registry nodeKernel -> do + -- reinstall `SIGHUP` handler + installSigHUPHandler (startupTracer tracers) (Consensus.kesAgentTracer $ consensusTracers tracers) blockType nc nodeKernel + localRootsVar publicRootsVar useLedgerVar useBootstrapVar + ledgerPeerSnapshotPathVar ledgerPeerSnapshotVar + rnNodeKernelHook nodeArgs registry nodeKernel } + StdRunNodeArgs + { srnBfcMaxConcurrencyBulkSync = unMaxConcurrencyBulkSync <$> ncMaxConcurrencyBulkSync nc + , srnBfcMaxConcurrencyDeadline = unMaxConcurrencyDeadline <$> ncMaxConcurrencyDeadline nc + , srnChainDbValidateOverride = ncValidateDB nc + , srnDatabasePath = dbPath + , srnDiffusionConfiguration = diffusionConfiguration + , srnDiffusionArguments = diffusionNodeArguments + , srnDiffusionTracers = diffusionTracers tracers + , srnEnableInDevelopmentVersions = ncExperimentalProtocolsEnabled nc + , srnTraceChainDB = chainDBTracer tracers + , srnMaybeMempoolCapacityOverride = ncMaybeMempoolCapacityOverride nc + , srnChainSyncIdleTimeout = customizeChainSyncTimeout + , srnSnapshotPolicyArgs = snapshotPolicyArgs + , srnQueryBatchSize = queryBatchSize + , srnLdbFlavorArgs = selectorToArgs ldbBackend + } where customizeChainSyncTimeout :: ChainSyncIdleTimeout customizeChainSyncTimeout = case ncChainSyncIdleTimeout nc of diff --git a/cardano-node/src/Cardano/Node/Tracing.hs b/cardano-node/src/Cardano/Node/Tracing.hs index 2a751a58562..69546bd11c1 100644 --- a/cardano-node/src/Cardano/Node/Tracing.hs +++ b/cardano-node/src/Cardano/Node/Tracing.hs @@ -10,6 +10,7 @@ module Cardano.Node.Tracing ) where import Cardano.Logging.Resources +import qualified Cardano.Network.Diffusion as Cardano.Diffusion import Cardano.Node.Handlers.Shutdown (ShutdownTrace) import Cardano.Node.Startup (NodeInfo, NodeStartupInfo, StartupTrace (..)) import Cardano.Node.Tracing.StateRep (NodeState) @@ -22,9 +23,8 @@ import qualified Ouroboros.Consensus.Network.NodeToNode as NodeToNode import qualified Ouroboros.Consensus.Node.Tracers as Consensus import qualified Ouroboros.Consensus.Storage.ChainDB as ChainDB import Ouroboros.Network.ConnectionId -import qualified Cardano.Network.Diffusion as Cardano.Diffusion -import Prelude (IO) +import Prelude (IO, String) import Codec.CBOR.Read (DeserialiseFailure) import "contra-tracer" Control.Tracer (Tracer (..)) @@ -50,4 +50,5 @@ data Tracers peer localPeer blk m = Tracers , nodeStateTracer :: !(Tracer IO NodeState) , resourcesTracer :: !(Tracer IO ResourceStats) , ledgerMetricsTracer :: !(Tracer IO LedgerMetrics) + , rpcTracer :: !(Tracer IO String) } diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers.hs index 485d28e71f0..14add0440d4 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers.hs @@ -152,6 +152,9 @@ mkDispatchTracers nodeKernel trBase trForward mbTrEKG trDataPoint trConfig p = d !churnModeTr <- mkCardanoTracer trBase trForward mbTrEKG ["Net", "PeerSelection", "ChurnMode"] configureTracers configReflection trConfig [churnModeTr] + !rpcTr <- mkCardanoTracer trBase trForward mbTrEKG [] + configureTracers configReflection trConfig [rpcTr] + traceTracerInfo trBase trForward configReflection let warnings = checkNodeTraceConfiguration' trConfig @@ -183,6 +186,7 @@ mkDispatchTracers nodeKernel trBase trForward mbTrEKG trDataPoint trConfig p = d , nodeVersionTracer = Tracer (traceWith nodeVersionTr) , resourcesTracer = Tracer (traceWith resourcesTr) , ledgerMetricsTracer = Tracer (traceWith ledgerMetricsTr) + , rpcTracer = Tracer (traceWith rpcTr) } mkConsensusTracers :: forall blk. diff --git a/cardano-node/src/Cardano/Tracing/Tracers.hs b/cardano-node/src/Cardano/Tracing/Tracers.hs index 7aaf9b03cb4..290f6538cf5 100644 --- a/cardano-node/src/Cardano/Tracing/Tracers.hs +++ b/cardano-node/src/Cardano/Tracing/Tracers.hs @@ -35,6 +35,9 @@ import Cardano.BM.Data.Transformers import Cardano.BM.Internal.ElidingTracer import Cardano.BM.Trace (traceNamedObject) import Cardano.BM.Tracing +import Cardano.Network.Diffusion (CardanoPeerSelectionCounters) +import qualified Cardano.Network.Diffusion.Types as Cardano.Diffusion +import qualified Cardano.Network.PeerSelection.Governor.Types as Cardano import Cardano.Node.Configuration.Logging import Cardano.Node.Protocol.Byron () import Cardano.Node.Protocol.Shelley () @@ -44,7 +47,6 @@ import qualified Cardano.Node.STM as STM import Cardano.Node.TraceConstraints import Cardano.Node.Tracing import Cardano.Node.Tracing.Tracers.NodeVersion -import Cardano.Network.Diffusion (CardanoPeerSelectionCounters) import Cardano.Protocol.TPraos.OCert (KESPeriod (..)) import Cardano.Slotting.Slot (EpochNo (..), SlotNo (..), WithOrigin (..)) import Cardano.Tracing.Config @@ -64,8 +66,8 @@ import Ouroboros.Consensus.Ledger.Abstract (LedgerErr, LedgerState) import Ouroboros.Consensus.Ledger.Extended (ledgerState) import Ouroboros.Consensus.Ledger.Inspect (InspectLedger, LedgerEvent) import Ouroboros.Consensus.Ledger.Query (BlockQuery, Query) -import Ouroboros.Consensus.Ledger.SupportsMempool (ApplyTxErr, GenTx, GenTxId, HasTxs, - LedgerSupportsMempool, ByteSize32 (..)) +import Ouroboros.Consensus.Ledger.SupportsMempool (ApplyTxErr, ByteSize32 (..), GenTx, + GenTxId, HasTxs, LedgerSupportsMempool) import Ouroboros.Consensus.Ledger.SupportsProtocol (LedgerSupportsProtocol) import Ouroboros.Consensus.Mempool (MempoolSize (..), TraceEventMempool (..)) import Ouroboros.Consensus.MiniProtocol.BlockFetch.Server @@ -79,10 +81,6 @@ import qualified Ouroboros.Consensus.Protocol.Ledger.HotKey as HotKey import qualified Ouroboros.Consensus.Storage.ChainDB as ChainDB import qualified Ouroboros.Consensus.Storage.LedgerDB as LedgerDB import Ouroboros.Consensus.Util.Enclose - -import qualified Cardano.Network.Diffusion.Types as Cardano.Diffusion -import qualified Cardano.Network.PeerSelection.Governor.Types as Cardano - import qualified Ouroboros.Network.AnchoredFragment as AF import Ouroboros.Network.Block (BlockNo (..), ChainUpdate (..), HasHeader (..), Point, StandardHash, blockNo, pointSlot, unBlockNo) @@ -100,8 +98,7 @@ import Ouroboros.Network.InboundGovernor.State as InboundGovernor import Ouroboros.Network.NodeToClient (LocalAddress) import Ouroboros.Network.NodeToNode (RemoteAddress) import Ouroboros.Network.PeerSelection.Churn (ChurnCounters (..)) -import Ouroboros.Network.PeerSelection.Governor ( - PeerSelectionView (..)) +import Ouroboros.Network.PeerSelection.Governor (PeerSelectionView (..)) import qualified Ouroboros.Network.PeerSelection.Governor as Governor import Ouroboros.Network.Point (fromWithOrigin, withOrigin) import Ouroboros.Network.Protocol.LocalStateQuery.Type (LocalStateQuery, ShowQuery) @@ -360,6 +357,7 @@ mkTracers blockConfig tOpts@(TracingOnLegacy trSel) tr nodeKern ekgDirect = do , nodeStateTracer = nullTracer , resourcesTracer = nullTracer , ledgerMetricsTracer = nullTracer + , rpcTracer = nullTracer } where traceForgeEnabledMetric :: Maybe EKGDirect -> StartupTrace blk -> IO () @@ -535,6 +533,7 @@ mkTracers _ _ _ _ _ = , nodeVersionTracer = nullTracer , resourcesTracer = nullTracer , ledgerMetricsTracer = nullTracer + , rpcTracer = nullTracer } -------------------------------------------------------------------------------- diff --git a/cardano-testnet/test/cardano-testnet-test/files/calculatePlutusScriptCost.json b/cardano-testnet/test/cardano-testnet-test/files/calculatePlutusScriptCost.json index 98a7e4de9bd..b2d1ef63f2c 100644 --- a/cardano-testnet/test/cardano-testnet-test/files/calculatePlutusScriptCost.json +++ b/cardano-testnet/test/cardano-testnet-test/files/calculatePlutusScriptCost.json @@ -7,4 +7,4 @@ "lovelaceCost": 34, "scriptHash": "186e32faa80a26810392fda6d559c7ed4721a65ce1c9d4ef3e1c87b4" } -] \ No newline at end of file +] From 04f9b6d35f457406228fbeb71d459ccc390a14a2 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Tue, 13 Jan 2026 15:31:40 +0100 Subject: [PATCH 16/23] wip tracing --- cardano-node/cardano-node.cabal | 1 + cardano-node/src/Cardano/Node/Run.hs | 4 +-- cardano-node/src/Cardano/Node/Tracing.hs | 5 +-- .../src/Cardano/Node/Tracing/Tracers.hs | 3 +- .../src/Cardano/Node/Tracing/Tracers/Rpc.hs | 31 +++++++++++++++++++ cardano-node/src/Cardano/Tracing/Tracers.hs | 1 + 6 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs diff --git a/cardano-node/cardano-node.cabal b/cardano-node/cardano-node.cabal index 403f5412bdb..8482a0c026d 100644 --- a/cardano-node/cardano-node.cabal +++ b/cardano-node/cardano-node.cabal @@ -113,6 +113,7 @@ library Cardano.Node.Tracing.Tracers.NodeVersion Cardano.Node.Tracing.Tracers.P2P Cardano.Node.Tracing.Tracers.Resources + Cardano.Node.Tracing.Tracers.Rpc Cardano.Node.Tracing.Tracers.Shutdown Cardano.Node.Tracing.Tracers.Startup Cardano.Node.Types diff --git a/cardano-node/src/Cardano/Node/Run.hs b/cardano-node/src/Cardano/Node/Run.hs index 160dcea8286..34b04ab6f00 100644 --- a/cardano-node/src/Cardano/Node/Run.hs +++ b/cardano-node/src/Cardano/Node/Run.hs @@ -195,7 +195,7 @@ runNode cmdPc = do forM_ mShelleyVrfFile $ runThrowExceptT . checkVRFFilePermissions earlyTracer . File - consensusProtocol@(SomeConsensusProtocol _ runP) <- + consensusProtocol <- runThrowExceptT $ mkConsensusProtocol ncProtocolConfig @@ -551,7 +551,7 @@ handleSimpleNode blockType runP tracers nc onKernel = do ProtocolInfo{pInfoConfig} = fst $ Api.protocolInfo @IO runP networkMagic :: Api.NetworkMagic = getNetworkMagic $ Consensus.configBlock pInfoConfig - withAsync (runRpcServer earlyTracer (pure $ (ncRpcConfig, networkMagic))) $ \_ -> + withAsync (runRpcServer (rpcTracer tracers) (pure (ncRpcConfig nc, networkMagic))) $ \_ -> Node.run nodeArgs { rnNodeKernelHook = \registry nodeKernel -> do diff --git a/cardano-node/src/Cardano/Node/Tracing.hs b/cardano-node/src/Cardano/Node/Tracing.hs index 69546bd11c1..127a1e75ce0 100644 --- a/cardano-node/src/Cardano/Node/Tracing.hs +++ b/cardano-node/src/Cardano/Node/Tracing.hs @@ -18,13 +18,14 @@ import Cardano.Node.Tracing.Tracers.ConsensusStartupException (ConsensusStartupException (..)) import Cardano.Node.Tracing.Tracers.LedgerMetrics (LedgerMetrics) import Cardano.Node.Tracing.Tracers.NodeVersion (NodeVersionTrace) +import Cardano.Rpc.Server (TraceRpc) import qualified Ouroboros.Consensus.Network.NodeToClient as NodeToClient import qualified Ouroboros.Consensus.Network.NodeToNode as NodeToNode import qualified Ouroboros.Consensus.Node.Tracers as Consensus import qualified Ouroboros.Consensus.Storage.ChainDB as ChainDB import Ouroboros.Network.ConnectionId -import Prelude (IO, String) +import Prelude (IO) import Codec.CBOR.Read (DeserialiseFailure) import "contra-tracer" Control.Tracer (Tracer (..)) @@ -50,5 +51,5 @@ data Tracers peer localPeer blk m = Tracers , nodeStateTracer :: !(Tracer IO NodeState) , resourcesTracer :: !(Tracer IO ResourceStats) , ledgerMetricsTracer :: !(Tracer IO LedgerMetrics) - , rpcTracer :: !(Tracer IO String) + , rpcTracer :: !(Tracer IO TraceRpc) } diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers.hs index 14add0440d4..f7f8e7b21ab 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers.hs @@ -36,6 +36,7 @@ import Cardano.Node.Tracing.Tracers.NodeToClient () import Cardano.Node.Tracing.Tracers.NodeToNode () import Cardano.Node.Tracing.Tracers.NodeVersion (getNodeVersion) import Cardano.Node.Tracing.Tracers.P2P () +import Cardano.Node.Tracing.Tracers.Rpc () import Cardano.Node.Tracing.Tracers.Shutdown () import Cardano.Node.Tracing.Tracers.Startup () import Ouroboros.Consensus.Ledger.Inspect (LedgerEvent) @@ -152,7 +153,7 @@ mkDispatchTracers nodeKernel trBase trForward mbTrEKG trDataPoint trConfig p = d !churnModeTr <- mkCardanoTracer trBase trForward mbTrEKG ["Net", "PeerSelection", "ChurnMode"] configureTracers configReflection trConfig [churnModeTr] - !rpcTr <- mkCardanoTracer trBase trForward mbTrEKG [] + !rpcTr <- mkCardanoTracer trBase trForward mbTrEKG ["RPC"] configureTracers configReflection trConfig [rpcTr] traceTracerInfo trBase trForward configReflection diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs new file mode 100644 index 00000000000..6a181335a40 --- /dev/null +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs @@ -0,0 +1,31 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -Wno-orphans #-} + +module Cardano.Node.Tracing.Tracers.Rpc () where + +import Cardano.Logging +import Cardano.Rpc.Server (TraceRpc (..), TraceRpcSubmit (..)) + + +instance LogFormatting TraceRpc where + forMachine _dtal _ = undefined + +instance MetaTrace TraceRpc where + namespaceFor = undefined + + severityFor = undefined + + documentFor = undefined + + allNamespaces = undefined + + + + diff --git a/cardano-node/src/Cardano/Tracing/Tracers.hs b/cardano-node/src/Cardano/Tracing/Tracers.hs index 290f6538cf5..a96d0dac0c4 100644 --- a/cardano-node/src/Cardano/Tracing/Tracers.hs +++ b/cardano-node/src/Cardano/Tracing/Tracers.hs @@ -47,6 +47,7 @@ import qualified Cardano.Node.STM as STM import Cardano.Node.TraceConstraints import Cardano.Node.Tracing import Cardano.Node.Tracing.Tracers.NodeVersion +import Cardano.Node.Tracing.Tracers.Rpc () import Cardano.Protocol.TPraos.OCert (KESPeriod (..)) import Cardano.Slotting.Slot (EpochNo (..), SlotNo (..), WithOrigin (..)) import Cardano.Tracing.Config From c07d60c7578ebb19303484af339f4c1ec2d94f35 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Thu, 15 Jan 2026 13:39:35 +0100 Subject: [PATCH 17/23] added some tracers --- .../src/Cardano/Node/Tracing/Tracers/Rpc.hs | 87 +++++++++++++++++-- .../Cardano/Testnet/Test/Rpc/Query.hs | 2 - 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs index 6a181335a40..8ed041a682a 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs @@ -3,6 +3,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -10,22 +11,96 @@ module Cardano.Node.Tracing.Tracers.Rpc () where +import Cardano.Api.Pretty + import Cardano.Logging -import Cardano.Rpc.Server (TraceRpc (..), TraceRpcSubmit (..)) +import Cardano.Rpc.Server (TraceRpc (..), TraceRpcQuery (..), TraceRpcSubmit (..), + TraceSpanEvent (..)) + +import Data.Aeson (Object, ToJSON, ToJSONKey, Value (..), object, toJSON, toJSONList, + (.=)) instance LogFormatting TraceRpc where - forMachine _dtal _ = undefined + forMachine _dtal tr = mconcat $ + ( "reason" .= prettyShow tr ) : + case tr of + TraceRpcTestTrace _ -> [ "kind" .= String "TestTrace" ] + TraceRpcFatalError _ -> [ "kind" .= String "FatalError" ] + TraceRpcError _ -> [ "kind" .= String "Error" ] + + TraceRpcQuery queryTrace -> [ "kind" .= String "Query" ] + <> case queryTrace of + TraceRpcQueryParamsSpan s -> + [ "queryName" .= String "ProtocolParameters" + , spanToObject s + ] + + TraceRpcSubmit submitTrace -> [ "kind" .= String "Submit" ] + <> case submitTrace of + TraceRpcSubmitTxDecodingFailure i _ -> [ "txIndex" .= show i ] + TraceRpcSubmitN2cConnectionError _ -> [ ] + TraceRpcSubmitTxValidationError i _ -> [ "txIndex" .= show i ] + TraceRpcSubmitSpan s -> [spanToObject s] + + forHuman = docToText . pretty instance MetaTrace TraceRpc where - namespaceFor = undefined + namespaceFor = Namespace [] . \case + TraceRpcTestTrace _ -> ["TestTrace"] + TraceRpcFatalError _ -> ["FatalError"] + TraceRpcError _ -> ["Error"] + + TraceRpcQuery queryTrace -> + "Query" : + case queryTrace of + TraceRpcQueryParamsSpan _ -> ["ProtocolParameters", "Span"] + + TraceRpcSubmit submitTrace -> + "Submit" : + case submitTrace of + TraceRpcSubmitTxDecodingFailure _ _ -> ["TxDecodingFailure"] + TraceRpcSubmitN2cConnectionError _ -> ["N2cConnectionError"] + TraceRpcSubmitTxValidationError _ _ -> ["TxValidationError"] + TraceRpcSubmitSpan _ -> ["Span"] + + severityFor (Namespace _ ["TestTrace"]) _ = Just Critical + severityFor (Namespace _ ["FatalError"]) _ = Just Critical + severityFor (Namespace _ ["Error"]) _ = Just Error + severityFor (Namespace _ ["Query", "ProtocolParameters", "Span"]) _ = Just Warning + severityFor (Namespace _ ["Submit", "Span"]) _ = Just Warning + severityFor (Namespace _ ["Submit", "TxDecodingFailure"]) _ = Just Warning + severityFor (Namespace _ ["Submit", "N2cConnectionError"]) _ = Just Warning + severityFor (Namespace _ ["Submit", "TxValidationError"]) _ = Just Warning + severityFor _ _ = Nothing - severityFor = undefined + documentFor (Namespace _ ["TestTrace"]) = Just "" + documentFor (Namespace _ ["FatalError"]) = Just "" + documentFor (Namespace _ ["Error"]) = Just "" + documentFor (Namespace _ ["Query", "ProtocolParameters", "Span"]) = Just "" + documentFor (Namespace _ ["Submit", "Span"]) = Just "" + documentFor (Namespace _ ["Submit", "TxDecodingFailure"]) = Just "" + documentFor (Namespace _ ["Submit", "N2cConnectionError"]) = Just "" + documentFor (Namespace _ ["Submit", "TxValidationError"]) = Just "" + documentFor _ = Nothing - documentFor = undefined + allNamespaces = + [ Namespace [] ["TestTrace"] + , Namespace [] ["FatalError"] + , Namespace [] ["Error"] + , Namespace [] ["Query", "ProtocolParameters", "Span"] + , Namespace [] ["Submit", "Span"] + , Namespace [] ["Submit", "TxDecodingFailure"] + , Namespace [] ["Submit", "N2cConnectionError"] + , Namespace [] ["Submit", "TxValidationError"] + ] - allNamespaces = undefined +-- helper functions +spanToObject :: TraceSpanEvent -> Object +spanToObject = \case + SpanBegin -> "span" .= String "begin" + SpanEnd -> "span" .= String "end" diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs index 19ab129d012..337cb261f85 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs @@ -60,8 +60,6 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem } <- createAndRunTestnet options def conf - H.failure - execConfig <- mkExecConfig tempAbsPath' nodeSprocket testnetMagic epochStateView <- getEpochStateView configurationFile (nodeSocketPath node0) pparams <- unLedgerProtocolParameters <$> getProtocolParams epochStateView ceo From 676edf5644c7352cc2e037621a7bff16bb1772cd Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Fri, 16 Jan 2026 18:17:39 +0100 Subject: [PATCH 18/23] WIP --- cardano-node/cardano-node.cabal | 1 + cardano-node/src/Cardano/Node/Run.hs | 2 +- cardano-node/src/Cardano/Node/Tracing/API.hs | 2 ++ .../src/Cardano/Node/Tracing/Documentation.hs | 15 ++++++++++++--- .../src/Cardano/Node/Tracing/Tracers/Rpc.hs | 14 ++++++-------- .../Cardano/Testnet/Test/Rpc/Query.hs | 3 +++ 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/cardano-node/cardano-node.cabal b/cardano-node/cardano-node.cabal index 8482a0c026d..78c2a7bfc4b 100644 --- a/cardano-node/cardano-node.cabal +++ b/cardano-node/cardano-node.cabal @@ -164,6 +164,7 @@ library , deepseq , directory , dns + , ekg , ekg-wai , ekg-core , filepath diff --git a/cardano-node/src/Cardano/Node/Run.hs b/cardano-node/src/Cardano/Node/Run.hs index 34b04ab6f00..073345d6f6d 100644 --- a/cardano-node/src/Cardano/Node/Run.hs +++ b/cardano-node/src/Cardano/Node/Run.hs @@ -170,7 +170,7 @@ import System.Win32.File import Paths_cardano_node (version) import Paths_cardano_node (version) -import GHC.Stack +import GHC.Stack {- HLINT ignore "Fuse concatMap/map" -} {- HLINT ignore "Redundant <$>" -} diff --git a/cardano-node/src/Cardano/Node/Tracing/API.hs b/cardano-node/src/Cardano/Node/Tracing/API.hs index e33d1c88915..ec33fdfdbdc 100644 --- a/cardano-node/src/Cardano/Node/Tracing/API.hs +++ b/cardano-node/src/Cardano/Node/Tracing/API.hs @@ -49,6 +49,7 @@ import Data.Time.Clock (getCurrentTime) import Network.Mux.Trace (TraceLabelPeer (..)) import Network.Socket (HostName) import System.Metrics as EKG +import System.Remote.Monitoring import Trace.Forward.Forwarding (InitForwardingConfig (..), initForwardingDelayed) import Trace.Forward.Utils.TraceObject (writeToSink) @@ -109,6 +110,7 @@ initTraceDispatcher nc p networkMagic nodeKernel noBlockForging = do ) mkTracers trConfig = mdo ekgStore <- EKG.newStore + forkServerWith ekgStore "localhost" 1900 EKG.registerGcMetrics ekgStore ekgTrace <- ekgTracer trConfig ekgStore diff --git a/cardano-node/src/Cardano/Node/Tracing/Documentation.hs b/cardano-node/src/Cardano/Node/Tracing/Documentation.hs index 1658bed634d..76581ad0df5 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Documentation.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Documentation.hs @@ -24,6 +24,9 @@ import Cardano.Git.Rev (gitRev) import Cardano.Logging as Logging import Cardano.Logging.Resources import Cardano.Logging.Resources.Types () +import qualified Cardano.Network.PeerSelection.ExtraRootPeers as Cardano.PublicRootPeers +import qualified Cardano.Network.PeerSelection.Governor.PeerSelectionState as Cardano +import qualified Cardano.Network.PeerSelection.Governor.Types as Cardano import Cardano.Network.PeerSelection.PeerTrustable (PeerTrustable (..)) import Cardano.Node.Handlers.Shutdown (ShutdownTrace) import Cardano.Node.Startup @@ -45,11 +48,10 @@ import Cardano.Node.Tracing.Tracers.NodeToClient () import Cardano.Node.Tracing.Tracers.NodeToNode () import Cardano.Node.Tracing.Tracers.NodeVersion (NodeVersionTrace) import Cardano.Node.Tracing.Tracers.P2P () +import Cardano.Node.Tracing.Tracers.Rpc () import Cardano.Node.Tracing.Tracers.Shutdown () import Cardano.Node.Tracing.Tracers.Startup () -import qualified Cardano.Network.PeerSelection.Governor.PeerSelectionState as Cardano -import qualified Cardano.Network.PeerSelection.Governor.Types as Cardano -import qualified Cardano.Network.PeerSelection.ExtraRootPeers as Cardano.PublicRootPeers +import Cardano.Rpc.Server (TraceRpc) import Cardano.Tracing.OrphanInstances.Network () import Ouroboros.Consensus.Block.SupportsSanityCheck (SanityCheckIssue) import Ouroboros.Consensus.BlockchainTime.WallClock.Types (RelativeTime) @@ -700,6 +702,11 @@ docTracersFirstPhase condConfigFileName = do Logging.Trace IO TraceDispatcherMessage) + rpcTr <- mkCardanoTracer trBase trForward mbTrEKG ["RPC"] + configureTracers configReflection trConfig [rpcTr] + rpcTrDoc <- documentTracer (rpcTr :: Logging.Trace IO TraceRpc) + + let bl = nodeInfoDpDoc <> nodeStartupInfoDpDoc <> stateTrDoc @@ -767,6 +774,8 @@ docTracersFirstPhase condConfigFileName = do <> localServerTrDoc <> localInboundGovernorTrDoc <> dtAcceptPolicyTrDoc +-- gRPC + <> rpcTrDoc -- Internal tracer <> internalTrDoc diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs index 8ed041a682a..73d379d11b9 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs @@ -25,7 +25,6 @@ instance LogFormatting TraceRpc where forMachine _dtal tr = mconcat $ ( "reason" .= prettyShow tr ) : case tr of - TraceRpcTestTrace _ -> [ "kind" .= String "TestTrace" ] TraceRpcFatalError _ -> [ "kind" .= String "FatalError" ] TraceRpcError _ -> [ "kind" .= String "Error" ] @@ -45,9 +44,12 @@ instance LogFormatting TraceRpc where forHuman = docToText . pretty + asMetrics = \case + TraceRpcQuery (TraceRpcQueryParamsSpan SpanBegin) -> [CounterM "rpc.request.query.count" Nothing] + _ -> [] + instance MetaTrace TraceRpc where namespaceFor = Namespace [] . \case - TraceRpcTestTrace _ -> ["TestTrace"] TraceRpcFatalError _ -> ["FatalError"] TraceRpcError _ -> ["Error"] @@ -64,17 +66,15 @@ instance MetaTrace TraceRpc where TraceRpcSubmitTxValidationError _ _ -> ["TxValidationError"] TraceRpcSubmitSpan _ -> ["Span"] - severityFor (Namespace _ ["TestTrace"]) _ = Just Critical severityFor (Namespace _ ["FatalError"]) _ = Just Critical severityFor (Namespace _ ["Error"]) _ = Just Error - severityFor (Namespace _ ["Query", "ProtocolParameters", "Span"]) _ = Just Warning + severityFor (Namespace _ ["Query", "ProtocolParameters", "Span"]) _ = Just Info severityFor (Namespace _ ["Submit", "Span"]) _ = Just Warning severityFor (Namespace _ ["Submit", "TxDecodingFailure"]) _ = Just Warning severityFor (Namespace _ ["Submit", "N2cConnectionError"]) _ = Just Warning severityFor (Namespace _ ["Submit", "TxValidationError"]) _ = Just Warning severityFor _ _ = Nothing - documentFor (Namespace _ ["TestTrace"]) = Just "" documentFor (Namespace _ ["FatalError"]) = Just "" documentFor (Namespace _ ["Error"]) = Just "" documentFor (Namespace _ ["Query", "ProtocolParameters", "Span"]) = Just "" @@ -85,8 +85,7 @@ instance MetaTrace TraceRpc where documentFor _ = Nothing allNamespaces = - [ Namespace [] ["TestTrace"] - , Namespace [] ["FatalError"] + [ Namespace [] ["FatalError"] , Namespace [] ["Error"] , Namespace [] ["Query", "ProtocolParameters", "Span"] , Namespace [] ["Submit", "Span"] @@ -95,7 +94,6 @@ instance MetaTrace TraceRpc where , Namespace [] ["Submit", "TxValidationError"] ] - -- helper functions spanToObject :: TraceSpanEvent -> Object diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs index 337cb261f85..907f2b22255 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs @@ -41,6 +41,7 @@ import Testnet.Start.Types import Hedgehog import qualified Hedgehog as H import qualified Hedgehog.Extras.Test.Base as H +import qualified Hedgehog.Extras.Test.Concurrent as H import qualified Hedgehog.Extras.Test.TestWatchdog as H hprop_rpc_query_pparams :: Property @@ -183,4 +184,6 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem utxoFromUtxoRpc <- H.leftFail $ utxosResponse ^. #items . to (anyUtxoDataUtxoRpcToUtxo $ convert ceo) utxos === utxoFromUtxoRpc + H.threadDelay 90000000 + H.failure From 836b388c4feea7aeb89ab38def06bf581650afdd Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Mon, 19 Jan 2026 20:30:35 +0100 Subject: [PATCH 19/23] wip --- .../src/Cardano/Node/Tracing/Tracers/Rpc.hs | 154 ++++++++++-------- cardano-testnet/src/Testnet/Defaults.hs | 13 +- 2 files changed, 97 insertions(+), 70 deletions(-) diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs index 73d379d11b9..7dbd46a318e 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs @@ -20,85 +20,103 @@ import Cardano.Rpc.Server (TraceRpc (..), TraceRpcQuery (..), TraceRpc import Data.Aeson (Object, ToJSON, ToJSONKey, Value (..), object, toJSON, toJSONList, (.=)) - instance LogFormatting TraceRpc where - forMachine _dtal tr = mconcat $ - ( "reason" .= prettyShow tr ) : - case tr of - TraceRpcFatalError _ -> [ "kind" .= String "FatalError" ] - TraceRpcError _ -> [ "kind" .= String "Error" ] - - TraceRpcQuery queryTrace -> [ "kind" .= String "Query" ] - <> case queryTrace of - TraceRpcQueryParamsSpan s -> - [ "queryName" .= String "ProtocolParameters" - , spanToObject s - ] - - TraceRpcSubmit submitTrace -> [ "kind" .= String "Submit" ] - <> case submitTrace of - TraceRpcSubmitTxDecodingFailure i _ -> [ "txIndex" .= show i ] - TraceRpcSubmitN2cConnectionError _ -> [ ] - TraceRpcSubmitTxValidationError i _ -> [ "txIndex" .= show i ] - TraceRpcSubmitSpan s -> [spanToObject s] + forMachine _dtal tr = + mconcat $ + ("reason" .= prettyShow tr) + : case tr of + TraceRpcFatalError _ -> ["kind" .= String "FatalError"] + TraceRpcError _ -> ["kind" .= String "Error"] + TraceRpcQuery queryTrace -> + ["kind" .= String "QueryService"] + <> case queryTrace of + TraceRpcQueryParamsSpan s -> + [ "queryName" .= String "ReadParams" + , spanToObject s + ] + TraceRpcQueryReadUtxosSpan s -> + [ "queryName" .= String "ReadUtxos" + , spanToObject s + ] + TraceRpcSubmit submitTrace -> + ["kind" .= String "SubmitService"] + <> case submitTrace of + TraceRpcSubmitTxDecodingFailure i _ -> ["txIndex" .= show i] + TraceRpcSubmitN2cConnectionError _ -> [] + TraceRpcSubmitTxValidationError i _ -> ["txIndex" .= show i] + TraceRpcSubmitSpan s -> [spanToObject s] forHuman = docToText . pretty asMetrics = \case - TraceRpcQuery (TraceRpcQueryParamsSpan SpanBegin) -> [CounterM "rpc.request.query.count" Nothing] + -- query names here are taken from UTXORPC spec: https://utxorpc.org/query/intro/#operations + TraceRpcQuery (TraceRpcQueryParamsSpan (SpanBegin _)) -> [CounterM "rpc.request.QueryService.ReadParams" Nothing] + TraceRpcQuery (TraceRpcQueryReadUtxosSpan (SpanBegin _)) -> [CounterM "rpc.request.QueryService.ReadUtxos" Nothing] + TraceRpcSubmit (TraceRpcSubmitSpan (SpanBegin _)) -> [CounterM "rpc.request.SubmitService.SubmitTx" Nothing] _ -> [] instance MetaTrace TraceRpc where - namespaceFor = Namespace [] . \case - TraceRpcFatalError _ -> ["FatalError"] - TraceRpcError _ -> ["Error"] - - TraceRpcQuery queryTrace -> - "Query" : - case queryTrace of - TraceRpcQueryParamsSpan _ -> ["ProtocolParameters", "Span"] - - TraceRpcSubmit submitTrace -> - "Submit" : - case submitTrace of - TraceRpcSubmitTxDecodingFailure _ _ -> ["TxDecodingFailure"] - TraceRpcSubmitN2cConnectionError _ -> ["N2cConnectionError"] - TraceRpcSubmitTxValidationError _ _ -> ["TxValidationError"] - TraceRpcSubmitSpan _ -> ["Span"] - - severityFor (Namespace _ ["FatalError"]) _ = Just Critical - severityFor (Namespace _ ["Error"]) _ = Just Error - severityFor (Namespace _ ["Query", "ProtocolParameters", "Span"]) _ = Just Info - severityFor (Namespace _ ["Submit", "Span"]) _ = Just Warning - severityFor (Namespace _ ["Submit", "TxDecodingFailure"]) _ = Just Warning - severityFor (Namespace _ ["Submit", "N2cConnectionError"]) _ = Just Warning - severityFor (Namespace _ ["Submit", "TxValidationError"]) _ = Just Warning - severityFor _ _ = Nothing - - documentFor (Namespace _ ["FatalError"]) = Just "" - documentFor (Namespace _ ["Error"]) = Just "" - documentFor (Namespace _ ["Query", "ProtocolParameters", "Span"]) = Just "" - documentFor (Namespace _ ["Submit", "Span"]) = Just "" - documentFor (Namespace _ ["Submit", "TxDecodingFailure"]) = Just "" - documentFor (Namespace _ ["Submit", "N2cConnectionError"]) = Just "" - documentFor (Namespace _ ["Submit", "TxValidationError"]) = Just "" - documentFor _ = Nothing + namespaceFor = + Namespace [] . \case + TraceRpcFatalError _ -> ["FatalError"] + TraceRpcError _ -> ["Error"] + TraceRpcQuery queryTrace -> + "QueryService" + : case queryTrace of + TraceRpcQueryParamsSpan _ -> ["ReadParams", "Span"] + TraceRpcQueryReadUtxosSpan _ -> ["ReadUtxos", "Span"] + TraceRpcSubmit submitTrace -> + "SubmitService" + : case submitTrace of + TraceRpcSubmitTxDecodingFailure _ _ -> ["TxDecodingFailure"] + TraceRpcSubmitN2cConnectionError _ -> ["N2cConnectionError"] + TraceRpcSubmitTxValidationError _ _ -> ["TxValidationError"] + TraceRpcSubmitSpan _ -> ["Span"] + + severityFor (Namespace _ nsInner) _ = case nsInner of + ["FatalError"] -> Just Critical + ["Error"] -> Just Error + ["QueryService", "ReadParams", "Span"] -> Just Info + ["QueryService", "ReadUtxos", "Span"] -> Just Info + ["SubmitService", "SubmitTx", "Span"] -> Just Info + ["SubmitService", "N2cConnectionError"] -> Just Warning -- this is a more serious error, this shouldn't happen + ["SubmitService", "TxDecodingFailure"] -> Just Info -- request error + ["SubmitService", "TxValidationError"] -> Just Info -- request error + _ -> Nothing + + documentFor (Namespace _ nsInner) = case nsInner of + ["FatalError"] -> Just "" + ["Error"] -> Just "" + ["QueryService", "ReadParams", "Span"] -> Just "" + ["QueryService", "ReadUtxos", "Span"] -> Just "" + ["SubmitService", "SubmitTx", "Span"] -> Just "" + ["SubmitService", "N2cConnectionError"] -> Just "" + ["SubmitService", "TxDecodingFailure"] -> Just "" + ["SubmitService", "TxValidationError"] -> Just "" + _ -> Nothing + + metricsDocFor (Namespace _ nsInner) = case nsInner of + ["QueryService", "ReadParams", "Span"] -> [("rpc.request.QueryService.ReadParams", "")] + ["QueryService", "ReadUtxos", "Span"] -> [("rpc.request.QueryService.ReadUtxos", "")] + ["SubmitService", "SubmitTx", "Span"] -> [("rpc.request.SubmitService.SubmitTx", "")] + _ -> [] allNamespaces = - [ Namespace [] ["FatalError"] - , Namespace [] ["Error"] - , Namespace [] ["Query", "ProtocolParameters", "Span"] - , Namespace [] ["Submit", "Span"] - , Namespace [] ["Submit", "TxDecodingFailure"] - , Namespace [] ["Submit", "N2cConnectionError"] - , Namespace [] ["Submit", "TxValidationError"] - ] + Namespace [] + <$> [ ["FatalError"] + , ["Error"] + , ["QueryService", "ReadParams", "Span"] + , ["QueryService", "ReadParams", "Span"] + , ["SubmitService", "SubmitTx", "Span"] + , ["SubmitService", "TxDecodingFailure"] + , ["SubmitService", "N2cConnectionError"] + , ["SubmitService", "TxValidationError"] + ] -- helper functions spanToObject :: TraceSpanEvent -> Object -spanToObject = \case - SpanBegin -> "span" .= String "begin" - SpanEnd -> "span" .= String "end" - - +spanToObject = + mconcat . \case + SpanBegin spanId -> ["span" .= String "begin", "spanId" .= spanId] + SpanEnd spanId -> ["span" .= String "end", "spanId" .= spanId] diff --git a/cardano-testnet/src/Testnet/Defaults.hs b/cardano-testnet/src/Testnet/Defaults.hs index 06286cb41aa..d925a536f30 100644 --- a/cardano-testnet/src/Testnet/Defaults.hs +++ b/cardano-testnet/src/Testnet/Defaults.hs @@ -310,12 +310,21 @@ defaultYamlHardforkViaConfig sbe = [ "backends" .= Aeson.Array [ "EKGBackend" , "PrometheusSimple suffix 0.0.0.0 12798" - , "Stdout HumanFormatColoured" + , "Stdout MachineFormat" + -- , "Stdout HumanFormatColoured" ] , "detail" .= ("DNormal" :: Aeson.Value) - , "severity" .= ("Notice" :: Aeson.Value) + -- , "severity" .= ("Notice" :: Aeson.Value) + , "severity" .= ("Debug" :: Aeson.Value) ] ] + -- traceOptions = + -- emptyTraceConfig + -- { tcOptions = Data.Map.fromList + -- [([], [ ConfSeverity (SeverityF (Just Info)) + -- , ConfBackend [Stdout HumanFormatColoured, EKGBackend]]) + -- ] + -- } defaultYamlConfig :: Aeson.KeyMap Aeson.Value defaultYamlConfig = From b77453c549b84c8cd3f523b60eb5858d97473052 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Mon, 19 Jan 2026 20:30:44 +0100 Subject: [PATCH 20/23] wip --- cardano-node/src/Cardano/Node/Tracing/API.hs | 1 - cardano-node/src/Cardano/Node/Tracing/Documentation.hs | 2 -- cardano-testnet/src/Testnet/Defaults.hs | 5 +++-- cardano-testnet/src/Testnet/Ping.hs | 1 + 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cardano-node/src/Cardano/Node/Tracing/API.hs b/cardano-node/src/Cardano/Node/Tracing/API.hs index ec33fdfdbdc..5a5b2fddbf8 100644 --- a/cardano-node/src/Cardano/Node/Tracing/API.hs +++ b/cardano-node/src/Cardano/Node/Tracing/API.hs @@ -110,7 +110,6 @@ initTraceDispatcher nc p networkMagic nodeKernel noBlockForging = do ) mkTracers trConfig = mdo ekgStore <- EKG.newStore - forkServerWith ekgStore "localhost" 1900 EKG.registerGcMetrics ekgStore ekgTrace <- ekgTracer trConfig ekgStore diff --git a/cardano-node/src/Cardano/Node/Tracing/Documentation.hs b/cardano-node/src/Cardano/Node/Tracing/Documentation.hs index 76581ad0df5..d3e341754fa 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Documentation.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Documentation.hs @@ -701,12 +701,10 @@ docTracersFirstPhase condConfigFileName = do internalTrDoc <- documentTracer (internalTr :: Logging.Trace IO TraceDispatcherMessage) - rpcTr <- mkCardanoTracer trBase trForward mbTrEKG ["RPC"] configureTracers configReflection trConfig [rpcTr] rpcTrDoc <- documentTracer (rpcTr :: Logging.Trace IO TraceRpc) - let bl = nodeInfoDpDoc <> nodeStartupInfoDpDoc <> stateTrDoc diff --git a/cardano-testnet/src/Testnet/Defaults.hs b/cardano-testnet/src/Testnet/Defaults.hs index d925a536f30..579614f9a64 100644 --- a/cardano-testnet/src/Testnet/Defaults.hs +++ b/cardano-testnet/src/Testnet/Defaults.hs @@ -98,6 +98,7 @@ import Lens.Micro import Numeric.Natural import System.FilePath (()) import System.IO.Unsafe +import GHC.Exts (IsList(..)) import Test.Cardano.Ledger.Core.Rational import Testnet.Start.Types @@ -318,9 +319,9 @@ defaultYamlHardforkViaConfig sbe = , "severity" .= ("Debug" :: Aeson.Value) ] ] - -- traceOptions = + -- traceOptions = toJSON $ -- emptyTraceConfig - -- { tcOptions = Data.Map.fromList + -- { tcOptions = fromList -- [([], [ ConfSeverity (SeverityF (Just Info)) -- , ConfBackend [Stdout HumanFormatColoured, EKGBackend]]) -- ] diff --git a/cardano-testnet/src/Testnet/Ping.hs b/cardano-testnet/src/Testnet/Ping.hs index 92c550d02cf..b2d2824de09 100644 --- a/cardano-testnet/src/Testnet/Ping.hs +++ b/cardano-testnet/src/Testnet/Ping.hs @@ -42,6 +42,7 @@ import qualified Network.Mux.Types as Mux import Network.Socket (AddrInfo (..), PortNumber, StructLinger (..)) import qualified Network.Socket as Socket import Prettyprinter + import Testnet.Process.RunIO (liftIOAnnotated) import qualified Hedgehog.Extras.Stock.IO.Network.Socket as IO From a9f87666f816dbb590625eb19d258aa1f4861924 Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Wed, 28 Jan 2026 16:24:14 +0100 Subject: [PATCH 21/23] update --- bench/tx-generator/tx-generator.cabal | 2 +- cardano-node/cardano-node.cabal | 2 +- cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs | 8 -------- cardano-submit-api/cardano-submit-api.cabal | 2 +- cardano-testnet/cardano-testnet.cabal | 2 +- 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/bench/tx-generator/tx-generator.cabal b/bench/tx-generator/tx-generator.cabal index 19ef08baeec..718a2217ae0 100644 --- a/bench/tx-generator/tx-generator.cabal +++ b/bench/tx-generator/tx-generator.cabal @@ -111,7 +111,7 @@ library , attoparsec-aeson , base16-bytestring , bytestring - , cardano-api ^>= 10.20 + , cardano-api ^>= 10.21 , cardano-binary , cardano-cli ^>= 10.14 , cardano-crypto-class diff --git a/cardano-node/cardano-node.cabal b/cardano-node/cardano-node.cabal index 78c2a7bfc4b..7603ff78d17 100644 --- a/cardano-node/cardano-node.cabal +++ b/cardano-node/cardano-node.cabal @@ -139,7 +139,7 @@ library , async , base16-bytestring , bytestring - , cardano-api ^>= 10.20 + , cardano-api ^>= 10.21 , cardano-crypto-class ^>=2.2.3.2 , cardano-crypto-wrapper , cardano-git-rev ^>=0.2.2 diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs index 7dbd46a318e..7acd043300b 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs @@ -41,9 +41,7 @@ instance LogFormatting TraceRpc where TraceRpcSubmit submitTrace -> ["kind" .= String "SubmitService"] <> case submitTrace of - TraceRpcSubmitTxDecodingFailure i _ -> ["txIndex" .= show i] TraceRpcSubmitN2cConnectionError _ -> [] - TraceRpcSubmitTxValidationError i _ -> ["txIndex" .= show i] TraceRpcSubmitSpan s -> [spanToObject s] forHuman = docToText . pretty @@ -68,9 +66,7 @@ instance MetaTrace TraceRpc where TraceRpcSubmit submitTrace -> "SubmitService" : case submitTrace of - TraceRpcSubmitTxDecodingFailure _ _ -> ["TxDecodingFailure"] TraceRpcSubmitN2cConnectionError _ -> ["N2cConnectionError"] - TraceRpcSubmitTxValidationError _ _ -> ["TxValidationError"] TraceRpcSubmitSpan _ -> ["Span"] severityFor (Namespace _ nsInner) _ = case nsInner of @@ -80,7 +76,6 @@ instance MetaTrace TraceRpc where ["QueryService", "ReadUtxos", "Span"] -> Just Info ["SubmitService", "SubmitTx", "Span"] -> Just Info ["SubmitService", "N2cConnectionError"] -> Just Warning -- this is a more serious error, this shouldn't happen - ["SubmitService", "TxDecodingFailure"] -> Just Info -- request error ["SubmitService", "TxValidationError"] -> Just Info -- request error _ -> Nothing @@ -92,7 +87,6 @@ instance MetaTrace TraceRpc where ["SubmitService", "SubmitTx", "Span"] -> Just "" ["SubmitService", "N2cConnectionError"] -> Just "" ["SubmitService", "TxDecodingFailure"] -> Just "" - ["SubmitService", "TxValidationError"] -> Just "" _ -> Nothing metricsDocFor (Namespace _ nsInner) = case nsInner of @@ -108,9 +102,7 @@ instance MetaTrace TraceRpc where , ["QueryService", "ReadParams", "Span"] , ["QueryService", "ReadParams", "Span"] , ["SubmitService", "SubmitTx", "Span"] - , ["SubmitService", "TxDecodingFailure"] , ["SubmitService", "N2cConnectionError"] - , ["SubmitService", "TxValidationError"] ] -- helper functions diff --git a/cardano-submit-api/cardano-submit-api.cabal b/cardano-submit-api/cardano-submit-api.cabal index e8b2068de48..248564ae3ba 100644 --- a/cardano-submit-api/cardano-submit-api.cabal +++ b/cardano-submit-api/cardano-submit-api.cabal @@ -39,7 +39,7 @@ library , aeson , async , bytestring - , cardano-api ^>= 10.20 + , cardano-api ^>= 10.21 , cardano-binary , cardano-cli ^>= 10.14 , cardano-crypto-class ^>=2.2.3.2 diff --git a/cardano-testnet/cardano-testnet.cabal b/cardano-testnet/cardano-testnet.cabal index 6b4454e9e91..53f54c2c887 100644 --- a/cardano-testnet/cardano-testnet.cabal +++ b/cardano-testnet/cardano-testnet.cabal @@ -41,7 +41,7 @@ library , annotated-exception , ansi-terminal , bytestring - , cardano-api ^>= 10.20 + , cardano-api ^>= 10.21 , cardano-cli:{cardano-cli, cardano-cli-test-lib} ^>= 10.14 , cardano-crypto-class ^>=2.2.3.2 , cardano-crypto-wrapper From fefa606150e718020b0deec6e38286232232f73d Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Wed, 28 Jan 2026 17:58:34 +0100 Subject: [PATCH 22/23] fix tests --- .../Cardano/Testnet/Test/Rpc/Query.hs | 32 ++++++++++++------- .../Cardano/Testnet/Test/Rpc/Transaction.hs | 26 +++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs index 907f2b22255..23322b04fb2 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs @@ -23,11 +23,13 @@ import qualified Cardano.Ledger.Plutus as L import qualified Cardano.Rpc.Client as Rpc import qualified Cardano.Rpc.Proto.Api.UtxoRpc.Query as UtxoRpc import Cardano.Rpc.Server.Internal.UtxoRpc.Query () -import Cardano.Rpc.Server.Internal.UtxoRpc.Type (anyUtxoDataUtxoRpcToUtxo) +import Cardano.Rpc.Server.Internal.UtxoRpc.Type (anyUtxoDataUtxoRpcToUtxo, + utxoRpcBigIntToInteger) import Cardano.Testnet import Prelude +import Control.Exception import qualified Data.ByteString.Short as SBS import Data.Default.Class import qualified Data.Map.Strict as M @@ -92,9 +94,9 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) req pure (pparams', utxos') - ------------------------ + --------------------------- -- Test readParams response - ------------------------ + --------------------------- pparamsResponse ^. #ledgerTip . #slot === slot pparamsResponse ^. #ledgerTip . #hash === blockHash pparamsResponse ^. #ledgerTip . #height === blockNo @@ -104,20 +106,20 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem let chainParams = pparamsResponse ^. #values . #cardano babbageEraOnwardsConstraints (convert ceo) $ do pparams ^. L.ppCoinsPerUTxOByteL . to L.unCoinPerByte . to L.unCoin - === chainParams ^. #coinsPerUtxoByte . to fromIntegral + ===^ chainParams ^. #coinsPerUtxoByte . to utxoRpcBigIntToInteger pparams ^. L.ppMaxTxSizeL === chainParams ^. #maxTxSize . to fromIntegral - pparams ^. L.ppMinFeeBL === chainParams ^. #minFeeCoefficient . to fromIntegral - pparams ^. L.ppMinFeeAL === chainParams ^. #minFeeConstant . to fromIntegral + pparams ^. L.ppMinFeeBL ===^ chainParams ^. #minFeeCoefficient . to (fmap L.Coin . utxoRpcBigIntToInteger) + pparams ^. L.ppMinFeeAL ===^ chainParams ^. #minFeeConstant . to (fmap L.Coin . utxoRpcBigIntToInteger) pparams ^. L.ppMaxBBSizeL === chainParams ^. #maxBlockBodySize . to fromIntegral pparams ^. L.ppMaxBHSizeL === chainParams ^. #maxBlockHeaderSize . to fromIntegral - pparams ^. L.ppKeyDepositL === chainParams ^. #stakeKeyDeposit . to fromIntegral - pparams ^. L.ppPoolDepositL === chainParams ^. #poolDeposit . to fromIntegral + pparams ^. L.ppKeyDepositL ===^ chainParams ^. #stakeKeyDeposit . to (fmap L.Coin . utxoRpcBigIntToInteger) + pparams ^. L.ppPoolDepositL ===^ chainParams ^. #poolDeposit . to (fmap L.Coin . utxoRpcBigIntToInteger) pparams ^. L.ppEMaxL . to L.unEpochInterval === chainParams ^. #poolRetirementEpochBound . to fromIntegral pparams ^. L.ppNOptL === chainParams ^. #desiredNumberOfPools . to fromIntegral pparams ^. L.ppA0L . to L.unboundRational === chainParams ^. #poolInfluence . to inject pparams ^. L.ppNOptL === chainParams ^. #desiredNumberOfPools . to fromIntegral pparams ^. L.ppRhoL . to L.unboundRational === chainParams ^. #monetaryExpansion . to inject - pparams ^. L.ppMinPoolCostL === chainParams ^. #minPoolCost . to fromIntegral + pparams ^. L.ppMinPoolCostL ===^ chainParams ^. #minPoolCost . to (fmap L.Coin . utxoRpcBigIntToInteger) ( pparams ^. L.ppProtocolVersionL . to L.pvMajor . to L.getVersion , pparams ^. L.ppProtocolVersionL . to L.pvMinor ) @@ -173,8 +175,8 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem === chainParams ^. #committeeTermLimit . to fromIntegral pparams ^. L.ppGovActionLifetimeL . to L.unEpochInterval === chainParams ^. #governanceActionValidityPeriod . to fromIntegral - pparams ^. L.ppGovActionDepositL === chainParams ^. #governanceActionDeposit . to fromIntegral - pparams ^. L.ppDRepDepositL === chainParams ^. #drepDeposit . to fromIntegral + pparams ^. L.ppGovActionDepositL ===^ chainParams ^. #governanceActionDeposit . to (fmap L.Coin . utxoRpcBigIntToInteger) + pparams ^. L.ppDRepDepositL ===^ chainParams ^. #drepDeposit . to (fmap L.Coin . utxoRpcBigIntToInteger) pparams ^. L.ppDRepActivityL . to L.unEpochInterval === chainParams ^. #drepInactivityPeriod . to fromIntegral -------------------------- @@ -187,3 +189,11 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem H.threadDelay 90000000 H.failure + + +(===^) :: (Eq a, Show a, H.MonadTest m) => a -> Either SomeException a -> m () +expected ===^ actual = do + v <- H.leftFail actual + expected === v + +infix 4 ===^ diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs index 61fe119418c..5f02b98b82d 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs @@ -82,7 +82,7 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readParams")) req utxos' <- do - let req = def & #cardanoAddresses . #items .~ [T.encodeUtf8 addrTxt0] + let req = def -- & #keys .~ [T.encodeUtf8 addrTxt0] Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) req pure (pparams', utxos') @@ -91,8 +91,8 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' txOut0 : _ <- H.noteShow $ utxosResponse ^. #items txIn0 <- txoRefToTxIn $ txOut0 ^. #txoRef - let outputCoin = txOut0 ^. #cardano . #coin . to fromIntegral - amount = 200_000_000 + outputCoin <- H.leftFail $ txOut0 ^. #cardano . #coin . to utxoRpcBigIntToInteger + let amount = 200_000_000 fee = 500 change = outputCoin - amount - fee txOut = TxOut addr1 (lovelaceToTxOutValue sbe $ L.Coin amount) TxOutDatumNone ReferenceScriptNone @@ -114,7 +114,7 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' (utxos, submitResponse) <- H.noteShowM . H.evalIO . Rpc.withConnection def rpcServer $ \conn -> do submitResponse <- Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.SubmitService "submitTx")) $ - def & #tx .~ [def & #raw .~ serialiseToCBOR signedTx] + def & #tx .~ (def & #raw .~ serialiseToCBOR signedTx) fix $ \loop -> do resp <- Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readParams")) def @@ -127,30 +127,20 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' loop utxos <- - Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) $ - def & #cardanoAddresses . #items .~ [T.encodeUtf8 addrTxt1] + Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf UtxoRpc.QueryService "readUtxos")) def -- & #keys .~ [T.encodeUtf8 addrTxt1] pure (utxos, submitResponse) - submittedTxIds <- forM (submitResponse ^. #results) $ \res -> do - let mErr = res ^. #maybe'errorMessage - mTxId = res ^. #maybe'ref - case (mErr, mTxId) of - (Just err, Nothing) -> H.noteShow_ err >> H.failure - (Nothing, Just txId'') -> - H.leftFail $ deserialiseFromRawBytes AsTxId txId'' - _ -> do - H.note_ $ "Protocol error: " <> show res - H.failure + submittedTxId <- H.leftFail . deserialiseFromRawBytes AsTxId $ submitResponse ^. #ref H.note_ "Ensure that submitted transaction ID is in the submitted transactions list" - [txId'] === submittedTxIds + txId' === submittedTxId H.note_ $ "Enxure that there are 2 UTXOs in the address " <> show addrTxt1 2 === length (utxos ^. #items) let outputsAmounts = map (^. #cardano . #coin) $ utxos ^. #items H.note_ $ "Ensure that the output sent is one of the utxos for the address " <> show addrTxt1 - H.assertWith outputsAmounts $ elem (fromIntegral amount) + H.assertWith outputsAmounts $ elem (inject amount) txoRefToTxIn :: (HasCallStack, MonadTest m) => Proto UtxoRpc.TxoRef -> m TxIn txoRefToTxIn r = withFrozenCallStack $ do From de5a15425b945af9624230012ce8aecce61952ed Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Thu, 29 Jan 2026 16:46:06 +0100 Subject: [PATCH 23/23] wip --- .../src/Cardano/Node/Tracing/Tracers/Rpc.hs | 25 ++++++++++++------- .../Cardano/Testnet/Test/Rpc/Query.hs | 5 ---- .../Cardano/Testnet/Test/Rpc/Transaction.hs | 19 ++++++++++---- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs index 7acd043300b..6ebe368b5a5 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs @@ -2,10 +2,8 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wno-orphans #-} @@ -42,11 +40,14 @@ instance LogFormatting TraceRpc where ["kind" .= String "SubmitService"] <> case submitTrace of TraceRpcSubmitN2cConnectionError _ -> [] + TraceRpcSubmitTxDecodingError _ -> [] + TraceRpcSubmitTxValidationError _ -> [] TraceRpcSubmitSpan s -> [spanToObject s] forHuman = docToText . pretty asMetrics = \case + -- metrics for each rpc request -- query names here are taken from UTXORPC spec: https://utxorpc.org/query/intro/#operations TraceRpcQuery (TraceRpcQueryParamsSpan (SpanBegin _)) -> [CounterM "rpc.request.QueryService.ReadParams" Nothing] TraceRpcQuery (TraceRpcQueryReadUtxosSpan (SpanBegin _)) -> [CounterM "rpc.request.QueryService.ReadUtxos" Nothing] @@ -67,16 +68,19 @@ instance MetaTrace TraceRpc where "SubmitService" : case submitTrace of TraceRpcSubmitN2cConnectionError _ -> ["N2cConnectionError"] + TraceRpcSubmitTxDecodingError _ -> ["TxDecodingError"] + TraceRpcSubmitTxValidationError _ -> ["TxValidationError"] TraceRpcSubmitSpan _ -> ["Span"] severityFor (Namespace _ nsInner) _ = case nsInner of - ["FatalError"] -> Just Critical - ["Error"] -> Just Error - ["QueryService", "ReadParams", "Span"] -> Just Info - ["QueryService", "ReadUtxos", "Span"] -> Just Info - ["SubmitService", "SubmitTx", "Span"] -> Just Info + ["FatalError"] -> Just Error -- RPC server startup errors + ["Error"] -> Just Debug -- those are normal operation errors, like request errors, hide them by default + ["QueryService", "ReadParams", "Span"] -> Just Debug + ["QueryService", "ReadUtxos", "Span"] -> Just Debug + ["SubmitService", "SubmitTx", "Span"] -> Just Debug ["SubmitService", "N2cConnectionError"] -> Just Warning -- this is a more serious error, this shouldn't happen - ["SubmitService", "TxValidationError"] -> Just Info -- request error + ["SubmitService", "TxDecodingError"] -> Just Debug -- request error + ["SubmitService", "TxValidationError"] -> Just Debug -- request error _ -> Nothing documentFor (Namespace _ nsInner) = case nsInner of @@ -86,7 +90,8 @@ instance MetaTrace TraceRpc where ["QueryService", "ReadUtxos", "Span"] -> Just "" ["SubmitService", "SubmitTx", "Span"] -> Just "" ["SubmitService", "N2cConnectionError"] -> Just "" - ["SubmitService", "TxDecodingFailure"] -> Just "" + ["SubmitService", "TxDecodingError"] -> Just "" + ["SubmitService", "TxValidationError"] -> Just "" _ -> Nothing metricsDocFor (Namespace _ nsInner) = case nsInner of @@ -103,6 +108,8 @@ instance MetaTrace TraceRpc where , ["QueryService", "ReadParams", "Span"] , ["SubmitService", "SubmitTx", "Span"] , ["SubmitService", "N2cConnectionError"] + , ["SubmitService", "TxDecodingError"] + , ["SubmitService", "TxValidationError"] ] -- helper functions diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs index 23322b04fb2..b4bb2e89882 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Query.hs @@ -186,11 +186,6 @@ hprop_rpc_query_pparams = integrationRetryWorkspace 2 "rpc-query-pparams" $ \tem utxoFromUtxoRpc <- H.leftFail $ utxosResponse ^. #items . to (anyUtxoDataUtxoRpcToUtxo $ convert ceo) utxos === utxoFromUtxoRpc - H.threadDelay 90000000 - - H.failure - - (===^) :: (Eq a, Show a, H.MonadTest m) => a -> Either SomeException a -> m () expected ===^ actual = do v <- H.leftFail actual diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs index 5f02b98b82d..0b0ef7db5fc 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/Transaction.hs @@ -40,7 +40,7 @@ import qualified Hedgehog as H import qualified Hedgehog.Extras.Test.Base as H import qualified Hedgehog.Extras.Test.TestWatchdog as H -import RIO (threadDelay) +import RIO (ByteString, threadDelay) hprop_rpc_transaction :: Property hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' -> H.runWithDefaultWatchdog_ $ do @@ -49,6 +49,7 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' (conwayBasedEra, asType) :: era ~ ConwayEra => (ConwayEraOnwards era, AsType era) sbe = convert ceo options = def{cardanoNodeEra = AnyShelleyBasedEra sbe, cardanoEnableRpc = True} + addrInEra = AsAddressInEra eraProxy TestnetRuntime { testnetNodes = node0 : _ @@ -60,10 +61,10 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' -- prepare tx inputs and output address H.noteShow_ addrTxt0 - addr0 <- H.nothingFail $ deserialiseAddress (AsAddressInEra eraProxy) addrTxt0 + addr0 <- H.nothingFail $ deserialiseAddress addrInEra addrTxt0 H.noteShow_ addrTxt1 - addr1 <- H.nothingFail $ deserialiseAddress (AsAddressInEra eraProxy) addrTxt1 + addr1 <- H.nothingFail $ deserialiseAddress addrInEra addrTxt1 -- read key witnesses wit0 :: ShelleyWitnessSigningKey <- @@ -88,7 +89,9 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' pparams <- H.leftFail $ utxoRpcPParamsToProtocolParams (convert ceo) $ pparamsResponse ^. #values . #cardano - txOut0 : _ <- H.noteShow $ utxosResponse ^. #items + txOut0 : _ <- H.noteShowM . flip filterM (utxosResponse ^. #items) $ \utxo -> do + utxoAddress <- deserialiseAddressBs addrInEra $ utxo ^. #cardano . #address + pure $ addr0 == utxoAddress txIn0 <- txoRefToTxIn $ txOut0 ^. #txoRef outputCoin <- H.leftFail $ txOut0 ^. #cardano . #coin . to utxoRpcBigIntToInteger @@ -136,7 +139,10 @@ hprop_rpc_transaction = integrationRetryWorkspace 2 "rpc-tx" $ \tempAbsBasePath' txId' === submittedTxId H.note_ $ "Enxure that there are 2 UTXOs in the address " <> show addrTxt1 - 2 === length (utxos ^. #items) + utxosForAddress <- H.noteShowM . flip filterM (utxos ^. #items) $ \utxo -> do + utxoAddress <- deserialiseAddressBs addrInEra $ utxo ^. #cardano . #address + pure $ addr1 == utxoAddress + 2 === length utxosForAddress let outputsAmounts = map (^. #cardano . #coin) $ utxos ^. #items H.note_ $ "Ensure that the output sent is one of the utxos for the address " <> show addrTxt1 @@ -146,3 +152,6 @@ txoRefToTxIn :: (HasCallStack, MonadTest m) => Proto UtxoRpc.TxoRef -> m TxIn txoRefToTxIn r = withFrozenCallStack $ do txId' <- H.leftFail $ deserialiseFromRawBytes AsTxId $ r ^. #hash pure $ TxIn txId' (TxIx . fromIntegral $ r ^. #index) + +deserialiseAddressBs :: (MonadTest m, SerialiseAddress c) => AsType c -> ByteString -> m c +deserialiseAddressBs addrInEra = H.nothingFail . deserialiseAddress addrInEra <=< H.leftFail . T.decodeUtf8'