Skip to content

Commit

Permalink
Merge pull request #2 from sqrrm/checkpointing
Browse files Browse the repository at this point in the history
Add checkpoint at block 586920
  • Loading branch information
ripcurlx authored Jul 29, 2019
2 parents d498e43 + 50f3504 commit 366314e
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@

package bisq.core.dao.monitoring;

import bisq.core.app.AppOptionKeys;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.monitoring.model.DaoStateBlock;
import bisq.core.dao.monitoring.model.DaoStateHash;
import bisq.core.dao.monitoring.model.UtxoMismatch;
import bisq.core.dao.monitoring.network.Checkpoint;
import bisq.core.dao.monitoring.network.DaoStateNetworkService;
import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesRequest;
import bisq.core.dao.monitoring.network.messages.NewDaoStateHashMessage;
Expand All @@ -37,14 +39,21 @@

import bisq.common.UserThread;
import bisq.common.crypto.Hash;
import bisq.common.storage.FileManager;
import bisq.common.storage.Storage;
import bisq.common.util.Utilities;

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

import org.apache.commons.lang3.ArrayUtils;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.io.File;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -80,6 +89,8 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe

public interface Listener {
void onChangeAfterBatchProcessing();

void onCheckpointFail();
}

private final DaoStateService daoStateService;
Expand All @@ -101,6 +112,13 @@ public interface Listener {
@Getter
private ObservableList<UtxoMismatch> utxoMismatches = FXCollections.observableArrayList();

private List<Checkpoint> checkpoints = Arrays.asList(
new Checkpoint(586920, Utilities.decodeFromHex("523aaad4e760f6ac6196fec1b3ec9a2f42e5b272"))
);
private boolean checkpointFailed;
private boolean ignoreDevMsg;

private final File storageDir;

///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
Expand All @@ -110,10 +128,14 @@ public interface Listener {
public DaoStateMonitoringService(DaoStateService daoStateService,
DaoStateNetworkService daoStateNetworkService,
GenesisTxInfo genesisTxInfo,
SeedNodeRepository seedNodeRepository) {
SeedNodeRepository seedNodeRepository,
@Named(Storage.STORAGE_DIR) File storageDir,
@Named(AppOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
this.daoStateService = daoStateService;
this.daoStateNetworkService = daoStateNetworkService;
this.genesisTxInfo = genesisTxInfo;
this.storageDir = storageDir;
this.ignoreDevMsg = ignoreDevMsg;
seedNodeAddresses = seedNodeRepository.getSeedNodeAddresses().stream()
.map(NodeAddress::getFullAddress)
.collect(Collectors.toSet());
Expand Down Expand Up @@ -150,6 +172,10 @@ public void onParseBlockChainComplete() {
// We wait for processing messages until we have completed batch processing
int fromHeight = daoStateService.getChainHeight() - 10;
daoStateNetworkService.requestHashesFromAllConnectedSeedNodes(fromHeight);

if (!ignoreDevMsg) {
verifyCheckpoints();
}
}

@Override
Expand All @@ -167,6 +193,7 @@ public void onDaoStateChanged(Block block) {
}
}


///////////////////////////////////////////////////////////////////////////////////////////
// StateNetworkService.Listener
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -291,7 +318,8 @@ private void updateHashChain(Block block) {
}
}

private boolean processPeersDaoStateHash(DaoStateHash daoStateHash, Optional<NodeAddress> peersNodeAddress, boolean notifyListeners) {
private boolean processPeersDaoStateHash(DaoStateHash daoStateHash, Optional<NodeAddress> peersNodeAddress,
boolean notifyListeners) {
AtomicBoolean changed = new AtomicBoolean(false);
AtomicBoolean inConflictWithNonSeedNode = new AtomicBoolean(this.isInConflictWithNonSeedNode);
AtomicBoolean inConflictWithSeedNode = new AtomicBoolean(this.isInConflictWithSeedNode);
Expand Down Expand Up @@ -338,4 +366,49 @@ else if (this.isInConflictWithNonSeedNode)

return changed.get();
}

private void verifyCheckpoints() {
// Checkpoint
checkpoints.forEach(checkpoint -> daoStateHashChain.stream()
.filter(daoStateHash -> daoStateHash.getHeight() == checkpoint.getHeight())
.findAny()
.ifPresent(daoStateHash -> {
if (Arrays.equals(daoStateHash.getHash(), checkpoint.getHash())) {
log.info("Passed checkpoint {}", checkpoint.toString());
} else {
if (checkpointFailed) {
return;
}
checkpointFailed = true;
try {
// Delete state and stop
removeFile("DaoStateStore");
removeFile("BlindVoteStore");
removeFile("ProposalStore");
removeFile("TempProposalStore");

listeners.forEach(Listener::onCheckpointFail);
log.error("Failed checkpoint {}", checkpoint.toString());
} catch (Throwable t) {
t.printStackTrace();
log.error(t.toString());
}
}
}));
}

private void removeFile(String storeName) {
long currentTime = System.currentTimeMillis();
String newFileName = storeName + "_" + currentTime;
String backupDirName = "out_of_sync_dao_data";
File corrupted = new File(storageDir, storeName);
try {
if (corrupted.exists()) {
FileManager.removeAndBackupFile(storageDir, corrupted, newFileName, backupDirName);
}
} catch (Throwable t) {
t.printStackTrace();
log.error(t.toString());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.core.dao.monitoring.network;

import bisq.common.util.Utilities;

import lombok.Getter;
import lombok.Setter;

@Getter
public class Checkpoint {
final int height;
final byte[] hash;
@Setter
boolean passed;

public Checkpoint(int height, byte[] hash) {
this.height = height;
this.hash = hash;
}

@Override
public String toString() {
return "Checkpoint {" +
"\n height=" + height +
",\n hash=" + Utilities.bytesAsHexString(hash) +
"\n}";
}

}
2 changes: 2 additions & 0 deletions core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1971,6 +1971,8 @@ dao.monitor.daoState.utxoConflicts=UTXO conflicts
dao.monitor.daoState.utxoConflicts.blockHeight=Block height: {0}
dao.monitor.daoState.utxoConflicts.sumUtxo=Sum of all UTXO: {0} BSQ
dao.monitor.daoState.utxoConflicts.sumBsq=Sum of all BSQ: {0} BSQ
dao.monitor.daoState.checkpoint.popup=DAO state is not in sync with the network. \
After restart the DAO state will resync.

dao.monitor.proposal.headline=Proposals state
dao.monitor.proposal.table.headline=Chain of proposal state hashes
Expand Down
32 changes: 30 additions & 2 deletions desktop/src/main/java/bisq/desktop/main/MainView.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import bisq.desktop.main.settings.SettingsView;
import bisq.desktop.util.Transitions;

import bisq.core.dao.monitoring.DaoStateMonitoringService;
import bisq.core.exceptions.BisqException;
import bisq.core.locale.GlobalSettings;
import bisq.core.locale.Res;
Expand Down Expand Up @@ -104,7 +105,8 @@

@FxmlView
@Slf4j
public class MainView extends InitializableView<StackPane, MainViewModel> {
public class MainView extends InitializableView<StackPane, MainViewModel>
implements DaoStateMonitoringService.Listener {
// If after 30 sec we have not got connected we show "open network settings" button
private final static int SHOW_TOR_SETTINGS_DELAY_SEC = 90;
private Label versionLabel;
Expand Down Expand Up @@ -150,18 +152,21 @@ public static void removeEffect() {
private ProgressBar btcSyncIndicator, p2pNetworkProgressBar;
private Label btcSplashInfo;
private Popup<?> p2PNetworkWarnMsgPopup, btcNetworkWarnMsgPopup;
private final DaoStateMonitoringService daoStateMonitoringService;

@Inject
public MainView(MainViewModel model,
CachingViewLoader viewLoader,
Navigation navigation,
Transitions transitions,
BSFormatter formatter) {
BSFormatter formatter,
DaoStateMonitoringService daoStateMonitoringService) {
super(model);
this.viewLoader = viewLoader;
this.navigation = navigation;
this.formatter = formatter;
MainView.transitions = transitions;
this.daoStateMonitoringService = daoStateMonitoringService;
}

@Override
Expand Down Expand Up @@ -387,10 +392,33 @@ protected Tooltip computeValue() {
}
});

daoStateMonitoringService.addListener(this);

// Delay a bit to give time for rendering the splash screen
UserThread.execute(() -> onUiReadyHandler.run());
}

///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateMonitoringService.Listener
///////////////////////////////////////////////////////////////////////////////////////////

@Override
public void onChangeAfterBatchProcessing() {
}

@Override
public void onCheckpointFail() {
new Popup<>().attention(Res.get("dao.monitor.daoState.checkpoint.popup"))
.useShutDownButton()
.hideCloseButton()
.show();
}


///////////////////////////////////////////////////////////////////////////////////////////
// Helpers
///////////////////////////////////////////////////////////////////////////////////////////

@NotNull
private Separator getNavigationSeparator() {
final Separator separator = new Separator(Orientation.VERTICAL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public void onChangeAfterBatchProcessing() {
}
}

@Override
public void onCheckpointFail() {
}

///////////////////////////////////////////////////////////////////////////////////////////
// Implementation abstract methods
Expand Down

0 comments on commit 366314e

Please sign in to comment.