diff --git a/ouroboros-network-api/CHANGELOG.md b/ouroboros-network-api/CHANGELOG.md index 4567f4ec5b..d555c5f48b 100644 --- a/ouroboros-network-api/CHANGELOG.md +++ b/ouroboros-network-api/CHANGELOG.md @@ -6,6 +6,8 @@ ### Non-Breaking changes +* Added `OutboundConnectionsState` data type + ## 0.7.1.0 -- 2024-03-14 ### Breaking changes diff --git a/ouroboros-network-api/ouroboros-network-api.cabal b/ouroboros-network-api/ouroboros-network-api.cabal index bcd543f666..456749965b 100644 --- a/ouroboros-network-api/ouroboros-network-api.cabal +++ b/ouroboros-network-api/ouroboros-network-api.cabal @@ -43,6 +43,7 @@ library Ouroboros.Network.PeerSelection.Bootstrap Ouroboros.Network.PeerSelection.LedgerPeers.Type + Ouroboros.Network.PeerSelection.LocalRootPeers Ouroboros.Network.PeerSelection.PeerMetric.Type Ouroboros.Network.PeerSelection.PeerAdvertise Ouroboros.Network.PeerSelection.PeerTrustable diff --git a/ouroboros-network-api/src/Ouroboros/Network/PeerSelection/LocalRootPeers.hs b/ouroboros-network-api/src/Ouroboros/Network/PeerSelection/LocalRootPeers.hs new file mode 100644 index 0000000000..f917d97cf8 --- /dev/null +++ b/ouroboros-network-api/src/Ouroboros/Network/PeerSelection/LocalRootPeers.hs @@ -0,0 +1,35 @@ +{-# LANGUAGE DeriveGeneric #-} + +module Ouroboros.Network.PeerSelection.LocalRootPeers (OutboundConnectionsState (..)) where + +import GHC.Generics +import NoThunks.Class + +data OutboundConnectionsState = + TrustedStateWithExternalPeers + -- ^ + -- * /in bootstrap mode/: connected only to trusted local peers and at least + -- one bootstrap peer. + -- * /in genesis mode/: meeting target of active big ledger peers, ledger + -- state judgement must be `TooOld`. + + | EnteringTrustedStateWithExternalPeers + -- ^ entering trusted state with eternal peers. + -- + -- * /in bootstrap mode/: `requiredBootstrapPeers` returns `True` and has + -- at least on bootstrap peer. + -- * /in genesis mode/: ledger state judgement returns `TooOld` + + | HiddenRelayOrBP + -- ^ A node is classified as a hidden relay or a BP if it is configured so + -- that it can only have a chance to be connected to local roots. This is + -- true in one of the two ways: + -- + -- * it's configured with `DontUseBootstrapPeers` and `DontUseLedgerPeers`; or + -- * the targets are set so the node can only be connected to local roots. + + | UntrustedState + -- ^ catch all other cases + deriving (Eq, Show, Generic) + +instance NoThunks OutboundConnectionsState diff --git a/ouroboros-network/CHANGELOG.md b/ouroboros-network/CHANGELOG.md index 626be97cc3..7c71b06dbb 100644 --- a/ouroboros-network/CHANGELOG.md +++ b/ouroboros-network/CHANGELOG.md @@ -8,6 +8,9 @@ / known sets, and added `PeerSelectionCountersHWC` which provides sizes of hot / warm / cold sets. The counters cover more groups including: all peers, big ledger peers, bootstrap peers, local roots and shared peers. +* Added `daUpdateOutboundConnectionsState :: OutboundConnectionsState -> STM m ()` + to `Diffusion.Common.Applications`. This callback is to be provided by + consensus and is propagated all the way to the peer selection governor. ### Non-Breaking changes diff --git a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Diffusion/Node.hs b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Diffusion/Node.hs index 34f6e3ea9f..2b79d3d0e9 100644 --- a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Diffusion/Node.hs +++ b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Diffusion/Node.hs @@ -94,6 +94,7 @@ import Simulation.Network.Snocket (AddressType (..), FD) import Ouroboros.Network.PeerSelection.Bootstrap (UseBootstrapPeers) import Ouroboros.Network.PeerSelection.LedgerPeers.Type (LedgerPeersConsensusInterface, UseLedgerPeers) +import Ouroboros.Network.PeerSelection.LocalRootPeers (OutboundConnectionsState) import Ouroboros.Network.PeerSelection.PeerAdvertise (PeerAdvertise (..)) import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing (..)) import Ouroboros.Network.PeerSelection.PeerTrustable (PeerTrustable) @@ -124,6 +125,8 @@ data Interfaces m = Interfaces , iDomainMap :: StrictTVar m (Map Domain [(IP, TTL)]) , iLedgerPeersConsensusInterface :: LedgerPeersConsensusInterface m + , iUpdateOutboundConnectionsState + :: OutboundConnectionsState -> STM m () } type NtNFD m = FD m NtNAddr @@ -410,6 +413,8 @@ run blockGeneratorArgs limits ni na tracersExtra tracerBlockFetch = , Node.aaShouldChainSyncExit = aShouldChainSyncExit na , Node.aaChainSyncEarlyExit = aChainSyncEarlyExit na , Node.aaOwnPeerSharing = aOwnPeerSharing na + , Node.aaUpdateOutboundConnectionsState = + iUpdateOutboundConnectionsState ni } --- Utils diff --git a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Diffusion/Node/MiniProtocols.hs b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Diffusion/Node/MiniProtocols.hs index b3ec1c025b..e79f6e158a 100644 --- a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Diffusion/Node/MiniProtocols.hs +++ b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Diffusion/Node/MiniProtocols.hs @@ -88,6 +88,7 @@ import Ouroboros.Network.NodeToNode (blockFetchMiniProtocolNum, chainSyncMiniProtocolNum, keepAliveMiniProtocolNum, peerSharingMiniProtocolNum) import Ouroboros.Network.PeerSelection.LedgerPeers +import Ouroboros.Network.PeerSelection.LocalRootPeers (OutboundConnectionsState) import Ouroboros.Network.PeerSelection.PeerSharing qualified as PSTypes import Ouroboros.Network.PeerSharing (PeerSharingAPI, bracketPeerSharingClient, peerSharingClient, peerSharingServer) @@ -205,6 +206,8 @@ data AppArgs header block m = AppArgs , aaChainSyncEarlyExit :: Bool , aaOwnPeerSharing :: PSTypes.PeerSharing + , aaUpdateOutboundConnectionsState + :: OutboundConnectionsState -> STM m () } @@ -253,6 +256,7 @@ applications debugTracer nodeKernel , aaShouldChainSyncExit , aaChainSyncEarlyExit , aaOwnPeerSharing + , aaUpdateOutboundConnectionsState } toHeader = Diff.Applications @@ -270,6 +274,8 @@ applications debugTracer nodeKernel localResponderApp , Diff.daLedgerPeersCtx = aaLedgerPeersConsensusInterface + , Diff.daUpdateOutboundConnectionsState = + aaUpdateOutboundConnectionsState } where initiatorApp diff --git a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/PeerSelection.hs b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/PeerSelection.hs index 5c9dc32bd6..9667a82d12 100644 --- a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/PeerSelection.hs +++ b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/PeerSelection.hs @@ -31,6 +31,7 @@ module Test.Ouroboros.Network.PeerSelection import Control.Concurrent.Class.MonadSTM.Strict import Control.Exception (AssertionFailed (..), catch, evaluate) +import Control.Monad (when) import Control.Monad.Class.MonadTime.SI import Control.Monad.Class.MonadTimer.SI import Control.Tracer (Tracer (..)) @@ -51,6 +52,8 @@ import Data.Set qualified as Set import Data.Void (Void) import System.Random (mkStdGen) +import Cardano.Slotting.Slot (WithOrigin (..)) + import Network.DNS qualified as DNS (defaultResolvConf) import Network.Socket (SockAddr) @@ -62,6 +65,7 @@ import Ouroboros.Network.PeerSelection.Governor hiding (PeerSelectionState (..), peerSharing) import Ouroboros.Network.PeerSelection.Governor qualified as Governor import Ouroboros.Network.PeerSelection.LedgerPeers +import Ouroboros.Network.PeerSelection.LocalRootPeers (OutboundConnectionsState) import Ouroboros.Network.PeerSelection.PeerAdvertise import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing (..)) import Ouroboros.Network.PeerSelection.PeerTrustable (PeerTrustable (..)) @@ -3481,9 +3485,18 @@ _governorFindingPublicRoots :: Int -> STM IO UseBootstrapPeers -> STM IO LedgerStateJudgement -> PeerSharing + -> StrictTVar IO OutboundConnectionsState -> IO Void -_governorFindingPublicRoots targetNumberOfRootPeers readDomains readUseBootstrapPeers readLedgerStateJudgement peerSharing = do +_governorFindingPublicRoots targetNumberOfRootPeers readDomains readUseBootstrapPeers readLedgerStateJudgement peerSharing olocVar = do dnsSemaphore <- newLedgerAndPublicRootDNSSemaphore + let interfaces = PeerSelectionInterfaces { + ledgerPeersConsensusInterface = LedgerPeersConsensusInterface { + lpGetLatestSlot = return Origin, + lpGetLedgerStateJudgement = return YoungEnough, + lpGetLedgerPeers = return [] + }, + readUseLedgerPeers = return DontUseLedgerPeers + } publicRootPeersProvider tracer (curry IP.toSockAddr) @@ -3505,6 +3518,7 @@ _governorFindingPublicRoots targetNumberOfRootPeers readDomains readUseBootstrap { requestPublicRootPeers = \_ -> transformPeerSelectionAction requestPublicRootPeers } policy + interfaces where tracer :: Show a => Tracer IO a tracer = Tracer (BS.putStrLn . BS.pack . show) @@ -3526,8 +3540,12 @@ _governorFindingPublicRoots targetNumberOfRootPeers readDomains readUseBootstrap closePeerConnection = error "closePeerConnection" }, readUseBootstrapPeers, - readLedgerStateJudgement - } + readLedgerStateJudgement, + updateOutboundConnectionsState = \a -> do + a' <- readTVar olocVar + when (a /= a') $ + writeTVar olocVar a + } targets :: PeerSelectionTargets targets = nullPeerSelectionTargets { diff --git a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/PeerSelection/MockEnvironment.hs b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/PeerSelection/MockEnvironment.hs index 12597770e7..17161ee39e 100644 --- a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/PeerSelection/MockEnvironment.hs +++ b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/PeerSelection/MockEnvironment.hs @@ -43,9 +43,12 @@ import Data.Typeable (Typeable) import Data.Void (Void) import System.Random (mkStdGen) +import Cardano.Slotting.Slot (WithOrigin (..)) + import Control.Concurrent.Class.MonadSTM import Control.Concurrent.Class.MonadSTM.Strict qualified as StrictTVar import Control.Exception (throw) +import Control.Monad (when) import Control.Monad.Class.MonadAsync import Control.Monad.Class.MonadFork import Control.Monad.Class.MonadSay @@ -76,10 +79,9 @@ import Test.Ouroboros.Network.PeerSelection.PeerGraph import Ouroboros.Network.PeerSelection.Bootstrap (UseBootstrapPeers (..), requiresBootstrapPeers) -import Ouroboros.Network.PeerSelection.LedgerPeers (IsBigLedgerPeer, - LedgerPeersKind (..)) -import Ouroboros.Network.PeerSelection.LedgerPeers.Type - (LedgerStateJudgement (..)) +import Ouroboros.Network.PeerSelection.LedgerPeers +import Ouroboros.Network.PeerSelection.LocalRootPeers + (OutboundConnectionsState (..)) import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing (..)) import Ouroboros.Network.PeerSelection.PublicRootPeers (PublicRootPeers (..)) import Ouroboros.Network.PeerSelection.PublicRootPeers qualified as PublicRootPeers @@ -215,6 +217,17 @@ governorAction mockEnv = do countersVar <- StrictTVar.newTVarIO emptyPeerSelectionCounters policy <- mockPeerSelectionPolicy mockEnv actions <- mockPeerSelectionActions tracerMockEnv mockEnv (readTVar usbVar) (readTVar lsjVar) policy + let interfaces = PeerSelectionInterfaces { + -- peer selection tests are not relying on the ledger API + ledgerPeersConsensusInterface = LedgerPeersConsensusInterface { + lpGetLatestSlot = return Origin, + lpGetLedgerStateJudgement = return YoungEnough, + lpGetLedgerPeers = return [] + }, + -- peer selection tests are not relying on `UseLedgerPeers` + readUseLedgerPeers = return DontUseLedgerPeers + } + exploreRaces -- explore races within the governor _ <- forkIO $ do -- races with the governor should be explored labelThisThread "outbound-governor" @@ -228,6 +241,7 @@ governorAction mockEnv = do debugVar actions policy + interfaces atomically retry atomically retry -- block to allow the governor to run @@ -301,12 +315,14 @@ mockPeerSelectionActions tracer v (\_ a -> TraceDynamic . TraceEnvPeersStatus <$> snapshotPeersStatus proxy a) return v + + onlyLocalOutboundConnsVar <- newTVarIO UntrustedState traceWith tracer (TraceEnvAddPeers peerGraph) traceWith tracer (TraceEnvSetLocalRoots localRootPeers) --TODO: make dynamic traceWith tracer (TraceEnvSetPublicRoots publicRootPeers) --TODO: make dynamic return $ mockPeerSelectionActions' tracer env policy - scripts targetsVar readUseBootstrapPeers getLedgerStateJudgement peerConns + scripts targetsVar readUseBootstrapPeers getLedgerStateJudgement peerConns onlyLocalOutboundConnsVar where proxy :: Proxy m proxy = Proxy @@ -331,6 +347,7 @@ mockPeerSelectionActions' :: forall m. -> STM m UseBootstrapPeers -> STM m LedgerStateJudgement -> TVar m (Map PeerAddr (TVar m PeerStatus)) + -> TVar m OutboundConnectionsState -> PeerSelectionActions PeerAddr (PeerConn m) m mockPeerSelectionActions' tracer GovernorMockEnvironment { @@ -343,7 +360,8 @@ mockPeerSelectionActions' tracer targetsVar readUseBootstrapPeers readLedgerStateJudgement - connsVar = + connsVar + outboundConnectionsStateVar = PeerSelectionActions { readLocalRootPeers = return (LocalRootPeers.toGroups localRootPeers), peerSharing = peerSharing, @@ -360,7 +378,11 @@ mockPeerSelectionActions' tracer closePeerConnection }, readUseBootstrapPeers, - readLedgerStateJudgement + readLedgerStateJudgement, + updateOutboundConnectionsState = \a -> do + a' <- readTVar outboundConnectionsStateVar + when (a /= a') $ + writeTVar outboundConnectionsStateVar a } where -- TODO: make this dynamic diff --git a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Testnet/Simulation/Node.hs b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Testnet/Simulation/Node.hs index 21f1161a04..1233d3c762 100644 --- a/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Testnet/Simulation/Node.hs +++ b/ouroboros-network/sim-tests-lib/Test/Ouroboros/Network/Testnet/Simulation/Node.hs @@ -34,7 +34,7 @@ module Test.Ouroboros.Network.Testnet.Simulation.Node import Control.Applicative (Alternative) import Control.Concurrent.Class.MonadMVar (MonadMVar) import Control.Concurrent.Class.MonadSTM.Strict -import Control.Monad (forM) +import Control.Monad (forM, when) import Control.Monad.Class.MonadAsync import Control.Monad.Class.MonadFork import Control.Monad.Class.MonadSay @@ -120,6 +120,8 @@ import Data.Typeable (Typeable) import Ouroboros.Network.BlockFetch (FetchMode (..), TraceFetchClientState, TraceLabelPeer (..)) import Ouroboros.Network.PeerSelection.Bootstrap (UseBootstrapPeers (..)) +import Ouroboros.Network.PeerSelection.LocalRootPeers + (OutboundConnectionsState (..)) import Ouroboros.Network.PeerSelection.PeerAdvertise (PeerAdvertise (..)) import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing) import Ouroboros.Network.PeerSelection.PeerTrustable (PeerTrustable) @@ -1066,6 +1068,7 @@ diffusionSimulation dMapVar = do chainSyncExitVar <- newTVarIO chainSyncExitOnBlockNo ledgerPeersVar <- initScript' ledgerPeers + onlyOutboundConnectionsStateVar <- newTVarIO UntrustedState let (bgaRng, rng) = Random.split $ mkStdGen seed acceptedConnectionsLimit = AcceptedConnectionsLimit maxBound maxBound 0 @@ -1147,6 +1150,11 @@ diffusionSimulation $ accPoolStake $ getLedgerPools $ ledgerPools) + , NodeKernel.iUpdateOutboundConnectionsState = + \a -> do + a' <- readTVar onlyOutboundConnectionsStateVar + when (a /= a') $ + writeTVar onlyOutboundConnectionsStateVar a } shouldChainSyncExit :: StrictTVar m (Maybe BlockNo) -> BlockHeader -> m Bool diff --git a/ouroboros-network/src/Ouroboros/Network/Diffusion/Common.hs b/ouroboros-network/src/Ouroboros/Network/Diffusion/Common.hs index b69bd29ed8..4a692fd821 100644 --- a/ouroboros-network/src/Ouroboros/Network/Diffusion/Common.hs +++ b/ouroboros-network/src/Ouroboros/Network/Diffusion/Common.hs @@ -34,6 +34,7 @@ import Ouroboros.Network.NodeToNode qualified as NodeToNode import Ouroboros.Network.PeerSelection.Governor.Types (PublicPeerSelectionState) import Ouroboros.Network.PeerSelection.LedgerPeers.Type (LedgerPeersConsensusInterface) +import Ouroboros.Network.PeerSelection.LocalRootPeers (OutboundConnectionsState) import Ouroboros.Network.Snocket (FileDescriptor) import Ouroboros.Network.Socket (SystemdSocketTracer) @@ -193,4 +194,13 @@ data Applications ntnAddr ntnVersion ntnVersionData -- -- TODO: it should be in 'InterfaceExtra' , daLedgerPeersCtx :: LedgerPeersConsensusInterface m + + -- | Callback provided by consensus to inform it if the node is + -- connected to only local roots or also some external peers. + -- + -- This is useful in order for the Bootstrap State Machine to + -- simply refuse to transition from TooOld to YoungEnough while + -- it only has local peers. + -- + , daUpdateOutboundConnectionsState :: OutboundConnectionsState -> STM m () } diff --git a/ouroboros-network/src/Ouroboros/Network/Diffusion/P2P.hs b/ouroboros-network/src/Ouroboros/Network/Diffusion/P2P.hs index e883a7c754..582878adc5 100644 --- a/ouroboros-network/src/Ouroboros/Network/Diffusion/P2P.hs +++ b/ouroboros-network/src/Ouroboros/Network/Diffusion/P2P.hs @@ -103,9 +103,9 @@ import Ouroboros.Network.PeerSelection.Governor qualified as Governor import Ouroboros.Network.PeerSelection.Governor.Types (ChurnMode (ChurnModeNormal), DebugPeerSelection (..), PeerSelectionActions, PeerSelectionCounters, - PeerSelectionPolicy (..), PeerSelectionState, - TracePeerSelection (..), emptyPeerSelectionCounters, - emptyPeerSelectionState) + PeerSelectionInterfaces (..), PeerSelectionPolicy (..), + PeerSelectionState, TracePeerSelection (..), + emptyPeerSelectionCounters, emptyPeerSelectionState) #ifdef POSIX import Ouroboros.Network.PeerSelection.Governor.Types (makeDebugPeerSelectionState) @@ -646,6 +646,7 @@ runM Interfaces , daLedgerPeersCtx = daLedgerPeersCtx@LedgerPeersConsensusInterface { lpGetLedgerStateJudgement } + , daUpdateOutboundConnectionsState } ApplicationsExtra { daRethrowPolicy @@ -983,7 +984,8 @@ runM Interfaces psReadUseBootstrapPeers = daReadUseBootstrapPeers, psPeerSharing = daOwnPeerSharing, psPeerConnToPeerSharing = pchPeerSharing diNtnPeerSharing, - psReadPeerSharingController = readTVar (getPeerSharingRegistry daPeerSharingRegistry) } + psReadPeerSharingController = readTVar (getPeerSharingRegistry daPeerSharingRegistry), + psUpdateOutboundConnectionsState = daUpdateOutboundConnectionsState } WithLedgerPeersArgs { wlpRng = ledgerPeersRng, wlpConsensusInterface = daLedgerPeersCtx, @@ -1009,6 +1011,11 @@ runM Interfaces dbgVar peerSelectionActions peerSelectionPolicy + PeerSelectionInterfaces { + ledgerPeersConsensusInterface = daLedgerPeersCtx, + readUseLedgerPeers = daReadUseLedgerPeers + } + -- -- The peer churn governor: diff --git a/ouroboros-network/src/Ouroboros/Network/PeerSelection/Governor.hs b/ouroboros-network/src/Ouroboros/Network/PeerSelection/Governor.hs index 62f222cd7e..8a11a9dbe2 100644 --- a/ouroboros-network/src/Ouroboros/Network/PeerSelection/Governor.hs +++ b/ouroboros-network/src/Ouroboros/Network/PeerSelection/Governor.hs @@ -1,9 +1,15 @@ {-# LANGUAGE BangPatterns #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} +#if __GLASGOW_HASKELL__ < 904 +{-# OPTIONS_GHC -Wno-name-shadowing #-} +#endif + -- | This subsystem manages the discovery and selection of /upstream/ peers. -- module Ouroboros.Network.PeerSelection.Governor @@ -14,6 +20,7 @@ module Ouroboros.Network.PeerSelection.Governor PeerSelectionPolicy (..) , PeerSelectionTargets (..) , PeerSelectionActions (..) + , PeerSelectionInterfaces (..) , PeerStateActions (..) , TracePeerSelection (..) , ChurnAction (..) @@ -43,6 +50,7 @@ module Ouroboros.Network.PeerSelection.Governor import Data.Foldable (traverse_) import Data.Hashable +import Data.Set qualified as Set import Data.Void (Void) import Control.Applicative (Alternative ((<|>))) @@ -56,6 +64,8 @@ import Control.Monad.Class.MonadTimer.SI import Control.Tracer (Tracer (..), traceWith) import System.Random +import Ouroboros.Network.PeerSelection.Bootstrap (UseBootstrapPeers (..), + requiresBootstrapPeers) import Ouroboros.Network.PeerSelection.Churn (ChurnCounters (..), peerChurnGovernor) import Ouroboros.Network.PeerSelection.Governor.ActivePeers qualified as ActivePeers @@ -65,8 +75,15 @@ import Ouroboros.Network.PeerSelection.Governor.KnownPeers qualified as KnownPee import Ouroboros.Network.PeerSelection.Governor.Monitor qualified as Monitor import Ouroboros.Network.PeerSelection.Governor.RootPeers qualified as RootPeers import Ouroboros.Network.PeerSelection.Governor.Types +import Ouroboros.Network.PeerSelection.LedgerPeers (getLedgerPeers) +import Ouroboros.Network.PeerSelection.LedgerPeers.Common (LedgerPeers (..)) +import Ouroboros.Network.PeerSelection.LedgerPeers.Type + (LedgerStateJudgement (..), UseLedgerPeers (..)) +import Ouroboros.Network.PeerSelection.LocalRootPeers + (OutboundConnectionsState (..)) import Ouroboros.Network.PeerSelection.State.EstablishedPeers qualified as EstablishedPeers import Ouroboros.Network.PeerSelection.State.KnownPeers qualified as KnownPeers +import Ouroboros.Network.PeerSelection.State.LocalRootPeers qualified as LocalRootPeers {- $overview @@ -459,8 +476,11 @@ peerSelectionGovernor :: ( Alternative (STM m) -> StrictTVar m (PeerSelectionState peeraddr peerconn) -> PeerSelectionActions peeraddr peerconn m -> PeerSelectionPolicy peeraddr m + -> PeerSelectionInterfaces m -> m Void -peerSelectionGovernor tracer debugTracer countersTracer fuzzRng countersVar publicStateVar debugStateVar actions policy = +peerSelectionGovernor tracer debugTracer countersTracer fuzzRng + countersVar publicStateVar debugStateVar + actions policy interfaces = JobPool.withJobPool $ \jobPool -> peerSelectionGovernorLoop tracer @@ -471,6 +491,7 @@ peerSelectionGovernor tracer debugTracer countersTracer fuzzRng countersVar publ debugStateVar actions policy + interfaces jobPool (emptyPeerSelectionState fuzzRng) @@ -507,6 +528,7 @@ peerSelectionGovernorLoop :: forall m peeraddr peerconn. -> StrictTVar m (PeerSelectionState peeraddr peerconn) -> PeerSelectionActions peeraddr peerconn m -> PeerSelectionPolicy peeraddr m + -> PeerSelectionInterfaces m -> JobPool () m (Completion m peeraddr peerconn) -> PeerSelectionState peeraddr peerconn -> m Void @@ -518,6 +540,7 @@ peerSelectionGovernorLoop tracer debugStateVar actions policy + interfaces jobPool pst = do loop pst (Time 0) `catch` (\e -> traceWith tracer (TraceOutboundGovernorCriticalFailure e) >> throwIO e) @@ -558,13 +581,74 @@ peerSelectionGovernorLoop tracer -- get the current time after the governor returned from the blocking -- 'evalGuardedDecisions' call. now <- getMonotonicTime - let Decision { decisionTrace, decisionJobs, decisionState } = + + let Decision { decisionTrace, decisionJobs, decisionState = st'' } = timedDecision now mbCounters <- atomically $ do + -- Update outbound connections state + let peerSelectionView = peerSelectionStateToView st'' + PeerSelectionView { + viewEstablishedPeers, + viewEstablishedBootstrapPeers, + viewActiveBootstrapPeers, + viewEstablishedBigLedgerPeers + } + = + fst <$> peerSelectionView + PeerSelectionCounters { + numberOfActiveBigLedgerPeers + } = snd <$> peerSelectionView + PeerSelectionTargets { + targetNumberOfActiveBigLedgerPeers + } = targets st'' + + trustableLocalRootSet = LocalRootPeers.trustableKeysSet (localRootPeers st'') + + isHiddenRealyOrBP <- readIsHiddenRelayOrBP interfaces + (bootstrapPeersFlag st'') + (targets st'') + (peerSelectionStateToView st'') + + let !outboundConnectionsState = + case (isHiddenRealyOrBP, bootstrapPeersFlag st'') of + + (True, _) -> HiddenRelayOrBP + + -- bootstrap mode + (False, UseBootstrapPeers bootstrapPeers) + | -- we are only connected to trusted local root peers or bootstrap peers + viewEstablishedPeers `Set.isSubsetOf` (viewEstablishedBootstrapPeers <> trustableLocalRootSet) + -- there's at least one active bootstrap peer + , not (Set.null viewActiveBootstrapPeers) + -> TrustedStateWithExternalPeers + + | requiresBootstrapPeers (bootstrapPeersFlag st'') (ledgerStateJudgement st'') + , not (null bootstrapPeers) + -> EnteringTrustedStateWithExternalPeers + + | otherwise + -> UntrustedState + + -- genesis mode + (False, DontUseBootstrapPeers) + | -- we are only connected to trusted local root peers or bootstrap peers + TooOld <- ledgerStateJudgement st'' + , viewEstablishedPeers `Set.isSubsetOf` (viewEstablishedBigLedgerPeers <> trustableLocalRootSet) + , numberOfActiveBigLedgerPeers >= targetNumberOfActiveBigLedgerPeers + -> TrustedStateWithExternalPeers + + | TooOld <- ledgerStateJudgement st'' + -> EnteringTrustedStateWithExternalPeers + + | otherwise + -> UntrustedState + + updateOutboundConnectionsState actions outboundConnectionsState + -- Update counters counters <- readTVar countersVar - let !counters' = peerSelectionStateToCounters decisionState + let !counters' = snd <$> peerSelectionView if counters' /= counters then writeTVar countersVar counters' >> return (Just counters') @@ -572,11 +656,11 @@ peerSelectionGovernorLoop tracer -- Trace counters traverse_ (traceWith countersTracer) mbCounters - + -- Trace peer selection traverse_ (traceWith tracer) decisionTrace mapM_ (JobPool.forkJob jobPool) decisionJobs - loop decisionState dbgUpdateAt' + loop st'' dbgUpdateAt' evalGuardedDecisions :: Time -> PeerSelectionState peeraddr peerconn @@ -667,3 +751,53 @@ wakeupDecision st _now = decisionState = st, decisionJobs = [] } + + +-- | A node is classified as a hidden relay or a BP if it is configured so that +-- it can only have a chance to be connected to local roots. See `HiddenRelayOrBP`. +-- +-- In the second case mentioned in `HiddenRelayOrBP` we are slightly more strict. +-- We not only check the targets but also current `PeerSelectionView`. +-- +readIsHiddenRelayOrBP + :: ( MonadSTM m + , Ord peeraddr + ) + => PeerSelectionInterfaces m + -> UseBootstrapPeers + -> PeerSelectionTargets + -> PeerSelectionSetsWithSizes peeraddr + -> STM m Bool +readIsHiddenRelayOrBP + PeerSelectionInterfaces { + ledgerPeersConsensusInterface, + readUseLedgerPeers + } + useBootstrapPeers + PeerSelectionTargets { + targetNumberOfKnownBigLedgerPeers + } + PeerSelectionView { + viewKnownPeers = (knownPeers, _), + viewKnownLocalRootPeers = (knownLocalRootPeers, _), + viewKnownBigLedgerPeers = (knownBigLedgerPeers, _) + } + = + do useLedgerPeers <- readUseLedgerPeers + case useLedgerPeers of + DontUseLedgerPeers -> + return $ case useBootstrapPeers of + UseBootstrapPeers {} -> False + DontUseBootstrapPeers -> True + + UseLedgerPeers afterSlot -> do + ledgerPeers <- getLedgerPeers ledgerPeersConsensusInterface afterSlot + return $ case ledgerPeers of + BeforeSlot -> True + LedgerPeers {} -> + -- there are no big ledger peers + targetNumberOfKnownBigLedgerPeers == 0 + && (Set.null knownBigLedgerPeers) + -- all known peers are local roots; this implies that there no + -- bootstrap peers, nor ledger peers or shared peers + && knownPeers `Set.isSubsetOf` knownLocalRootPeers diff --git a/ouroboros-network/src/Ouroboros/Network/PeerSelection/Governor/Types.hs b/ouroboros-network/src/Ouroboros/Network/PeerSelection/Governor/Types.hs index e5144fdaab..be1b2b5263 100644 --- a/ouroboros-network/src/Ouroboros/Network/PeerSelection/Governor/Types.hs +++ b/ouroboros-network/src/Ouroboros/Network/PeerSelection/Governor/Types.hs @@ -29,6 +29,7 @@ module Ouroboros.Network.PeerSelection.Governor.Types -- These records are needed to run the peer selection. , PeerStateActions (..) , PeerSelectionActions (..) + , PeerSelectionInterfaces (..) , ChurnMode (..) -- * P2P governor internals , PeerSelectionState (..) @@ -143,7 +144,8 @@ import Ouroboros.Network.PeerSelection.Bootstrap (UseBootstrapPeers (..)) import Ouroboros.Network.PeerSelection.LedgerPeers (IsBigLedgerPeer, LedgerPeersKind) import Ouroboros.Network.PeerSelection.LedgerPeers.Type - (LedgerStateJudgement (..)) + (LedgerPeersConsensusInterface, LedgerStateJudgement (..), UseLedgerPeers (..)) +import Ouroboros.Network.PeerSelection.LocalRootPeers (OutboundConnectionsState) import Ouroboros.Network.PeerSelection.PeerAdvertise (PeerAdvertise) import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing) import Ouroboros.Network.PeerSelection.PeerTrustable (PeerTrustable) @@ -386,9 +388,31 @@ data PeerSelectionActions peeraddr peerconn m = PeerSelectionActions { -- | Read the current ledger state judgement -- - readLedgerStateJudgement :: STM m LedgerStateJudgement + readLedgerStateJudgement :: STM m LedgerStateJudgement, + + -- | Callback provided by consensus to inform it if the node is + -- connected to only local roots or also some external peers. + -- + -- This is useful in order for the Bootstrap State Machine to + -- simply refuse to transition from TooOld to YoungEnough while + -- it only has local peers. + -- + updateOutboundConnectionsState :: OutboundConnectionsState -> STM m () + } +-- | Interfaces required by the peer selection governor, which do not need to +-- be shared with actions and thus are not part of `PeerSelectionActions`. +-- +-- TODO: add other `TVar`s which outbound governor is using. +-- +data PeerSelectionInterfaces m = PeerSelectionInterfaces { + ledgerPeersConsensusInterface :: LedgerPeersConsensusInterface m, + + readUseLedgerPeers :: STM m UseLedgerPeers + } + + -- | Callbacks which are performed to change peer state. -- data PeerStateActions peeraddr peerconn m = PeerStateActions { @@ -622,8 +646,7 @@ makePublicPeerSelectionStateVar = newTVarIO emptyPublicPeerSelectionState -- toPublicState :: PeerSelectionState peeraddr peerconn -> PublicPeerSelectionState peeraddr -toPublicState PeerSelectionState { knownPeers - } = +toPublicState PeerSelectionState { knownPeers } = PublicPeerSelectionState { availableToShare = KnownPeers.getPeerSharingResponsePeers knownPeers diff --git a/ouroboros-network/src/Ouroboros/Network/PeerSelection/PeerSelectionActions.hs b/ouroboros-network/src/Ouroboros/Network/PeerSelection/PeerSelectionActions.hs index fd5fa8eed1..d3ad6ecbb6 100644 --- a/ouroboros-network/src/Ouroboros/Network/PeerSelection/PeerSelectionActions.hs +++ b/ouroboros-network/src/Ouroboros/Network/PeerSelection/PeerSelectionActions.hs @@ -37,6 +37,7 @@ import Ouroboros.Network.PeerSelection.Bootstrap (UseBootstrapPeers (..), requiresBootstrapPeers) import Ouroboros.Network.PeerSelection.Governor.Types import Ouroboros.Network.PeerSelection.LedgerPeers hiding (getLedgerPeers) +import Ouroboros.Network.PeerSelection.LocalRootPeers (OutboundConnectionsState) import Ouroboros.Network.PeerSelection.PeerAdvertise (PeerAdvertise (..)) import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing) import Ouroboros.Network.PeerSelection.PeerTrustable (PeerTrustable) @@ -65,8 +66,11 @@ data PeerSelectionActionsArgs peeraddr peerconn exception m = PeerSelectionActio -- ^ peer sharing configured value psPeerConnToPeerSharing :: peerconn -> PeerSharing, -- ^ Extract peer sharing information from peerconn - psReadPeerSharingController :: STM m (Map peeraddr (PeerSharingController peeraddr m)) + psReadPeerSharingController :: STM m (Map peeraddr (PeerSharingController peeraddr m)), -- ^ peer sharing registry + psUpdateOutboundConnectionsState + :: OutboundConnectionsState -> STM m () + -- ^ Callback which updates information about outbound connections state. } -- | Record of remaining parameters for withPeerSelectionActions @@ -111,7 +115,8 @@ withPeerSelectionActions psReadUseBootstrapPeers = useBootstrapped, psPeerSharing = sharing, psPeerConnToPeerSharing = peerConnToPeerSharing, - psReadPeerSharingController = sharingController } + psReadPeerSharingController = sharingController, + psUpdateOutboundConnectionsState = updateOutboundConnectionsState } ledgerPeersArgs PeerSelectionActionsDiffusionMode { psNewInboundConnections = readNewInboundConnections, psPeerStateActions = peerStateActions } k = do @@ -131,7 +136,8 @@ withPeerSelectionActions requestPeerShare, peerStateActions, readUseBootstrapPeers = useBootstrapped, - readLedgerStateJudgement = judgement } + readLedgerStateJudgement = judgement, + updateOutboundConnectionsState } withAsync (localRootPeersProvider localTracer diff --git a/ouroboros-network/src/Ouroboros/Network/PeerSelection/State/LocalRootPeers.hs b/ouroboros-network/src/Ouroboros/Network/PeerSelection/State/LocalRootPeers.hs index a01a761392..fac3f731f4 100644 --- a/ouroboros-network/src/Ouroboros/Network/PeerSelection/State/LocalRootPeers.hs +++ b/ouroboros-network/src/Ouroboros/Network/PeerSelection/State/LocalRootPeers.hs @@ -23,6 +23,7 @@ module Ouroboros.Network.PeerSelection.State.LocalRootPeers , toGroupSets , toMap , keysSet + , trustableKeysSet -- * Special operations , clampToLimit , clampToTrustable @@ -251,3 +252,12 @@ isPeerTrustable peeraddr lrp = case Map.lookup peeraddr (toMap lrp) of Just (_, IsTrustable) -> True _ -> False + +trustableKeysSet :: LocalRootPeers peeraddr + -> Set peeraddr +trustableKeysSet (LocalRootPeers m _) = + Map.keysSet + . Map.filter (\(_, trustable) -> case trustable of + IsTrustable -> True + IsNotTrustable -> False) + $ m