Skip to content

Commit

Permalink
Merge pull request #4846 from bisq-network/release/v1.5.0
Browse files Browse the repository at this point in the history
Release/v1.5.0
  • Loading branch information
sqrrm authored Nov 26, 2020
2 parents 1110fb5 + 11b0a46 commit 3cc9e63
Show file tree
Hide file tree
Showing 181 changed files with 9,912 additions and 3,039 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ configure(subprojects) {

ext { // in alphabetical order
bcVersion = '1.63'
bitcoinjVersion = 'a733034'
bitcoinjVersion = '7752cb7'
btcdCli4jVersion = '27b94333'
codecVersion = '1.13'
easybindVersion = '1.0.3'
Expand Down Expand Up @@ -386,7 +386,7 @@ configure(project(':desktop')) {
apply plugin: 'witness'
apply from: '../gradle/witness/gradle-witness.gradle'

version = '1.4.2-SNAPSHOT'
version = '1.5.0-SNAPSHOT'

mainClassName = 'bisq.desktop.app.BisqAppMain'

Expand Down
11 changes: 7 additions & 4 deletions common/src/main/java/bisq/common/app/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ public class Version {
// VERSION = 0.5.0 introduces proto buffer for the P2P network and local DB and is a not backward compatible update
// Therefore all sub versions start again with 1
// We use semantic versioning with major, minor and patch
public static final String VERSION = "1.4.2";
public static final String VERSION = "1.5.0";

/**
* Holds a list of the tagged resource files for optimizing the getData requests.
* This must not contain each version but only those where we add new version-tagged resource files for
* historical data stores.
*/
public static final List<String> HISTORICAL_RESOURCE_FILE_VERSION_TAGS = Arrays.asList("1.4.0");
public static final List<String> HISTORICAL_RESOURCE_FILE_VERSION_TAGS = Arrays.asList("1.4.0", "1.5.0");

public static int getMajorVersion(String version) {
return getSubVersion(version, 0);
Expand Down Expand Up @@ -92,10 +92,13 @@ private static int getSubVersion(String version, int index) {

// The version no. of the current protocol. The offer holds that version.
// A taker will check the version of the offers to see if his version is compatible.
// Offers created with the old version will become invalid and have to be canceled.
// For the switch to version 2, offers created with the old version will become invalid and have to be canceled.
// For the switch to version 3, offers created with the old version can be migrated to version 3 just by opening
// the Bisq app.
// VERSION = 0.5.0 -> TRADE_PROTOCOL_VERSION = 1
// Version 1.2.2 -> TRADE_PROTOCOL_VERSION = 2
public static final int TRADE_PROTOCOL_VERSION = 2;
// Version 1.5.0 -> TRADE_PROTOCOL_VERSION = 3
public static final int TRADE_PROTOCOL_VERSION = 3;
private static int p2pMessageVersion;

public static final String BSQ_TX_VERSION = "1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public boolean isRegtest() {
return "BTC_REGTEST".equals(name());
}

public long getDefaultMinFeePerByte() {
public long getDefaultMinFeePerVbyte() {
return 2;
}
}
114 changes: 85 additions & 29 deletions common/src/main/java/bisq/common/persistence/PersistenceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

Expand Down Expand Up @@ -80,43 +81,59 @@ public class PersistenceManager<T extends PersistableEnvelope> {
///////////////////////////////////////////////////////////////////////////////////////////

public static final Map<String, PersistenceManager<?>> ALL_PERSISTENCE_MANAGERS = new HashMap<>();
public static boolean FLUSH_ALL_DATA_TO_DISK_CALLED = false;

// We don't know from which thread we are called so we map back to user thread

// We require being called only once from the global shutdown routine. As the shutdown routine has a timeout
// and error condition where we call the method as well beside the standard path and it could be that those
// alternative code paths call our method after it was called already, so it is a valid but rare case.
// We add a guard to prevent repeated calls.
public static void flushAllDataToDisk(ResultHandler completeHandler) {
log.info("Start flushAllDataToDisk at shutdown");
AtomicInteger openInstances = new AtomicInteger(ALL_PERSISTENCE_MANAGERS.size());
// We don't know from which thread we are called so we map to user thread
UserThread.execute(() -> {
if (FLUSH_ALL_DATA_TO_DISK_CALLED) {
log.warn("We got flushAllDataToDisk called again. This can happen in some rare cases. We ignore the repeated call.");
return;
}

if (openInstances.get() == 0) {
log.info("flushAllDataToDisk completed");
UserThread.execute(completeHandler::handleResult);
}
FLUSH_ALL_DATA_TO_DISK_CALLED = true;

log.info("Start flushAllDataToDisk at shutdown");
AtomicInteger openInstances = new AtomicInteger(ALL_PERSISTENCE_MANAGERS.size());

if (openInstances.get() == 0) {
log.info("No PersistenceManager instances have been created yet.");
completeHandler.handleResult();
}

new HashSet<>(ALL_PERSISTENCE_MANAGERS.values()).forEach(persistenceManager -> {
// For Priority.HIGH data we want to write to disk in any case to be on the safe side if we might have missed
// a requestPersistence call after an important state update. Those are usually rather small data stores.
// Otherwise we only persist if requestPersistence was called since the last persist call.
if (persistenceManager.source.flushAtShutDown || persistenceManager.persistenceRequested) {
// We don't know from which thread we are called so we map back to user thread when calling persistNow
UserThread.execute(() -> {
new HashSet<>(ALL_PERSISTENCE_MANAGERS.values()).forEach(persistenceManager -> {
// For Priority.HIGH data we want to write to disk in any case to be on the safe side if we might have missed
// a requestPersistence call after an important state update. Those are usually rather small data stores.
// Otherwise we only persist if requestPersistence was called since the last persist call.
if (persistenceManager.source.flushAtShutDown || persistenceManager.persistenceRequested) {
// We always get our completeHandler called even if exceptions happen. In case a file write fails
// we still call our shutdown and count down routine as the completeHandler is triggered in any case.

// We get our result handler called from the write thread so we map back to user thread.
persistenceManager.persistNow(() ->
onWriteCompleted(completeHandler, openInstances, persistenceManager));
});
} else {
onWriteCompleted(completeHandler, openInstances, persistenceManager);
}
UserThread.execute(() -> onWriteCompleted(completeHandler, openInstances, persistenceManager)));
} else {
onWriteCompleted(completeHandler, openInstances, persistenceManager);
}
});
});
}

// We get called always from user thread here.
private static void onWriteCompleted(ResultHandler completeHandler,
AtomicInteger openInstances,
PersistenceManager<?> persistenceManager) {
persistenceManager.shutdown();
if (openInstances.decrementAndGet() == 0) {
log.info("flushAllDataToDisk completed");
UserThread.execute(completeHandler::handleResult);
completeHandler.handleResult();
}

}


Expand All @@ -126,25 +143,25 @@ private static void onWriteCompleted(ResultHandler completeHandler,

public enum Source {
// For data stores we received from the network and which could be rebuilt. We store only for avoiding too much network traffic.
NETWORK(1, TimeUnit.HOURS.toSeconds(1), false),
NETWORK(1, TimeUnit.MINUTES.toMillis(5), false),

// For data stores which are created from private local data. This data could only be rebuilt from backup files.
PRIVATE(10, TimeUnit.SECONDS.toSeconds(30), true),
PRIVATE(10, 200, true),

// For data stores which are created from private local data. Loss of that data would not have any critical consequences.
PRIVATE_LOW_PRIO(4, TimeUnit.HOURS.toSeconds(2), false);
// For data stores which are created from private local data. Loss of that data would not have critical consequences.
PRIVATE_LOW_PRIO(4, TimeUnit.MINUTES.toMillis(1), false);


@Getter
private final int numMaxBackupFiles;
@Getter
private final long delayInSec;
private final long delay;
@Getter
private final boolean flushAtShutDown;

Source(int numMaxBackupFiles, long delayInSec, boolean flushAtShutDown) {
Source(int numMaxBackupFiles, long delay, boolean flushAtShutDown) {
this.numMaxBackupFiles = numMaxBackupFiles;
this.delayInSec = delayInSec;
this.delay = delay;
this.flushAtShutDown = flushAtShutDown;
}
}
Expand All @@ -166,6 +183,7 @@ public enum Source {
@Nullable
private Timer timer;
private ExecutorService writeToDiskExecutor;
public final AtomicBoolean initCalled = new AtomicBoolean(false);


///////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -190,6 +208,29 @@ public void initialize(T persistable, Source source) {
}

public void initialize(T persistable, String fileName, Source source) {
if (FLUSH_ALL_DATA_TO_DISK_CALLED) {
log.warn("We have started the shut down routine already. We ignore that initialize call.");
return;
}

if (ALL_PERSISTENCE_MANAGERS.containsKey(fileName)) {
RuntimeException runtimeException = new RuntimeException("We must not create multiple " +
"PersistenceManager instances for file " + fileName + ".");
// We want to get logged from where we have been called so lets print the stack trace.
runtimeException.printStackTrace();
throw runtimeException;
}

if (initCalled.get()) {
RuntimeException runtimeException = new RuntimeException("We must not call initialize multiple times. " +
"PersistenceManager for file: " + fileName + ".");
// We want to get logged from where we have been called so lets print the stack trace.
runtimeException.printStackTrace();
throw runtimeException;
}

initCalled.set(true);

this.persistable = persistable;
this.fileName = fileName;
this.source = source;
Expand Down Expand Up @@ -233,6 +274,11 @@ public void readPersisted(Consumer<T> resultHandler, Runnable orElse) {
* @param orElse Called if no file exists or reading of file failed.
*/
public void readPersisted(String fileName, Consumer<T> resultHandler, Runnable orElse) {
if (FLUSH_ALL_DATA_TO_DISK_CALLED) {
log.warn("We have started the shut down routine already. We ignore that readPersisted call.");
return;
}

new Thread(() -> {
T persisted = getPersisted(fileName);
if (persisted != null) {
Expand All @@ -252,6 +298,11 @@ public T getPersisted() {

@Nullable
public T getPersisted(String fileName) {
if (FLUSH_ALL_DATA_TO_DISK_CALLED) {
log.warn("We have started the shut down routine already. We ignore that getPersisted call.");
return null;
}

File storageFile = new File(dir, fileName);
if (!storageFile.exists()) {
return null;
Expand Down Expand Up @@ -288,6 +339,11 @@ public T getPersisted(String fileName) {
///////////////////////////////////////////////////////////////////////////////////////////

public void requestPersistence() {
if (FLUSH_ALL_DATA_TO_DISK_CALLED) {
log.warn("We have started the shut down routine already. We ignore that requestPersistence call.");
return;
}

persistenceRequested = true;

// We write to disk with a delay to avoid frequent write operations. Depending on the priority those delays
Expand All @@ -296,7 +352,7 @@ public void requestPersistence() {
timer = UserThread.runAfter(() -> {
persistNow(null);
UserThread.execute(() -> timer = null);
}, source.delayInSec, TimeUnit.SECONDS);
}, source.delay, TimeUnit.MILLISECONDS);
}
}

Expand Down Expand Up @@ -398,7 +454,7 @@ public String toString() {
",\n dir=" + dir +
",\n storageFile=" + storageFile +
",\n persistable=" + persistable +
",\n priority=" + source +
",\n source=" + source +
",\n usedTempFilePath=" + usedTempFilePath +
",\n persistenceRequested=" + persistenceRequested +
"\n}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public boolean sendPrivateNotificationMessageIfKeyIsValid(PrivateNotificationPay
}

public void removePrivateNotification() {
p2PService.removeEntryFromMailbox(decryptedMessageWithPubKey);
p2PService.removeMailboxMsg(decryptedMessageWithPubKey);
}

private boolean isKeyValid(String privKeyString) {
Expand Down
59 changes: 42 additions & 17 deletions core/src/main/java/bisq/core/app/BisqExecutable.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
System.exit(EXIT_SUCCESS);
});
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();
UserThread.runAfter(() -> System.exit(EXIT_SUCCESS), 1);
});
} else {
UserThread.runAfter(() -> System.exit(EXIT_SUCCESS), 1);
}
});
});
walletsSetup.shutDown();
Expand All @@ -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();
System.exit(EXIT_SUCCESS);
});
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();
UserThread.runAfter(() -> System.exit(EXIT_SUCCESS), 1);
});
} else {
UserThread.runAfter(() -> System.exit(EXIT_SUCCESS), 1);
}

}, 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();
System.exit(EXIT_FAILURE);
});
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();
UserThread.runAfter(() -> System.exit(EXIT_FAILURE), 1);
});
} else {
UserThread.runAfter(() -> System.exit(EXIT_FAILURE), 1);
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/bisq/core/app/BisqHeadlessApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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"));
Expand Down
Loading

0 comments on commit 3cc9e63

Please sign in to comment.