diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index a7d028bc..9123a130 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -623,6 +623,11 @@ public class Controller extends Thread { return peerChainTipData == null || peerChainTipData.getLastBlockSignature() == null || inferiorChainTips.contains(new ByteArray(peerChainTipData.getLastBlockSignature())); }; + public static final Predicate hasOldVersion = peer -> { + final String minPeerVersion = Settings.getInstance().getMinPeerVersion(); + return peer.isAtLeastVersion(minPeerVersion) == false; + }; + private void potentiallySynchronize() throws InterruptedException { // Already synchronizing via another thread? if (this.isSynchronizing) @@ -639,11 +644,15 @@ public class Controller extends Thread { // Disregard peers that don't have a recent block peers.removeIf(hasNoRecentBlock); + // Disregard peers that are on an old version + peers.removeIf(hasOldVersion); + checkRecoveryModeForPeers(peers); if (recoveryMode) { peers = Network.getInstance().getHandshakedPeers(); peers.removeIf(hasOnlyGenesisBlock); peers.removeIf(hasMisbehaved); + peers.removeIf(hasOldVersion); } // Check we have enough peers to potentially synchronize diff --git a/src/main/java/org/qortal/network/Handshake.java b/src/main/java/org/qortal/network/Handshake.java index 3c033e88..ed6d02db 100644 --- a/src/main/java/org/qortal/network/Handshake.java +++ b/src/main/java/org/qortal/network/Handshake.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -51,7 +50,7 @@ public enum Handshake { String versionString = helloMessage.getVersionString(); - Matcher matcher = VERSION_PATTERN.matcher(versionString); + Matcher matcher = peer.VERSION_PATTERN.matcher(versionString); if (!matcher.lookingAt()) { LOGGER.debug(() -> String.format("Peer %s sent invalid HELLO version string '%s'", peer, versionString)); return null; @@ -244,8 +243,6 @@ public enum Handshake { /** Maximum allowed difference between peer's reported timestamp and when they connected, in milliseconds. */ private static final long MAX_TIMESTAMP_DELTA = 30 * 1000L; // ms - private static final Pattern VERSION_PATTERN = Pattern.compile(Controller.VERSION_PREFIX + "(\\d{1,3})\\.(\\d{1,5})\\.(\\d{1,5})"); - private static final long PEER_VERSION_131 = 0x0100030001L; private static final int POW_BUFFER_SIZE_PRE_131 = 8 * 1024 * 1024; // bytes diff --git a/src/main/java/org/qortal/network/Peer.java b/src/main/java/org/qortal/network/Peer.java index 08db0dd9..ffc90dc7 100644 --- a/src/main/java/org/qortal/network/Peer.java +++ b/src/main/java/org/qortal/network/Peer.java @@ -20,9 +20,12 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.qortal.controller.Controller; import org.qortal.data.block.CommonBlockData; import org.qortal.data.network.PeerChainTipData; import org.qortal.data.network.PeerData; @@ -87,6 +90,9 @@ public class Peer { byte[] ourChallenge; + // Versioning + public static final Pattern VERSION_PATTERN = Pattern.compile(Controller.VERSION_PREFIX + "(\\d{1,3})\\.(\\d{1,5})\\.(\\d{1,5})"); + // Peer info private final Object peerInfoLock = new Object(); @@ -634,6 +640,35 @@ public class Peer { } + // Minimum version + + public boolean isAtLeastVersion(String minVersionString) { + if (minVersionString == null) + return false; + + // Add the version prefix + minVersionString = Controller.VERSION_PREFIX + minVersionString; + + Matcher matcher = VERSION_PATTERN.matcher(minVersionString); + if (!matcher.lookingAt()) + return false; + + // We're expecting 3 positive shorts, so we can convert 1.2.3 into 0x0100020003 + long minVersion = 0; + for (int g = 1; g <= 3; ++g) { + long value = Long.parseLong(matcher.group(g)); + + if (value < 0 || value > Short.MAX_VALUE) + return false; + + minVersion <<= 16; + minVersion |= value; + } + + return this.getPeersVersion() >= minVersion; + } + + // Common block data public boolean canUseCachedCommonBlockData() { diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index ba9678f2..f94db927 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -124,6 +124,8 @@ public class Settings { private int networkPoWComputePoolSize = 2; /** Maximum number of retry attempts if a peer fails to respond with the requested data */ private int maxRetries = 2; + /** Minimum peer version number required in order to sync with them */ + private String minPeerVersion = "1.5.0"; // Which blockchains this node is running private String blockchainConfig = null; // use default from resources @@ -412,6 +414,8 @@ public class Settings { public int getMaxRetries() { return this.maxRetries; } + public String getMinPeerVersion() { return this.minPeerVersion; } + public String getBlockchainConfig() { return this.blockchainConfig; }