Problem Statement
Currently, node.backup.members and seed.node.ip.list in the node configuration only accept raw IP addresses. This creates operational friction in dynamic or cloud-native deployments where:
- Infrastructure IP addresses change frequently (auto-scaling groups, cloud instances, Kubernetes pods)
- Operators prefer to manage a small set of stable DNS records rather than updating config files across many nodes whenever IPs rotate
- High-availability backup setups require the member list to stay accurate even as IPs change at runtime
Neither field documents or handles hostname inputs — any domain name silently fails to resolve, causing nodes to be silently dropped from the member/seed list.
Who benefits: Node operators running large-scale deployments, SR infrastructure teams, and any operator managing nodes on infrastructure with dynamic IP allocation.
Proposed Solution
Extend the parsers for both node.backup.members and seed.node.ip.list to accept hostname entries in addition to raw IPv4/IPv6 addresses.
Domain resolution should reuse the DNS resolution pattern from the libp2p library (see tronprotocol/libp2p#130) — specifically the dnsjava SimpleResolver with explicit per-query timeout — rather than re-implementing resolution logic. Note: libp2p's existing lookUpTxt queries TXT records for node discovery; for resolving hostnames to IPs (A/AAAA records) a parallel lookUpA function following the same pattern is needed.
The two fields have different reliability requirements and therefore adopt different resolution strategies:
| Field |
Resolution timing |
Startup failure on DNS error |
Periodic re-resolution |
node.backup.members |
Synchronous, blocks startup until done |
Fatal if entry fails |
Yes (fixed interval constant in BackupManager) |
seed.node.ip.list |
Asynchronous, does not block startup |
Warning per entry, startup continues |
No (p2p handles dynamic expansion) |
Specification
Configuration Changes
node.backup.members — extend to accept hostnames (no port, since the backup port is global). No new config options are added; the re-resolution interval is a fixed constant inside BackupManager:
node.backup {
port = 10001
priority = 8
keepAliveInterval = 3000
// support both peer's ip or domain list, but only fill one of them
members = [
// "192.168.1.10", <- IP, used as-is
// "backup-node.example.com" <- domain, resolved at startup and refreshed periodically
]
}
seed.node.ip.list — extend to accept host:port where host may be a domain name. No new config options are added; resolution is asynchronous and best-effort:
seed.node = {
// List of seed nodes. Entries may be IP:port or domain:port.
ip.list = [
"52.8.46.215:18888", // IP:port — used as-is
"seed1.tron.network:18888" // domain:port — resolved asynchronously at startup
]
}
Resolution Behavior
node.backup.members (high-reliability, synchronous):
- Due to the high reliability requirements of primary/standby election, domain resolution is synchronous and blocks node startup until resolution finished.
- If the domain entry failed to resolve, the node refuses to start and logs a prominent fatal error with the exact hostname and remediation advice:
[FATAL] Failed to start: cannot resolve backup member "backup-node.example.com".
Please check DNS configuration or replace the entry with a reachable IP address.
- After startup, a background task re-resolves the domain entry on a periodic schedule (interval defined as a constant in
BackupManager, not configurable).
- On refresh, successfully resolved IP replace the previous one for that domain.
- If the domain temporarily fails to resolve on refresh, the last known IP is retained and a warning is logged — the node does not stop.
seed.node.ip.list (best-effort, asynchronous):
- To avoid lengthening startup time, domain entries are resolved asynchronously in the background. Peer discovery begins immediately using raw IP entries; resolved IPs are added to the seed pool as results arrive.
- All domain lookups are submitted in parallel so resolution finishes as quickly as possible.
- Per-entry failures are logged as warnings; the entry is skipped and startup always continues:
[WARN] Cannot resolve seed node "seed1.tron.network" — skipping. Check DNS or update ip.list.
- No periodic re-resolution after startup. The p2p protocol dynamically discovers and expands the peer list; the seed list is only used for initial bootstrap.
API Changes
None — this is a configuration parsing enhancement only.
Protocol Changes
None.
Scope of Impact
- Network layer (peer discovery, backup node election)
- Configuration parsing (
Args.java, CommonParameter.java)
Breaking Changes
None. All existing IP-only configurations remain valid.
Backward Compatibility
Fully backward compatible. Raw IP entries continue to work as-is; the parser treats an entry as a domain only when it is not a valid IP literal (e.g., via Guava's InetAddresses.isInetAddress()).
Implementation
Ideas regarding implementation:
- In
Args.java, after reading node.backup.members and seed.node.ip.list, pass each entry through a helper that distinguishes IP literals from hostnames.
- Invoke the DNS resolver interface exposed by the libp2p library to perform hostname-to-IP resolution.
- For
node.backup.members:
- At startup, submit the domain entry to the resolver and wait synchronously; fail fast with a descriptive message if the resolution failed.
- In
BackupManager, add a ScheduledExecutorService task that re-resolves the domain entry at a fixed interval (e.g. 10 minutes, defined as a constant) and atomically swaps the live member ip set. On refresh failure, retain the last known IP and log a warning.
- For
seed.node.ip.list:
- At startup, submit all domain entries to a thread pool asynchronously (fire-and-forget with callbacks); node startup continues immediately using raw IP entries.
- Each callback logs
WARN on failure or adds resolved IPs to the seed pool on success.
- No scheduled re-resolution task needed.
Are you willing to implement this feature?
Yes.
Estimated Complexity
Medium — the core parsing/resolution change is straightforward, but the periodic re-resolution in BackupManager and the synchronous-vs-asynchronous split require careful implementation and testing.
Testing Strategy
Test Scenarios
node.backup.members:
- IP entry: behavior unchanged.
- Hostname entry: resolved IP used correctly by backup election.
- For unresolvable hostname at startup: node refuses to start with a clear fatal error message.
- Periodic refresh: updated IP reflected in member set without restart.
- Transient refresh failure: last known IP retained, warning logged, node continues.
seed.node.ip.list:
- IP-only entries: behavior unchanged.
- Hostname entries: resolved IPs used as initial seeds.
- Mixed IP + hostname entries: both work correctly.
- Some or all hostnames unresolvable: warnings logged per entry, node starts and continues with available IPs.
- Asynchronous resolution: startup is not delayed; domain lookups complete in the background.
- Parallel resolution: all domain lookups submitted concurrently, total resolution time bounded by the slowest single lookup.
Performance Considerations
node.backup.members resolution is synchronous and blocks startup.
seed.node.ip.list resolution is asynchronous and does not add to startup time; raw IP entries are usable immediately.
- Per-lookup timeout: libp2p's
SimpleResolver uses setTimeout(Duration.ofMillis(1000)) per attempt with up to 5 retries (worst-case ~5 s per domain). The same configuration should be reused for consistency.
- Periodic re-resolution in
BackupManager must run on a dedicated scheduled thread, not on the backup UDP event loop.
Alternatives Considered
- Re-implement DNS resolution in java-tron — rejected to avoid duplicating logic already maintained in the libp2p library.
- Periodic refresh for seed nodes — rejected; seed nodes are numerous and the p2p protocol already handles dynamic peer discovery after bootstrap, so the added complexity is not justified.
Additional Context
Related Issues/PRs
References
- Current config:
framework/src/main/resources/config.conf — node.backup, seed.node
- Backup member usage:
framework/src/main/java/org/tron/common/backup/BackupManager.java
- Seed node config parsing:
framework/src/main/java/org/tron/core/config/args/Args.java
Problem Statement
Currently,
node.backup.membersandseed.node.ip.listin the node configuration only accept raw IP addresses. This creates operational friction in dynamic or cloud-native deployments where:Neither field documents or handles hostname inputs — any domain name silently fails to resolve, causing nodes to be silently dropped from the member/seed list.
Who benefits: Node operators running large-scale deployments, SR infrastructure teams, and any operator managing nodes on infrastructure with dynamic IP allocation.
Proposed Solution
Extend the parsers for both
node.backup.membersandseed.node.ip.listto accept hostname entries in addition to raw IPv4/IPv6 addresses.Domain resolution should reuse the DNS resolution pattern from the libp2p library (see tronprotocol/libp2p#130) — specifically the dnsjava
SimpleResolverwith explicit per-query timeout — rather than re-implementing resolution logic. Note: libp2p's existinglookUpTxtqueries TXT records for node discovery; for resolving hostnames to IPs (A/AAAA records) a parallellookUpAfunction following the same pattern is needed.The two fields have different reliability requirements and therefore adopt different resolution strategies:
node.backup.membersBackupManager)seed.node.ip.listSpecification
Configuration Changes
node.backup.members— extend to accept hostnames (no port, since the backup port is global). No new config options are added; the re-resolution interval is a fixed constant insideBackupManager:seed.node.ip.list— extend to accepthost:portwherehostmay be a domain name. No new config options are added; resolution is asynchronous and best-effort:Resolution Behavior
node.backup.members(high-reliability, synchronous):BackupManager, not configurable).seed.node.ip.list(best-effort, asynchronous):API Changes
None — this is a configuration parsing enhancement only.
Protocol Changes
None.
Scope of Impact
Args.java,CommonParameter.java)Breaking Changes
None. All existing IP-only configurations remain valid.
Backward Compatibility
Fully backward compatible. Raw IP entries continue to work as-is; the parser treats an entry as a domain only when it is not a valid IP literal (e.g., via Guava's
InetAddresses.isInetAddress()).Implementation
Ideas regarding implementation:
Args.java, after readingnode.backup.membersandseed.node.ip.list, pass each entry through a helper that distinguishes IP literals from hostnames.node.backup.members:BackupManager, add aScheduledExecutorServicetask that re-resolves the domain entry at a fixed interval (e.g. 10 minutes, defined as a constant) and atomically swaps the livemember ipset. On refresh failure, retain the last known IP and log a warning.seed.node.ip.list:WARNon failure or adds resolved IPs to the seed pool on success.Are you willing to implement this feature?
Yes.
Estimated Complexity
Medium — the core parsing/resolution change is straightforward, but the periodic re-resolution in
BackupManagerand the synchronous-vs-asynchronous split require careful implementation and testing.Testing Strategy
Test Scenarios
node.backup.members:seed.node.ip.list:Performance Considerations
node.backup.membersresolution is synchronous and blocks startup.seed.node.ip.listresolution is asynchronous and does not add to startup time; raw IP entries are usable immediately.SimpleResolverusessetTimeout(Duration.ofMillis(1000))per attempt with up to 5 retries (worst-case ~5 s per domain). The same configuration should be reused for consistency.BackupManagermust run on a dedicated scheduled thread, not on the backup UDP event loop.Alternatives Considered
Additional Context
Related Issues/PRs
References
framework/src/main/resources/config.conf—node.backup,seed.nodeframework/src/main/java/org/tron/common/backup/BackupManager.javaframework/src/main/java/org/tron/core/config/args/Args.java