Merge pull request #6504 from HenrikJannsen/add_check_for_isInConflictWithSeedNode

Add check for dao state hash conflict with seed node
This commit is contained in:
Alejandro García 2023-01-06 14:09:01 +00:00 committed by GitHub
commit dda32c09ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 149 additions and 93 deletions

View file

@ -291,7 +291,8 @@ public abstract class WalletService {
continue; continue;
} }
if (!connectedOutput.isMine(wallet)) { if (!connectedOutput.isMine(wallet)) {
log.error("connectedOutput is not mine"); log.info("ConnectedOutput is not mine. This can be the case for BSQ transactions where the " +
"input gets signed by the other wallet. connectedOutput={}", connectedOutput);
continue; continue;
} }

View file

@ -57,7 +57,6 @@ import bisq.core.dao.monitoring.DaoStateMonitoringService;
import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.BaseTx; import bisq.core.dao.state.model.blockchain.BaseTx;
import bisq.core.dao.state.model.blockchain.BaseTxOutput;
import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.blockchain.Tx; import bisq.core.dao.state.model.blockchain.Tx;
import bisq.core.dao.state.model.blockchain.TxOutput; import bisq.core.dao.state.model.blockchain.TxOutput;
@ -531,10 +530,13 @@ public class DaoFacade implements DaoSetupService {
return daoStateService.getBlockAtHeight(chainHeight); return daoStateService.getBlockAtHeight(chainHeight);
} }
public boolean daoStateNeedsRebuilding() { public boolean isDaoStateReadyAndInSync() {
return daoStateMonitoringService.isInConflictWithSeedNode() || daoStateMonitoringService.isDaoStateBlockChainNotConnecting(); return daoStateService.isParseBlockChainComplete() &&
!daoStateMonitoringService.isInConflictWithSeedNode() &&
!daoStateMonitoringService.isDaoStateBlockChainNotConnecting();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Use case: Bonding // Use case: Bonding
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -578,10 +580,6 @@ public class DaoFacade implements DaoSetupService {
return daoStateService.getTotalAmountOfConfiscatedTxOutputs(); return daoStateService.getTotalAmountOfConfiscatedTxOutputs();
} }
public long getTotalAmountOfInvalidatedBsq() {
return daoStateService.getTotalAmountOfInvalidatedBsq();
}
// Contains burned fee and invalidated bsq due invalid txs // Contains burned fee and invalidated bsq due invalid txs
public long getTotalAmountOfBurntBsq() { public long getTotalAmountOfBurntBsq() {
return daoStateService.getTotalAmountOfBurntBsq(); return daoStateService.getTotalAmountOfBurntBsq();
@ -595,11 +593,6 @@ public class DaoFacade implements DaoSetupService {
return daoStateService.getIrregularTxs(); return daoStateService.getIrregularTxs();
} }
public long getTotalAmountOfUnspentTxOutputs() {
// Does not consider confiscated outputs (they stay as utxo)
return daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum();
}
public Optional<Integer> getLockTime(String txId) { public Optional<Integer> getLockTime(String txId) {
return daoStateService.getLockTime(txId); return daoStateService.getLockTime(txId);
} }

View file

@ -78,7 +78,6 @@ public class DelayedPayoutTxReceiverService implements DaoStateListener {
// spike when opening arbitration. // spike when opening arbitration.
private static final long DPT_MIN_TX_FEE_RATE = 10; private static final long DPT_MIN_TX_FEE_RATE = 10;
private final DaoStateService daoStateService; private final DaoStateService daoStateService;
private final BurningManService burningManService; private final BurningManService burningManService;
private int currentChainHeight; private int currentChainHeight;
@ -127,12 +126,6 @@ public class DelayedPayoutTxReceiverService implements DaoStateListener {
burningManService.getActiveBurningManCandidates(burningManSelectionHeight) : burningManService.getActiveBurningManCandidates(burningManSelectionHeight) :
burningManService.getBurningManCandidatesByName(burningManSelectionHeight).values(); burningManService.getBurningManCandidatesByName(burningManSelectionHeight).values();
if (burningManCandidates.isEmpty()) {
// If there are no compensation requests (e.g. at dev testing) we fall back to the legacy BM
return List.of(new Tuple2<>(inputAmount, burningManService.getLegacyBurningManAddress(burningManSelectionHeight)));
}
// We need to use the same txFeePerVbyte value for both traders. // We need to use the same txFeePerVbyte value for both traders.
// We use the tradeTxFee value which is calculated from the average of taker fee tx size and deposit tx size. // We use the tradeTxFee value which is calculated from the average of taker fee tx size and deposit tx size.
// Otherwise, we would need to sync the fee rate of both traders. // Otherwise, we would need to sync the fee rate of both traders.
@ -146,12 +139,19 @@ public class DelayedPayoutTxReceiverService implements DaoStateListener {
// Smallest tx size is 246. With additional change output we add 32. To be safe we use the largest expected size. // Smallest tx size is 246. With additional change output we add 32. To be safe we use the largest expected size.
double txSize = 278; double txSize = 278;
long txFeePerVbyte = Math.max(DPT_MIN_TX_FEE_RATE, Math.round(tradeTxFee / txSize)); long txFeePerVbyte = Math.max(DPT_MIN_TX_FEE_RATE, Math.round(tradeTxFee / txSize));
if (burningManCandidates.isEmpty()) {
// If there are no compensation requests (e.g. at dev testing) we fall back to the legacy BM
long spendableAmount = getSpendableAmount(1, inputAmount, txFeePerVbyte);
return List.of(new Tuple2<>(spendableAmount, burningManService.getLegacyBurningManAddress(burningManSelectionHeight)));
}
long spendableAmount = getSpendableAmount(burningManCandidates.size(), inputAmount, txFeePerVbyte); long spendableAmount = getSpendableAmount(burningManCandidates.size(), inputAmount, txFeePerVbyte);
// We only use outputs > 1000 sat or at least 2 times the cost for the output (32 bytes). // We only use outputs > 1000 sat or at least 2 times the cost for the output (32 bytes).
// If we remove outputs it will be spent as miner fee. // If we remove outputs it will be spent as miner fee.
long minOutputAmount = Math.max(DPT_MIN_OUTPUT_AMOUNT, txFeePerVbyte * 32 * 2); long minOutputAmount = Math.max(DPT_MIN_OUTPUT_AMOUNT, txFeePerVbyte * 32 * 2);
// Sanity check that max share of a non-legacy BM is 20% over MAX_BURN_SHARE (taking into account potential increase due adjustment) // Sanity check that max share of a non-legacy BM is 20% over MAX_BURN_SHARE (taking into account potential increase due adjustment)
long maxOutputAmount = Math.round(inputAmount * (BurningManService.MAX_BURN_SHARE * 1.2)); long maxOutputAmount = Math.round(spendableAmount * (BurningManService.MAX_BURN_SHARE * 1.2));
// We accumulate small amounts which gets filtered out and subtract it from 1 to get an adjustment factor // We accumulate small amounts which gets filtered out and subtract it from 1 to get an adjustment factor
// used later to be applied to the remaining burningmen share. // used later to be applied to the remaining burningmen share.
double adjustment = 1 - burningManCandidates.stream() double adjustment = 1 - burningManCandidates.stream()

View file

@ -206,7 +206,7 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List
} }
private void handleRepublishGovernanceDataRequest() { private void handleRepublishGovernanceDataRequest() {
log.warn("We received a RepublishGovernanceDataRequest and re-published all proposalPayloads and " + log.info("We received a RepublishGovernanceDataRequest and re-published all proposalPayloads and " +
"blindVotePayloads to the P2P network."); "blindVotePayloads to the P2P network.");
missingDataRequestService.reRepublishAllGovernanceData(); missingDataRequestService.reRepublishAllGovernanceData();
} }

View file

@ -25,6 +25,7 @@ import bisq.core.trade.protocol.bisq_v1.messages.DepositTxAndDelayedPayoutTxMess
import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxRequest; import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxRequest;
import bisq.core.trade.protocol.bisq_v1.messages.PayoutTxPublishedMessage; import bisq.core.trade.protocol.bisq_v1.messages.PayoutTxPublishedMessage;
import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter; import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter;
import bisq.core.trade.protocol.bisq_v1.tasks.CheckIfDaoStateIsInSync;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask; import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;
import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerFinalizesDelayedPayoutTx; import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerFinalizesDelayedPayoutTx;
import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest; import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest;
@ -71,6 +72,7 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
.with(message) .with(message)
.from(peer)) .from(peer))
.setup(tasks( .setup(tasks(
CheckIfDaoStateIsInSync.class,
MakerProcessesInputsForDepositTxRequest.class, MakerProcessesInputsForDepositTxRequest.class,
ApplyFilter.class, ApplyFilter.class,
getVerifyPeersFeePaymentClass(), getVerifyPeersFeePaymentClass(),

View file

@ -27,6 +27,7 @@ import bisq.core.trade.protocol.bisq_v1.messages.DepositTxAndDelayedPayoutTxMess
import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxResponse; import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxResponse;
import bisq.core.trade.protocol.bisq_v1.messages.PayoutTxPublishedMessage; import bisq.core.trade.protocol.bisq_v1.messages.PayoutTxPublishedMessage;
import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter; import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter;
import bisq.core.trade.protocol.bisq_v1.tasks.CheckIfDaoStateIsInSync;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask; import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;
import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerFinalizesDelayedPayoutTx; import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerFinalizesDelayedPayoutTx;
import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest; import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest;
@ -77,6 +78,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
expect(phase(Trade.Phase.INIT) expect(phase(Trade.Phase.INIT)
.with(TakerEvent.TAKE_OFFER)) .with(TakerEvent.TAKE_OFFER))
.setup(tasks( .setup(tasks(
CheckIfDaoStateIsInSync.class,
ApplyFilter.class, ApplyFilter.class,
getVerifyPeersFeePaymentClass(), getVerifyPeersFeePaymentClass(),
CreateTakerFeeTx.class, CreateTakerFeeTx.class,

View file

@ -27,6 +27,7 @@ import bisq.core.trade.protocol.bisq_v1.messages.DelayedPayoutTxSignatureRespons
import bisq.core.trade.protocol.bisq_v1.messages.DepositTxMessage; import bisq.core.trade.protocol.bisq_v1.messages.DepositTxMessage;
import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxRequest; import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxRequest;
import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter; import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter;
import bisq.core.trade.protocol.bisq_v1.tasks.CheckIfDaoStateIsInSync;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask; import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;
import bisq.core.trade.protocol.bisq_v1.tasks.maker.MakerCreateAndSignContract; import bisq.core.trade.protocol.bisq_v1.tasks.maker.MakerCreateAndSignContract;
import bisq.core.trade.protocol.bisq_v1.tasks.maker.MakerProcessesInputsForDepositTxRequest; import bisq.core.trade.protocol.bisq_v1.tasks.maker.MakerProcessesInputsForDepositTxRequest;
@ -73,6 +74,7 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
.with(message) .with(message)
.from(peer)) .from(peer))
.setup(tasks( .setup(tasks(
CheckIfDaoStateIsInSync.class,
MaybeCreateSubAccount.class, MaybeCreateSubAccount.class,
MakerProcessesInputsForDepositTxRequest.class, MakerProcessesInputsForDepositTxRequest.class,
ApplyFilter.class, ApplyFilter.class,

View file

@ -26,6 +26,7 @@ import bisq.core.trade.protocol.bisq_v1.messages.CounterCurrencyTransferStartedM
import bisq.core.trade.protocol.bisq_v1.messages.DelayedPayoutTxSignatureResponse; import bisq.core.trade.protocol.bisq_v1.messages.DelayedPayoutTxSignatureResponse;
import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxResponse; import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxResponse;
import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter; import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter;
import bisq.core.trade.protocol.bisq_v1.tasks.CheckIfDaoStateIsInSync;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask; import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;
import bisq.core.trade.protocol.bisq_v1.tasks.seller.MaybeCreateSubAccount; import bisq.core.trade.protocol.bisq_v1.tasks.seller.MaybeCreateSubAccount;
import bisq.core.trade.protocol.bisq_v1.tasks.seller.SellerCreatesDelayedPayoutTx; import bisq.core.trade.protocol.bisq_v1.tasks.seller.SellerCreatesDelayedPayoutTx;
@ -73,6 +74,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
.with(TakerEvent.TAKE_OFFER) .with(TakerEvent.TAKE_OFFER)
.from(trade.getTradingPeerNodeAddress())) .from(trade.getTradingPeerNodeAddress()))
.setup(tasks( .setup(tasks(
CheckIfDaoStateIsInSync.class,
MaybeCreateSubAccount.class, MaybeCreateSubAccount.class,
ApplyFilter.class, ApplyFilter.class,
getVerifyPeersFeePaymentClass(), getVerifyPeersFeePaymentClass(),

View file

@ -0,0 +1,46 @@
/*
* 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.trade.protocol.bisq_v1.tasks;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.common.taskrunner.TaskRunner;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
@Slf4j
public class CheckIfDaoStateIsInSync extends TradeTask {
public CheckIfDaoStateIsInSync(TaskRunner<Trade> taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
checkArgument(processModel.getDaoFacade().isDaoStateReadyAndInSync(), "DAO state is not in sync with seed nodes");
complete();
} catch (Throwable t) {
failed(t);
}
}
}

View file

@ -32,7 +32,6 @@ import java.util.List;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
@ -74,8 +73,19 @@ public class BuyerVerifiesFinalDelayedPayoutTx extends TradeTask {
depositTx, depositTx,
delayedPayoutTxReceivers, delayedPayoutTxReceivers,
lockTime); lockTime);
checkArgument(buyersDelayedPayoutTx.getTxId().equals(finalDelayedPayoutTx.getTxId()),
"TxIds of buyersDelayedPayoutTx and finalDelayedPayoutTx must be the same"); if (!buyersDelayedPayoutTx.getTxId().equals(finalDelayedPayoutTx.getTxId())) {
String errorMsg = "TxIds of buyersDelayedPayoutTx and finalDelayedPayoutTx must be the same.";
log.error("{} \nbuyersDelayedPayoutTx={}, \nfinalDelayedPayoutTx={}, " +
"\nBtcWalletService.chainHeight={}, " +
"\nDaoState.chainHeight={}, " +
"\nisDaoStateIsInSync={}",
errorMsg, buyersDelayedPayoutTx, finalDelayedPayoutTx,
processModel.getBtcWalletService().getBestChainHeight(),
processModel.getDaoFacade().getChainHeight(),
processModel.getDaoFacade().isDaoStateReadyAndInSync());
throw new IllegalArgumentException(errorMsg);
}
} }
complete(); complete();

View file

@ -32,7 +32,6 @@ import java.util.List;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
@ -66,8 +65,18 @@ public class BuyerVerifiesPreparedDelayedPayoutTx extends TradeTask {
preparedDepositTx, preparedDepositTx,
delayedPayoutTxReceivers, delayedPayoutTxReceivers,
lockTime); lockTime);
checkArgument(buyersPreparedDelayedPayoutTx.getTxId().equals(sellersPreparedDelayedPayoutTx.getTxId()), if (!buyersPreparedDelayedPayoutTx.getTxId().equals(sellersPreparedDelayedPayoutTx.getTxId())) {
"TxIds of buyersPreparedDelayedPayoutTx and sellersPreparedDelayedPayoutTx must be the same"); String errorMsg = "TxIds of buyersPreparedDelayedPayoutTx and sellersPreparedDelayedPayoutTx must be the same.";
log.error("{} \nbuyersPreparedDelayedPayoutTx={}, \nsellersPreparedDelayedPayoutTx={}, " +
"\nBtcWalletService.chainHeight={}, " +
"\nDaoState.chainHeight={}, " +
"\nisDaoStateIsInSync={}",
errorMsg, buyersPreparedDelayedPayoutTx, sellersPreparedDelayedPayoutTx,
processModel.getBtcWalletService().getBestChainHeight(),
processModel.getDaoFacade().getChainHeight(),
processModel.getDaoFacade().isDaoStateReadyAndInSync());
throw new IllegalArgumentException(errorMsg);
}
} }
// If the deposit tx is non-malleable, we already know its final ID, so should check that now // If the deposit tx is non-malleable, we already know its final ID, so should check that now

View file

@ -310,7 +310,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
setupClockWatcherPopup(); setupClockWatcherPopup();
marketPricePresentation.setup(); marketPricePresentation.setup();
daoPresentation.setup(); daoPresentation.init();
accountPresentation.setup(); accountPresentation.setup();
settingsPresentation.setup(); settingsPresentation.setup();
@ -505,7 +505,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
.show()); .show());
bisqSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress()); bisqSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress());
daoPresentation.getBsqSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress()); daoPresentation.getDaoStateSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress());
bisqSetup.setFilterWarningHandler(warning -> new Popup().warning(warning).show()); bisqSetup.setFilterWarningHandler(warning -> new Popup().warning(warning).show());
@ -704,7 +704,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
if (btcSyncProgress.doubleValue() < 1) { if (btcSyncProgress.doubleValue() < 1) {
combinedSyncProgress.set(btcSyncProgress.doubleValue()); combinedSyncProgress.set(btcSyncProgress.doubleValue());
} else { } else {
combinedSyncProgress.set(daoPresentation.getBsqSyncProgress().doubleValue()); combinedSyncProgress.set(daoPresentation.getDaoStateSyncProgress().doubleValue());
} }
} }
@ -783,7 +783,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
StringProperty getCombinedFooterInfo() { StringProperty getCombinedFooterInfo() {
final StringProperty combinedInfo = new SimpleStringProperty(); final StringProperty combinedInfo = new SimpleStringProperty();
combinedInfo.bind(Bindings.concat(this.footerVersionInfo, " ", daoPresentation.getBsqInfo())); combinedInfo.bind(Bindings.concat(this.footerVersionInfo, " ", daoPresentation.getDaoStateInfo()));
return combinedInfo; return combinedInfo;
} }

View file

@ -1,11 +1,15 @@
package bisq.desktop.main.presentation; package bisq.desktop.main.presentation;
import bisq.desktop.Navigation; import bisq.desktop.Navigation;
import bisq.desktop.util.GUIUtil; import bisq.desktop.main.MainView;
import bisq.desktop.main.dao.DaoView;
import bisq.desktop.main.dao.monitor.MonitorView;
import bisq.desktop.main.dao.monitor.daostate.DaoStateMonitorView;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.DaoFacade; import bisq.core.dao.monitoring.DaoStateMonitoringService;
import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.Block;
@ -28,24 +32,21 @@ import javafx.collections.MapChangeListener;
import lombok.Getter; import lombok.Getter;
@Singleton @Singleton
public class DaoPresentation implements DaoStateListener { public class DaoPresentation implements DaoStateListener, DaoStateMonitoringService.Listener {
public static final String DAO_NEWS = "daoNews"; public static final String DAO_NEWS = "daoNews";
private final Preferences preferences;
private final Navigation navigation; private final Navigation navigation;
private final BtcWalletService btcWalletService; private final BtcWalletService btcWalletService;
private final DaoFacade daoFacade; private final DaoStateMonitoringService daoStateMonitoringService;
private final BsqWalletService bsqWalletService; private final BsqWalletService bsqWalletService;
private final DaoStateService daoStateService; private final DaoStateService daoStateService;
private final ChangeListener<Number> walletChainHeightListener; private final ChangeListener<Number> walletChainHeightListener;
@Getter @Getter
private final DoubleProperty bsqSyncProgress = new SimpleDoubleProperty(-1); private final DoubleProperty daoStateSyncProgress = new SimpleDoubleProperty(-1);
@Getter @Getter
private final StringProperty bsqInfo = new SimpleStringProperty(""); private final StringProperty daoStateInfo = new SimpleStringProperty("");
private final SimpleBooleanProperty showNotification = new SimpleBooleanProperty(false); private final SimpleBooleanProperty showNotification = new SimpleBooleanProperty(false);
private boolean daoConflictWarningShown = false; // allow only one conflict warning per session
@Inject @Inject
public DaoPresentation(Preferences preferences, public DaoPresentation(Preferences preferences,
@ -53,12 +54,11 @@ public class DaoPresentation implements DaoStateListener {
BtcWalletService btcWalletService, BtcWalletService btcWalletService,
BsqWalletService bsqWalletService, BsqWalletService bsqWalletService,
DaoStateService daoStateService, DaoStateService daoStateService,
DaoFacade daoFacade) { DaoStateMonitoringService daoStateMonitoringService) {
this.preferences = preferences;
this.navigation = navigation; this.navigation = navigation;
this.btcWalletService = btcWalletService; this.btcWalletService = btcWalletService;
this.bsqWalletService = bsqWalletService; this.bsqWalletService = bsqWalletService;
this.daoFacade = daoFacade; this.daoStateMonitoringService = daoStateMonitoringService;
this.daoStateService = daoStateService; this.daoStateService = daoStateService;
preferences.getDontShowAgainMapAsObservable().addListener((MapChangeListener<? super String, ? super Boolean>) change -> { preferences.getDontShowAgainMapAsObservable().addListener((MapChangeListener<? super String, ? super Boolean>) change -> {
@ -73,34 +73,24 @@ public class DaoPresentation implements DaoStateListener {
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Private // Public
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void onUpdateAnyChainHeight() { public void init() {
final int bsqBlockChainHeight = daoFacade.getChainHeight(); showNotification.set(false);
final int bsqWalletChainHeight = bsqWalletService.getBestChainHeight();
if (bsqWalletChainHeight > 0) { btcWalletService.getChainHeightProperty().addListener(walletChainHeightListener);
final boolean synced = bsqWalletChainHeight == bsqBlockChainHeight; daoStateService.addDaoStateListener(this);
if (bsqBlockChainHeight != bsqWalletChainHeight) { daoStateMonitoringService.addListener(this);
bsqSyncProgress.set(-1);
} else { onUpdateAnyChainHeight();
bsqSyncProgress.set(0);
} }
if (synced) { public BooleanProperty getShowDaoUpdatesNotification() {
bsqInfo.set(""); return showNotification;
if (daoFacade.daoStateNeedsRebuilding() && !daoConflictWarningShown) {
daoConflictWarningShown = true; // only warn max 1 time per session so as not to annoy
GUIUtil.showDaoNeedsResyncPopup(navigation);
}
} else {
bsqInfo.set(Res.get("mainView.footer.bsqInfo.synchronizing"));
}
} else {
bsqInfo.set(Res.get("mainView.footer.bsqInfo.synchronizing"));
}
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener // DaoStateListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -110,22 +100,37 @@ public class DaoPresentation implements DaoStateListener {
onUpdateAnyChainHeight(); onUpdateAnyChainHeight();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Public // DaoStateMonitoringService.Listener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public BooleanProperty getShowDaoUpdatesNotification() { @Override
return showNotification; public void onDaoStateHashesChanged() {
if (!daoStateService.isParseBlockChainComplete()) {
return;
} }
public void setup() { if (daoStateMonitoringService.isInConflictWithSeedNode() ||
// devs enable this when a news badge is required daoStateMonitoringService.isDaoStateBlockChainNotConnecting()) {
//showNotification.set(DevEnv.isDaoActivated() && preferences.showAgain(DAO_NEWS)); new Popup().warning(Res.get("popup.warning.daoNeedsResync"))
showNotification.set(false); .actionButtonTextWithGoTo("navigation.dao.networkMonitor")
.onAction(() -> navigation.navigateTo(MainView.class, DaoView.class, MonitorView.class, DaoStateMonitorView.class))
.show();
}
}
this.btcWalletService.getChainHeightProperty().addListener(walletChainHeightListener);
daoStateService.addDaoStateListener(this);
onUpdateAnyChainHeight(); ///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void onUpdateAnyChainHeight() {
int bsqWalletChainHeight = bsqWalletService.getBestChainHeight();
int daoStateChainHeight = daoStateService.getChainHeight();
boolean chainHeightsInSync = bsqWalletChainHeight > 0 && bsqWalletChainHeight == daoStateChainHeight;
boolean isDaoStateReady = chainHeightsInSync && daoStateService.isParseBlockChainComplete();
daoStateSyncProgress.set(isDaoStateReady ? 0 : -1);
daoStateInfo.set(isDaoStateReady ? "" : Res.get("mainView.footer.bsqInfo.synchronizing"));
} }
} }

View file

@ -27,9 +27,6 @@ import bisq.desktop.components.indicator.TxConfidenceIndicator;
import bisq.desktop.main.MainView; import bisq.desktop.main.MainView;
import bisq.desktop.main.account.AccountView; import bisq.desktop.main.account.AccountView;
import bisq.desktop.main.account.content.fiataccounts.FiatAccountsView; import bisq.desktop.main.account.content.fiataccounts.FiatAccountsView;
import bisq.desktop.main.dao.DaoView;
import bisq.desktop.main.dao.monitor.MonitorView;
import bisq.desktop.main.dao.monitor.daostate.DaoStateMonitorView;
import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.popups.Popup;
import bisq.core.account.witness.AccountAgeWitness; import bisq.core.account.witness.AccountAgeWitness;
@ -806,19 +803,6 @@ public class GUIUtil {
return false; return false;
} }
public static void showDaoNeedsResyncPopup(Navigation navigation) {
String key = "showDaoNeedsResyncPopup";
if (DontShowAgainLookup.showAgain(key)) {
UserThread.runAfter(() -> new Popup().warning(Res.get("popup.warning.daoNeedsResync"))
.dontShowAgainId(key)
.actionButtonTextWithGoTo("navigation.dao.networkMonitor")
.onAction(() -> {
navigation.navigateTo(MainView.class, DaoView.class, MonitorView.class, DaoStateMonitorView.class);
})
.show(), 5, TimeUnit.SECONDS);
}
}
public static boolean isReadyForTxBroadcastOrShowPopup(P2PService p2PService, WalletsSetup walletsSetup) { public static boolean isReadyForTxBroadcastOrShowPopup(P2PService p2PService, WalletsSetup walletsSetup) {
if (!GUIUtil.isBootstrappedOrShowPopup(p2PService)) { if (!GUIUtil.isBootstrappedOrShowPopup(p2PService)) {
return false; return false;