Skip to content

Commit

Permalink
Bring back PeerConnected and PeerDisconnected
Browse files Browse the repository at this point in the history
Indicate connectivity to peers by also switching from 'NodeId' to
'Host'. This will make sure that the reported connection/disconnection
is associated with --peer command line arguments.
  • Loading branch information
ch1bo committed Feb 22, 2025
1 parent e1a6272 commit 3e52f27
Show file tree
Hide file tree
Showing 16 changed files with 197 additions and 68 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ changes.
## [0.21.0] - UNRELEASED

- **BREAKING** Switch to using `etcd` internally to establish a reliable L2 network
- Drops `PeerConnected` and `PeerDisconnected` messages in favor of `NetworkConnected` and `NetworkDisconnected` equivalents. These now indicate overall connectivity to the L2 network (which corresponds to the `etcd` cluster).
- New run-time dependency onto `etcd` binary
- Adds `NetworkConnected` and `NetworkDisconnected` outputs which are most indicative of whether the L2 network is up or not.
- Change `PeerConnected` and `PeerDisconnected` to indicate connectivity to `--peer` items and not the remote `node-id`.
- Log outputs related to the network components changed significantly.
- Persisted state (write ahead logs) of the network components changed significantly.

Expand All @@ -20,7 +22,7 @@ changes.
- An example was if the datum would contain CBOR instead of just hex encoded bytes.

- **BREAKING** Enable multi-party, networked "offline" heads by providing an `--offline-head-seed` option to `hydra-node`.
- Drop `hydra-nodde offline` as a sub-command. Use `--offline-head-seed` and `--initial-utxo` options to switch to offline mode.
- Drop `hydra-node offline` as a sub-command. Use `--offline-head-seed` and `--initial-utxo` options to switch to offline mode.

- Stream historical data from disk in the hydra-node API server.

Expand Down
8 changes: 8 additions & 0 deletions hydra-node/golden/ServerOutput/NetworkConnected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"samples": [
{
"tag": "NetworkConnected"
}
],
"seed": -2100000504
}
8 changes: 8 additions & 0 deletions hydra-node/golden/ServerOutput/NetworkDisconnected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"samples": [
{
"tag": "NetworkDisconnected"
}
],
"seed": -1678395060
}
7 changes: 5 additions & 2 deletions hydra-node/golden/ServerOutput/PeerConnected.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"samples": [
{
"peer": "qitfwqawhsprnrebktoraqk",
"peer": {
"hostname": "0.0.122.110",
"port": 1964
},
"tag": "PeerConnected"
}
],
"seed": 662696084
"seed": 1214056638
}
7 changes: 5 additions & 2 deletions hydra-node/golden/ServerOutput/PeerDisconnected.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"samples": [
{
"peer": "zocryeobfgljgxe",
"peer": {
"hostname": "0.0.47.58",
"port": 17398
},
"tag": "PeerDisconnected"
}
],
"seed": -1791938521
"seed": 966094347
}
83 changes: 78 additions & 5 deletions hydra-node/json-schemas/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ channels:
message:
oneOf:
- $ref: "api.yaml#/components/messages/Greetings"
- $ref: "api.yaml#/components/messages/PeerConnected"
- $ref: "api.yaml#/components/messages/PeerDisconnected"
- $ref: "api.yaml#/components/messages/NetworkConnected"
- $ref: "api.yaml#/components/messages/NetworkDisconnected"
- $ref: "api.yaml#/components/messages/PeerHandshakeFailure"
Expand Down Expand Up @@ -458,6 +460,9 @@ components:
$ref: "api.yaml#/components/schemas/Greetings"

NetworkConnected:
title: NetworkConnected
description: |
Node connected to the L2 Hydra network.
payload:
$ref: "api.yaml#/components/schemas/NetworkConnected"

Expand All @@ -468,6 +473,21 @@ components:
payload:
$ref: "api.yaml#/components/schemas/NetworkDisconnected"

PeerConnected:
title: PeerConnected
description: |
A peer appeared alive on the L2 Hydra network.
payload:
$ref: "api.yaml#/components/schemas/PeerConnected"

PeerDisconnected:
title: PeerDisconnected
description: |
A peer disappeared (has not been seen active in a while) from the L2
Hydra network.
payload:
$ref: "api.yaml#/components/schemas/PeerDisconnected"

