diff --git a/cardano-node/src/Cardano/Node/Protocol/Checkpoints.hs b/cardano-node/src/Cardano/Node/Protocol/Checkpoints.hs index 029d1decb04..e8d68378532 100644 --- a/cardano-node/src/Cardano/Node/Protocol/Checkpoints.hs +++ b/cardano-node/src/Cardano/Node/Protocol/Checkpoints.hs @@ -11,10 +11,10 @@ module Cardano.Node.Protocol.Checkpoints import Cardano.Api import qualified Cardano.Crypto.Hash.Class as Crypto -import Cardano.Protocol.Crypto (StandardCrypto) import Cardano.Node.Types +import Cardano.Protocol.Crypto () import Ouroboros.Consensus.Block -import Ouroboros.Consensus.Cardano +import Ouroboros.Consensus.Cardano () import Ouroboros.Consensus.Config (CheckpointsMap (..), emptyCheckpointsMap) import Control.Exception (IOException) diff --git a/cardano-node/src/Cardano/Node/Protocol/Shelley.hs b/cardano-node/src/Cardano/Node/Protocol/Shelley.hs index a816b8e91ed..551bc9aa754 100644 --- a/cardano-node/src/Cardano/Node/Protocol/Shelley.hs +++ b/cardano-node/src/Cardano/Node/Protocol/Shelley.hs @@ -39,7 +39,7 @@ import Cardano.Node.Tracing.Era.Shelley () import Cardano.Node.Tracing.Formatting () import Cardano.Node.Tracing.Tracers.ChainDB () import Cardano.Node.Types -import Cardano.Protocol.Crypto (StandardCrypto) +import Cardano.Protocol.Crypto () import qualified Ouroboros.Consensus.Cardano as Consensus import Ouroboros.Consensus.HardFork.Combinator.AcrossEras () import Ouroboros.Consensus.Protocol.Praos.Common (PraosCanBeLeader (..), diff --git a/cardano-node/src/Cardano/Node/Run.hs b/cardano-node/src/Cardano/Node/Run.hs index f6fe4e745d9..c9bc027415b 100644 --- a/cardano-node/src/Cardano/Node/Run.hs +++ b/cardano-node/src/Cardano/Node/Run.hs @@ -50,6 +50,7 @@ import Cardano.Node.Protocol.Types import Cardano.Node.Queries import Cardano.Rpc.Server import Cardano.Rpc.Server.Config +import Data.IORef import Cardano.Node.Startup import Cardano.Node.TraceConstraints (TraceConstraints) import Cardano.Node.Tracing (Tracers (..)) @@ -449,6 +450,7 @@ handleSimpleNode blockType runP tracers nc cmdPc networkMagic onKernel = do #endif nForkPolicy <- getForkPolicy $ ncResponderCoreAffinityPolicy nc cForkPolicy <- getForkPolicy $ ncResponderCoreAffinityPolicy nc + nodeKernelAccessRef <- newIORef Nothing void $ let diffusionNodeArguments :: Cardano.Diffusion.CardanoNodeArguments IO diffusionNodeArguments = Cardano.Diffusion.CardanoNodeArguments { @@ -481,7 +483,7 @@ handleSimpleNode blockType runP tracers nc cmdPc networkMagic onKernel = do (readTVar ledgerPeerSnapshotVar) nc in - withAsync (rpcServerLoop (startupTracer tracers) (rpcTracer tracers) rpcConfigVar networkMagic) $ \_ -> + withAsync (rpcServerLoop (startupTracer tracers) (rpcTracer tracers) rpcConfigVar networkMagic nodeKernelAccessRef) $ \_ -> Node.run nodeArgs { rnNodeKernelHook = \registry nodeKernel -> do @@ -491,6 +493,8 @@ handleSimpleNode blockType runP tracers nc cmdPc networkMagic onKernel = do useBootstrapVar ledgerPeerSnapshotPathVar ledgerPeerSnapshotVar rpcConfigVar rnNodeKernelHook nodeArgs registry nodeKernel + mkNodeKernelAccess (contramap RpcUnsupportedBlockType (startupTracer tracers)) blockType nodeKernel + >>= writeIORef nodeKernelAccessRef } StdRunNodeArgs { srnBfcMaxConcurrencyBulkSync = unMaxConcurrencyBulkSync <$> ncMaxConcurrencyBulkSync nc @@ -767,8 +771,9 @@ rpcServerLoop :: Tracer IO (StartupTrace blk) -> Tracer IO TraceRpc -> StrictTVar IO RpcConfig -> NetworkMagic + -> IORef (Maybe NodeKernelAccess) -> IO () -rpcServerLoop startupTracer rpcTracer rpcConfigVar networkMagic = go +rpcServerLoop startupTracer rpcTracer rpcConfigVar networkMagic nodeKernelAccessRef = go where go = do config@RpcConfig{isEnabled = Identity enabled} <- readTVarIO rpcConfigVar @@ -776,7 +781,7 @@ rpcServerLoop startupTracer rpcTracer rpcConfigVar networkMagic = go then race_ (do - runRpcServer rpcTracer (config, networkMagic) + runRpcServer rpcTracer config networkMagic nodeKernelAccessRef traceWith startupTracer RpcForceDisabled disableRpcServer) (waitForRpcConfigChange config) diff --git a/cardano-node/src/Cardano/Node/Startup.hs b/cardano-node/src/Cardano/Node/Startup.hs index af9b9edee9f..e666321571e 100644 --- a/cardano-node/src/Cardano/Node/Startup.hs +++ b/cardano-node/src/Cardano/Node/Startup.hs @@ -145,6 +145,8 @@ data StartupTrace blk = | RpcConfigUpdate Text -- | Log RPC configuration update error | RpcConfigUpdateError Text + -- | Log that node kernel access is not supported for the running block type. + | RpcUnsupportedBlockType Text -- | Log RPC is forcefully disabled after a RPC server crash. | RpcForceDisabled diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs index f9781f5efe6..7c0bc7c010d 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers/Rpc.hs @@ -13,7 +13,7 @@ import Cardano.Api.Pretty import Cardano.Logging hiding (nsInner) import Cardano.Rpc.Server (TraceRpc (..), TraceRpcQuery (..), TraceRpcSubmit (..), - TraceSpanEvent (..)) + TraceRpcSync (..), TraceSpanEvent (..)) import Data.Aeson (Object, Value (..), (.=)) @@ -48,6 +48,13 @@ instance LogFormatting TraceRpc where TraceRpcSubmitSpan s -> [spanToObject s] TraceRpcEvalTxDecodingError _ -> [] TraceRpcEvalTxSpan s -> [spanToObject s] + TraceRpcSync syncTrace -> + ["kind" .= String "SyncService"] + <> case syncTrace of + TraceRpcFetchBlockSpan s -> [spanToObject s] + TraceRpcFetchBlockNotFound _ -> [] + TraceRpcNodeKernelAccessUnavailable -> [] + TraceRpcForkerError _ -> [] forHuman = docToText . pretty @@ -59,6 +66,7 @@ instance LogFormatting TraceRpc where TraceRpcQuery (TraceRpcQuerySearchUtxosSpan (SpanBegin _)) -> [CounterM "rpc.request.QueryService.SearchUtxos" Nothing] TraceRpcSubmit (TraceRpcSubmitSpan (SpanBegin _)) -> [CounterM "rpc.request.SubmitService.SubmitTx" Nothing] TraceRpcSubmit (TraceRpcEvalTxSpan (SpanBegin _)) -> [CounterM "rpc.request.SubmitService.EvalTx" Nothing] + TraceRpcSync (TraceRpcFetchBlockSpan (SpanBegin _)) -> [CounterM "rpc.request.SyncService.FetchBlock" Nothing] _ -> [] instance MetaTrace TraceRpc where @@ -81,6 +89,13 @@ instance MetaTrace TraceRpc where TraceRpcSubmitSpan _ -> ["SubmitTx", "Span"] TraceRpcEvalTxDecodingError _ -> ["EvalTxDecodingError"] TraceRpcEvalTxSpan _ -> ["EvalTx", "Span"] + TraceRpcSync syncTrace -> + "SyncService" + : case syncTrace of + TraceRpcFetchBlockSpan _ -> ["FetchBlock", "Span"] + TraceRpcFetchBlockNotFound _ -> ["FetchBlockNotFound"] + TraceRpcNodeKernelAccessUnavailable -> ["NodeKernelAccessUnavailable"] + TraceRpcForkerError _ -> ["ForkerError"] severityFor (Namespace _ nsInner) _ = case nsInner of ["FatalError"] -> Just Error -- RPC server startup errors @@ -94,6 +109,10 @@ instance MetaTrace TraceRpc where ["SubmitService", "TxDecodingError"] -> Just Debug -- request error ["SubmitService", "TxValidationError"] -> Just Debug -- request error ["SubmitService", "EvalTxDecodingError"] -> Just Debug -- request error + ["SyncService", "FetchBlock", "Span"] -> Just Debug + ["SyncService", "FetchBlockNotFound"] -> Just Debug -- normal: block may have been pruned + ["SyncService", "NodeKernelAccessUnavailable"] -> Just Warning -- kernel not yet ready + ["SyncService", "ForkerError"] -> Just Warning -- unexpected ledger forker error _ -> Nothing documentFor (Namespace _ nsInner) = case nsInner of @@ -110,6 +129,10 @@ instance MetaTrace TraceRpc where ["SubmitService", "TxDecodingError"] -> Just "A regular request error, when submitted transaction decoding fails." ["SubmitService", "TxValidationError"] -> Just "A regular request error, when submitted transaction is invalid." ["SubmitService", "EvalTxDecodingError"] -> Just "A regular request error, when evalTx transaction decoding fails." + ["SyncService", "FetchBlock", "Span"] -> Just "Span for the FetchBlock SyncService method." + ["SyncService", "FetchBlockNotFound"] -> Just "Requested block was not found in ChainDB." + ["SyncService", "NodeKernelAccessUnavailable"] -> Just "Node kernel access not yet initialised. The node is still starting up." + ["SyncService", "ForkerError"] -> Just "Unexpected error from ledger forker." _ -> Nothing metricsDocFor (Namespace _ nsInner) = case nsInner of @@ -123,6 +146,8 @@ instance MetaTrace TraceRpc where [("rpc.request.SubmitService.SubmitTx", "Span for the SubmitTx UTXORPC method.")] ["SubmitService", "EvalTx", "Span"] -> [("rpc.request.SubmitService.EvalTx", "Span for the EvalTx UTXORPC method.")] + ["SyncService", "FetchBlock", "Span"] -> + [("rpc.request.SyncService.FetchBlock", "Span for the FetchBlock SyncService method.")] _ -> [] allNamespaces = @@ -138,6 +163,10 @@ instance MetaTrace TraceRpc where , ["SubmitService", "TxDecodingError"] , ["SubmitService", "TxValidationError"] , ["SubmitService", "EvalTxDecodingError"] + , ["SyncService", "FetchBlock", "Span"] + , ["SyncService", "FetchBlockNotFound"] + , ["SyncService", "NodeKernelAccessUnavailable"] + , ["SyncService", "ForkerError"] ] -- helper functions diff --git a/cardano-node/src/Cardano/Node/Tracing/Tracers/Startup.hs b/cardano-node/src/Cardano/Node/Tracing/Tracers/Startup.hs index c8eefaef1cf..44bc25173e0 100644 --- a/cardano-node/src/Cardano/Node/Tracing/Tracers/Startup.hs +++ b/cardano-node/src/Cardano/Node/Tracing/Tracers/Startup.hs @@ -17,7 +17,6 @@ module Cardano.Node.Tracing.Tracers.Startup import Cardano.Api (NetworkMagic (..), SlotNo (..)) import qualified Cardano.Api as Api -import Cardano.Network.OrphanInstances () import qualified Cardano.Chain.Genesis as Gen import Cardano.Git.Rev (gitRev) @@ -25,6 +24,7 @@ import Cardano.Ledger.Shelley.API as SL import Cardano.Logging import Cardano.Network.NodeToClient (LocalAddress (..)) import Cardano.Network.NodeToNode (DiffusionMode (..)) +import Cardano.Network.OrphanInstances () import Cardano.Node.Configuration.POM (NodeConfiguration, ncProtocol) import Cardano.Node.Configuration.Socket import Cardano.Node.Protocol (SomeConsensusProtocol (..)) @@ -288,6 +288,9 @@ instance ( Show (BlockNodeToNodeVersion blk) forMachine _dtal (RpcConfigUpdateError err) = mconcat [ "kind" .= String "RpcConfigUpdateError" , "error" .= String ("Error while updating RPC configuration: " <> err) ] + forMachine _dtal (RpcUnsupportedBlockType blockType) = + mconcat [ "kind" .= String "RpcUnsupportedBlockType" + , "blockType" .= String blockType ] forMachine _dtal RpcForceDisabled = mconcat [ "kind" .= String "RpcForceDisabled" , "error" .= String (ppStartupInfoTrace RpcForceDisabled)] @@ -360,6 +363,8 @@ instance MetaTrace (StartupTrace blk) where Namespace [] ["RpcConfigUpdate"] namespaceFor RpcConfigUpdateError {} = Namespace [] ["RpcConfigUpdateError"] + namespaceFor RpcUnsupportedBlockType {} = + Namespace [] ["RpcUnsupportedBlockType"] namespaceFor RpcForceDisabled = Namespace [] ["RpcForceDisabled"] namespaceFor MovedTopLevelOption {} = @@ -376,6 +381,7 @@ instance MetaTrace (StartupTrace blk) where severityFor (Namespace _ ["WarningDevelopmentNodeToClientVersions"]) _ = Just Warning severityFor (Namespace _ ["RpcConfigUpdate"]) _ = Just Notice severityFor (Namespace _ ["RpcConfigUpdateError"]) _ = Just Error + severityFor (Namespace _ ["RpcUnsupportedBlockType"]) _ = Just Warning severityFor (Namespace _ ["RpcForceDisabled"]) _ = Just Error severityFor (Namespace _ ["BlockForgingUpdateError"]) _ = Just Error severityFor (Namespace _ ["BlockForgingBlockTypeMismatch"]) _ = Just Error @@ -407,6 +413,8 @@ instance MetaTrace (StartupTrace blk) where "" documentFor (Namespace [] ["RpcConfigUpdateError"]) = Just "" + documentFor (Namespace [] ["RpcUnsupportedBlockType"]) = Just + "" documentFor (Namespace [] ["RpcForceDisabled"]) = Just "" documentFor (Namespace [] ["NetworkConfigUpdate"]) = Just @@ -480,6 +488,7 @@ instance MetaTrace (StartupTrace blk) where , Namespace [] ["BlockForgingBlockTypeMismatch"] , Namespace [] ["RpcConfigUpdate"] , Namespace [] ["RpcConfigUpdateError"] + , Namespace [] ["RpcUnsupportedBlockType"] , Namespace [] ["RpcForceDisabled"] , Namespace [] ["NetworkConfigUpdate"] , Namespace [] ["NetworkConfigUpdateUnsupported"] @@ -605,6 +614,7 @@ ppStartupInfoTrace (LedgerPeerSnapshotLoaded slotNo) = ppStartupInfoTrace (RpcConfigUpdate config) = "Performing RPC configuration update: " <> config ppStartupInfoTrace (RpcConfigUpdateError err) = "Error while updating RPC configuration: " <> err +ppStartupInfoTrace (RpcUnsupportedBlockType blockType) = "RPC node kernel access is not supported for block type: " <> blockType ppStartupInfoTrace RpcForceDisabled = "RPC endpoint has crashed and because of that it got disabled. Enable gRPC endpoint and send SIGHUP to the node to reenable." ppStartupInfoTrace NonP2PWarning = nonP2PWarningMessage diff --git a/cardano-testnet/cardano-testnet.cabal b/cardano-testnet/cardano-testnet.cabal index f939a8be8d1..bf9c77c0280 100644 --- a/cardano-testnet/cardano-testnet.cabal +++ b/cardano-testnet/cardano-testnet.cabal @@ -239,6 +239,7 @@ test-suite cardano-testnet-test Cardano.Testnet.Test.Gov.TreasuryGrowth Cardano.Testnet.Test.Gov.TreasuryWithdrawal Cardano.Testnet.Test.Rpc.Eval + Cardano.Testnet.Test.Rpc.FetchBlock Cardano.Testnet.Test.Rpc.Query Cardano.Testnet.Test.Rpc.SearchUtxos Cardano.Testnet.Test.Rpc.Transaction diff --git a/cardano-testnet/src/Testnet/Runtime.hs b/cardano-testnet/src/Testnet/Runtime.hs index e271f264d42..526a823225e 100644 --- a/cardano-testnet/src/Testnet/Runtime.hs +++ b/cardano-testnet/src/Testnet/Runtime.hs @@ -27,6 +27,7 @@ import qualified Cardano.Api as Api import qualified Cardano.Ledger.Api as L import qualified Cardano.Ledger.Shelley.LedgerState as L import qualified Cardano.Ledger.Shelley.State as L +import Cardano.Node.Testnet.Paths (defaultSocketName) import Prelude @@ -53,11 +54,10 @@ import qualified System.Process as IO import System.Process (waitForProcess) import Testnet.Filepath -import Cardano.Node.Testnet.Paths (defaultSocketName) import qualified Testnet.Ping as Ping import Testnet.Process.Run (ProcessError (..), initiateProcess) -import Testnet.Process.RunIO (execCli_, execKesAgentControl_, liftIOAnnotated, - procCustom, procKesAgent, procNode) +import Testnet.Process.RunIO (execCli_, execKesAgentControl_, liftIOAnnotated, procCustom, + procKesAgent, procNode) import Testnet.Types (TestnetKesAgent (..), TestnetNode (..), TestnetRuntime (configurationFile), showIpv4Address, testnetSprockets) @@ -506,9 +506,9 @@ startLedgerNewEpochStateLogging testnetRuntime tmpWorkspace = withFrozenCallStac -> SlotNo -> BlockNo -> StateT (Maybe AnyNewEpochState) IO ConditionResult - handler outputFp diffFp anes@(AnyNewEpochState !sbe !nes _) _ (BlockNo blockNo) = handleException $ do + handler outputFp diffFp anes@(AnyNewEpochState !sbe !nes _) _ (BlockNo currentBlockNo) = handleException $ do let prettyNes = shelleyBasedEraConstraints sbe (encodePretty nes) - blockLabel = "#### BLOCK " <> show blockNo <> " ####" + blockLabel = "#### BLOCK " <> show currentBlockNo <> " ####" liftIOAnnotated . BSC.appendFile outputFp $ BSC.unlines [BSC.pack blockLabel, prettyNes, ""] -- store epoch state for logging of differences diff --git a/cardano-testnet/src/Testnet/Start/Cardano.hs b/cardano-testnet/src/Testnet/Start/Cardano.hs index c4df60856cb..250cd845779 100644 --- a/cardano-testnet/src/Testnet/Start/Cardano.hs +++ b/cardano-testnet/src/Testnet/Start/Cardano.hs @@ -42,42 +42,42 @@ import qualified Cardano.Api.Byron as Byron import Cardano.Network.Diffusion.Topology (CardanoNetworkTopology) import Cardano.Node.Configuration.NodeAddress (PortNumber) import Cardano.Node.Configuration.TopologyP2P () +import Cardano.Node.Testnet.Paths (defaultConfigFile, defaultNodeEnvFile, defaultPortFile, + defaultUtxoAddrPath) import Cardano.Prelude (NonEmpty ((:|)), canonicalEncodePretty, readMaybe) import Ouroboros.Network.PeerSelection.RelayAccessPoint (RelayAccessPoint (..)) import Prelude hiding (lines) import Control.Concurrent (threadDelay) -import Control.Monad (forM, forM_, guard, unless, when) -import Control.Monad.Trans.Maybe (runMaybeT) import Control.Exception (IOException) +import Control.Monad (forM, forM_, guard, unless, when) import Control.Monad.Catch +import Control.Monad.Trans.Maybe (runMaybeT) import Control.Monad.Trans.Resource (MonadResource, getInternalState) import Data.Aeson import qualified Data.Aeson.Encode.Pretty as A -import qualified Data.Yaml as Yaml import qualified Data.ByteString.Lazy as LBS import Data.Default.Class () import Data.Either -import Data.Maybe (mapMaybe) import Data.Functor import Data.List (sort, stripPrefix, uncons) import qualified Data.List.NonEmpty as NEL import qualified Data.Map as Map +import Data.Maybe (mapMaybe) import Data.MonoTraversable (Element, MonoFunctor, omap) import qualified Data.Text as Text import Data.Time (diffUTCTime) import Data.Time.Clock (NominalDiffTime) import qualified Data.Time.Clock as DTC +import qualified Data.Yaml as Yaml import GHC.Stack import qualified System.Directory as IO -import qualified System.Process as Process import System.FilePath (()) +import qualified System.Process as Process import Testnet.Components.Configuration import qualified Testnet.Defaults as Defaults -import Cardano.Node.Testnet.Paths (defaultConfigFile, defaultNodeEnvFile, - defaultPortFile, defaultUtxoAddrPath) import Testnet.Filepath import Testnet.Handlers (interruptNodesOnSigINT) import Testnet.Orphans () @@ -440,9 +440,9 @@ cardanoTestnet QuickValidation (EpochNo maxBound) minBound - $ \_ slotNo blockNo -> do + $ \_ slotNo currentBlockNo -> do put slotNo - pure $ if blockNo >= 1 + pure $ if currentBlockNo >= 1 then ConditionMet -- we got one block else ConditionNotMet diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/FetchBlock.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/FetchBlock.hs new file mode 100644 index 00000000000..908cb2debc4 --- /dev/null +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Rpc/FetchBlock.hs @@ -0,0 +1,101 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + +module Cardano.Testnet.Test.Rpc.FetchBlock ( + hprop_rpc_fetch_block, +) +where + +import Cardano.Api +import qualified Cardano.Api.Experimental as Exp + +import Cardano.CLI.Type.Output (QueryTipLocalStateOutput (..)) +import qualified Cardano.Rpc.Client as Rpc +import qualified Cardano.Rpc.Proto.Api.UtxoRpc.Sync as U5c +import Cardano.Testnet + +import Prelude + +import qualified Data.ByteString as BS +import qualified Data.ByteString.Short as SBS +import Data.Default.Class +import Lens.Micro + +import Testnet.Process.Run +import Testnet.Property.Util (integrationRetryWorkspace) +import Testnet.Start.Types + +import qualified Hedgehog as H +import qualified Hedgehog.Extras as H + +-- | Run with: +-- @TASTY_PATTERN='/RPC FetchBlock/' cabal test cardano-testnet-test@ +-- +hprop_rpc_fetch_block :: H.Property +hprop_rpc_fetch_block = integrationRetryWorkspace 2 "rpc-fetch-block" $ \tempAbsBasePath' -> H.runWithDefaultWatchdog_ $ do + conf@Conf{tempAbsPath} <- mkConf tempAbsBasePath' + let tempAbsPath' = unTmpAbsPath tempAbsPath + + let era = Exp.ConwayEra + sbe = convert era + eraName = eraToString sbe + creationOptions = def{creationEra = AnyShelleyBasedEra sbe} + runtimeOptions = def{runtimeEnableRpc = RpcEnabled} + + _tr@TestnetRuntime + { testnetMagic + , testnetNodes = node0@TestnetNode{nodeSprocket} : _ + } <- + createAndRunTestnet creationOptions runtimeOptions conf + + execConfig <- mkExecConfig tempAbsPath' nodeSprocket testnetMagic + rpcSocket <- H.note . unFile $ nodeRpcSocketPath node0 + + -- Get chain tip via CLI + QueryTipLocalStateOutput{localStateChainTip} <- + H.noteShowM $ execCliStdoutToJson execConfig [eraName, "query", "tip"] + (slot, blockHash, blockNo) <- case localStateChainTip of + ChainTipAtGenesis -> H.failure + ChainTip (SlotNo tipSlot) (HeaderHash hash) (BlockNo bn) -> pure (tipSlot, SBS.fromShort hash, bn) + + H.note_ $ "Tip slot: " <> show slot + H.note_ $ "Tip block number: " <> show blockNo + H.note_ $ "Tip hash: " <> show (BS.length blockHash) <> " bytes" + + -- Call FetchBlock via gRPC + let rpcServer = Rpc.ServerUnix rpcSocket + blockRef = def & U5c.slot .~ slot & U5c.hash .~ blockHash + request = def & U5c.ref .~ blockRef + + response <- H.evalIO . Rpc.withConnection def rpcServer $ \conn -> + Rpc.nonStreaming conn (Rpc.rpc @(Rpc.Protobuf U5c.SyncService "fetchBlock")) request + + let block = response ^. U5c.block + + -- Verify nativeBytes is non-empty + let rawBytes = block ^. U5c.nativeBytes + H.note_ $ "Block CBOR: " <> show (BS.length rawBytes) <> " bytes" + H.assertWith rawBytes $ not . BS.null + + -- Verify cardano block header matches the requested tip + block ^. U5c.cardano . U5c.header . U5c.slot H.=== slot + block ^. U5c.cardano . U5c.header . U5c.hash H.=== blockHash + + -- height is the block number from ChainDB + block ^. U5c.cardano . U5c.header . U5c.height H.=== blockNo + + -- TODO: verify timestamp once FetchBlock populates it from EraHistory (node kernel snapshot migration) + -- connectionInfo <- nodeConnectionInfo tr 0 + -- (systemStart, eraHistory) <- + -- (H.leftFail <=< H.leftFailM) . H.evalIO $ + -- executeLocalStateQueryExpr connectionInfo VolatileTip $ do + -- ss <- querySystemStart + -- eh <- queryEraHistory + -- pure $ (,) <$> ss <*> eh + -- expectedTimestampMs :: Word64 <- H.leftFail $ do + -- utcTime <- slotToUTCTime systemStart eraHistory (SlotNo slot) + -- pure . round $ utcTimeToPOSIXSeconds utcTime * 1000 + -- H.assertWithinTolerance (block ^. U5c.cardano . U5c.timestamp) expectedTimestampMs 1000 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 c1d7ab52310..e760ed94e21 100644 --- a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs +++ b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs @@ -36,6 +36,7 @@ import qualified Cardano.Testnet.Test.MainnetParams import qualified Cardano.Testnet.Test.Node.Shutdown import qualified Cardano.Testnet.Test.Parser import qualified Cardano.Testnet.Test.Rpc.Eval +import qualified Cardano.Testnet.Test.Rpc.FetchBlock import qualified Cardano.Testnet.Test.Rpc.Query import qualified Cardano.Testnet.Test.Rpc.SearchUtxos import qualified Cardano.Testnet.Test.Rpc.Transaction @@ -148,7 +149,8 @@ tests = do [ 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 FetchBlock" Cardano.Testnet.Test.Rpc.FetchBlock.hprop_rpc_fetch_block + , ignoreOnWindows "RPC Query Protocol Params" Cardano.Testnet.Test.Rpc.Query.hprop_rpc_query_pparams , ignoreOnWindows "RPC SearchUtxos" Cardano.Testnet.Test.Rpc.SearchUtxos.hprop_rpc_search_utxos , ignoreOnWindows "RPC Transaction Submit" Cardano.Testnet.Test.Rpc.Transaction.hprop_rpc_transaction , ignoreOnWindows "RPC Eval Tx" Cardano.Testnet.Test.Rpc.Eval.hprop_rpc_eval_tx