Merge pull request #2674 from ManfredKarrer/add-more-info-to-dao-monitor

Handle dao state conflicts better
This commit is contained in:
Manfred Karrer 2019-04-09 12:19:41 -05:00 committed by GitHub
commit 2945150e7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 239 additions and 117 deletions

View file

@ -162,16 +162,21 @@ public class FileManager<T extends PersistableEnvelope> {
}
}
public synchronized void removeAndBackupFile(String fileName) throws IOException {
File corruptedBackupDir = new File(Paths.get(dir.getAbsolutePath(), "backup_of_corrupted_data").toString());
public static void removeAndBackupFile(File dbDir, File storageFile, String fileName, String backupFolderName)
throws IOException {
File corruptedBackupDir = new File(Paths.get(dbDir.getAbsolutePath(), backupFolderName).toString());
if (!corruptedBackupDir.exists())
if (!corruptedBackupDir.mkdir())
log.warn("make dir failed");
File corruptedFile = new File(Paths.get(dir.getAbsolutePath(), "backup_of_corrupted_data", fileName).toString());
File corruptedFile = new File(Paths.get(dbDir.getAbsolutePath(), backupFolderName, fileName).toString());
FileUtil.renameFile(storageFile, corruptedFile);
}
public synchronized void removeAndBackupFile(String fileName) throws IOException {
removeAndBackupFile(dir, storageFile, fileName, "backup_of_corrupted_data");
}
public synchronized void backupFile(String fileName, int numMaxBackupFiles) {
FileUtil.rollingBackup(dir, fileName, numMaxBackupFiles);
}

View file

@ -36,6 +36,7 @@ import bisq.core.dao.state.model.governance.DaoPhase;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.UserThread;
import bisq.common.crypto.Hash;
@ -49,6 +50,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@ -81,6 +83,7 @@ public class BlindVoteStateMonitoringService implements DaoSetupService, DaoStat
private final GenesisTxInfo genesisTxInfo;
private final PeriodService periodService;
private final BlindVoteListService blindVoteListService;
private final Set<String> seedNodeAddresses;
@Getter
private final LinkedList<BlindVoteStateBlock> blindVoteStateBlockChain = new LinkedList<>();
@ -88,7 +91,9 @@ public class BlindVoteStateMonitoringService implements DaoSetupService, DaoStat
private final LinkedList<BlindVoteStateHash> blindVoteStateHashChain = new LinkedList<>();
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
@Getter
private boolean isInConflict;
private boolean isInConflictWithNonSeedNode;
@Getter
private boolean isInConflictWithSeedNode;
private boolean parseBlockChainComplete;
@ -101,12 +106,16 @@ public class BlindVoteStateMonitoringService implements DaoSetupService, DaoStat
BlindVoteStateNetworkService blindVoteStateNetworkService,
GenesisTxInfo genesisTxInfo,
PeriodService periodService,
BlindVoteListService blindVoteListService) {
BlindVoteListService blindVoteListService,
SeedNodeRepository seedNodeRepository) {
this.daoStateService = daoStateService;
this.blindVoteStateNetworkService = blindVoteStateNetworkService;
this.genesisTxInfo = genesisTxInfo;
this.periodService = periodService;
this.blindVoteListService = blindVoteListService;
seedNodeAddresses = seedNodeRepository.getSeedNodeAddresses().stream()
.map(NodeAddress::getFullAddress)
.collect(Collectors.toSet());
}
@ -276,7 +285,8 @@ public class BlindVoteStateMonitoringService implements DaoSetupService, DaoStat
private boolean processPeersBlindVoteStateHash(BlindVoteStateHash blindVoteStateHash, Optional<NodeAddress> peersNodeAddress, boolean notifyListeners) {
AtomicBoolean changed = new AtomicBoolean(false);
AtomicBoolean isInConflict = new AtomicBoolean(this.isInConflict);
AtomicBoolean inConflictWithNonSeedNode = new AtomicBoolean(this.isInConflictWithNonSeedNode);
AtomicBoolean inConflictWithSeedNode = new AtomicBoolean(this.isInConflictWithSeedNode);
StringBuilder sb = new StringBuilder();
blindVoteStateBlockChain.stream()
.filter(e -> e.getHeight() == blindVoteStateHash.getHeight()).findAny()
@ -286,7 +296,12 @@ public class BlindVoteStateMonitoringService implements DaoSetupService, DaoStat
daoStateBlock.putInPeersMap(peersNodeAddressAsString, blindVoteStateHash);
if (!daoStateBlock.getMyStateHash().hasEqualHash(blindVoteStateHash)) {
daoStateBlock.putInConflictMap(peersNodeAddressAsString, blindVoteStateHash);
isInConflict.set(true);
if (seedNodeAddresses.contains(peersNodeAddressAsString)) {
inConflictWithSeedNode.set(true);
} else {
inConflictWithNonSeedNode.set(true);
}
sb.append("We received a block hash from peer ")
.append(peersNodeAddressAsString)
.append(" which conflicts with our block hash.\n")
@ -298,11 +313,15 @@ public class BlindVoteStateMonitoringService implements DaoSetupService, DaoStat
changed.set(true);
});
this.isInConflict = isInConflict.get();
this.isInConflictWithNonSeedNode = inConflictWithNonSeedNode.get();
this.isInConflictWithSeedNode = inConflictWithSeedNode.get();
String conflictMsg = sb.toString();
if (this.isInConflict && !conflictMsg.isEmpty()) {
log.warn(conflictMsg);
if (!conflictMsg.isEmpty()) {
if (this.isInConflictWithSeedNode)
log.warn("Conflict with seed nodes: {}", conflictMsg);
else if (this.isInConflictWithNonSeedNode)
log.info("Conflict with non-seed nodes: {}", conflictMsg);
}
if (notifyListeners && changed.get()) {

View file

@ -33,6 +33,7 @@ import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.UserThread;
import bisq.common.crypto.Hash;
@ -48,6 +49,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@ -83,6 +85,8 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe
private final DaoStateService daoStateService;
private final DaoStateNetworkService daoStateNetworkService;
private final GenesisTxInfo genesisTxInfo;
private final Set<String> seedNodeAddresses;
@Getter
private final LinkedList<DaoStateBlock> daoStateBlockChain = new LinkedList<>();
@ -91,7 +95,9 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private boolean parseBlockChainComplete;
@Getter
private boolean isInConflict;
private boolean isInConflictWithNonSeedNode;
@Getter
private boolean isInConflictWithSeedNode;
@Getter
private ObservableList<UtxoMismatch> utxoMismatches = FXCollections.observableArrayList();
@ -103,10 +109,14 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe
@Inject
public DaoStateMonitoringService(DaoStateService daoStateService,
DaoStateNetworkService daoStateNetworkService,
GenesisTxInfo genesisTxInfo) {
GenesisTxInfo genesisTxInfo,
SeedNodeRepository seedNodeRepository) {
this.daoStateService = daoStateService;
this.daoStateNetworkService = daoStateNetworkService;
this.genesisTxInfo = genesisTxInfo;
seedNodeAddresses = seedNodeRepository.getSeedNodeAddresses().stream()
.map(NodeAddress::getFullAddress)
.collect(Collectors.toSet());
}
@ -283,7 +293,8 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe
private boolean processPeersDaoStateHash(DaoStateHash daoStateHash, Optional<NodeAddress> peersNodeAddress, boolean notifyListeners) {
AtomicBoolean changed = new AtomicBoolean(false);
AtomicBoolean isInConflict = new AtomicBoolean(this.isInConflict);
AtomicBoolean inConflictWithNonSeedNode = new AtomicBoolean(this.isInConflictWithNonSeedNode);
AtomicBoolean inConflictWithSeedNode = new AtomicBoolean(this.isInConflictWithSeedNode);
StringBuilder sb = new StringBuilder();
daoStateBlockChain.stream()
.filter(e -> e.getHeight() == daoStateHash.getHeight()).findAny()
@ -293,7 +304,11 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe
daoStateBlock.putInPeersMap(peersNodeAddressAsString, daoStateHash);
if (!daoStateBlock.getMyStateHash().hasEqualHash(daoStateHash)) {
daoStateBlock.putInConflictMap(peersNodeAddressAsString, daoStateHash);
isInConflict.set(true);
if (seedNodeAddresses.contains(peersNodeAddressAsString)) {
inConflictWithSeedNode.set(true);
} else {
inConflictWithNonSeedNode.set(true);
}
sb.append("We received a block hash from peer ")
.append(peersNodeAddressAsString)
.append(" which conflicts with our block hash.\n")
@ -305,13 +320,18 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe
changed.set(true);
});
this.isInConflict = isInConflict.get();
this.isInConflictWithNonSeedNode = inConflictWithNonSeedNode.get();
this.isInConflictWithSeedNode = inConflictWithSeedNode.get();
String conflictMsg = sb.toString();
if (this.isInConflict && !conflictMsg.isEmpty()) {
log.warn(conflictMsg);
if (!conflictMsg.isEmpty()) {
if (this.isInConflictWithSeedNode)
log.warn("Conflict with seed nodes: {}", conflictMsg);
else if (this.isInConflictWithNonSeedNode)
log.info("Conflict with non-seed nodes: {}", conflictMsg);
}
if (notifyListeners && changed.get()) {
listeners.forEach(Listener::onChangeAfterBatchProcessing);
}

View file

@ -36,6 +36,7 @@ import bisq.core.dao.state.model.governance.Proposal;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.UserThread;
import bisq.common.crypto.Hash;
@ -49,6 +50,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@ -81,6 +83,8 @@ public class ProposalStateMonitoringService implements DaoSetupService, DaoState
private final GenesisTxInfo genesisTxInfo;
private final PeriodService periodService;
private final ProposalService proposalService;
private final Set<String> seedNodeAddresses;
@Getter
private final LinkedList<ProposalStateBlock> proposalStateBlockChain = new LinkedList<>();
@ -88,7 +92,9 @@ public class ProposalStateMonitoringService implements DaoSetupService, DaoState
private final LinkedList<ProposalStateHash> proposalStateHashChain = new LinkedList<>();
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
@Getter
private boolean isInConflict;
private boolean isInConflictWithNonSeedNode;
@Getter
private boolean isInConflictWithSeedNode;
private boolean parseBlockChainComplete;
@ -101,12 +107,16 @@ public class ProposalStateMonitoringService implements DaoSetupService, DaoState
ProposalStateNetworkService proposalStateNetworkService,
GenesisTxInfo genesisTxInfo,
PeriodService periodService,
ProposalService proposalService) {
ProposalService proposalService,
SeedNodeRepository seedNodeRepository) {
this.daoStateService = daoStateService;
this.proposalStateNetworkService = proposalStateNetworkService;
this.genesisTxInfo = genesisTxInfo;
this.periodService = periodService;
this.proposalService = proposalService;
seedNodeAddresses = seedNodeRepository.getSeedNodeAddresses().stream()
.map(NodeAddress::getFullAddress)
.collect(Collectors.toSet());
}
@ -280,7 +290,8 @@ public class ProposalStateMonitoringService implements DaoSetupService, DaoState
private boolean processPeersProposalStateHash(ProposalStateHash proposalStateHash, Optional<NodeAddress> peersNodeAddress, boolean notifyListeners) {
AtomicBoolean changed = new AtomicBoolean(false);
AtomicBoolean isInConflict = new AtomicBoolean(this.isInConflict);
AtomicBoolean inConflictWithNonSeedNode = new AtomicBoolean(this.isInConflictWithNonSeedNode);
AtomicBoolean inConflictWithSeedNode = new AtomicBoolean(this.isInConflictWithSeedNode);
StringBuilder sb = new StringBuilder();
proposalStateBlockChain.stream()
.filter(e -> e.getHeight() == proposalStateHash.getHeight()).findAny()
@ -290,7 +301,11 @@ public class ProposalStateMonitoringService implements DaoSetupService, DaoState
daoStateBlock.putInPeersMap(peersNodeAddressAsString, proposalStateHash);
if (!daoStateBlock.getMyStateHash().hasEqualHash(proposalStateHash)) {
daoStateBlock.putInConflictMap(peersNodeAddressAsString, proposalStateHash);
isInConflict.set(true);
if (seedNodeAddresses.contains(peersNodeAddressAsString)) {
inConflictWithSeedNode.set(true);
} else {
inConflictWithNonSeedNode.set(true);
}
sb.append("We received a block hash from peer ")
.append(peersNodeAddressAsString)
.append(" which conflicts with our block hash.\n")
@ -302,11 +317,15 @@ public class ProposalStateMonitoringService implements DaoSetupService, DaoState
changed.set(true);
});
this.isInConflict = isInConflict.get();
this.isInConflictWithNonSeedNode = inConflictWithNonSeedNode.get();
this.isInConflictWithSeedNode = inConflictWithSeedNode.get();
String conflictMsg = sb.toString();
if (this.isInConflict && !conflictMsg.isEmpty()) {
log.warn(conflictMsg);
if (!conflictMsg.isEmpty()) {
if (this.isInConflictWithSeedNode)
log.warn("Conflict with seed nodes: {}", conflictMsg);
else if (this.isInConflictWithNonSeedNode)
log.info("Conflict with non-seed nodes: {}", conflictMsg);
}
if (notifyListeners && changed.get()) {

View file

@ -910,7 +910,8 @@ settings.preferences.selectCurrencyNetwork=Select network
setting.preferences.daoOptions=DAO options
setting.preferences.dao.resync.label=Rebuild DAO state from genesis tx
setting.preferences.dao.resync.button=Resync
setting.preferences.dao.resync.popup=After an application restart the BSQ consensus state will be rebuilt from the genesis transaction.
setting.preferences.dao.resync.popup=After an application restart the P2P network governance data will be reloaded from \
the seed nodes and the BSQ consensus state will be rebuilt from the genesis transaction.
setting.preferences.dao.isDaoFullNode=Run Bisq as DAO full node
setting.preferences.dao.rpcUser=RPC username
setting.preferences.dao.rpcPw=RPC password
@ -1904,11 +1905,9 @@ dao.monitor.requestAlHashes=Request all hashes
dao.monitor.resync=Resync DAO state
dao.monitor.table.header.cycleBlockHeight=Cycle / block height
dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1}
dao.monitor.table.seedPeers=Seed node: {0}
dao.monitor.daoState.headline=DAO state
dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network
dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your \
DAO state.
dao.monitor.daoState.table.headline=Chain of DAO state hashes
dao.monitor.daoState.table.blockHeight=Block height
dao.monitor.daoState.table.hash=Hash of DAO state
@ -1920,9 +1919,6 @@ dao.monitor.daoState.utxoConflicts.sumUtxo=Sum of all UTXO: {0} BSQ
dao.monitor.daoState.utxoConflicts.sumBsq=Sum of all BSQ: {0} BSQ
dao.monitor.proposal.headline=Proposals state
dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network
dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your \
application.
dao.monitor.proposal.table.headline=Chain of proposal state hashes
dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict
@ -1930,11 +1926,13 @@ dao.monitor.proposal.table.hash=Hash of proposal state
dao.monitor.proposal.table.prev=Previous hash
dao.monitor.proposal.table.numProposals=No. proposals
dao.monitor.isInConflictWithSeedNode=Your local data is not in consensus with at least one seed node. \
Please resync the DAO state.
dao.monitor.isInConflictWithNonSeedNode=One of your peers is not in consensus with the network but your node \
is in sync with the seed nodes.
dao.monitor.daoStateInSync=Your local node is in consensus with the network
dao.monitor.blindVote.headline=Blind votes state
dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network
dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your \
application.
dao.monitor.blindVote.table.headline=Chain of blind vote state hashes
dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict
dao.monitor.blindVote.table.hash=Hash of blind vote state

View file

@ -20,8 +20,12 @@ package bisq.desktop.main.dao.monitor;
import bisq.core.dao.monitoring.model.StateHash;
import bisq.core.locale.Res;
import bisq.network.p2p.NodeAddress;
import bisq.common.util.Utilities;
import java.util.Set;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@ -36,10 +40,15 @@ public abstract class StateInConflictListItem<T extends StateHash> {
private final String prevHash;
private final T stateHash;
protected StateInConflictListItem(String peerAddress, T stateHash, int cycleIndex) {
protected StateInConflictListItem(String peerAddress, T stateHash, int cycleIndex,
Set<NodeAddress> seedNodeAddresses) {
this.stateHash = stateHash;
this.peerAddress = peerAddress;
height = Res.get("dao.monitor.table.cycleBlockHeight", cycleIndex + 1, String.valueOf(stateHash.getHeight()));
this.peerAddress = seedNodeAddresses.stream().anyMatch(e -> e.getFullAddress().equals(peerAddress)) ?
Res.get("dao.monitor.table.seedPeers", peerAddress) :
peerAddress;
height = Res.get("dao.monitor.table.cycleBlockHeight",
cycleIndex + 1,
String.valueOf(stateHash.getHeight()));
hash = Utilities.bytesAsHexString(stateHash.getHash());
prevHash = stateHash.getPrevHash().length > 0 ?
Utilities.bytesAsHexString(stateHash.getPrevHash()) : "-";

View file

@ -23,6 +23,7 @@ import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.TableGroupHeadline;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout;
@ -36,7 +37,10 @@ import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.locale.Res;
import javax.inject.Inject;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.storage.FileManager;
import de.jensd.fx.fontawesome.AwesomeIcon;
@ -64,8 +68,12 @@ import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
import java.io.File;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@FxmlView
@ -78,6 +86,8 @@ public abstract class StateMonitorView<StH extends StateHash,
protected final DaoFacade daoFacade;
protected final CycleService cycleService;
protected final PeriodService periodService;
protected final Set<NodeAddress> seedNodeAddresses;
private final File storageDir;
protected TextField statusTextField;
protected Button resyncButton;
@ -91,22 +101,26 @@ public abstract class StateMonitorView<StH extends StateHash,
protected int gridRow = 0;
private Subscription selectedItemSubscription;
protected final BooleanProperty isInConflict = new SimpleBooleanProperty();
protected final BooleanProperty isInConflictWithNonSeedNode = new SimpleBooleanProperty();
protected final BooleanProperty isInConflictWithSeedNode = new SimpleBooleanProperty();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public StateMonitorView(DaoStateService daoStateService,
DaoFacade daoFacade,
CycleService cycleService,
PeriodService periodService) {
protected StateMonitorView(DaoStateService daoStateService,
DaoFacade daoFacade,
CycleService cycleService,
PeriodService periodService,
SeedNodeRepository seedNodeRepository,
File storageDir) {
this.daoStateService = daoStateService;
this.daoFacade = daoFacade;
this.cycleService = cycleService;
this.periodService = periodService;
this.seedNodeAddresses = new HashSet<>(seedNodeRepository.getSeedNodeAddresses());
this.storageDir = storageDir;
}
@Override
@ -124,8 +138,36 @@ public abstract class StateMonitorView<StH extends StateHash,
daoStateService.addDaoStateListener(this);
resyncButton.visibleProperty().bind(isInConflict);
resyncButton.managedProperty().bind(isInConflict);
resyncButton.visibleProperty().bind(isInConflictWithSeedNode);
resyncButton.managedProperty().bind(isInConflictWithSeedNode);
resyncButton.setOnAction(ev -> {
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()) {
onDataUpdate();
@ -250,6 +292,17 @@ public abstract class StateMonitorView<StH extends StateHash,
///////////////////////////////////////////////////////////////////////////////////////////
protected void onDataUpdate() {
if (isInConflictWithSeedNode.get()) {
statusTextField.setText(Res.get("dao.monitor.isInConflictWithSeedNode"));
statusTextField.getStyleClass().add("dao-inConflict");
} else if (isInConflictWithNonSeedNode.get()) {
statusTextField.setText(Res.get("dao.monitor.isInConflictWithNonSeedNode"));
statusTextField.getStyleClass().remove("dao-inConflict");
} else {
statusTextField.setText(Res.get("dao.monitor.daoStateInSync"));
statusTextField.getStyleClass().remove("dao-inConflict");
}
GUIUtil.setFitToRowsForTableView(tableView, 25, 28, 2, 5);
}
@ -455,7 +508,7 @@ public abstract class StateMonitorView<StH extends StateHash,
column = new AutoTooltipTableColumn<>(getPeersTableHeader());
column.setMinWidth(80);
column.setMinWidth(150);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@ -479,7 +532,7 @@ public abstract class StateMonitorView<StH extends StateHash,
column = new AutoTooltipTableColumn<>(getHashTableHeader());
column.setMinWidth(150);
column.setMinWidth(120);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@ -503,7 +556,7 @@ public abstract class StateMonitorView<StH extends StateHash,
column = new AutoTooltipTableColumn<>(getPrevHashTableHeader());
column.setMinWidth(150);
column.setMinWidth(120);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@ -527,7 +580,7 @@ public abstract class StateMonitorView<StH extends StateHash,
column = new AutoTooltipTableColumn<>("");
column.setMinWidth(100);
column.setMinWidth(120);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {

View file

@ -21,6 +21,10 @@ import bisq.desktop.main.dao.monitor.StateInConflictListItem;
import bisq.core.dao.monitoring.model.BlindVoteStateHash;
import bisq.network.p2p.NodeAddress;
import java.util.Set;
import lombok.EqualsAndHashCode;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
@ -31,8 +35,9 @@ import lombok.extern.slf4j.Slf4j;
class BlindVoteStateInConflictListItem extends StateInConflictListItem<BlindVoteStateHash> {
private final String numBlindVotes;
BlindVoteStateInConflictListItem(String peerAddress, BlindVoteStateHash stateHash, int cycleIndex) {
super(peerAddress, stateHash, cycleIndex);
BlindVoteStateInConflictListItem(String peerAddress, BlindVoteStateHash stateHash, int cycleIndex,
Set<NodeAddress> seedNodeAddresses) {
super(peerAddress, stateHash, cycleIndex, seedNodeAddresses);
numBlindVotes = String.valueOf(stateHash.getNumBlindVotes());
}

View file

@ -20,7 +20,6 @@ package bisq.desktop.main.dao.monitor.blindvotes;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.main.dao.monitor.StateMonitorView;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.FormBuilder;
import bisq.core.dao.DaoFacade;
@ -32,7 +31,12 @@ import bisq.core.dao.monitoring.model.BlindVoteStateHash;
import bisq.core.dao.state.DaoStateService;
import bisq.core.locale.Res;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.storage.Storage;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
@ -41,6 +45,8 @@ import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.util.Callback;
import java.io.File;
import java.util.Comparator;
import java.util.Map;
import java.util.stream.Collectors;
@ -50,6 +56,7 @@ public class BlindVoteStateMonitorView extends StateMonitorView<BlindVoteStateHa
implements BlindVoteStateMonitoringService.Listener {
private final BlindVoteStateMonitoringService blindVoteStateMonitoringService;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@ -60,8 +67,10 @@ public class BlindVoteStateMonitorView extends StateMonitorView<BlindVoteStateHa
DaoFacade daoFacade,
BlindVoteStateMonitoringService blindVoteStateMonitoringService,
CycleService cycleService,
PeriodService periodService) {
super(daoStateService, daoFacade, cycleService, periodService);
PeriodService periodService,
SeedNodeRepository seedNodeRepository,
@Named(Storage.STORAGE_DIR) File storageDir) {
super(daoStateService, daoFacade, cycleService, periodService, seedNodeRepository, storageDir);
this.blindVoteStateMonitoringService = blindVoteStateMonitoringService;
}
@ -80,14 +89,8 @@ public class BlindVoteStateMonitorView extends StateMonitorView<BlindVoteStateHa
@Override
protected void activate() {
super.activate();
blindVoteStateMonitoringService.addListener(this);
resyncButton.setOnAction(e -> daoFacade.resyncDao(() ->
new Popup<>().attention(Res.get("setting.preferences.dao.resync.popup"))
.useShutDownButton()
.hideCloseButton()
.show())
);
blindVoteStateMonitoringService.addListener(this);
}
@Override
@ -123,7 +126,7 @@ public class BlindVoteStateMonitorView extends StateMonitorView<BlindVoteStateHa
protected BlindVoteStateInConflictListItem getStateInConflictListItem(Map.Entry<String, BlindVoteStateHash> mapEntry) {
BlindVoteStateHash blindVoteStateHash = mapEntry.getValue();
int cycleIndex = periodService.getCycle(blindVoteStateHash.getHeight()).map(cycleService::getCycleIndex).orElse(0);
return new BlindVoteStateInConflictListItem(mapEntry.getKey(), mapEntry.getValue(), cycleIndex);
return new BlindVoteStateInConflictListItem(mapEntry.getKey(), mapEntry.getValue(), cycleIndex, seedNodeAddresses);
}
@Override
@ -173,15 +176,8 @@ public class BlindVoteStateMonitorView extends StateMonitorView<BlindVoteStateHa
@Override
protected void onDataUpdate() {
isInConflict.set(blindVoteStateMonitoringService.isInConflict());
if (isInConflict.get()) {
statusTextField.setText(Res.get("dao.monitor.blindVote.daoStateNotInSync"));
statusTextField.getStyleClass().add("dao-inConflict");
} else {
statusTextField.setText(Res.get("dao.monitor.blindVote.daoStateInSync"));
statusTextField.getStyleClass().remove("dao-inConflict");
}
isInConflictWithSeedNode.set(blindVoteStateMonitoringService.isInConflictWithSeedNode());
isInConflictWithNonSeedNode.set(blindVoteStateMonitoringService.isInConflictWithNonSeedNode());
listItems.setAll(blindVoteStateMonitoringService.getBlindVoteStateBlockChain().stream()
.map(this::getStateBlockListItem)
@ -202,7 +198,7 @@ public class BlindVoteStateMonitorView extends StateMonitorView<BlindVoteStateHa
TableColumn<BlindVoteStateBlockListItem, BlindVoteStateBlockListItem> column;
column = new AutoTooltipTableColumn<>(Res.get("dao.monitor.blindVote.table.numBlindVotes"));
column.setMinWidth(110);
column.setMinWidth(90);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {
@ -232,7 +228,7 @@ public class BlindVoteStateMonitorView extends StateMonitorView<BlindVoteStateHa
TableColumn<BlindVoteStateInConflictListItem, BlindVoteStateInConflictListItem> column;
column = new AutoTooltipTableColumn<>(Res.get("dao.monitor.blindVote.table.numBlindVotes"));
column.setMinWidth(110);
column.setMinWidth(90);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(
new Callback<>() {

View file

@ -21,6 +21,10 @@ import bisq.desktop.main.dao.monitor.StateInConflictListItem;
import bisq.core.dao.monitoring.model.DaoStateHash;
import bisq.network.p2p.NodeAddress;
import java.util.Set;
import lombok.EqualsAndHashCode;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
@ -29,7 +33,8 @@ import lombok.extern.slf4j.Slf4j;
@Value
@EqualsAndHashCode(callSuper = true)
class DaoStateInConflictListItem extends StateInConflictListItem<DaoStateHash> {
DaoStateInConflictListItem(String peerAddress, DaoStateHash stateHash, int cycleIndex) {
super(peerAddress, stateHash, cycleIndex);
DaoStateInConflictListItem(String peerAddress, DaoStateHash stateHash, int cycleIndex,
Set<NodeAddress> seedNodeAddresses) {
super(peerAddress, stateHash, cycleIndex, seedNodeAddresses);
}
}

View file

@ -33,12 +33,18 @@ import bisq.core.dao.monitoring.model.UtxoMismatch;
import bisq.core.dao.state.DaoStateService;
import bisq.core.locale.Res;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.storage.Storage;
import bisq.common.util.Utilities;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.collections.ListChangeListener;
import java.io.File;
import java.util.Map;
import java.util.stream.Collectors;
@ -60,8 +66,10 @@ public class DaoStateMonitorView extends StateMonitorView<DaoStateHash, DaoState
DaoFacade daoFacade,
DaoStateMonitoringService daoStateMonitoringService,
CycleService cycleService,
PeriodService periodService) {
super(daoStateService, daoFacade, cycleService, periodService);
PeriodService periodService,
SeedNodeRepository seedNodeRepository,
@Named(Storage.STORAGE_DIR) File storageDir) {
super(daoStateService, daoFacade, cycleService, periodService, seedNodeRepository, storageDir);
this.daoStateMonitoringService = daoStateMonitoringService;
}
@ -87,13 +95,6 @@ public class DaoStateMonitorView extends StateMonitorView<DaoStateHash, DaoState
daoStateMonitoringService.getUtxoMismatches().addListener(utxoMismatchListChangeListener);
updateUtxoMismatches();
resyncButton.setOnAction(e -> daoFacade.resyncDao(() ->
new Popup<>().attention(Res.get("setting.preferences.dao.resync.popup"))
.useShutDownButton()
.hideCloseButton()
.show())
);
}
@Override
@ -131,7 +132,7 @@ public class DaoStateMonitorView extends StateMonitorView<DaoStateHash, DaoState
protected DaoStateInConflictListItem getStateInConflictListItem(Map.Entry<String, DaoStateHash> mapEntry) {
DaoStateHash daoStateHash = mapEntry.getValue();
int cycleIndex = periodService.getCycle(daoStateHash.getHeight()).map(cycleService::getCycleIndex).orElse(0);
return new DaoStateInConflictListItem(mapEntry.getKey(), daoStateHash, cycleIndex);
return new DaoStateInConflictListItem(mapEntry.getKey(), daoStateHash, cycleIndex, seedNodeAddresses);
}
@Override
@ -181,15 +182,8 @@ public class DaoStateMonitorView extends StateMonitorView<DaoStateHash, DaoState
@Override
protected void onDataUpdate() {
isInConflict.set(daoStateMonitoringService.isInConflict());
if (isInConflict.get()) {
statusTextField.setText(Res.get("dao.monitor.daoState.daoStateNotInSync"));
statusTextField.getStyleClass().add("dao-inConflict");
} else {
statusTextField.setText(Res.get("dao.monitor.daoState.daoStateInSync"));
statusTextField.getStyleClass().remove("dao-inConflict");
}
isInConflictWithSeedNode.set(daoStateMonitoringService.isInConflictWithSeedNode());
isInConflictWithNonSeedNode.set(daoStateMonitoringService.isInConflictWithNonSeedNode());
listItems.setAll(daoStateMonitoringService.getDaoStateBlockChain().stream()
.map(this::getStateBlockListItem)

View file

@ -21,6 +21,10 @@ import bisq.desktop.main.dao.monitor.StateInConflictListItem;
import bisq.core.dao.monitoring.model.ProposalStateHash;
import bisq.network.p2p.NodeAddress;
import java.util.Set;
import lombok.EqualsAndHashCode;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
@ -31,8 +35,9 @@ import lombok.extern.slf4j.Slf4j;
class ProposalStateInConflictListItem extends StateInConflictListItem<ProposalStateHash> {
private final String numProposals;
ProposalStateInConflictListItem(String peerAddress, ProposalStateHash stateHash, int cycleIndex) {
super(peerAddress, stateHash, cycleIndex);
ProposalStateInConflictListItem(String peerAddress, ProposalStateHash stateHash, int cycleIndex,
Set<NodeAddress> seedNodeAddresses) {
super(peerAddress, stateHash, cycleIndex, seedNodeAddresses);
numProposals = String.valueOf(stateHash.getNumProposals());
}

View file

@ -20,7 +20,6 @@ package bisq.desktop.main.dao.monitor.proposals;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.main.dao.monitor.StateMonitorView;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.FormBuilder;
import bisq.core.dao.DaoFacade;
@ -32,7 +31,12 @@ import bisq.core.dao.monitoring.model.ProposalStateHash;
import bisq.core.dao.state.DaoStateService;
import bisq.core.locale.Res;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.storage.Storage;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
@ -41,6 +45,8 @@ import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.util.Callback;
import java.io.File;
import java.util.Comparator;
import java.util.Map;
import java.util.stream.Collectors;
@ -60,8 +66,10 @@ public class ProposalStateMonitorView extends StateMonitorView<ProposalStateHash
DaoFacade daoFacade,
ProposalStateMonitoringService proposalStateMonitoringService,
CycleService cycleService,
PeriodService periodService) {
super(daoStateService, daoFacade, cycleService, periodService);
PeriodService periodService,
SeedNodeRepository seedNodeRepository,
@Named(Storage.STORAGE_DIR) File storageDir) {
super(daoStateService, daoFacade, cycleService, periodService, seedNodeRepository, storageDir);
this.proposalStateMonitoringService = proposalStateMonitoringService;
}
@ -81,13 +89,6 @@ public class ProposalStateMonitorView extends StateMonitorView<ProposalStateHash
protected void activate() {
super.activate();
proposalStateMonitoringService.addListener(this);
resyncButton.setOnAction(e -> daoFacade.resyncDao(() ->
new Popup<>().attention(Res.get("setting.preferences.dao.resync.popup"))
.useShutDownButton()
.hideCloseButton()
.show())
);
}
@Override
@ -123,7 +124,7 @@ public class ProposalStateMonitorView extends StateMonitorView<ProposalStateHash
protected ProposalStateInConflictListItem getStateInConflictListItem(Map.Entry<String, ProposalStateHash> mapEntry) {
ProposalStateHash proposalStateHash = mapEntry.getValue();
int cycleIndex = periodService.getCycle(proposalStateHash.getHeight()).map(cycleService::getCycleIndex).orElse(0);
return new ProposalStateInConflictListItem(mapEntry.getKey(), mapEntry.getValue(), cycleIndex);
return new ProposalStateInConflictListItem(mapEntry.getKey(), mapEntry.getValue(), cycleIndex, seedNodeAddresses);
}
@Override
@ -173,15 +174,8 @@ public class ProposalStateMonitorView extends StateMonitorView<ProposalStateHash
@Override
protected void onDataUpdate() {
isInConflict.set(proposalStateMonitoringService.isInConflict());
if (isInConflict.get()) {
statusTextField.setText(Res.get("dao.monitor.proposal.daoStateNotInSync"));
statusTextField.getStyleClass().add("dao-inConflict");
} else {
statusTextField.setText(Res.get("dao.monitor.proposal.daoStateInSync"));
statusTextField.getStyleClass().remove("dao-inConflict");
}
isInConflictWithSeedNode.set(proposalStateMonitoringService.isInConflictWithSeedNode());
isInConflictWithNonSeedNode.set(proposalStateMonitoringService.isInConflictWithNonSeedNode());
listItems.setAll(proposalStateMonitoringService.getProposalStateBlockChain().stream()
.map(this::getStateBlockListItem)