~zinigor/cardano-node/trunk

« back to all changes in this revision

Viewing changes to cardano-node/src/Cardano/Node/Run.hs

  • Committer: Igor Zinovyev
  • Date: 2021-08-13 19:12:27 UTC
  • Revision ID: zinigor@gmail.com-20210813191227-stlnsj3mc5ypwn0c
Tags: upstream-1.27.0
ImportĀ upstreamĀ versionĀ 1.27.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
{-# LANGUAGE BangPatterns #-}
 
2
{-# LANGUAGE CPP #-}
 
3
{-# LANGUAGE DataKinds #-}
 
4
{-# LANGUAGE FlexibleContexts #-}
 
5
{-# LANGUAGE GADTs #-}
 
6
{-# LANGUAGE NamedFieldPuns #-}
 
7
{-# LANGUAGE ScopedTypeVariables #-}
 
8
 
 
9
#if !defined(mingw32_HOST_OS)
 
10
#define UNIX
 
11
#endif
 
12
 
 
13
module Cardano.Node.Run
 
14
  ( runNode
 
15
  , checkVRFFilePermissions
 
16
  ) where
 
17
 
 
18
import           Cardano.Prelude hiding (ByteString, atomically, take, trace)
 
19
import           Prelude (String)
 
20
 
 
21
import qualified Control.Concurrent.Async as Async
 
22
import           Control.Monad.Trans.Except.Extra (left)
 
23
import           Control.Tracer
 
24
import           Data.Text (breakOn, pack, take)
 
25
import qualified Data.Text as Text
 
26
import           Data.Time.Clock (getCurrentTime)
 
27
import           Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
 
28
import           Data.Version (showVersion)
 
29
import           Network.HostName (getHostName)
 
30
import           Network.Socket (AddrInfo, Socket)
 
31
import           System.Directory (canonicalizePath, createDirectoryIfMissing, makeAbsolute)
 
32
import           System.Environment (lookupEnv)
 
33
#ifdef UNIX
 
34
import           System.Posix.Files
 
35
import           System.Posix.Types (FileMode)
 
36
#else
 
37
import           System.Win32.File
 
38
#endif
 
39
 
 
40
import           Cardano.BM.Data.LogItem (LOContent (..), LogObject (..), PrivacyAnnotation (..),
 
41
                   mkLOMeta)
 
42
import           Cardano.BM.Data.Tracer (ToLogObject (..), TracingVerbosity (..))
 
43
import           Cardano.BM.Data.Transformers (setHostname)
 
44
import           Cardano.BM.Trace
 
45
import           Paths_cardano_node (version)
 
46
 
 
47
import qualified Cardano.Crypto.Libsodium as Crypto
 
48
 
 
49
import           Cardano.Node.Configuration.Logging (LoggingLayer (..), Severity (..),
 
50
                   createLoggingLayer, nodeBasicInfo, shutdownLoggingLayer)
 
51
import           Cardano.Node.Configuration.POM (NodeConfiguration (..),
 
52
                   PartialNodeConfiguration (..), defaultPartialNodeConfiguration,
 
53
                   makeNodeConfiguration, parseNodeConfigurationFP)
 
54
import           Cardano.Node.Types
 
55
import           Cardano.Tracing.Config (TraceOptions (..), TraceSelection (..))
 
56
import           Cardano.Tracing.Constraints (TraceConstraints)
 
57
import           Cardano.Tracing.Metrics (HasKESMetricsData (..), HasKESInfo (..))
 
58
 
 
59
import qualified Ouroboros.Consensus.Config as Consensus
 
60
import           Ouroboros.Consensus.Config.SupportsNode (getNetworkMagic)
 
61
import           Ouroboros.Consensus.Node (DiffusionArguments (..), DiffusionTracers (..),
 
62
                   DnsSubscriptionTarget (..), IPSubscriptionTarget (..), RunNode, RunNodeArgs (..),
 
63
                   StdRunNodeArgs (..))
 
64
import qualified Ouroboros.Consensus.Node as Node (getChainDB, run)
 
65
import           Ouroboros.Consensus.Node.ProtocolInfo
 
66
import           Ouroboros.Consensus.Util.Orphans ()
 
67
import           Ouroboros.Network.Magic (NetworkMagic (..))
 
68
import           Ouroboros.Network.NodeToNode (AcceptedConnectionsLimit (..), DiffusionMode)
 
69
 
 
70
import qualified Cardano.Api.Protocol.Types as Protocol
 
71
import           Cardano.Node.Configuration.Socket (SocketOrSocketInfo (..),
 
72
                   gatherConfiguredSockets, getSocketOrSocketInfoAddr, renderSocketConfigError)
 
73
import           Cardano.Node.Configuration.Topology
 
74
import           Cardano.Node.Handlers.Shutdown
 
75
import           Cardano.Node.Protocol (mkConsensusProtocol, renderProtocolInstantiationError)
 
76
import           Cardano.Node.Protocol.Types
 
77
import           Cardano.Tracing.Kernel
 
78
import           Cardano.Tracing.Peer
 
79
import           Cardano.Tracing.Tracers
 
80
 
 
81
{- HLINT ignore "Use fewer imports" -}
 
82
 
 
83
runNode
 
84
  :: PartialNodeConfiguration
 
85
  -> IO ()
 
86
runNode cmdPc = do
 
87
    -- TODO: Remove sodiumInit: https://github.com/input-output-hk/cardano-base/issues/175
 
88
    Crypto.sodiumInit
 
89
 
 
90
    configYamlPc <- parseNodeConfigurationFP . getLast $ pncConfigFile cmdPc
 
91
 
 
92
    nc <- case makeNodeConfiguration $ defaultPartialNodeConfiguration <> configYamlPc <> cmdPc of
 
93
            Left err -> panic $ "Error in creating the NodeConfiguration: " <> Text.pack err
 
94
            Right nc' -> return nc'
 
95
 
 
96
    case shelleyVRFFile $ ncProtocolFiles nc of
 
97
      Just vrfFp -> do vrf <- runExceptT $ checkVRFFilePermissions vrfFp
 
98
                       case vrf of
 
99
                         Left err ->
 
100
                           putTextLn (renderVRFPrivateKeyFilePermissionError err) >> exitFailure
 
101
                         Right () ->
 
102
                           pure ()
 
103
      Nothing -> pure ()
 
104
 
 
105
    eitherSomeProtocol <- runExceptT $ mkConsensusProtocol nc
 
106
 
 
107
    p :: SomeConsensusProtocol <-
 
108
      case eitherSomeProtocol of
 
109
        Left err -> putTextLn (renderProtocolInstantiationError err) >> exitFailure
 
110
        Right p -> pure p
 
111
 
 
112
    eLoggingLayer <- runExceptT $ createLoggingLayer
 
113
                     (Text.pack (showVersion version))
 
114
                     nc
 
115
                     p
 
116
 
 
117
    loggingLayer <- case eLoggingLayer of
 
118
                      Left err  -> putTextLn (show err) >> exitFailure
 
119
                      Right res -> return res
 
120
 
 
121
    !trace <- setupTrace loggingLayer
 
122
    let tracer = contramap pack $ toLogObject trace
 
123
 
 
124
    logTracingVerbosity nc tracer
 
125
 
 
126
    let handleNodeWithTracers
 
127
          :: ( HasKESMetricsData blk
 
128
             , HasKESInfo blk
 
129
             , TraceConstraints blk
 
130
             , Protocol.Protocol IO blk
 
131
             )
 
132
          => Protocol.ProtocolInfoArgs IO blk
 
133
          -> IO ()
 
134
        handleNodeWithTracers runP = do
 
135
          -- This IORef contains node kernel structure which holds node kernel.
 
136
          -- Used for ledger queries and peer connection status.
 
137
          nodeKernelData <- mkNodeKernelData
 
138
          let ProtocolInfo { pInfoConfig = cfg } = Protocol.protocolInfo runP
 
139
          tracers <- mkTracers
 
140
                       (Consensus.configBlock cfg)
 
141
                       (ncTraceConfig nc)
 
142
                       trace
 
143
                       nodeKernelData
 
144
                       (llEKGDirect loggingLayer)
 
145
          Async.withAsync (handlePeersListSimple trace nodeKernelData)
 
146
              $ \_peerLogingThread ->
 
147
                -- We ignore peer loging thread if it dies, but it will be killed
 
148
                -- when 'handleSimpleNode' terminates.
 
149
                handleSimpleNode p runP trace tracers nc (setNodeKernel nodeKernelData)
 
150
                `finally`
 
151
                shutdownLoggingLayer loggingLayer
 
152
 
 
153
    case p of
 
154
      SomeConsensusProtocol _ runP -> handleNodeWithTracers runP
 
155
 
 
156
logTracingVerbosity :: NodeConfiguration -> Tracer IO String -> IO ()
 
157
logTracingVerbosity nc tracer =
 
158
  case ncTraceConfig nc of
 
159
    TracingOff -> return ()
 
160
    TracingOn traceConf ->
 
161
      case traceVerbosity traceConf of
 
162
        NormalVerbosity -> traceWith tracer "tracing verbosity = normal verbosity "
 
163
        MinimalVerbosity -> traceWith tracer "tracing verbosity = minimal verbosity "
 
164
        MaximalVerbosity -> traceWith tracer "tracing verbosity = maximal verbosity "
 
165
 
 
166
-- | Add the application name and unqualified hostname to the logging
 
167
-- layer basic trace.
 
168
--
 
169
-- If the @CARDANO_NODE_LOGGING_HOSTNAME@ environment variable is set,
 
170
-- it overrides the system hostname. This is useful when running a
 
171
-- local test cluster with all nodes on the same host.
 
172
setupTrace
 
173
  :: LoggingLayer
 
174
  -> IO (Trace IO Text)
 
175
setupTrace loggingLayer = do
 
176
    hn <- maybe hostname (pure . pack) =<< lookupEnv "CARDANO_NODE_LOGGING_HOSTNAME"
 
177
    return $
 
178
        setHostname hn $
 
179
        llAppendName loggingLayer "node" (llBasicTrace loggingLayer)
 
180
  where
 
181
    hostname = do
 
182
      hn0 <- pack <$> getHostName
 
183
      return $ take 8 $ fst $ breakOn "." hn0
 
184
 
 
185
handlePeersListSimple
 
186
  :: Trace IO Text
 
187
  -> NodeKernelData blk
 
188
  -> IO ()
 
189
handlePeersListSimple tr nodeKern = forever $ do
 
190
  getCurrentPeers nodeKern >>= tracePeers tr
 
191
  threadDelay 2000000 -- 2 seconds.
 
192
 
 
193
-- | Sets up a simple node, which will run the chain sync protocol and block
 
194
-- fetch protocol, and, if core, will also look at the mempool when trying to
 
195
-- create a new block.
 
196
 
 
197
handleSimpleNode
 
198
  :: forall blk
 
199
  . ( RunNode blk
 
200
    , Protocol.Protocol IO blk
 
201
    )
 
202
  => SomeConsensusProtocol
 
203
  -> Protocol.ProtocolInfoArgs IO blk
 
204
  -> Trace IO Text
 
205
  -> Tracers RemoteConnectionId LocalConnectionId blk
 
206
  -> NodeConfiguration
 
207
  -> (NodeKernel IO RemoteConnectionId LocalConnectionId blk -> IO ())
 
208
  -- ^ Called on the 'NodeKernel' after creating it, but before the network
 
209
  -- layer is initialised.  This implies this function must not block,
 
210
  -- otherwise the node won't actually start.
 
211
  -> IO ()
 
212
handleSimpleNode scp runP trace nodeTracers nc onKernel = do
 
213
  meta <- mkLOMeta Notice Public
 
214
 
 
215
  let pInfo = Protocol.protocolInfo runP
 
216
      tracer = toLogObject trace
 
217
 
 
218
  createTracers nc trace tracer
 
219
 
 
220
  (publicIPv4SocketOrAddr, publicIPv6SocketOrAddr, localSocketOrPath) <- do
 
221
    result <- runExceptT (gatherConfiguredSockets nc)
 
222
    case result of
 
223
      Right triplet -> return triplet
 
224
      Left error -> do
 
225
        traceNamedObject
 
226
          (appendName "error" trace)
 
227
          (meta, LogMessage (Text.pack (renderSocketConfigError error)))
 
228
        throwIO error
 
229
 
 
230
  dbPath <- canonDbPath nc
 
231
 
 
232
  eitherTopology <- readTopologyFile nc
 
233
  nt <- either (\err -> panic $ "Cardano.Node.Run.handleSimpleNode.readTopologyFile: " <> err) pure eitherTopology
 
234
 
 
235
  let diffusionTracers :: DiffusionTracers
 
236
      diffusionTracers = createDiffusionTracers nodeTracers
 
237
 
 
238
      (ipProducerAddrs, dnsProducerAddrs) = producerAddresses nt
 
239
 
 
240
      dnsProducers :: [DnsSubscriptionTarget]
 
241
      dnsProducers = uncurry dnsSubscriptionTarget `map` dnsProducerAddrs
 
242
 
 
243
      ipProducers :: IPSubscriptionTarget
 
244
      ipProducers = ipSubscriptionTargets ipProducerAddrs
 
245
 
 
246
      diffusionArguments :: DiffusionArguments
 
247
      diffusionArguments =
 
248
        createDiffusionArguments
 
249
          publicIPv4SocketOrAddr
 
250
          publicIPv6SocketOrAddr
 
251
          localSocketOrPath
 
252
          (ncDiffusionMode nc)
 
253
          ipProducers
 
254
          dnsProducers
 
255
 
 
256
  ipv4 <- traverse getSocketOrSocketInfoAddr publicIPv4SocketOrAddr
 
257
  ipv6 <- traverse getSocketOrSocketInfoAddr publicIPv6SocketOrAddr
 
258
 
 
259
  traceNamedObject
 
260
    (appendName "addresses" trace)
 
261
    (meta, LogMessage . Text.pack . show $ catMaybes [ipv4, ipv6])
 
262
  traceNamedObject
 
263
    (appendName "diffusion-mode" trace)
 
264
    (meta, LogMessage . Text.pack . show . ncDiffusionMode $ nc)
 
265
  traceNamedObject
 
266
    (appendName "dns-producers" trace)
 
267
    (meta, LogMessage . Text.pack . show $ dnsProducers)
 
268
  traceNamedObject
 
269
    (appendName "ip-producers" trace)
 
270
    (meta, LogMessage . Text.pack . show $ ipProducers)
 
271
 
 
272
  withShutdownHandling nc trace $ \sfds ->
 
273
   Node.run
 
274
     RunNodeArgs
 
275
       { rnTraceConsensus = consensusTracers nodeTracers
 
276
       , rnTraceNTN       = nodeToNodeTracers nodeTracers
 
277
       , rnTraceNTC       = nodeToClientTracers nodeTracers
 
278
       , rnProtocolInfo   = pInfo
 
279
       , rnNodeKernelHook = \registry nodeKernel -> do
 
280
           maybeSpawnOnSlotSyncedShutdownHandler nc sfds trace registry
 
281
             (Node.getChainDB nodeKernel)
 
282
           onKernel nodeKernel
 
283
       }
 
284
     StdRunNodeArgs
 
285
       { srnBfcMaxConcurrencyBulkSync   = unMaxConcurrencyBulkSync <$> ncMaxConcurrencyBulkSync nc
 
286
       , srnBfcMaxConcurrencyDeadline   = unMaxConcurrencyDeadline <$> ncMaxConcurrencyDeadline nc
 
287
       , srnChainDbValidateOverride     = ncValidateDB nc
 
288
       , srnSnapshotInterval            = ncSnapshotInterval nc
 
289
       , srnDatabasePath                = dbPath
 
290
       , srnDiffusionArguments          = diffusionArguments
 
291
       , srnDiffusionTracers            = diffusionTracers
 
292
       , srnEnableInDevelopmentVersions = False -- TODO get this value from the node configuration
 
293
       , srnTraceChainDB                = chainDBTracer nodeTracers
 
294
       }
 
295
 where
 
296
  createDiffusionTracers :: Tracers RemoteConnectionId LocalConnectionId blk
 
297
                         -> DiffusionTracers
 
298
  createDiffusionTracers nodeTracers' = DiffusionTracers
 
299
    { dtIpSubscriptionTracer = ipSubscriptionTracer nodeTracers'
 
300
    , dtDnsSubscriptionTracer = dnsSubscriptionTracer nodeTracers'
 
301
    , dtDnsResolverTracer = dnsResolverTracer nodeTracers'
 
302
    , dtErrorPolicyTracer = errorPolicyTracer nodeTracers'
 
303
    , dtLocalErrorPolicyTracer = localErrorPolicyTracer nodeTracers'
 
304
    , dtAcceptPolicyTracer = acceptPolicyTracer nodeTracers'
 
305
    , dtMuxTracer = muxTracer nodeTracers'
 
306
    , dtMuxLocalTracer = muxLocalTracer nodeTracers'
 
307
    , dtHandshakeTracer = handshakeTracer nodeTracers'
 
308
    , dtHandshakeLocalTracer = localHandshakeTracer nodeTracers'
 
309
    , dtDiffusionInitializationTracer = diffusionInitializationTracer nodeTracers'
 
310
    , dtLedgerPeersTracer = nullTracer -- TODO network team
 
311
    }
 
312
 
 
313
  createTracers
 
314
    :: NodeConfiguration
 
315
    -> Trace IO Text
 
316
    -> Tracer IO Text
 
317
    -> IO ()
 
318
  createTracers NodeConfiguration { ncValidateDB }
 
319
                tr tracer = do
 
320
    let ProtocolInfo{ pInfoConfig = cfg } = Protocol.protocolInfo runP
 
321
 
 
322
    meta <- mkLOMeta Notice Public
 
323
    traceNamedObject (appendName "networkMagic" tr)
 
324
                     (meta, LogMessage ("NetworkMagic " <> show (unNetworkMagic . getNetworkMagic $ Consensus.configBlock cfg)))
 
325
 
 
326
    startTime <- getCurrentTime
 
327
    traceNodeBasicInfo tr =<< nodeBasicInfo nc scp startTime
 
328
    traceCounter "nodeStartTime" tr (ceiling $ utcTimeToPOSIXSeconds startTime)
 
329
 
 
330
    when ncValidateDB $ traceWith tracer "Performing DB validation"
 
331
 
 
332
  traceNodeBasicInfo :: Trace IO Text -> [LogObject Text] -> IO ()
 
333
  traceNodeBasicInfo tr basicInfoItems =
 
334
    forM_ basicInfoItems $ \(LogObject nm mt content) ->
 
335
      traceNamedObject (appendName nm tr) (mt, content)
 
336
 
 
337
--------------------------------------------------------------------------------
 
338
-- Helper functions
 
339
--------------------------------------------------------------------------------
 
340
 
 
341
canonDbPath :: NodeConfiguration -> IO FilePath
 
342
canonDbPath NodeConfiguration{ncDatabaseFile = DbFile dbFp} = do
 
343
  fp <- canonicalizePath =<< makeAbsolute dbFp
 
344
  createDirectoryIfMissing True fp
 
345
  return fp
 
346
 
 
347
 
 
348
-- | Make sure the VRF private key file is readable only
 
349
-- by the current process owner the node is running under.
 
350
checkVRFFilePermissions :: FilePath -> ExceptT VRFPrivateKeyFilePermissionError IO ()
 
351
#ifdef UNIX
 
352
checkVRFFilePermissions vrfPrivKey = do
 
353
  fs <- liftIO $ getFileStatus vrfPrivKey
 
354
  let fm = fileMode fs
 
355
  -- Check the the VRF private key file does not give read/write/exec permissions to others.
 
356
  when (hasOtherPermissions fm)
 
357
       (left $ OtherPermissionsExist vrfPrivKey)
 
358
  -- Check the the VRF private key file does not give read/write/exec permissions to any group.
 
359
  when (hasGroupPermissions fm)
 
360
       (left $ GroupPermissionsExist vrfPrivKey)
 
361
 where
 
362
  hasPermission :: FileMode -> FileMode -> Bool
 
363
  hasPermission fModeA fModeB = fModeA `intersectFileModes` fModeB /= nullFileMode
 
364
 
 
365
  hasOtherPermissions :: FileMode -> Bool
 
366
  hasOtherPermissions fm' = fm' `hasPermission` otherModes
 
367
 
 
368
  hasGroupPermissions :: FileMode -> Bool
 
369
  hasGroupPermissions fm' = fm' `hasPermission` groupModes
 
370
#else
 
371
checkVRFFilePermissions vrfPrivKey = do
 
372
  attribs <- liftIO $ getFileAttributes vrfPrivKey
 
373
  -- https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
 
374
  -- https://docs.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
 
375
  -- https://docs.microsoft.com/en-us/windows/win32/secauthz/standard-access-rights
 
376
  -- https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
 
377
  -- https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask
 
378
  when (attribs `hasPermission` genericPermissions)
 
379
       (left $ GenericPermissionsExist vrfPrivKey)
 
380
 where
 
381
  genericPermissions = gENERIC_ALL .|. gENERIC_READ .|. gENERIC_WRITE .|. gENERIC_EXECUTE
 
382
  hasPermission fModeA fModeB = fModeA .&. fModeB /= gENERIC_NONE
 
383
#endif
 
384
 
 
385
createDiffusionArguments
 
386
  :: Maybe (SocketOrSocketInfo Socket AddrInfo)
 
387
   -- ^ Either a socket bound to IPv4 address provided by systemd or IPv4
 
388
   -- address to bind to for NodeToNode communication.
 
389
  -> Maybe (SocketOrSocketInfo Socket AddrInfo)
 
390
   -- ^ Either a socket bound to IPv6 address provided by systemd or IPv6
 
391
   -- address to bind to for NodeToNode communication.
 
392
  -> Maybe (SocketOrSocketInfo Socket SocketPath)
 
393
  -- ^ Either a SOCKET_UNIX socket provided by systemd or a path for
 
394
  -- NodeToClient communication.
 
395
  -> DiffusionMode
 
396
  -> IPSubscriptionTarget
 
397
  -> [DnsSubscriptionTarget]
 
398
  -> DiffusionArguments
 
399
createDiffusionArguments publicIPv4SocketsOrAddrs
 
400
                         publicIPv6SocketsOrAddrs
 
401
                         mLocalSocketOrPath
 
402
                         diffusionMode
 
403
                         ipProducers dnsProducers
 
404
                         =
 
405
  DiffusionArguments
 
406
    -- This is not elegant, but it will change once `coot/connection-manager` is
 
407
    -- merged into `ouroboros-networ`.
 
408
    { daIPv4Address = eitherSocketOrSocketInfo <$> publicIPv4SocketsOrAddrs
 
409
    , daIPv6Address = eitherSocketOrSocketInfo <$> publicIPv6SocketsOrAddrs
 
410
    , daLocalAddress = mLocalSocketOrPath >>= return . fmap unSocketPath
 
411
                                                     . eitherSocketOrSocketInfo
 
412
    , daIpProducers  = ipProducers
 
413
    , daDnsProducers = dnsProducers
 
414
    -- TODO: these limits are arbitrary at the moment;
 
415
    -- issue: https://github.com/input-output-hk/ouroboros-network/issues/1836
 
416
    , daAcceptedConnectionsLimit = AcceptedConnectionsLimit {
 
417
        acceptedConnectionsHardLimit = 512
 
418
      , acceptedConnectionsSoftLimit = 384
 
419
      , acceptedConnectionsDelay     = 5
 
420
      }
 
421
    , daDiffusionMode = diffusionMode
 
422
    }
 
423
  where
 
424
    eitherSocketOrSocketInfo :: SocketOrSocketInfo a b -> Either a b
 
425
    eitherSocketOrSocketInfo (ActualSocket a) = Left a
 
426
    eitherSocketOrSocketInfo (SocketInfo b)   = Right b
 
427
 
 
428
dnsSubscriptionTarget :: NodeDnsAddress -> Int -> DnsSubscriptionTarget
 
429
dnsSubscriptionTarget na valency =
 
430
  DnsSubscriptionTarget { dstDomain  = nodeHostDnsAddressToDomain (naHostAddress na)
 
431
                        , dstPort    = naPort na
 
432
                        , dstValency = valency
 
433
                        }
 
434
 
 
435
ipSubscriptionTargets :: [NodeIPAddress] -> IPSubscriptionTarget
 
436
ipSubscriptionTargets ipProdAddrs =
 
437
  let ips = nodeAddressToSockAddr <$> ipProdAddrs
 
438
  in IPSubscriptionTarget { ispIps = ips
 
439
                          , ispValency = length ips
 
440
                          }
 
441
 
 
442
 
 
443
producerAddresses
 
444
  :: NetworkTopology
 
445
  -> ( [NodeIPAddress]
 
446
     , [(NodeDnsAddress, Int)])
 
447
producerAddresses nt =
 
448
  case nt of
 
449
    RealNodeTopology producers' ->
 
450
        partitionEithers
 
451
      . mapMaybe remoteAddressToNodeAddress
 
452
      $ producers'
 
453
    MockNodeTopology nodeSetup ->
 
454
        partitionEithers
 
455
      . mapMaybe remoteAddressToNodeAddress
 
456
      . concatMap producers
 
457
      $ nodeSetup