@Override
  public void start() {
    if (!started.compareAndSet(false, true)) {
      LOG.warn("Attempted to start an already started " + getClass().getSimpleName());
      return;
    }

    if (config.getDiscovery().isDiscoveryV5Enabled()) {
      LOG.warn("Discovery Protocol v5 is not available");
    }

    final String address = config.getDiscovery().getAdvertisedHost();
    final int configuredDiscoveryPort = config.getDiscovery().getBindPort();
    final int configuredRlpxPort = config.getRlpx().getBindPort();

    Optional.ofNullable(config.getDiscovery().getDNSDiscoveryURL())
        .ifPresent(
            disco -> {
              // These lists are updated every 12h
              // We retrieve the list every 10 minutes (600000 msec)
              LOG.info("Starting DNS discovery with URL {}", disco);
              config
                  .getDnsDiscoveryServerOverride()
                  .ifPresent(
                      dnsServer ->
                          LOG.info(
                              "Starting DNS discovery with DNS Server override {}", dnsServer));

              final DNSDaemon dnsDaemon =
                  new DNSDaemon(
                      disco,
                      createDaemonListener(),
                      0L,
                      1000L, // start after 1 second
                      600000L,
                      config.getDnsDiscoveryServerOverride().orElse(null));

              // Use Java 21 virtual thread to deploy verticle
              final DeploymentOptions options =
                  new DeploymentOptions()
                      .setThreadingModel(ThreadingModel.VIRTUAL_THREAD)
                      .setInstances(1)
                      .setWorkerPoolSize(1);

              final Future<String> deployId = vertx.deployVerticle(dnsDaemon, options);
              deployId.toCompletionStage().toCompletableFuture().join();
              dnsDaemonRef.set(Optional.of(dnsDaemon));
            });

    final int listeningPort = rlpxAgent.start().join();
    final int discoveryPort =
        peerDiscoveryAgent
            .start(
                (configuredDiscoveryPort == 0 && configuredRlpxPort == 0)
                    ? listeningPort
                    : configuredDiscoveryPort)
            .join();

    final Consumer<? super NatManager> natAction =
        natManager -> {
          final UpnpNatManager upnpNatManager = (UpnpNatManager) natManager;
          upnpNatManager.requestPortForward(
              discoveryPort, NetworkProtocol.UDP, NatServiceType.DISCOVERY);
          upnpNatManager.requestPortForward(
              listeningPort, NetworkProtocol.TCP, NatServiceType.RLPX);
        };

    natService.ifNatEnvironment(NatMethod.UPNP, natAction);
    natService.ifNatEnvironment(NatMethod.UPNPP2PONLY, natAction);

    setLocalNode(address, listeningPort, discoveryPort);

    // Call checkMaintainedConnectionPeers() now that the local node is up, for immediate peer
    // additions
    checkMaintainedConnectionPeers();

    // Periodically check maintained connections
    final int checkMaintainedConnectionsSec = config.getCheckMaintainedConnectionsFrequencySec();
    peerConnectionScheduler.scheduleWithFixedDelay(
        this::checkMaintainedConnectionPeers, 2, checkMaintainedConnectionsSec, TimeUnit.SECONDS);
    // Periodically initiate outgoing connections to discovered peers
    final int checkConnectionsSec = config.getInitiateConnectionsFrequencySec();
    peerConnectionScheduler.scheduleWithFixedDelay(
        this::attemptPeerConnections, checkConnectionsSec, checkConnectionsSec, TimeUnit.SECONDS);
  }
Creates a peer networking service for production purposes.

<p>The caller is expected to provide the IP address to be advertised (normally this node's
public IP address), as well as TCP and UDP port numbers for the RLPx agent and the discovery
agent, respectively.

@param localNode A representation of the local node
@param peerDiscoveryAgent The agent responsible for discovering peers on the network.
@param nodeKey The node key through which cryptographic operations can be performed
@param config The network configuration to use.
@param peerPermissions An object that determines whether peers are allowed to connect
@param natService The NAT environment manager.
@param maintainedPeers A collection of peers for which we are expected to maintain connections
@param reputationManager An object that inspect disconnections for misbehaving peers that can
    then be blacklisted.
@param vertx the Vert.x instance managing network resources
ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java