Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5d2e86d
Add behind firewall flag to local root config
the-headless-ghost Nov 8, 2025
c2e3eec
Add ConnectionMode to AcquireOutboundConnection
the-headless-ghost Nov 9, 2025
a12338a
CM and jobPromoteColdPeer fixes
the-headless-ghost Nov 17, 2025
405b005
Add diffusion property test
the-headless-ghost Nov 17, 2025
e8fcd54
Update cardano-diffusion/tests/lib/Test/Cardano/Network/Diffusion/Tes…
the-headless-ghost Dec 5, 2025
a496ced
Update ouroboros-network/framework/lib/Ouroboros/Network/ConnectionMa…
the-headless-ghost Dec 5, 2025
c3e7c29
Update cardano-diffusion/tests/lib/Test/Cardano/Network/Diffusion/Tes…
the-headless-ghost Dec 5, 2025
f0ef9bb
Update cardano-diffusion/tests/lib/Test/Cardano/Network/Diffusion/Tes…
the-headless-ghost Dec 5, 2025
b82fd8d
Naming fix
the-headless-ghost Dec 5, 2025
baa81e7
Update ouroboros-network/framework/lib/Ouroboros/Network/ConnectionMa…
the-headless-ghost Dec 5, 2025
b843f5c
swap establishPeerConnection arguments
the-headless-ghost Dec 5, 2025
1fedb07
Replace ConnectionMode with Provenance
the-headless-ghost Dec 5, 2025
00072a1
correct var binding name
the-headless-ghost Dec 5, 2025
09c19d2
Reuse FromJSON instance for RootConfig
the-headless-ghost Dec 5, 2025
a0b92e2
Remove ConnectionMode from the behind firewall changelog fragment
the-headless-ghost Dec 5, 2025
cc2519c
remove mutableConnState from failed aqcuire outbound connection
the-headless-ghost Dec 5, 2025
6858015
Rename connection mode variables
the-headless-ghost Dec 5, 2025
53beda8
EstablishedPeers cleanup
the-headless-ghost Dec 10, 2025
01d2727
Fix comment for AcquireOutboundConnection
the-headless-ghost Dec 10, 2025
34a5105
Replace local root peers fields with provenance fields
the-headless-ghost Dec 10, 2025
0ef4c30
Update changelog fragment
the-headless-ghost Dec 10, 2025
2e20fd0
Fix naming around TrInboundConnectionNotFound
the-headless-ghost Dec 10, 2025
8fecf76
Cleanup: Formatting, comments
the-headless-ghost Dec 10, 2025
0364702
Remove provenance from InboundConnectionNotFound
the-headless-ghost Dec 11, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!--
A new scriv changelog fragment.

Uncomment the section that is right (remove the HTML comment wrapper).
For top level release notes, leave all the headers commented out.
-->

### Non-Breaking

- Added a property test to verify that the node never connects to peers behind a firewall.
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ isValidTrustedPeerConfiguration
IsTrustable -> not
. null
. rootAccessPoints
. rootConfig
$ localRoots
) lprgs
158 changes: 128 additions & 30 deletions cardano-diffusion/tests/lib/Test/Cardano/Network/Diffusion/Testnet.hs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import Cardano.Network.PeerSelection
-- import Cardano.Network.PeerSelection.PublicRootPeers (PublicRootPeers (..))
import Cardano.Network.PeerSelection.PublicRootPeers qualified as Cardano.PublicRootPeers

import Ouroboros.Network.ConnectionManager.Types (Provenance(Outbound))
import Ouroboros.Network.DiffusionMode
import Ouroboros.Network.ExitPolicy (RepromoteDelay (..))
import Ouroboros.Network.PeerSelection
Expand Down Expand Up @@ -3206,7 +3207,6 @@ prop_governor_target_established_above (MaxTime maxTime) env =
<*> govInProgressIneligibleSig
<*> demotionOpportunitiesIgnoredTooLong)


