Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid resync from genesis in case of dao state issues #4971

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core/src/main/java/bisq/core/app/BisqHeadlessApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ protected void setupHandlers() {
bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler"));
bisqSetup.setDownGradePreventionHandler(lastVersion -> log.info("Downgrade from version {} to version {} is not supported",
lastVersion, Version.VERSION));
bisqSetup.setDaoRequiresRestartHandler(() -> {
log.info("There was a problem with synchronizing the DAO state. " +
"A restart of the application is required to fix the issue.");
gracefulShutDownHandler.gracefulShutDown(() -> {
});
});

corruptedStorageFileHandler.getFiles().ifPresent(files -> log.warn("getCorruptedDatabaseFiles. files={}", files));
tradeManager.setTakeOfferRequestErrorMessageHandler(errorMessage -> log.error("onTakeOfferRequestErrorMessageHandler"));
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/java/bisq/core/app/BisqSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ default void onRequestWalletPassword() {
private Runnable qubesOSInfoHandler;
@Setter
@Nullable
private Runnable daoRequiresRestartHandler;
@Setter
@Nullable
private Consumer<String> downGradePreventionHandler;

@Getter
Expand Down Expand Up @@ -443,7 +446,8 @@ private void initDomainServices() {
daoWarnMessageHandler,
filterWarningHandler,
voteResultExceptionHandler,
revolutAccountsUpdateHandler);
revolutAccountsUpdateHandler,
daoRequiresRestartHandler);

if (walletsSetup.downloadPercentageProperty().get() == 1) {
checkForLockedUpFunds();
Expand Down
11 changes: 9 additions & 2 deletions core/src/main/java/bisq/core/app/DomainInitialisation.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import bisq.core.dao.DaoSetup;
import bisq.core.dao.governance.voteresult.VoteResultException;
import bisq.core.dao.governance.voteresult.VoteResultService;
import bisq.core.dao.state.DaoStateSnapshotService;
import bisq.core.filter.FilterManager;
import bisq.core.notifications.MobileNotificationService;
import bisq.core.notifications.alerts.DisputeMsgEvents;
Expand Down Expand Up @@ -104,6 +105,7 @@ public class DomainInitialisation {
private final PriceAlert priceAlert;
private final MarketAlerts marketAlerts;
private final User user;
private final DaoStateSnapshotService daoStateSnapshotService;

@Inject
public DomainInitialisation(ClockWatcher clockWatcher,
Expand Down Expand Up @@ -138,7 +140,8 @@ public DomainInitialisation(ClockWatcher clockWatcher,
DisputeMsgEvents disputeMsgEvents,
PriceAlert priceAlert,
MarketAlerts marketAlerts,
User user) {
User user,
DaoStateSnapshotService daoStateSnapshotService) {
this.clockWatcher = clockWatcher;
this.tradeLimits = tradeLimits;
this.arbitrationManager = arbitrationManager;
Expand Down Expand Up @@ -172,6 +175,7 @@ public DomainInitialisation(ClockWatcher clockWatcher,
this.priceAlert = priceAlert;
this.marketAlerts = marketAlerts;
this.user = user;
this.daoStateSnapshotService = daoStateSnapshotService;
}

public void initDomainServices(Consumer<String> rejectedTxErrorMessageHandler,
Expand All @@ -180,7 +184,8 @@ public void initDomainServices(Consumer<String> rejectedTxErrorMessageHandler,
Consumer<String> daoWarnMessageHandler,
Consumer<String> filterWarningHandler,
Consumer<VoteResultException> voteResultExceptionHandler,
Consumer<List<RevolutAccount>> revolutAccountsUpdateHandler) {
Consumer<List<RevolutAccount>> revolutAccountsUpdateHandler,
Runnable daoRequiresRestartHandler) {
clockWatcher.start();

tradeLimits.onAllServicesInitialized();
Expand Down Expand Up @@ -222,6 +227,8 @@ public void initDomainServices(Consumer<String> rejectedTxErrorMessageHandler,
if (daoWarnMessageHandler != null)
daoWarnMessageHandler.accept(warningMessage);
});

daoStateSnapshotService.setDaoRequiresRestartHandler(daoRequiresRestartHandler);
}

tradeStatisticsManager.onAllServicesInitialized();
Expand Down
56 changes: 36 additions & 20 deletions core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,29 @@

package bisq.core.dao.state;

import bisq.core.dao.governance.period.CycleService;
import bisq.core.dao.monitoring.DaoStateMonitoringService;
import bisq.core.dao.monitoring.model.DaoStateHash;
import bisq.core.dao.state.model.DaoState;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.storage.DaoStateStorageService;

import bisq.common.config.Config;

import javax.inject.Inject;
import javax.inject.Named;

import com.google.common.annotations.VisibleForTesting;

import java.io.File;
import java.io.IOException;

import java.util.LinkedList;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;

/**
* Manages periodical snapshots of the DaoState.
* At startup we apply a snapshot if available.
Expand All @@ -45,13 +53,16 @@ public class DaoStateSnapshotService {

private final DaoStateService daoStateService;
private final GenesisTxInfo genesisTxInfo;
private final CycleService cycleService;
private final DaoStateStorageService daoStateStorageService;
private final DaoStateMonitoringService daoStateMonitoringService;
private final File storageDir;

private DaoState daoStateSnapshotCandidate;
private LinkedList<DaoStateHash> daoStateHashChainSnapshotCandidate = new LinkedList<>();
private int chainHeightOfLastApplySnapshot;
@Setter
@Nullable
private Runnable daoRequiresRestartHandler;


///////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -61,14 +72,14 @@ public class DaoStateSnapshotService {
@Inject
public DaoStateSnapshotService(DaoStateService daoStateService,
GenesisTxInfo genesisTxInfo,
CycleService cycleService,
DaoStateStorageService daoStateStorageService,
DaoStateMonitoringService daoStateMonitoringService) {
DaoStateMonitoringService daoStateMonitoringService,
@Named(Config.STORAGE_DIR) File storageDir) {
this.daoStateService = daoStateService;
this.genesisTxInfo = genesisTxInfo;
this.cycleService = cycleService;
this.daoStateStorageService = daoStateStorageService;
this.daoStateMonitoringService = daoStateMonitoringService;
this.storageDir = storageDir;
}


Expand Down Expand Up @@ -128,15 +139,19 @@ public void applySnapshot(boolean fromReorg) {
} else {
// The reorg might have been caused by the previous parsing which might contains a range of
// blocks.
log.warn("We applied already a snapshot with chainHeight {}. We will reset the daoState and " +
"start over from the genesis transaction again.", chainHeightOfLastApplySnapshot);
applyEmptySnapshot();
log.warn("We applied already a snapshot with chainHeight {}. " +
"We remove all dao store files and shutdown. After a restart resource files will " +
"be applied if available.",
chainHeightOfLastApplySnapshot);
resyncDaoStateFromResources();
}
}
} else if (fromReorg) {
log.info("We got a reorg and we want to apply the snapshot but it is empty. That is expected in the first blocks until the " +
"first snapshot has been created. We use our applySnapshot method and restart from the genesis tx");
applyEmptySnapshot();
log.info("We got a reorg and we want to apply the snapshot but it is empty. " +
"That is expected in the first blocks until the first snapshot has been created. " +
"We remove all dao store files and shutdown. " +
"After a restart resource files will be applied if available.");
resyncDaoStateFromResources();
}
} else {
log.info("Try to apply snapshot but no stored snapshot available. That is expected at first blocks.");
Expand All @@ -152,16 +167,17 @@ private boolean isValidHeight(int heightOfLastBlock) {
return heightOfLastBlock >= genesisTxInfo.getGenesisBlockHeight();
}

private void applyEmptySnapshot() {
DaoState emptyDaoState = new DaoState();
int genesisBlockHeight = genesisTxInfo.getGenesisBlockHeight();
emptyDaoState.setChainHeight(genesisBlockHeight);
chainHeightOfLastApplySnapshot = genesisBlockHeight;
daoStateService.applySnapshot(emptyDaoState);
// In case we apply an empty snapshot we need to trigger the cycleService.addFirstCycle method
cycleService.addFirstCycle();
private void resyncDaoStateFromResources() {
log.info("resyncDaoStateFromResources called");
try {
daoStateStorageService.resyncDaoStateFromResources(storageDir);

daoStateMonitoringService.applySnapshot(new LinkedList<>());
if (daoRequiresRestartHandler != null) {
daoRequiresRestartHandler.run();
}
} catch (IOException e) {
log.error("Error at resyncDaoStateFromResources: {}", e.toString());
}
}

@VisibleForTesting
Expand Down
1 change: 1 addition & 0 deletions core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2850,6 +2850,7 @@ popup.info.shutDownWithOpenOffers=Bisq is being shut down, but there are open of
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.warn.daoRequiresRestart=There was a problem with synchronizing the DAO state. You have to restart the application to fix the issue.

popup.privateNotification.headline=Important private notification!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package bisq.core.dao.state;

import bisq.core.dao.governance.period.CycleService;
import bisq.core.dao.monitoring.DaoStateMonitoringService;
import bisq.core.dao.state.storage.DaoStateStorageService;

Expand All @@ -37,9 +36,9 @@ public class DaoStateSnapshotServiceTest {
public void setup() {
daoStateSnapshotService = new DaoStateSnapshotService(mock(DaoStateService.class),
mock(GenesisTxInfo.class),
mock(CycleService.class),
mock(DaoStateStorageService.class),
mock(DaoStateMonitoringService.class));
mock(DaoStateMonitoringService.class),
null);
}

@Test
Expand Down
5 changes: 5 additions & 0 deletions desktop/src/main/java/bisq/desktop/main/MainViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,11 @@ private void setupHandlers() {
.show();
});

bisqSetup.setDaoRequiresRestartHandler(() -> new Popup().warning("popup.warn.daoRequiresRestart")
.useShutDownButton()
.hideCloseButton()
.show());

corruptedStorageFileHandler.getFiles().ifPresent(files -> new Popup()
.warning(Res.get("popup.warning.incompatibleDB", files.toString(), config.appDataDir))
.useShutDownButton()
Expand Down
7 changes: 7 additions & 0 deletions seednode/src/main/java/bisq/seednode/SeedNodeMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import bisq.core.app.TorSetup;
import bisq.core.app.misc.ExecutableForAppWithP2p;
import bisq.core.app.misc.ModuleForAppWithP2p;
import bisq.core.dao.state.DaoStateSnapshotService;

import bisq.network.p2p.P2PService;
import bisq.network.p2p.P2PServiceListener;
Expand All @@ -30,6 +31,7 @@
import bisq.common.app.AppModule;
import bisq.common.app.Capabilities;
import bisq.common.app.Capability;
import bisq.common.app.DevEnv;
import bisq.common.config.BaseCurrencyNetwork;
import bisq.common.config.Config;
import bisq.common.handlers.ResultHandler;
Expand Down Expand Up @@ -98,6 +100,11 @@ protected void applyInjector() {
super.applyInjector();

seedNode.setInjector(injector);

if (DevEnv.isDaoActivated()) {
injector.getInstance(DaoStateSnapshotService.class).setDaoRequiresRestartHandler(() -> gracefulShutDown(() -> {
}));
}
}

@Override
Expand Down