From f59d6fe591593e34d9c0d02bb1436cf2c672aa8d Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 4 Nov 2021 10:28:30 +0100 Subject: [PATCH 01/38] Add "Buy BSQ" button next to trade fee selector Conflicts: desktop/src/main/java/bisq/desktop/main/offer/OfferView.java --- .../resources/i18n/displayStrings.properties | 4 ++ .../bisq/desktop/main/offer/OfferView.java | 30 +++++++++------ .../main/offer/bisq_v1/MutableOfferView.java | 14 ++++++- .../main/offer/bisq_v1/OfferViewUtil.java | 38 ++++++++++++++++++- .../bisq_v1/takeoffer/TakeOfferView.java | 4 +- 5 files changed, 74 insertions(+), 16 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 04f2a585ac..7ec8e6aae0 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -481,6 +481,10 @@ createOffer.triggerPrice.tooltip=As protection against drastic price movements y createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0} +createOffer.buyBsq.popupMessage=Trading fees are paid to fund the Bisq DAO when making or taking an offer. Fees can be paid in BSQ or BTC.\n\n\ + Bisq encourages traders to use BSQ by offering a discount of approximately 50%, since BSQ fees help fund Bisq's development. \ + This discount varies as the BSQ/BTC rate fluctuates. To maintain the 50% discount target, trading fees are updated every cycle as necessary. + # new entries createOffer.placeOfferButton=Review: Place offer to {0} bitcoin createOffer.createOfferFundWalletInfo.headline=Fund your offer diff --git a/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java index 8e828c4204..5374cfd134 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java @@ -176,7 +176,9 @@ public abstract class OfferView extends ActivatableView { root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener); root.getTabs().addListener(tabListChangeListener); navigation.addListener(navigationListener); - navigation.navigateTo(MainView.class, this.getClass(), OfferBookView.class); + if (offerBookView == null) { + navigation.navigateTo(MainView.class, this.getClass(), OfferBookView.class); + } } @Override @@ -202,17 +204,21 @@ public abstract class OfferView extends ActivatableView { View view; boolean isBuy = direction == OfferDirection.BUY; - if (viewClass == OfferBookView.class && offerBookView == null) { - view = viewLoader.load(viewClass); - // Offerbook must not be cached by ViewLoader as we use 2 instances for sell and buy screens. - offerBookTab = new Tab(isBuy ? Res.get("shared.buyBitcoin").toUpperCase() : Res.get("shared.sellBitcoin").toUpperCase()); - offerBookTab.setClosable(false); - offerBookTab.setContent(view.getRoot()); - tabPane.getTabs().add(offerBookTab); - offerBookView = (OfferBookView) view; - offerBookView.onTabSelected(true); - offerBookView.setOfferActionHandler(offerActionHandler); - offerBookView.setDirection(direction); + if (viewClass == OfferBookView.class) { + if (offerBookTab != null && offerBookView != null) { + tabPane.getSelectionModel().select(offerBookTab); + } else { + view = viewLoader.load(viewClass); + // Offerbook must not be cached by ViewLoader as we use 2 instances for sell and buy screens. + offerBookTab = new Tab(isBuy ? Res.get("shared.buyBitcoin").toUpperCase() : Res.get("shared.sellBitcoin").toUpperCase()); + offerBookTab.setClosable(false); + offerBookTab.setContent(view.getRoot()); + tabPane.getTabs().add(offerBookTab); + offerBookView = (OfferBookView) view; + offerBookView.onTabSelected(true); + offerBookView.setOfferActionHandler(offerActionHandler); + offerBookView.setDirection(direction); + } } else if (viewClass == CreateOfferView.class && createOfferView == null) { view = viewLoader.load(viewClass); // CreateOffer and TakeOffer must not be cached by ViewLoader as we cannot use a view multiple times diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index 07a86f35e4..d493dfa250 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -137,7 +137,7 @@ public abstract class MutableOfferView> exten private TitledGroupBg payFundsTitledGroupBg, setDepositTitledGroupBg, paymentTitledGroupBg; protected TitledGroupBg amountTitledGroupBg; private BusyAnimation waitingForFundsSpinner; - private AutoTooltipButton nextButton, cancelButton1, cancelButton2, placeOfferButton; + private AutoTooltipButton nextButton, cancelButton1, cancelButton2, placeOfferButton, buyBsqButton; private Button priceTypeToggleButton; private InputTextField fixedPriceTextField, marketBasedPriceTextField, triggerPriceInputTextField; protected InputTextField amountTextField, minAmountTextField, volumeTextField, buyerSecurityDepositInputTextField; @@ -270,6 +270,8 @@ public abstract class MutableOfferView> exten tradeFeeInBtcToggle.setManaged(false); tradeFeeInBsqToggle.setVisible(false); tradeFeeInBsqToggle.setManaged(false); + buyBsqButton.setVisible(false); + buyBsqButton.setManaged(false); } Label popOverLabel = OfferViewUtil.createPopOverLabel(Res.get("createOffer.triggerPrice.tooltip")); @@ -397,6 +399,9 @@ public abstract class MutableOfferView> exten cancelButton1.setVisible(false); cancelButton1.setManaged(false); cancelButton1.setOnAction(null); + buyBsqButton.setVisible(false); + buyBsqButton.setManaged(false); + buyBsqButton.setOnAction(null); tradeFeeInBtcToggle.setMouseTransparent(true); tradeFeeInBsqToggle.setMouseTransparent(true); @@ -894,6 +899,7 @@ public abstract class MutableOfferView> exten if (DevEnv.isDaoActivated()) { tradeFeeInBtcToggle.setVisible(newValue); tradeFeeInBsqToggle.setVisible(newValue); + buyBsqButton.setVisible(newValue); } }; @@ -1110,7 +1116,11 @@ public abstract class MutableOfferView> exten GridPane.setMargin(advancedOptionsBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); gridPane.getChildren().add(advancedOptionsBox); - advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), getTradeFeeFieldsBox()); + Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox(navigation, preferences); + buyBsqButton = buyBsqButtonBox.first; + buyBsqButton.setVisible(false); + + advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), getTradeFeeFieldsBox(), buyBsqButtonBox.second); Tuple2 tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, Res.get("shared.nextStep"), Res.get("shared.cancel")); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index f5aff0937d..5d0d6b55e6 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -17,9 +17,23 @@ package bisq.desktop.main.offer.bisq_v1; -import javafx.scene.control.Label; +import bisq.desktop.Navigation; +import bisq.desktop.components.AutoTooltipButton; +import bisq.desktop.main.MainView; +import bisq.desktop.main.offer.offerbook.OfferBookView; +import bisq.desktop.main.overlays.popups.Popup; +import bisq.core.locale.Res; +import bisq.core.user.Preferences; + +import bisq.common.util.Tuple2; + +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; + +import javafx.geometry.HPos; import javafx.geometry.Insets; +import javafx.geometry.Pos; // Shared utils for Views public class OfferViewUtil { @@ -31,4 +45,26 @@ public class OfferViewUtil { label.setPadding(new Insets(10)); return label; } + + public static Tuple2 createBuyBsqButtonBox(Navigation navigation, Preferences preferences) { + String buyBsqText = Res.get("shared.buyCurrency", "BSQ"); + var buyBsqButton = new AutoTooltipButton(buyBsqText); + buyBsqButton.getStyleClass().add("action-button"); + buyBsqButton.setStyle("-fx-pref-height: 20; -fx-padding: 3 8 3 8; -fx-font-size: 0.769em; -fx-border-radius: 5;"); + buyBsqButton.setOnAction(e -> new Popup().headLine(buyBsqText) + .information(Res.get("createOffer.buyBsq.popupMessage")) + .actionButtonText(buyBsqText) + .buttonAlignment(HPos.CENTER) + .onAction(() -> { + preferences.setSellScreenCurrencyCode("BSQ"); + navigation.navigateTo(MainView.class, SellOfferView.class, OfferBookView.class); + }).show()); + + final VBox buyBsqButtonVBox = new VBox(buyBsqButton); + buyBsqButtonVBox.setAlignment(Pos.BOTTOM_LEFT); + buyBsqButtonVBox.setPadding(new Insets(0, 0, 0, -20)); + VBox.setMargin(buyBsqButton, new Insets(0, 0, 4, 0)); + + return new Tuple2<>(buyBsqButton, buyBsqButtonVBox); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java index 3eb2482023..bab6cb8016 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java @@ -884,7 +884,9 @@ public class TakeOfferView extends ActivatableViewAndModel buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox(navigation, model.dataModel.preferences); + + advancedOptionsBox.getChildren().addAll(getTradeFeeFieldsBox(), buyBsqButtonBox.second); } private void addButtons() { From 107cc3a8dd33171a7c8feab12a5d3d6359178e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Louck=C3=BD?= Date: Fri, 1 Jan 2021 21:15:29 +0100 Subject: [PATCH 02/38] Tiny refactoring --- desktop/src/main/java/bisq/desktop/main/offer/OfferView.java | 2 +- .../java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java index 5374cfd134..cb61fe568b 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java @@ -253,7 +253,7 @@ public abstract class OfferView extends ActivatableView { // in different graphs takeOfferView = (TakeOfferView) view; takeOfferView.initWithData(offer); - takeOfferPane = ((TakeOfferView) view).getRoot(); + takeOfferPane = takeOfferView.getRoot(); takeOfferTab = new Tab(getTakeOfferTabName()); takeOfferTab.setClosable(true); // close handler from close on take offer action diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index d493dfa250..fd51553ce6 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -338,7 +338,7 @@ public abstract class MutableOfferView> exten showInsufficientBsqFundsForBtcFeePaymentPopup(); } - // called form parent as the view does not get notified when the tab is closed + // called from parent as the view does not get notified when the tab is closed public void onClose() { // we use model.placeOfferCompleted to not react on close which was triggered by a successful placeOffer if (model.getDataModel().getBalance().get().isPositive() && !model.placeOfferCompleted.get()) { From 4c770442744b781f03a693f9eed00636d1a2fab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Louck=C3=BD?= Date: Sun, 16 May 2021 19:58:31 +0200 Subject: [PATCH 03/38] Move button style to bisq.css Add link to bisq.wiki --- core/src/main/resources/i18n/displayStrings.properties | 3 ++- desktop/src/main/java/bisq/desktop/bisq.css | 7 +++++++ .../bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 7ec8e6aae0..673f7ad5cb 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -483,7 +483,8 @@ createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0} createOffer.buyBsq.popupMessage=Trading fees are paid to fund the Bisq DAO when making or taking an offer. Fees can be paid in BSQ or BTC.\n\n\ Bisq encourages traders to use BSQ by offering a discount of approximately 50%, since BSQ fees help fund Bisq's development. \ - This discount varies as the BSQ/BTC rate fluctuates. To maintain the 50% discount target, trading fees are updated every cycle as necessary. + This discount varies as the BSQ/BTC rate fluctuates. To maintain the 50% discount target, trading fees are updated every cycle as necessary.\n\n\ + For more details see [HYPERLINK:https://bisq.wiki/Trading_fees] # new entries createOffer.placeOfferButton=Review: Place offer to {0} bitcoin diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index d8aac0472d..8037df530b 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -175,6 +175,13 @@ -fx-padding: 0 10 0 10; } +.tiny-button, .action-button.tiny-button { + -fx-font-size: 0.769em; + -fx-pref-height: 20; + -fx-padding: 3 8 3 8; + -fx-border-radius: 5; +} + .text-button { -fx-background-color: transparent; -fx-underline: true; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index 5d0d6b55e6..09d4247c67 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -50,7 +50,7 @@ public class OfferViewUtil { String buyBsqText = Res.get("shared.buyCurrency", "BSQ"); var buyBsqButton = new AutoTooltipButton(buyBsqText); buyBsqButton.getStyleClass().add("action-button"); - buyBsqButton.setStyle("-fx-pref-height: 20; -fx-padding: 3 8 3 8; -fx-font-size: 0.769em; -fx-border-radius: 5;"); + buyBsqButton.getStyleClass().add("tiny-button"); buyBsqButton.setOnAction(e -> new Popup().headLine(buyBsqText) .information(Res.get("createOffer.buyBsq.popupMessage")) .actionButtonText(buyBsqText) From 57b9c6f13820fbf4b65083b21edbd918d9b60c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Louck=C3=BD?= Date: Mon, 17 May 2021 23:57:28 +0200 Subject: [PATCH 04/38] Display button only when insufficient BSQ funds --- .../bisq/desktop/main/offer/bisq_v1/MutableOfferView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index fd51553ce6..dd3511df29 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -899,7 +899,9 @@ public abstract class MutableOfferView> exten if (DevEnv.isDaoActivated()) { tradeFeeInBtcToggle.setVisible(newValue); tradeFeeInBsqToggle.setVisible(newValue); - buyBsqButton.setVisible(newValue); + if (!(newValue && model.dataModel.isBsqForFeeAvailable())) { + buyBsqButton.setVisible(newValue); + } } }; From 717d7c205b3341d39e4fc978612dd2f0ccf8e86d Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 4 Nov 2021 10:35:50 +0100 Subject: [PATCH 05/38] Close current SELL offer tab TODO: fix bug with currency combobox Conflicts: desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java --- .../main/offer/bisq_v1/MutableOfferView.java | 16 +++++++++++++++- .../main/offer/bisq_v1/OfferViewUtil.java | 14 ++++++++++++-- .../offer/bisq_v1/takeoffer/TakeOfferView.java | 3 ++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index dd3511df29..e57614d2e9 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -168,6 +168,7 @@ public abstract class MutableOfferView> exten private ChangeListener tradeCurrencyCodeListener, errorMessageListener, marketPriceMarginListener, volumeListener, buyerSecurityDepositInBTCListener; private ChangeListener marketPriceAvailableListener; + private Navigation.Listener navigationWithCloseListener; private EventHandler currencyComboBoxSelectionHandler, paymentAccountsComboBoxSelectionHandler; private OfferView.CloseHandler closeHandler; @@ -916,6 +917,14 @@ public abstract class MutableOfferView> exten buyerSecurityDepositInputTextField.setDisable(false); } }); + + navigationWithCloseListener = (path, data) -> { + log.warn("*** target path={}, data={}, dir={}", path, data, model.getDataModel().getDirection()); + if ("closeOfferView".equals(data)) { + log.warn("*** WILL CLOSE"); + close(); + } + }; } private void setIsCurrencyForMakerFeeBtc(boolean isCurrencyForMakerFeeBtc) { @@ -972,6 +981,8 @@ public abstract class MutableOfferView> exten // UI actions paymentAccountsComboBox.setOnAction(paymentAccountsComboBoxSelectionHandler); currencyComboBox.setOnAction(currencyComboBoxSelectionHandler); + + navigation.addListener(navigationWithCloseListener); } private void removeListeners() { @@ -1007,6 +1018,8 @@ public abstract class MutableOfferView> exten // UI actions paymentAccountsComboBox.setOnAction(null); currencyComboBox.setOnAction(null); + + navigation.removeListener(navigationWithCloseListener); } @@ -1118,7 +1131,8 @@ public abstract class MutableOfferView> exten GridPane.setMargin(advancedOptionsBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); gridPane.getChildren().add(advancedOptionsBox); - Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox(navigation, preferences); + Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( + navigation, preferences, model.dataModel::getDirection); buyBsqButton = buyBsqButtonBox.first; buyBsqButton.setVisible(false); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index 09d4247c67..3a3acec292 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -24,6 +24,7 @@ import bisq.desktop.main.offer.offerbook.OfferBookView; import bisq.desktop.main.overlays.popups.Popup; import bisq.core.locale.Res; +import bisq.core.offer.OfferPayload; import bisq.core.user.Preferences; import bisq.common.util.Tuple2; @@ -46,7 +47,13 @@ public class OfferViewUtil { return label; } - public static Tuple2 createBuyBsqButtonBox(Navigation navigation, Preferences preferences) { + public interface DirectionClosure { + OfferPayload.Direction getDirection(); + } + + public static Tuple2 createBuyBsqButtonBox(Navigation navigation, + Preferences preferences, + DirectionClosure directionClosure) { String buyBsqText = Res.get("shared.buyCurrency", "BSQ"); var buyBsqButton = new AutoTooltipButton(buyBsqText); buyBsqButton.getStyleClass().add("action-button"); @@ -57,7 +64,10 @@ public class OfferViewUtil { .buttonAlignment(HPos.CENTER) .onAction(() -> { preferences.setSellScreenCurrencyCode("BSQ"); - navigation.navigateTo(MainView.class, SellOfferView.class, OfferBookView.class); + navigation.navigateToWithData( + // FIXME: replace "closeOfferView" with a more unique object? + directionClosure.getDirection() == OfferPayload.Direction.SELL ? "closeOfferView" : null, + MainView.class, SellOfferView.class, OfferBookView.class); }).show()); final VBox buyBsqButtonVBox = new VBox(buyBsqButton); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java index bab6cb8016..9dbaca8359 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java @@ -884,7 +884,8 @@ public class TakeOfferView extends ActivatableViewAndModel buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox(navigation, model.dataModel.preferences); + Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( + navigation, model.dataModel.preferences, model.dataModel::getDirection); advancedOptionsBox.getChildren().addAll(getTradeFeeFieldsBox(), buyBsqButtonBox.second); } From 6692ce574c7df626cf693fa342b8ec2267d50b5f Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 4 Oct 2021 11:19:40 +0200 Subject: [PATCH 06/38] Improve wording Co-authored-by: m52go <735155+m52go@users.noreply.github.com> --- core/src/main/resources/i18n/displayStrings.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 673f7ad5cb..610a939149 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -481,10 +481,10 @@ createOffer.triggerPrice.tooltip=As protection against drastic price movements y createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0} createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0} -createOffer.buyBsq.popupMessage=Trading fees are paid to fund the Bisq DAO when making or taking an offer. Fees can be paid in BSQ or BTC.\n\n\ - Bisq encourages traders to use BSQ by offering a discount of approximately 50%, since BSQ fees help fund Bisq's development. \ +createOffer.buyBsq.popupMessage=Trading fees are paid to fund the Bisq DAO. Fees can be paid in BSQ or BTC.\n\n\ + BSQ fees directly help fund Bisq's development, so Bisq encourages traders to use BSQ by offering a 50% discount on trading fees. \ This discount varies as the BSQ/BTC rate fluctuates. To maintain the 50% discount target, trading fees are updated every cycle as necessary.\n\n\ - For more details see [HYPERLINK:https://bisq.wiki/Trading_fees] + For more about fees, see [HYPERLINK:https://bisq.wiki/Trading_fees]. For more about the Bisq DAO, see [HYPERLINK:https://bisq.wiki/Introduction_to_the_DAO#The_Bisq_DAO]. # new entries createOffer.placeOfferButton=Review: Place offer to {0} bitcoin From 35e76e30b2ea8c0ec2ebbf646a35fc9abd9bf912 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 30 Sep 2021 12:26:42 +0200 Subject: [PATCH 07/38] Add Button to confirm payment in BSQ wallet --- .../resources/i18n/displayStrings.properties | 1 + .../steps/seller/SellerStep3View.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 610a939149..e050df1b73 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -865,6 +865,7 @@ portfolio.pending.step3_seller.xmrTxHash=Transaction ID portfolio.pending.step3_seller.xmrTxKey=Transaction key portfolio.pending.step3_seller.buyersAccount=Buyers account data portfolio.pending.step3_seller.confirmReceipt=Confirm payment receipt +portfolio.pending.step3_seller.showBsqWallet=Show payment in BSQ wallet portfolio.pending.step3_seller.buyerStartedPayment=The BTC buyer has started the {0} payment.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Check for blockchain confirmations at your altcoin wallet or block explorer and confirm the payment when you have sufficient blockchain confirmations. portfolio.pending.step3_seller.buyerStartedPayment.fiat=Check at your trading account (e.g. bank account) and confirm when you have received the payment. 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 cb6715a573..2d9c46e5b5 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 @@ -17,10 +17,15 @@ package bisq.desktop.main.portfolio.pendingtrades.steps.seller; +import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.BusyAnimation; import bisq.desktop.components.InfoTextField; import bisq.desktop.components.TextFieldWithCopyIcon; import bisq.desktop.components.indicator.TxConfidenceIndicator; +import bisq.desktop.main.MainView; +import bisq.desktop.main.dao.DaoView; +import bisq.desktop.main.dao.wallet.BsqWalletView; +import bisq.desktop.main.dao.wallet.tx.BsqTxView; import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel; import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView; @@ -82,6 +87,7 @@ import static com.google.common.base.Preconditions.checkNotNull; public class SellerStep3View extends TradeStepView { private Button confirmButton; + private AutoTooltipButton showBsqWallet; private Label statusLabel; private BusyAnimation busyAnimation; private Subscription tradeStatePropertySubscription; @@ -296,11 +302,21 @@ public class SellerStep3View extends TradeStepView { Tuple4 tuple = addButtonBusyAnimationLabelAfterGroup(gridPane, ++gridRow, Res.get("portfolio.pending.step3_seller.confirmReceipt")); + HBox hBox = tuple.fourth; GridPane.setColumnSpan(tuple.fourth, 2); confirmButton = tuple.first; confirmButton.setOnAction(e -> onPaymentReceived()); busyAnimation = tuple.second; statusLabel = tuple.third; + + if (trade.getOffer().getCurrencyCode().equals("BSQ")) { + showBsqWallet = new AutoTooltipButton(Res.get("portfolio.pending.step3_seller.showBsqWallet")); + hBox.getChildren().add(1, showBsqWallet); + showBsqWallet.setOnAction(e -> { + model.getNavigation().navigateTo(MainView.class, DaoView.class, BsqWalletView.class, + BsqTxView.class); + }); + } } From 9cf373a9bb1fcee7b4c09932ced0d7fdf43c82e2 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 6 Oct 2021 10:36:06 +0200 Subject: [PATCH 08/38] Not show buy bsq hint for buy bsq offers --- .../desktop/main/offer/bisq_v1/MutableOfferDataModel.java | 4 ++++ .../bisq/desktop/main/offer/bisq_v1/MutableOfferView.java | 2 +- .../desktop/main/offer/bisq_v1/MutableOfferViewModel.java | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferDataModel.java index 3321e64707..da401709ee 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferDataModel.java @@ -781,6 +781,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs return offerUtil.isBsqForMakerFeeAvailable(amount.get()); } + boolean isBuyBsqOffer() { + return !isBuyOffer() && getTradeCurrency().getCode().equals("BSQ"); + } + boolean canPlaceOffer() { return GUIUtil.isBootstrappedOrShowPopup(p2PService) && GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation, tradeCurrency); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index e57614d2e9..e42ff2ccd0 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -900,7 +900,7 @@ public abstract class MutableOfferView> exten if (DevEnv.isDaoActivated()) { tradeFeeInBtcToggle.setVisible(newValue); tradeFeeInBsqToggle.setVisible(newValue); - if (!(newValue && model.dataModel.isBsqForFeeAvailable())) { + if (model.isShowBuyBsqHint()) { buyBsqButton.setVisible(newValue); } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java index 344966a016..5121e2242c 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java @@ -1355,4 +1355,8 @@ public abstract class MutableOfferViewModel ext } } } + + public boolean isShowBuyBsqHint() { + return !dataModel.isBsqForFeeAvailable() && !dataModel.isBuyBsqOffer(); + } } From e1b405f20bb30e0435117733955702b0df8e4ff8 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 4 Nov 2021 10:36:46 +0100 Subject: [PATCH 09/38] Apply minor refactorings Conflicts: desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java --- .../main/offer/bisq_v1/MutableOfferView.java | 78 +++++-------------- .../offer/bisq_v1/MutableOfferViewModel.java | 4 +- .../bisq_v1/takeoffer/TakeOfferView.java | 49 ++---------- .../main/java/bisq/desktop/util/GUIUtil.java | 56 +++++++++++++ 4 files changed, 85 insertions(+), 102 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index e42ff2ccd0..01e963fd27 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -83,7 +83,6 @@ import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -123,6 +122,7 @@ import org.jetbrains.annotations.NotNull; import static bisq.core.payment.payload.PaymentMethod.HAL_CASH_ID; import static bisq.desktop.util.FormBuilder.*; +import static bisq.desktop.util.GUIUtil.addPayInfoEntry; import static javafx.beans.binding.Bindings.createStringBinding; public abstract class MutableOfferView> extends ActivatableViewAndModel { @@ -375,6 +375,16 @@ public abstract class MutableOfferView> exten } private void showInsufficientBsqFundsForBtcFeePaymentPopup() { + String message = getMissingBsqForFeePaymentMessage(); + + if (message != null) + new Popup().warning(message) + .actionButtonTextWithGoTo("navigation.dao.wallet.receive") + .onAction(() -> navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class, BsqReceiveView.class)) + .show(); + } + + private String getMissingBsqForFeePaymentMessage() { Coin makerFee = model.getDataModel().getMakerFee(false); String message = null; if (makerFee != null) { @@ -384,11 +394,7 @@ public abstract class MutableOfferView> exten } else if (model.getDataModel().getUsableBsqBalance().isZero()) message = Res.get("popup.warning.noBsqFundsForBtcFeePayment"); - if (message != null) - new Popup().warning(message) - .actionButtonTextWithGoTo("navigation.dao.wallet.receive") - .onAction(() -> navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class, BsqReceiveView.class)) - .show(); + return message; } private void onShowPayFundsScreen() { @@ -520,17 +526,7 @@ public abstract class MutableOfferView> exten private void maybeShowAccountWarning(PaymentAccount paymentAccount, boolean isBuyer) { String msgKey = paymentAccount.getPreTradeMessage(isBuyer); - if (msgKey == null || paymentAccountWarningDisplayed.getOrDefault(msgKey, false)) { - return; - } - paymentAccountWarningDisplayed.put(msgKey, true); - UserThread.runAfter(() -> { - new Popup().information(Res.get(msgKey)) - .width(900) - .closeButtonText(Res.get("shared.iConfirm")) - .dontShowAgainId(msgKey) - .show(); - }, 500, TimeUnit.MILLISECONDS); + GUIUtil.showPaymentAccountWarning(msgKey, paymentAccountWarningDisplayed); } protected void onPaymentAccountsComboBoxSelected() { @@ -1028,15 +1024,7 @@ public abstract class MutableOfferView> exten /////////////////////////////////////////////////////////////////////////////////////////// private void addScrollPane() { - scrollPane = new ScrollPane(); - scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); - scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); - scrollPane.setFitToWidth(true); - scrollPane.setFitToHeight(true); - AnchorPane.setLeftAnchor(scrollPane, 0d); - AnchorPane.setTopAnchor(scrollPane, 0d); - AnchorPane.setRightAnchor(scrollPane, 0d); - AnchorPane.setBottomAnchor(scrollPane, 0d); + scrollPane = GUIUtil.createScrollPane(); root.getChildren().add(scrollPane); } @@ -1046,13 +1034,7 @@ public abstract class MutableOfferView> exten gridPane.setPadding(new Insets(30, 25, -1, 25)); gridPane.setHgap(5); gridPane.setVgap(5); - ColumnConstraints columnConstraints1 = new ColumnConstraints(); - columnConstraints1.setHalignment(HPos.RIGHT); - columnConstraints1.setHgrow(Priority.NEVER); - columnConstraints1.setMinWidth(200); - ColumnConstraints columnConstraints2 = new ColumnConstraints(); - columnConstraints2.setHgrow(Priority.ALWAYS); - gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2); + GUIUtil.setDefaultTwoColumnConstraintsForGridPane(gridPane); scrollPane.setContent(gridPane); } @@ -1178,15 +1160,7 @@ public abstract class MutableOfferView> exten boolean isPreferredFeeCurrencyBtc = model.getDataModel().isPreferredFeeCurrencyBtc(); boolean isBsqForFeeAvailable = model.getDataModel().isBsqForFeeAvailable(); if (!isPreferredFeeCurrencyBtc && !isBsqForFeeAvailable) { - Coin makerFee = model.getDataModel().getMakerFee(false); - String missingBsq = null; - if (makerFee != null) { - missingBsq = Res.get("popup.warning.insufficientBsqFundsForBtcFeePayment", - bsqFormatter.formatCoinWithCode(makerFee.subtract(model.getDataModel().getUsableBsqBalance()))); - - } else if (model.getDataModel().getUsableBsqBalance().isZero()) { - missingBsq = Res.get("popup.warning.noBsqFundsForBtcFeePayment"); - } + String missingBsq = getMissingBsqForFeePaymentMessage(); if (missingBsq != null) { new Popup().warning(missingBsq) @@ -1568,8 +1542,9 @@ public abstract class MutableOfferView> exten infoGridPane.setPadding(new Insets(10, 10, 10, 10)); int i = 0; - if (model.isSellOffer()) - addPayInfoEntry(infoGridPane, i++, Res.getWithCol("shared.tradeAmount"), model.tradeAmount.get()); + if (model.isSellOffer()) { + addPayInfoEntry(infoGridPane, i++, Res.getWithCol("shared.tradeAmount"), model.getTradeAmount()); + } addPayInfoEntry(infoGridPane, i++, Res.getWithCol("shared.yourSecurityDeposit"), model.getSecurityDepositInfo()); addPayInfoEntry(infoGridPane, i++, Res.getWithCol("createOffer.fundsBox.offerFee"), model.getTradeFee()); @@ -1579,19 +1554,8 @@ public abstract class MutableOfferView> exten separator.getStyleClass().add("offer-separator"); GridPane.setConstraints(separator, 1, i++); infoGridPane.getChildren().add(separator); - addPayInfoEntry(infoGridPane, i, Res.getWithCol("shared.total"), model.getTotalToPayInfo()); + addPayInfoEntry(infoGridPane, i, Res.getWithCol("shared.total"), + model.getTotalToPayInfo()); return infoGridPane; } - - private void addPayInfoEntry(GridPane infoGridPane, int row, String labelText, String value) { - Label label = new AutoTooltipLabel(labelText); - TextField textField = new TextField(value); - textField.setMinWidth(500); - textField.setEditable(false); - textField.setFocusTraversable(false); - textField.setId("payment-info"); - GridPane.setConstraints(label, 0, row, 1, 1, HPos.RIGHT, VPos.CENTER); - GridPane.setConstraints(textField, 1, row); - infoGridPane.getChildren().addAll(label, textField); - } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java index 5121e2242c..c07dde3d61 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java @@ -678,11 +678,10 @@ public abstract class MutableOfferViewModel ext updateSpinnerInfo(); } - boolean fundFromSavingsWallet() { + void fundFromSavingsWallet() { dataModel.fundFromSavingsWallet(); if (dataModel.getIsBtcWalletFunded().get()) { updateButtonDisableState(); - return true; } else { new Popup().warning(Res.get("shared.notEnoughFunds", btcFormatter.formatCoinWithCode(dataModel.totalToPayAsCoinProperty().get()), @@ -690,7 +689,6 @@ public abstract class MutableOfferViewModel ext .actionButtonTextWithGoTo("navigation.funds.depositFunds") .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class)) .show(); - return false; } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java index 9dbaca8359..b405e89284 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java @@ -90,7 +90,6 @@ import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -119,6 +118,7 @@ import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; import static bisq.desktop.util.FormBuilder.*; +import static bisq.desktop.util.GUIUtil.addPayInfoEntry; import static javafx.beans.binding.Bindings.createStringBinding; @FxmlView @@ -672,7 +672,7 @@ public class TakeOfferView extends ActivatableViewAndModel new Popup().warning(newValue + "\n\n" + - Res.get("takeOffer.alreadyPaidInFunds")) + Res.get("takeOffer.alreadyPaidInFunds")) .actionButtonTextWithGoTo("navigation.funds.availableForWithdrawal") .onAction(() -> { errorPopupDisplayed.set(true); @@ -787,15 +787,7 @@ public class TakeOfferView extends ActivatableViewAndModel { - new Popup().information(Res.get(msgKey)) - .width(900) - .closeButtonText(Res.get("shared.iConfirm")) - .dontShowAgainId(msgKey) - .show(); - }, 500, TimeUnit.MILLISECONDS); + GUIUtil.showPaymentAccountWarning(msgKey, paymentAccountWarningDisplayed); } private void maybeShowCashByMailWarning(PaymentAccount paymentAccount, Offer offer) { @@ -1332,8 +1308,9 @@ public class TakeOfferView extends ActivatableViewAndModel paymentAccountWarningDisplayed) { + if (msgKey == null || paymentAccountWarningDisplayed.getOrDefault(msgKey, false)) { + return; + } + paymentAccountWarningDisplayed.put(msgKey, true); + UserThread.runAfter(() -> { + new Popup().information(Res.get(msgKey)) + .width(900) + .closeButtonText(Res.get("shared.iConfirm")) + .dontShowAgainId(msgKey) + .show(); + }, 500, TimeUnit.MILLISECONDS); + } + + public static void addPayInfoEntry(GridPane infoGridPane, int row, String labelText, String value) { + Label label = new AutoTooltipLabel(labelText); + TextField textField = new TextField(value); + textField.setMinWidth(500); + textField.setEditable(false); + textField.setFocusTraversable(false); + textField.setId("payment-info"); + GridPane.setConstraints(label, 0, row, 1, 1, HPos.RIGHT, VPos.CENTER); + GridPane.setConstraints(textField, 1, row); + infoGridPane.getChildren().addAll(label, textField); + } } From d2baabc180c5254c4698eda6d7fdd4cb10c435b9 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 6 Oct 2021 11:15:15 +0200 Subject: [PATCH 10/38] Move utility methods from GUIUtil to OfferViewUtil --- .../main/offer/bisq_v1/MutableOfferView.java | 4 +-- .../main/offer/bisq_v1/OfferViewUtil.java | 35 +++++++++++++++++++ .../bisq_v1/takeoffer/TakeOfferView.java | 4 +-- .../main/java/bisq/desktop/util/GUIUtil.java | 28 --------------- 4 files changed, 39 insertions(+), 32 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index 01e963fd27..27586e1e84 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -121,8 +121,8 @@ import lombok.Setter; import org.jetbrains.annotations.NotNull; import static bisq.core.payment.payload.PaymentMethod.HAL_CASH_ID; +import static bisq.desktop.main.offer.OfferViewUtil.addPayInfoEntry; import static bisq.desktop.util.FormBuilder.*; -import static bisq.desktop.util.GUIUtil.addPayInfoEntry; import static javafx.beans.binding.Bindings.createStringBinding; public abstract class MutableOfferView> extends ActivatableViewAndModel { @@ -526,7 +526,7 @@ public abstract class MutableOfferView> exten private void maybeShowAccountWarning(PaymentAccount paymentAccount, boolean isBuyer) { String msgKey = paymentAccount.getPreTradeMessage(isBuyer); - GUIUtil.showPaymentAccountWarning(msgKey, paymentAccountWarningDisplayed); + OfferViewUtil.showPaymentAccountWarning(msgKey, paymentAccountWarningDisplayed); } protected void onPaymentAccountsComboBoxSelected() { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index 3a3acec292..42e4987485 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -19,6 +19,7 @@ package bisq.desktop.main.offer.bisq_v1; import bisq.desktop.Navigation; import bisq.desktop.components.AutoTooltipButton; +import bisq.desktop.components.AutoTooltipLabel; import bisq.desktop.main.MainView; import bisq.desktop.main.offer.offerbook.OfferBookView; import bisq.desktop.main.overlays.popups.Popup; @@ -27,14 +28,21 @@ import bisq.core.locale.Res; import bisq.core.offer.OfferPayload; import bisq.core.user.Preferences; +import bisq.common.UserThread; import bisq.common.util.Tuple2; import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; +import javafx.geometry.VPos; + +import java.util.HashMap; +import java.util.concurrent.TimeUnit; // Shared utils for Views public class OfferViewUtil { @@ -47,6 +55,33 @@ public class OfferViewUtil { return label; } + public static void showPaymentAccountWarning(String msgKey, + HashMap paymentAccountWarningDisplayed) { + if (msgKey == null || paymentAccountWarningDisplayed.getOrDefault(msgKey, false)) { + return; + } + paymentAccountWarningDisplayed.put(msgKey, true); + UserThread.runAfter(() -> { + new Popup().information(Res.get(msgKey)) + .width(900) + .closeButtonText(Res.get("shared.iConfirm")) + .dontShowAgainId(msgKey) + .show(); + }, 500, TimeUnit.MILLISECONDS); + } + + public static void addPayInfoEntry(GridPane infoGridPane, int row, String labelText, String value) { + Label label = new AutoTooltipLabel(labelText); + TextField textField = new TextField(value); + textField.setMinWidth(500); + textField.setEditable(false); + textField.setFocusTraversable(false); + textField.setId("payment-info"); + GridPane.setConstraints(label, 0, row, 1, 1, HPos.RIGHT, VPos.CENTER); + GridPane.setConstraints(textField, 1, row); + infoGridPane.getChildren().addAll(label, textField); + } + public interface DirectionClosure { OfferPayload.Direction getDirection(); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java index b405e89284..bfdc949985 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java @@ -117,8 +117,8 @@ import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; +import static bisq.desktop.main.offer.OfferViewUtil.addPayInfoEntry; import static bisq.desktop.util.FormBuilder.*; -import static bisq.desktop.util.GUIUtil.addPayInfoEntry; import static javafx.beans.binding.Bindings.createStringBinding; @FxmlView @@ -1268,7 +1268,7 @@ public class TakeOfferView extends ActivatableViewAndModel paymentAccountWarningDisplayed) { - if (msgKey == null || paymentAccountWarningDisplayed.getOrDefault(msgKey, false)) { - return; - } - paymentAccountWarningDisplayed.put(msgKey, true); - UserThread.runAfter(() -> { - new Popup().information(Res.get(msgKey)) - .width(900) - .closeButtonText(Res.get("shared.iConfirm")) - .dontShowAgainId(msgKey) - .show(); - }, 500, TimeUnit.MILLISECONDS); - } - - public static void addPayInfoEntry(GridPane infoGridPane, int row, String labelText, String value) { - Label label = new AutoTooltipLabel(labelText); - TextField textField = new TextField(value); - textField.setMinWidth(500); - textField.setEditable(false); - textField.setFocusTraversable(false); - textField.setId("payment-info"); - GridPane.setConstraints(label, 0, row, 1, 1, HPos.RIGHT, VPos.CENTER); - GridPane.setConstraints(textField, 1, row); - infoGridPane.getChildren().addAll(label, textField); - } } From ee30cd9739cdcf5f5579e1bdb873f3bf1818d3eb Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 6 Oct 2021 11:23:33 +0200 Subject: [PATCH 11/38] Refactor auto-closing of sell BTC offer view --- .../desktop/main/offer/bisq_v1/MutableOfferView.java | 12 +++++------- .../desktop/main/offer/bisq_v1/OfferViewUtil.java | 9 ++++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index 27586e1e84..a54f7bdad1 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -168,7 +168,7 @@ public abstract class MutableOfferView> exten private ChangeListener tradeCurrencyCodeListener, errorMessageListener, marketPriceMarginListener, volumeListener, buyerSecurityDepositInBTCListener; private ChangeListener marketPriceAvailableListener; - private Navigation.Listener navigationWithCloseListener; + private Navigation.Listener closeViewListener; private EventHandler currencyComboBoxSelectionHandler, paymentAccountsComboBoxSelectionHandler; private OfferView.CloseHandler closeHandler; @@ -914,10 +914,8 @@ public abstract class MutableOfferView> exten } }); - navigationWithCloseListener = (path, data) -> { - log.warn("*** target path={}, data={}, dir={}", path, data, model.getDataModel().getDirection()); - if ("closeOfferView".equals(data)) { - log.warn("*** WILL CLOSE"); + closeViewListener = (path, data) -> { + if (OfferViewUtil.isCloseView((String) data)) { close(); } }; @@ -978,7 +976,7 @@ public abstract class MutableOfferView> exten paymentAccountsComboBox.setOnAction(paymentAccountsComboBoxSelectionHandler); currencyComboBox.setOnAction(currencyComboBoxSelectionHandler); - navigation.addListener(navigationWithCloseListener); + navigation.addListener(closeViewListener); } private void removeListeners() { @@ -1015,7 +1013,7 @@ public abstract class MutableOfferView> exten paymentAccountsComboBox.setOnAction(null); currencyComboBox.setOnAction(null); - navigation.removeListener(navigationWithCloseListener); + navigation.removeListener(closeViewListener); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index 42e4987485..b9aca7a73b 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -46,6 +46,9 @@ import java.util.concurrent.TimeUnit; // Shared utils for Views public class OfferViewUtil { + + public static final String CLOSE_VIEW = "closeView"; + public static Label createPopOverLabel(String text) { final Label label = new Label(text); label.setPrefWidth(300); @@ -101,7 +104,7 @@ public class OfferViewUtil { preferences.setSellScreenCurrencyCode("BSQ"); navigation.navigateToWithData( // FIXME: replace "closeOfferView" with a more unique object? - directionClosure.getDirection() == OfferPayload.Direction.SELL ? "closeOfferView" : null, + directionClosure.getDirection() == OfferPayload.Direction.SELL ? CLOSE_VIEW : null, MainView.class, SellOfferView.class, OfferBookView.class); }).show()); @@ -112,4 +115,8 @@ public class OfferViewUtil { return new Tuple2<>(buyBsqButton, buyBsqButtonVBox); } + + public static boolean isCloseView(String data) { + return CLOSE_VIEW.equals(data); + } } From 9e2be927870ee9df19ba3e30012f868f1b762a22 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 6 Oct 2021 11:34:30 +0200 Subject: [PATCH 12/38] Not auto-close create offer view --- .../main/offer/bisq_v1/MutableOfferView.java | 13 +------------ .../main/offer/bisq_v1/OfferViewUtil.java | 18 ++---------------- .../offer/bisq_v1/takeoffer/TakeOfferView.java | 2 +- 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index a54f7bdad1..b37ef3f28e 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -168,7 +168,6 @@ public abstract class MutableOfferView> exten private ChangeListener tradeCurrencyCodeListener, errorMessageListener, marketPriceMarginListener, volumeListener, buyerSecurityDepositInBTCListener; private ChangeListener marketPriceAvailableListener; - private Navigation.Listener closeViewListener; private EventHandler currencyComboBoxSelectionHandler, paymentAccountsComboBoxSelectionHandler; private OfferView.CloseHandler closeHandler; @@ -913,12 +912,6 @@ public abstract class MutableOfferView> exten buyerSecurityDepositInputTextField.setDisable(false); } }); - - closeViewListener = (path, data) -> { - if (OfferViewUtil.isCloseView((String) data)) { - close(); - } - }; } private void setIsCurrencyForMakerFeeBtc(boolean isCurrencyForMakerFeeBtc) { @@ -975,8 +968,6 @@ public abstract class MutableOfferView> exten // UI actions paymentAccountsComboBox.setOnAction(paymentAccountsComboBoxSelectionHandler); currencyComboBox.setOnAction(currencyComboBoxSelectionHandler); - - navigation.addListener(closeViewListener); } private void removeListeners() { @@ -1012,8 +1003,6 @@ public abstract class MutableOfferView> exten // UI actions paymentAccountsComboBox.setOnAction(null); currencyComboBox.setOnAction(null); - - navigation.removeListener(closeViewListener); } @@ -1112,7 +1101,7 @@ public abstract class MutableOfferView> exten gridPane.getChildren().add(advancedOptionsBox); Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( - navigation, preferences, model.dataModel::getDirection); + navigation, preferences); buyBsqButton = buyBsqButtonBox.first; buyBsqButton.setVisible(false); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index b9aca7a73b..28a0faba5f 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -25,7 +25,6 @@ import bisq.desktop.main.offer.offerbook.OfferBookView; import bisq.desktop.main.overlays.popups.Popup; import bisq.core.locale.Res; -import bisq.core.offer.OfferPayload; import bisq.core.user.Preferences; import bisq.common.UserThread; @@ -47,8 +46,6 @@ import java.util.concurrent.TimeUnit; // Shared utils for Views public class OfferViewUtil { - public static final String CLOSE_VIEW = "closeView"; - public static Label createPopOverLabel(String text) { final Label label = new Label(text); label.setPrefWidth(300); @@ -85,13 +82,8 @@ public class OfferViewUtil { infoGridPane.getChildren().addAll(label, textField); } - public interface DirectionClosure { - OfferPayload.Direction getDirection(); - } - public static Tuple2 createBuyBsqButtonBox(Navigation navigation, - Preferences preferences, - DirectionClosure directionClosure) { + Preferences preferences) { String buyBsqText = Res.get("shared.buyCurrency", "BSQ"); var buyBsqButton = new AutoTooltipButton(buyBsqText); buyBsqButton.getStyleClass().add("action-button"); @@ -102,9 +94,7 @@ public class OfferViewUtil { .buttonAlignment(HPos.CENTER) .onAction(() -> { preferences.setSellScreenCurrencyCode("BSQ"); - navigation.navigateToWithData( - // FIXME: replace "closeOfferView" with a more unique object? - directionClosure.getDirection() == OfferPayload.Direction.SELL ? CLOSE_VIEW : null, + navigation.navigateTo( MainView.class, SellOfferView.class, OfferBookView.class); }).show()); @@ -115,8 +105,4 @@ public class OfferViewUtil { return new Tuple2<>(buyBsqButton, buyBsqButtonVBox); } - - public static boolean isCloseView(String data) { - return CLOSE_VIEW.equals(data); - } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java index bfdc949985..2c9f6a2e90 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java @@ -871,7 +871,7 @@ public class TakeOfferView extends ActivatableViewAndModel buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( - navigation, model.dataModel.preferences, model.dataModel::getDirection); + navigation, model.dataModel.preferences); advancedOptionsBox.getChildren().addAll(getTradeFeeFieldsBox(), buyBsqButtonBox.second); } From 552720889496f8f6aae7b0d51932158c7ff503bb Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 6 Oct 2021 12:16:12 +0200 Subject: [PATCH 13/38] Add more context to buy BSQ button --- .../desktop/components/HyperlinkWithIcon.java | 12 +++++++-- .../main/offer/bisq_v1/MutableOfferView.java | 21 +++++++-------- .../main/offer/bisq_v1/OfferViewUtil.java | 27 ++++++++++++++----- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/components/HyperlinkWithIcon.java b/desktop/src/main/java/bisq/desktop/components/HyperlinkWithIcon.java index b3738ee74d..3eebe0aab8 100644 --- a/desktop/src/main/java/bisq/desktop/components/HyperlinkWithIcon.java +++ b/desktop/src/main/java/bisq/desktop/components/HyperlinkWithIcon.java @@ -41,11 +41,15 @@ public class HyperlinkWithIcon extends Hyperlink { this(text, AwesomeIcon.INFO_SIGN); } - public HyperlinkWithIcon(String text, AwesomeIcon awesomeIcon) { + public HyperlinkWithIcon(String text, String fontSize) { + this(text, AwesomeIcon.INFO_SIGN, fontSize); + } + + public HyperlinkWithIcon(String text, AwesomeIcon awesomeIcon, String fontSize) { super(text); Label icon = new Label(); - AwesomeDude.setIcon(icon, awesomeIcon); + AwesomeDude.setIcon(icon, awesomeIcon, fontSize); icon.setMinWidth(20); icon.setOpacity(0.7); icon.getStyleClass().addAll("hyperlink", "no-underline"); @@ -55,6 +59,10 @@ public class HyperlinkWithIcon extends Hyperlink { setIcon(icon); } + public HyperlinkWithIcon(String text, AwesomeIcon awesomeIcon) { + this(text, awesomeIcon, "1.231em"); + } + public HyperlinkWithIcon(String text, GlyphIcons icon) { this(text, icon, null); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index b37ef3f28e..8310f49cbb 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -137,7 +137,7 @@ public abstract class MutableOfferView> exten private TitledGroupBg payFundsTitledGroupBg, setDepositTitledGroupBg, paymentTitledGroupBg; protected TitledGroupBg amountTitledGroupBg; private BusyAnimation waitingForFundsSpinner; - private AutoTooltipButton nextButton, cancelButton1, cancelButton2, placeOfferButton, buyBsqButton; + private AutoTooltipButton nextButton, cancelButton1, cancelButton2, placeOfferButton; private Button priceTypeToggleButton; private InputTextField fixedPriceTextField, marketBasedPriceTextField, triggerPriceInputTextField; protected InputTextField amountTextField, minAmountTextField, volumeTextField, buyerSecurityDepositInputTextField; @@ -153,7 +153,7 @@ public abstract class MutableOfferView> exten private ComboBox paymentAccountsComboBox; private ComboBox currencyComboBox; private ImageView qrCodeImageView; - private VBox currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox; + private VBox currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox, buyBsqBox; private HBox fundingHBox, firstRowHBox, secondRowHBox, placeOfferBox, amountValueCurrencyBox, priceAsPercentageValueCurrencyBox, volumeValueCurrencyBox, priceValueCurrencyBox, minAmountValueCurrencyBox, advancedOptionsBox, triggerPriceHBox; @@ -270,8 +270,8 @@ public abstract class MutableOfferView> exten tradeFeeInBtcToggle.setManaged(false); tradeFeeInBsqToggle.setVisible(false); tradeFeeInBsqToggle.setManaged(false); - buyBsqButton.setVisible(false); - buyBsqButton.setManaged(false); + buyBsqBox.setVisible(false); + buyBsqBox.setManaged(false); } Label popOverLabel = OfferViewUtil.createPopOverLabel(Res.get("createOffer.triggerPrice.tooltip")); @@ -405,9 +405,8 @@ public abstract class MutableOfferView> exten cancelButton1.setVisible(false); cancelButton1.setManaged(false); cancelButton1.setOnAction(null); - buyBsqButton.setVisible(false); - buyBsqButton.setManaged(false); - buyBsqButton.setOnAction(null); + buyBsqBox.setVisible(false); + buyBsqBox.setManaged(false); tradeFeeInBtcToggle.setMouseTransparent(true); tradeFeeInBsqToggle.setMouseTransparent(true); @@ -896,7 +895,7 @@ public abstract class MutableOfferView> exten tradeFeeInBtcToggle.setVisible(newValue); tradeFeeInBsqToggle.setVisible(newValue); if (model.isShowBuyBsqHint()) { - buyBsqButton.setVisible(newValue); + buyBsqBox.setVisible(newValue); } } }; @@ -1102,10 +1101,10 @@ public abstract class MutableOfferView> exten Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( navigation, preferences); - buyBsqButton = buyBsqButtonBox.first; - buyBsqButton.setVisible(false); + buyBsqBox = buyBsqButtonBox.second; + buyBsqBox.setVisible(false); - advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), getTradeFeeFieldsBox(), buyBsqButtonBox.second); + advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), getTradeFeeFieldsBox(), buyBsqBox); Tuple2 tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, Res.get("shared.nextStep"), Res.get("shared.cancel")); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index 28a0faba5f..816ad8cac8 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -20,6 +20,7 @@ package bisq.desktop.main.offer.bisq_v1; import bisq.desktop.Navigation; import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipLabel; +import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.main.MainView; import bisq.desktop.main.offer.offerbook.OfferBookView; import bisq.desktop.main.overlays.popups.Popup; @@ -33,6 +34,7 @@ import bisq.common.util.Tuple2; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.geometry.HPos; @@ -88,21 +90,32 @@ public class OfferViewUtil { var buyBsqButton = new AutoTooltipButton(buyBsqText); buyBsqButton.getStyleClass().add("action-button"); buyBsqButton.getStyleClass().add("tiny-button"); - buyBsqButton.setOnAction(e -> new Popup().headLine(buyBsqText) + buyBsqButton.setOnAction(e -> openBuyBsqOfferBook(navigation, preferences) + ); + + var info = new AutoTooltipLabel("BSQ is colored BTC that helps fund Bisq developers."); + var learnMore = new HyperlinkWithIcon("Learn More"); + learnMore.setOnAction(e -> new Popup().headLine(buyBsqText) .information(Res.get("createOffer.buyBsq.popupMessage")) .actionButtonText(buyBsqText) .buttonAlignment(HPos.CENTER) - .onAction(() -> { - preferences.setSellScreenCurrencyCode("BSQ"); - navigation.navigateTo( - MainView.class, SellOfferView.class, OfferBookView.class); - }).show()); + .onAction(() -> openBuyBsqOfferBook(navigation, preferences)).show()); - final VBox buyBsqButtonVBox = new VBox(buyBsqButton); + final HBox buyBsqBox = new HBox(buyBsqButton, info, learnMore); + buyBsqBox.setAlignment(Pos.BOTTOM_LEFT); + buyBsqBox.setSpacing(10); + + final VBox buyBsqButtonVBox = new VBox(buyBsqBox); buyBsqButtonVBox.setAlignment(Pos.BOTTOM_LEFT); buyBsqButtonVBox.setPadding(new Insets(0, 0, 0, -20)); VBox.setMargin(buyBsqButton, new Insets(0, 0, 4, 0)); return new Tuple2<>(buyBsqButton, buyBsqButtonVBox); } + + private static void openBuyBsqOfferBook(Navigation navigation, Preferences preferences) { + preferences.setSellScreenCurrencyCode("BSQ"); + navigation.navigateTo( + MainView.class, SellOfferView.class, OfferBookView.class); + } } From d6cd83ae88b01ac5770aabfe1a9a4f9b137bf9c4 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 11 Oct 2021 10:59:49 +0200 Subject: [PATCH 14/38] Improve informational popup --- core/src/main/resources/i18n/displayStrings.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index e050df1b73..1424726c24 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -484,7 +484,7 @@ createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0} createOffer.buyBsq.popupMessage=Trading fees are paid to fund the Bisq DAO. Fees can be paid in BSQ or BTC.\n\n\ BSQ fees directly help fund Bisq's development, so Bisq encourages traders to use BSQ by offering a 50% discount on trading fees. \ This discount varies as the BSQ/BTC rate fluctuates. To maintain the 50% discount target, trading fees are updated every cycle as necessary.\n\n\ - For more about fees, see [HYPERLINK:https://bisq.wiki/Trading_fees]. For more about the Bisq DAO, see [HYPERLINK:https://bisq.wiki/Introduction_to_the_DAO#The_Bisq_DAO]. + For more about fees, see [HYPERLINK:https://bisq.wiki/Trading_fees]. For more about trading BSQ, see [HYPERLINK:https://bisq.wiki/Trading_BSQ]. For more about the Bisq DAO, see [HYPERLINK:https://bisq.wiki/Introduction_to_the_DAO#The_Bisq_DAO]. # new entries createOffer.placeOfferButton=Review: Place offer to {0} bitcoin From 4063b72b7dc824c99322435c211c824f135ae922 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 4 Nov 2021 10:40:16 +0100 Subject: [PATCH 15/38] Add tooltip for disabled create offer button Conflicts: desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java --- .../resources/i18n/displayStrings.properties | 1 + desktop/src/main/java/bisq/desktop/bisq.css | 3 +- .../main/offer/offerbook/OfferBookView.java | 34 ++++++++++++++----- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 1424726c24..fca54c8adb 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -388,6 +388,7 @@ offerbook.createOfferToBuy.withFiat=Create new offer to buy {0} with {1} offerbook.createOfferToSell.forFiat=Create new offer to sell {0} for {1} offerbook.createOfferToBuy.withCrypto=Create new offer to sell {0} (buy {1}) offerbook.createOfferToSell.forCrypto=Create new offer to buy {0} (sell {1}) +offerbook.createOfferDisabled.tooltip=You can only create one offer at a time offerbook.takeOfferButton.tooltip=Take offer for {0} offerbook.yesCreateOffer=Yes, create offer diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index 8037df530b..51862da556 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -175,7 +175,8 @@ -fx-padding: 0 10 0 10; } -.tiny-button, .action-button.tiny-button { +.tiny-button, +.action-button.tiny-button { -fx-font-size: 0.769em; -fx-pref-height: 20; -fx-padding: 3 8 3 8; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 5c60d5f9b7..4998f64552 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -91,6 +91,7 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; @@ -146,6 +147,7 @@ public class OfferBookView extends ActivatableViewAndModel priceFeedUpdateCounterListener; private Subscription currencySelectionSubscriber; private static final int SHOW_ALL = 0; + private Label disabledCreateOfferButtonTooltip; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle @@ -200,13 +202,23 @@ public class OfferBookView extends ActivatableViewAndModel { - createOfferButton.setDisable(true); - offerActionHandler.onCreateOffer(selectedTradeCurrency, selectedPaymentMethod); + disableCreateOfferButton(); }) .secondaryActionButtonText(Res.get("offerbook.setupNewAccount")) .onSecondaryAction(() -> { @@ -614,8 +633,7 @@ public class OfferBookView extends ActivatableViewAndModel Date: Tue, 12 Oct 2021 11:50:10 +0200 Subject: [PATCH 16/38] Refactor account name generation --- .../paymentmethods/AdvancedCashForm.java | 18 +- .../components/paymentmethods/AssetsForm.java | 7 +- .../paymentmethods/CapitualForm.java | 9 +- .../paymentmethods/JapanBankTransferForm.java | 229 ++++++++---------- .../paymentmethods/MoneyGramForm.java | 8 +- .../paymentmethods/PaymentMethodForm.java | 7 +- .../fiataccounts/FiatAccountsView.java | 2 +- .../java/bisq/desktop/util/DisplayUtils.java | 12 + 8 files changed, 129 insertions(+), 163 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AdvancedCashForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AdvancedCashForm.java index f384b5a1c6..caf34bdf32 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AdvancedCashForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AdvancedCashForm.java @@ -34,8 +34,6 @@ import bisq.core.util.validation.InputValidator; import bisq.common.util.Tuple2; -import org.apache.commons.lang3.StringUtils; - import javafx.scene.control.Label; import javafx.scene.layout.FlowPane; import javafx.scene.layout.GridPane; @@ -62,8 +60,13 @@ public class AdvancedCashForm extends PaymentMethodForm { return gridRow; } - public AdvancedCashForm(PaymentAccount paymentAccount, AccountAgeWitnessService accountAgeWitnessService, AdvancedCashValidator advancedCashValidator, - InputValidator inputValidator, GridPane gridPane, int gridRow, CoinFormatter formatter) { + public AdvancedCashForm(PaymentAccount paymentAccount, + AccountAgeWitnessService accountAgeWitnessService, + AdvancedCashValidator advancedCashValidator, + InputValidator inputValidator, + GridPane gridPane, + int gridRow, + CoinFormatter formatter) { super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter); this.advancedCashAccount = (AdvancedCashAccount) paymentAccount; this.advancedCashValidator = advancedCashValidator; @@ -101,12 +104,7 @@ public class AdvancedCashForm extends PaymentMethodForm { @Override protected void autoFillNameTextField() { - if (useCustomAccountNameToggleButton != null && !useCustomAccountNameToggleButton.isSelected()) { - String accountNr = accountNrInputTextField.getText(); - accountNr = StringUtils.abbreviate(accountNr, 9); - String method = Res.get(paymentAccount.getPaymentMethod().getId()); - accountNameTextField.setText(method.concat(": ").concat(accountNr)); - } + setAccountNameWithString(accountNrInputTextField.getText()); } @Override diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java index 2a72811dea..625b267d4a 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java @@ -41,8 +41,6 @@ import bisq.core.util.validation.InputValidator; import bisq.common.UserThread; import bisq.common.util.Tuple3; -import org.apache.commons.lang3.StringUtils; - import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.TextField; @@ -53,6 +51,7 @@ import javafx.geometry.Insets; import javafx.util.StringConverter; +import static bisq.desktop.util.DisplayUtils.createAssetsAccountName; import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField; import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; import static bisq.desktop.util.FormBuilder.addLabelCheckBox; @@ -169,9 +168,7 @@ public class AssetsForm extends PaymentMethodForm { if (useCustomAccountNameToggleButton != null && !useCustomAccountNameToggleButton.isSelected()) { String currency = paymentAccount.getSingleTradeCurrency() != null ? paymentAccount.getSingleTradeCurrency().getCode() : ""; if (currency != null) { - String address = addressInputTextField.getText(); - address = StringUtils.abbreviate(address, 9); - accountNameTextField.setText(currency.concat(": ").concat(address)); + accountNameTextField.setText(createAssetsAccountName(currency, addressInputTextField.getText())); } } } diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/CapitualForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/CapitualForm.java index e4fa6163a4..4f7201cc54 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/CapitualForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/CapitualForm.java @@ -34,8 +34,6 @@ import bisq.core.util.validation.InputValidator; import bisq.common.util.Tuple2; -import org.apache.commons.lang3.StringUtils; - import javafx.scene.control.Label; import javafx.scene.layout.FlowPane; import javafx.scene.layout.GridPane; @@ -100,12 +98,7 @@ public class CapitualForm extends PaymentMethodForm { @Override protected void autoFillNameTextField() { - if (useCustomAccountNameToggleButton != null && !useCustomAccountNameToggleButton.isSelected()) { - String accountNr = accountNrInputTextField.getText(); - accountNr = StringUtils.abbreviate(accountNr, 9); - String method = Res.get(paymentAccount.getPaymentMethod().getId()); - accountNameTextField.setText(method.concat(": ").concat(accountNr)); - } + setAccountNameWithString(accountNrInputTextField.getText()); } @Override diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/JapanBankTransferForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/JapanBankTransferForm.java index 22eac439a5..bfbde452b1 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/JapanBankTransferForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/JapanBankTransferForm.java @@ -25,7 +25,6 @@ import bisq.desktop.util.validation.JapanBankAccountNameValidator; import bisq.desktop.util.validation.JapanBankAccountNumberValidator; import bisq.desktop.util.validation.JapanBankBranchCodeValidator; import bisq.desktop.util.validation.JapanBankBranchNameValidator; -import bisq.desktop.util.validation.JapanBankTransferValidator; import bisq.desktop.util.validation.LengthValidator; import bisq.core.account.witness.AccountAgeWitnessService; @@ -57,24 +56,17 @@ import javafx.util.StringConverter; import static bisq.desktop.util.FormBuilder.*; import static bisq.desktop.util.GUIUtil.getComboBoxButtonCell; -public class JapanBankTransferForm extends PaymentMethodForm -{ +public class JapanBankTransferForm extends PaymentMethodForm { private final JapanBankAccount japanBankAccount; - protected ComboBox bankComboBox, bankAccountTypeComboBox; - private InputTextField bankAccountNumberInputTextField; + protected ComboBox bankComboBox; - private JapanBankTransferValidator japanBankTransferValidator; - private JapanBankBranchNameValidator japanBankBranchNameValidator; - private JapanBankBranchCodeValidator japanBankBranchCodeValidator; - private JapanBankAccountNameValidator japanBankAccountNameValidator; - private JapanBankAccountNumberValidator japanBankAccountNumberValidator; + private final JapanBankBranchNameValidator japanBankBranchNameValidator; + private final JapanBankBranchCodeValidator japanBankBranchCodeValidator; + private final JapanBankAccountNameValidator japanBankAccountNameValidator; + private final JapanBankAccountNumberValidator japanBankAccountNumberValidator; - private LengthValidator lengthValidator; - private RegexValidator regexValidator; - - public static int addFormForBuyer(GridPane gridPane, int gridRow, // {{{ - PaymentAccountPayload paymentAccountPayload) - { + public static int addFormForBuyer(GridPane gridPane, int gridRow, + PaymentAccountPayload paymentAccountPayload) { JapanBankAccountPayload japanBankAccount = ((JapanBankAccountPayload) paymentAccountPayload); String bankText = japanBankAccount.getBankCode() + " " + japanBankAccount.getBankName(); @@ -90,30 +82,26 @@ public class JapanBankTransferForm extends PaymentMethodForm addCompactTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, Res.get("payment.japan.recipient"), accountNameText); return gridRow; - } // }}} + } public JapanBankTransferForm(PaymentAccount paymentAccount, - AccountAgeWitnessService accountAgeWitnessService, - JapanBankTransferValidator japanBankTransferValidator, - InputValidator inputValidator, GridPane gridPane, - int gridRow, CoinFormatter formatter) - { + AccountAgeWitnessService accountAgeWitnessService, + InputValidator inputValidator, GridPane gridPane, + int gridRow, CoinFormatter formatter) { super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter); this.japanBankAccount = (JapanBankAccount) paymentAccount; - this.japanBankTransferValidator = japanBankTransferValidator; this.japanBankBranchCodeValidator = new JapanBankBranchCodeValidator(); this.japanBankAccountNumberValidator = new JapanBankAccountNumberValidator(); - this.lengthValidator = new LengthValidator(); - this.regexValidator = new RegexValidator(); + LengthValidator lengthValidator = new LengthValidator(); + RegexValidator regexValidator = new RegexValidator(); this.japanBankBranchNameValidator = new JapanBankBranchNameValidator(lengthValidator, regexValidator); this.japanBankAccountNameValidator = new JapanBankAccountNameValidator(lengthValidator, regexValidator); } @Override - public void addFormForDisplayAccount() // {{{ - { + public void addFormForDisplayAccount() { gridRowFrom = gridRow; addTopLabelTextField(gridPane, ++gridRow, Res.get("payment.account.name"), @@ -128,37 +116,36 @@ public class JapanBankTransferForm extends PaymentMethodForm addBankAccountTypeDisplay(); addLimitations(true); - } // }}} - private void addBankDisplay() // {{{ - { + } + + private void addBankDisplay() { String bankText = japanBankAccount.getBankCode() + " " + japanBankAccount.getBankName(); TextField bankTextField = addCompactTopLabelTextField(gridPane, ++gridRow, JapanBankData.getString("bank"), bankText).second; bankTextField.setEditable(false); - } // }}} - private void addBankBranchDisplay() // {{{ - { + } + + private void addBankBranchDisplay() { String branchText = japanBankAccount.getBankBranchCode() + " " + japanBankAccount.getBankBranchName(); TextField branchTextField = addCompactTopLabelTextField(gridPane, ++gridRow, JapanBankData.getString("branch"), branchText).second; branchTextField.setEditable(false); - } // }}} - private void addBankAccountDisplay() // {{{ - { + } + + private void addBankAccountDisplay() { String accountText = japanBankAccount.getBankAccountNumber() + " " + japanBankAccount.getBankAccountName(); TextField accountTextField = addCompactTopLabelTextField(gridPane, ++gridRow, JapanBankData.getString("account"), accountText).second; accountTextField.setEditable(false); - } // }}} - private void addBankAccountTypeDisplay() // {{{ - { + } + + private void addBankAccountTypeDisplay() { TradeCurrency singleTradeCurrency = japanBankAccount.getSingleTradeCurrency(); String currency = singleTradeCurrency != null ? singleTradeCurrency.getNameAndCode() : "null"; String accountTypeText = currency + " " + japanBankAccount.getBankAccountType(); TextField accountTypeTextField = addCompactTopLabelTextField(gridPane, ++gridRow, JapanBankData.getString("account.type"), accountTypeText).second; accountTypeTextField.setEditable(false); - } // }}} + } @Override - public void addFormForAddAccount() // {{{ - { + public void addFormForAddAccount() { gridRowFrom = gridRow; addBankInput(); @@ -168,9 +155,9 @@ public class JapanBankTransferForm extends PaymentMethodForm addLimitations(false); addAccountNameTextFieldWithAutoFillToggleButton(); - } // }}} - private void addBankInput() // {{{ - { + } + + private void addBankInput() { gridRow++; Tuple4> tuple4 = addTopLabelTextFieldAutocompleteComboBox(gridPane, gridRow, JapanBankData.getString("bank.code"), JapanBankData.getString("bank.name"), 10); @@ -185,14 +172,13 @@ public class JapanBankTransferForm extends PaymentMethodForm bankComboBox = tuple4.fourth; bankComboBox.setPromptText(JapanBankData.getString("bank.select")); bankComboBox.setButtonCell(getComboBoxButtonCell(JapanBankData.getString("bank.name"), bankComboBox)); - bankComboBox.getEditor().focusedProperty().addListener(observable -> { - bankComboBox.setPromptText(""); - }); - bankComboBox.setConverter(new StringConverter() { + bankComboBox.getEditor().focusedProperty().addListener(observable -> bankComboBox.setPromptText("")); + bankComboBox.setConverter(new StringConverter<>() { @Override public String toString(String bank) { return bank != null ? bank : ""; } + public String fromString(String s) { return s != null ? s : ""; } @@ -208,8 +194,7 @@ public class JapanBankTransferForm extends PaymentMethodForm // parse first 4 characters as bank code String bankCode = StringUtils.substring(bank, 0, 4); - if (bankCode != null) - { + if (bankCode != null) { // set bank code field to this value bankCodeField.setText(bankCode); // save to payload @@ -217,26 +202,20 @@ public class JapanBankTransferForm extends PaymentMethodForm // parse remainder as bank name String bankNameFull = StringUtils.substringAfter(bank, JapanBankData.SPACE); - if (bankNameFull != null) - { - // parse beginning as Japanese bank name - String bankNameJa = StringUtils.substringBefore(bankNameFull, JapanBankData.SPACE); - if (bankNameJa != null) - { - // set bank name field to this value - bankComboBox.getEditor().setText(bankNameJa); - // save to payload - japanBankAccount.setBankName(bankNameJa); - } - } + // parse beginning as Japanese bank name + String bankNameJa = StringUtils.substringBefore(bankNameFull, JapanBankData.SPACE); + // set bank name field to this value + bankComboBox.getEditor().setText(bankNameJa); + // save to payload + japanBankAccount.setBankName(bankNameJa); } updateFromInputs(); }); - } // }}} - private void addBankBranchInput() // {{{ - { + } + + private void addBankBranchInput() { gridRow++; Tuple2 tuple2 = addInputTextFieldInputTextField(gridPane, gridRow, JapanBankData.getString("branch.code"), JapanBankData.getString("branch.name")); @@ -259,14 +238,14 @@ public class JapanBankTransferForm extends PaymentMethodForm japanBankAccount.setBankBranchName(newValue); updateFromInputs(); }); - } // }}} - private void addBankAccountInput() // {{{ - { + } + + private void addBankAccountInput() { gridRow++; Tuple2 tuple2 = addInputTextFieldInputTextField(gridPane, gridRow, JapanBankData.getString("account.number"), JapanBankData.getString("account.name")); // account number - bankAccountNumberInputTextField = tuple2.first; + InputTextField bankAccountNumberInputTextField = tuple2.first; bankAccountNumberInputTextField.setValidator(japanBankAccountNumberValidator); bankAccountNumberInputTextField.setPrefWidth(200); bankAccountNumberInputTextField.setMaxWidth(200); @@ -284,9 +263,9 @@ public class JapanBankTransferForm extends PaymentMethodForm japanBankAccount.setBankAccountName(newValue); updateFromInputs(); }); - } // }}} - private void addBankAccountTypeInput() // {{{ - { + } + + private void addBankAccountTypeInput() { // account currency gridRow++; @@ -299,13 +278,13 @@ public class JapanBankTransferForm extends PaymentMethodForm ToggleGroup toggleGroup = new ToggleGroup(); Tuple3 tuple3 = - addTopLabelRadioButtonRadioButton( - gridPane, gridRow, toggleGroup, - JapanBankData.getString("account.type.select"), - JapanBankData.getString("account.type.futsu"), - JapanBankData.getString("account.type.touza"), - 0 - ); + addTopLabelRadioButtonRadioButton( + gridPane, gridRow, toggleGroup, + JapanBankData.getString("account.type.select"), + JapanBankData.getString("account.type.futsu"), + JapanBankData.getString("account.type.touza"), + 0 + ); toggleGroup.getToggles().get(0).setSelected(true); japanBankAccount.setBankAccountType(JapanBankData.getString("account.type.futsu.ja")); @@ -314,66 +293,60 @@ public class JapanBankTransferForm extends PaymentMethodForm RadioButton touza = tuple3.third; toggleGroup.selectedToggleProperty().addListener - ( - (ov, oldValue, newValue) -> - { - if (futsu.isSelected()) - japanBankAccount.setBankAccountType(JapanBankData.getString("account.type.futsu.ja")); - if (touza.isSelected()) - japanBankAccount.setBankAccountType(JapanBankData.getString("account.type.touza.ja")); - } - ); - } // }}} + ( + (ov, oldValue, newValue) -> + { + if (futsu.isSelected()) + japanBankAccount.setBankAccountType(JapanBankData.getString("account.type.futsu.ja")); + if (touza.isSelected()) + japanBankAccount.setBankAccountType(JapanBankData.getString("account.type.touza.ja")); + } + ); + } @Override - public void updateFromInputs() // {{{ - { + public void updateFromInputs() { System.out.println("JapanBankTransferForm: updateFromInputs()"); - System.out.println("bankName: "+japanBankAccount.getBankName()); - System.out.println("bankCode: "+japanBankAccount.getBankCode()); - System.out.println("bankBranchName: "+japanBankAccount.getBankBranchName()); - System.out.println("bankBranchCode: "+japanBankAccount.getBankBranchCode()); - System.out.println("bankAccountType: "+japanBankAccount.getBankAccountType()); - System.out.println("bankAccountName: "+japanBankAccount.getBankAccountName()); - System.out.println("bankAccountNumber: "+japanBankAccount.getBankAccountNumber()); + System.out.println("bankName: " + japanBankAccount.getBankName()); + System.out.println("bankCode: " + japanBankAccount.getBankCode()); + System.out.println("bankBranchName: " + japanBankAccount.getBankBranchName()); + System.out.println("bankBranchCode: " + japanBankAccount.getBankBranchCode()); + System.out.println("bankAccountType: " + japanBankAccount.getBankAccountType()); + System.out.println("bankAccountName: " + japanBankAccount.getBankAccountName()); + System.out.println("bankAccountNumber: " + japanBankAccount.getBankAccountNumber()); super.updateFromInputs(); - } // }}} + } @Override - protected void autoFillNameTextField() // {{{ - { - if (useCustomAccountNameToggleButton != null && !useCustomAccountNameToggleButton.isSelected()) - { + protected void autoFillNameTextField() { + if (useCustomAccountNameToggleButton != null && !useCustomAccountNameToggleButton.isSelected()) { accountNameTextField.setText( Res.get(paymentAccount.getPaymentMethod().getId()) - .concat(": ") - .concat(japanBankAccount.getBankName()) - .concat(" ") - .concat(japanBankAccount.getBankBranchName()) - .concat(" ") - .concat(japanBankAccount.getBankAccountNumber()) - .concat(" ") - .concat(japanBankAccount.getBankAccountName()) + .concat(": ") + .concat(japanBankAccount.getBankName()) + .concat(" ") + .concat(japanBankAccount.getBankBranchName()) + .concat(" ") + .concat(japanBankAccount.getBankAccountNumber()) + .concat(" ") + .concat(japanBankAccount.getBankAccountName()) ); } - } // }}} + } @Override - public void updateAllInputsValid() // {{{ - { + public void updateAllInputsValid() { boolean result = - ( - isAccountNameValid() && - inputValidator.validate(japanBankAccount.getBankCode()).isValid && - inputValidator.validate(japanBankAccount.getBankName()).isValid && - japanBankBranchCodeValidator.validate(japanBankAccount.getBankBranchCode()).isValid && - japanBankBranchNameValidator.validate(japanBankAccount.getBankBranchName()).isValid && - japanBankAccountNumberValidator.validate(japanBankAccount.getBankAccountNumber()).isValid && - japanBankAccountNameValidator.validate(japanBankAccount.getBankAccountName()).isValid && - inputValidator.validate(japanBankAccount.getBankAccountType()).isValid - ); + ( + isAccountNameValid() && + inputValidator.validate(japanBankAccount.getBankCode()).isValid && + inputValidator.validate(japanBankAccount.getBankName()).isValid && + japanBankBranchCodeValidator.validate(japanBankAccount.getBankBranchCode()).isValid && + japanBankBranchNameValidator.validate(japanBankAccount.getBankBranchName()).isValid && + japanBankAccountNumberValidator.validate(japanBankAccount.getBankAccountNumber()).isValid && + japanBankAccountNameValidator.validate(japanBankAccount.getBankAccountName()).isValid && + inputValidator.validate(japanBankAccount.getBankAccountType()).isValid + ); allInputsValid.set(result); - } // }}} + } } - -// vim:ts=4:sw=4:expandtab:foldmethod=marker:nowrap: diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/MoneyGramForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/MoneyGramForm.java index 799a79a35f..f02e59dbad 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/MoneyGramForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/MoneyGramForm.java @@ -37,8 +37,6 @@ import bisq.core.util.validation.InputValidator; import bisq.common.util.Tuple2; -import org.apache.commons.lang3.StringUtils; - import javafx.scene.control.Label; import javafx.scene.layout.FlowPane; import javafx.scene.layout.GridPane; @@ -172,11 +170,7 @@ public class MoneyGramForm extends PaymentMethodForm { @Override protected void autoFillNameTextField() { - if (useCustomAccountNameToggleButton != null && !useCustomAccountNameToggleButton.isSelected()) { - accountNameTextField.setText(Res.get(paymentAccount.getPaymentMethod().getId()) - .concat(": ") - .concat(StringUtils.abbreviate(holderNameInputTextField.getText(), 9))); - } + setAccountNameWithString(holderNameInputTextField.getText()); } @Override diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java index df2a8fd2d3..5909dd0f54 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java @@ -73,6 +73,7 @@ import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import static bisq.desktop.util.DisplayUtils.createAccountName; import static bisq.desktop.util.FormBuilder.*; @Slf4j @@ -287,10 +288,8 @@ public abstract class PaymentMethodForm { void setAccountNameWithString(String name) { if (useCustomAccountNameToggleButton != null && !useCustomAccountNameToggleButton.isSelected()) { - name = name.trim(); - name = StringUtils.abbreviate(name, 9); - String method = Res.get(paymentAccount.getPaymentMethod().getId()); - accountNameTextField.setText(method.concat(": ").concat(name)); + String accountName = createAccountName(paymentAccount.getPaymentMethod().getId(), name); + accountNameTextField.setText(accountName); } } diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java index baf3dd625f..4742864e44 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java @@ -520,7 +520,7 @@ public class FiatAccountsView extends PaymentAccountsView Date: Thu, 4 Nov 2021 10:41:46 +0100 Subject: [PATCH 17/38] Simplify and clean up data models Conflicts: desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java --- .../AltCoinAccountsDataModel.java | 29 ++----------------- .../fiataccounts/FiatAccountsDataModel.java | 17 +++-------- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java index da91488357..183d4f1419 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java @@ -20,9 +20,7 @@ package bisq.desktop.main.account.content.altcoinaccounts; import bisq.desktop.common.model.ActivatableDataModel; import bisq.desktop.util.GUIUtil; -import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.locale.CryptoCurrency; -import bisq.core.locale.FiatCurrency; import bisq.core.locale.TradeCurrency; import bisq.core.offer.OpenOfferManager; import bisq.core.payment.AssetAccount; @@ -44,7 +42,6 @@ import javafx.collections.SetChangeListener; import java.util.ArrayList; import java.util.Comparator; -import java.util.List; import java.util.stream.Collectors; class AltCoinAccountsDataModel extends ActivatableDataModel { @@ -53,7 +50,6 @@ class AltCoinAccountsDataModel extends ActivatableDataModel { private final Preferences preferences; private final OpenOfferManager openOfferManager; private final TradeManager tradeManager; - private final AccountAgeWitnessService accountAgeWitnessService; final ObservableList paymentAccounts = FXCollections.observableArrayList(); private final SetChangeListener setChangeListener; private final String accountsFileName = "AltcoinPaymentAccounts"; @@ -65,14 +61,12 @@ class AltCoinAccountsDataModel extends ActivatableDataModel { Preferences preferences, OpenOfferManager openOfferManager, TradeManager tradeManager, - AccountAgeWitnessService accountAgeWitnessService, PersistenceProtoResolver persistenceProtoResolver, CorruptedStorageFileHandler corruptedStorageFileHandler) { this.user = user; this.preferences = preferences; this.openOfferManager = openOfferManager; this.tradeManager = tradeManager; - this.accountAgeWitnessService = accountAgeWitnessService; this.persistenceProtoResolver = persistenceProtoResolver; this.corruptedStorageFileHandler = corruptedStorageFileHandler; setChangeListener = change -> fillAndSortPaymentAccounts(); @@ -105,25 +99,8 @@ class AltCoinAccountsDataModel extends ActivatableDataModel { public void onSaveNewAccount(PaymentAccount paymentAccount) { TradeCurrency singleTradeCurrency = paymentAccount.getSingleTradeCurrency(); - List tradeCurrencies = paymentAccount.getTradeCurrencies(); - if (singleTradeCurrency != null) { - if (singleTradeCurrency instanceof FiatCurrency) - preferences.addFiatCurrency((FiatCurrency) singleTradeCurrency); - else - preferences.addCryptoCurrency((CryptoCurrency) singleTradeCurrency); - } else if (tradeCurrencies != null && !tradeCurrencies.isEmpty()) { - tradeCurrencies.forEach(tradeCurrency -> { - if (tradeCurrency instanceof FiatCurrency) - preferences.addFiatCurrency((FiatCurrency) tradeCurrency); - else - preferences.addCryptoCurrency((CryptoCurrency) tradeCurrency); - }); - } - + preferences.addCryptoCurrency((CryptoCurrency) singleTradeCurrency); user.addPaymentAccount(paymentAccount); - - if (!(paymentAccount instanceof AssetAccount)) - accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); } public boolean onDeleteAccount(PaymentAccount paymentAccount) { @@ -147,9 +124,9 @@ class AltCoinAccountsDataModel extends ActivatableDataModel { public void exportAccounts(Stage stage) { if (user.getPaymentAccounts() != null) { - ArrayList accounts = new ArrayList<>(user.getPaymentAccounts().stream() + ArrayList accounts = user.getPaymentAccounts().stream() .filter(paymentAccount -> paymentAccount instanceof AssetAccount) - .collect(Collectors.toList())); + .collect(Collectors.toCollection(ArrayList::new)); GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler); } } diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java index f3c30abc36..b114c4cbbc 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java @@ -21,7 +21,6 @@ import bisq.desktop.common.model.ActivatableDataModel; import bisq.desktop.util.GUIUtil; import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.locale.CryptoCurrency; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.FiatCurrency; import bisq.core.locale.TradeCurrency; @@ -109,22 +108,14 @@ class FiatAccountsDataModel extends ActivatableDataModel { TradeCurrency singleTradeCurrency = paymentAccount.getSingleTradeCurrency(); List tradeCurrencies = paymentAccount.getTradeCurrencies(); if (singleTradeCurrency != null) { - if (singleTradeCurrency instanceof FiatCurrency) - preferences.addFiatCurrency((FiatCurrency) singleTradeCurrency); - else - preferences.addCryptoCurrency((CryptoCurrency) singleTradeCurrency); + preferences.addFiatCurrency((FiatCurrency) singleTradeCurrency); } else if (tradeCurrencies != null && !tradeCurrencies.isEmpty()) { if (tradeCurrencies.contains(CurrencyUtil.getDefaultTradeCurrency())) paymentAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency()); else paymentAccount.setSelectedTradeCurrency(tradeCurrencies.get(0)); - tradeCurrencies.forEach(tradeCurrency -> { - if (tradeCurrency instanceof FiatCurrency) - preferences.addFiatCurrency((FiatCurrency) tradeCurrency); - else - preferences.addCryptoCurrency((CryptoCurrency) tradeCurrency); - }); + tradeCurrencies.forEach(tradeCurrency -> preferences.addFiatCurrency((FiatCurrency) tradeCurrency)); } user.addPaymentAccount(paymentAccount); @@ -154,9 +145,9 @@ class FiatAccountsDataModel extends ActivatableDataModel { public void exportAccounts(Stage stage) { if (user.getPaymentAccounts() != null) { - ArrayList accounts = new ArrayList<>(user.getPaymentAccounts().stream() + ArrayList accounts = user.getPaymentAccounts().stream() .filter(paymentAccount -> !(paymentAccount instanceof AssetAccount)) - .collect(Collectors.toList())); + .collect(Collectors.toCollection(ArrayList::new)); GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler); } } From b7c4e5ed1718da25dd278e011b8f74a5a099a49b Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 4 Nov 2021 10:46:27 +0100 Subject: [PATCH 18/38] Create BSQ account and take offer if not existing Conflicts: desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java desktop/src/main/java/bisq/desktop/util/DisplayUtils.java --- .../resources/i18n/displayStrings.properties | 2 + .../components/paymentmethods/AssetsForm.java | 5 +- .../main/offer/bisq_v1/MutableOfferView.java | 11 ++- .../bisq_v1/takeoffer/TakeOfferDataModel.java | 4 + .../bisq_v1/takeoffer/TakeOfferView.java | 20 ++++- .../bisq_v1/takeoffer/TakeOfferViewModel.java | 4 + .../main/offer/offerbook/OfferBookView.java | 73 +++++++++++-------- .../offer/offerbook/OfferBookViewModel.java | 52 +++++++++++-- .../java/bisq/desktop/util/DisplayUtils.java | 8 +- 9 files changed, 131 insertions(+), 48 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index fca54c8adb..849ed0a3d7 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -336,6 +336,7 @@ market.trades.showVolumeInUSD=Show volume in USD offerbook.createOffer=Create offer offerbook.takeOffer=Take offer +offerbook.takeOffer.createAccount=Create account and take offer offerbook.takeOfferToBuy=Take offer to buy {0} offerbook.takeOfferToSell=Take offer to sell {0} offerbook.trader=Trader @@ -403,6 +404,7 @@ offerbook.warning.noTradingAccountForCurrency.headline=No payment account for se offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead? offerbook.warning.noMatchingAccount.headline=No matching payment account. offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now? +offerbook.warning.noMatchingBsqAccount.msg=This offer uses BSQ as a payment method you haven't set up yet. \n\nWould you like to automatically create an account now? offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due to counterparty trade restrictions diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java index 625b267d4a..30f32c9ee5 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java @@ -166,10 +166,7 @@ public class AssetsForm extends PaymentMethodForm { @Override protected void autoFillNameTextField() { if (useCustomAccountNameToggleButton != null && !useCustomAccountNameToggleButton.isSelected()) { - String currency = paymentAccount.getSingleTradeCurrency() != null ? paymentAccount.getSingleTradeCurrency().getCode() : ""; - if (currency != null) { - accountNameTextField.setText(createAssetsAccountName(currency, addressInputTextField.getText())); - } + accountNameTextField.setText(createAssetsAccountName(paymentAccount, addressInputTextField.getText())); } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index 8310f49cbb..1789d58ecb 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -274,6 +274,11 @@ public abstract class MutableOfferView> exten buyBsqBox.setManaged(false); } + if (!model.isShowBuyBsqHint()) { + buyBsqBox.setVisible(false); + buyBsqBox.setManaged(false); + } + Label popOverLabel = OfferViewUtil.createPopOverLabel(Res.get("createOffer.triggerPrice.tooltip")); triggerPriceInfoInputTextField.setContentForPopOver(popOverLabel, AwesomeIcon.SHIELD); } @@ -405,11 +410,11 @@ public abstract class MutableOfferView> exten cancelButton1.setVisible(false); cancelButton1.setManaged(false); cancelButton1.setOnAction(null); - buyBsqBox.setVisible(false); - buyBsqBox.setManaged(false); tradeFeeInBtcToggle.setMouseTransparent(true); tradeFeeInBsqToggle.setMouseTransparent(true); + buyBsqBox.setVisible(false); + buyBsqBox.setManaged(false); setDepositTitledGroupBg.setVisible(false); setDepositTitledGroupBg.setManaged(false); @@ -896,6 +901,7 @@ public abstract class MutableOfferView> exten tradeFeeInBsqToggle.setVisible(newValue); if (model.isShowBuyBsqHint()) { buyBsqBox.setVisible(newValue); + buyBsqBox.setManaged(newValue); } } }; @@ -1102,6 +1108,7 @@ public abstract class MutableOfferView> exten Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( navigation, preferences); buyBsqBox = buyBsqButtonBox.second; + buyBsqBox.setManaged(false); buyBsqBox.setVisible(false); advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), getTradeFeeFieldsBox(), buyBsqBox); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java index c5914b0869..a0ced384b1 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java @@ -712,4 +712,8 @@ class TakeOfferDataModel extends OfferDataModel { public boolean isBsqForFeeAvailable() { return offerUtil.isBsqForTakerFeeAvailable(amount.get()); } + + public boolean isBuyBsqOffer() { + return !isBuyOffer() && getOffer().getCurrencyCode().equals("BSQ"); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java index 2c9f6a2e90..f87aa496cd 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java @@ -132,7 +132,7 @@ public class TakeOfferView extends ActivatableViewAndModel buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( navigation, model.dataModel.preferences); + buyBsqBox = buyBsqButtonBox.second; + buyBsqBox.setManaged(false); + buyBsqBox.setVisible(false); - advancedOptionsBox.getChildren().addAll(getTradeFeeFieldsBox(), buyBsqButtonBox.second); + advancedOptionsBox.getChildren().addAll(getTradeFeeFieldsBox(), buyBsqBox); } private void addButtons() { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java index 1376b9baec..61c52a4355 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java @@ -800,4 +800,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel im Res.get("shared.aboveInPercent"); } } + + public boolean isShowBuyBsqHint() { + return !dataModel.isBsqForFeeAvailable() && !dataModel.isBuyBsqOffer(); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 4998f64552..191068be48 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -33,6 +33,7 @@ import bisq.desktop.components.PeerInfoIconTrading; import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.MainView; import bisq.desktop.main.account.AccountView; +import bisq.desktop.main.account.content.altcoinaccounts.AltCoinAccountsView; import bisq.desktop.main.account.content.fiataccounts.FiatAccountsView; import bisq.desktop.main.funds.FundsView; import bisq.desktop.main.funds.withdrawal.WithdrawalView; @@ -136,11 +137,15 @@ public class OfferBookView extends ActivatableViewAndModel paymentMethodComboBox; private AutoTooltipButton createOfferButton; private AutoTooltipSlideToggleButton matchingOffersToggle; - private AutoTooltipTableColumn amountColumn, volumeColumn, marketColumn, - priceColumn, paymentMethodColumn, depositColumn, signingStateColumn, avatarColumn; + private AutoTooltipTableColumn amountColumn; + private AutoTooltipTableColumn volumeColumn; + private AutoTooltipTableColumn marketColumn; + private AutoTooltipTableColumn priceColumn; + private AutoTooltipTableColumn depositColumn; + private AutoTooltipTableColumn signingStateColumn; + private AutoTooltipTableColumn avatarColumn; private TableView tableView; - private OfferView.OfferActionHandler offerActionHandler; private int gridRow = 0; private Label nrOfOffersLabel; private ListChangeListener offerListListener; @@ -243,7 +248,7 @@ public class OfferBookView extends ActivatableViewAndModel paymentMethodColumn = getPaymentMethodColumn(); tableView.getColumns().add(paymentMethodColumn); depositColumn = getDepositColumn(); tableView.getColumns().add(depositColumn); @@ -559,7 +564,7 @@ public class OfferBookView extends ActivatableViewAndModel { - disableCreateOfferButton(); - }) + .onAction(this::disableCreateOfferButton) .secondaryActionButtonText(Res.get("offerbook.setupNewAccount")) .onSecondaryAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); @@ -639,17 +642,12 @@ public class OfferBookView extends ActivatableViewAndModel offerActionHandler.onTakeOffer(offer)) + .onAction(() -> model.onTakeOffer(offer)) .show(); } else { - offerActionHandler.onTakeOffer(offer); + model.onTakeOffer(offer); } } } @@ -746,14 +745,26 @@ public class OfferBookView extends ActivatableViewAndModel { - navigation.setReturnPath(navigation.getCurrentPath()); - navigation.navigateTo(MainView.class, AccountView.class, target); - }).show(); + private void openPopupForMissingAccountSetup(Offer offer) { + String headline = Res.get("offerbook.warning.noMatchingAccount.headline"); + + if (offer.getCurrencyCode().equals("BSQ")) { + new Popup().headLine(headline) + .instruction(Res.get("offerbook.warning.noMatchingBsqAccount.msg")) + .actionButtonText(Res.get("offerbook.takeOffer.createAccount")) + .onAction(() -> model.createBsqAccountAndTakeOffer(offer)).show(); + } else { + + var accountViewClass = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) ? FiatAccountsView.class : AltCoinAccountsView.class; + + new Popup().headLine(headline) + .instruction(Res.get("offerbook.warning.noMatchingAccount.msg")) + .actionButtonTextWithGoTo("navigation.account") + .onAction(() -> { + navigation.setReturnPath(navigation.getCurrentPath()); + navigation.navigateTo(MainView.class, AccountView.class, accountViewClass); + }).show(); + } } /////////////////////////////////////////////////////////////////////////////////////////// @@ -1086,13 +1097,11 @@ public class OfferBookView extends ActivatableViewAndModel { - // ugly hack to get the icon clickable when deactivated - if (!(e.getTarget() instanceof ImageView || e.getTarget() instanceof Canvas)) - onShowInfo(offer, canTakeOfferResult); - }); - } + tableRow.setOnMousePressed(e -> { + // ugly hack to get the icon clickable when deactivated + if (!(e.getTarget() instanceof ImageView || e.getTarget() instanceof Canvas)) + onShowInfo(offer, canTakeOfferResult); + }); } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java index 1e9a4d226b..5bd62a5b7d 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java @@ -20,13 +20,17 @@ package bisq.desktop.main.offer.offerbook; import bisq.desktop.Navigation; import bisq.desktop.common.model.ActivatableViewModel; import bisq.desktop.main.MainView; +import bisq.desktop.main.offer.OfferView; +import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.settings.SettingsView; import bisq.desktop.main.settings.preferences.PreferencesView; import bisq.desktop.util.DisplayUtils; import bisq.desktop.util.GUIUtil; import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.api.CoreApi; import bisq.core.btc.setup.WalletsSetup; +import bisq.core.btc.wallet.BsqWalletService; import bisq.core.locale.BankUtil; import bisq.core.locale.CountryUtil; import bisq.core.locale.CryptoCurrency; @@ -111,6 +115,8 @@ class OfferBookViewModel extends ActivatableViewModel { private final BsqFormatter bsqFormatter; private final FilteredList filteredItems; + private final BsqWalletService bsqWalletService; + private final CoreApi coreApi; private final SortedList sortedItems; private final ListChangeListener tradeCurrencyListChangeListener; private final ListChangeListener filterItemsListener; @@ -121,6 +127,8 @@ class OfferBookViewModel extends ActivatableViewModel { final StringProperty tradeCurrencyCode = new SimpleStringProperty(); + private OfferView.OfferActionHandler offerActionHandler; + // If id is empty string we ignore filter (display all methods) PaymentMethod selectedPaymentMethod = getShowAllEntryForPaymentMethod(); @@ -154,7 +162,9 @@ class OfferBookViewModel extends ActivatableViewModel { PriceUtil priceUtil, OfferFilterService offerFilterService, @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter, - BsqFormatter bsqFormatter) { + BsqFormatter bsqFormatter, + BsqWalletService bsqWalletService, + CoreApi coreApi) { super(); this.openOfferManager = openOfferManager; @@ -173,6 +183,8 @@ class OfferBookViewModel extends ActivatableViewModel { this.bsqFormatter = bsqFormatter; this.filteredItems = new FilteredList<>(offerBook.getOfferBookListItems()); + this.bsqWalletService = bsqWalletService; + this.coreApi = coreApi; this.sortedItems = new SortedList<>(filteredItems); tradeCurrencyListChangeListener = c -> fillAllTradeCurrencies(); @@ -207,7 +219,7 @@ class OfferBookViewModel extends ActivatableViewModel { .filter(o -> o.getOffer().isUseMarketBasedPrice()) .max(Comparator.comparing(o -> new DecimalFormat("#0.00").format(o.getOffer().getMarketPriceMargin() * 100).length())); - highestMarketPriceMarginOffer.ifPresent(offerBookListItem -> maxPlacesForMarketPriceMargin.set(formatMarketPriceMargin(offerBookListItem.getOffer(), false).length())); + highestMarketPriceMarginOffer.ifPresent(offerBookListItem -> maxPlacesForMarketPriceMargin.set(formatMarketPriceMargin(offerBookListItem.getOffer()).length())); }; } @@ -413,16 +425,12 @@ class OfferBookViewModel extends ActivatableViewModel { return priceUtil.getMarketBasedPrice(offer, direction); } - String formatMarketPriceMargin(Offer offer, boolean decimalAligned) { + String formatMarketPriceMargin(Offer offer) { String postFix = ""; if (offer.isUseMarketBasedPrice()) { postFix = " (" + FormattingUtils.formatPercentagePrice(offer.getMarketPriceMargin()) + ")"; } - if (decimalAligned) { - postFix = FormattingUtils.fillUpPlacesWithEmptyStrings(postFix, maxPlacesForMarketPriceMargin.get()); - } - return postFix; } @@ -670,4 +678,34 @@ class OfferBookViewModel extends ActivatableViewModel { PaymentMethod getShowAllEntryForPaymentMethod() { return PaymentMethod.getDummyPaymentMethod(GUIUtil.SHOW_ALL_FLAG); } + + public void createBsqAccountAndTakeOffer(Offer offer) { + var unusedBsqAddressAsString = bsqWalletService.getUnusedBsqAddressAsString(); + + // create required BSQ payment account + boolean isInstantPaymentMethod = offer.getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS_INSTANT); + coreApi.createCryptoCurrencyPaymentAccount(DisplayUtils.createAssetsAccountName("BSQ", unusedBsqAddressAsString), + "BSQ", + unusedBsqAddressAsString, + isInstantPaymentMethod); + + if (isInstantPaymentMethod) { + new Popup().information(Res.get("payment.altcoin.tradeInstant.popup")).show(); + } + + // take offer + onTakeOffer(offer); + } + + public void setOfferActionHandler(OfferView.OfferActionHandler offerActionHandler) { + this.offerActionHandler = offerActionHandler; + } + + public void onCreateOffer() { + offerActionHandler.onCreateOffer(getSelectedTradeCurrency(), selectedPaymentMethod); + } + + public void onTakeOffer(Offer offer) { + offerActionHandler.onTakeOffer(offer); + } } diff --git a/desktop/src/main/java/bisq/desktop/util/DisplayUtils.java b/desktop/src/main/java/bisq/desktop/util/DisplayUtils.java index 80f159fd48..fb9674f9dd 100644 --- a/desktop/src/main/java/bisq/desktop/util/DisplayUtils.java +++ b/desktop/src/main/java/bisq/desktop/util/DisplayUtils.java @@ -6,8 +6,9 @@ import bisq.core.locale.Res; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; import bisq.core.offer.Offer; -import bisq.core.offer.OfferDirection; import bisq.core.util.FormattingUtils; +import bisq.core.offer.OfferDirection; +import bisq.core.payment.PaymentAccount; import bisq.core.util.ParsingUtils; import bisq.core.util.VolumeUtil; import bisq.core.util.coin.CoinFormatter; @@ -259,6 +260,11 @@ public class DisplayUtils { return method.concat(": ").concat(name); } + public static String createAssetsAccountName(PaymentAccount paymentAccount, String address) { + String currency = paymentAccount.getSingleTradeCurrency() != null ? paymentAccount.getSingleTradeCurrency().getCode() : ""; + return createAssetsAccountName(currency, address); + } + public static String createAssetsAccountName(String currency, String address) { address = StringUtils.abbreviate(address, 9); return currency.concat(": ").concat(address); From 55826cb4a23ff374aed6d4f4ed4f63ee5c576ffa Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 13 Oct 2021 12:46:51 +0200 Subject: [PATCH 19/38] Fix failing tests --- .../offerbook/OfferBookViewModelTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java index d8fd3ca11a..5e5e25cb8f 100644 --- a/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java @@ -239,7 +239,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); assertEquals(0, model.maxPlacesForAmount.intValue()); } @@ -253,7 +253,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); model.activate(); assertEquals(6, model.maxPlacesForAmount.intValue()); @@ -271,7 +271,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); model.activate(); assertEquals(15, model.maxPlacesForAmount.intValue()); @@ -290,7 +290,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); assertEquals(0, model.maxPlacesForVolume.intValue()); } @@ -304,7 +304,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); model.activate(); assertEquals(5, model.maxPlacesForVolume.intValue()); @@ -322,7 +322,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); model.activate(); assertEquals(9, model.maxPlacesForVolume.intValue()); @@ -341,7 +341,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); assertEquals(0, model.maxPlacesForPrice.intValue()); } @@ -355,7 +355,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); model.activate(); assertEquals(7, model.maxPlacesForPrice.intValue()); @@ -373,7 +373,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); assertEquals(0, model.maxPlacesForMarketPriceMargin.intValue()); } @@ -408,7 +408,7 @@ public class OfferBookViewModelTest { offerBookListItems.addAll(item1, item2); final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, priceFeedService, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); model.activate(); assertEquals(8, model.maxPlacesForMarketPriceMargin.intValue()); //" (1.97%)" @@ -429,7 +429,7 @@ public class OfferBookViewModelTest { when(priceFeedService.getMarketPrice(anyString())).thenReturn(new MarketPrice("USD", 12684.0450, Instant.now().getEpochSecond(), true)); final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null, - null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter()); + null, null, null, getPriceUtil(), null, coinFormatter, new BsqFormatter(), null, null); final OfferBookListItem item = make(btcBuyItem.but( with(useMarketBasedPrice, true), From e17fd9ef54646b6e3084864f585290b0850002b0 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 15 Oct 2021 11:10:18 +0200 Subject: [PATCH 20/38] Add informational popup after account creation --- .../resources/i18n/displayStrings.properties | 7 +++++++ .../main/offer/offerbook/OfferBookView.java | 13 +++++++++++- .../offer/offerbook/OfferBookViewModel.java | 20 +++++++------------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 849ed0a3d7..fc6a23dfe5 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -443,6 +443,13 @@ offerbook.info.buyAtFixedPrice=You will buy at this fixed price. offerbook.info.sellAtFixedPrice=You will sell at this fixed price. offerbook.info.noArbitrationInUserLanguage=In case of a dispute, please note that arbitration for this offer will be handled in {0}. Language is currently set to {1}. offerbook.info.roundedFiatVolume=The amount was rounded to increase the privacy of your trade. +offerbook.info.accountCreated.headline=Congratulations +offerbook.info.accountCreated.message=You''ve just successfully created a BSQ payment account.\n\ + Your account can be found under Account > Altcoins Accounts > {0} and your BSQ wallet under DAO > BSQ Wallet.\n\n +offerbook.info.accountCreated.tradeInstant=As you've chosen to take a BSQ instant offer a BSQ instant account was created for you. \ + For instant trading it is required that both trading peers are online to be able \ + to complete the trade in less than 1 hour.\n\n +offerbook.info.accountCreated.takeOffer=You can no proceed to take this offer after closing this popup. offerbook.bsqSwap.createOffer=Create Bsq swap offer diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 191068be48..4d5c9055eb 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -752,7 +752,18 @@ public class OfferBookView extends ActivatableViewAndModel model.createBsqAccountAndTakeOffer(offer)).show(); + .onAction(() -> { + var bsqAccount = model.createBsqAccount(offer); + var message = Res.get("offerbook.info.accountCreated.message", bsqAccount.getAccountName()); + if (model.isInstantPaymentMethod(offer)) { + message += Res.get("offerbook.info.accountCreated.tradeInstant"); + } + message += Res.get("offerbook.info.accountCreated.takeOffer"); + new Popup().headLine(Res.get("offerbook.info.accountCreated.headline")) + .information(message) + .onClose(() -> model.onTakeOffer(offer)) + .show(); + }).show(); } else { var accountViewClass = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) ? FiatAccountsView.class : AltCoinAccountsView.class; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java index 5bd62a5b7d..51c0da573a 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java @@ -21,7 +21,6 @@ import bisq.desktop.Navigation; import bisq.desktop.common.model.ActivatableViewModel; import bisq.desktop.main.MainView; import bisq.desktop.main.offer.OfferView; -import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.settings.SettingsView; import bisq.desktop.main.settings.preferences.PreferencesView; import bisq.desktop.util.DisplayUtils; @@ -679,22 +678,17 @@ class OfferBookViewModel extends ActivatableViewModel { return PaymentMethod.getDummyPaymentMethod(GUIUtil.SHOW_ALL_FLAG); } - public void createBsqAccountAndTakeOffer(Offer offer) { + public boolean isInstantPaymentMethod(Offer offer) { + return offer.getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS_INSTANT); + } + + public PaymentAccount createBsqAccount(Offer offer) { var unusedBsqAddressAsString = bsqWalletService.getUnusedBsqAddressAsString(); - // create required BSQ payment account - boolean isInstantPaymentMethod = offer.getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS_INSTANT); - coreApi.createCryptoCurrencyPaymentAccount(DisplayUtils.createAssetsAccountName("BSQ", unusedBsqAddressAsString), + return coreApi.createCryptoCurrencyPaymentAccount(DisplayUtils.createAssetsAccountName("BSQ", unusedBsqAddressAsString), "BSQ", unusedBsqAddressAsString, - isInstantPaymentMethod); - - if (isInstantPaymentMethod) { - new Popup().information(Res.get("payment.altcoin.tradeInstant.popup")).show(); - } - - // take offer - onTakeOffer(offer); + isInstantPaymentMethod(offer)); } public void setOfferActionHandler(OfferView.OfferActionHandler offerActionHandler) { From 0e4933f8d1814a901aafddb4b49df0e536c24f6a Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 4 Nov 2021 10:55:51 +0100 Subject: [PATCH 21/38] Fix rebase merge errors --- .../java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java | 2 +- .../java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java | 1 + .../desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java | 2 +- .../bisq/desktop/main/offer/offerbook/OfferBookViewModel.java | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index 1789d58ecb..244c2f5299 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -121,7 +121,7 @@ import lombok.Setter; import org.jetbrains.annotations.NotNull; import static bisq.core.payment.payload.PaymentMethod.HAL_CASH_ID; -import static bisq.desktop.main.offer.OfferViewUtil.addPayInfoEntry; +import static bisq.desktop.main.offer.bisq_v1.OfferViewUtil.addPayInfoEntry; import static bisq.desktop.util.FormBuilder.*; import static javafx.beans.binding.Bindings.createStringBinding; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index 816ad8cac8..2558c62a45 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -22,6 +22,7 @@ import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipLabel; import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.main.MainView; +import bisq.desktop.main.offer.SellOfferView; import bisq.desktop.main.offer.offerbook.OfferBookView; import bisq.desktop.main.overlays.popups.Popup; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java index f87aa496cd..520eb9d9f3 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java @@ -117,7 +117,7 @@ import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; -import static bisq.desktop.main.offer.OfferViewUtil.addPayInfoEntry; +import static bisq.desktop.main.offer.bisq_v1.OfferViewUtil.addPayInfoEntry; import static bisq.desktop.util.FormBuilder.*; import static javafx.beans.binding.Bindings.createStringBinding; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java index 51c0da573a..6ac643e533 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java @@ -688,7 +688,8 @@ class OfferBookViewModel extends ActivatableViewModel { return coreApi.createCryptoCurrencyPaymentAccount(DisplayUtils.createAssetsAccountName("BSQ", unusedBsqAddressAsString), "BSQ", unusedBsqAddressAsString, - isInstantPaymentMethod(offer)); + isInstantPaymentMethod(offer), + false); } public void setOfferActionHandler(OfferView.OfferActionHandler offerActionHandler) { From aad658fc948b8806b0d039d5a24b508de8b4b3af Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 21 Oct 2021 18:12:44 +0200 Subject: [PATCH 22/38] Add api test, Cleanup --- .../method/trade/BsqSwapTradeTestLoop.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTestLoop.java diff --git a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTestLoop.java b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTestLoop.java new file mode 100644 index 0000000000..d5cf9b7dd7 --- /dev/null +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTestLoop.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ + +package bisq.apitest.method.trade; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + + + +import bisq.apitest.method.offer.AbstractOfferTest; + +// @Disabled +@Slf4j +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BsqSwapTradeTestLoop extends AbstractOfferTest { + + @BeforeAll + public static void setUp() { + AbstractOfferTest.setUp(); + createBsqSwapBsqPaymentAccounts(); + } + + @Test + @Order(1) + public void testGetBalancesBeforeTrade() { + BsqSwapTradeTest test = new BsqSwapTradeTest(); + runTradeLoop(test); + } + + private void runTradeLoop(BsqSwapTradeTest test) { + // TODO Fix wallet inconsistency bugs after 2nd trades. + for (int tradeCount = 1; tradeCount <= 2; tradeCount++) { + log.warn("================================ Trade # {} ================================", tradeCount); + test.testGetBalancesBeforeTrade(); + + test.testAliceCreateBsqSwapBuyOffer(); + genBtcBlocksThenWait(1, 8000); + + test.testBobTakesBsqSwapOffer(); + genBtcBlocksThenWait(1, 8000); + + test.testGetBalancesAfterTrade(); + } + } +} From 3ac61c59f10dd90554297154c057901c991ee926 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 26 Oct 2021 13:49:59 +0200 Subject: [PATCH 23/38] Cleanups, improve logs --- core/src/main/java/bisq/core/dao/node/full/FullNode.java | 5 +++-- core/src/main/java/bisq/core/dao/node/lite/LiteNode.java | 2 +- .../core/dao/node/lite/network/LiteNodeNetworkService.java | 2 ++ .../main/java/bisq/core/dao/node/parser/BlockParser.java | 7 +++++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/node/full/FullNode.java b/core/src/main/java/bisq/core/dao/node/full/FullNode.java index 1bdd6b1ffb..099fd0ad75 100644 --- a/core/src/main/java/bisq/core/dao/node/full/FullNode.java +++ b/core/src/main/java/bisq/core/dao/node/full/FullNode.java @@ -135,10 +135,11 @@ public class FullNode extends BsqNode { protected void onParseBlockChainComplete() { super.onParseBlockChainComplete(); - if (p2pNetworkReady) + if (p2pNetworkReady) { addBlockHandler(); - else + } else { log.info("onParseBlockChainComplete but P2P network is not ready yet."); + } } diff --git a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java index c0c8b64980..966cfb545b 100644 --- a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java +++ b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java @@ -222,7 +222,7 @@ public class LiteNode extends BsqNode { runDelayedBatchProcessing(new ArrayList<>(blockList), () -> { - log.info("runDelayedBatchProcessing Parsing {} blocks took {} seconds.", blockList.size(), + log.info("Parsing {} blocks took {} seconds.", blockList.size(), (System.currentTimeMillis() - ts) / 1000d); // We only request again if wallet is synced, otherwise we would get repeated calls we want to avoid. // We deal with that case at the setupWalletBestBlockListener method above. diff --git a/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java b/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java index 189afea334..9c46f8220a 100644 --- a/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java +++ b/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java @@ -200,6 +200,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen @Override public void onAllConnectionsLost() { + log.info("onAllConnectionsLost"); closeAllHandlers(); stopRetryTimer(); stopped = true; @@ -208,6 +209,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen @Override public void onNewConnectionAfterAllConnectionsLost() { + log.info("onNewConnectionAfterAllConnectionsLost"); closeAllHandlers(); stopped = false; tryWithNewSeedNode(lastRequestedBlockHeight); diff --git a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java index 01ca2c34e8..51cb323295 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java @@ -113,8 +113,11 @@ public class BlockParser { .ifPresent(tx -> daoStateService.onNewTxForLastBlock(block, tx))); daoStateService.onParseBlockComplete(block); - log.info("Parsing {} transactions at block height {} took {} ms", rawBlock.getRawTxs().size(), - blockHeight, System.currentTimeMillis() - startTs); + long duration = System.currentTimeMillis() - startTs; + if (duration > 10) { + log.info("Parsing {} transactions at block height {} took {} ms", rawBlock.getRawTxs().size(), + blockHeight, duration); + } GcUtil.maybeReleaseMemory(); return block; From cd3e8217ae86c10999246e7318351c54674d894c Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 26 Oct 2021 13:50:42 +0200 Subject: [PATCH 24/38] Rename onChangeAfterBatchProcessing to onDaoStateHashesChanged at DaoStateMonitoringService.Listener --- .../core/dao/monitoring/DaoStateMonitoringService.java | 8 ++++---- desktop/src/main/java/bisq/desktop/main/MainView.java | 2 +- .../main/dao/monitor/daostate/DaoStateMonitorView.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java index 35c7ebad0b..0a9643b514 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java @@ -88,7 +88,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe DaoStateNetworkService.Listener { public interface Listener { - void onChangeAfterBatchProcessing(); + void onDaoStateHashesChanged(); void onCheckpointFail(); } @@ -235,7 +235,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe }); if (hasChanged.get()) { - listeners.forEach(Listener::onChangeAfterBatchProcessing); + listeners.forEach(Listener::onDaoStateHashesChanged); } } @@ -318,7 +318,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe // We only broadcast after parsing of blockchain is complete if (parseBlockChainComplete) { // We notify listeners only after batch processing to avoid performance issues at UI code - listeners.forEach(Listener::onChangeAfterBatchProcessing); + listeners.forEach(Listener::onDaoStateHashesChanged); // We delay broadcast to give peers enough time to have received the block. // Otherwise they would ignore our data if received block is in future to their local blockchain. @@ -379,7 +379,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe } if (notifyListeners && changed.get()) { - listeners.forEach(Listener::onChangeAfterBatchProcessing); + listeners.forEach(Listener::onDaoStateHashesChanged); } GcUtil.maybeReleaseMemory(); diff --git a/desktop/src/main/java/bisq/desktop/main/MainView.java b/desktop/src/main/java/bisq/desktop/main/MainView.java index 259f1439a7..72d6a36263 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainView.java +++ b/desktop/src/main/java/bisq/desktop/main/MainView.java @@ -416,7 +416,7 @@ public class MainView extends InitializableView /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void onChangeAfterBatchProcessing() { + public void onDaoStateHashesChanged() { } @Override diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java index a1862413e4..72242cd3ef 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java @@ -111,7 +111,7 @@ public class DaoStateMonitorView extends StateMonitorView Date: Tue, 26 Oct 2021 13:51:34 +0200 Subject: [PATCH 25/38] Add method. Inline var --- .../dao/monitoring/network/StateNetworkService.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/StateNetworkService.java b/core/src/main/java/bisq/core/dao/monitoring/network/StateNetworkService.java index 8300c76b23..92e110c9c1 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/network/StateNetworkService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/network/StateNetworkService.java @@ -102,7 +102,8 @@ public abstract class StateNetworkService listener); + protected abstract Han getRequestStateHashesHandler(NodeAddress nodeAddress, + RequestStateHashesHandler.Listener listener); /////////////////////////////////////////////////////////////////////////////////////////// @@ -155,8 +156,7 @@ public abstract class StateNetworkService Date: Tue, 26 Oct 2021 13:52:49 +0200 Subject: [PATCH 26/38] Fix getCycleIndex method The recursive version is very slow with more cycles. --- .../java/bisq/core/dao/governance/period/CycleService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/period/CycleService.java b/core/src/main/java/bisq/core/dao/governance/period/CycleService.java index a52fb127af..af1db2fcb1 100644 --- a/core/src/main/java/bisq/core/dao/governance/period/CycleService.java +++ b/core/src/main/java/bisq/core/dao/governance/period/CycleService.java @@ -90,8 +90,7 @@ public class CycleService implements DaoStateListener, DaoSetupService { } public int getCycleIndex(Cycle cycle) { - Optional previousCycle = getCycle(cycle.getHeightOfFirstBlock() - 1, daoStateService.getCycles()); - return previousCycle.map(cycle1 -> getCycleIndex(cycle1) + 1).orElse(0); + return daoStateService.getCycles().indexOf(cycle); } public boolean isTxInCycle(Cycle cycle, String txId) { From 7b68686b28bfdb969829e88c3d007ae2b22547b2 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 26 Oct 2021 13:56:35 +0200 Subject: [PATCH 27/38] Improve GcUtil Increase duration for autoReleaseMemory from 60 to 120 sec. Improve logging. Add print stack trace when in dev mode to show caller for debugging/tuning. Remove inefficient GC calls (based on test runs when no reduction occurred at those calls). --- .../persistence/PersistenceManager.java | 2 -- .../main/java/bisq/common/util/GcUtil.java | 24 +++++++++++++++---- .../core/dao/node/parser/BlockParser.java | 2 -- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/bisq/common/persistence/PersistenceManager.java b/common/src/main/java/bisq/common/persistence/PersistenceManager.java index 823d417c59..ba9d62625b 100644 --- a/common/src/main/java/bisq/common/persistence/PersistenceManager.java +++ b/common/src/main/java/bisq/common/persistence/PersistenceManager.java @@ -501,8 +501,6 @@ public class PersistenceManager { if (completeHandler != null) { UserThread.execute(completeHandler); } - - GcUtil.maybeReleaseMemory(); } } diff --git a/common/src/main/java/bisq/common/util/GcUtil.java b/common/src/main/java/bisq/common/util/GcUtil.java index 62dbcb7db3..65227f2d91 100644 --- a/common/src/main/java/bisq/common/util/GcUtil.java +++ b/common/src/main/java/bisq/common/util/GcUtil.java @@ -18,6 +18,7 @@ package bisq.common.util; import bisq.common.UserThread; +import bisq.common.app.DevEnv; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -47,18 +48,31 @@ public class GcUtil { * @param trigger Threshold for free memory in MB when we invoke the garbage collector */ private static void autoReleaseMemory(long trigger) { - UserThread.runPeriodically(() -> maybeReleaseMemory(trigger), 60); + UserThread.runPeriodically(() -> maybeReleaseMemory(trigger), 120); } /** * @param trigger Threshold for free memory in MB when we invoke the garbage collector */ private static void maybeReleaseMemory(long trigger) { - long totalMemory = Runtime.getRuntime().totalMemory(); - if (totalMemory > trigger * 1024 * 1024) { - log.info("Invoke garbage collector. Total memory: {} {} {}", Utilities.readableFileSize(totalMemory), totalMemory, trigger * 1024 * 1024); + long ts = System.currentTimeMillis(); + long preGcMemory = Runtime.getRuntime().totalMemory(); + if (preGcMemory > trigger * 1024 * 1024) { System.gc(); - log.info("Total memory after gc() call: {}", Utilities.readableFileSize(Runtime.getRuntime().totalMemory())); + long postGcMemory = Runtime.getRuntime().totalMemory(); + log.info("GC reduced memory by {}. Total memory before/after: {}/{}. Took {} ms.", + Utilities.readableFileSize(preGcMemory - postGcMemory), + Utilities.readableFileSize(preGcMemory), + Utilities.readableFileSize(postGcMemory), + System.currentTimeMillis() - ts); + if (DevEnv.isDevMode()) { + try { + // To see from where we got called + throw new RuntimeException("Dummy Exception for print stacktrace at maybeReleaseMemory"); + } catch (Throwable t) { + t.printStackTrace(); + } + } } } } diff --git a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java index 51cb323295..92157f2b7b 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java @@ -24,7 +24,6 @@ import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Block; import bisq.common.app.DevEnv; -import bisq.common.util.GcUtil; import org.bitcoinj.core.Coin; @@ -119,7 +118,6 @@ public class BlockParser { blockHeight, duration); } - GcUtil.maybeReleaseMemory(); return block; } From 1abe68637dcc3676fcf4171bbb0087d2abc6b16f Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 26 Oct 2021 14:01:22 +0200 Subject: [PATCH 28/38] Add useFullModeDaoMonitor field to PreferencesPayload Add toggle to PreferencesView Set useFullModeDaoMonitor default to true for headless nodes --- .../core/app/misc/AppSetupWithP2PAndDAO.java | 7 +++++ .../main/java/bisq/core/user/Preferences.java | 7 +++++ .../bisq/core/user/PreferencesPayload.java | 7 +++-- .../resources/i18n/displayStrings.properties | 12 +++++++++ .../settings/preferences/PreferencesView.java | 27 ++++++++++++++++--- proto/src/main/proto/pb.proto | 1 + 6 files changed, 55 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java b/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java index 95751832c9..1c77a6529e 100644 --- a/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java +++ b/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java @@ -28,6 +28,7 @@ import bisq.core.dao.governance.proofofburn.MyProofOfBurnListService; import bisq.core.dao.governance.proposal.MyProposalListService; import bisq.core.filter.FilterManager; import bisq.core.trade.statistics.TradeStatisticsManager; +import bisq.core.user.Preferences; import bisq.network.p2p.P2PService; import bisq.network.p2p.peers.PeerManager; @@ -42,6 +43,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { private final DaoSetup daoSetup; + private final Preferences preferences; @Inject public AppSetupWithP2PAndDAO(P2PService p2PService, @@ -58,6 +60,7 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { MyProposalListService myProposalListService, MyReputationListService myReputationListService, MyProofOfBurnListService myProofOfBurnListService, + Preferences preferences, Config config) { super(p2PService, p2PDataStorage, @@ -69,6 +72,7 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { config); this.daoSetup = daoSetup; + this.preferences = preferences; // TODO Should be refactored/removed. In the meantime keep in sync with CorePersistedDataHost if (config.daoActivated) { @@ -86,5 +90,8 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { super.onBasicServicesInitialized(); daoSetup.onAllServicesInitialized(log::error, log::warn); + + // For seed nodes we need to set default value to true + preferences.setUseFullModeDaoMonitor(true); } } diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index 30e93dc800..70eef439fb 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -800,6 +800,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid requestPersistence(); } + public void setUseFullModeDaoMonitor(boolean value) { + prefPayload.setUseFullModeDaoMonitor(value); + requestPersistence(); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Getter @@ -1115,5 +1120,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid void setDenyApiTaker(boolean value); void setNotifyOnPreRelease(boolean value); + + void setUseFullModeDaoMonitor(boolean value); } } diff --git a/core/src/main/java/bisq/core/user/PreferencesPayload.java b/core/src/main/java/bisq/core/user/PreferencesPayload.java index 3eb6ecfa7b..ffa943f8ca 100644 --- a/core/src/main/java/bisq/core/user/PreferencesPayload.java +++ b/core/src/main/java/bisq/core/user/PreferencesPayload.java @@ -134,6 +134,7 @@ public final class PreferencesPayload implements PersistableEnvelope { private boolean showOffersMatchingMyAccounts; private boolean denyApiTaker; private boolean notifyOnPreRelease; + private boolean useFullModeDaoMonitor; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -201,7 +202,8 @@ public final class PreferencesPayload implements PersistableEnvelope { .setHideNonAccountPaymentMethods(hideNonAccountPaymentMethods) .setShowOffersMatchingMyAccounts(showOffersMatchingMyAccounts) .setDenyApiTaker(denyApiTaker) - .setNotifyOnPreRelease(notifyOnPreRelease); + .setNotifyOnPreRelease(notifyOnPreRelease) + .setUseFullModeDaoMonitor(useFullModeDaoMonitor); Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory); Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((protobuf.TradeCurrency) e.toProtoMessage())); @@ -299,7 +301,8 @@ public final class PreferencesPayload implements PersistableEnvelope { proto.getHideNonAccountPaymentMethods(), proto.getShowOffersMatchingMyAccounts(), proto.getDenyApiTaker(), - proto.getNotifyOnPreRelease() + proto.getNotifyOnPreRelease(), + proto.getUseFullModeDaoMonitor() ); } } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 04f2a585ac..6321aed0ce 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1335,6 +1335,18 @@ setting.preferences.dao.resyncFromGenesis.resync=Resync from genesis and shutdow setting.preferences.dao.isDaoFullNode=Run Bisq as DAO full node setting.preferences.dao.activated=DAO activated setting.preferences.dao.activated.popup=The change will be applied after a restart + +setting.preferences.dao.fullModeDaoMonitor=Full-mode DAO state monitoring +setting.preferences.dao.fullModeDaoMonitor.popup=If full-mode DAO state monitoring is activated the DAO state \ + hashes are created during parsing the BSQ blocks. This comes with considerable performance costs at the initial DAO sync.\n\n\ + For users who are regularily using Bisq this should not be an issue as there are not many blocks pending for parsing, though for \ + users who only use Bisq casually creating the DAO state hashes for 100s or 1000s of blocks degrades heavily the user experience.\n\n\ + In case full-mode is deactivated (default) the missing DAO state hashes are requested from network nodes and \ + the DAO state hash based on the most recent block will be created by the user. As all hashes are connected by \ + reference to the previous hash a correct hash at the chain tip means that all past hashes are correct as well. The \ + main functionality of the DAO state monitoring - to detect if the local DAO state is out of sync with the rest of the network - \ + is therefore still fulfilled. + setting.preferences.dao.rpcUser=RPC username setting.preferences.dao.rpcPw=RPC password setting.preferences.dao.blockNotifyPort=Block notify port 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 c137041f0d..5b92c76697 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 @@ -48,6 +48,7 @@ import bisq.core.locale.TradeCurrency; import bisq.core.payment.PaymentAccount; import bisq.core.payment.payload.PaymentMethod; import bisq.core.provider.fee.FeeService; +import bisq.core.user.DontShowAgainLookup; import bisq.core.user.Preferences; import bisq.core.user.User; import bisq.core.util.FormattingUtils; @@ -122,13 +123,12 @@ public class PreferencesView extends ActivatableViewAndModel { + preferences.setUseFullModeDaoMonitor(fullModeDaoMonitorToggleButton.isSelected()); + if (fullModeDaoMonitorToggleButton.isSelected()) { + String key = "fullModeDaoMonitor"; + if (DontShowAgainLookup.showAgain(key)) { + new Popup().information(Res.get("setting.preferences.dao.fullModeDaoMonitor.popup")) + .width(1000) + .dontShowAgainId(key) + .closeButtonText(Res.get("shared.iUnderstand")) + .show(); + } + } + }); + boolean daoFullNode = preferences.isDaoFullNode(); isDaoFullNodeToggleButton.setSelected(daoFullNode); @@ -1173,6 +1191,7 @@ public class PreferencesView extends ActivatableViewAndModel Date: Tue, 26 Oct 2021 14:10:19 +0200 Subject: [PATCH 29/38] Remove prevHash from StateHash classes (the prevHash was only used for display. For creating the hash we take the hash from the previous element. By removing it we safe about 3 MB on data) Add isSelfCreated field to DaoStateHash (indicates if we have created the hash by ourself or if we have received it from a peer -> will be part of later commits) --- .../BlindVoteStateMonitoringService.java | 2 +- .../monitoring/DaoStateMonitoringService.java | 2 +- .../ProposalStateMonitoringService.java | 2 +- .../monitoring/model/BlindVoteStateHash.java | 6 +-- .../dao/monitoring/model/DaoStateBlock.java | 6 ++- .../dao/monitoring/model/DaoStateHash.java | 24 ++++++--- .../monitoring/model/ProposalStateHash.java | 6 +-- .../core/dao/monitoring/model/StateBlock.java | 4 -- .../core/dao/monitoring/model/StateHash.java | 6 +-- .../resources/i18n/displayStrings.properties | 3 ++ .../main/dao/monitor/StateBlockListItem.java | 4 +- .../dao/monitor/StateInConflictListItem.java | 3 -- .../main/dao/monitor/StateMonitorView.java | 52 +------------------ .../blindvotes/BlindVoteStateMonitorView.java | 5 -- .../daostate/DaoStateBlockListItem.java | 7 +++ .../monitor/daostate/DaoStateMonitorView.java | 44 +++++++++++++--- .../proposals/ProposalStateMonitorView.java | 5 -- .../daostate/DaoStateBlockListItemTest.java | 2 +- proto/src/main/proto/pb.proto | 7 +-- 19 files changed, 85 insertions(+), 105 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java index a3a5162994..f803060a53 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java @@ -274,7 +274,7 @@ public class BlindVoteStateMonitoringService implements DaoSetupService, DaoStat byte[] combined = ArrayUtils.addAll(prevHash, serializedBlindVotes); byte[] hash = Hash.getSha256Ripemd160hash(combined); - BlindVoteStateHash myBlindVoteStateHash = new BlindVoteStateHash(blockHeight, hash, prevHash, blindVotes.size()); + BlindVoteStateHash myBlindVoteStateHash = new BlindVoteStateHash(blockHeight, hash, blindVotes.size()); BlindVoteStateBlock blindVoteStateBlock = new BlindVoteStateBlock(myBlindVoteStateHash); blindVoteStateBlockChain.add(blindVoteStateBlock); blindVoteStateHashChain.add(myBlindVoteStateHash); diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java index 0a9643b514..33500938ae 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java @@ -310,7 +310,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe byte[] combined = ArrayUtils.addAll(prevHash, stateAsBytes); byte[] hash = Hash.getSha256Ripemd160hash(combined); - DaoStateHash myDaoStateHash = new DaoStateHash(height, hash, prevHash); + DaoStateHash myDaoStateHash = new DaoStateHash(height, hash, true); DaoStateBlock daoStateBlock = new DaoStateBlock(myDaoStateHash); daoStateBlockChain.add(daoStateBlock); daoStateHashChain.add(myDaoStateHash); diff --git a/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java index 81d40496d8..c28f33a235 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java @@ -275,7 +275,7 @@ public class ProposalStateMonitoringService implements DaoSetupService, DaoState } byte[] combined = ArrayUtils.addAll(prevHash, serializedProposals); byte[] hash = Hash.getSha256Ripemd160hash(combined); - ProposalStateHash myProposalStateHash = new ProposalStateHash(blockHeight, hash, prevHash, proposals.size()); + ProposalStateHash myProposalStateHash = new ProposalStateHash(blockHeight, hash, proposals.size()); ProposalStateBlock proposalStateBlock = new ProposalStateBlock(myProposalStateHash); proposalStateBlockChain.add(proposalStateBlock); proposalStateHashChain.add(myProposalStateHash); diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateHash.java b/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateHash.java index 79a59032c7..ce08528d95 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateHash.java +++ b/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateHash.java @@ -29,8 +29,8 @@ public final class BlindVoteStateHash extends StateHash { @Getter private final int numBlindVotes; - public BlindVoteStateHash(int cycleStartBlockHeight, byte[] hash, byte[] prevHash, int numBlindVotes) { - super(cycleStartBlockHeight, hash, prevHash); + public BlindVoteStateHash(int cycleStartBlockHeight, byte[] hash, int numBlindVotes) { + super(cycleStartBlockHeight, hash); this.numBlindVotes = numBlindVotes; } @@ -43,14 +43,12 @@ public final class BlindVoteStateHash extends StateHash { return protobuf.BlindVoteStateHash.newBuilder() .setHeight(height) .setHash(ByteString.copyFrom(hash)) - .setPrevHash(ByteString.copyFrom(prevHash)) .setNumBlindVotes(numBlindVotes).build(); } public static BlindVoteStateHash fromProto(protobuf.BlindVoteStateHash proto) { return new BlindVoteStateHash(proto.getHeight(), proto.getHash().toByteArray(), - proto.getPrevHash().toByteArray(), proto.getNumBlindVotes()); } diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateBlock.java b/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateBlock.java index 8bd91e67fa..04e24b4d45 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateBlock.java +++ b/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateBlock.java @@ -18,12 +18,14 @@ package bisq.core.dao.monitoring.model; import lombok.EqualsAndHashCode; -import lombok.Getter; -@Getter @EqualsAndHashCode(callSuper = true) public class DaoStateBlock extends StateBlock { public DaoStateBlock(DaoStateHash myDaoStateHash) { super(myDaoStateHash); } + + public boolean isSelfCreated() { + return myStateHash.isSelfCreated(); + } } diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateHash.java b/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateHash.java index e0e7f5e403..e2a624576c 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateHash.java +++ b/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateHash.java @@ -21,11 +21,17 @@ package bisq.core.dao.monitoring.model; import com.google.protobuf.ByteString; import lombok.EqualsAndHashCode; +import lombok.Getter; +@Getter @EqualsAndHashCode(callSuper = true) public final class DaoStateHash extends StateHash { - public DaoStateHash(int height, byte[] hash, byte[] prevHash) { - super(height, hash, prevHash); + // If we have built the hash by ourself opposed to that we got delivered the hash from seed nodes or resources + private final boolean isSelfCreated; + + public DaoStateHash(int height, byte[] hash, boolean isSelfCreated) { + super(height, hash); + this.isSelfCreated = isSelfCreated; } @@ -38,12 +44,18 @@ public final class DaoStateHash extends StateHash { return protobuf.DaoStateHash.newBuilder() .setHeight(height) .setHash(ByteString.copyFrom(hash)) - .setPrevHash(ByteString.copyFrom(prevHash)).build(); + .setIsSelfCreated(isSelfCreated) + .build(); } public static DaoStateHash fromProto(protobuf.DaoStateHash proto) { - return new DaoStateHash(proto.getHeight(), - proto.getHash().toByteArray(), - proto.getPrevHash().toByteArray()); + return new DaoStateHash(proto.getHeight(), proto.getHash().toByteArray(), proto.getIsSelfCreated()); + } + + @Override + public String toString() { + return "DaoStateHash{" + + "\r\n isSelfCreated=" + isSelfCreated + + "\r\n} " + super.toString(); } } diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateHash.java b/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateHash.java index dc2ccdeabf..995b28cefc 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateHash.java +++ b/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateHash.java @@ -29,8 +29,8 @@ public final class ProposalStateHash extends StateHash { @Getter private final int numProposals; - public ProposalStateHash(int cycleStartBlockHeight, byte[] hash, byte[] prevHash, int numProposals) { - super(cycleStartBlockHeight, hash, prevHash); + public ProposalStateHash(int cycleStartBlockHeight, byte[] hash, int numProposals) { + super(cycleStartBlockHeight, hash); this.numProposals = numProposals; } @@ -43,14 +43,12 @@ public final class ProposalStateHash extends StateHash { return protobuf.ProposalStateHash.newBuilder() .setHeight(height) .setHash(ByteString.copyFrom(hash)) - .setPrevHash(ByteString.copyFrom(prevHash)) .setNumProposals(numProposals).build(); } public static ProposalStateHash fromProto(protobuf.ProposalStateHash proto) { return new ProposalStateHash(proto.getHeight(), proto.getHash().toByteArray(), - proto.getPrevHash().toByteArray(), proto.getNumProposals()); } diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/StateBlock.java b/core/src/main/java/bisq/core/dao/monitoring/model/StateBlock.java index b41ddc2296..8f5a27b78f 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/model/StateBlock.java +++ b/core/src/main/java/bisq/core/dao/monitoring/model/StateBlock.java @@ -56,10 +56,6 @@ public abstract class StateBlock { return myStateHash.getHash(); } - public byte[] getPrevHash() { - return myStateHash.getPrevHash(); - } - @Override public String toString() { return "StateBlock{" + diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/StateHash.java b/core/src/main/java/bisq/core/dao/monitoring/model/StateHash.java index 12cb299a11..63771d83ee 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/model/StateHash.java +++ b/core/src/main/java/bisq/core/dao/monitoring/model/StateHash.java @@ -40,13 +40,10 @@ import lombok.extern.slf4j.Slf4j; public abstract class StateHash implements PersistablePayload, NetworkPayload { protected final int height; protected final byte[] hash; - // For first block the prevHash is an empty byte array - protected final byte[] prevHash; - StateHash(int height, byte[] hash, byte[] prevHash) { + StateHash(int height, byte[] hash) { this.height = height; this.hash = hash; - this.prevHash = prevHash; } /////////////////////////////////////////////////////////////////////////////////////////// @@ -67,7 +64,6 @@ public abstract class StateHash implements PersistablePayload, NetworkPayload { return "StateHash{" + "\n height=" + height + ",\n hash=" + Utilities.bytesAsHexString(hash) + - ",\n prevHash=" + Utilities.bytesAsHexString(prevHash) + "\n}"; } } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 6321aed0ce..c2dcbe4ab7 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2495,6 +2495,9 @@ dao.monitor.proposals=Proposals state dao.monitor.blindVotes=Blind votes state dao.monitor.table.peers=Peers +dao.monitor.table.hashCreator=Hash creator +dao.monitor.table.hashCreator.self=Self +dao.monitor.table.hashCreator.peer=Peer dao.monitor.table.conflicts=Conflicts dao.monitor.state=Status dao.monitor.requestAlHashes=Request all hashes diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateBlockListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateBlockListItem.java index eff55bb186..b7093b8cea 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateBlockListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateBlockListItem.java @@ -36,10 +36,9 @@ import lombok.extern.slf4j.Slf4j; @Getter @EqualsAndHashCode public abstract class StateBlockListItem> { - private final StateBlock stateBlock; + protected final StateBlock stateBlock; private final Supplier height; private final String hash; - private final String prevHash; private final String numNetworkMessages; private final String numMisMatches; private final boolean isInSync; @@ -58,7 +57,6 @@ public abstract class StateBlockListItem 0 ? Utilities.bytesAsHexString(stateBlock.getPrevHash()) : "-"; numNetworkMessages = String.valueOf(stateBlock.getPeersMap().size()); int size = stateBlock.getInConflictMap().size(); numMisMatches = String.valueOf(size); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateInConflictListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateInConflictListItem.java index cf51c261c2..3ca42f1474 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateInConflictListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateInConflictListItem.java @@ -38,7 +38,6 @@ public abstract class StateInConflictListItem { private final String peerAddressString; private final String height; private final String hash; - private final String prevHash; private final T stateHash; protected StateInConflictListItem(String peerAddress, T stateHash, int cycleIndex, @@ -52,7 +51,5 @@ public abstract class StateInConflictListItem { cycleIndex + 1, String.valueOf(stateHash.getHeight())); hash = Utilities.bytesAsHexString(stateHash.getHash()); - prevHash = stateHash.getPrevHash().length > 0 ? - Utilities.bytesAsHexString(stateHash.getPrevHash()) : "-"; } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java index 962ca6eaf2..ea3c9f531d 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java @@ -177,8 +177,6 @@ public abstract class StateMonitorView(getPrevHashTableHeader()); - column.setMinWidth(120); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory( - new Callback<>() { - - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final BLI item, boolean empty) { - super.updateItem(item, empty); - if (item != null) - setText(item.getPrevHash()); - else - setText(""); - } - }; - } - }); - column.setComparator(Comparator.comparing(BLI::getPrevHash)); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(getPeersTableHeader()); column.setMinWidth(80); column.setMaxWidth(column.getMinWidth()); @@ -402,6 +375,7 @@ public abstract class StateMonitorView e.getStateBlock().getPeersMap().size())); tableView.getColumns().add(column); + column = new AutoTooltipTableColumn<>(getConflictsTableHeader()); column.setMinWidth(80); column.setMaxWidth(column.getMinWidth()); @@ -543,30 +517,6 @@ public abstract class StateMonitorView(getPrevHashTableHeader()); - column.setMinWidth(120); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call( - TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final CLI item, boolean empty) { - super.updateItem(item, empty); - if (item != null) - setText(item.getPrevHash()); - else - setText(""); - } - }; - } - }); - column.setComparator(Comparator.comparing(CLI::getPrevHash)); - conflictTableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(""); column.setMinWidth(120); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java index 6949fa8b02..03b3e59bd9 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java @@ -149,11 +149,6 @@ public class BlindVoteStateMonitorView extends StateMonitorView column = new AutoTooltipTableColumn<>(Res.get("dao.monitor.table.hashCreator")); + column.setMinWidth(90); + column.setMaxWidth(column.getMinWidth()); + column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setCellFactory( + new Callback<>() { + @Override + public TableCell call( + TableColumn column) { + return new TableCell<>() { + @Override + public void updateItem(final DaoStateBlockListItem item, boolean empty) { + super.updateItem(item, empty); + if (item != null) + setText(item.hashCreator()); + else + setText(""); + } + }; + } + }); + column.setComparator(Comparator.comparing(e -> e.getStateBlock().getPeersMap().size())); + tableView.getColumns().add(2, column); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Private diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/proposals/ProposalStateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/proposals/ProposalStateMonitorView.java index f12e574694..29cc78eb8a 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/proposals/ProposalStateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/proposals/ProposalStateMonitorView.java @@ -147,11 +147,6 @@ public class ProposalStateMonitorView extends StateMonitorView Date: Tue, 26 Oct 2021 14:18:05 +0200 Subject: [PATCH 30/38] Add daoStateBlockChainNotConnecting property (impl. will come in other commits) Add Preferences to service classes --- .../dao/monitoring/DaoStateMonitoringService.java | 6 ++++++ .../bisq/core/dao/state/DaoStateSnapshotService.java | 4 ++++ .../src/main/resources/i18n/displayStrings.properties | 2 ++ .../core/dao/state/DaoStateSnapshotServiceTest.java | 1 + .../desktop/main/dao/monitor/StateMonitorView.java | 11 +++++++++-- .../dao/monitor/daostate/DaoStateMonitorView.java | 1 + 6 files changed, 23 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java index 33500938ae..6387b4bc93 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java @@ -31,6 +31,7 @@ import bisq.core.dao.state.GenesisTxInfo; import bisq.core.dao.state.model.blockchain.BaseTxOutput; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.governance.IssuanceType; +import bisq.core.user.Preferences; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.Connection; @@ -110,6 +111,8 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe @Getter private boolean isInConflictWithSeedNode; @Getter + private boolean daoStateBlockChainNotConnecting; + @Getter private final ObservableList utxoMismatches = FXCollections.observableArrayList(); private final List checkpoints = Arrays.asList( @@ -120,6 +123,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe private int numCalls; private long accumulatedDuration; + private final Preferences preferences; private final File storageDir; /////////////////////////////////////////////////////////////////////////////////////////// @@ -131,11 +135,13 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe DaoStateNetworkService daoStateNetworkService, GenesisTxInfo genesisTxInfo, SeedNodeRepository seedNodeRepository, + Preferences preferences, @Named(Config.STORAGE_DIR) File storageDir, @Named(Config.IGNORE_DEV_MSG) boolean ignoreDevMsg) { this.daoStateService = daoStateService; this.daoStateNetworkService = daoStateNetworkService; this.genesisTxInfo = genesisTxInfo; + this.preferences = preferences; this.storageDir = storageDir; this.ignoreDevMsg = ignoreDevMsg; seedNodeAddresses = seedNodeRepository.getSeedNodeAddresses().stream() diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java index 3b179551d3..58c7cac07d 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java @@ -22,6 +22,7 @@ import bisq.core.dao.monitoring.model.DaoStateHash; import bisq.core.dao.state.model.DaoState; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.storage.DaoStateStorageService; +import bisq.core.user.Preferences; import bisq.common.config.Config; import bisq.common.util.GcUtil; @@ -56,6 +57,7 @@ public class DaoStateSnapshotService { private final GenesisTxInfo genesisTxInfo; private final DaoStateStorageService daoStateStorageService; private final DaoStateMonitoringService daoStateMonitoringService; + private final Preferences preferences; private final File storageDir; private DaoState daoStateSnapshotCandidate; @@ -76,11 +78,13 @@ public class DaoStateSnapshotService { GenesisTxInfo genesisTxInfo, DaoStateStorageService daoStateStorageService, DaoStateMonitoringService daoStateMonitoringService, + Preferences preferences, @Named(Config.STORAGE_DIR) File storageDir) { this.daoStateService = daoStateService; this.genesisTxInfo = genesisTxInfo; this.daoStateStorageService = daoStateStorageService; this.daoStateMonitoringService = daoStateMonitoringService; + this.preferences = preferences; this.storageDir = storageDir; } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index c2dcbe4ab7..4dbba91740 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2531,6 +2531,8 @@ dao.monitor.isInConflictWithSeedNode=Your local data is not in consensus with at Please resync the DAO state. dao.monitor.isInConflictWithNonSeedNode=One of your peers is not in consensus with the network but your node \ is in sync with the seed nodes. +dao.monitor.isDaoStateBlockChainNotConnecting=You dao state chain is not connecting with the new data. \ + Please resync the DAO state. dao.monitor.daoStateInSync=Your local node is in consensus with the network dao.monitor.blindVote.headline=Blind votes state diff --git a/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java b/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java index a9e773878a..5e8ccefe05 100644 --- a/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java +++ b/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java @@ -38,6 +38,7 @@ public class DaoStateSnapshotServiceTest { mock(GenesisTxInfo.class), mock(DaoStateStorageService.class), mock(DaoStateMonitoringService.class), + null, null); } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java index ea3c9f531d..1a07e709db 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java @@ -99,6 +99,7 @@ public abstract class StateMonitorView resyncDaoState()); @@ -270,6 +273,9 @@ public abstract class StateMonitorView e.getStateBlock().getInConflictMap().size())); tableView.getColumns().add(column); + column = new AutoTooltipTableColumn<>(""); column.setMinWidth(40); column.setMaxWidth(column.getMinWidth()); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java index 93e1479874..b85fcab1d6 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java @@ -192,6 +192,7 @@ public class DaoStateMonitorView extends StateMonitorView Date: Tue, 26 Oct 2021 14:21:37 +0200 Subject: [PATCH 31/38] Delete DaoEventCoordinator (we will handle coordination in DaoStateSnapshotService instead in next commits) Let DaoStateSnapshotService implement DaoSetupService (impl in next commits) --- .../bisq/core/dao/DaoEventCoordinator.java | 69 ------------------- .../main/java/bisq/core/dao/DaoModule.java | 1 - .../src/main/java/bisq/core/dao/DaoSetup.java | 9 +-- .../dao/state/DaoStateSnapshotService.java | 15 +++- 4 files changed, 17 insertions(+), 77 deletions(-) delete mode 100644 core/src/main/java/bisq/core/dao/DaoEventCoordinator.java diff --git a/core/src/main/java/bisq/core/dao/DaoEventCoordinator.java b/core/src/main/java/bisq/core/dao/DaoEventCoordinator.java deleted file mode 100644 index 4c8cc0f409..0000000000 --- a/core/src/main/java/bisq/core/dao/DaoEventCoordinator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 . - */ - -package bisq.core.dao; - -import bisq.core.dao.monitoring.DaoStateMonitoringService; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.DaoStateSnapshotService; -import bisq.core.dao.state.model.blockchain.Block; - -import javax.inject.Inject; - -public class DaoEventCoordinator implements DaoSetupService, DaoStateListener { - private final DaoStateService daoStateService; - private final DaoStateSnapshotService daoStateSnapshotService; - private final DaoStateMonitoringService daoStateMonitoringService; - - @Inject - public DaoEventCoordinator(DaoStateService daoStateService, - DaoStateSnapshotService daoStateSnapshotService, - DaoStateMonitoringService daoStateMonitoringService) { - this.daoStateService = daoStateService; - this.daoStateSnapshotService = daoStateSnapshotService; - this.daoStateMonitoringService = daoStateMonitoringService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - this.daoStateService.addDaoStateListener(this); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - // We listen onDaoStateChanged to ensure the dao state has been processed from listener clients after parsing. - // We need to listen during batch processing as well to write snapshots during that process. - @Override - public void onDaoStateChanged(Block block) { - // We need to execute first the daoStateMonitoringService - daoStateMonitoringService.createHashFromBlock(block); - daoStateSnapshotService.maybeCreateSnapshot(block); - } -} diff --git a/core/src/main/java/bisq/core/dao/DaoModule.java b/core/src/main/java/bisq/core/dao/DaoModule.java index 87d5f9e7a0..ed2f0efef3 100644 --- a/core/src/main/java/bisq/core/dao/DaoModule.java +++ b/core/src/main/java/bisq/core/dao/DaoModule.java @@ -102,7 +102,6 @@ public class DaoModule extends AppModule { protected void configure() { bind(DaoSetup.class).in(Singleton.class); bind(DaoFacade.class).in(Singleton.class); - bind(DaoEventCoordinator.class).in(Singleton.class); bind(DaoKillSwitch.class).in(Singleton.class); // Node, parser diff --git a/core/src/main/java/bisq/core/dao/DaoSetup.java b/core/src/main/java/bisq/core/dao/DaoSetup.java index b8c13582f7..728f94108a 100644 --- a/core/src/main/java/bisq/core/dao/DaoSetup.java +++ b/core/src/main/java/bisq/core/dao/DaoSetup.java @@ -39,6 +39,7 @@ import bisq.core.dao.node.BsqNode; import bisq.core.dao.node.BsqNodeProvider; import bisq.core.dao.node.explorer.ExportJsonFilesService; import bisq.core.dao.state.DaoStateService; +import bisq.core.dao.state.DaoStateSnapshotService; import com.google.inject.Inject; @@ -78,16 +79,11 @@ public class DaoSetup { DaoStateMonitoringService daoStateMonitoringService, ProposalStateMonitoringService proposalStateMonitoringService, BlindVoteStateMonitoringService blindVoteStateMonitoringService, - DaoEventCoordinator daoEventCoordinator) { + DaoStateSnapshotService daoStateSnapshotService) { bsqNode = bsqNodeProvider.getBsqNode(); // We need to take care of order of execution. - - // For order critical event flow we use the daoEventCoordinator to delegate the calls from anonymous listeners - // to concrete clients. - daoSetupServices.add(daoEventCoordinator); - daoSetupServices.add(daoStateService); daoSetupServices.add(cycleService); daoSetupServices.add(ballotListService); @@ -110,6 +106,7 @@ public class DaoSetup { daoSetupServices.add(daoStateMonitoringService); daoSetupServices.add(proposalStateMonitoringService); daoSetupServices.add(blindVoteStateMonitoringService); + daoSetupServices.add(daoStateSnapshotService); daoSetupServices.add(bsqNodeProvider.getBsqNode()); } diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java index 58c7cac07d..f43a7dbc89 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java @@ -17,6 +17,7 @@ package bisq.core.dao.state; +import bisq.core.dao.DaoSetupService; import bisq.core.dao.monitoring.DaoStateMonitoringService; import bisq.core.dao.monitoring.model.DaoStateHash; import bisq.core.dao.state.model.DaoState; @@ -50,7 +51,7 @@ import javax.annotation.Nullable; * SNAPSHOT_GRID old not less than 2 times the SNAPSHOT_GRID old. */ @Slf4j -public class DaoStateSnapshotService { +public class DaoStateSnapshotService implements DaoSetupService { private static final int SNAPSHOT_GRID = 20; private final DaoStateService daoStateService; @@ -88,6 +89,18 @@ public class DaoStateSnapshotService { this.storageDir = storageDir; } + /////////////////////////////////////////////////////////////////////////////////////////// + // DaoSetupService + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void addListeners() { + } + + @Override + public void start() { + } + /////////////////////////////////////////////////////////////////////////////////////////// // API From 5ccef962d07c61a5c6924dba0a0a6b7be6f52ad7 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 26 Oct 2021 14:24:07 +0200 Subject: [PATCH 32/38] Add pruneStore method We set both data to null and call GC after persistence is done. --- .../dao/state/storage/DaoStateStorageService.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/state/storage/DaoStateStorageService.java b/core/src/main/java/bisq/core/dao/state/storage/DaoStateStorageService.java index 2abf8bafe5..84625a0f7a 100644 --- a/core/src/main/java/bisq/core/dao/state/storage/DaoStateStorageService.java +++ b/core/src/main/java/bisq/core/dao/state/storage/DaoStateStorageService.java @@ -27,6 +27,7 @@ import bisq.network.p2p.storage.persistence.StoreService; import bisq.common.config.Config; import bisq.common.file.FileUtil; import bisq.common.persistence.PersistenceManager; +import bisq.common.util.GcUtil; import javax.inject.Inject; import javax.inject.Named; @@ -92,15 +93,20 @@ public class DaoStateStorageService extends StoreService { new Thread(() -> { Thread.currentThread().setName("Serialize and write DaoState"); persistenceManager.persistNow(() -> { - // After we have written to disk we remove the the daoState in the store to avoid that it stays in + // After we have written to disk we remove the daoState in the store to avoid that it stays in // memory there until the next persist call. - store.setDaoState(null); - + pruneStore(); completeHandler.run(); }); }).start(); } + public void pruneStore() { + store.setDaoState(null); + store.setDaoStateHashChain(null); + GcUtil.maybeReleaseMemory(); + } + public DaoState getPersistedBsqState() { return store.getDaoState(); } From 84ff332aeb1ce4ca217ce8d324e50d844078bff1 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Tue, 26 Oct 2021 14:26:00 +0200 Subject: [PATCH 33/38] Implement new behaviour for dao state monitoring and snapshotting See https://github.com/bisq-network/bisq/issues/5779 for spec --- .../monitoring/DaoStateMonitoringService.java | 258 +++++++++++------- .../dao/state/DaoStateSnapshotService.java | 147 +++++++--- 2 files changed, 277 insertions(+), 128 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java index 6387b4bc93..178f29cdc1 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java @@ -41,7 +41,6 @@ import bisq.common.UserThread; import bisq.common.config.Config; import bisq.common.crypto.Hash; import bisq.common.file.FileUtil; -import bisq.common.util.GcUtil; import bisq.common.util.Utilities; import javax.inject.Inject; @@ -55,19 +54,20 @@ import javafx.collections.ObservableList; import java.io.File; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Random; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import static com.google.common.base.Preconditions.checkArgument; +import javax.annotation.Nullable; /** * Monitors the DaoState by using a hash for the complete daoState and make it accessible to the network @@ -99,7 +99,6 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe private final GenesisTxInfo genesisTxInfo; private final Set seedNodeAddresses; - @Getter private final LinkedList daoStateBlockChain = new LinkedList<>(); @Getter @@ -125,6 +124,11 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe private final Preferences preferences; private final File storageDir; + @Nullable + private Runnable createSnapshotHandler; + // Lookup map + private Map daoStateBlockByHeight = new HashMap<>(); + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -169,16 +173,19 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe // DaoStateListener /////////////////////////////////////////////////////////////////////////////////////////// - // We do not use onDaoStateChanged but let the DaoEventCoordinator call createHashFromBlock to ensure the - // correct order of execution. - @Override public void onParseBlockChainComplete() { parseBlockChainComplete = true; + daoStateService.getLastBlock().ifPresent(this::checkUtxos); + daoStateNetworkService.addListeners(); - // We wait for processing messages until we have completed batch processing - int fromHeight = daoStateService.getChainHeight() - 10; + // We take either the height of the previous hashBlock we have or 10 blocks below the chain tip. + int nextBlockHeight = daoStateBlockChain.isEmpty() ? + genesisTxInfo.getGenesisBlockHeight() : + daoStateBlockChain.getLast().getHeight() + 1; + int past10 = daoStateService.getChainHeight() - 10; + int fromHeight = Math.min(nextBlockHeight, past10); daoStateNetworkService.requestHashesFromAllConnectedSeedNodes(fromHeight); if (!ignoreDevMsg) { @@ -194,16 +201,9 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe @Override public void onDaoStateChanged(Block block) { - long genesisTotalSupply = daoStateService.getGenesisTotalSupply().value; - long compensationIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.COMPENSATION); - long reimbursementIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT); - long totalAmountOfBurntBsq = daoStateService.getTotalAmountOfBurntBsq(); - // confiscated funds are still in the utxo set - long sumUtxo = daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); - long sumBsq = genesisTotalSupply + compensationIssuance + reimbursementIssuance - totalAmountOfBurntBsq; - - if (sumBsq != sumUtxo) { - utxoMismatches.add(new UtxoMismatch(block.getHeight(), sumUtxo, sumBsq)); + // During syncing we do not call checkUtxos as its a bit slow (about 4 ms) + if (parseBlockChainComplete) { + checkUtxos(block); } } @@ -214,44 +214,47 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe @Override public void onNewStateHashMessage(NewDaoStateHashMessage newStateHashMessage, Connection connection) { - if (newStateHashMessage.getStateHash().getHeight() <= daoStateService.getChainHeight()) { - processPeersDaoStateHash(newStateHashMessage.getStateHash(), connection.getPeersNodeAddressOptional(), true); + // Called when receiving NewDaoStateHashMessages from peers after a new block + DaoStateHash peersDaoStateHash = newStateHashMessage.getStateHash(); + if (peersDaoStateHash.getHeight() <= daoStateService.getChainHeight()) { + putInPeersMapAndCheckForConflicts(getPeersAddress(connection.getPeersNodeAddressOptional()), peersDaoStateHash); + listeners.forEach(Listener::onDaoStateHashesChanged); + } + } + + @Override + public void onPeersStateHashes(List stateHashes, Optional peersNodeAddress) { + // Called when receiving GetDaoStateHashesResponse from seed nodes + processPeersDaoStateHashes(stateHashes, peersNodeAddress); + listeners.forEach(Listener::onDaoStateHashesChanged); + if (createSnapshotHandler != null) { + createSnapshotHandler.run(); + // As we get called multiple times from hashes of diff. seed nodes we want to avoid to + // call our handler multiple times. + createSnapshotHandler = null; } } @Override public void onGetStateHashRequest(Connection connection, GetDaoStateHashesRequest getStateHashRequest) { int fromHeight = getStateHashRequest.getHeight(); - List daoStateHashes = daoStateBlockChain.stream() + List daoStateHashes = daoStateHashChain.stream() .filter(e -> e.getHeight() >= fromHeight) - .map(DaoStateBlock::getMyStateHash) .collect(Collectors.toList()); daoStateNetworkService.sendGetStateHashesResponse(connection, getStateHashRequest.getNonce(), daoStateHashes); } - @Override - public void onPeersStateHashes(List stateHashes, Optional peersNodeAddress) { - AtomicBoolean hasChanged = new AtomicBoolean(false); - - stateHashes.forEach(daoStateHash -> { - boolean changed = processPeersDaoStateHash(daoStateHash, peersNodeAddress, false); - if (changed) { - hasChanged.set(true); - } - }); - - if (hasChanged.get()) { - listeners.forEach(Listener::onDaoStateHashesChanged); - } - } - /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// public void createHashFromBlock(Block block) { - updateHashChain(block); + createDaoStateBlock(block); + if (parseBlockChainComplete) { + // We notify listeners only after batch processing to avoid performance issues at UI code + listeners.forEach(Listener::onDaoStateHashesChanged); + } } public void requestHashesFromGenesisBlockHeight(String peersAddress) { @@ -262,6 +265,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe // We could get a reset from a reorg, so we clear all and start over from the genesis block. daoStateHashChain.clear(); daoStateBlockChain.clear(); + daoStateBlockByHeight.clear(); daoStateNetworkService.reset(); if (!persistedDaoStateHashChain.isEmpty()) { @@ -269,7 +273,15 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe persistedDaoStateHashChain.size(), persistedDaoStateHashChain.getLast()); } daoStateHashChain.addAll(persistedDaoStateHashChain); - daoStateHashChain.forEach(e -> daoStateBlockChain.add(new DaoStateBlock(e))); + daoStateHashChain.forEach(daoStateHash -> { + DaoStateBlock daoStateBlock = new DaoStateBlock(daoStateHash); + daoStateBlockChain.add(daoStateBlock); + daoStateBlockByHeight.put(daoStateHash.getHeight(), daoStateBlock); + }); + } + + public void setCreateSnapshotHandler(Runnable handler) { + createSnapshotHandler = handler; } @@ -290,7 +302,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe // Private /////////////////////////////////////////////////////////////////////////////////////////// - private void updateHashChain(Block block) { + private Optional createDaoStateBlock(Block block) { long ts = System.currentTimeMillis(); byte[] prevHash; int height = block.getHeight(); @@ -301,15 +313,25 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe } else { log.warn("DaoStateBlockchain is empty but we received the block which was not the genesis block. " + "We stop execution here."); - return; + daoStateBlockChainNotConnecting = true; + listeners.forEach(Listener::onDaoStateHashesChanged); + return Optional.empty(); } } else { - checkArgument(height == daoStateBlockChain.getLast().getHeight() + 1, - "New block must be 1 block above previous block. height={}, " + - "daoStateBlockChain.getLast().getHeight()={}", - height, daoStateBlockChain.getLast().getHeight()); - prevHash = daoStateBlockChain.getLast().getHash(); + DaoStateBlock last = daoStateBlockChain.getLast(); + int heightOfLastBlock = last.getHeight(); + if (height == heightOfLastBlock + 1) { + prevHash = last.getHash(); + } else { + log.warn("New block must be 1 block above previous block. height={}, " + + "daoStateBlockChain.getLast().getHeight()={}", + height, heightOfLastBlock); + daoStateBlockChainNotConnecting = true; + listeners.forEach(Listener::onDaoStateHashesChanged); + return Optional.empty(); + } } + byte[] stateAsBytes = daoStateService.getSerializedStateForHashChain(); // We include the prev. hash in our new hash so we can be sure that if one hash is matching all the past would // match as well. @@ -319,16 +341,17 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe DaoStateHash myDaoStateHash = new DaoStateHash(height, hash, true); DaoStateBlock daoStateBlock = new DaoStateBlock(myDaoStateHash); daoStateBlockChain.add(daoStateBlock); + daoStateBlockByHeight.put(height, daoStateBlock); daoStateHashChain.add(myDaoStateHash); // We only broadcast after parsing of blockchain is complete if (parseBlockChainComplete) { - // We notify listeners only after batch processing to avoid performance issues at UI code - listeners.forEach(Listener::onDaoStateHashesChanged); - // We delay broadcast to give peers enough time to have received the block. // Otherwise they would ignore our data if received block is in future to their local blockchain. int delayInSec = 5 + new Random().nextInt(10); + if (Config.baseCurrencyNetwork().isRegtest()) { + delayInSec = 1; + } UserThread.runAfter(() -> daoStateNetworkService.broadcastMyStateHash(myDaoStateHash), delayInSec); } long duration = System.currentTimeMillis() - ts; @@ -338,59 +361,93 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe duration); accumulatedDuration += duration; numCalls++; + return Optional.of(daoStateBlock); } - private boolean processPeersDaoStateHash(DaoStateHash daoStateHash, - Optional peersNodeAddress, - boolean notifyListeners) { - GcUtil.maybeReleaseMemory(); + private void processPeersDaoStateHashes(List stateHashes, Optional peersNodeAddress) { + boolean useDaoMonitor = preferences.isUseFullModeDaoMonitor(); + stateHashes.forEach(peersHash -> { + Optional optionalDaoStateBlock; + // If we do not add own hashes during initial parsing we fill the missing hashes from the peer and create + // the at the last block our own hash. + int height = peersHash.getHeight(); + if (!useDaoMonitor && + !findDaoStateBlock(height).isPresent()) { + if (daoStateService.getChainHeight() == height) { + // At the most recent block we create our own hash + optionalDaoStateBlock = daoStateService.getLastBlock() + .map(this::createDaoStateBlock) + .orElse(findDaoStateBlock(height)); + } else { + // Otherwise we create a block from the peers daoStateHash + DaoStateHash daoStateHash = new DaoStateHash(height, peersHash.getHash(), false); + DaoStateBlock daoStateBlock = new DaoStateBlock(daoStateHash); + daoStateBlockChain.add(daoStateBlock); + daoStateBlockByHeight.put(height, daoStateBlock); + daoStateHashChain.add(daoStateHash); + optionalDaoStateBlock = Optional.of(daoStateBlock); + } + } else { + optionalDaoStateBlock = findDaoStateBlock(height); + } - AtomicBoolean changed = new AtomicBoolean(false); - AtomicBoolean inConflictWithNonSeedNode = new AtomicBoolean(this.isInConflictWithNonSeedNode); - AtomicBoolean inConflictWithSeedNode = new AtomicBoolean(this.isInConflictWithSeedNode); - StringBuilder sb = new StringBuilder(); - daoStateBlockChain.stream() - .filter(e -> e.getHeight() == daoStateHash.getHeight()).findAny() - .ifPresent(daoStateBlock -> { - String peersNodeAddressAsString = peersNodeAddress.map(NodeAddress::getFullAddress) - .orElseGet(() -> "Unknown peer " + new Random().nextInt(10000)); - daoStateBlock.putInPeersMap(peersNodeAddressAsString, daoStateHash); - if (!daoStateBlock.getMyStateHash().hasEqualHash(daoStateHash)) { - daoStateBlock.putInConflictMap(peersNodeAddressAsString, daoStateHash); - if (seedNodeAddresses.contains(peersNodeAddressAsString)) { - inConflictWithSeedNode.set(true); - } else { - inConflictWithNonSeedNode.set(true); - } - sb.append("We received a block hash from peer ") - .append(peersNodeAddressAsString) - .append(" which conflicts with our block hash.\n") - .append("my daoStateHash=") - .append(daoStateBlock.getMyStateHash()) - .append("\npeers daoStateHash=") - .append(daoStateHash); - } - changed.set(true); - }); + // In any case we add the peer to our peersMap and check for conflicts on the relevant daoStateBlock + putInPeersMapAndCheckForConflicts(optionalDaoStateBlock, getPeersAddress(peersNodeAddress), peersHash); + }); + } - this.isInConflictWithNonSeedNode = inConflictWithNonSeedNode.get(); - this.isInConflictWithSeedNode = inConflictWithSeedNode.get(); + private void putInPeersMapAndCheckForConflicts(String peersAddress, DaoStateHash peersHash) { + putInPeersMapAndCheckForConflicts(findDaoStateBlock(peersHash.getHeight()), peersAddress, peersHash); + } - String conflictMsg = sb.toString(); - if (!conflictMsg.isEmpty()) { - if (this.isInConflictWithSeedNode) - log.warn("Conflict with seed nodes: {}", conflictMsg); - else if (this.isInConflictWithNonSeedNode) - log.debug("Conflict with non-seed nodes: {}", conflictMsg); + private void putInPeersMapAndCheckForConflicts(Optional optionalDaoStateBlock, + String peersAddress, + DaoStateHash peersHash) { + optionalDaoStateBlock.ifPresent(daoStateBlock -> { + daoStateBlock.putInPeersMap(peersAddress, peersHash); + checkForHashConflicts(peersHash, peersAddress, daoStateBlock); + }); + } + + private void checkForHashConflicts(DaoStateHash peersDaoStateHash, + String peersNodeAddress, + DaoStateBlock daoStateBlock) { + if (daoStateBlock.getMyStateHash().hasEqualHash(peersDaoStateHash)) { + return; } - if (notifyListeners && changed.get()) { - listeners.forEach(Listener::onDaoStateHashesChanged); + daoStateBlock.putInConflictMap(peersNodeAddress, peersDaoStateHash); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("We received a block hash from peer ") + .append(peersNodeAddress) + .append(" which conflicts with our block hash.\n") + .append("my peersDaoStateHash=") + .append(daoStateBlock.getMyStateHash()) + .append("\npeers peersDaoStateHash=") + .append(peersDaoStateHash); + String conflictMsg = stringBuilder.toString(); + + if (isSeedNode(peersNodeAddress)) { + isInConflictWithSeedNode = true; + log.warn("Conflict with seed nodes: {}", conflictMsg); + } else { + isInConflictWithNonSeedNode = true; + log.debug("Conflict with non-seed nodes: {}", conflictMsg); } + } - GcUtil.maybeReleaseMemory(); + private void checkUtxos(Block block) { + long genesisTotalSupply = daoStateService.getGenesisTotalSupply().value; + long compensationIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.COMPENSATION); + long reimbursementIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT); + long totalAmountOfBurntBsq = daoStateService.getTotalAmountOfBurntBsq(); + // confiscated funds are still in the utxo set + long sumUtxo = daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); + long sumBsq = genesisTotalSupply + compensationIssuance + reimbursementIssuance - totalAmountOfBurntBsq; - return changed.get(); + if (sumBsq != sumUtxo) { + utxoMismatches.add(new UtxoMismatch(block.getHeight(), sumUtxo, sumBsq)); + } } private void verifyCheckpoints() { @@ -437,4 +494,17 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe log.error(t.toString()); } } + + private boolean isSeedNode(String peersNodeAddress) { + return seedNodeAddresses.contains(peersNodeAddress); + } + + private String getPeersAddress(Optional peersNodeAddress) { + return peersNodeAddress.map(NodeAddress::getFullAddress) + .orElseGet(() -> "Unknown peer " + new Random().nextInt(10000)); + } + + private Optional findDaoStateBlock(int height) { + return Optional.ofNullable(daoStateBlockByHeight.get(height)); + } } diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java index f43a7dbc89..f13748aa71 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java @@ -51,7 +51,7 @@ import javax.annotation.Nullable; * SNAPSHOT_GRID old not less than 2 times the SNAPSHOT_GRID old. */ @Slf4j -public class DaoStateSnapshotService implements DaoSetupService { +public class DaoStateSnapshotService implements DaoSetupService, DaoStateListener { private static final int SNAPSHOT_GRID = 20; private final DaoStateService daoStateService; @@ -67,7 +67,8 @@ public class DaoStateSnapshotService implements DaoSetupService { @Setter @Nullable private Runnable daoRequiresRestartHandler; - private boolean requestPersistenceCalled; + private boolean readyForPersisting = true; + private boolean isParseBlockChainComplete; /////////////////////////////////////////////////////////////////////////////////////////// @@ -89,12 +90,14 @@ public class DaoStateSnapshotService implements DaoSetupService { this.storageDir = storageDir; } + /////////////////////////////////////////////////////////////////////////////////////////// // DaoSetupService /////////////////////////////////////////////////////////////////////////////////////////// @Override public void addListeners() { + daoStateService.addDaoStateListener(this); } @Override @@ -102,12 +105,61 @@ public class DaoStateSnapshotService implements DaoSetupService { } + /////////////////////////////////////////////////////////////////////////////////////////// + // DaoStateListener + /////////////////////////////////////////////////////////////////////////////////////////// + + // We listen onDaoStateChanged to ensure the dao state has been processed from listener clients after parsing. + // We need to listen during batch processing as well to write snapshots during that process. + @Override + public void onDaoStateChanged(Block block) { + // If we have isUseDaoMonitor activated we apply the hash and snapshots at each new block during initial parsing. + // Otherwise we do it only after the initial blockchain parsing is completed to not delay the parsing. + // In that case we get the missing hashes from the seed nodes. At any new block we do the hash calculation + // ourself and therefore get back confidence that our DAO state is in sync with the network. + if (preferences.isUseFullModeDaoMonitor() || isParseBlockChainComplete) { + // We need to execute first the daoStateMonitoringService.createHashFromBlock to get the hash created + daoStateMonitoringService.createHashFromBlock(block); + maybeCreateSnapshot(block); + } + } + + @Override + public void onParseBlockChainComplete() { + isParseBlockChainComplete = true; + + // In case we have dao monitoring deactivated we create the snapshot after we are completed with parsing + // and we got called back from daoStateMonitoringService once the hashes are created from peers data. + if (!preferences.isUseFullModeDaoMonitor()) { + // We register a callback handler once the daoStateMonitoringService has received the missing hashes from + // the seed node and applied the latest hash. After that we are ready to make a snapshot and persist it. + daoStateMonitoringService.setCreateSnapshotHandler(() -> { + // As we did not have created any snapshots during initial parsing we create it now. We cannot use the past + // snapshot height as we have not cloned a candidate (that would cause quite some delay during parsing). + // The next snapshots will be created again according to the snapshot height grid (each 20 blocks). + // This also comes with the improvement that the user does not need to load the past blocks back to the last + // snapshot height. Thought it comes also with the small risk that in case of re-orgs the user need to do + // a resync in case the dao state would have been affected by that reorg. + long ts = System.currentTimeMillis(); + // We do not keep a copy of the clone as we use it immediately for persistence. + GcUtil.maybeReleaseMemory(); + log.info("Create snapshot at height {}", daoStateService.getChainHeight()); + daoStateStorageService.requestPersistence(daoStateService.getClone(), + new LinkedList<>(daoStateMonitoringService.getDaoStateHashChain()), + () -> { + log.info("Persisted daoState after parsing completed at height {}. Took {} ms", + daoStateService.getChainHeight(), System.currentTimeMillis() - ts); + }); + GcUtil.maybeReleaseMemory(); + }); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// - // We do not use DaoStateListener.onDaoStateChanged but let the DaoEventCoordinator call maybeCreateSnapshot to ensure the - // correct order of execution. // We need to process during batch processing as well to write snapshots during that process. public void maybeCreateSnapshot(Block block) { int chainHeight = block.getHeight(); @@ -124,42 +176,68 @@ public class DaoStateSnapshotService implements DaoSetupService { // We protect to get called while we are not completed with persisting the daoState. This can take about // 20 seconds and it is not expected that we get triggered another snapshot event in that period, but this // check guards that we would skip such calls.. - if (requestPersistenceCalled) { - log.warn("We try to persist a daoState but the previous call has not completed yet. " + - "We ignore that call and skip that snapshot. " + - "Snapshot will be created at next snapshot height again. This is not to be expected with live " + - "blockchain data."); + if (!readyForPersisting) { + if (preferences.isUseFullModeDaoMonitor()) { + // In case we dont use isUseFullModeDaoMonitor we might called here too often as the parsing is much + // faster than the persistence and we likely create only 1 snapshot during initial parsing, so + // we log only if isUseFullModeDaoMonitor is true as then parsing is likely slower and we would + // expect that we do a snapshot at each trigger block. + log.info("We try to persist a daoState but the previous call has not completed yet. " + + "We ignore that call and skip that snapshot. " + + "Snapshot will be created at next snapshot height again. This is not to be expected with live " + + "blockchain data."); + } return; } - GcUtil.maybeReleaseMemory(); - - // At trigger event we store the latest snapshotCandidate to disc - long ts = System.currentTimeMillis(); - requestPersistenceCalled = true; - daoStateStorageService.requestPersistence(daoStateSnapshotCandidate, - daoStateHashChainSnapshotCandidate, - () -> { - log.info("Serializing snapshotCandidate for writing to Disc with height {} at height {} took {} ms", - daoStateSnapshotCandidate != null ? daoStateSnapshotCandidate.getChainHeight() : "N/A", - chainHeight, - System.currentTimeMillis() - ts); - - long ts2 = System.currentTimeMillis(); - - GcUtil.maybeReleaseMemory(); - - // Now we clone and keep it in memory for the next trigger event - daoStateSnapshotCandidate = daoStateService.getClone(); - daoStateHashChainSnapshotCandidate = new LinkedList<>(daoStateMonitoringService.getDaoStateHashChain()); - - log.info("Cloned new snapshotCandidate at height {} took {} ms", chainHeight, System.currentTimeMillis() - ts2); - requestPersistenceCalled = false; - GcUtil.maybeReleaseMemory(); - }); + if (daoStateSnapshotCandidate != null) { + persist(); + } else { + createClones(); + } } } + private void persist() { + long ts = System.currentTimeMillis(); + readyForPersisting = false; + daoStateStorageService.requestPersistence(daoStateSnapshotCandidate, + daoStateHashChainSnapshotCandidate, + () -> { + log.info("Serializing snapshotCandidate for writing to Disc at chainHeight {} took {} ms.\n" + + "daoStateSnapshotCandidate.height={};\n" + + "daoStateHashChainSnapshotCandidate.height={}", + daoStateService.getChainHeight(), + System.currentTimeMillis() - ts, + daoStateSnapshotCandidate != null ? daoStateSnapshotCandidate.getChainHeight() : "N/A", + daoStateHashChainSnapshotCandidate != null && !daoStateHashChainSnapshotCandidate.isEmpty() ? + daoStateHashChainSnapshotCandidate.getLast().getHeight() : "N/A"); + + createClones(); + readyForPersisting = true; + }); + } + + private void createClones() { + long ts = System.currentTimeMillis(); + // Now we clone and keep it in memory for the next trigger event + // We do not fit into the target grid of 20 blocks as we get called here once persistence is + // done from the write thread (mapped back to user thread). + // As we want to prevent to maintain 2 clones we prefer that strategy. If we would do the clone + // after the persist call we would keep an additional copy in memory. + daoStateSnapshotCandidate = daoStateService.getClone(); + daoStateHashChainSnapshotCandidate = new LinkedList<>(daoStateMonitoringService.getDaoStateHashChain()); + GcUtil.maybeReleaseMemory(); + + log.info("Cloned new snapshotCandidate at chainHeight {} took {} ms.\n" + + "daoStateSnapshotCandidate.height={};\n" + + "daoStateHashChainSnapshotCandidate.height={}", + daoStateService.getChainHeight(), System.currentTimeMillis() - ts, + daoStateSnapshotCandidate != null ? daoStateSnapshotCandidate.getChainHeight() : "N/A", + daoStateHashChainSnapshotCandidate != null && !daoStateHashChainSnapshotCandidate.isEmpty() ? + daoStateHashChainSnapshotCandidate.getLast().getHeight() : "N/A"); + } + public void applySnapshot(boolean fromReorg) { DaoState persistedBsqState = daoStateStorageService.getPersistedBsqState(); LinkedList persistedDaoStateHashChain = daoStateStorageService.getPersistedDaoStateHashChain(); @@ -173,6 +251,7 @@ public class DaoStateSnapshotService implements DaoSetupService { chainHeightOfLastApplySnapshot = chainHeightOfPersisted; daoStateService.applySnapshot(persistedBsqState); daoStateMonitoringService.applySnapshot(persistedDaoStateHashChain); + daoStateStorageService.pruneStore(); } else { // The reorg might have been caused by the previous parsing which might contains a range of // blocks. From fac4b3c5c30186af64737955f180f433403230c5 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 4 Nov 2021 13:22:32 +0100 Subject: [PATCH 34/38] Fix combobox BSQ selection error --- .../main/offer/offerbook/OfferBookView.java | 5 ++++ .../offer/offerbook/OfferBookViewModel.java | 29 ++++++++++++------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 4d5c9055eb..4f571c2bc1 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -613,6 +613,11 @@ public class OfferBookView extends ActivatableViewAndModel Date: Thu, 4 Nov 2021 13:23:00 +0100 Subject: [PATCH 35/38] Fix translations --- core/src/main/resources/i18n/displayStrings.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index fc6a23dfe5..0fbc9aa99a 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -446,10 +446,10 @@ offerbook.info.roundedFiatVolume=The amount was rounded to increase the privacy offerbook.info.accountCreated.headline=Congratulations offerbook.info.accountCreated.message=You''ve just successfully created a BSQ payment account.\n\ Your account can be found under Account > Altcoins Accounts > {0} and your BSQ wallet under DAO > BSQ Wallet.\n\n -offerbook.info.accountCreated.tradeInstant=As you've chosen to take a BSQ instant offer a BSQ instant account was created for you. \ - For instant trading it is required that both trading peers are online to be able \ - to complete the trade in less than 1 hour.\n\n -offerbook.info.accountCreated.takeOffer=You can no proceed to take this offer after closing this popup. +offerbook.info.accountCreated.tradeInstant=You've chosen to take a BSQ instant offer, so a BSQ instant payment account was created for you. \ + Be aware that instant trades are meant to be completed within 1 hour, \ + so you should be online and available for the next 1 hour.\n\n +offerbook.info.accountCreated.takeOffer=You can now proceed to take this offer after closing this popup. offerbook.bsqSwap.createOffer=Create Bsq swap offer From 2b906332639224e7c5ed3197599d3b2b1d233bdd Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 8 Nov 2021 11:05:27 +0100 Subject: [PATCH 36/38] Improve naming of helper method --- .../desktop/main/offer/bisq_v1/MutableOfferDataModel.java | 4 +++- .../desktop/main/offer/bisq_v1/MutableOfferViewModel.java | 2 +- .../main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java | 4 +++- .../main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferDataModel.java index da401709ee..f5fdba99dc 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferDataModel.java @@ -781,7 +781,9 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs return offerUtil.isBsqForMakerFeeAvailable(amount.get()); } - boolean isBuyBsqOffer() { + boolean isAttemptToBuyBsq() { + // When you buy an asset you actually sell BTC. + // This is why an offer to buy BSQ is actually an offer to sell BTC for BSQ. return !isBuyOffer() && getTradeCurrency().getCode().equals("BSQ"); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java index c07dde3d61..3ab607ca95 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferViewModel.java @@ -1355,6 +1355,6 @@ public abstract class MutableOfferViewModel ext } public boolean isShowBuyBsqHint() { - return !dataModel.isBsqForFeeAvailable() && !dataModel.isBuyBsqOffer(); + return !dataModel.isBsqForFeeAvailable() && !dataModel.isAttemptToBuyBsq(); } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java index a0ced384b1..26171216a1 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferDataModel.java @@ -713,7 +713,9 @@ class TakeOfferDataModel extends OfferDataModel { return offerUtil.isBsqForTakerFeeAvailable(amount.get()); } - public boolean isBuyBsqOffer() { + public boolean isAttemptToBuyBsq() { + // When you buy an asset you actually sell BTC. + // This is why an offer to buy BSQ is actually an offer to sell BTC for BSQ. return !isBuyOffer() && getOffer().getCurrencyCode().equals("BSQ"); } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java index 61c52a4355..5d8c180d35 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferViewModel.java @@ -802,6 +802,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel im } public boolean isShowBuyBsqHint() { - return !dataModel.isBsqForFeeAvailable() && !dataModel.isBuyBsqOffer(); + return !dataModel.isBsqForFeeAvailable() && !dataModel.isAttemptToBuyBsq(); } } From 07e224e40ad838a968b1883be3ff8bfa6384abb0 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 8 Nov 2021 11:06:00 +0100 Subject: [PATCH 37/38] Simplify containers and improve truncation behavior --- .../main/offer/bisq_v1/MutableOfferView.java | 11 +++++++---- .../desktop/main/offer/bisq_v1/OfferViewUtil.java | 15 ++++++--------- .../offer/bisq_v1/takeoffer/TakeOfferView.java | 11 +++++++---- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java index 244c2f5299..af7581701a 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/MutableOfferView.java @@ -153,10 +153,10 @@ public abstract class MutableOfferView> exten private ComboBox paymentAccountsComboBox; private ComboBox currencyComboBox; private ImageView qrCodeImageView; - private VBox currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox, buyBsqBox; + private VBox currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox; private HBox fundingHBox, firstRowHBox, secondRowHBox, placeOfferBox, amountValueCurrencyBox, priceAsPercentageValueCurrencyBox, volumeValueCurrencyBox, priceValueCurrencyBox, - minAmountValueCurrencyBox, advancedOptionsBox, triggerPriceHBox; + minAmountValueCurrencyBox, advancedOptionsBox, triggerPriceHBox, buyBsqBox; private Subscription isWaitingForFundsSubscription, balanceSubscription; private ChangeListener amountFocusedListener, minAmountFocusedListener, volumeFocusedListener, @@ -1100,18 +1100,21 @@ public abstract class MutableOfferView> exten advancedOptionsBox.setSpacing(40); GridPane.setRowIndex(advancedOptionsBox, gridRow); + GridPane.setColumnSpan(advancedOptionsBox, GridPane.REMAINING); GridPane.setColumnIndex(advancedOptionsBox, 0); GridPane.setHalignment(advancedOptionsBox, HPos.LEFT); GridPane.setMargin(advancedOptionsBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); gridPane.getChildren().add(advancedOptionsBox); - Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( + Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( navigation, preferences); buyBsqBox = buyBsqButtonBox.second; buyBsqBox.setManaged(false); buyBsqBox.setVisible(false); - advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), getTradeFeeFieldsBox(), buyBsqBox); + VBox tradeFeeFieldsBox = getTradeFeeFieldsBox(); + tradeFeeFieldsBox.setMinWidth(240); + advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), tradeFeeFieldsBox, buyBsqBox); Tuple2 tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, Res.get("shared.nextStep"), Res.get("shared.cancel")); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java index 2558c62a45..712a8fdd20 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/OfferViewUtil.java @@ -36,7 +36,6 @@ import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; import javafx.geometry.HPos; import javafx.geometry.Insets; @@ -85,12 +84,13 @@ public class OfferViewUtil { infoGridPane.getChildren().addAll(label, textField); } - public static Tuple2 createBuyBsqButtonBox(Navigation navigation, + public static Tuple2 createBuyBsqButtonBox(Navigation navigation, Preferences preferences) { String buyBsqText = Res.get("shared.buyCurrency", "BSQ"); var buyBsqButton = new AutoTooltipButton(buyBsqText); buyBsqButton.getStyleClass().add("action-button"); buyBsqButton.getStyleClass().add("tiny-button"); + buyBsqButton.setMinWidth(60); buyBsqButton.setOnAction(e -> openBuyBsqOfferBook(navigation, preferences) ); @@ -101,17 +101,14 @@ public class OfferViewUtil { .actionButtonText(buyBsqText) .buttonAlignment(HPos.CENTER) .onAction(() -> openBuyBsqOfferBook(navigation, preferences)).show()); + learnMore.setMinWidth(100); - final HBox buyBsqBox = new HBox(buyBsqButton, info, learnMore); + HBox buyBsqBox = new HBox(buyBsqButton, info, learnMore); buyBsqBox.setAlignment(Pos.BOTTOM_LEFT); buyBsqBox.setSpacing(10); + buyBsqBox.setPadding(new Insets(0, 0, 4, -20)); - final VBox buyBsqButtonVBox = new VBox(buyBsqBox); - buyBsqButtonVBox.setAlignment(Pos.BOTTOM_LEFT); - buyBsqButtonVBox.setPadding(new Insets(0, 0, 0, -20)); - VBox.setMargin(buyBsqButton, new Insets(0, 0, 4, 0)); - - return new Tuple2<>(buyBsqButton, buyBsqButtonVBox); + return new Tuple2<>(buyBsqButton, buyBsqBox); } private static void openBuyBsqOfferBook(Navigation navigation, Preferences preferences) { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java index 520eb9d9f3..21a08d06e3 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bisq_v1/takeoffer/TakeOfferView.java @@ -132,10 +132,10 @@ public class TakeOfferView extends ActivatableViewAndModel paymentAccountsComboBox; private Label amountDescriptionLabel, paymentMethodLabel, @@ -878,18 +878,21 @@ public class TakeOfferView extends ActivatableViewAndModel buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( + Tuple2 buyBsqButtonBox = OfferViewUtil.createBuyBsqButtonBox( navigation, model.dataModel.preferences); buyBsqBox = buyBsqButtonBox.second; buyBsqBox.setManaged(false); buyBsqBox.setVisible(false); - advancedOptionsBox.getChildren().addAll(getTradeFeeFieldsBox(), buyBsqBox); + VBox tradeFeeFieldsBox = getTradeFeeFieldsBox(); + tradeFeeFieldsBox.setMinWidth(240); + advancedOptionsBox.getChildren().addAll(tradeFeeFieldsBox, buyBsqBox); } private void addButtons() { From 7e1696b09540bd7efb98721f68d63199b8b1d303 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 8 Nov 2021 21:49:08 +0100 Subject: [PATCH 38/38] Fix typos, cleanups --- .../bisq/core/dao/monitoring/DaoStateMonitoringService.java | 2 +- .../main/java/bisq/core/dao/state/DaoStateSnapshotService.java | 2 +- .../bisq/core/trade/statistics/TradeStatisticsManager.java | 1 - .../core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java | 2 +- core/src/main/resources/i18n/displayStrings.properties | 2 +- .../java/bisq/desktop/main/dao/monitor/StateMonitorView.java | 3 --- .../main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java | 1 - .../main/dao/monitor/proposals/ProposalStateMonitorView.java | 1 - 8 files changed, 4 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java index 178f29cdc1..cb8d0b9533 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java @@ -369,7 +369,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe stateHashes.forEach(peersHash -> { Optional optionalDaoStateBlock; // If we do not add own hashes during initial parsing we fill the missing hashes from the peer and create - // the at the last block our own hash. + // at the last block our own hash. int height = peersHash.getHeight(); if (!useDaoMonitor && !findDaoStateBlock(height).isPresent()) { diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java index f13748aa71..7a724a3f20 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java @@ -138,7 +138,7 @@ public class DaoStateSnapshotService implements DaoSetupService, DaoStateListene // snapshot height as we have not cloned a candidate (that would cause quite some delay during parsing). // The next snapshots will be created again according to the snapshot height grid (each 20 blocks). // This also comes with the improvement that the user does not need to load the past blocks back to the last - // snapshot height. Thought it comes also with the small risk that in case of re-orgs the user need to do + // snapshot height. Though it comes also with the small risk that in case of re-orgs the user need to do // a resync in case the dao state would have been affected by that reorg. long ts = System.currentTimeMillis(); // We do not keep a copy of the clone as we use it immediately for persistence. diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java index 6049e67e38..e94ed5d683 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -82,7 +82,6 @@ public class TradeStatisticsManager { this.storageDir = storageDir; this.dumpStatistics = dumpStatistics; - appendOnlyDataStoreService.addService(tradeStatistics3StorageService); } 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 2560d71a11..cbd20b8738 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 @@ -138,7 +138,7 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade { // 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 + // Though numRequiredConfirmations can be changed during request process and will be read from // autoConfirmSettings at result parsing. List serviceAddresses = autoConfirmSettings.getServiceAddresses(); numRequiredSuccessResults = serviceAddresses.size(); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 4dbba91740..37505f0e41 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2531,7 +2531,7 @@ dao.monitor.isInConflictWithSeedNode=Your local data is not in consensus with at Please resync the DAO state. dao.monitor.isInConflictWithNonSeedNode=One of your peers is not in consensus with the network but your node \ is in sync with the seed nodes. -dao.monitor.isDaoStateBlockChainNotConnecting=You dao state chain is not connecting with the new data. \ +dao.monitor.isDaoStateBlockChainNotConnecting=Your DAO state chain is not connecting with the new data. \ Please resync the DAO state. dao.monitor.daoStateInSync=Your local node is in consensus with the network diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java index 1a07e709db..8816f0b95e 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/StateMonitorView.java @@ -332,7 +332,6 @@ public abstract class StateMonitorView(getHashTableHeader()); column.setMinWidth(120); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); @@ -381,7 +380,6 @@ public abstract class StateMonitorView e.getStateBlock().getPeersMap().size())); tableView.getColumns().add(column); - column = new AutoTooltipTableColumn<>(getConflictsTableHeader()); column.setMinWidth(80); column.setMaxWidth(column.getMinWidth()); @@ -406,7 +404,6 @@ public abstract class StateMonitorView e.getStateBlock().getInConflictMap().size())); tableView.getColumns().add(column); - column = new AutoTooltipTableColumn<>(""); column.setMinWidth(40); column.setMaxWidth(column.getMinWidth()); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java index 03b3e59bd9..5f470109bf 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/blindvotes/BlindVoteStateMonitorView.java @@ -216,7 +216,6 @@ public class BlindVoteStateMonitorView extends StateMonitorView