From df9ef80edfd6ad8cb495769ce34287ffe6a62304 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Fri, 20 Nov 2020 15:10:39 -0500 Subject: [PATCH 1/2] Avoid nullpointer --- desktop/src/main/java/bisq/desktop/main/MainView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/main/MainView.java b/desktop/src/main/java/bisq/desktop/main/MainView.java index 321235521d..55bda988a6 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 @@ public class MainView extends InitializableView 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"); } From 9360e89ae8d1a07a758ab84713c46cfa4451a17d Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Fri, 20 Nov 2020 15:27:50 -0500 Subject: [PATCH 2/2] Check if user has downgraded to an older version. If so require shutdown and do not read or write persisted data. We had recently a case where a user downgraded from 1.4.2 to 1.3.9 and this caused failed trades and the wallet funds have been missing due to some complexities of the wallet wegwit upgrade. The fund could be recovered but it took quite some effort. As downgrade is never tested and can lead to all kind of weird bugs we should prevent that users accidentally can do it. If there is valid reason to downgrade they can remove the version file. --- .../java/bisq/core/app/BisqExecutable.java | 53 +++++++++---- .../java/bisq/core/app/BisqHeadlessApp.java | 3 + .../main/java/bisq/core/app/BisqSetup.java | 79 ++++++++++++++++++- .../resources/i18n/displayStrings.properties | 1 + .../java/bisq/desktop/main/MainViewModel.java | 7 ++ 5 files changed, 128 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index b96ca3c865..264298cf8b 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 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet 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 abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet 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 abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet // 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 71a6ab4e39..38c9f47852 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 @@ package bisq.core.app; 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 @@ public class BisqHeadlessApp implements HeadlessApp { 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 3325e37453..925b5e793a 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.Timer; 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 javafx.collections.SetChangeListener; 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 @@ import javax.annotation.Nullable; @Slf4j @Singleton public class BisqSetup { + private static final String VERSION_FILE_NAME = "version"; public interface BisqSetupListener { default void onInitP2pNetwork() { @@ -172,6 +178,9 @@ public class BisqSetup { @Setter @Nullable private Runnable qubesOSInfoHandler; + @Setter + @Nullable + private Consumer downGradePreventionHandler; @Getter final BooleanProperty newVersionAvailableProperty = new SimpleBooleanProperty(false); @@ -255,6 +264,12 @@ public class BisqSetup { } 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 @@ public class BisqSetup { 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 @@ public class BisqSetup { }); } + @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 edc53a45e1..dabbc076e3 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/MainViewModel.java b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java index c2815370c5..75c724ec1d 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 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { } }); + 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()