-
Notifications
You must be signed in to change notification settings - Fork 1
feat(net): support config domain besides IP #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
f040f31
cbf1222
b3706ef
1483681
aadffe7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,13 +4,18 @@ | |
| import static org.tron.common.backup.BackupManager.BackupStatusEnum.MASTER; | ||
| import static org.tron.common.backup.BackupManager.BackupStatusEnum.SLAVER; | ||
| import static org.tron.common.backup.message.UdpMessageTypeEnum.BACKUP_KEEP_ALIVE; | ||
| import static org.tron.core.config.args.InetUtil.resolveInetAddress; | ||
|
|
||
| import io.netty.util.internal.ConcurrentSet; | ||
| import java.net.InetAddress; | ||
| import java.net.InetSocketAddress; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import java.util.concurrent.ScheduledExecutorService; | ||
| import java.util.concurrent.TimeUnit; | ||
| import lombok.Getter; | ||
| import lombok.Setter; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Component; | ||
| import org.tron.common.backup.message.KeepAliveMessage; | ||
|
|
@@ -20,46 +25,46 @@ | |
| import org.tron.common.backup.socket.UdpEvent; | ||
| import org.tron.common.es.ExecutorServiceManager; | ||
| import org.tron.common.parameter.CommonParameter; | ||
| import org.tron.p2p.utils.NetUtil; | ||
|
|
||
| @Slf4j(topic = "backup") | ||
| @Component | ||
| public class BackupManager implements EventHandler { | ||
|
|
||
| private CommonParameter parameter = CommonParameter.getInstance(); | ||
| private final CommonParameter parameter = CommonParameter.getInstance(); | ||
|
|
||
| private int priority = parameter.getBackupPriority(); | ||
| private final int priority = parameter.getBackupPriority(); | ||
|
|
||
| private int port = parameter.getBackupPort(); | ||
| private final int port = parameter.getBackupPort(); | ||
|
|
||
| private int keepAliveInterval = parameter.getKeepAliveInterval(); | ||
| private final int keepAliveInterval = parameter.getKeepAliveInterval(); | ||
|
|
||
| private int keepAliveTimeout = keepAliveInterval * 6; | ||
| private final int keepAliveTimeout = keepAliveInterval * 6; | ||
|
|
||
| private String localIp = ""; | ||
|
|
||
| private Set<String> members = new ConcurrentSet<>(); | ||
| private final Set<String> members = new ConcurrentSet<>(); | ||
|
|
||
| private final String esName = "backup-manager"; | ||
| private final Map<String, String> domainIpCache = new ConcurrentHashMap<>(); | ||
|
|
||
| private ScheduledExecutorService executorService = | ||
| private final String esName = "backup-manager"; | ||
| private final ScheduledExecutorService executorService = | ||
| ExecutorServiceManager.newSingleThreadScheduledExecutor(esName); | ||
|
|
||
| private final String dnsEsName = "backup-dns-refresh"; | ||
| private final ScheduledExecutorService dnsExecutorService = | ||
| ExecutorServiceManager.newSingleThreadScheduledExecutor(dnsEsName); | ||
|
|
||
| @Setter | ||
| private MessageHandler messageHandler; | ||
|
|
||
| @Getter | ||
| private BackupStatusEnum status = MASTER; | ||
|
|
||
| private volatile long lastKeepAliveTime; | ||
|
|
||
| private volatile boolean isInit = false; | ||
|
|
||
| public void setMessageHandler(MessageHandler messageHandler) { | ||
| this.messageHandler = messageHandler; | ||
| } | ||
|
|
||
| public BackupStatusEnum getStatus() { | ||
| return status; | ||
| } | ||
|
|
||
| public void setStatus(BackupStatusEnum status) { | ||
| logger.info("Change backup status to {}", status); | ||
| this.status = status; | ||
|
|
@@ -78,10 +83,20 @@ public void init() { | |
| logger.warn("Failed to get local ip"); | ||
| } | ||
|
|
||
| for (String member : parameter.getBackupMembers()) { | ||
| if (!localIp.equals(member)) { | ||
| members.add(member); | ||
| for (String ipOrDomain : parameter.getBackupMembers()) { | ||
| InetAddress inetAddress = resolveInetAddress(ipOrDomain); | ||
| if (inetAddress == null) { | ||
| logger.warn("Failed to resolve backup member domain: {}", ipOrDomain); | ||
| continue; | ||
| } | ||
| String ip = inetAddress.getHostAddress(); | ||
| if (localIp.equals(ip)) { | ||
| continue; | ||
| } | ||
| if (!NetUtil.validIpV4(ipOrDomain) && !NetUtil.validIpV6(ipOrDomain)) { | ||
| domainIpCache.put(ipOrDomain, ip); | ||
| } | ||
| members.add(ip); | ||
| } | ||
|
|
||
| logger.info("Backup localIp:{}, members: size= {}, {}", localIp, members.size(), members); | ||
|
|
@@ -111,6 +126,16 @@ public void init() { | |
| logger.error("Exception in send keep alive", t); | ||
| } | ||
| }, 1000, keepAliveInterval, TimeUnit.MILLISECONDS); | ||
|
|
||
| if (!domainIpCache.isEmpty()) { | ||
| dnsExecutorService.scheduleWithFixedDelay(() -> { | ||
| try { | ||
| refreshMemberIps(); | ||
| } catch (Throwable t) { | ||
| logger.error("Exception in backup DNS refresh", t); | ||
| } | ||
| }, 60_000L, 60_000L, TimeUnit.MILLISECONDS); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -149,6 +174,7 @@ public void handleEvent(UdpEvent udpEvent) { | |
|
|
||
| public void stop() { | ||
| ExecutorServiceManager.shutdownAndAwaitTermination(executorService, esName); | ||
| ExecutorServiceManager.shutdownAndAwaitTermination(dnsExecutorService, dnsEsName); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -162,4 +188,28 @@ public enum BackupStatusEnum { | |
| MASTER | ||
| } | ||
|
|
||
| /** | ||
| * Re-resolves all tracked domain entries. If an IP has changed, the old IP is | ||
| * removed from {@link #members} and the new IP is added. | ||
| */ | ||
| private void refreshMemberIps() { | ||
| for (Map.Entry<String, String> entry : domainIpCache.entrySet()) { | ||
| String domain = entry.getKey(); | ||
| String oldIp = entry.getValue(); | ||
| InetAddress inetAddress = resolveInetAddress(domain); | ||
| if (inetAddress == null) { | ||
| logger.warn("DNS refresh: failed to re-resolve backup member domain {}, keep it", domain); | ||
| continue; | ||
| } | ||
| String newIp = inetAddress.getHostAddress(); | ||
| if (!newIp.equals(oldIp)) { | ||
| logger.info("DNS refresh: backup member {} IP changed {} -> {}", domain, oldIp, newIp); | ||
| members.remove(oldIp); | ||
| if (!localIp.equals(newIp)) { | ||
| members.add(newIp); | ||
| } | ||
| domainIpCache.put(domain, newIp); | ||
| } | ||
|
Comment on lines
+195
to
+212
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't remove a shared IP blindly during DNS refresh. Line 207 drops
🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||
| import static org.tron.core.Constant.MIN_PROPOSAL_EXPIRE_TIME; | ||||||||||||||||||||||||||||||||||||||||||||||
| import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCE_TIMEOUT_PERCENT; | ||||||||||||||||||||||||||||||||||||||||||||||
| import static org.tron.core.config.Parameter.ChainConstant.MAX_ACTIVE_WITNESS_NUM; | ||||||||||||||||||||||||||||||||||||||||||||||
| import static org.tron.core.config.args.InetUtil.resolveInetAddress; | ||||||||||||||||||||||||||||||||||||||||||||||
| import static org.tron.core.exception.TronError.ErrCode.PARAMETER_INIT; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import com.beust.jcommander.JCommander; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -37,7 +38,6 @@ | |||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Objects; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Properties; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Set; | ||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.BlockingQueue; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -74,6 +74,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||
| import org.tron.core.exception.TronError; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.tron.core.store.AccountStore; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.tron.p2p.P2pConfig; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.tron.p2p.dns.lookup.LookUpTxt; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.tron.p2p.dns.update.DnsType; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.tron.p2p.dns.update.PublishConfig; | ||||||||||||||||||||||||||||||||||||||||||||||
| import org.tron.p2p.utils.NetUtil; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1308,8 +1309,7 @@ public static List<InetSocketAddress> getInetSocketAddress( | |||||||||||||||||||||||||||||||||||||||||||||
| return ret; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| List<String> list = config.getStringList(path); | ||||||||||||||||||||||||||||||||||||||||||||||
| for (String configString : list) { | ||||||||||||||||||||||||||||||||||||||||||||||
| InetSocketAddress inetSocketAddress = NetUtil.parseInetSocketAddress(configString); | ||||||||||||||||||||||||||||||||||||||||||||||
| for (InetSocketAddress inetSocketAddress : InetUtil.getInetSocketAddressList(list)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (filter) { | ||||||||||||||||||||||||||||||||||||||||||||||
| String ip = inetSocketAddress.getAddress().getHostAddress(); | ||||||||||||||||||||||||||||||||||||||||||||||
| int port = inetSocketAddress.getPort(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1660,6 +1660,17 @@ private static void initBackupProperty(Config config) { | |||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| PARAMETER.backupMembers = config.hasPath(ConfigKey.NODE_BACKUP_MEMBERS) | ||||||||||||||||||||||||||||||||||||||||||||||
| ? config.getStringList(ConfigKey.NODE_BACKUP_MEMBERS) : new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||
| checkBackupMembers(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private static void checkBackupMembers() { | ||||||||||||||||||||||||||||||||||||||||||||||
| for (String member : PARAMETER.backupMembers) { | ||||||||||||||||||||||||||||||||||||||||||||||
| InetAddress inetAddress = resolveInetAddress(member); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (inetAddress == null) { | ||||||||||||||||||||||||||||||||||||||||||||||
| throw new TronError("Failed to resolve backup member: " + member, | ||||||||||||||||||||||||||||||||||||||||||||||
| TronError.ErrCode.PARAMETER_INIT); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1663
to
+1673
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't make node startup fail on transient DNS for backup peers.
Possible fix private static void checkBackupMembers() {
for (String member : PARAMETER.backupMembers) {
InetAddress inetAddress = resolveInetAddress(member);
if (inetAddress == null) {
- throw new TronError("Failed to resolve backup member: " + member,
- TronError.ErrCode.PARAMETER_INIT);
+ logger.warn("Failed to resolve backup member during startup, will retry later: {}",
+ member);
}
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| public static void logConfig() { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| package org.tron.core.config.args; | ||
|
|
||
| import java.net.InetAddress; | ||
| import java.net.InetSocketAddress; | ||
| import java.net.UnknownHostException; | ||
| import java.util.ArrayList; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.concurrent.ExecutionException; | ||
| import java.util.concurrent.ExecutorService; | ||
| import java.util.concurrent.Future; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.tron.common.es.ExecutorServiceManager; | ||
| import org.tron.p2p.dns.lookup.LookUpTxt; | ||
| import org.tron.p2p.utils.NetUtil; | ||
|
|
||
| @Slf4j(topic = "app") | ||
| public class InetUtil { | ||
|
|
||
| public static List<InetSocketAddress> getInetSocketAddressList(List<String> items) { | ||
| List<InetSocketAddress> ret = new ArrayList<>(); | ||
| if (items.isEmpty()) { | ||
| return ret; | ||
| } | ||
| // Collect entries whose host part is a domain name (not an IP literal). | ||
| List<String> domainEntries = new ArrayList<>(); | ||
| for (String item : items) { | ||
| String host = NetUtil.parseInetSocketAddress(item).getHostString(); | ||
| if (!NetUtil.validIpV4(host) && !NetUtil.validIpV6(host)) { | ||
|
Comment on lines
+21
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard Line 29 and Line 65 call Possible fix+ private static InetSocketAddress tryParseInetSocketAddress(String configString) {
+ try {
+ return NetUtil.parseInetSocketAddress(configString);
+ } catch (IllegalArgumentException e) {
+ logger.warn("Invalid address format, skip: {}", configString);
+ return null;
+ }
+ }
+
public static List<InetSocketAddress> getInetSocketAddressList(List<String> items) {
List<InetSocketAddress> ret = new ArrayList<>();
if (items.isEmpty()) {
return ret;
}
@@
List<String> domainEntries = new ArrayList<>();
for (String item : items) {
- String host = NetUtil.parseInetSocketAddress(item).getHostString();
+ InetSocketAddress parsed = tryParseInetSocketAddress(item);
+ if (parsed == null) {
+ continue;
+ }
+ String host = parsed.getHostString();
if (!NetUtil.validIpV4(host) && !NetUtil.validIpV6(host)) {
domainEntries.add(item);
}
}
@@
for (String configString : items) {
InetSocketAddress inetSocketAddress;
- InetSocketAddress parsed = NetUtil.parseInetSocketAddress(configString);
+ InetSocketAddress parsed = tryParseInetSocketAddress(configString);
+ if (parsed == null) {
+ continue;
+ }
if (NetUtil.validIpV4(parsed.getHostString()) || NetUtil.validIpV6(parsed.getHostString())) {
inetSocketAddress = parsed;
} else {
inetSocketAddress = domainResolved.get(configString);
}Also applies to: 63-66 🤖 Prompt for AI Agents |
||
| domainEntries.add(item); | ||
| } | ||
| } | ||
|
|
||
| // Resolve domain names: spin up a thread pool only when there are multiple domains | ||
| Map<String, InetSocketAddress> domainResolved = new HashMap<>(); | ||
| if (domainEntries.size() > 1) { | ||
| String poolName = "args-dns-lookup"; | ||
| ExecutorService dnsPool = ExecutorServiceManager | ||
| .newFixedThreadPool(poolName, domainEntries.size(), true); | ||
| List<Future<InetSocketAddress>> futures = new ArrayList<>(domainEntries.size()); | ||
| for (String entry : domainEntries) { | ||
| futures.add(dnsPool.submit(() -> resolveInetSocketAddress(entry))); | ||
| } | ||
| for (int i = 0; i < domainEntries.size(); i++) { | ||
| String entry = domainEntries.get(i); | ||
| try { | ||
| domainResolved.put(entry, futures.get(i).get()); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| logger.warn("DNS lookup interrupted for: {}", entry); | ||
| } catch (ExecutionException e) { | ||
| logger.warn("Failed to resolve address, skip: {}", entry); | ||
| } | ||
| } | ||
| ExecutorServiceManager.shutdownAndAwaitTermination(dnsPool, poolName); | ||
| } else if (domainEntries.size() == 1) { | ||
| String entry = domainEntries.get(0); | ||
| domainResolved.put(entry, resolveInetSocketAddress(entry)); | ||
| } | ||
|
|
||
| // Build the result list preserving the original config order. | ||
| for (String configString : items) { | ||
| InetSocketAddress inetSocketAddress; | ||
| InetSocketAddress parsed = NetUtil.parseInetSocketAddress(configString); | ||
| if (NetUtil.validIpV4(parsed.getHostString()) || NetUtil.validIpV6(parsed.getHostString())) { | ||
| inetSocketAddress = parsed; | ||
| } else { | ||
| inetSocketAddress = domainResolved.get(configString); | ||
| } | ||
| if (inetSocketAddress == null) { | ||
| continue; | ||
| } | ||
| ret.add(inetSocketAddress); | ||
| } | ||
| return ret; | ||
| } | ||
|
|
||
| /** | ||
| * Resolves a {@code domain:port} address string to an {@link InetSocketAddress} via DNS. | ||
| */ | ||
| private static InetSocketAddress resolveInetSocketAddress(String configString) { | ||
| InetSocketAddress parsed = NetUtil.parseInetSocketAddress(configString); | ||
| String host = parsed.getHostString(); | ||
| int port = parsed.getPort(); | ||
| InetAddress address = LookUpTxt.lookUpIp(host, true); | ||
| if (address == null) { | ||
| address = LookUpTxt.lookUpIp(host, false); | ||
| } | ||
| if (address == null) { | ||
| return null; | ||
| } | ||
| logger.info("Resolve {} to {}", host, address.getHostAddress()); | ||
| return new InetSocketAddress(address, port); | ||
| } | ||
|
|
||
| /** | ||
| * Resolves a hostname or IP string to a numeric IP address string. | ||
| */ | ||
| public static InetAddress resolveInetAddress(String ipOrDomain) { | ||
| // Fast path: already a numeric address — no lookup needed. | ||
| if (NetUtil.validIpV4(ipOrDomain) || NetUtil.validIpV6(ipOrDomain)) { | ||
| try { | ||
| return InetAddress.getByName(ipOrDomain); | ||
| } catch (UnknownHostException e) { | ||
| return null; | ||
| } | ||
| } | ||
| InetAddress address = LookUpTxt.lookUpIp(ipOrDomain, true); | ||
| if (address == null) { | ||
| address = LookUpTxt.lookUpIp(ipOrDomain, false); | ||
| } | ||
| return address; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P0: Supply-chain risk: This replaces the official
io.github.tronprotocollibp2p artifact with a personal GitHub fork (com.github.317787106) resolved via JitPack. For a blockchain node, pulling a core networking dependency from an individual's fork introduces serious supply-chain risk — the fork is outside the project's normal review/release process and could contain unaudited changes. If this is intentional for local development, it should not be merged to a shared branch; if the upstream library needs patches, they should be contributed back and released under the official group ID.Prompt for AI agents