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.
This commit is contained in:
chimp1984 2020-11-20 15:27:50 -05:00
parent df9ef80edf
commit 9360e89ae8
No known key found for this signature in database
GPG key ID: 9801B4EC591F90E3
5 changed files with 128 additions and 15 deletions

View file

@ -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();
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);
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");
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();
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);
}
}
}

View file

@ -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"));

View file

@ -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<String> 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);
}
@ -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<String> 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();

View file

@ -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!

View file

@ -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()