hydra-cluster
Safe HaskellSafe-Inferred
LanguageGHC2021

HydraNode

Synopsis

Documentation

data HydraClient Source #

Constructors

HydraClient 

Fields

data HydraNodePorts Source #

The three ports a hydra-node binds: API, peer-to-peer listen, and the optional Prometheus monitoring endpoint. Callers allocate these via allocateHydraNodePorts (or pre-allocate a full cluster) and thread them through prepareHydraNode / withHydraNode.

Constructors

HydraNodePorts 

Fields

Instances

Instances details
Show HydraNodePorts Source # 
Instance details

Defined in HydraNode

Eq HydraNodePorts Source # 
Instance details

Defined in HydraNode

allocateHydraNodePortsFor :: [Int] -> IO (Map Int HydraNodePorts) Source #

Allocate ports for every node in a cluster up front. The returned map must be passed to every prepareHydraNode / withHydraNode call for nodes in this cluster so peers can be addressed correctly.

Listen ports are taken via randomUnusedTCPPortsWithDerived so each listen port's derived etcd client port — peerPortToClientPort — is actually held bound at allocation time. That defends against two failure modes: an unrelated process on the host occupying the derived port (which a plain randomUnusedTCPPorts would not catch and which explodes as EADDRINUSE the moment etcd starts), and an unlucky draw where the derived port lands on top of another node's api/monitoring port (which used to make the GRPC client talk to Warp instead of etcd).

Api and monitoring ports are then acquired in a second batch and checked to be disjoint from both the listen ports and the derived client ports; on collision we retry that second batch.

getProtocolParameters :: HydraClient -> IO (PParams LedgerEra) Source #

Get the protocol-parameters from the hydra-node.

getSnapshotConfirmed :: HydraClient -> IO (ConfirmedSnapshot Tx) Source #

Get the latest snapshot from the hydra-node. NOTE: While we usually avoid parsing responses using the same data types as the system under test, this parses the response as a ConfirmedSnapshot type as we often need to pick it apart.

getSnapshotUTxO :: HydraClient -> IO UTxO Source #

Get the latest snapshot UTxO from the hydra-node. NOTE: While we usually avoid parsing responses using the same data types as the system under test, this parses the response as a UTxO type as we often need to pick it apart.

input :: Text -> [Pair] -> Value Source #

Create an input as expected by send.

output :: Text -> [Pair] -> Value Source #

Create an output as expected by waitFor and waitForAll.

postDecommit :: HydraClient -> Tx -> IO () Source #

Submit a decommit transaction to the hydra-node.

prepareHydraNode :: HasCallStack => ChainConfig -> FilePath -> Int -> Secret (SigningKey HydraKey) -> [VerificationKey HydraKey] -> Map Int HydraNodePorts -> (Value -> Value) -> IO RunOptions Source #

Prepare RunOptions to run a hydra-node with given ChainConfig and using the config from config/.

The nodePorts map must contain an entry for this hydraNodeId and for every peer this node should connect to. Use allocateHydraNodePortsFor to build it for a cluster, or allocateHydraNodePorts + a singleton map for a standalone node.

requestCommitTx :: HydraClient -> UTxO -> IO Tx Source #

Helper to make it easy to obtain a commit tx using some wallet utxo

scaledFailAfter :: HasCallStack => NominalDiffTime -> IO a -> IO a Source #

failAfter with the budget scaled like scaleWaitTime. Use for whole-test backstops in end-to-end tests.

send :: HydraClient -> Value -> IO () Source #

waitFor :: HasCallStack => Tracer IO HydraNodeLog -> NominalDiffTime -> [HydraClient] -> Value -> IO () Source #

Wait some time for a single API server output from each of given nodes. This function waits for delay seconds for message expected to be seen by all given nodes.

waitForAllMatch :: (Eq a, Show a, HasCallStack) => NominalDiffTime -> [HydraClient] -> (Value -> Maybe a) -> IO a Source #

Wait up to some delay for some JSON Value to match given function.

This is a generalisation of waitMatch to multiple nodes.

waitForSnapshotUTxO :: HasCallStack => NominalDiffTime -> HydraClient -> UTxO -> IO () Source #

Wait for the node's confirmed snapshot to hold exactly the given UTxO.

NOTE: snapshotutxo serves the latest confirmed snapshot, which can still predate an increment or decommit just after its finalisation event. Sampling it once therefore races on a slow machine, reporting the UTxO as it was one snapshot ago. The last value seen is kept so that a genuine mismatch is still reported as a mismatch, rather than as a bare timeout.

waitMatch :: HasCallStack => NominalDiffTime -> HydraClient -> (Value -> Maybe a) -> IO a Source #

Wait up to some time for an API server output to match the given predicate. The budget is scaled to the environment, see scaleWaitTime.

withConnectionToNode :: forall a. Tracer IO HydraNodeLog -> Int -> Host -> Maybe PortNumber -> (HydraClient -> IO a) -> IO a Source #

withHydraCluster Source #

Arguments

