Add resync from resources button

We add a second button (displayed as first) to the preferences UI for
resync from latest resources. We add a warning to the resync from
genesis as it takes very long time and causes heavy network load for
seeds.
The resync button at the stateMonitor does now a resync from resources,
not from genesis.
We add handling of the UnconfirmedBsqChangeOutputList file as well.
In Filemanager we add a check if file exists.
This commit is contained in:
chimp1984 2020-04-28 15:18:11 -05:00 committed by Christoph Atteneder
parent bc7f570a18
commit f7dfef253a
No known key found for this signature in database
GPG Key ID: CD5DC1C529CDFD3B
6 changed files with 101 additions and 49 deletions

View File

@ -165,8 +165,10 @@ public class FileManager<T extends PersistableEnvelope> {
log.warn("make dir failed"); log.warn("make dir failed");
File corruptedFile = new File(Paths.get(dbDir.getAbsolutePath(), backupFolderName, fileName).toString()); File corruptedFile = new File(Paths.get(dbDir.getAbsolutePath(), backupFolderName, fileName).toString());
if (storageFile.exists()) {
FileUtil.renameFile(storageFile, corruptedFile); FileUtil.renameFile(storageFile, corruptedFile);
} }
}
synchronized void removeAndBackupFile(String fileName) throws IOException { synchronized void removeAndBackupFile(String fileName) throws IOException {
removeAndBackupFile(dir, storageFile, fileName, "backup_of_corrupted_data"); removeAndBackupFile(dir, storageFile, fileName, "backup_of_corrupted_data");

View File

@ -91,6 +91,7 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.FilteredList;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -517,7 +518,10 @@ public class DaoFacade implements DaoSetupService {
lockupTxService.publishLockupTx(lockupAmount, lockTime, lockupReason, hash, resultHandler, exceptionHandler); lockupTxService.publishLockupTx(lockupAmount, lockTime, lockupReason, hash, resultHandler, exceptionHandler);
} }
public Tuple2<Coin, Integer> getLockupTxMiningFeeAndTxSize(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash) public Tuple2<Coin, Integer> getLockupTxMiningFeeAndTxSize(Coin lockupAmount,
int lockTime,
LockupReason lockupReason,
byte[] hash)
throws InsufficientMoneyException, IOException, TransactionVerificationException, WalletException { throws InsufficientMoneyException, IOException, TransactionVerificationException, WalletException {
return lockupTxService.getMiningFeeAndTxSize(lockupAmount, lockTime, lockupReason, hash); return lockupTxService.getMiningFeeAndTxSize(lockupAmount, lockTime, lockupReason, hash);
} }
@ -700,8 +704,12 @@ public class DaoFacade implements DaoSetupService {
return daoStateService.getParamValue(param, blockHeight); return daoStateService.getParamValue(param, blockHeight);
} }
public void resyncDao(Runnable resultHandler) { public void resyncDaoStateFromGenesis(Runnable resultHandler) {
daoStateStorageService.resetDaoState(resultHandler); daoStateStorageService.resyncDaoStateFromGenesis(resultHandler);
}
public void resyncDaoStateFromResources(File storageDir) throws IOException {
daoStateStorageService.resyncDaoStateFromResources(storageDir);
} }
public boolean isMyRole(Role role) { public boolean isMyRole(Role role) {

View File

@ -26,12 +26,14 @@ import bisq.network.p2p.storage.persistence.StoreService;
import bisq.common.UserThread; import bisq.common.UserThread;
import bisq.common.config.Config; import bisq.common.config.Config;
import bisq.common.storage.FileManager;
import bisq.common.storage.Storage; import bisq.common.storage.Storage;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -101,11 +103,33 @@ public class DaoStateStorageService extends StoreService<DaoStateStore> {
return store.getDaoStateHashChain(); return store.getDaoStateHashChain();
} }
public void resetDaoState(Runnable resultHandler) { public void resyncDaoStateFromGenesis(Runnable resultHandler) {
persist(new DaoState(), new LinkedList<>(), 1); persist(new DaoState(), new LinkedList<>(), 1);
UserThread.runAfter(resultHandler, 300, TimeUnit.MILLISECONDS); UserThread.runAfter(resultHandler, 300, TimeUnit.MILLISECONDS);
} }
public void resyncDaoStateFromResources(File storageDir) throws IOException {
// We delete all DAO consensus payload data and remove the daoState so it will rebuild from latest
// resource files.
long currentTime = System.currentTimeMillis();
String backupDirName = "out_of_sync_dao_data";
String newFileName = "BlindVoteStore_" + currentTime;
FileManager.removeAndBackupFile(storageDir, new File(storageDir, "BlindVoteStore"), newFileName, backupDirName);
newFileName = "ProposalStore_" + currentTime;
FileManager.removeAndBackupFile(storageDir, new File(storageDir, "ProposalStore"), newFileName, backupDirName);
// We also need to remove ballot list as it contains the proposals as well. It will be recreated at resync
newFileName = "BallotList_" + currentTime;
FileManager.removeAndBackupFile(storageDir, new File(storageDir, "BallotList"), newFileName, backupDirName);
newFileName = "UnconfirmedBsqChangeOutputList_" + currentTime;
FileManager.removeAndBackupFile(storageDir, new File(storageDir, "UnconfirmedBsqChangeOutputList"), newFileName, backupDirName);
newFileName = "DaoStateStore_" + currentTime;
FileManager.removeAndBackupFile(storageDir, new File(storageDir, "DaoStateStore"), newFileName, backupDirName);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Protected // Protected

View File

@ -1074,10 +1074,15 @@ settings.preferences.languageChange=To apply the language change to all screens
settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}. settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}.
settings.preferences.selectCurrencyNetwork=Select network settings.preferences.selectCurrencyNetwork=Select network
setting.preferences.daoOptions=DAO options setting.preferences.daoOptions=DAO options
setting.preferences.dao.resync.label=Rebuild DAO state from genesis tx setting.preferences.dao.resyncFromGenesis.label=Rebuild DAO state from genesis tx
setting.preferences.dao.resync.button=Resync setting.preferences.dao.resyncFromResources.label=Rebuild DAO state from resources
setting.preferences.dao.resync.popup=After an application restart the Bisq network governance data will be reloaded from \ setting.preferences.dao.resyncFromResources.popup=After an application restart the Bisq network governance data will be reloaded from \
the seed nodes and the BSQ consensus state will be rebuilt from the latest resource files.
setting.preferences.dao.resyncFromGenesis.popup=A resync from genesis transaction can take considerable time and CPU \
resources. Are you sure you want to do that? Mostly a resync from latest resource files is sufficient and much faster.\n\n\
If you proceed, after an application restart the Bisq network governance data will be reloaded from \
the seed nodes and the BSQ consensus state will be rebuilt from the genesis transaction. the seed nodes and the BSQ consensus state will be rebuilt from the genesis transaction.
setting.preferences.dao.resyncFromGenesis.resync=Resync from genesis and shutdown
setting.preferences.dao.isDaoFullNode=Run Bisq as DAO full node setting.preferences.dao.isDaoFullNode=Run Bisq as DAO full node
setting.preferences.dao.rpcUser=RPC username setting.preferences.dao.rpcUser=RPC username
setting.preferences.dao.rpcPw=RPC password setting.preferences.dao.rpcPw=RPC password

