Skip to content

feat(net): support domain names in peer configuration#6727

Open
317787106 wants to merge 17 commits intotronprotocol:developfrom
317787106:feature/support_domain
Open

feat(net): support domain names in peer configuration#6727
317787106 wants to merge 17 commits intotronprotocol:developfrom
317787106:feature/support_domain

Conversation

@317787106
Copy link
Copy Markdown
Collaborator

@317787106 317787106 commented Apr 28, 2026

Summary

Allows node.seed.ip.list, node.active, node.passive, node.fastForward, and node.backup.members configuration entries to specify domain names in addition to IP literals. Domains are resolved to IPs at startup; backup member IPs are refreshed automatically every 60 seconds when DNS returns a different address.
Closes #6634

Problem

All network peer configuration fields previously required numeric IP addresses. Operators running nodes behind dynamic DNS or cloud load-balancers had to manually update config files whenever IPs changed. Backup members in particular need continuous accuracy because they participate in keep-alive health checks.

Changes

New: InetUtil

Central DNS resolution utility used by both startup parsing and runtime refresh:

  • resolveInetSocketAddressList(List<String>) — converts a list of ipOrDomain:port strings to InetSocketAddress objects. IP literals are used as-is; domain entries are resolved via LookUpTxt.lookUpIp (IPv4 first, IPv6 fallback). When there are multiple domain entries, resolution is parallelised using a bounded thread pool (up to 10 threads). Unresolvable entries are silently skipped.
  • resolveInetAddress(String) — resolves a bare host (no port) to InetAddress; IP literals take the fast path without a DNS lookup.

BackupManager (framework)

  • At init(), each configured backup member is resolved via InetUtil.resolveInetAddress. The resolved IP is stored in members; entries with a domain host are additionally tracked in a domainIpCache.
  • A new dnsExecutorService scheduler runs refreshMemberIps() every 60 seconds when domainIpCache is non-empty. If DNS returns a new IP, the old IP is removed from members and the new IP is added; localIp is never added.
  • dnsExecutorService is shut down alongside executorService in stop().

Args (framework)

  • Extracted a shared getInetSockerAddress(config, path) helper that calls InetUtil.resolveInetSocketAddressList, used by both getInetSocketAddress (seed/active/passive nodes) and getInetAddress. Unresolvable domain entries in seed lists are silently dropped; unresolvable backup member entries throw TronError(PARAMETER_INIT) at startup via the new checkBackupMembers() validator.

Dependency

  • Switched libp2p from io.github.tronprotocol:libp2p:2.2.7 to io.github.tronprotocol:libp2p:2.2.8, which exposes the LookUpTxt.lookUpIp API used for DNS resolution.

Behaviour Summary

Config field Unresolvable domain Domain IP change
Seed / active / passive / fastForward Entry skipped silently Re-read at next startup
node.backup.members TronError thrown at startup, skip when BackupManager.init() as it will not be triggered until catch up Auto-refreshed every 60 s

Test

  • InetUtilTest (18 tests): resolveInetSocketAddressList — IPv4/IPv6 literals, domain resolution, IPv6 fallback, input order preserved, unresolvable entries dropped. resolveInetAddress — same coverage for the no-port variant.
  • BackupManagerTest (6 new tests): init() domain-to-IP mapping and local-IP exclusion; refreshMemberIps() — IP changed, IP unchanged, DNS failure retains old IP.
  • ArgsTest (3 new tests): checkBackupMembers() — IP members pass, unresolvable domain throws TronError(PARAMETER_INIT), resolvable domain passes.

@github-actions github-actions Bot requested a review from xxo1shine April 28, 2026 14:01
@halibobo1205 halibobo1205 added the topic:net p2p net work, synchronization label Apr 29, 2026
@halibobo1205 halibobo1205 added this to the GreatVoyage-v4.8.2 milestone Apr 29, 2026
Comment thread framework/src/main/java/org/tron/core/config/args/InetUtil.java Outdated
Comment thread framework/src/main/java/org/tron/core/config/args/InetUtil.java Outdated
Comment thread common/build.gradle
api 'org.aspectj:aspectjweaver:1.9.8'
api 'org.aspectj:aspectjtools:1.9.8'
api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.7',{
api group: 'com.github.tronprotocol', name: 'libp2p', version: 'release-v2.2.8-SNAPSHOT',{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this release-v2.2.8-SNAPSHOT a correct usage?
Why change from io.github.tronprotocol to com.github.tronprotocol

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a gitpack temporary dependency now. After the libp2p v2.2.8 releases, it will be updated as

api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.8'

* may mix IP literals and domain names
* @return resolved addresses in the same order as the input, omitting unresolvable entries
*/
public static List<InetSocketAddress> resolveInetSocketAddressList(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest this function rewrite as

private static Map<String, InetSocketAddress> resolveDomainsInParallel(
    List<String> domainEntries) {
  Map<String, InetSocketAddress> resolved = new HashMap<>();
  if (domainEntries.isEmpty()) {
    return resolved;
  }

  int poolSize = StrictMath.min(domainEntries.size(), DNS_POOL_MAX_SIZE);
  ExecutorService dnsPool = ExecutorServiceManager
      .newFixedThreadPool(DNS_POOL_NAME, poolSize, true);

  try {
    LinkedHashMap<String, CompletableFuture<InetSocketAddress>> futures =
        new LinkedHashMap<>();
    for (String entry : domainEntries) {
      futures.put(entry, CompletableFuture.supplyAsync(
          () -> resolveInetSocketAddress(entry), dnsPool));
    }

    // Single global deadline for all lookups combined.
    try {
      CompletableFuture
          .allOf(futures.values().toArray(new CompletableFuture[0]))
          .get(DNS_LOOKUP_TIMEOUT_SECONDS, TimeUnit.SECONDS);
    } catch (TimeoutException e) {
      logger.warn("DNS lookup budget {}s exceeded, dropping unresolved entries",
          DNS_LOOKUP_TIMEOUT_SECONDS);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    } catch (ExecutionException ignored) {
      // per-entry exceptions handled below
    }

    // Collect whatever finished; drop pending/failed entries.
    for (Map.Entry<String, CompletableFuture<InetSocketAddress>> e : futures.entrySet()) {
      CompletableFuture<InetSocketAddress> f = e.getValue();
      if (f.isDone() && !f.isCompletedExceptionally()) {
        InetSocketAddress addr = f.getNow(null);
        if (addr != null) {
          resolved.put(e.getKey(), addr);
        }
      } else {
        logger.warn("DNS unresolved or timed out, skip: {}", e.getKey());
      }
    }
  } finally {
    ExecutorServiceManager.shutdownAndAwaitTermination(dnsPool, DNS_POOL_NAME);
  }
  return resolved;
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using CompletableFuture is a good idea, accept it. Thanks for your review.

Comment thread framework/src/main/java/org/tron/common/backup/BackupManager.java
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:net p2p net work, synchronization

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[Feature] Support domain names in node.backup.members and seed.node.ip.list

4 participants