diff --git a/pom.xml b/pom.xml index 55fa4b36..2691c52e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,11 +3,12 @@ 4.0.0 org.qortal qortal - 4.5.2 + 4.6.1 jar UTF-8 true + 7dc8c6f 0.15.10 1.70 @@ -15,26 +16,26 @@ 1.4.2 3.8.0 1.12.0 - 2.16.1 - 1.26.2 - 3.14.0 + 2.17.0 + 1.27.1 + 3.17.0 1.2.2 0.12.3 4.9.10 - 1.65.0 - 33.2.1-jre + 1.68.1 + 33.3.1-jre 2.2 1.2.1 2.5.1 - 75.1 + 76.1 4.12 4.0.1 2.3.9 2.42 - 9.4.54.v20240208 + 9.4.56.v20240826 1.1.1 20240303 - 1.17.2 + 1.18.1 5.11.0-M2 1.0.0 2.23.1 @@ -44,19 +45,18 @@ 3.6.1 3.4.2 1.1.0 - - 3.12.1 - 0.16 + 2.17.1 + 0.17 3.3.1 3.6.0 - 3.3.0 + 3.5.2 3.25.3 1.5.3 1.17 2.0.10 5.17.14 1.2 - 1.9 + 1.10 1.18.30 2.16.1 2.0.12 diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index 5ebcb2cd..43a2b209 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -13,6 +13,7 @@ import org.qortal.block.Block; import org.qortal.block.BlockChain; import org.qortal.block.BlockChain.BlockTimingByHeight; import org.qortal.controller.arbitrary.*; +import org.qortal.controller.hsqldb.HSQLDBDataCacheManager; import org.qortal.controller.repository.NamesDatabaseIntegrityCheck; import org.qortal.controller.repository.PruneManager; import org.qortal.controller.tradebot.TradeBot; @@ -33,8 +34,10 @@ import org.qortal.gui.SysTray; import org.qortal.network.Network; import org.qortal.network.RNSNetwork; import org.qortal.network.Peer; +import org.qortal.network.PeerAddress; import org.qortal.network.message.*; import org.qortal.repository.*; +import org.qortal.repository.hsqldb.HSQLDBRepository; import org.qortal.repository.hsqldb.HSQLDBRepositoryFactory; import org.qortal.settings.Settings; import org.qortal.transaction.Transaction; @@ -49,8 +52,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.SecureRandom; import java.security.Security; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -96,7 +102,7 @@ public class Controller extends Thread { private final long buildTimestamp; // seconds private final String[] savedArgs; - private ExecutorService callbackExecutor = Executors.newFixedThreadPool(3); + private ExecutorService callbackExecutor = Executors.newFixedThreadPool(4); private volatile boolean notifyGroupMembershipChange = false; /** Latest blocks on our chain. Note: tail/last is the latest block. */ @@ -404,8 +410,17 @@ public class Controller extends Thread { RepositoryManager.setRequestedCheckpoint(Boolean.TRUE); try (final Repository repository = RepositoryManager.getRepository()) { - RepositoryManager.rebuildTransactionSequences(repository); + // RepositoryManager.rebuildTransactionSequences(repository); ArbitraryDataCacheManager.getInstance().buildArbitraryResourcesCache(repository, false); + + if( Settings.getInstance().isDbCacheEnabled() ) { + LOGGER.info("Db Cache Starting ..."); + HSQLDBDataCacheManager hsqldbDataCacheManager = new HSQLDBDataCacheManager((HSQLDBRepository) repositoryFactory.getRepository()); + hsqldbDataCacheManager.start(); + } + else { + LOGGER.info("Db Cache Disabled"); + } } } catch (DataException e) { // If exception has no cause or message then repository is in use by some other process. @@ -496,7 +511,6 @@ public class Controller extends Thread { @Override public void run() { Thread.currentThread().setName("Shutdown hook"); - Controller.getInstance().shutdown(); } }); @@ -576,10 +590,33 @@ public class Controller extends Thread { // If GUI is enabled, we're no longer starting up but actually running now Gui.getInstance().notifyRunning(); - // Check every 10 minutes to see if the block minter is running - Timer timer = new Timer(); + if (Settings.getInstance().isAutoRestartEnabled()) { + // Check every 10 minutes if we have enough connected peers + Timer checkConnectedPeers = new Timer(); - timer.schedule(new TimerTask() { + checkConnectedPeers.schedule(new TimerTask() { + @Override + public void run() { + // Get the connected peers + int myConnectedPeers = Network.getInstance().getImmutableHandshakedPeers().size(); + LOGGER.debug("Node have {} connected peers", myConnectedPeers); + if (myConnectedPeers == 0) { + // Restart node if we have 0 peers + LOGGER.info("Node have no connected peers, restarting node"); + try { + RestartNode.attemptToRestart(); + } catch (Exception e) { + LOGGER.error("Unable to restart the node", e); + } + } + } + }, 10*60*1000, 10*60*1000); + } + + // Check every 10 minutes to see if the block minter is running + Timer checkBlockMinter = new Timer(); + + checkBlockMinter.schedule(new TimerTask() { @Override public void run() { if (blockMinter.isAlive()) { @@ -603,6 +640,73 @@ public class Controller extends Thread { } } }, 10*60*1000, 10*60*1000); + + // Check if we need sync from genesis and start syncing + Timer syncFromGenesis = new Timer(); + syncFromGenesis.schedule(new TimerTask() { + @Override + public void run() { + LOGGER.debug("Start sync from genesis check."); + boolean canBootstrap = Settings.getInstance().getBootstrap(); + boolean needsArchiveRebuild = false; + int checkHeight = 0; + Repository repository = null; + + try { + repository = RepositoryManager.getRepository(); + needsArchiveRebuild = (repository.getBlockArchiveRepository().fromHeight(2) == null); + checkHeight = repository.getBlockRepository().getBlockchainHeight(); + } catch (DataException e) { + throw new RuntimeException(e); + } + + if (canBootstrap || !needsArchiveRebuild || checkHeight > 3) { + LOGGER.debug("Bootstrapping is enabled or we have more than 2 blocks, cancel sync from genesis check."); + syncFromGenesis.cancel(); + return; + } + + if (needsArchiveRebuild && !canBootstrap) { + LOGGER.info("Start syncing from genesis!"); + List seeds = new ArrayList<>(Network.getInstance().getImmutableHandshakedPeers()); + + // Check if have a qualified peer to sync + if (seeds.isEmpty()) { + LOGGER.info("No connected peers, will try again later."); + return; + } + + int index = new SecureRandom().nextInt(seeds.size()); + 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); + Synchronizer.SynchronizationResult syncResult; + + try { + do { + try { + syncResult = Synchronizer.getInstance().actuallySynchronize(targetPeer, true); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + while (syncResult == Synchronizer.SynchronizationResult.OK); + } finally { + // We are syncing now, so can cancel the check + syncFromGenesis.cancel(); + } + } + } + }, 3*60*1000, 3*60*1000); } /** Called by AdvancedInstaller's launch EXE in single-instance mode, when an instance is already running. */