View File

@ -18,7 +18,6 @@
package bisq.desktop.main.dao.monitor; package bisq.desktop.main.dao.monitor;
import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel; import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.AutoTooltipTableColumn;
@ -40,8 +39,6 @@ import bisq.core.locale.Res;
import bisq.network.p2p.NodeAddress; import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.seed.SeedNodeRepository; import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.storage.FileManager;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@ -140,33 +137,7 @@ public abstract class StateMonitorView<StH extends StateHash,
resyncButton.visibleProperty().bind(isInConflictWithSeedNode); resyncButton.visibleProperty().bind(isInConflictWithSeedNode);
resyncButton.managedProperty().bind(isInConflictWithSeedNode); resyncButton.managedProperty().bind(isInConflictWithSeedNode);
resyncButton.setOnAction(ev -> { resyncButton.setOnAction(ev -> resyncDaoState());
try {
// We delete all consensus payload data and reset the daoState so it will rebuild from genesis.
// Deleting the daoState would cause to read the file from the resources and we would not rebuild from
// genesis if a snapshot exist!
long currentTime = System.currentTimeMillis();
String backupDirName = "out_of_sync_dao_data";
String newFileName = "BlindVoteStore_" + currentTime;
FileManager.removeAndBackupFile(storageDir, new File(storageDir, "BlindVoteStore"), newFileName, backupDirName);
newFileName = "ProposalStore_" + currentTime;
FileManager.removeAndBackupFile(storageDir, new File(storageDir, "ProposalStore"), newFileName, backupDirName);
// We also need to remove ballot list as it contains the proposals as well. It will be recreated at resync
newFileName = "BallotList_" + currentTime;
FileManager.removeAndBackupFile(storageDir, new File(storageDir, "BallotList"), newFileName, backupDirName);
daoFacade.resyncDao(() -> new Popup().attention(Res.get("setting.preferences.dao.resync.popup"))
.useShutDownButton()
.hideCloseButton()
.show());
} catch (Throwable t) {
t.printStackTrace();
log.error(t.toString());
new Popup().error(t.toString()).show();
}
});
if (daoStateService.isParseBlockChainComplete()) { if (daoStateService.isParseBlockChainComplete()) {
onDataUpdate(); onDataUpdate();
@ -294,7 +265,9 @@ public abstract class StateMonitorView<StH extends StateHash,
protected void onDataUpdate() { protected void onDataUpdate() {
if (isInConflictWithSeedNode.get()) { if (isInConflictWithSeedNode.get()) {
statusTextField.setText(Res.get("dao.monitor.isInConflictWithSeedNode")); String msg = Res.get("dao.monitor.isInConflictWithSeedNode");
log.warn(msg);
statusTextField.setText(msg);
statusTextField.getStyleClass().add("dao-inConflict"); statusTextField.getStyleClass().add("dao-inConflict");
} else if (isInConflictWithNonSeedNode.get()) { } else if (isInConflictWithNonSeedNode.get()) {
statusTextField.setText(Res.get("dao.monitor.isInConflictWithNonSeedNode")); statusTextField.setText(Res.get("dao.monitor.isInConflictWithNonSeedNode"));
@ -307,6 +280,20 @@ public abstract class StateMonitorView<StH extends StateHash,
GUIUtil.setFitToRowsForTableView(tableView, 25, 28, 2, 5); GUIUtil.setFitToRowsForTableView(tableView, 25, 28, 2, 5);
} }
private void resyncDaoState() {
try {
daoFacade.resyncDaoStateFromResources(storageDir);
new Popup().attention(Res.get("setting.preferences.dao.resyncFromResources.popup"))
.useShutDownButton()
.hideCloseButton()
.show();
} catch (Throwable t) {
t.printStackTrace();
log.error(t.toString());
new Popup().error(t.toString()).show();
}
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// TableColumns // TableColumns

View File

@ -17,6 +17,7 @@
package bisq.desktop.main.settings.preferences; package bisq.desktop.main.settings.preferences;
import bisq.desktop.app.BisqApp;
import bisq.desktop.common.view.ActivatableViewAndModel; import bisq.desktop.common.view.ActivatableViewAndModel;
import bisq.desktop.common.view.FxmlView; import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipButton;
@ -88,6 +89,8 @@ import javafx.collections.ObservableList;
import javafx.util.Callback; import javafx.util.Callback;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -123,12 +126,13 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
private final AssetService assetService; private final AssetService assetService;
private final FilterManager filterManager; private final FilterManager filterManager;
private final DaoFacade daoFacade; private final DaoFacade daoFacade;
private final File storageDir;
private ListView<FiatCurrency> fiatCurrenciesListView; private ListView<FiatCurrency> fiatCurrenciesListView;
private ComboBox<FiatCurrency> fiatCurrenciesComboBox; private ComboBox<FiatCurrency> fiatCurrenciesComboBox;
private ListView<CryptoCurrency> cryptoCurrenciesListView; private ListView<CryptoCurrency> cryptoCurrenciesListView;
private ComboBox<CryptoCurrency> cryptoCurrenciesComboBox; private ComboBox<CryptoCurrency> cryptoCurrenciesComboBox;
private Button resetDontShowAgainButton, resyncDaoButton; private Button resetDontShowAgainButton, resyncDaoFromGenesisButton, resyncDaoFromResourcesButton;
// private ListChangeListener<TradeCurrency> displayCurrenciesListChangeListener; // private ListChangeListener<TradeCurrency> displayCurrenciesListChangeListener;
private ObservableList<BlockChainExplorer> blockExplorers; private ObservableList<BlockChainExplorer> blockExplorers;
private ObservableList<BlockChainExplorer> bsqBlockChainExplorers; private ObservableList<BlockChainExplorer> bsqBlockChainExplorers;
@ -162,13 +166,15 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
Config config, Config config,
@Named(Config.RPC_USER) String rpcUser, @Named(Config.RPC_USER) String rpcUser,
@Named(Config.RPC_PASSWORD) String rpcPassword, @Named(Config.RPC_PASSWORD) String rpcPassword,
@Named(Config.RPC_BLOCK_NOTIFICATION_PORT) int rpcBlockNotificationPort) { @Named(Config.RPC_BLOCK_NOTIFICATION_PORT) int rpcBlockNotificationPort,
@Named(Config.STORAGE_DIR) File storageDir) {
super(model); super(model);
this.preferences = preferences; this.preferences = preferences;
this.feeService = feeService; this.feeService = feeService;
this.assetService = assetService; this.assetService = assetService;
this.filterManager = filterManager; this.filterManager = filterManager;
this.daoFacade = daoFacade; this.daoFacade = daoFacade;
this.storageDir = storageDir;
daoOptionsSet = config.fullDaoNodeOptionSetExplicitly && daoOptionsSet = config.fullDaoNodeOptionSetExplicitly &&
!rpcUser.isEmpty() && !rpcUser.isEmpty() &&
!rpcPassword.isEmpty() && !rpcPassword.isEmpty() &&
@ -600,10 +606,14 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
} }
private void initializeDaoOptions() { private void initializeDaoOptions() {
daoOptionsTitledGroupBg = addTitledGroupBg(root, ++gridRow, 1, Res.get("setting.preferences.daoOptions"), Layout.GROUP_DISTANCE); daoOptionsTitledGroupBg = addTitledGroupBg(root, ++gridRow, 2, Res.get("setting.preferences.daoOptions"), Layout.GROUP_DISTANCE);
resyncDaoButton = addButton(root, gridRow, Res.get("setting.preferences.dao.resync.label"), Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); resyncDaoFromResourcesButton = addButton(root, gridRow, Res.get("setting.preferences.dao.resyncFromResources.label"), Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE);
resyncDaoButton.setMaxWidth(Double.MAX_VALUE); resyncDaoFromResourcesButton.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(resyncDaoButton, Priority.ALWAYS); GridPane.setHgrow(resyncDaoFromResourcesButton, Priority.ALWAYS);
resyncDaoFromGenesisButton = addButton(root, ++gridRow, Res.get("setting.preferences.dao.resyncFromGenesis.label"));
resyncDaoFromGenesisButton.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(resyncDaoFromGenesisButton, Priority.ALWAYS);
isDaoFullNodeToggleButton = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.dao.isDaoFullNode")); isDaoFullNodeToggleButton = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.dao.isDaoFullNode"));
rpcUserTextField = addInputTextField(root, ++gridRow, Res.get("setting.preferences.dao.rpcUser")); rpcUserTextField = addInputTextField(root, ++gridRow, Res.get("setting.preferences.dao.rpcUser"));
@ -865,11 +875,26 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
blockNotifyPortTextField.setText(blockNotifyPort > 0 ? String.valueOf(blockNotifyPort) : ""); blockNotifyPortTextField.setText(blockNotifyPort > 0 ? String.valueOf(blockNotifyPort) : "");
updateDaoFields(); updateDaoFields();
resyncDaoButton.setOnAction(e -> daoFacade.resyncDao(() -> resyncDaoFromResourcesButton.setOnAction(e -> {
new Popup().attention(Res.get("setting.preferences.dao.resync.popup")) try {
daoFacade.resyncDaoStateFromResources(storageDir);
new Popup().attention(Res.get("setting.preferences.dao.resyncFromResources.popup"))
.useShutDownButton() .useShutDownButton()
.hideCloseButton() .hideCloseButton()
.show())); .show();
} catch (Throwable t) {
t.printStackTrace();
log.error(t.toString());
new Popup().error(t.toString()).show();
}
});
resyncDaoFromGenesisButton.setOnAction(e ->
new Popup().attention(Res.get("setting.preferences.dao.resyncFromGenesis.popup"))
.actionButtonText(Res.get("setting.preferences.dao.resyncFromGenesis.resync"))
.onAction(() -> daoFacade.resyncDaoStateFromGenesis(() -> BisqApp.getShutDownHandler().run()))
.closeButtonText(Res.get("shared.cancel"))
.show());
isDaoFullNodeToggleButton.setOnAction(e -> { isDaoFullNodeToggleButton.setOnAction(e -> {
String key = "daoFullModeInfoShown"; String key = "daoFullModeInfoShown";
@ -973,7 +998,8 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
} }
private void deactivateDaoPreferences() { private void deactivateDaoPreferences() {
resyncDaoButton.setOnAction(null); resyncDaoFromResourcesButton.setOnAction(null);
resyncDaoFromGenesisButton.setOnAction(null);
isDaoFullNodeToggleButton.setOnAction(null); isDaoFullNodeToggleButton.setOnAction(null);
rpcUserTextField.textProperty().removeListener(rpcUserListener); rpcUserTextField.textProperty().removeListener(rpcUserListener);
rpcPwTextField.textProperty().removeListener(rpcPwListener); rpcPwTextField.textProperty().removeListener(rpcPwListener);