:: HasCallStack 
=> Tracer IO HydraNodeLog 
-> Timing 
-> FilePath 
-> SocketPath 
-> Int

First node id This sets the starting point for assigning ports

-> [(VerificationKey PaymentKey, Secret (SigningKey PaymentKey))]

NOTE: This decides on the size of the cluster!

-> [Secret (SigningKey HydraKey)] 
-> [TxId]

Transaction ids at which Hydra scripts should have been published.

-> (NonEmpty HydraClient -> IO a) 
-> IO a 

withHydraNode :: HasCallStack => Tracer IO HydraNodeLog -> NominalDiffTime -> ChainConfig -> FilePath -> Int -> Secret (SigningKey HydraKey) -> [VerificationKey HydraKey] -> Map Int HydraNodePorts -> (HydraClient -> IO a) -> IO a Source #

Run a hydra-node just like withHydraNode; but before running any action, observe a Greetings message with the node in sync first. NOTE that importantly, any messages seen BEFORE we observe this will be lost; i.e. unobservable by subsequent waitFors.

See prepareHydraNode for how to build the port map.

withHydraNodeCatchingUp :: HasCallStack => Tracer IO HydraNodeLog -> ChainConfig -> FilePath -> Int -> Secret (SigningKey HydraKey) -> [VerificationKey HydraKey] -> Map Int HydraNodePorts -> (HydraClient -> IO a) -> IO a Source #

Run a hydra-node with given ChainConfig and using the config from config and catching up with chain backend/.

withPreparedHydraNode :: HasCallStack => Tracer IO HydraNodeLog -> FilePath -> Int -> RunOptions -> (HydraClient -> IO a) -> IO a Source #

Run a hydra-node with given RunOptions.

withPreparedHydraNodeWithEnv :: HasCallStack => [(String, String)] -> Tracer IO HydraNodeLog -> FilePath -> Int -> RunOptions -> (HydraClient -> IO a) -> IO a Source #

Like withPreparedHydraNode but with extra environment entries for the hydra-node process (also inherited by its etcd child). Use this instead of a process-global setEnv, which would leak into every other concurrently spawned node.

withSoloHydraNode :: HasCallStack => Tracer IO HydraNodeLog -> NominalDiffTime -> ChainConfig -> FilePath -> Int -> Secret (SigningKey HydraKey) -> [VerificationKey HydraKey] -> (HydraClient -> IO a) -> IO a Source #

Convenience: run a single hydra-node with no peers and freshly allocated dynamic ports. Equivalent to withHydraNode with a singleton port map.

withSoloHydraNodeCatchingUp :: HasCallStack => Tracer IO HydraNodeLog -> ChainConfig -> FilePath -> Int -> Secret (SigningKey HydraKey) -> [VerificationKey HydraKey] -> (HydraClient -> IO a) -> IO a Source #

Convenience: withHydraNodeCatchingUp for a single node with freshly allocated dynamic ports.

withUnsyncedHydraNode :: HasCallStack => Tracer IO HydraNodeLog -> ChainConfig -> FilePath -> Int -> Secret (SigningKey HydraKey) -> [VerificationKey HydraKey] -> Map Int HydraNodePorts -> (HydraClient -> IO a) -> IO a Source #

Run a hydra-node with given ChainConfig and using the config from config/, but, importantly, do NOT wait for the sync status to be reported.

withUnsyncedSoloHydraNode :: HasCallStack => Tracer IO HydraNodeLog -> ChainConfig -> FilePath -> Int -> Secret (SigningKey HydraKey) -> [VerificationKey HydraKey] -> (HydraClient -> IO a) -> IO a Source #

Convenience: withUnsyncedHydraNode for a single node with freshly allocated dynamic ports.

scaleWaitTime :: NominalDiffTime -> IO NominalDiffTime Source #

Scale a wait budget to the environment. Blockfrost runs triple it (the follower observes ~1 block behind tip plus one poll interval, on a network with much longer block times than the devnet timings most waits are written for) and HYDRA_TEST_WAIT_MULTIPLIER multiplies further; CI sets it to compensate for slow shared runners, local runs default to 1. Only failure latency is affected: a passing wait returns as soon as its message arrives.

Budgets get a constant floor: the many N * blockTime waits come to well under a second on the 0.1s devnet, underestimating the fixed costs they also cover (tx submission, observation, node processing). Sub-second waits fired exactly when several suites shared one machine.

waitForAll :: HasCallStack => Tracer IO HydraNodeLog -> NominalDiffTime -> [HydraClient] -> [Value] -> IO () Source #

Wait some time for a list of outputs from each of given nodes. This function is the generalised version of waitFor, allowing several messages to be waited for and received in any order.

waitNoMatch :: HasCallStack => NominalDiffTime -> HydraClient -> (Value -> Maybe a) -> IO () Source #

Wait up to some time and succeed if no API server output matches the given predicate. The window is deliberately NOT scaled by scaleWaitTime: the timeout here is the success path, so scaling it would slow every passing run.

waitMatchWith :: HasCallStack => NominalDiffTime -> HydraClient -> (Value -> Maybe a) -> IO a Source #