-- | Like 'prop_governor_target_established_above' but for big ledger peers.
--
prop_governor_target_established_big_ledger_peers_above
Expand Down Expand Up @@ -4431,8 +4431,8 @@ prop_issue_3550 = prop_governor_target_established_below defaultMaxTime $
(PeerAddr 29,[],GovernorScripts {peerShareScript = Script (Nothing :| []), peerSharingScript = Script (PeerSharingDisabled :| []), connectionScript = Script ((ToWarm,NoDelay) :| [(ToCold,NoDelay),(Noop,NoDelay)])})
],
localRootPeers = LocalRootPeers.fromGroups
[ (1, 1, Map.fromList [(PeerAddr 16, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode IsNotTrustable)])
, (1, 1, Map.fromList [(PeerAddr 4, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode IsNotTrustable)])
[ (1, 1, Map.fromList [(PeerAddr 16, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode Outbound IsNotTrustable)])
, (1, 1, Map.fromList [(PeerAddr 4, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode Outbound IsNotTrustable)])
],
publicRootPeers = Cardano.PublicRootPeers.fromPublicRootPeers
(Map.fromList [ (PeerAddr 14, DoNotAdvertisePeer)
Expand Down Expand Up @@ -4479,7 +4479,7 @@ prop_issue_3515 = prop_governor_nolivelock $
peerSharingScript = Script (PeerSharingDisabled :| []),
connectionScript = Script ((ToCold,NoDelay) :| [(Noop,NoDelay)])
})],
localRootPeers = LocalRootPeers.fromGroups [(1,1,Map.fromList [(PeerAddr 10, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode IsNotTrustable)])],
localRootPeers = LocalRootPeers.fromGroups [(1,1,Map.fromList [(PeerAddr 10, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode Outbound IsNotTrustable)])],
publicRootPeers = PublicRootPeers.empty Cardano.ExtraPeers.empty,
targets = Script . NonEmpty.fromList $ targets'',
pickKnownPeersForPeerShare = Script (PickFirst :| []),
Expand Down Expand Up @@ -4521,7 +4521,7 @@ prop_issue_3494 = prop_governor_nofail $
peerSharingScript = Script (PeerSharingDisabled :| []),
connectionScript = Script ((ToCold,NoDelay) :| [(Noop,NoDelay)])
})],
localRootPeers = LocalRootPeers.fromGroups [(1,1,Map.fromList [(PeerAddr 64, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode IsNotTrustable)])],
localRootPeers = LocalRootPeers.fromGroups [(1,1,Map.fromList [(PeerAddr 64, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode Outbound IsNotTrustable)])],
publicRootPeers = PublicRootPeers.empty Cardano.ExtraPeers.empty,
targets = Script . NonEmpty.fromList $ targets'',
pickKnownPeersForPeerShare = Script (PickFirst :| []),
Expand Down Expand Up @@ -4571,8 +4571,8 @@ prop_issue_3233 = prop_governor_nolivelock $
(PeerAddr 15,[],GovernorScripts {peerShareScript = Script (Just ([],PeerShareTimeSlow) :| []), peerSharingScript = Script (PeerSharingDisabled :| []), connectionScript = Script ((Noop,NoDelay) :| [])})
],
localRootPeers = LocalRootPeers.fromGroups
[ (1, 1, Map.fromList [(PeerAddr 15, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode IsNotTrustable)])
, (1, 1, Map.fromList [(PeerAddr 13, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode IsNotTrustable)])
[ (1, 1, Map.fromList [(PeerAddr 15, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode Outbound IsNotTrustable)])
, (1, 1, Map.fromList [(PeerAddr 13, LocalRootConfig DoAdvertisePeer InitiatorAndResponderDiffusionMode Outbound IsNotTrustable)])
],
publicRootPeers = Cardano.PublicRootPeers.fromPublicRootPeers
(Map.fromList [(PeerAddr 4, DoNotAdvertisePeer)]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import Control.Monad.IOSim
import Control.Tracer (Tracer (..), contramap, traceWith)

import Ouroboros.Network.BlockFetch (FetchMode (..), PraosFetchMode (..))
import Ouroboros.Network.ConnectionManager.Types (Provenance)
import Ouroboros.Network.DiffusionMode
import Ouroboros.Network.ExitPolicy
import Ouroboros.Network.PeerSelection hiding (requestPublicRootPeers)
Expand Down Expand Up @@ -550,8 +551,8 @@ mockPeerSelectionActions' tracer
traceWith tracer (TraceEnvPeerShareResult addr peeraddrs)
return (PeerSharingResult peeraddrs)

establishPeerConnection :: IsBigLedgerPeer -> DiffusionMode -> PeerAddr -> m (PeerConn m)
establishPeerConnection _ _ peeraddr = do
establishPeerConnection :: IsBigLedgerPeer -> DiffusionMode -> Provenance -> PeerAddr -> m (PeerConn m)
establishPeerConnection _ _ _ peeraddr = do
--TODO: add support for variable delays and synchronous failure
traceWith tracer (TraceEnvEstablishConn peeraddr)
threadDelay 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!--
A new scriv changelog fragment.

Uncomment the section that is right (remove the HTML comment wrapper).
For top level release notes, leave all the headers commented out.
-->

### Breaking

- Changed the type of `localRoots` to `LocalRoots`.
- Modified `AcquireOutboundConnection` to include an additional parameter: `Provenance`.
- `acquireOutboundConnectionImpl` only creates a new connection if `Provenance` permits it.
- `jobPromoteColdPeer` only creates a new connection if no inbound connection is found and provenance is set to `Outbound`.

### Non-Breaking

- Added `LocalRoots` type in `Ouroboros.Network.PeerSelection.State.LocalRootPeers` with the following fields:
- `rootConfig` of type `RootConfig`
- `provenance` of type `Provenance`
- Added `localProvenance` field to `LocalRootConfig`.
- Added a new constructor `InboundConnectionNotFound` for `ConnectionManagerError`.
4 changes: 2 additions & 2 deletions ouroboros-network/demo/connection-manager.hs
Original file line number Diff line number Diff line change
Expand Up @@ -569,9 +569,9 @@ bidirectionalExperiment
(HandlerError
UnversionedProtocol))
connect n cm | n <= 1 =
acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr
acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr Outbound
connect n cm =
acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr
acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr Outbound
`catch` \(_ :: IOException) -> threadDelay 1
>> connect (pred n) cm
`catch` \(_ :: Mux.Error) -> threadDelay 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1338,12 +1338,12 @@ with args@Arguments {
-> ConnectionHandlerFn handlerTrace socket peerAddr handle handleError version versionData m
-> DiffusionMode
-> peerAddr
-> Provenance
-> m (Connected peerAddr handle handleError)
acquireOutboundConnectionImpl stateVar stdGenVar handler diffusionMode peerAddr = do
acquireOutboundConnectionImpl stateVar stdGenVar handler diffusionMode peerAddr connProv = do
let provenance = Outbound
traceWith tracer (TrIncludeConnection provenance peerAddr)
(trace, mutableConnState@MutableConnState { connVar, connStateId }
, eHandleWedge) <- atomically $ do
(trace, eHandleWedge) <- atomically $ do
state <- readTMVar stateVar
stdGen <- stateTVar stdGenVar Random.split
case State.lookupByRemoteAddr stdGen peerAddr state of
Expand All @@ -1353,14 +1353,12 @@ with args@Arguments {
case connState of
ReservedOutboundState ->
return ( Just (Right (TrConnectionExists provenance peerAddr st))
, mutableConnState
, Left (withCallStack
(ConnectionExists provenance peerAddr))
)

UnnegotiatedState Outbound _connId _connThread -> do
return ( Just (Right (TrConnectionExists provenance peerAddr st))
, mutableConnState
, Left (withCallStack
(ConnectionExists provenance peerAddr))
)
Expand All @@ -1370,34 +1368,29 @@ with args@Arguments {
-- return 'There' to indicate that we need to block on
-- the connection state.
return ( Nothing
, mutableConnState
, Right (There connId)
, Right (mutableConnState, There connId)
)

OutboundUniState {} -> do
return ( Just (Right (TrConnectionExists provenance peerAddr st))
, mutableConnState
, Left (withCallStack
(ConnectionExists provenance peerAddr))
)

OutboundDupState {} -> do
return ( Just (Right (TrConnectionExists provenance peerAddr st))
, mutableConnState
, Left (withCallStack
(ConnectionExists provenance peerAddr))
)

OutboundIdleState _connId _connThread _handle _dataFlow ->
let tr = State.abstractState (Known connState) in
return ( Just (Right (TrForbiddenOperation peerAddr tr))
, mutableConnState
, Left (withCallStack (ForbiddenOperation peerAddr tr))
)

InboundIdleState connId _connThread _handle Unidirectional -> do
return ( Just (Right (TrForbiddenConnection connId))
, mutableConnState
, Left (withCallStack
(ForbiddenConnection connId))
)
Expand All @@ -1412,15 +1405,13 @@ with args@Arguments {
return ( Just (Left (TransitionTrace
connStateId
(mkTransition connState connState')))
, mutableConnState
, Right (Here (Connected connId dataFlow handle))
, Right (mutableConnState, Here (Connected connId dataFlow handle))
)

InboundState connId _connThread _handle Unidirectional -> do
-- the remote side negotiated unidirectional connection, we
-- cannot re-use it.
return ( Just (Right (TrForbiddenConnection connId))
, mutableConnState
, Left (withCallStack
(ForbiddenConnection connId))
)
Expand All @@ -1435,13 +1426,11 @@ with args@Arguments {
return ( Just (Left (TransitionTrace
connStateId
(mkTransition connState connState')))
, mutableConnState
, Right (Here (Connected connId dataFlow handle))
, Right (mutableConnState, Here (Connected connId dataFlow handle))
)

DuplexState _connId _connThread _handle ->
return ( Just (Right (TrConnectionExists provenance peerAddr st))
, mutableConnState
, Left (withCallStack
(ConnectionExists provenance peerAddr))
)
Expand All @@ -1458,25 +1447,33 @@ with args@Arguments {
retry

Nothing -> do
let connState' = ReservedOutboundState
(mutableConnState@MutableConnState { connVar, connStateId }
:: MutableConnState peerAddr handle handleError
version m)
<- State.newMutableConnState peerAddr connStateIdSupply connState'
-- TODO: label `connVar` using 'ConnectionId'
labelTVar connVar ("conn-state-" ++ show peerAddr)

writeTMVar stateVar
(State.insertUnknownLocalAddr peerAddr mutableConnState state)
return ( Just (Left (TransitionTrace
connStateId
Transition {
fromState = Unknown,
toState = Known connState'
}))
, mutableConnState
, Right Nowhere
)
-- Only proceed if creating a new connection is allowed
if connProv == Inbound
then do
return ( Just (Right (TrInboundConnectionNotFound connProv peerAddr))
, Left (withCallStack
(InboundConnectionNotFound connProv peerAddr))
)
else do
let connState' = ReservedOutboundState
(mutableConnState@MutableConnState { connVar, connStateId }
:: MutableConnState peerAddr handle handleError
version m)
<- State.newMutableConnState peerAddr connStateIdSupply connState'

-- TODO: label `connVar` using 'ConnectionId'
labelTVar connVar ("conn-state-" ++ show peerAddr)

writeTMVar stateVar
(State.insertUnknownLocalAddr peerAddr mutableConnState state)
return ( Just (Left (TransitionTrace
connStateId
Transition {
fromState = Unknown,
toState = Known connState'
}))
, Right (mutableConnState, Nowhere)
)

traverse_ (either (traceWith trTracer) (traceWith tracer)) trace
traceCounters stateVar
Expand All @@ -1485,7 +1482,7 @@ with args@Arguments {
throwIO e

-- connection manager does not have a connection with @peerAddr@.
Right Nowhere -> do
Right (mutableConnState@MutableConnState { connVar, connStateId }, Nowhere) -> do
(reader, writer) <- newEmptyPromiseIO

(connId, connThread) <-
Expand Down Expand Up @@ -1719,7 +1716,7 @@ with args@Arguments {
Right _ -> Connected connId dataFlow handle
Left err -> Disconnected connId err

Right (There connId) -> do
Right (MutableConnState { connVar, connStateId }, There connId) -> do
-- We can only enter the 'There' case if there is an inbound
-- connection, and we are about to reuse it, but we need to wait
-- for handshake.
Expand Down Expand Up @@ -1811,7 +1808,7 @@ with args@Arguments {
return connected

-- Connection manager has a connection which can be reused.
Right (Here connected) -> do
Right (_, Here connected) -> do
traceCounters stateVar
return connected

Expand Down Expand Up @@ -2468,6 +2465,7 @@ withCallStack k = k callStack
--
data Trace peerAddr handlerTrace
= TrIncludeConnection Provenance peerAddr
| TrInboundConnectionNotFound Provenance peerAddr
| TrReleaseConnection Provenance (ConnectionId peerAddr)
| TrConnect (Maybe peerAddr) -- ^ local address
peerAddr -- ^ remote address
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,13 @@ data Connected peerAddr handle handleError =
--
| Disconnected !(ConnectionId peerAddr) !(DisconnectionException handleError)


-- | Acquire outbound connection
-- 'Inbound' provenance restricts acquiring an outbound connection to those
-- initiated by a remote peer.
-- 'Outbound' provenance doesn't require any additional pre-conditions.
type AcquireOutboundConnection peerAddr handle handleError m
= DiffusionMode -> peerAddr -> m (Connected peerAddr handle handleError)
= DiffusionMode -> peerAddr -> Provenance -> m (Connected peerAddr handle handleError)

type IncludeInboundConnection socket peerAddr handle handleError m
= Word32
-- ^ inbound connections hard limit.
Expand Down Expand Up @@ -723,6 +727,11 @@ data ConnectionManagerError peerAddr
--
| ForbiddenConnection !(ConnectionId peerAddr) !CallStack

-- | No matching inbound connection found while establishing a new connection is
-- not allowed.
--
| InboundConnectionNotFound !Provenance !peerAddr !CallStack

-- | Connections that would be forbidden by the kernel (@TCP@ semantics).
--
| ImpossibleConnection !(ConnectionId peerAddr) !CallStack
Expand Down Expand Up @@ -774,6 +783,14 @@ instance ( Show peerAddr
, "\n"
, prettyCallStack cs
]
displayException (InboundConnectionNotFound connMode peerAddr cs) =
concat [ "No matching inbound connection found and creating new connection is not allowed with peer "
, show connMode
, "\n"
, show peerAddr
, "\n"
, prettyCallStack cs
]
displayException (ConnectionTerminating connId cs) =
concat [ "Connection terminating "
, show connId
Expand Down
Loading
Loading