diff --git a/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/maven-metadata-local.xml b/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/maven-metadata-local.xml deleted file mode 100644 index 0c856aac..00000000 --- a/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/maven-metadata-local.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - io.reticulum - reticulum-network-stack - 1.0-SNAPSHOT - - - true - - 20250418180444 - - - jar - 1.0-SNAPSHOT - 20250418180444 - - - pom - 1.0-SNAPSHOT - 20241218212752 - - - - diff --git a/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/reticulum-network-stack-1.0-SNAPSHOT.jar b/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/reticulum-network-stack-1.0-SNAPSHOT.jar deleted file mode 100644 index e1d6ad48..00000000 Binary files a/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/reticulum-network-stack-1.0-SNAPSHOT.jar and /dev/null differ diff --git a/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/reticulum-network-stack-1.0-SNAPSHOT.pom b/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/reticulum-network-stack-1.0-SNAPSHOT.pom deleted file mode 100644 index 1b1cc206..00000000 --- a/lib/io/reticulum/reticulum-network-stack/1.0-SNAPSHOT/reticulum-network-stack-1.0-SNAPSHOT.pom +++ /dev/null @@ -1,9 +0,0 @@ - - - 4.0.0 - io.reticulum - reticulum-network-stack - 1.0-SNAPSHOT - POM was created from install:install-file - diff --git a/lib/io/reticulum/reticulum-network-stack/maven-metadata-local.xml b/lib/io/reticulum/reticulum-network-stack/maven-metadata-local.xml deleted file mode 100644 index 45dbe562..00000000 --- a/lib/io/reticulum/reticulum-network-stack/maven-metadata-local.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - io.reticulum - reticulum-network-stack - - - 1.0-SNAPSHOT - - 20250418180444 - - diff --git a/pom.xml b/pom.xml index 82e0f42f..11d747fd 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,7 @@ 3.5.2 3.25.3 1.5.3 + c0eeadf 1.17 2.0.10 5.18.2 @@ -512,6 +513,12 @@ altcoinj ${altcoinj.version} + + + com.github.sergst83 + reticulum-network-stack + ${reticulum.version} + com.googlecode.json-simple diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index ae2d6a99..733286c1 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -543,8 +543,8 @@ public class Controller extends Thread { LOGGER.info("Starting synchronizer"); Synchronizer.getInstance().start(); - //LOGGER.info("Starting synchronizer over Reticulum"); - //RNSSynchronizer.getInstance().start(); + LOGGER.info("Starting synchronizer over Reticulum"); + RNSSynchronizer.getInstance().start(); LOGGER.info("Starting block minter"); blockMinter = new BlockMinter(); @@ -743,6 +743,73 @@ public class Controller extends Thread { } } }, 3*60*1000, 3*60*1000); + //Timer syncFromGenesisRNS = new Timer(); + //syncFromGenesisRNS.schedule(new TimerTask() { + // @Override + // public void run() { + // LOGGER.debug("Start sync from genesis check (RNS)."); + // boolean canBootstrap = Settings.getInstance().getBootstrap(); + // boolean needsArchiveRebuildRNS = false; + // int checkHeightRNS = 0; + // + // try (final Repository repository = RepositoryManager.getRepository()){ + // needsArchiveRebuildRNS = (repository.getBlockArchiveRepository().fromHeight(2) == null); + // checkHeightRNS = repository.getBlockRepository().getBlockchainHeight(); + // } catch (DataException e) { + // throw new RuntimeException(e); + // } + // + // if (canBootstrap || !needsArchiveRebuildRNS || checkHeightRNS > 3) { + // LOGGER.debug("Bootstrapping is enabled or we have more than 2 blocks, cancel sync from genesis check."); + // syncFromGenesisRNS.cancel(); + // return; + // } + // + // if (needsArchiveRebuildRNS && !canBootstrap) { + // LOGGER.info("Start syncing from genesis (RNS)!"); + // List seeds = new ArrayList<>(RNSNetwork.getInstance().getImmutableActiveLinkedPeers()); + // + // // Check if have a qualified peer to sync + // if (seeds.isEmpty()) { + // LOGGER.info("No connected RNSPeer(s), will try again later."); + // return; + // } + // + // int index = new SecureRandom().nextInt(seeds.size()); + // RNSPeer syncPeer = seeds.get(index); + // var syncPeerLinkAsString = syncPeer.getPeerLink().toString(); + // //String syncNode = String.valueOf(seeds.get(index)); + // //PeerAddress peerAddress = PeerAddress.fromString(syncNode); + // //InetSocketAddress resolvedAddress = null; + // // + // //try { + // // resolvedAddress = peerAddress.toSocketAddress(); + // //} catch (UnknownHostException e) { + // // throw new RuntimeException(e); + // //} + // // + // //InetSocketAddress finalResolvedAddress = resolvedAddress; + // //Peer targetPeer = seeds.stream().filter(peer -> peer.getResolvedAddress().equals(finalResolvedAddress)).findFirst().orElse(null); + // //RNSPeer targetPeerRNS = seeds.stream().findFirst().orElse(null); + // RNSPeer targetPeerRNS = seeds.stream().filter(peer -> peer.getPeerLink().toString().equals(syncPeerLinkAsString)).findFirst().orElse(null); + // RNSSynchronizer.SynchronizationResult syncResultRNS; + // + // try { + // do { + // try { + // syncResultRNS = RNSSynchronizer.getInstance().actuallySynchronize(targetPeerRNS, true); + // } catch (InterruptedException e) { + // throw new RuntimeException(e); + // } + // } + // while (syncResultRNS == RNSSynchronizer.SynchronizationResult.OK); + // } finally { + // // We are syncing now, so can cancel the check + // syncFromGenesisRNS.cancel(); + // } + // } + // } + //}, 3*60*1000, 3*60*1000); } /** Called by AdvancedInstaller's launch EXE in single-instance mode, when an instance is already running. */ @@ -858,29 +925,29 @@ public class Controller extends Thread { repositoryMaintenanceInterval = getRandomRepositoryMaintenanceInterval(); } - //// Prune stuck/slow/old peers - //if (now >= prunePeersTimestamp + prunePeersInterval) { - // prunePeersTimestamp = now + prunePeersInterval; - // - // try { - // LOGGER.debug("Pruning peers..."); - // Network.getInstance().prunePeers(); - // } catch (DataException e) { - // LOGGER.warn(String.format("Repository issue when trying to prune peers: %s", e.getMessage())); - // } - //} + // Prune stuck/slow/old peers + if (now >= prunePeersTimestamp + prunePeersInterval) { + prunePeersTimestamp = now + prunePeersInterval; - //// Q: Do we need global pruning? - //if (now >= pruneRNSPeersTimestamp + pruneRNSPeersInterval) { - // pruneRNSPeersTimestamp = now + pruneRNSPeersInterval; - // - // try { - // LOGGER.debug("Pruning Reticulum peers..."); - // RNSNetwork.getInstance().prunePeers(); - // } catch (DataException e) { - // LOGGER.warn(String.format("Repository issue when trying to prune Reticulum peers: %s", e.getMessage())); - // } - //} + try { + LOGGER.debug("Pruning peers..."); + Network.getInstance().prunePeers(); + } catch (DataException e) { + LOGGER.warn(String.format("Repository issue when trying to prune peers: %s", e.getMessage())); + } + } + + // Q: Do we need global pruning? + if (now >= pruneRNSPeersTimestamp + pruneRNSPeersInterval) { + pruneRNSPeersTimestamp = now + pruneRNSPeersInterval; + + try { + LOGGER.debug("Pruning Reticulum peers..."); + RNSNetwork.getInstance().prunePeers(); + } catch (DataException e) { + LOGGER.warn(String.format("Repository issue when trying to prune Reticulum peers: %s", e.getMessage())); + } + } // Delete expired transactions if (now >= deleteExpiredTimestamp) { diff --git a/src/main/java/org/qortal/network/RNSNetwork.java b/src/main/java/org/qortal/network/RNSNetwork.java index 85d26bf6..d0e3708b 100644 --- a/src/main/java/org/qortal/network/RNSNetwork.java +++ b/src/main/java/org/qortal/network/RNSNetwork.java @@ -24,7 +24,7 @@ import static io.reticulum.link.TeardownSession.TIMEOUT; import static io.reticulum.link.LinkStatus.ACTIVE; import static io.reticulum.link.LinkStatus.STALE; import static io.reticulum.link.LinkStatus.CLOSED; -//import static io.reticulum.link.LinkStatus.PENDING; +import static io.reticulum.link.LinkStatus.PENDING; import static io.reticulum.link.LinkStatus.HANDSHAKE; //import static io.reticulum.packet.PacketContextType.LINKCLOSE; //import static io.reticulum.identity.IdentityKnownDestination.recall; @@ -372,7 +372,7 @@ public class RNSNetwork { newPeer.setMessageMagic(getMessageMagic()); // make sure the peer has a channel and buffer newPeer.getOrInitPeerBuffer(); - incomingPeers.add(newPeer); + addIncomingPeer(newPeer); log.info("***> Client connected, link: {}", link); } @@ -409,7 +409,7 @@ public class RNSNetwork { // add to peer list if we can use more peers //synchronized (this) { - var lps = RNSNetwork.getInstance().getLinkedPeers(); + var lps = RNSNetwork.getInstance().getImmutableLinkedPeers(); for (RNSPeer p: lps) { var pl = p.getPeerLink(); if ((nonNull(pl) && (pl.getStatus() == ACTIVE))) { @@ -488,11 +488,11 @@ public class RNSNetwork { final Long now = NTP.getTime(); - // Prune stuck/slow/old peers (moved from Controller) - task = maybeProduceRNSPrunePeersTask(now); - if (task != null) { - return task; - } + //// Prune stuck/slow/old peers (moved from Controller) + //task = maybeProduceRNSPrunePeersTask(now); + //if (task != null) { + // return task; + //} // ping task (Link+Channel+Buffer) task = maybeProducePeerPingTask(now); @@ -505,11 +505,11 @@ public class RNSNetwork { return task; } - // Prune stuck/slow/old peers (moved from Controller) - task = maybeProduceRNSPrunePeersTask(now); - if (task != null) { - return task; - } + //// Prune stuck/slow/old peers (moved from Controller) + //task = maybeProduceRNSPrunePeersTask(now); + //if (task != null) { + // return task; + //} return null; } @@ -609,9 +609,9 @@ public class RNSNetwork { } public void removeLinkedPeer(RNSPeer peer) { - if (nonNull(peer.getPeerBuffer())) { - peer.getPeerBuffer().close(); - } + //if (nonNull(peer.getPeerBuffer())) { + // peer.getPeerBuffer().close(); + //} if (nonNull(peer.getPeerLink())) { peer.getPeerLink().teardown(); } @@ -649,23 +649,6 @@ public class RNSNetwork { // TODO, methods for: getAvailablePeer - // maintenance - //public void removePeer(RNSPeer peer) { - // synchronized(this) { - // List peerList = this.linkedPeers; - // log.info("removing peer {} on peer shutdown", peer); - // peerList.remove(peer); - // } - //} - - //public void pingPeer(RNSPeer peer) { - // if (nonNull(peer)) { - // peer.pingRemote(); - // } else { - // log.error("peer argument is null"); - // } - //} - private Boolean isUnreachable(RNSPeer peer) { var result = peer.getDeleteMe(); var now = Instant.now(); @@ -713,22 +696,30 @@ public class RNSNetwork { //@Synchronized public void prunePeers() throws DataException { // run periodically (by the Controller) - var peerList = getLinkedPeers(); - var incomingPeerList = getIncomingPeers(); - log.info("number of links (linkedPeers / incomingPeers) before prunig: {}, {}", peerList.size(), - incomingPeerList.size()); + var peerList = getImmutableLinkedPeers(); + var incomingPeerList = getImmutableIncomingPeers(); + //log.info("number of links (linkedPeers / incomingPeers) before prunig: {}, {}", peerList.size(), + // incomingPeerList.size()); + log.info("number of links (linkedPeers (active) / incomingPeers before prunig: {} ({}), {}", + getImmutableLinkedPeers().size(), getImmutableActiveLinkedPeers().size(), + getImmutableIncomingPeers().size()); // prune initiator peers - List lps = getLinkedPeers(); + List lps = getImmutableLinkedPeers(); for (RNSPeer p : lps) { var pLink = p.getPeerLink(); if (nonNull(pLink)) { - log.info("peer link: {}, status: {}", pLink, pLink.getStatus()); - if (pLink.getStatus() == ACTIVE) { - p.pingRemote(); + if (pLink.getStatus() == PENDING) { + pLink.teardown(); } if (p.getPeerTimedOut()) { pLink.teardown(); } + log.info("peer link: {}, status: {}", pLink, pLink.getStatus()); + if (pLink.getStatus() == ACTIVE) { + p.pingRemote(); + } else { + removeLinkedPeer(p); + } } } //Link pLink; @@ -773,15 +764,17 @@ public class RNSNetwork { List inaps = incomingNonActivePeers(); //log.info("number of inactive incoming peers: {}", inaps.size()); for (RNSPeer p: inaps) { - incomingPeerList.remove(incomingPeerList.indexOf(p)); + //incomingPeerList.remove(incomingPeerList.indexOf(p)); + removeIncomingPeer(p); } - log.info("number of links (linkedPeers / incomingPeers) after prunig: {}, {}", peerList.size(), - incomingPeerList.size()); + log.info("number of links (linkedPeers (active) / incomingPeers after prunig: {} ({}), {}", + getImmutableLinkedPeers().size(), getImmutableActiveLinkedPeers().size(), + getImmutableIncomingPeers().size()); maybeAnnounce(getBaseDestination()); } public void maybeAnnounce(Destination d) { - if (getLinkedPeers().size() < MIN_DESIRED_PEERS) { + if (getImmutableActiveLinkedPeers().size() < MIN_DESIRED_PEERS) { d.announce(); } } diff --git a/src/main/java/org/qortal/network/RNSPeer.java b/src/main/java/org/qortal/network/RNSPeer.java index 81d693d5..dba40878 100644 --- a/src/main/java/org/qortal/network/RNSPeer.java +++ b/src/main/java/org/qortal/network/RNSPeer.java @@ -108,8 +108,8 @@ public class RNSPeer { // for qortal networking private static final int RESPONSE_TIMEOUT = 3000; // [ms] - private static final int PING_INTERVAL = 34_000; // [ms] - private static final long LINK_PING_INTERVAL = 34 * 1000L; // ms + private static final int PING_INTERVAL = 55_000; // [ms] + private static final long LINK_PING_INTERVAL = 55 * 1000L; // ms private byte[] messageMagic; // set in message creating classes private Long lastPing = null; // last (packet) ping roundtrip time [ms] private Long lastPingSent = null; // time last (packet) ping was sent, or null if not started. @@ -370,7 +370,7 @@ public class RNSPeer { break; case PONG: - //log.info("PONG received"); + log.trace("PONG received"); break; // Do we need this ? (no need to relay peer list...) @@ -378,30 +378,30 @@ public class RNSPeer { // onPeersV2Message(peer, message); // break; - case BLOCK_SUMMARIES: - // from Synchronizer - addToQueue(message); - break; - - case BLOCK_SUMMARIES_V2: - // from Synchronizer - addToQueue(message); - break; - - case SIGNATURES: - // from Synchronizer - addToQueue(message); - break; - - case BLOCK: - // from Synchronizer - addToQueue(message); - break; - - case BLOCK_V2: - // from Synchronizer - addToQueue(message); - break; + //case BLOCK_SUMMARIES: + // // from Synchronizer + // addToQueue(message); + // break; + // + //case BLOCK_SUMMARIES_V2: + // // from Synchronizer + // addToQueue(message); + // break; + // + //case SIGNATURES: + // // from Synchronizer + // addToQueue(message); + // break; + // + //case BLOCK: + // // from Synchronizer + // addToQueue(message); + // break; + // + //case BLOCK_V2: + // // from Synchronizer + // addToQueue(message); + // break; default: log.info("default - type {} message received ({} bytes)", message.getType(), data.length); @@ -419,7 +419,7 @@ public class RNSPeer { } /** - * we need to queue all incomming messages that follow request/response + * we need to queue all incoming messages that follow request/response * with explicit handling of the response message. */ public void addToQueue(Message message) { @@ -499,10 +499,12 @@ public class RNSPeer { public void packetTimedOut(PacketReceipt receipt) { log.info("packet timed out, receipt status: {}", receipt.getStatus()); - if (receipt.getStatus() == PacketReceiptStatus.FAILED) { - this.peerTimedOut = true; - this.peerLink.teardown(); - } + //if (receipt.getStatus() == PacketReceiptStatus.FAILED) { + // this.peerTimedOut = true; + // this.peerLink.teardown(); + //} + this.peerTimedOut = true; + this.peerLink.teardown(); } /** Link Request callbacks */ diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index f3f84e12..b15abefe 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -616,6 +616,8 @@ public class Settings { // Related to Reticulum networking + /** Preferred network: tcpip or reticulum */ + private String preferredNetwork = "reticulum"; /** Maximum number of Reticulum peers allowed. */ private int reticulumMaxPeers = 55; /** Minimum number of Reticulum peers desired. */ @@ -1380,6 +1382,10 @@ public class Settings { return connectionPoolMonitorEnabled; } + public String getPreferredNetwork () { + return this.preferredNetwork.toLowerCase(Locale.getDefault()); + } + public int getReticulumMaxPeers() { return this.reticulumMaxPeers; }