diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index b96ca3c865c..264298cf8b1 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -68,6 +68,7 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet protected AppModule module; protected Config config; private boolean isShutdownInProgress; + private boolean hasDowngraded; public BisqExecutable(String fullName, String scriptName, String appName, String version) { this.fullName = fullName; @@ -133,9 +134,17 @@ protected void onApplicationLaunched() { CommonSetup.setupUncaughtExceptionHandler(this); setupGuice(); setupAvoidStandbyMode(); - readAllPersisted(this::startApplication); - } + hasDowngraded = BisqSetup.hasDowngraded(); + if (hasDowngraded) { + // If user tried to downgrade we do not read the persisted data to avoid data corruption + // We call startApplication to enable UI to show popup. We prevent in BisqSetup to go further + // in the process and require a shut down. + startApplication(); + } else { + readAllPersisted(this::startApplication); + } + } /////////////////////////////////////////////////////////////////////////////////////////// // We continue with a series of synchronous execution tasks @@ -236,11 +245,16 @@ public void gracefulShutDown(ResultHandler resultHandler) { injector.getInstance(P2PService.class).shutDown(() -> { log.info("P2PService shutdown completed"); module.close(injector); - PersistenceManager.flushAllDataToDisk(() -> { - log.info("Graceful shutdown completed. Exiting now."); - resultHandler.handleResult(); + if (!hasDowngraded) { + // If user tried to downgrade we do not write the persistable data to avoid data corruption + PersistenceManager.flushAllDataToDisk(() -> { + log.info("Graceful shutdown completed. Exiting now."); + resultHandler.handleResult(); + System.exit(EXIT_SUCCESS); + }); + } else { System.exit(EXIT_SUCCESS); - }); + } }); }); walletsSetup.shutDown(); @@ -250,20 +264,31 @@ public void gracefulShutDown(ResultHandler resultHandler) { // Wait max 20 sec. UserThread.runAfter(() -> { log.warn("Timeout triggered resultHandler"); - PersistenceManager.flushAllDataToDisk(() -> { - log.info("Graceful shutdown resulted in a timeout. Exiting now."); - resultHandler.handleResult(); + if (!hasDowngraded) { + // If user tried to downgrade we do not write the persistable data to avoid data corruption + PersistenceManager.flushAllDataToDisk(() -> { + log.info("Graceful shutdown resulted in a timeout. Exiting now."); + resultHandler.handleResult(); + System.exit(EXIT_SUCCESS); + }); + } else { System.exit(EXIT_SUCCESS); - }); + } + }, 20); } catch (Throwable t) { log.error("App shutdown failed with exception {}", t.toString()); t.printStackTrace(); - PersistenceManager.flushAllDataToDisk(() -> { - log.info("Graceful shutdown resulted in an error. Exiting now."); - resultHandler.handleResult(); + if (!hasDowngraded) { + // If user tried to downgrade we do not write the persistable data to avoid data corruption + PersistenceManager.flushAllDataToDisk(() -> { + log.info("Graceful shutdown resulted in an error. Exiting now."); + resultHandler.handleResult(); + System.exit(EXIT_FAILURE); + }); + } else { System.exit(EXIT_FAILURE); - }); + } } } diff --git a/core/src/main/java/bisq/core/app/BisqHeadlessApp.java b/core/src/main/java/bisq/core/app/BisqHeadlessApp.java index 71a6ab4e394..38c9f478524 100644 --- a/core/src/main/java/bisq/core/app/BisqHeadlessApp.java +++ b/core/src/main/java/bisq/core/app/BisqHeadlessApp.java @@ -20,6 +20,7 @@ import bisq.core.trade.TradeManager; import bisq.common.UserThread; +import bisq.common.app.Version; import bisq.common.file.CorruptedStorageFileHandler; import bisq.common.setup.GracefulShutDownHandler; @@ -94,6 +95,8 @@ protected void setupHandlers() { bisqSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> log.info("setRevolutAccountsUpdateHandler: revolutAccountList={}", revolutAccountList)); bisqSetup.setOsxKeyLoggerWarningHandler(() -> log.info("setOsxKeyLoggerWarningHandler")); bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler")); + bisqSetup.setDownGradePreventionHandler(lastVersion -> log.info("Downgrade from version {} to version {} is not supported", + lastVersion, Version.VERSION)); corruptedStorageFileHandler.getFiles().ifPresent(files -> log.warn("getCorruptedDatabaseFiles. files={}", files)); tradeManager.setTakeOfferRequestErrorMessageHandler(errorMessage -> log.error("onTakeOfferRequestErrorMessageHandler")); diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 3325e37453c..925b5e793a1 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -48,6 +48,7 @@ import bisq.common.UserThread; import bisq.common.app.DevEnv; import bisq.common.app.Log; +import bisq.common.app.Version; import bisq.common.config.Config; import bisq.common.util.InvalidVersionException; import bisq.common.util.Utilities; @@ -71,11 +72,15 @@ import org.bouncycastle.crypto.params.KeyParameter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Scanner; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @@ -92,6 +97,7 @@ @Slf4j @Singleton public class BisqSetup { + private static final String VERSION_FILE_NAME = "version"; public interface BisqSetupListener { default void onInitP2pNetwork() { @@ -172,6 +178,9 @@ default void onRequestWalletPassword() { @Setter @Nullable private Runnable qubesOSInfoHandler; + @Setter + @Nullable + private Consumer downGradePreventionHandler; @Getter final BooleanProperty newVersionAvailableProperty = new SimpleBooleanProperty(false); @@ -255,6 +264,12 @@ public void addBisqSetupListener(BisqSetupListener listener) { } public void start() { + // If user tried to downgrade we require a shutdown + if (hasDowngraded(downGradePreventionHandler)) { + return; + } + + persistBisqVersion(); maybeReSyncSPVChain(); maybeShowTac(this::step2); } @@ -387,7 +402,7 @@ private void initWallet() { requestWalletPasswordHandler.accept(aesKey -> { walletsManager.setAesKey(aesKey); walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().btcWallet(), - aesKey); + aesKey); if (preferences.isResyncSpvRequested()) { if (showFirstPopupIfResyncSPVRequestedHandler != null) showFirstPopupIfResyncSPVRequestedHandler.run(); @@ -487,6 +502,68 @@ private void checkForInvalidMakerFeeTxs() { }); } + @Nullable + public static String getLastBisqVersion() { + File versionFile = getVersionFile(); + if (!versionFile.exists()) { + return null; + } + try (Scanner scanner = new Scanner(versionFile)) { + // We only expect 1 line + if (scanner.hasNextLine()) { + return scanner.nextLine(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + private static File getVersionFile() { + return new File(Config.appDataDir(), VERSION_FILE_NAME); + } + + public static boolean hasDowngraded() { + return hasDowngraded(getLastBisqVersion()); + } + + public static boolean hasDowngraded(String lastVersion) { + return lastVersion != null && Version.isNewVersion(lastVersion, Version.VERSION); + } + + public static boolean hasDowngraded(@Nullable Consumer downGradePreventionHandler) { + String lastVersion = getLastBisqVersion(); + boolean hasDowngraded = hasDowngraded(lastVersion); + if (hasDowngraded) { + log.error("Downgrade from version {} to version {} is not supported", lastVersion, Version.VERSION); + if (downGradePreventionHandler != null) { + downGradePreventionHandler.accept(lastVersion); + } + } + return hasDowngraded; + } + + public static void persistBisqVersion() { + File versionFile = getVersionFile(); + if (!versionFile.exists()) { + try { + if (!versionFile.createNewFile()) { + log.error("Version file could not be created"); + } + } catch (IOException e) { + e.printStackTrace(); + log.error("Version file could not be created. {}", e.toString()); + } + } + + try (FileWriter fileWriter = new FileWriter(versionFile, false)) { + fileWriter.write(Version.VERSION); + } catch (IOException e) { + e.printStackTrace(); + log.error("Writing Version failed. {}", e.toString()); + } + } + private void checkForCorrectOSArchitecture() { if (!Utilities.isCorrectOSArchitecture() && wrongOSArchitectureHandler != null) { String osArchitecture = Utilities.getOSArchitecture(); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index cfed8ffda8c..9c621af54a9 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2819,6 +2819,7 @@ popup.info.shutDownWithOpenOffers=Bisq is being shut down, but there are open of (i.e., make sure it doesn't go into standby mode...monitor standby is not a problem). popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\n\ Please make sure your Bisq qube is setup according to our Setup Guide at [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes]. +popup.warn.downGradePrevention=Downgrade from version {0} to version {1} is not supported. Please use the latest Bisq version. popup.privateNotification.headline=Important private notification! diff --git a/desktop/src/main/java/bisq/desktop/main/MainView.java b/desktop/src/main/java/bisq/desktop/main/MainView.java index 321235521dc..55bda988a6e 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainView.java +++ b/desktop/src/main/java/bisq/desktop/main/MainView.java @@ -809,7 +809,7 @@ private class NavButton extends AutoTooltipToggleButton { this.setToggleGroup(navButtons); this.getStyleClass().add("nav-button"); // Japanese fonts are dense, increase top nav button text size - if (model.getPreferences().getUserLanguage().equals("ja")) { + if (model.getPreferences() != null && "ja".equals(model.getPreferences().getUserLanguage())) { this.getStyleClass().add("nav-button-japanese"); } diff --git a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java index c2815370c53..75c724ec1d4 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java @@ -403,6 +403,13 @@ private void setupHandlers() { } }); + bisqSetup.setDownGradePreventionHandler(lastVersion -> { + new Popup().warning(Res.get("popup.warn.downGradePrevention", lastVersion, Version.VERSION)) + .useShutDownButton() + .hideCloseButton() + .show(); + }); + corruptedStorageFileHandler.getFiles().ifPresent(files -> new Popup() .warning(Res.get("popup.warning.incompatibleDB", files.toString(), config.appDataDir)) .useShutDownButton()