diff --git a/common/build.gradle b/common/build.gradle index 98fc3257190..b7494f1c1d4 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -21,7 +21,8 @@ dependencies { api 'org.aspectj:aspectjrt:1.9.8' 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.317787106', name: 'libp2p', version: 'v0.0.9',{ + //api group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.7',{ exclude group: 'io.grpc', module: 'grpc-context' exclude group: 'io.grpc', module: 'grpc-core' exclude group: 'io.grpc', module: 'grpc-netty' diff --git a/framework/src/main/java/org/tron/common/backup/BackupManager.java b/framework/src/main/java/org/tron/common/backup/BackupManager.java index a8812a62bb4..c70d6df4d63 100644 --- a/framework/src/main/java/org/tron/common/backup/BackupManager.java +++ b/framework/src/main/java/org/tron/common/backup/BackupManager.java @@ -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 members = new ConcurrentSet<>(); + private final Set members = new ConcurrentSet<>(); - private final String esName = "backup-manager"; + private final Map 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 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); + } + } + } } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 83d7fd2c63d..4ef22624611 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -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 getInetSocketAddress( return ret; } List 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); + } + } } public static void logConfig() { diff --git a/framework/src/main/java/org/tron/core/config/args/InetUtil.java b/framework/src/main/java/org/tron/core/config/args/InetUtil.java new file mode 100644 index 00000000000..a38a6774e4e --- /dev/null +++ b/framework/src/main/java/org/tron/core/config/args/InetUtil.java @@ -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 getInetSocketAddressList(List items) { + List ret = new ArrayList<>(); + if (items.isEmpty()) { + return ret; + } + // Collect entries whose host part is a domain name (not an IP literal). + List domainEntries = new ArrayList<>(); + for (String item : items) { + String host = NetUtil.parseInetSocketAddress(item).getHostString(); + if (!NetUtil.validIpV4(host) && !NetUtil.validIpV6(host)) { + domainEntries.add(item); + } + } + + // Resolve domain names: spin up a thread pool only when there are multiple domains + Map domainResolved = new HashMap<>(); + if (domainEntries.size() > 1) { + String poolName = "args-dns-lookup"; + ExecutorService dnsPool = ExecutorServiceManager + .newFixedThreadPool(poolName, domainEntries.size(), true); + List> 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; + } +} diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 4d0bf1013d6..db17d29e0cf 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -247,6 +247,14 @@ + + + + + + + +