PeerHandshakeFailure:
title: PeerHandshakeFailure
description: |
Expand Down Expand Up @@ -707,6 +727,8 @@ components:
- $ref: "api.yaml#/components/schemas/Greetings"
- $ref: "api.yaml#/components/schemas/NetworkConnected"
- $ref: "api.yaml#/components/schemas/NetworkDisconnected"
- $ref: "api.yaml#/components/schemas/PeerConnected"
- $ref: "api.yaml#/components/schemas/PeerDisconnected"
- $ref: "api.yaml#/components/schemas/PeerHandshakeFailure"
- $ref: "api.yaml#/components/schemas/HeadIsInitializing"
- $ref: "api.yaml#/components/schemas/Committed"
Expand Down Expand Up @@ -761,9 +783,6 @@ components:
$ref: "api.yaml#/components/schemas/HydraNodeVersion"

NetworkConnected:
title: NetworkConnected
description: |
Node connected to the L2 Hydra network.
type: object
required:
- tag
Expand All @@ -787,12 +806,49 @@ components:
properties:
tag:
type: string
enum: ["NetworkConnected"]
enum: ["NetworkDisconnected"]
seq:
$ref: "api.yaml#/components/schemas/SequenceNumber"
timestamp:
$ref: "api.yaml#/components/schemas/UTCTime"

PeerConnected:
type: object
required:
- tag
- peer
- seq
- timestamp
properties:
tag:
type: string
enum: ["PeerConnected"]
peer:
$ref: "api.yaml#/components/schemas/Host"
seq:
$ref: "api.yaml#/components/schemas/SequenceNumber"
timestamp:
$ref: "api.yaml#/components/schemas/UTCTime"

PeerDisconnected:
type: object
required:
- tag
- peer
- seq
- timestamp
properties:
tag:
type: string
enum: ["PeerDisconnected"]
peer:
$ref: "api.yaml#/components/schemas/Host"
seq:
$ref: "api.yaml#/components/schemas/SequenceNumber"
timestamp:
$ref: "api.yaml#/components/schemas/UTCTime"

# TODO: Update to indicate network-level incompatibility
PeerHandshakeFailure:
type: object
required:
Expand All @@ -808,7 +864,7 @@ components:
enum: ["PeerHandshakeFailure"]
remoteHost:
type: object
$ref: "api.yaml#/components/schemas/IP"
$ref: "api.yaml#/components/schemas/Host"
ourVersion:
type: integer
minimum: 0
Expand Down Expand Up @@ -2640,6 +2696,23 @@ components:
# NOTE: We don't want anyone to depend on this!
ChainState: {}

Host:
description: |
Describes a host we can connect to using TCP.
type: object
required:
- hostname
- port
properties:
hostname:
type: string
port:
type: integer
minimum: 0
maximum: 65535
example: 5000


