Merge pull request #4971 from chimp1984/use-resource-file-instead-of-resync-from-genesis

Avoid resync from genesis in case of dao state issues
This commit is contained in:
sqrrm 2020-12-21 22:00:42 +01:00 committed by GitHub
commit c5218c66c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 71 additions and 26 deletions

View file

@ -97,6 +97,12 @@ public class BisqHeadlessApp implements HeadlessApp {
bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler")); bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler"));
bisqSetup.setDownGradePreventionHandler(lastVersion -> log.info("Downgrade from version {} to version {} is not supported", bisqSetup.setDownGradePreventionHandler(lastVersion -> log.info("Downgrade from version {} to version {} is not supported",
lastVersion, Version.VERSION)); 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)); corruptedStorageFileHandler.getFiles().ifPresent(files -> log.warn("getCorruptedDatabaseFiles. files={}", files));
tradeManager.setTakeOfferRequestErrorMessageHandler(errorMessage -> log.error("onTakeOfferRequestErrorMessageHandler")); tradeManager.setTakeOfferRequestErrorMessageHandler(errorMessage -> log.error("onTakeOfferRequestErrorMessageHandler"));

View file

@ -182,6 +182,9 @@ public class BisqSetup {
private Runnable qubesOSInfoHandler; private Runnable qubesOSInfoHandler;
@Setter @Setter
@Nullable @Nullable
private Runnable daoRequiresRestartHandler;
@Setter
@Nullable
private Consumer<String> downGradePreventionHandler; private Consumer<String> downGradePreventionHandler;
@Getter @Getter
@ -443,7 +446,8 @@ public class BisqSetup {
daoWarnMessageHandler, daoWarnMessageHandler,
filterWarningHandler, filterWarningHandler,
voteResultExceptionHandler, voteResultExceptionHandler,
revolutAccountsUpdateHandler); revolutAccountsUpdateHandler,
daoRequiresRestartHandler);
if (walletsSetup.downloadPercentageProperty().get() == 1) { if (walletsSetup.downloadPercentageProperty().get() == 1) {
checkForLockedUpFunds(); checkForLockedUpFunds();

View file

@ -25,6 +25,7 @@ import bisq.core.btc.Balances;
import bisq.core.dao.DaoSetup; import bisq.core.dao.DaoSetup;
import bisq.core.dao.governance.voteresult.VoteResultException; import bisq.core.dao.governance.voteresult.VoteResultException;
import bisq.core.dao.governance.voteresult.VoteResultService; import bisq.core.dao.governance.voteresult.VoteResultService;
import bisq.core.dao.state.DaoStateSnapshotService;
import bisq.core.filter.FilterManager; import bisq.core.filter.FilterManager;
import bisq.core.notifications.MobileNotificationService; import bisq.core.notifications.MobileNotificationService;
import bisq.core.notifications.alerts.DisputeMsgEvents; import bisq.core.notifications.alerts.DisputeMsgEvents;
@ -104,6 +105,7 @@ public class DomainInitialisation {
private final PriceAlert priceAlert; private final PriceAlert priceAlert;
private final MarketAlerts marketAlerts; private final MarketAlerts marketAlerts;
private final User user; private final User user;
private final DaoStateSnapshotService daoStateSnapshotService;
@Inject @Inject
public DomainInitialisation(ClockWatcher clockWatcher, public DomainInitialisation(ClockWatcher clockWatcher,
@ -138,7 +140,8 @@ public class DomainInitialisation {
DisputeMsgEvents disputeMsgEvents, DisputeMsgEvents disputeMsgEvents,
PriceAlert priceAlert, PriceAlert priceAlert,
MarketAlerts marketAlerts, MarketAlerts marketAlerts,
User user) { User user,
DaoStateSnapshotService daoStateSnapshotService) {
this.clockWatcher = clockWatcher; this.clockWatcher = clockWatcher;
this.tradeLimits = tradeLimits; this.tradeLimits = tradeLimits;
this.arbitrationManager = arbitrationManager; this.arbitrationManager = arbitrationManager;
@ -172,6 +175,7 @@ public class DomainInitialisation {
this.priceAlert = priceAlert; this.priceAlert = priceAlert;
this.marketAlerts = marketAlerts; this.marketAlerts = marketAlerts;
this.user = user; this.user = user;
this.daoStateSnapshotService = daoStateSnapshotService;
} }
public void initDomainServices(Consumer<String> rejectedTxErrorMessageHandler, public void initDomainServices(Consumer<String> rejectedTxErrorMessageHandler,
@ -180,7 +184,8 @@ public class DomainInitialisation {
Consumer<String> daoWarnMessageHandler, Consumer<String> daoWarnMessageHandler,
Consumer<String> filterWarningHandler, Consumer<String> filterWarningHandler,
Consumer<VoteResultException> voteResultExceptionHandler, Consumer<VoteResultException> voteResultExceptionHandler,
Consumer<List<RevolutAccount>> revolutAccountsUpdateHandler) { Consumer<List<RevolutAccount>> revolutAccountsUpdateHandler,
Runnable daoRequiresRestartHandler) {
clockWatcher.start(); clockWatcher.start();
tradeLimits.onAllServicesInitialized(); tradeLimits.onAllServicesInitialized();
@ -222,6 +227,8 @@ public class DomainInitialisation {
if (daoWarnMessageHandler != null) if (daoWarnMessageHandler != null)
daoWarnMessageHandler.accept(warningMessage); daoWarnMessageHandler.accept(warningMessage);
}); });
daoStateSnapshotService.setDaoRequiresRestartHandler(daoRequiresRestartHandler);
} }
tradeStatisticsManager.onAllServicesInitialized(); tradeStatisticsManager.onAllServicesInitialized();

View file

@ -17,21 +17,29 @@
package bisq.core.dao.state; package bisq.core.dao.state;
import bisq.core.dao.governance.period.CycleService;
import bisq.core.dao.monitoring.DaoStateMonitoringService; import bisq.core.dao.monitoring.DaoStateMonitoringService;
import bisq.core.dao.monitoring.model.DaoStateHash; import bisq.core.dao.monitoring.model.DaoStateHash;
import bisq.core.dao.state.model.DaoState; import bisq.core.dao.state.model.DaoState;
import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.storage.DaoStateStorageService; import bisq.core.dao.state.storage.DaoStateStorageService;
import bisq.common.config.Config;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
/** /**
* Manages periodical snapshots of the DaoState. * Manages periodical snapshots of the DaoState.
* At startup we apply a snapshot if available. * At startup we apply a snapshot if available.
@ -45,13 +53,16 @@ public class DaoStateSnapshotService {
private final DaoStateService daoStateService; private final DaoStateService daoStateService;
private final GenesisTxInfo genesisTxInfo; private final GenesisTxInfo genesisTxInfo;
private final CycleService cycleService;
private final DaoStateStorageService daoStateStorageService; private final DaoStateStorageService daoStateStorageService;
private final DaoStateMonitoringService daoStateMonitoringService; private final DaoStateMonitoringService daoStateMonitoringService;
private final File storageDir;
private DaoState daoStateSnapshotCandidate; private DaoState daoStateSnapshotCandidate;
private LinkedList<DaoStateHash> daoStateHashChainSnapshotCandidate = new LinkedList<>(); private LinkedList<DaoStateHash> daoStateHashChainSnapshotCandidate = new LinkedList<>();
private int chainHeightOfLastApplySnapshot; private int chainHeightOfLastApplySnapshot;
@Setter
@Nullable
private Runnable daoRequiresRestartHandler;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -61,14 +72,14 @@ public class DaoStateSnapshotService {
@Inject @Inject
public DaoStateSnapshotService(DaoStateService daoStateService, public DaoStateSnapshotService(DaoStateService daoStateService,
GenesisTxInfo genesisTxInfo, GenesisTxInfo genesisTxInfo,
CycleService cycleService,
DaoStateStorageService daoStateStorageService, DaoStateStorageService daoStateStorageService,
DaoStateMonitoringService daoStateMonitoringService) { DaoStateMonitoringService daoStateMonitoringService,
@Named(Config.STORAGE_DIR) File storageDir) {
this.daoStateService = daoStateService; this.daoStateService = daoStateService;
this.genesisTxInfo = genesisTxInfo; this.genesisTxInfo = genesisTxInfo;
this.cycleService = cycleService;
this.daoStateStorageService = daoStateStorageService; this.daoStateStorageService = daoStateStorageService;
this.daoStateMonitoringService = daoStateMonitoringService; this.daoStateMonitoringService = daoStateMonitoringService;
this.storageDir = storageDir;
} }
@ -128,15 +139,19 @@ public class DaoStateSnapshotService {
} else { } else {
// The reorg might have been caused by the previous parsing which might contains a range of // The reorg might have been caused by the previous parsing which might contains a range of
// blocks. // blocks.
log.warn("We applied already a snapshot with chainHeight {}. We will reset the daoState and " + log.warn("We applied already a snapshot with chainHeight {}. " +
"start over from the genesis transaction again.", chainHeightOfLastApplySnapshot); "We remove all dao store files and shutdown. After a restart resource files will " +
applyEmptySnapshot(); "be applied if available.",
chainHeightOfLastApplySnapshot);
resyncDaoStateFromResources();
} }
} }
} else if (fromReorg) { } 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 " + log.info("We got a reorg and we want to apply the snapshot but it is empty. " +
"first snapshot has been created. We use our applySnapshot method and restart from the genesis tx"); "That is expected in the first blocks until the first snapshot has been created. " +
applyEmptySnapshot(); "We remove all dao store files and shutdown. " +
"After a restart resource files will be applied if available.");
resyncDaoStateFromResources();
} }
} else { } else {
log.info("Try to apply snapshot but no stored snapshot available. That is expected at first blocks."); log.info("Try to apply snapshot but no stored snapshot available. That is expected at first blocks.");
@ -152,16 +167,17 @@ public class DaoStateSnapshotService {
return heightOfLastBlock >= genesisTxInfo.getGenesisBlockHeight(); return heightOfLastBlock >= genesisTxInfo.getGenesisBlockHeight();
} }
private void applyEmptySnapshot() { private void resyncDaoStateFromResources() {
DaoState emptyDaoState = new DaoState(); log.info("resyncDaoStateFromResources called");
int genesisBlockHeight = genesisTxInfo.getGenesisBlockHeight(); try {
emptyDaoState.setChainHeight(genesisBlockHeight); daoStateStorageService.resyncDaoStateFromResources(storageDir);
chainHeightOfLastApplySnapshot = genesisBlockHeight;
daoStateService.applySnapshot(emptyDaoState);
// In case we apply an empty snapshot we need to trigger the cycleService.addFirstCycle method
cycleService.addFirstCycle();
daoStateMonitoringService.applySnapshot(new LinkedList<>()); if (daoRequiresRestartHandler != null) {
daoRequiresRestartHandler.run();
}
} catch (IOException e) {
log.error("Error at resyncDaoStateFromResources: {}", e.toString());
}
} }
@VisibleForTesting @VisibleForTesting

View file

@ -2856,6 +2856,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\ 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]. 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.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! popup.privateNotification.headline=Important private notification!

View file

@ -17,7 +17,6 @@
package bisq.core.dao.state; package bisq.core.dao.state;
import bisq.core.dao.governance.period.CycleService;
import bisq.core.dao.monitoring.DaoStateMonitoringService; import bisq.core.dao.monitoring.DaoStateMonitoringService;
import bisq.core.dao.state.storage.DaoStateStorageService; import bisq.core.dao.state.storage.DaoStateStorageService;
@ -37,9 +36,9 @@ public class DaoStateSnapshotServiceTest {
public void setup() { public void setup() {
daoStateSnapshotService = new DaoStateSnapshotService(mock(DaoStateService.class), daoStateSnapshotService = new DaoStateSnapshotService(mock(DaoStateService.class),
mock(GenesisTxInfo.class), mock(GenesisTxInfo.class),
mock(CycleService.class),
mock(DaoStateStorageService.class), mock(DaoStateStorageService.class),
mock(DaoStateMonitoringService.class)); mock(DaoStateMonitoringService.class),
null);
} }
@Test @Test

View file

@ -417,6 +417,11 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
.show(); .show();
}); });
bisqSetup.setDaoRequiresRestartHandler(() -> new Popup().warning("popup.warn.daoRequiresRestart")
.useShutDownButton()
.hideCloseButton()
.show());
corruptedStorageFileHandler.getFiles().ifPresent(files -> new Popup() corruptedStorageFileHandler.getFiles().ifPresent(files -> new Popup()
.warning(Res.get("popup.warning.incompatibleDB", files.toString(), config.appDataDir)) .warning(Res.get("popup.warning.incompatibleDB", files.toString(), config.appDataDir))
.useShutDownButton() .useShutDownButton()

View file

@ -20,6 +20,7 @@ package bisq.seednode;
import bisq.core.app.TorSetup; import bisq.core.app.TorSetup;
import bisq.core.app.misc.ExecutableForAppWithP2p; import bisq.core.app.misc.ExecutableForAppWithP2p;
import bisq.core.app.misc.ModuleForAppWithP2p; import bisq.core.app.misc.ModuleForAppWithP2p;
import bisq.core.dao.state.DaoStateSnapshotService;
import bisq.network.p2p.P2PService; import bisq.network.p2p.P2PService;
import bisq.network.p2p.P2PServiceListener; import bisq.network.p2p.P2PServiceListener;
@ -30,6 +31,7 @@ import bisq.common.UserThread;
import bisq.common.app.AppModule; import bisq.common.app.AppModule;
import bisq.common.app.Capabilities; import bisq.common.app.Capabilities;
import bisq.common.app.Capability; import bisq.common.app.Capability;
import bisq.common.app.DevEnv;
import bisq.common.config.BaseCurrencyNetwork; import bisq.common.config.BaseCurrencyNetwork;
import bisq.common.config.Config; import bisq.common.config.Config;
import bisq.common.handlers.ResultHandler; import bisq.common.handlers.ResultHandler;
@ -98,6 +100,11 @@ public class SeedNodeMain extends ExecutableForAppWithP2p {
super.applyInjector(); super.applyInjector();
seedNode.setInjector(injector); seedNode.setInjector(injector);
if (DevEnv.isDaoActivated()) {
injector.getInstance(DaoStateSnapshotService.class).setDaoRequiresRestartHandler(() -> gracefulShutDown(() -> {
}));
}
} }
@Override @Override