Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Initial ledger events integration into cardano-testnet #5523

Merged
merged 1 commit into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cardano-testnet/cardano-testnet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ test-suite cardano-testnet-test
Cardano.Testnet.Test.Cli.QuerySlotNumber
Cardano.Testnet.Test.FoldBlocks
Cardano.Testnet.Test.Misc
Cardano.Testnet.Test.Node.LedgerEvents
Cardano.Testnet.Test.Node.Shutdown

type: exitcode-stdio-1.0
Expand All @@ -186,6 +187,7 @@ test-suite cardano-testnet-test
, text
, time
, transformers
, transformers-except

ghc-options: -threaded -rtsopts -with-rtsopts=-N -with-rtsopts=-T

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Cardano.Testnet.Test.Node.LedgerEvents
( hprop_ledger_events_sanity_check
) where

import Cardano.Api

import Cardano.Testnet

import Prelude

import Control.Monad.Trans.Except
import Control.Monad.Trans.Except.Extra
import qualified Data.Text as Text
import GHC.IO.Exception (IOException)
import GHC.Stack (callStack)
import System.FilePath ((</>))

import Hedgehog
import qualified Hedgehog.Extras.Stock.IO.Network.Sprocket as IO
import qualified Hedgehog.Extras.Test.Base as H

import qualified Testnet.Property.Utils as H
import Testnet.Runtime

newtype AdditionalCatcher
= IOE IOException
deriving Show

-- Ledger events can be emitted upon the application of the various ledger rules.
-- Event definition example: https://github.com/input-output-hk/cardano-ledger/blob/afedb7d519761ccdd9c013444aa4b3e0bf0e68ef/eras/conway/impl/src/Cardano/Ledger/Conway/Rules/Gov.hs#L198
-- Event emission: https://github.com/input-output-hk/cardano-ledger/blob/afedb7d519761ccdd9c013444aa4b3e0bf0e68ef/eras/conway/impl/src/Cardano/Ledger/Conway/Rules/Gov.hs#L389
-- We can directly access these events via `foldBlocks` exposed by cardano-api. In the normal operation of a node, these events are ignored
-- (see: https://github.com/input-output-hk/ouroboros-consensus/commit/c1abf51948a673a2bbd540e5b7929ce1f07c108e#diff-4274b4c9494fc060b0980695df1b5de3412eccd31cd10c77836ef5bc66e40dd8R123) however a node's client
-- that is requesting blocks can reconstruct the ledger state and access the ledger events via `tickThenApplyLedgerResult`. This is what
-- `foldBlocks` does. Below is a simple test that illustrates `foldBlocks` pattern matching on the RetiredPools event (https://github.com/input-output-hk/cardano-ledger/blob/afedb7d519761ccdd9c013444aa4b3e0bf0e68ef/eras/shelley/impl/src/Cardano/Ledger/Shelley/Rules/PoolReap.hs#L177).
-- This sets the stage for more direct testing of clusters allowing us to avoid querying the node, dealing with serialization to and from disk,
-- setting timeouts for expected results etc.
hprop_ledger_events_sanity_check :: Property
hprop_ledger_events_sanity_check = H.integrationRetryWorkspace 2 "ledger-events-sanity-check" $ \tempAbsBasePath' -> do
-- Start a local test net
conf <- H.noteShowM $ mkConf tempAbsBasePath'

let fastTestnetOptions = cardanoDefaultTestnetOptions
{ cardanoEpochLength = 100
, cardanoSlotLength = 0.1
}

!testnetRuntime
<- cardanoTestnet fastTestnetOptions conf
NodeRuntime{nodeSprocket} <- H.headM $ poolRuntime <$> poolNodes testnetRuntime
let socketName' = IO.sprocketName nodeSprocket
socketBase = IO.sprocketBase nodeSprocket -- /tmp
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you still need the /tmp comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I do to remind myself that the base is tmp

socketPath = socketBase </> socketName'

H.note_ $ "Sprocket: " <> show nodeSprocket
H.note_ $ "Abs path: " <> tempAbsBasePath'
H.note_ $ "Socketpath: " <> socketPath


!ret <- runExceptT $ handleIOExceptT IOE
$ runExceptT $ foldBlocks
(File $ configurationFile testnetRuntime)
(File socketPath)
FullValidation
[] -- Initial accumulator state
foldBlocksAccumulator
case ret of
Left (IOE e) ->
H.failMessage callStack $ "foldBlocks failed with: " <> show e
Right (Left e) ->
H.failMessage callStack $ "foldBlocks failed with: " <> Text.unpack (renderFoldBlocksError e)
Right (Right _v) -> success


foldBlocksAccumulator
:: Env
-> LedgerState
-> [LedgerEvent]
-> BlockInMode -- Block i
-> [LedgerEvent] -- ^ Accumulator at block i - 1
-> IO ([LedgerEvent], FoldStatus) -- ^ Accumulator at block i and fold status
foldBlocksAccumulator _ _ allEvents _ _ =
if any filterPoolReap allEvents
then return (allEvents , StopFold)
else return ([], ContinueFold)
where
-- We end the fold on PoolReap ledger event
Copy link
Contributor

@carbolymer carbolymer Nov 2, 2023

Choose a reason for hiding this comment

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

that's not 100% true, because foldBlocks will continue iterating over knownLedgerStates' even when we return StopFold

Copy link
Contributor Author

Choose a reason for hiding this comment

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

filterPoolReap :: LedgerEvent -> Bool
filterPoolReap (PoolReap _) = True
filterPoolReap _ = False


Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ hprop_shutdown = H.integrationRetryWorkspace 2 "shutdown" $ \tempAbsBasePath' ->

return ()


hprop_shutdownOnSlotSynced :: Property
hprop_shutdownOnSlotSynced = H.integrationRetryWorkspace 2 "shutdown-on-slot-synced" $ \tempAbsBasePath' -> do
-- Start a local test net
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import qualified Cardano.Testnet.Test.Cli.Babbage.StakeSnapshot
import qualified Cardano.Testnet.Test.Cli.KesPeriodInfo
import qualified Cardano.Testnet.Test.Cli.QuerySlotNumber
import qualified Cardano.Testnet.Test.FoldBlocks
import qualified Cardano.Testnet.Test.Node.LedgerEvents
import qualified Cardano.Testnet.Test.Node.Shutdown

import Prelude
Expand All @@ -26,7 +27,8 @@ import qualified Testnet.Property.Run as H
tests :: IO TestTree
tests = pure $ T.testGroup "test/Spec.hs"
[ T.testGroup "Spec"
[ H.ignoreOnWindows "Shutdown" Cardano.Testnet.Test.Node.Shutdown.hprop_shutdown
[ H.ignoreOnWindows "Shutdown" Cardano.Testnet.Test.Node.Shutdown.hprop_shutdown
, H.ignoreOnWindows "LedgerEvents" Cardano.Testnet.Test.Node.LedgerEvents.hprop_ledger_events_sanity_check
, H.ignoreOnWindows "ShutdownOnSigint" Cardano.Testnet.Test.Node.Shutdown.hprop_shutdownOnSigint
-- ShutdownOnSlotSynced FAILS Still. The node times out and it seems the "shutdown-on-slot-synced" flag does nothing
-- , H.ignoreOnWindows "ShutdownOnSlotSynced" Cardano.Testnet.Test.Node.Shutdown.hprop_shutdownOnSlotSynced
Expand Down
Loading