Like waitMatch but with the given wall-clock budget, unscaled.

withHydraClusterWith :: HasCallStack => Maybe String -> (RunOptions -> RunOptions) -> Tracer IO HydraNodeLog -> Timing -> FilePath -> SocketPath -> Int -> [(VerificationKey PaymentKey, Secret (SigningKey PaymentKey))] -> [Secret (SigningKey HydraKey)] -> [TxId] -> (NonEmpty HydraClient -> IO a) -> IO a Source #

Like withHydraCluster but connecting each node's API client with the given query string (e.g. "/?history=yes&snapshot-utxo=no") instead of the default "/?history=yes", and adjusting each node's RunOptions (e.g. to disable logging) before it is started.

withHydraNodeWith :: HasCallStack => Maybe String -> (RunOptions -> RunOptions) -> Tracer IO HydraNodeLog -> NominalDiffTime -> ChainConfig -> FilePath -> Int -> Secret (SigningKey HydraKey) -> [VerificationKey HydraKey] -> Map Int HydraNodePorts -> (HydraClient -> IO a) -> IO a Source #

Like withHydraNode but connecting the API client with the given query string instead of the default "/?history=yes", and adjusting the node's RunOptions before it is started.

allocateHydraNodePorts :: IO HydraNodePorts Source #

Allocate three unused TCP ports from the OS for a single hydra-node.

soloHydraNodePortsCache :: IORef (Map (FilePath, Int) HydraNodePorts) Source #

Process-global cache mapping each (workDir, nodeId) to its allocated ports. The cache exists because restart-style tests (re-running withSoloHydraNode or similar against the same workDir) depend on etcd's persistent cluster state, which is keyed by the listen URL. If a restart picked fresh ports, etcd would refuse to start.

soloHydraNodePortsFor :: FilePath -> Int -> IO HydraNodePorts Source #

Allocate ports for a single hydra-node, memoizing the result for the given (workDir, nodeId) so that subsequent calls reuse the same ports.

preparePParams :: ChainConfig -> FilePath -> (Value -> Value) -> IO FilePath Source #

Prepare protocol-parameters to run a hydra-node with given ChainConfig and using the config from config/.

withPreparedHydraNodeWithQuery :: HasCallStack => Maybe String -> [(String, String)] -> Tracer IO HydraNodeLog -> FilePath -> Int -> RunOptions -> (HydraClient -> IO a) -> IO a Source #

Like withPreparedHydraNode but connecting the API client with the given query string instead of the default "/?history=yes".

withConnectionToNodeHost :: forall a. Tracer IO HydraNodeLog -> Int -> Host -> Maybe PortNumber -> Maybe String -> (HydraClient -> IO a) -> IO a Source #

data HydraNodeLog Source #

Constructors

HydraNodeCommandSpec 

Fields

NodeStarted 

Fields

SentMessage 

Fields

StartWaiting 

Fields

ReceivedMessage 

Fields

EndWaiting 

Fields

Instances

Instances details
ToJSON HydraNodeLog Source # 
Instance details

Defined in CardanoNode

Methods

toJSON :: HydraNodeLog -> Value

toEncoding :: HydraNodeLog -> Encoding

toJSONList :: [HydraNodeLog] -> Value

toEncodingList :: [HydraNodeLog] -> Encoding

omitField :: HydraNodeLog -> Bool

Generic HydraNodeLog Source # 
Instance details

Defined in CardanoNode

Associated Types

type Rep HydraNodeLog :: Type -> Type Source #

Show HydraNodeLog Source # 
Instance details

Defined in CardanoNode

Eq HydraNodeLog Source # 
Instance details

Defined in CardanoNode

type Rep HydraNodeLog Source # 
Instance details

Defined in CardanoNode

type Rep HydraNodeLog = D1 ('MetaData "HydraNodeLog" "CardanoNode" "hydra-cluster-2.3.0-AKuIRMQTBGS8QbUdMcH2lm" 'False) ((C1 ('MetaCons "HydraNodeCommandSpec" 'PrefixI 'True) (S1 ('MetaSel ('Just "cmd") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text)) :+: (C1 ('MetaCons "NodeStarted" 'PrefixI 'True) (S1 ('MetaSel ('Just "nodeId") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Int)) :+: C1 ('MetaCons "SentMessage" 'PrefixI 'True) (S1 ('MetaSel ('Just "nodeId") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Int) :*: S1 ('MetaSel ('Just "message") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Value)))) :+: (C1 ('MetaCons "StartWaiting" 'PrefixI 'True) (S1 ('MetaSel ('Just "nodeIds") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [Int]) :*: S1 ('MetaSel ('Just "messages") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [Value])) :+: (C1 ('MetaCons "ReceivedMessage" 'PrefixI 'True) (S1 ('MetaSel ('Just "nodeId") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Int) :*: S1 ('MetaSel ('Just "message") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Value)) :+: C1 ('MetaCons "EndWaiting" 'PrefixI 'True) (S1 ('MetaSel ('Just "nodeId") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Int)))))