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 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"); } 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 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 edc53a45e1b..dabbc076e3e 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 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()