IP:
type: object
oneOf:
Expand Down
6 changes: 6 additions & 0 deletions hydra-node/src/Hydra/API/ServerOutput.hs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ instance ArbitraryIsTx tx => Arbitrary (DecommitInvalidReason tx) where
data ServerOutput tx
= NetworkConnected
| NetworkDisconnected
| PeerConnected {peer :: Host}
| PeerDisconnected {peer :: Host}
| PeerHandshakeFailure
{ remoteHost :: Host
, ourVersion :: Natural
Expand Down Expand Up @@ -168,6 +170,8 @@ instance (ArbitraryIsTx tx, IsChainState tx) => Arbitrary (ServerOutput tx) wher
-- Overlapping instances with 'UTxOType tx' even though for a fixed `tx`, there
-- should be only one 'UTxOType tx'
shrink = \case
PeerConnected p -> PeerConnected <$> shrink p
PeerDisconnected p -> PeerDisconnected <$> shrink p
NetworkConnected -> pure NetworkConnected
NetworkDisconnected -> pure NetworkDisconnected
PeerHandshakeFailure rh ov tv -> PeerHandshakeFailure <$> shrink rh <*> shrink ov <*> shrink tv
Expand Down Expand Up @@ -235,6 +239,8 @@ prepareServerOutput ::
LBS.ByteString
prepareServerOutput ServerOutputConfig{utxoInSnapshot} response =
case output response of
PeerConnected{} -> encodedResponse
PeerDisconnected{} -> encodedResponse
NetworkConnected{} -> encodedResponse
NetworkDisconnected{} -> encodedResponse
PeerHandshakeFailure{} -> encodedResponse
Expand Down
9 changes: 6 additions & 3 deletions hydra-node/src/Hydra/HeadLogic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,14 @@ defaultTTL = 5

onConnectionEvent :: Connectivity -> Outcome tx
onConnectionEvent = \case
-- FIXME: bring back PeerConnected and PeerDisconnected
NetworkConnected{} ->
NetworkConnected ->
causes [ClientEffect ServerOutput.NetworkConnected]
NetworkDisconnected{} ->
NetworkDisconnected ->
causes [ClientEffect ServerOutput.NetworkDisconnected]
PeerConnected{peer} ->
causes [ClientEffect ServerOutput.PeerConnected{peer}]
PeerDisconnected{peer} ->
causes [ClientEffect ServerOutput.PeerDisconnected{peer}]
HandshakeFailure{remoteHost, ourVersion, theirVersions} ->
causes
[ ClientEffect
Expand Down
46 changes: 23 additions & 23 deletions hydra-node/src/Hydra/Network.hs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,25 @@ deriving anyclass instance FromJSON IP

-- ** PortNumber (Orphans)

readPort :: MonadFail m => String -> m PortNumber
readPort s =
case readMaybe s of
Nothing -> fail "cannot read port"
Just n
| n >= minPort && n <= maxPort -> pure $ fromInteger n
| otherwise ->
fail $
"readPort: "
<> show n
<> " not within valid port range: ("
<> show minPort
<> ", "
<> show maxPort
<> ")"
where
maxPort = fromIntegral (maxBound :: Word16)
minPort = fromIntegral (minBound :: Word16)

instance ToJSON PortNumber where
toJSON = toJSON . toInteger

Expand All @@ -105,15 +124,15 @@ instance FromJSON PortNumber where
instance Arbitrary PortNumber where
arbitrary = fromIntegral @Word16 <$> arbitrary

-- ** NodeId

newtype NodeId = NodeId {nodeId :: Text}
deriving newtype (Eq, Show, IsString, ToString, Read, Ord, ToJSON, FromJSON)

instance Arbitrary NodeId where
arbitrary =
NodeId . pack <$> suchThat (listOf (elements ['a' .. 'z'])) (not . null)

-- return $ NodeId $ pack c

instance FromCBOR NodeId where
fromCBOR = NodeId <$> fromCBOR

Expand Down Expand Up @@ -163,34 +182,15 @@ readHost s =
(h, ':' : p) -> Host (pack h) <$> readPort p
_ -> fail $ "readHost: missing : in " <> s

readPort :: MonadFail m => String -> m PortNumber
readPort s =
case readMaybe s of
Nothing -> fail "cannot read port"
Just n
| n >= minPort && n <= maxPort -> pure $ fromInteger n
| otherwise ->
fail $
"readPort: "
<> show n
<> " not within valid port range: ("
<> show minPort
<> ", "
<> show maxPort
<> ")"
where
maxPort = fromIntegral (maxBound :: Word16)
minPort = fromIntegral (minBound :: Word16)

-- ** Connectivity & versions

-- TODO: improve these types

data Connectivity
= -- | Individual peer appeared alive on network.
Connected {peer :: Host}
PeerConnected {peer :: Host}
| -- | Individual peer disappeared from network (has not been seen active in a while).
Disconnected {peer :: Host}
PeerDisconnected {peer :: Host}
| -- | Connected to Hydra network.
NetworkConnected
| -- | Disconnected from Hydra network.
Expand Down
4 changes: 2 additions & 2 deletions hydra-node/src/Hydra/Network/Etcd.hs
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,8 @@ pollConnectivity conn localHost NetworkCallback{onConnectivity} = do
alive <- getAlive
let othersAlive = alive \\ [localHost]
seenAlive <- atomically $ swapTVar seenAliveVar othersAlive
forM_ (othersAlive \\ seenAlive) $ onConnectivity . Connected
forM_ (seenAlive \\ othersAlive) $ onConnectivity . Disconnected
forM_ (othersAlive \\ seenAlive) $ onConnectivity . PeerConnected
forM_ (seenAlive \\ othersAlive) $ onConnectivity . PeerDisconnected
threadDelay 1
where
ttl = 3
Expand Down
4 changes: 2 additions & 2 deletions hydra-node/src/Hydra/Network/Heartbeat.hs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ updateStateFromIncomingMessages heartbeatState callback =
notifyAlive peer = do
now <- getMonotonicTime
aliveSet <- alive <$> readTVarIO heartbeatState
unless (peer `Map.member` aliveSet) $ onConnectivity (Connected peer)
unless (peer `Map.member` aliveSet) $ onConnectivity (PeerConnected peer)
atomically $
modifyTVar' heartbeatState $ \s ->
s
Expand Down Expand Up @@ -177,7 +177,7 @@ checkRemoteParties heartbeatState onConnectivity =
threadDelay (heartbeatDelay * 2)
now <- getMonotonicTime
updateSuspected heartbeatState now
>>= mapM_ (onConnectivity . Disconnected)
>>= mapM_ (onConnectivity . PeerDisconnected)

updateSuspected :: MonadSTM m => TVar m HeartbeatState -> Time -> m (Set Host)
updateSuspected heartbeatState now =
Expand Down
Loading

0 comments on commit 3e52f27

Please sign in to comment.