From 0434ba52b6674c5182eb998585ce9535409f92c0 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 14:36:28 -0500 Subject: [PATCH 01/13] Refactor dependency structure to enable adding dispute managers If we would add DisputeManager to previous structure it would cause a circular dependency error from guice. We change dependency structure so that TradeManager does not know XmrTxProofService but XmrTxProofService gets an instance of TradeManager. It makes code cleaner in total as well as responsibility is better defined. Next commit will contain the DisputeManager addition. --- .../java/bisq/core/app/BisqExecutable.java | 2 + .../main/java/bisq/core/app/BisqSetup.java | 5 + .../java/bisq/core/trade/TradeManager.java | 44 +------ ...CounterCurrencyTransferStartedMessage.java | 6 - .../trade/txproof/AssetTxProofService.java | 12 +- .../trade/txproof/xmr/XmrTxProofService.java | 110 +++++++++++------- .../pendingtrades/PendingTradesDataModel.java | 2 +- 7 files changed, 83 insertions(+), 98 deletions(-) diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 2cb4760f386..2fe5be9ee0c 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -26,6 +26,7 @@ import bisq.core.setup.CoreSetup; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; import bisq.core.trade.TradeManager; +import bisq.core.trade.txproof.xmr.XmrTxProofService; import bisq.network.p2p.P2PService; @@ -221,6 +222,7 @@ public void gracefulShutDown(ResultHandler resultHandler) { try { injector.getInstance(ArbitratorManager.class).shutDown(); injector.getInstance(TradeManager.class).shutDown(); + injector.getInstance(XmrTxProofService.class).shutDown(); injector.getInstance(DaoSetup.class).shutDown(); injector.getInstance(AvoidStandbyModeService.class).shutDown(); injector.getInstance(OpenOfferManager.class).shutDown(() -> { diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 9440a98559b..2bafa44b858 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -59,6 +59,7 @@ import bisq.core.trade.TradeManager; import bisq.core.trade.TradeTxException; import bisq.core.trade.statistics.TradeStatisticsManager; +import bisq.core.trade.txproof.xmr.XmrTxProofService; import bisq.core.user.Preferences; import bisq.core.user.User; import bisq.core.util.FormattingUtils; @@ -167,6 +168,7 @@ default void onRequestWalletPassword() { private final PrivateNotificationManager privateNotificationManager; private final FilterManager filterManager; private final TradeStatisticsManager tradeStatisticsManager; + private final XmrTxProofService xmrTxProofService; private final ClockWatcher clockWatcher; private final FeeService feeService; private final DaoSetup daoSetup; @@ -263,6 +265,7 @@ public BisqSetup(P2PNetworkSetup p2PNetworkSetup, PrivateNotificationManager privateNotificationManager, FilterManager filterManager, TradeStatisticsManager tradeStatisticsManager, + XmrTxProofService xmrTxProofService, ClockWatcher clockWatcher, FeeService feeService, DaoSetup daoSetup, @@ -308,6 +311,7 @@ public BisqSetup(P2PNetworkSetup p2PNetworkSetup, this.privateNotificationManager = privateNotificationManager; this.filterManager = filterManager; this.tradeStatisticsManager = tradeStatisticsManager; + this.xmrTxProofService = xmrTxProofService; this.clockWatcher = clockWatcher; this.feeService = feeService; this.daoSetup = daoSetup; @@ -686,6 +690,7 @@ private void initDomainServices() { traderChatManager.onAllServicesInitialized(); tradeManager.onAllServicesInitialized(); + xmrTxProofService.onAllServicesInitialized(); if (walletsSetup.downloadPercentageProperty().get() == 1) { checkForLockedUpFunds(); diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index 8d3c2d738ca..66962f10d68 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -46,8 +46,6 @@ import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.statistics.ReferralIdService; import bisq.core.trade.statistics.TradeStatisticsManager; -import bisq.core.trade.txproof.AssetTxProofResult; -import bisq.core.trade.txproof.xmr.XmrTxProofService; import bisq.core.user.User; import bisq.core.util.Validator; @@ -59,7 +57,6 @@ import bisq.network.p2p.SendMailboxMessageListener; import bisq.common.ClockWatcher; -import bisq.common.UserThread; import bisq.common.config.Config; import bisq.common.crypto.KeyRing; import bisq.common.handlers.ErrorMessageHandler; @@ -111,8 +108,6 @@ import javax.annotation.Nullable; -import static com.google.common.base.Preconditions.checkArgument; - public class TradeManager implements PersistedDataHost { private static final Logger log = LoggerFactory.getLogger(TradeManager.class); @@ -132,7 +127,6 @@ public class TradeManager implements PersistedDataHost { private final ReferralIdService referralIdService; private final AccountAgeWitnessService accountAgeWitnessService; @Getter - private final XmrTxProofService xmrTxProofService; private final ArbitratorManager arbitratorManager; private final MediatorManager mediatorManager; private final RefundAgentManager refundAgentManager; @@ -174,7 +168,6 @@ public TradeManager(User user, TradeStatisticsManager tradeStatisticsManager, ReferralIdService referralIdService, AccountAgeWitnessService accountAgeWitnessService, - XmrTxProofService xmrTxProofService, ArbitratorManager arbitratorManager, MediatorManager mediatorManager, RefundAgentManager refundAgentManager, @@ -197,7 +190,6 @@ public TradeManager(User user, this.tradeStatisticsManager = tradeStatisticsManager; this.referralIdService = referralIdService; this.accountAgeWitnessService = accountAgeWitnessService; - this.xmrTxProofService = xmrTxProofService; this.arbitratorManager = arbitratorManager; this.mediatorManager = mediatorManager; this.refundAgentManager = refundAgentManager; @@ -286,7 +278,7 @@ public void onUpdatedDataReceived() { } public void shutDown() { - xmrTxProofService.shutDown(); + // Do nothing here } private void initPendingTrades() { @@ -329,13 +321,6 @@ private void initPendingTrades() { addTradeToFailedTradesList.add(trade); } } - - if (trade.getState() == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) { - // This state can be only appear at a SellerTrade - checkArgument(trade instanceof SellerTrade, "Trade must be instance of SellerTrade"); - // We delay a bit as at startup lots of stuff is happening - UserThread.runAfter(() -> maybeStartXmrTxProofServices((SellerTrade) trade), 1); - } } ); @@ -360,31 +345,10 @@ private void initPendingTrades() { pendingTradesInitialized.set(true); } - public void maybeStartXmrTxProofServices(SellerTrade sellerTrade) { - xmrTxProofService.maybeStartRequests(sellerTrade, tradableList.getList(), - assetTxProofResult -> { - if (assetTxProofResult == AssetTxProofResult.COMPLETED) { - log.info("###########################################################################################"); - log.info("We auto-confirm trade {} as our all our services for the tx proof completed successfully", sellerTrade.getShortId()); - log.info("###########################################################################################"); - autoConfirmFiatPaymentReceived(sellerTrade); - } - }, - (errorMessage, throwable) -> { - log.error(errorMessage); - }); - } - - private void autoConfirmFiatPaymentReceived(SellerTrade sellerTrade) { - onFiatPaymentReceived(sellerTrade, - () -> { - }, errorMessage -> { - }); - } - public void onFiatPaymentReceived(SellerTrade sellerTrade, - ResultHandler resultHandler, - ErrorMessageHandler errorMessageHandler) { + public void onUserConfirmedFiatPaymentReceived(SellerTrade sellerTrade, + ResultHandler resultHandler, + ErrorMessageHandler errorMessageHandler) { sellerTrade.onFiatPaymentReceived(resultHandler, errorMessageHandler); //TODO move to trade protocol task diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerProcessCounterCurrencyTransferStartedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerProcessCounterCurrencyTransferStartedMessage.java index 29ca5bc63ea..ee996ffd9c1 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerProcessCounterCurrencyTransferStartedMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerProcessCounterCurrencyTransferStartedMessage.java @@ -17,7 +17,6 @@ package bisq.core.trade.protocol.tasks.seller; -import bisq.core.trade.SellerTrade; import bisq.core.trade.Trade; import bisq.core.trade.messages.CounterCurrencyTransferStartedMessage; import bisq.core.trade.protocol.tasks.TradeTask; @@ -27,7 +26,6 @@ import lombok.extern.slf4j.Slf4j; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @Slf4j @@ -62,10 +60,6 @@ protected void run() { trade.setCounterCurrencyExtraData(counterCurrencyExtraData); } - checkArgument(trade instanceof SellerTrade, "Trade must be instance of SellerTrade"); - // We return early in the service if its not XMR. We prefer to not have additional checks outside... - processModel.getTradeManager().maybeStartXmrTxProofServices((SellerTrade) trade); - processModel.removeMailboxMessageAfterProcessing(trade); trade.setState(Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG); diff --git a/core/src/main/java/bisq/core/trade/txproof/AssetTxProofService.java b/core/src/main/java/bisq/core/trade/txproof/AssetTxProofService.java index 56e2a7462e8..c5c8476a41b 100644 --- a/core/src/main/java/bisq/core/trade/txproof/AssetTxProofService.java +++ b/core/src/main/java/bisq/core/trade/txproof/AssetTxProofService.java @@ -17,18 +17,8 @@ package bisq.core.trade.txproof; -import bisq.core.trade.Trade; - -import bisq.common.handlers.FaultHandler; - -import java.util.List; -import java.util.function.Consumer; - public interface AssetTxProofService { - void maybeStartRequests(Trade trade, - List activeTrades, - Consumer resultHandler, - FaultHandler faultHandler); + void onAllServicesInitialized(); void shutDown(); } diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java index bd63e1df114..5590c56c5ef 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java @@ -23,6 +23,7 @@ import bisq.core.payment.payload.AssetsAccountPayload; import bisq.core.trade.SellerTrade; import bisq.core.trade.Trade; +import bisq.core.trade.TradeManager; import bisq.core.trade.closed.ClosedTradableManager; import bisq.core.trade.failed.FailedTradesManager; import bisq.core.trade.txproof.AssetTxProofHttpClient; @@ -34,16 +35,16 @@ import bisq.network.p2p.P2PService; import bisq.common.app.DevEnv; -import bisq.common.handlers.FaultHandler; import javax.inject.Inject; import javax.inject.Singleton; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; + import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.function.Consumer; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; @@ -59,12 +60,14 @@ public class XmrTxProofService implements AssetTxProofService { private final FilterManager filterManager; private final Preferences preferences; + private final TradeManager tradeManager; private final ClosedTradableManager closedTradableManager; private final FailedTradesManager failedTradesManager; private final P2PService p2PService; private final WalletsSetup walletsSetup; private final AssetTxProofHttpClient httpClient; private final Map servicesByTradeId = new HashMap<>(); + private AutoConfirmSettings autoConfirmSettings; /////////////////////////////////////////////////////////////////////////////////////////// @@ -75,6 +78,7 @@ public class XmrTxProofService implements AssetTxProofService { @Inject public XmrTxProofService(FilterManager filterManager, Preferences preferences, + TradeManager tradeManager, ClosedTradableManager closedTradableManager, FailedTradesManager failedTradesManager, P2PService p2PService, @@ -82,11 +86,25 @@ public XmrTxProofService(FilterManager filterManager, AssetTxProofHttpClient httpClient) { this.filterManager = filterManager; this.preferences = preferences; + this.tradeManager = tradeManager; this.closedTradableManager = closedTradableManager; this.failedTradesManager = failedTradesManager; this.p2PService = p2PService; this.walletsSetup = walletsSetup; this.httpClient = httpClient; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void onAllServicesInitialized() { + if (!preferences.findAutoConfirmSettings("XMR").isPresent()) { + log.error("AutoConfirmSettings is not present"); + } + autoConfirmSettings = preferences.findAutoConfirmSettings("XMR").get(); filterManager.filterProperty().addListener((observable, oldValue, newValue) -> { if (isAutoConfDisabledByFilter()) { @@ -96,22 +114,39 @@ public XmrTxProofService(FilterManager filterManager, shutDown(); } }); + + ObservableList tradableList = tradeManager.getTradableList(); + tradableList.addListener((ListChangeListener) c -> { + c.next(); + if (c.wasAdded()) { + processTrades(c.getAddedSubList()); + } + }); + processTrades(tradableList); + } + + @Override + public void shutDown() { + servicesByTradeId.values().forEach(XmrTxProofRequestsPerTrade::terminate); + servicesByTradeId.clear(); } /////////////////////////////////////////////////////////////////////////////////////////// - // API + // Private /////////////////////////////////////////////////////////////////////////////////////////// - @Override - public void maybeStartRequests(Trade trade, - List activeTrades, - Consumer resultHandler, - FaultHandler faultHandler) { - if (!isXmrBuyer(trade)) { - return; - } + private void processTrades(List trades) { + trades.stream() + .filter(trade -> trade instanceof SellerTrade) + .map(trade -> (SellerTrade) trade) + .filter(this::isExpectedTradeState) + .filter(this::isXmrBuyer) + .filter(trade -> networkAndWalletReady()) + .forEach(this::processTrade); + } + private void processTrade(SellerTrade trade) { String txId = trade.getCounterCurrencyTxId(); String txHash = trade.getCounterCurrencyExtraData(); if (is32BitHexStringInValid(txId) || is32BitHexStringInValid(txHash)) { @@ -119,25 +154,13 @@ public void maybeStartRequests(Trade trade, return; } - if (!networkAndWalletReady()) { - return; - } - - Optional optionalAutoConfirmSettings = preferences.findAutoConfirmSettings("XMR"); - if (!optionalAutoConfirmSettings.isPresent()) { - // Not expected - log.error("autoConfirmSettings is not present"); - return; - } - AutoConfirmSettings autoConfirmSettings = optionalAutoConfirmSettings.get(); - if (isAutoConfDisabledByFilter()) { trade.setAssetTxProofResult(AssetTxProofResult.FEATURE_DISABLED .details(Res.get("portfolio.pending.autoConf.state.filterDisabledFeature"))); return; } - if (wasTxKeyReUsed(trade, activeTrades)) { + if (wasTxKeyReUsed(trade, tradeManager.getTradableList())) { trade.setAssetTxProofResult(AssetTxProofResult.INVALID_DATA .details(Res.get("portfolio.pending.autoConf.state.xmr.txKeyReused"))); return; @@ -151,19 +174,22 @@ public void maybeStartRequests(Trade trade, assetTxProofResult -> { trade.setAssetTxProofResult(assetTxProofResult); + if (assetTxProofResult == AssetTxProofResult.COMPLETED) { + log.info("###########################################################################################"); + log.info("We auto-confirm trade {} as our all our services for the tx proof completed successfully", trade.getShortId()); + log.info("###########################################################################################"); + trade.onFiatPaymentReceived(() -> { + }, errorMessage -> { + }); + } + if (assetTxProofResult.isTerminal()) { servicesByTradeId.remove(trade.getId()); } - - resultHandler.accept(assetTxProofResult); }, - faultHandler); - } - - @Override - public void shutDown() { - servicesByTradeId.values().forEach(XmrTxProofRequestsPerTrade::terminate); - servicesByTradeId.clear(); + (errorMessage, throwable) -> { + log.error(errorMessage); + }); } @@ -171,6 +197,10 @@ public void shutDown() { // Validation /////////////////////////////////////////////////////////////////////////////////////////// + private boolean isExpectedTradeState(SellerTrade sellerTrade) { + return sellerTrade.getState() == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG; + } + private boolean isXmrBuyer(Trade trade) { if (!checkNotNull(trade.getOffer()).getCurrencyCode().equals("XMR")) { return false; @@ -183,6 +213,12 @@ private boolean isXmrBuyer(Trade trade) { return checkNotNull(trade.getContract()).getSellerPaymentAccountPayload() instanceof AssetsAccountPayload; } + private boolean networkAndWalletReady() { + return p2PService.isBootstrapped() && + walletsSetup.isDownloadComplete() && + walletsSetup.hasSufficientPeersForBroadcast(); + } + private boolean is32BitHexStringInValid(String hexString) { if (hexString == null || hexString.isEmpty() || !hexString.matches("[a-fA-F0-9]{64}")) { log.warn("Invalid hexString: {}", hexString); @@ -192,12 +228,6 @@ private boolean is32BitHexStringInValid(String hexString) { return false; } - private boolean networkAndWalletReady() { - return p2PService.isBootstrapped() && - walletsSetup.isDownloadComplete() && - walletsSetup.hasSufficientPeersForBroadcast(); - } - private boolean isAutoConfDisabledByFilter() { return filterManager.getFilter() != null && filterManager.getFilter().isDisableAutoConf(); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index 19e2ec7e1ad..fb03b413f8a 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -194,7 +194,7 @@ public void onPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler er public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { checkNotNull(getTrade(), "trade must not be null"); checkArgument(getTrade() instanceof SellerTrade, "Trade must be instance of SellerTrade"); - tradeManager.onFiatPaymentReceived((SellerTrade) getTrade(), resultHandler, errorMessageHandler); + tradeManager.onUserConfirmedFiatPaymentReceived((SellerTrade) getTrade(), resultHandler, errorMessageHandler); } public void onWithdrawRequest(String toAddress, From e74edc254ad5d6b139298a66fb78ee6e114006ce Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 15:38:47 -0500 Subject: [PATCH 02/13] Add check if dispute is open If a mediation or arbitration dispute has been opened we do not use the auto-confirm feature. --- .../trade/txproof/AssetTxProofResult.java | 1 + .../xmr/XmrTxProofRequestsPerTrade.java | 119 +++++++++++++++--- .../trade/txproof/xmr/XmrTxProofService.java | 88 +++++++++---- .../resources/i18n/displayStrings.properties | 2 + .../main/java/bisq/desktop/util/GUIUtil.java | 1 + 5 files changed, 168 insertions(+), 43 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java b/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java index 0fc61d7cfd0..0f6ba959709 100644 --- a/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java +++ b/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java @@ -26,6 +26,7 @@ public enum AssetTxProofResult { TRADE_LIMIT_EXCEEDED, INVALID_DATA, // Peer provided invalid data. Might be a scam attempt (e.g. txKey reused) PAYOUT_TX_ALREADY_PUBLISHED, + DISPUTE_OPENED, REQUESTS_STARTED(false), PENDING(false), diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java index f49919232b5..774423b3292 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java @@ -18,6 +18,9 @@ package bisq.core.trade.txproof.xmr; import bisq.core.locale.Res; +import bisq.core.support.dispute.Dispute; +import bisq.core.support.dispute.mediation.MediationManager; +import bisq.core.support.dispute.refund.RefundManager; import bisq.core.trade.Trade; import bisq.core.trade.txproof.AssetTxProofHttpClient; import bisq.core.trade.txproof.AssetTxProofRequestsPerTrade; @@ -30,6 +33,9 @@ import javafx.beans.value.ChangeListener; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; + import java.util.HashSet; import java.util.List; import java.util.Set; @@ -46,6 +52,8 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade { @Getter private final Trade trade; private final AutoConfirmSettings autoConfirmSettings; + private final MediationManager mediationManager; + private final RefundManager refundManager; private final AssetTxProofHttpClient httpClient; private int numRequiredSuccessResults; @@ -54,6 +62,7 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade { private int numSuccessResults; private ChangeListener tradeStateListener; private AutoConfirmSettings.Listener autoConfirmSettingsListener; + private ListChangeListener mediationListener, refundListener; /////////////////////////////////////////////////////////////////////////////////////////// @@ -62,10 +71,14 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade { XmrTxProofRequestsPerTrade(AssetTxProofHttpClient httpClient, Trade trade, - AutoConfirmSettings autoConfirmSettings) { + AutoConfirmSettings autoConfirmSettings, + MediationManager mediationManager, + RefundManager refundManager) { this.httpClient = httpClient; this.trade = trade; this.autoConfirmSettings = autoConfirmSettings; + this.mediationManager = mediationManager; + this.refundManager = refundManager; } @@ -75,44 +88,56 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade { @Override public void requestFromAllServices(Consumer resultHandler, FaultHandler faultHandler) { - // We set serviceAddresses at request time. If user changes AutoConfirmSettings after request has started - // it will have no impact on serviceAddresses and numRequiredSuccessResults. - // Thought numRequiredConfirmations can be changed during request process and will be read from - // autoConfirmSettings at result parsing. - List serviceAddresses = autoConfirmSettings.getServiceAddresses(); - numRequiredSuccessResults = serviceAddresses.size(); - + // isTradeAmountAboveLimit if (isTradeAmountAboveLimit(trade)) { callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.TRADE_LIMIT_EXCEEDED); return; } + // isPayoutPublished if (trade.isPayoutPublished()) { callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.PAYOUT_TX_ALREADY_PUBLISHED); return; } + // IsEnabled() // We will stop all our services if the user changes the enable state in the AutoConfirmSettings - autoConfirmSettingsListener = () -> { - if (!autoConfirmSettings.isEnabled()) { - callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.FEATURE_DISABLED); - } - }; - autoConfirmSettings.addListener(autoConfirmSettingsListener); if (!autoConfirmSettings.isEnabled()) { callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.FEATURE_DISABLED); return; } + addSettingsListener(resultHandler); - tradeStateListener = (observable, oldValue, newValue) -> { - if (trade.isPayoutPublished()) { - callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.PAYOUT_TX_ALREADY_PUBLISHED); - } - }; - trade.stateProperty().addListener(tradeStateListener); + // TradeState + setupTradeStateListener(resultHandler); + // We checked initially for current trade state so no need to check again here + + // Check if mediation dispute and add listener + ObservableList mediationDisputes = mediationManager.getDisputesAsObservableList(); + if (isDisputed(mediationDisputes)) { + callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.DISPUTE_OPENED); + return; + } + setupMediationListener(resultHandler, mediationDisputes); + + // Check if arbitration dispute and add listener + ObservableList refundDisputes = refundManager.getDisputesAsObservableList(); + if (isDisputed(refundDisputes)) { + callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.DISPUTE_OPENED); + return; + } + setupArbitrationListener(resultHandler, refundDisputes); + // All good so we start callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.REQUESTS_STARTED); + // We set serviceAddresses at request time. If user changes AutoConfirmSettings after request has started + // it will have no impact on serviceAddresses and numRequiredSuccessResults. + // Thought numRequiredConfirmations can be changed during request process and will be read from + // autoConfirmSettings at result parsing. + List serviceAddresses = autoConfirmSettings.getServiceAddresses(); + numRequiredSuccessResults = serviceAddresses.size(); + for (String serviceAddress : serviceAddresses) { XmrTxProofModel model = new XmrTxProofModel(trade, serviceAddress, autoConfirmSettings); XmrTxProofRequest request = new XmrTxProofRequest(httpClient, model); @@ -174,16 +199,66 @@ public void requestFromAllServices(Consumer resultHandler, F } } + protected void addSettingsListener(Consumer resultHandler) { + autoConfirmSettingsListener = () -> { + if (!autoConfirmSettings.isEnabled()) { + callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.FEATURE_DISABLED); + } + }; + autoConfirmSettings.addListener(autoConfirmSettingsListener); + } + + protected void setupTradeStateListener(Consumer resultHandler) { + tradeStateListener = (observable, oldValue, newValue) -> { + if (trade.isPayoutPublished()) { + callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.PAYOUT_TX_ALREADY_PUBLISHED); + } + }; + trade.stateProperty().addListener(tradeStateListener); + } + + protected void setupArbitrationListener(Consumer resultHandler, + ObservableList refundDisputes) { + refundListener = c -> { + c.next(); + if (c.wasAdded() && isDisputed(c.getAddedSubList())) { + callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.DISPUTE_OPENED); + } + }; + refundDisputes.addListener(refundListener); + } + + protected void setupMediationListener(Consumer resultHandler, + ObservableList mediationDisputes) { + mediationListener = c -> { + c.next(); + if (c.wasAdded() && isDisputed(c.getAddedSubList())) { + callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.DISPUTE_OPENED); + } + }; + mediationDisputes.addListener(mediationListener); + } + @Override public void terminate() { requests.forEach(XmrTxProofRequest::terminate); requests.clear(); + if (tradeStateListener != null) { trade.stateProperty().removeListener(tradeStateListener); } + if (autoConfirmSettingsListener != null) { autoConfirmSettings.removeListener(autoConfirmSettingsListener); } + + if (mediationListener != null) { + mediationManager.getDisputesAsObservableList().removeListener(mediationListener); + } + + if (refundListener != null) { + refundManager.getDisputesAsObservableList().removeListener(refundListener); + } } @@ -235,4 +310,8 @@ private boolean isTradeAmountAboveLimit(Trade trade) { } return false; } + + private boolean isDisputed(List disputes) { + return disputes.stream().anyMatch(e -> e.getTradeId().equals(trade.getId())); + } } diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java index 5590c56c5ef..86920ebf59e 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java @@ -20,7 +20,8 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.filter.FilterManager; import bisq.core.locale.Res; -import bisq.core.payment.payload.AssetsAccountPayload; +import bisq.core.support.dispute.mediation.MediationManager; +import bisq.core.support.dispute.refund.RefundManager; import bisq.core.trade.SellerTrade; import bisq.core.trade.Trade; import bisq.core.trade.TradeManager; @@ -34,11 +35,14 @@ import bisq.network.p2p.P2PService; +import bisq.common.UserThread; import bisq.common.app.DevEnv; import javax.inject.Inject; import javax.inject.Singleton; +import javafx.beans.value.ChangeListener; + import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; @@ -63,11 +67,14 @@ public class XmrTxProofService implements AssetTxProofService { private final TradeManager tradeManager; private final ClosedTradableManager closedTradableManager; private final FailedTradesManager failedTradesManager; + private final MediationManager mediationManager; + private final RefundManager refundManager; private final P2PService p2PService; private final WalletsSetup walletsSetup; private final AssetTxProofHttpClient httpClient; private final Map servicesByTradeId = new HashMap<>(); private AutoConfirmSettings autoConfirmSettings; + private Map> tradeStateListenerMap = new HashMap<>(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -81,6 +88,8 @@ public XmrTxProofService(FilterManager filterManager, TradeManager tradeManager, ClosedTradableManager closedTradableManager, FailedTradesManager failedTradesManager, + MediationManager mediationManager, + RefundManager refundManager, P2PService p2PService, WalletsSetup walletsSetup, AssetTxProofHttpClient httpClient) { @@ -89,6 +98,8 @@ public XmrTxProofService(FilterManager filterManager, this.tradeManager = tradeManager; this.closedTradableManager = closedTradableManager; this.failedTradesManager = failedTradesManager; + this.mediationManager = mediationManager; + this.refundManager = refundManager; this.p2PService = p2PService; this.walletsSetup = walletsSetup; this.httpClient = httpClient; @@ -122,7 +133,9 @@ public void onAllServicesInitialized() { processTrades(c.getAddedSubList()); } }); - processTrades(tradableList); + // Network is usually not ready at onAllServicesInitialized + //TODO we need to add listeners + UserThread.runAfter(() -> processTrades(tradableList), 1); } @Override @@ -140,13 +153,49 @@ private void processTrades(List trades) { trades.stream() .filter(trade -> trade instanceof SellerTrade) .map(trade -> (SellerTrade) trade) - .filter(this::isExpectedTradeState) + .filter(this::isXmrTrade) + .forEach(this::processCandidate); + + /* trades.stream() + .filter(trade -> trade instanceof SellerTrade) + .map(trade -> (SellerTrade) trade) .filter(this::isXmrBuyer) + .filter(this::isExpectedTradeState) .filter(trade -> networkAndWalletReady()) .forEach(this::processTrade); + */ + } + + // Basic requirements are fulfilled. + // We might register a state listener to process further if expected state appears + private void processCandidate(SellerTrade trade) { + if (trade.isFiatReceived()) { + // We are done already. + return; + } + + if (trade.getState() == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) { + processTrade(trade); + } else { + // We are expecting SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG in the future, so listen to changes + ChangeListener tradeStateListener = (observable, oldValue, newValue) -> { + if (newValue == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) { + processTrade(trade); + } + }; + tradeStateListenerMap.put(trade.getId(), tradeStateListener); + trade.stateProperty().addListener(tradeStateListener); + } } private void processTrade(SellerTrade trade) { + tradeStateListenerMap.remove(trade.getId()); + + if (!networkAndWalletReady()) { + //TODO handle listeners + return; + } + String txId = trade.getCounterCurrencyTxId(); String txHash = trade.getCounterCurrencyExtraData(); if (is32BitHexStringInValid(txId) || is32BitHexStringInValid(txHash)) { @@ -168,7 +217,9 @@ private void processTrade(SellerTrade trade) { XmrTxProofRequestsPerTrade service = new XmrTxProofRequestsPerTrade(httpClient, trade, - autoConfirmSettings); + autoConfirmSettings, + mediationManager, + refundManager); servicesByTradeId.put(trade.getId(), service); service.requestFromAllServices( assetTxProofResult -> { @@ -178,9 +229,10 @@ private void processTrade(SellerTrade trade) { log.info("###########################################################################################"); log.info("We auto-confirm trade {} as our all our services for the tx proof completed successfully", trade.getShortId()); log.info("###########################################################################################"); - trade.onFiatPaymentReceived(() -> { + + /* trade.onFiatPaymentReceived(() -> { }, errorMessage -> { - }); + });*/ } if (assetTxProofResult.isTerminal()) { @@ -197,26 +249,16 @@ private void processTrade(SellerTrade trade) { // Validation /////////////////////////////////////////////////////////////////////////////////////////// - private boolean isExpectedTradeState(SellerTrade sellerTrade) { - return sellerTrade.getState() == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG; - } - - private boolean isXmrBuyer(Trade trade) { - if (!checkNotNull(trade.getOffer()).getCurrencyCode().equals("XMR")) { - return false; - } - - if (!(trade instanceof SellerTrade)) { - return false; - } - - return checkNotNull(trade.getContract()).getSellerPaymentAccountPayload() instanceof AssetsAccountPayload; + private boolean isXmrTrade(Trade trade) { + return (checkNotNull(trade.getOffer()).getCurrencyCode().equals("XMR")); } private boolean networkAndWalletReady() { - return p2PService.isBootstrapped() && - walletsSetup.isDownloadComplete() && - walletsSetup.hasSufficientPeersForBroadcast(); + //TODO We need to check if false and add listeners + boolean bootstrapped = p2PService.isBootstrapped(); + boolean downloadComplete = walletsSetup.isDownloadComplete(); + boolean hasSufficientPeersForBroadcast = walletsSetup.hasSufficientPeersForBroadcast(); + return bootstrapped && downloadComplete && hasSufficientPeersForBroadcast; } private boolean is32BitHexStringInValid(String hexString) { diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 1474fb0628e..4bf4b53acf1 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -588,6 +588,8 @@ portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} # suppress inspection "UnusedProperty" portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. # suppress inspection "UnusedProperty" +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Service stopped. +# suppress inspection "UnusedProperty" portfolio.pending.autoConf.state.REQUESTS_STARTED=Proof requests started # suppress inspection "UnusedProperty" portfolio.pending.autoConf.state.PENDING=Service results: {0}/{1}; {2} diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index 3fb3ad6b27f..375347939a2 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -1176,6 +1176,7 @@ public static String getProofResultAsString(@Nullable AssetTxProofResult result) case INVALID_DATA: return Res.get(key, result.getDetails()); case PAYOUT_TX_ALREADY_PUBLISHED: + case DISPUTE_OPENED: case REQUESTS_STARTED: return Res.get(key); case PENDING: From 8e9e87ce6e30febd4aaad30acf6d73ce78ca15e0 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 16:39:14 -0500 Subject: [PATCH 03/13] Enable onFiatPaymentReceived again (was for dev testing) - Remove commented out code - do isFiatReceived in stream filter --- .../trade/txproof/xmr/XmrTxProofService.java | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java index 86920ebf59e..f74c2034bb3 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java @@ -154,26 +154,13 @@ private void processTrades(List trades) { .filter(trade -> trade instanceof SellerTrade) .map(trade -> (SellerTrade) trade) .filter(this::isXmrTrade) - .forEach(this::processCandidate); - - /* trades.stream() - .filter(trade -> trade instanceof SellerTrade) - .map(trade -> (SellerTrade) trade) - .filter(this::isXmrBuyer) - .filter(this::isExpectedTradeState) - .filter(trade -> networkAndWalletReady()) - .forEach(this::processTrade); - */ + .filter(trade -> !trade.isFiatReceived()) + .forEach(this::processOrAddListener); } // Basic requirements are fulfilled. // We might register a state listener to process further if expected state appears - private void processCandidate(SellerTrade trade) { - if (trade.isFiatReceived()) { - // We are done already. - return; - } - + private void processOrAddListener(SellerTrade trade) { if (trade.getState() == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) { processTrade(trade); } else { @@ -230,9 +217,9 @@ private void processTrade(SellerTrade trade) { log.info("We auto-confirm trade {} as our all our services for the tx proof completed successfully", trade.getShortId()); log.info("###########################################################################################"); - /* trade.onFiatPaymentReceived(() -> { + trade.onFiatPaymentReceived(() -> { }, errorMessage -> { - });*/ + }); } if (assetTxProofResult.isTerminal()) { From 99dca96b143a05b7258326d49f27cdbaff260d09 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 17:32:03 -0500 Subject: [PATCH 04/13] Ensure that p2p network and wallet are ready --- .../trade/txproof/xmr/XmrTxProofService.java | 154 +++++++++++++----- 1 file changed, 117 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java index f74c2034bb3..8585af47aa0 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java @@ -33,14 +33,19 @@ import bisq.core.user.AutoConfirmSettings; import bisq.core.user.Preferences; +import bisq.network.p2p.BootstrapListener; import bisq.network.p2p.P2PService; -import bisq.common.UserThread; import bisq.common.app.DevEnv; import javax.inject.Inject; import javax.inject.Singleton; +import org.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.monadic.MonadicBinding; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.collections.ListChangeListener; @@ -75,6 +80,9 @@ public class XmrTxProofService implements AssetTxProofService { private final Map servicesByTradeId = new HashMap<>(); private AutoConfirmSettings autoConfirmSettings; private Map> tradeStateListenerMap = new HashMap<>(); + private ChangeListener btcPeersListener, btcBlockListener; + private BootstrapListener bootstrapListener; + private MonadicBinding p2pNetworkAndWalletReady; /////////////////////////////////////////////////////////////////////////////////////////// @@ -112,11 +120,41 @@ public XmrTxProofService(FilterManager filterManager, @Override public void onAllServicesInitialized() { + // As we might trigger the payout tx we want to be sure that we are well connected to the Bitcoin network. + // onAllServicesInitialized is called once we have received the initial data but we want to have our + // hidden service published and upDatedDataResponse received before we start. + p2pNetworkAndWalletReady = EasyBind.combine(isP2pBootstrapped(), hasSufficientBtcPeers(), isBtcBlockDownloadComplete(), + (isP2pBootstrapped, hasSufficientBtcPeers, isBtcBlockDownloadComplete) -> { + log.info("isP2pBootstrapped={}, hasSufficientBtcPeers={} isBtcBlockDownloadComplete={}", + isP2pBootstrapped, hasSufficientBtcPeers, isBtcBlockDownloadComplete); + return isP2pBootstrapped && hasSufficientBtcPeers && isBtcBlockDownloadComplete; + }); + + p2pNetworkAndWalletReady.subscribe((observable, oldValue, newValue) -> { + if (newValue) { + onP2pNetworkAndWalletReady(); + } + }); + } + + @Override + public void shutDown() { + servicesByTradeId.values().forEach(XmrTxProofRequestsPerTrade::terminate); + servicesByTradeId.clear(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void onP2pNetworkAndWalletReady() { if (!preferences.findAutoConfirmSettings("XMR").isPresent()) { log.error("AutoConfirmSettings is not present"); } autoConfirmSettings = preferences.findAutoConfirmSettings("XMR").get(); + // We register a listener to stop running services. For new trades we check anyway in the trade validation filterManager.filterProperty().addListener((observable, oldValue, newValue) -> { if (isAutoConfDisabledByFilter()) { servicesByTradeId.values().stream().map(XmrTxProofRequestsPerTrade::getTrade).forEach(trade -> @@ -126,6 +164,7 @@ public void onAllServicesInitialized() { } }); + // We listen on new trades ObservableList tradableList = tradeManager.getTradableList(); tradableList.addListener((ListChangeListener) c -> { c.next(); @@ -133,41 +172,35 @@ public void onAllServicesInitialized() { processTrades(c.getAddedSubList()); } }); - // Network is usually not ready at onAllServicesInitialized - //TODO we need to add listeners - UserThread.runAfter(() -> processTrades(tradableList), 1); - } - @Override - public void shutDown() { - servicesByTradeId.values().forEach(XmrTxProofRequestsPerTrade::terminate); - servicesByTradeId.clear(); + // Process existing trades + processTrades(tradableList); } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - private void processTrades(List trades) { trades.stream() .filter(trade -> trade instanceof SellerTrade) .map(trade -> (SellerTrade) trade) .filter(this::isXmrTrade) - .filter(trade -> !trade.isFiatReceived()) - .forEach(this::processOrAddListener); + .filter(trade -> !trade.isFiatReceived()) // Phase name is from the time when it was fiat only. Means counter currency (XMR) received. + .forEach(this::processTradeOrAddListener); } // Basic requirements are fulfilled. - // We might register a state listener to process further if expected state appears - private void processOrAddListener(SellerTrade trade) { - if (trade.getState() == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) { - processTrade(trade); + // We process further if we are in the expected state or register a listener + private void processTradeOrAddListener(SellerTrade trade) { + if (isExpectedTradeState(trade.getState())) { + startRequestsIfValid(trade); } else { - // We are expecting SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG in the future, so listen to changes + // We are expecting SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG in the future, so listen on changes ChangeListener tradeStateListener = (observable, oldValue, newValue) -> { - if (newValue == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) { - processTrade(trade); + if (isExpectedTradeState(newValue)) { + ChangeListener listener = tradeStateListenerMap.remove(trade.getId()); + if (listener != null) { + trade.stateProperty().removeListener(listener); + } + + startRequestsIfValid(trade); } }; tradeStateListenerMap.put(trade.getId(), tradeStateListener); @@ -175,14 +208,7 @@ private void processOrAddListener(SellerTrade trade) { } } - private void processTrade(SellerTrade trade) { - tradeStateListenerMap.remove(trade.getId()); - - if (!networkAndWalletReady()) { - //TODO handle listeners - return; - } - + private void startRequestsIfValid(SellerTrade trade) { String txId = trade.getCounterCurrencyTxId(); String txHash = trade.getCounterCurrencyExtraData(); if (is32BitHexStringInValid(txId) || is32BitHexStringInValid(txHash)) { @@ -202,6 +228,10 @@ private void processTrade(SellerTrade trade) { return; } + startRequests(trade); + } + + private void startRequests(SellerTrade trade) { XmrTxProofRequestsPerTrade service = new XmrTxProofRequestsPerTrade(httpClient, trade, autoConfirmSettings, @@ -232,6 +262,60 @@ private void processTrade(SellerTrade trade) { } + /////////////////////////////////////////////////////////////////////////////////////////// + // Startup checks + /////////////////////////////////////////////////////////////////////////////////////////// + + private BooleanProperty isBtcBlockDownloadComplete() { + BooleanProperty result = new SimpleBooleanProperty(); + if (walletsSetup.isDownloadComplete()) { + result.set(true); + } else { + btcBlockListener = (observable, oldValue, newValue) -> { + if (walletsSetup.isDownloadComplete()) { + walletsSetup.downloadPercentageProperty().removeListener(btcBlockListener); + result.set(true); + } + }; + walletsSetup.downloadPercentageProperty().addListener(btcBlockListener); + } + return result; + } + + private BooleanProperty hasSufficientBtcPeers() { + BooleanProperty result = new SimpleBooleanProperty(); + if (walletsSetup.hasSufficientPeersForBroadcast()) { + result.set(true); + } else { + btcPeersListener = (observable, oldValue, newValue) -> { + if (walletsSetup.hasSufficientPeersForBroadcast()) { + walletsSetup.numPeersProperty().removeListener(btcPeersListener); + result.set(true); + } + }; + walletsSetup.numPeersProperty().addListener(btcPeersListener); + } + return result; + } + + private BooleanProperty isP2pBootstrapped() { + BooleanProperty result = new SimpleBooleanProperty(); + if (p2PService.isBootstrapped()) { + result.set(true); + } else { + bootstrapListener = new BootstrapListener() { + @Override + public void onUpdatedDataReceived() { + p2PService.removeP2PServiceListener(bootstrapListener); + result.set(true); + } + }; + p2PService.addP2PServiceListener(bootstrapListener); + } + return result; + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Validation /////////////////////////////////////////////////////////////////////////////////////////// @@ -240,12 +324,8 @@ private boolean isXmrTrade(Trade trade) { return (checkNotNull(trade.getOffer()).getCurrencyCode().equals("XMR")); } - private boolean networkAndWalletReady() { - //TODO We need to check if false and add listeners - boolean bootstrapped = p2PService.isBootstrapped(); - boolean downloadComplete = walletsSetup.isDownloadComplete(); - boolean hasSufficientPeersForBroadcast = walletsSetup.hasSufficientPeersForBroadcast(); - return bootstrapped && downloadComplete && hasSufficientPeersForBroadcast; + private boolean isExpectedTradeState(Trade.State newValue) { + return newValue == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG; } private boolean is32BitHexStringInValid(String hexString) { From 6a573329112894631b98a2c4fd074aa454c4d1fb Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 18:49:00 -0500 Subject: [PATCH 05/13] Make var local --- .../java/bisq/core/trade/txproof/xmr/XmrTxProofService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java index 8585af47aa0..99d5f670d36 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofService.java @@ -82,7 +82,6 @@ public class XmrTxProofService implements AssetTxProofService { private Map> tradeStateListenerMap = new HashMap<>(); private ChangeListener btcPeersListener, btcBlockListener; private BootstrapListener bootstrapListener; - private MonadicBinding p2pNetworkAndWalletReady; /////////////////////////////////////////////////////////////////////////////////////////// @@ -123,7 +122,7 @@ public void onAllServicesInitialized() { // As we might trigger the payout tx we want to be sure that we are well connected to the Bitcoin network. // onAllServicesInitialized is called once we have received the initial data but we want to have our // hidden service published and upDatedDataResponse received before we start. - p2pNetworkAndWalletReady = EasyBind.combine(isP2pBootstrapped(), hasSufficientBtcPeers(), isBtcBlockDownloadComplete(), + MonadicBinding p2pNetworkAndWalletReady = EasyBind.combine(isP2pBootstrapped(), hasSufficientBtcPeers(), isBtcBlockDownloadComplete(), (isP2pBootstrapped, hasSufficientBtcPeers, isBtcBlockDownloadComplete) -> { log.info("isP2pBootstrapped={}, hasSufficientBtcPeers={} isBtcBlockDownloadComplete={}", isP2pBootstrapped, hasSufficientBtcPeers, isBtcBlockDownloadComplete); From b80082005ce69d151269d14566d08a8a68b4387d Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 19:15:42 -0500 Subject: [PATCH 06/13] Set default services based on localhost check not on devMode check --- .../trade/txproof/xmr/XmrTxProofRequest.java | 4 +-- .../bisq/core/user/AutoConfirmSettings.java | 25 +++++++++++---- .../main/java/bisq/core/user/Preferences.java | 32 +++++++++++-------- .../bisq/core/user/PreferencesPayload.java | 2 -- .../settings/preferences/PreferencesView.java | 2 +- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequest.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequest.java index c853af2410c..cfac06f4b23 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequest.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequest.java @@ -268,12 +268,10 @@ public String toString() { /////////////////////////////////////////////////////////////////////////////////////////// private String getShortId() { - return Utilities.getShortId(model.getTradeId()) + " @ " + - model.getServiceAddress().substring(0, 6); + return Utilities.getShortId(model.getTradeId()) + " @ " + model.getServiceAddress().substring(0, 6); } private boolean isTimeOutReached() { return System.currentTimeMillis() - firstRequest > MAX_REQUEST_PERIOD; } - } diff --git a/core/src/main/java/bisq/core/user/AutoConfirmSettings.java b/core/src/main/java/bisq/core/user/AutoConfirmSettings.java index 81fb2a57058..f94e0538492 100644 --- a/core/src/main/java/bisq/core/user/AutoConfirmSettings.java +++ b/core/src/main/java/bisq/core/user/AutoConfirmSettings.java @@ -25,10 +25,13 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Getter public final class AutoConfirmSettings implements PersistablePayload { public interface Listener { @@ -42,13 +45,21 @@ public interface Listener { private String currencyCode; private List listeners = new CopyOnWriteArrayList<>(); - static AutoConfirmSettings getDefaultForXmr(List serviceAddresses) { - return new AutoConfirmSettings( - false, - 5, - Coin.COIN.value, - serviceAddresses, - "XMR"); + @SuppressWarnings("SameParameterValue") + static Optional getDefault(List serviceAddresses, String currencyCode) { + //noinspection SwitchStatementWithTooFewBranches + switch (currencyCode) { + case "XMR": + return Optional.of(new AutoConfirmSettings( + false, + 5, + Coin.COIN.value, + serviceAddresses, + "XMR")); + default: + log.error("No AutoConfirmSettings supported yet for currency {}", currencyCode); + return Optional.empty(); + } } public AutoConfirmSettings(boolean enabled, diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index d267cfa604e..f4ef6285e0e 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -33,7 +33,6 @@ import bisq.network.p2p.network.BridgeAddressProvider; -import bisq.common.app.DevEnv; import bisq.common.config.BaseCurrencyNetwork; import bisq.common.config.Config; import bisq.common.proto.persistable.PersistedDataHost; @@ -124,16 +123,12 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid new BlockChainExplorer("bsq.bisq.cc (@m52go)", "https://bsq.bisq.cc/tx.html?tx=", "https://bsq.bisq.cc/Address.html?addr=") )); - // list of XMR proof providers : this list will be used if no preference has been set - public static final List getDefaultXmrProofProviders() { - if (DevEnv.isDevMode()) { - return new ArrayList<>(Arrays.asList("78.47.61.90:8081")); - } else { - // TODO we need at least 2 for release - return new ArrayList<>(Arrays.asList( - "monero3bec7m26vx6si6qo7q7imlaoz45ot5m2b5z2ppgoooo6jx2rqd.onion")); - } - } + //TODO add a second before release + private static final ArrayList XMR_TX_PROOF_SERVICES_CLEAR_NET = new ArrayList<>(Arrays.asList( + "78.47.61.90:8081")); + //TODO add a second before release + private static final ArrayList XMR_TX_PROOF_SERVICES = new ArrayList<>(Arrays.asList( + "monero3bec7m26vx6si6qo7q7imlaoz45ot5m2b5z2ppgoooo6jx2rqd.onion")); public static final boolean USE_SYMMETRIC_SECURITY_DEPOSIT = true; @@ -321,7 +316,11 @@ public void readPersisted() { } if (prefPayload.getAutoConfirmSettingsList().isEmpty()) { - getAutoConfirmSettingsList().add(AutoConfirmSettings.getDefaultForXmr(getDefaultXmrProofProviders())); + List defaultXmrTxProofServices = getDefaultXmrTxProofServices(); + AutoConfirmSettings.getDefault(defaultXmrTxProofServices, "XMR") + .ifPresent(xmrAutoConfirmSettings -> { + getAutoConfirmSettingsList().add(xmrAutoConfirmSettings); + }); } // We set the capability in CoreNetworkCapabilities if the program argument is set. @@ -332,7 +331,6 @@ public void readPersisted() { persist(); } - /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// @@ -875,6 +873,14 @@ public int getBlockNotifyPort() { } } + public List getDefaultXmrTxProofServices() { + if (config.useLocalhostForP2P) { + return XMR_TX_PROOF_SERVICES_CLEAR_NET; + } else { + return XMR_TX_PROOF_SERVICES; + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // Private diff --git a/core/src/main/java/bisq/core/user/PreferencesPayload.java b/core/src/main/java/bisq/core/user/PreferencesPayload.java index fbf97ea575f..3af5ecfa86d 100644 --- a/core/src/main/java/bisq/core/user/PreferencesPayload.java +++ b/core/src/main/java/bisq/core/user/PreferencesPayload.java @@ -52,9 +52,7 @@ public final class PreferencesPayload implements UserThreadMappedPersistableEnvelope { private String userLanguage; private Country userCountry; - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private List fiatCurrencies = new ArrayList<>(); - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private List cryptoCurrencies = new ArrayList<>(); private BlockChainExplorer blockChainExplorerMainNet; private BlockChainExplorer blockChainExplorerTestNet; diff --git a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java index 60ab77a3418..19c07503448 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java @@ -677,7 +677,7 @@ private void initializeAutoConfirmOptions() { List serviceAddresses = Arrays.asList(StringUtils.deleteWhitespace(newValue).split(",")); // revert to default service providers when user empties the list if (serviceAddresses.size() == 1 && serviceAddresses.get(0).isEmpty()) { - serviceAddresses = Preferences.getDefaultXmrProofProviders(); + serviceAddresses = preferences.getDefaultXmrTxProofServices(); } preferences.setAutoConfServiceAddresses("XMR", serviceAddresses); } From 023eb97f04f1d5842fda993e6a61a65a39ba166b Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 20:33:49 -0500 Subject: [PATCH 07/13] Change min required confirmations from 0 to 1. If users accept visible in mempool only txs its their own risk and they can manually confirm anyway. We should not support 0 conf txs. --- .../bisq/desktop/main/settings/preferences/PreferencesView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java index 19c07503448..2a4075cfbcb 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java @@ -661,7 +661,7 @@ private void initializeAutoConfirmOptions() { autoConfirmXmrToggle = addSlideToggleButton(autoConfirmGridPane, localRowIndex, Res.get("setting.preferences.autoConfirmEnabled"), Layout.FIRST_ROW_DISTANCE); autoConfRequiredConfirmationsTf = addInputTextField(autoConfirmGridPane, ++localRowIndex, Res.get("setting.preferences.autoConfirmRequiredConfirmations")); - autoConfRequiredConfirmationsTf.setValidator(new IntegerValidator(0, DevEnv.isDevMode() ? 100000000 : 1000)); + autoConfRequiredConfirmationsTf.setValidator(new IntegerValidator(1, DevEnv.isDevMode() ? 100000000 : 1000)); autoConfTradeLimitTf = addInputTextField(autoConfirmGridPane, ++localRowIndex, Res.get("setting.preferences.autoConfirmMaxTradeSize")); autoConfTradeLimitTf.setValidator(new BtcValidator(formatter)); From 7cc021d7d6e628eece704b0943e5f11c4d556edd Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 20:50:46 -0500 Subject: [PATCH 08/13] Add monero orange color code for tx confidence indicator --- desktop/src/main/java/bisq/desktop/bisq.css | 6 ++++++ desktop/src/main/java/bisq/desktop/theme-light.css | 3 +++ 2 files changed, 9 insertions(+) diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index e9ab97167cf..5556df770f2 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -856,6 +856,12 @@ textfield */ -fx-max-height: 20; } +#xmr-confidence { + -fx-progress-color: -xmr-orange; + -fx-max-width: 20; + -fx-max-height: 20; +} + .hyperlink, .hyperlink.force-underline .text, .hyperlink:hover, diff --git a/desktop/src/main/java/bisq/desktop/theme-light.css b/desktop/src/main/java/bisq/desktop/theme-light.css index 165d4e9d082..bff781eaf47 100644 --- a/desktop/src/main/java/bisq/desktop/theme-light.css +++ b/desktop/src/main/java/bisq/desktop/theme-light.css @@ -106,6 +106,9 @@ /* dao chart colors */ -bs-chart-dao-line1: -bs-color-green-3; -bs-chart-dao-line2: -bs-color-blue-5; + + /* Monero orange color code */ + -xmr-orange: #F26822; } .warning-box { From 4e27b93acfa355cc13b377bd89956358f42aaa0e Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 20:51:19 -0500 Subject: [PATCH 09/13] Show tx confidence indicator for XMR blocks. Show info icon --- .../trade/txproof/AssetTxProofResult.java | 16 ++++ .../trade/txproof/xmr/XmrTxProofParser.java | 2 +- .../trade/txproof/xmr/XmrTxProofRequest.java | 2 + .../xmr/XmrTxProofRequestsPerTrade.java | 9 ++- .../resources/i18n/displayStrings.properties | 4 +- .../steps/seller/SellerStep3View.java | 73 ++++++++++++++++--- 6 files changed, 91 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java b/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java index 0f6ba959709..73f22a5ebfd 100644 --- a/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java +++ b/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java @@ -45,6 +45,10 @@ public enum AssetTxProofResult { @Getter private int numRequiredSuccessResults; @Getter + private int numConfirmations; + @Getter + private int numRequiredConfirmations; + @Getter private String details = ""; // If isTerminal is set it means that we stop the service @Getter @@ -69,6 +73,16 @@ public AssetTxProofResult numRequiredSuccessResults(int numRequiredSuccessResult return this; } + public AssetTxProofResult numConfirmations(int numConfirmations) { + this.numConfirmations = numConfirmations; + return this; + } + + public AssetTxProofResult numRequiredConfirmations(int numRequiredConfirmations) { + this.numRequiredConfirmations = numRequiredConfirmations; + return this; + } + public AssetTxProofResult details(String details) { this.details = details; return this; @@ -79,6 +93,8 @@ public String toString() { return "AssetTxProofResult{" + "\n numSuccessResults=" + numSuccessResults + ",\n requiredSuccessResults=" + numRequiredSuccessResults + + ",\n numConfirmations=" + numConfirmations + + ",\n numRequiredConfirmations=" + numRequiredConfirmations + ",\n details='" + details + '\'' + "\n} " + super.toString(); } diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofParser.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofParser.java index cf7b7835cfc..5a11cbc5c6b 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofParser.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofParser.java @@ -162,7 +162,7 @@ public XmrTxProofRequest.Result parse(XmrTxProofModel model, String jsonTxt) { if (confirmations < confirmsRequired) { return XmrTxProofRequest.Result.PENDING.with(XmrTxProofRequest.Detail.PENDING_CONFIRMATIONS.numConfirmations(confirmations)); } else { - return XmrTxProofRequest.Result.SUCCESS; + return XmrTxProofRequest.Result.SUCCESS.with(XmrTxProofRequest.Detail.SUCCESS.numConfirmations(confirmations)); } } catch (JsonParseException | NullPointerException e) { diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequest.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequest.java index cfac06f4b23..2be07d291cb 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequest.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequest.java @@ -85,6 +85,8 @@ enum Detail { TX_NOT_FOUND, // Tx not visible in network yet. Could be also other error PENDING_CONFIRMATIONS, + SUCCESS, + // Error states CONNECTION_FAILURE, API_INVALID, diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java index 774423b3292..634f6b8ae79 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java @@ -171,7 +171,12 @@ public void requestFromAllServices(Consumer resultHandler, F // have completed on the service level. log.info("All {} tx proof requests for trade {} have been successful.", numRequiredSuccessResults, trade.getShortId()); - assetTxProofResult = AssetTxProofResult.COMPLETED; + XmrTxProofRequest.Detail detail = result.getDetail(); + assetTxProofResult = AssetTxProofResult.COMPLETED + .numSuccessResults(numSuccessResults) + .numRequiredSuccessResults(numRequiredSuccessResults) + .numConfirmations(detail != null ? detail.getNumConfirmations() : 0) + .numRequiredConfirmations(autoConfirmSettings.getRequiredConfirmations()); } break; case FAILED: @@ -292,6 +297,8 @@ private AssetTxProofResult getAssetTxProofResultForPending(XmrTxProofRequest.Res return AssetTxProofResult.PENDING .numSuccessResults(numSuccessResults) .numRequiredSuccessResults(numRequiredSuccessResults) + .numConfirmations(detail != null ? detail.getNumConfirmations() : 0) + .numRequiredConfirmations(autoConfirmSettings.getRequiredConfirmations()) .details(detailString); } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 4bf4b53acf1..d1120b03fb7 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -572,9 +572,9 @@ portfolio.pending.step5.completed=Completed portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status portfolio.pending.autoConf=Auto-confirmed - +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. -portfolio.pending.autoConf.state.confirmations=Confirmations: {0}/{1} +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java index 4bff3492dfe..5f25baaa097 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java @@ -18,8 +18,10 @@ package bisq.desktop.main.portfolio.pendingtrades.steps.seller; import bisq.desktop.components.BusyAnimation; +import bisq.desktop.components.InfoTextField; import bisq.desktop.components.TextFieldWithCopyIcon; import bisq.desktop.components.TitledGroupBg; +import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel; import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView; @@ -49,6 +51,7 @@ import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.DevEnv; +import bisq.common.util.Tuple2; import bisq.common.util.Tuple4; import javafx.scene.control.Button; @@ -57,6 +60,9 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; + +import javafx.geometry.Insets; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -65,20 +71,18 @@ import java.util.Optional; -import static bisq.desktop.util.FormBuilder.addButtonBusyAnimationLabelAfterGroup; -import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; -import static bisq.desktop.util.FormBuilder.addTopLabelTextFieldWithCopyIcon; +import static bisq.desktop.util.FormBuilder.*; public class SellerStep3View extends TradeStepView { + private final ChangeListener proofResultListener; private Button confirmButton; private Label statusLabel; private BusyAnimation busyAnimation; private Subscription tradeStatePropertySubscription; private Timer timeoutTimer; - private TextFieldWithCopyIcon assetTxProofResultField; - private final ChangeListener proofResultListener; + private InfoTextField assetTxProofResultField; + private TxConfidenceIndicator assetTxConfidenceIndicator; /////////////////////////////////////////////////////////////////////////////////////////// @@ -230,9 +234,28 @@ protected void addContent() { } if (isBlockChain && trade.getOffer().getCurrencyCode().equals("XMR")) { - assetTxProofResultField = addTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, - Res.get("portfolio.pending.step3_seller.autoConf.status.label"), - "", Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE).second; + assetTxProofResultField = new InfoTextField(); + + Tuple2 topLabelWithVBox = getTopLabelWithVBox(Res.get("portfolio.pending.step3_seller.autoConf.status.label"), assetTxProofResultField); + VBox vBox = topLabelWithVBox.second; + + assetTxConfidenceIndicator = new TxConfidenceIndicator(); + assetTxConfidenceIndicator.setId("xmr-confidence"); + assetTxConfidenceIndicator.setProgress(0); + assetTxConfidenceIndicator.setTooltip(new Tooltip()); + assetTxProofResultField.setContentForInfoPopOver(createPopoverLabel(Res.get("setting.info.msg"))); + + HBox.setMargin(assetTxConfidenceIndicator, new Insets(Layout.FLOATING_LABEL_DISTANCE, 0, 0, 0)); + + HBox hBox = new HBox(); + HBox.setHgrow(vBox, Priority.ALWAYS); + hBox.setSpacing(10); + hBox.getChildren().addAll(vBox, assetTxConfidenceIndicator); + + GridPane.setRowIndex(hBox, gridRow); + GridPane.setColumnIndex(hBox, 1); + GridPane.setMargin(hBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE + Layout.FLOATING_LABEL_DISTANCE, 0, 0, 0)); + gridPane.getChildren().add(hBox); } TextFieldWithCopyIcon myPaymentDetailsTextField = addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, @@ -274,7 +297,6 @@ protected void addContent() { // Info /////////////////////////////////////////////////////////////////////////////////////////// - @Override protected String getInfoText() { String currencyCode = model.dataModel.getCurrencyCode(); @@ -446,7 +468,36 @@ else if (paymentAccountPayload instanceof SepaInstantAccountPayload) private void applyAssetTxProofResult(AssetTxProofResult result) { String txt = GUIUtil.getProofResultAsString(result); assetTxProofResultField.setText(txt); - assetTxProofResultField.setTooltip(new Tooltip(txt)); + switch (result) { + case PENDING: + case COMPLETED: + if (result.getNumRequiredConfirmations() > 0) { + int numRequiredConfirmations = result.getNumRequiredConfirmations(); + int numConfirmations = result.getNumConfirmations(); + if (numConfirmations == 0) { + assetTxConfidenceIndicator.setProgress(-1); + } else { + double progress = Math.min(1, (double) numConfirmations / (double) numRequiredConfirmations); + assetTxConfidenceIndicator.setProgress(progress); + assetTxConfidenceIndicator.getTooltip().setText( + Res.get("portfolio.pending.autoConf.blocks", + numConfirmations, numRequiredConfirmations)); + } + } + break; + default: + // Set invisible by default + assetTxConfidenceIndicator.setProgress(0); + break; + } + } + + private Label createPopoverLabel(String text) { + Label label = new Label(text); + label.setPrefWidth(600); + label.setWrapText(true); + label.setPadding(new Insets(10)); + return label; } @Override From 6ab679c4793d8e81de4cecb3a86ed1542d6b4b8b Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 21:02:16 -0500 Subject: [PATCH 10/13] Adjust auto-conf css for badge - make it xmr orange - make with dynamic with 10 px padding left/right - fix 1 px vertical offset --- desktop/src/main/java/bisq/desktop/bisq.css | 6 ++++-- .../portfolio/pendingtrades/steps/buyer/BuyerStep4View.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index 5556df770f2..90e24fc0c47 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -436,8 +436,10 @@ tree-table-view:focused { -fx-pref-width: 30; } -.jfx-badge.autoconf .badge-pane { - -fx-pref-width: 100; +.jfx-badge.auto-conf .badge-pane { + -fx-background-color: -xmr-orange; + -fx-pref-width: -1; + -fx-padding: -1 10 0 10; } .jfx-badge .badge-pane .label { diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java index b0d6295cfc2..29076fd47ef 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java @@ -112,7 +112,7 @@ protected void addContent() { JFXBadge autoConfBadge = new JFXBadge(new Label(""), Pos.BASELINE_RIGHT); autoConfBadge.setText(Res.get("portfolio.pending.autoConf")); - autoConfBadge.getStyleClass().add("autoconf"); + autoConfBadge.getStyleClass().add("auto-conf"); HBox hBox2 = new HBox(1, completedTradeLabel, autoConfBadge); GridPane.setMargin(hBox2, new Insets(18, -10, -12, -10)); From 1016c73b354b6805cc7703c136e115f4f477dd5d Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 21:14:06 -0500 Subject: [PATCH 11/13] Add null check. Improve text --- core/src/main/resources/i18n/displayStrings.properties | 6 +++--- .../pendingtrades/steps/seller/SellerStep3View.java | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index d1120b03fb7..d917b267fe5 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -588,11 +588,11 @@ portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0} # suppress inspection "UnusedProperty" portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published. # suppress inspection "UnusedProperty" -portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Service stopped. +portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade. # suppress inspection "UnusedProperty" -portfolio.pending.autoConf.state.REQUESTS_STARTED=Proof requests started +portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started # suppress inspection "UnusedProperty" -portfolio.pending.autoConf.state.PENDING=Service results: {0}/{1}; {2} +portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2} # suppress inspection "UnusedProperty" portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded # suppress inspection "UnusedProperty" diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java index 5f25baaa097..b0e0fa57c22 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java @@ -71,6 +71,8 @@ import java.util.Optional; +import javax.annotation.Nullable; + import static bisq.desktop.util.FormBuilder.*; public class SellerStep3View extends TradeStepView { @@ -465,9 +467,14 @@ else if (paymentAccountPayload instanceof SepaInstantAccountPayload) } } - private void applyAssetTxProofResult(AssetTxProofResult result) { + private void applyAssetTxProofResult(@Nullable AssetTxProofResult result) { String txt = GUIUtil.getProofResultAsString(result); assetTxProofResultField.setText(txt); + if (result == null) { + assetTxConfidenceIndicator.setProgress(0); + return; + } + switch (result) { case PENDING: case COMPLETED: From 5b588877e72c067fb67f216f502459b2e06dcce7 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 21:21:18 -0500 Subject: [PATCH 12/13] Do not overwrite useDevMode with useDevModeHeader If --useDevModeHeader is not set it is false by default. If user has --useDevMode=true set it would overwrite his value. --- core/src/main/java/bisq/core/app/BisqExecutable.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 2fe5be9ee0c..bb36e1ffe49 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -165,7 +165,6 @@ protected void applyInjector() { protected void setupDevEnv() { DevEnv.setDevMode(config.useDevMode); - DevEnv.setDevMode(config.useDevModeHeader); DevEnv.setDaoActivated(config.daoActivated); } From a7e10155b1a8a4972146b2b3716d7755b590cb38 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 3 Sep 2020 21:22:54 -0500 Subject: [PATCH 13/13] Change hex color code to lower case --- desktop/src/main/java/bisq/desktop/theme-light.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/theme-light.css b/desktop/src/main/java/bisq/desktop/theme-light.css index bff781eaf47..910f7f4233d 100644 --- a/desktop/src/main/java/bisq/desktop/theme-light.css +++ b/desktop/src/main/java/bisq/desktop/theme-light.css @@ -108,7 +108,7 @@ -bs-chart-dao-line2: -bs-color-blue-5; /* Monero orange color code */ - -xmr-orange: #F26822; + -xmr-orange: #f26822; } .warning-box {