From 860e41844a7db04cf0e328144ee6ec3893264e56 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 2 Feb 2018 08:48:59 +0100 Subject: [PATCH 01/62] Remove unecessary style --- gui/src/main/java/io/bisq/gui/bisq.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/bisq.css b/gui/src/main/java/io/bisq/gui/bisq.css index fdf6b1dbe25..de4edc8ef12 100644 --- a/gui/src/main/java/io/bisq/gui/bisq.css +++ b/gui/src/main/java/io/bisq/gui/bisq.css @@ -362,10 +362,6 @@ textfield */ -fx-padding: 4 4 4 4; } -#address-text-field:hover { - -fx-text-fill: -bs-black; -} - #funds-confidence { -fx-progress-color: -bs-dim-grey; } From 509b7d2f5e04db66fac3f71025b88ccbace81bcf Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 2 Feb 2018 09:22:30 +0100 Subject: [PATCH 02/62] Remove unnecessary style --- gui/src/main/java/io/bisq/gui/bisq.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/bisq.css b/gui/src/main/java/io/bisq/gui/bisq.css index de4edc8ef12..9aa384c2351 100644 --- a/gui/src/main/java/io/bisq/gui/bisq.css +++ b/gui/src/main/java/io/bisq/gui/bisq.css @@ -498,10 +498,6 @@ textfield */ -fx-text-fill: -bs-medium-grey; } -#clickable-icon:hover { - -fx-text-fill: -bs-grey; -} - /******************************************************************************* * * * Images * From 50fde309ac41003b6510bc5916b63e63ca3c9058 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 2 Feb 2018 09:24:03 +0100 Subject: [PATCH 03/62] Reposition and colorize existing icons and add security deposit with currency code --- gui/src/main/java/io/bisq/gui/bisq.css | 8 ++++++++ .../io/bisq/gui/components/AddressTextField.java | 6 +++--- .../io/bisq/gui/components/FundsTextField.java | 16 ++++++++-------- .../main/offer/createoffer/CreateOfferView.java | 2 +- .../offer/createoffer/CreateOfferViewModel.java | 5 ++--- .../gui/main/offer/takeoffer/TakeOfferView.java | 2 +- .../main/offer/takeoffer/TakeOfferViewModel.java | 5 ++--- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/bisq.css b/gui/src/main/java/io/bisq/gui/bisq.css index 9aa384c2351..a1021f97356 100644 --- a/gui/src/main/java/io/bisq/gui/bisq.css +++ b/gui/src/main/java/io/bisq/gui/bisq.css @@ -126,6 +126,14 @@ bg color of non edit textFields: fafafa -fx-text-fill: -bs-black; } +.info { + -fx-text-fill: -bs-bg-green2; +} + +.info:hover { + -fx-text-fill: -bs-grey; +} + .headline-label { -fx-font-weight: bold; -fx-font-size: 22; diff --git a/gui/src/main/java/io/bisq/gui/components/AddressTextField.java b/gui/src/main/java/io/bisq/gui/components/AddressTextField.java index 09b0c6988fb..6228154d61b 100644 --- a/gui/src/main/java/io/bisq/gui/components/AddressTextField.java +++ b/gui/src/main/java/io/bisq/gui/components/AddressTextField.java @@ -87,12 +87,12 @@ public AddressTextField() { Utilities.copyToClipboard(address.get()); })); - AnchorPane.setRightAnchor(copyIcon, 5.0); - AnchorPane.setRightAnchor(extWalletIcon, 30.0); + AnchorPane.setRightAnchor(copyIcon, 30.0); + AnchorPane.setRightAnchor(extWalletIcon, 5.0); AnchorPane.setRightAnchor(textField, 55.0); AnchorPane.setLeftAnchor(textField, 0.0); - getChildren().addAll(textField, extWalletIcon, copyIcon); + getChildren().addAll(textField, copyIcon, extWalletIcon); } private void openWallet() { diff --git a/gui/src/main/java/io/bisq/gui/components/FundsTextField.java b/gui/src/main/java/io/bisq/gui/components/FundsTextField.java index 6d0fd820d01..adf960120ab 100644 --- a/gui/src/main/java/io/bisq/gui/components/FundsTextField.java +++ b/gui/src/main/java/io/bisq/gui/components/FundsTextField.java @@ -58,8 +58,7 @@ public FundsTextField() { infoIcon = new Label(); infoIcon.setLayoutY(3); - infoIcon.setId("clickable-icon"); - infoIcon.getStyleClass().addAll("highlight", "show-hand"); + infoIcon.getStyleClass().addAll("icon", "info"); AwesomeDude.setIcon(infoIcon, AwesomeIcon.INFO_SIGN); Label copyIcon = new Label(); @@ -81,9 +80,9 @@ public FundsTextField() { } }); - AnchorPane.setRightAnchor(copyIcon, 5.0); - AnchorPane.setRightAnchor(infoIcon, 37.0); - AnchorPane.setRightAnchor(textField, 30.0); + AnchorPane.setRightAnchor(copyIcon, 30.0); + AnchorPane.setRightAnchor(infoIcon, 62.0); + AnchorPane.setRightAnchor(textField, 55.0); AnchorPane.setLeftAnchor(textField, 0.0); getChildren().addAll(textField, infoIcon, copyIcon); @@ -95,7 +94,7 @@ public FundsTextField() { public void setContentForInfoPopOver(Node node) { // As we don't use binding here we need to recreate it on mouse over to reflect the current state - infoIcon.setOnMouseEntered(e -> createInfoPopOver(node)); + infoIcon.setOnMouseEntered(e -> showInfoPopOver(node)); infoIcon.setOnMouseExited(e -> { if (infoPopover != null) infoPopover.hide(); @@ -106,10 +105,11 @@ public void setContentForInfoPopOver(Node node) { // Private /////////////////////////////////////////////////////////////////////////////////////////// - private void createInfoPopOver(Node node) { + private void showInfoPopOver(Node node) { node.getStyleClass().add("default-text"); - infoPopover = new PopOver(node); + if (infoPopover == null) infoPopover = new PopOver(node); + if (infoIcon.getScene() != null) { infoPopover.setDetachable(false); infoPopover.setArrowLocation(PopOver.ArrowLocation.RIGHT_TOP); diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java index 16ba5a77390..c2d5d37300f 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java @@ -408,7 +408,7 @@ private void onShowPayFundsScreen() { cancelButton2.setVisible(true); totalToPayTextField.setFundsStructure(Res.get("createOffer.fundsBox.fundsStructure", - model.getSecurityDepositPercentage(), model.getMakerFeePercentage(), model.getTxFeePercentage())); + model.getSecurityDepositWithCode(), model.getMakerFeePercentage(), model.getTxFeePercentage())); totalToPayTextField.setContentForInfoPopOver(createInfoPopover()); final byte[] imageBytes = QRCode diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java index 273aca314e9..288eb6f77f1 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java @@ -831,9 +831,8 @@ public String getSecurityDepositInfo() { GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDeposit(), dataModel.getAmount().get(), btcFormatter); } - public String getSecurityDepositPercentage() { - return GUIUtil.getPercentage(dataModel.getSecurityDeposit(), dataModel.getAmount().get(), - btcFormatter); + public String getSecurityDepositWithCode() { + return btcFormatter.formatCoinWithCode(dataModel.getSecurityDeposit()); } public String getMakerFee() { diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java index 7ee6758d8d9..66b51de44fb 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java @@ -419,7 +419,7 @@ private void onShowPayFundsScreen() { balanceTextField.setVisible(true); totalToPayTextField.setFundsStructure(Res.get("createOffer.fundsBox.fundsStructure", - model.getSecurityDepositPercentage(), model.getMakerFeePercentage(), model.getTxFeePercentage())); + model.getSecurityDepositWithCode(), model.getMakerFeePercentage(), model.getTxFeePercentage())); totalToPayTextField.setContentForInfoPopOver(createInfoPopover()); if (model.dataModel.isWalletFunded.get()) { diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferViewModel.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferViewModel.java index 66fd32c93a4..5286f0ea308 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferViewModel.java @@ -584,9 +584,8 @@ public String getSecurityDepositInfo() { GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDeposit(), dataModel.getAmount().get(), btcFormatter); } - public String getSecurityDepositPercentage() { - return GUIUtil.getPercentage(dataModel.getSecurityDeposit(), dataModel.getAmount().get(), - btcFormatter); + public String getSecurityDepositWithCode() { + return btcFormatter.formatCoinWithCode(dataModel.getSecurityDeposit()); } public String getTakerFee() { From e4e376deadf8315905e02d232ca5f318a7bd7603 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Fri, 2 Feb 2018 13:22:02 +0100 Subject: [PATCH 04/62] Don't return genesis if no tx found --- .../java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java index 2967f29802b..a6a343b7a5b 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java @@ -384,8 +384,6 @@ public boolean containsTx(String txId) { public Optional findTx(String txId) { Tx tx = getTxMap().get(txId); - if (tx == null) - tx = getGenesisTx(); //todo have to be in txmap already -> remove after check if (tx != null) return Optional.of(tx); else From 3f4a3afb2fdc32be508b5ca79b06902986a7ba8f Mon Sep 17 00:00:00 2001 From: sqrrm Date: Fri, 2 Feb 2018 13:35:37 +0100 Subject: [PATCH 05/62] Remove comment, map is used in many ways --- .../java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java index a6a343b7a5b..c68fc0be055 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqBlockChain.java @@ -394,7 +394,6 @@ public int getChainHeadHeight() { return lock.read(() -> chainHeadHeight); } - // Only used for Json Exporter public Map getTxMap() { return lock.read(() -> txMap); } From 75f0b4ad7185f40b90c49561a64b1af7395474b8 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 2 Feb 2018 15:21:45 +0100 Subject: [PATCH 06/62] Remove translation that is not used --- common/src/main/resources/i18n/displayStrings.properties | 1 - common/src/main/resources/i18n/displayStrings_de.properties | 1 - common/src/main/resources/i18n/displayStrings_el.properties | 1 - common/src/main/resources/i18n/displayStrings_es.properties | 1 - common/src/main/resources/i18n/displayStrings_hu.properties | 1 - common/src/main/resources/i18n/displayStrings_pt.properties | 1 - common/src/main/resources/i18n/displayStrings_ro.properties | 1 - common/src/main/resources/i18n/displayStrings_ru.properties | 1 - common/src/main/resources/i18n/displayStrings_sr.properties | 1 - common/src/main/resources/i18n/displayStrings_zh.properties | 1 - 10 files changed, 10 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 99ce4e5a7f1..af1651c3f2a 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -1526,7 +1526,6 @@ navigation.dao.wallet.receive=\"DAO/BSQ Wallet/Receive\" #################################################################### formatter.formatVolumeLabel={0} amount{1} -formatter.tradePeriodOver=Trade period is over formatter.makerTaker=Maker as {0} {1} / Taker as {2} {3} formatter.youAreAsMaker=You are {0} {1} as maker / Taker is {2} {3} formatter.youAreAsTaker=You are {0} {1} as taker / Maker is {2} {3} diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 270fcdeea71..270f6216435 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -1287,7 +1287,6 @@ navigation.dao.wallet.receive=\"DAO/BSQ-Wallet/Erhalten\" #################################################################### formatter.formatVolumeLabel={0} Betrag{1} -formatter.tradePeriodOver=Die Handelsdauer ist abgelaufen formatter.makerTaker=Ersteller als {0} {1} / Abnehmer als {2} {3} formatter.youAreAsMaker=Sie {0} {1} als Ersteller / Abnehmer wird {3} {2} formatter.youAreAsTaker=Sie {0} {1} als Abnehmer / Ersteller wird {3} {2} diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index bf2a11e7d82..91b46244721 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -1250,7 +1250,6 @@ navigation.dao.wallet.receive=\"DAO/πορτοφόλι BSQ/Λήψη\" #################################################################### formatter.formatVolumeLabel={0} ποσό{1} -formatter.tradePeriodOver=Λήξη περιόδου συναλλαγής formatter.makerTaker=Maker ως {0} {1} / Taker ως {2} {3} formatter.youAreAsMaker=Είσαι {0} {1} ως maker / Taker είναι {2} {3{} formatter.youAreAsTaker=Είσαι {0} {1} ως taker / Maker είναι {2} {3} diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index 6e4bd4e215f..aefe7f02103 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -1250,7 +1250,6 @@ navigation.dao.wallet.receive=\"DAO/Monedero BSQ/Recibir\" #################################################################### formatter.formatVolumeLabel={0} cantidad{1} -formatter.tradePeriodOver=El periodo de intercambio se acabó formatter.makerTaker=Creador como {0} {1} / Tomador como {2} {3} formatter.youAreAsMaker=Usted es {0} {1} como creador / Tomador es {2} {3} formatter.youAreAsTaker=Usted es {0} {1} como tomador / Creador es {2} {3} diff --git a/common/src/main/resources/i18n/displayStrings_hu.properties b/common/src/main/resources/i18n/displayStrings_hu.properties index 1289b95070c..986436439e2 100644 --- a/common/src/main/resources/i18n/displayStrings_hu.properties +++ b/common/src/main/resources/i18n/displayStrings_hu.properties @@ -1250,7 +1250,6 @@ navigation.dao.wallet.receive=\"DAO/BSQ Pénztárca/Fogadás\" #################################################################### formatter.formatVolumeLabel={0} összeg{1} -formatter.tradePeriodOver=Tranzakció időszak véget ért formatter.makerTaker=Ajánló mint {0} {1} / Vevő mint {2} {3} formatter.youAreAsMaker=Ön {0} {1} mint ajánló / A vevő {2} {3} formatter.youAreAsTaker=Ön {0} {1} mint vevő / Az ajánló {2} {3} diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index 7ec55e74b7d..abb50edd7ca 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -1250,7 +1250,6 @@ navigation.dao.wallet.receive=\"DAO/Carteira BSQ/Receber\" #################################################################### formatter.formatVolumeLabel={0} quantia{1} -formatter.tradePeriodOver=O período de negociação acabou formatter.makerTaker=Ofertante como {0} {1} / Aceitador como {2} {3} formatter.youAreAsMaker=Você está {0} {1} como ofertante / Aceitador está {2} {3} formatter.youAreAsTaker=Você está {0} {1} como aceitador / Ofetante é {2} {3} diff --git a/common/src/main/resources/i18n/displayStrings_ro.properties b/common/src/main/resources/i18n/displayStrings_ro.properties index b6434b03e5c..71556cc63b2 100644 --- a/common/src/main/resources/i18n/displayStrings_ro.properties +++ b/common/src/main/resources/i18n/displayStrings_ro.properties @@ -1250,7 +1250,6 @@ navigation.dao.wallet.receive=\"Portofel/Încasare DAO/BSQ\" #################################################################### formatter.formatVolumeLabel={0} suma{1} -formatter.tradePeriodOver=Perioada de tranzacționare s-a încheiat formatter.makerTaker=Ofertant ca {0} {1} / Acceptant ca {2} {3} formatter.youAreAsMaker=Tu ești {0} {1} ca ofertant / Acceptantul este {2} {3} formatter.youAreAsTaker=Tu ești {0} {1} ca acceptant / Ofertantul este {2} {3} diff --git a/common/src/main/resources/i18n/displayStrings_ru.properties b/common/src/main/resources/i18n/displayStrings_ru.properties index 9350dfaa541..4660bf8f07b 100644 --- a/common/src/main/resources/i18n/displayStrings_ru.properties +++ b/common/src/main/resources/i18n/displayStrings_ru.properties @@ -1250,7 +1250,6 @@ navigation.dao.wallet.receive=\"ДАО/BSQ Кошелёк/Получить\" #################################################################### formatter.formatVolumeLabel={0} сумма{1} -formatter.tradePeriodOver=Время сделки истекло formatter.makerTaker=как создающий предложение {0} {1} / Как принимающий предложение {2} {3} formatter.youAreAsMaker=Вы {0} {1}, как дающий предложение / Принимающий {2} {3} formatter.youAreAsTaker=Вы {0} {1}, как принимающий предложение / Создающий {2} {3} diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index a48805ba13d..4a1132f5ddf 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -1250,7 +1250,6 @@ navigation.dao.wallet.receive=\"DAO/BSQ Novčanik/Primi\" #################################################################### formatter.formatVolumeLabel={0} iznos{1} -formatter.tradePeriodOver=Period trgovine je završen formatter.makerTaker=Tvorac kao {0} {1} / Uzimalac kao {2} {3} formatter.youAreAsMaker=Vi ste {0} {1} kao tvorac / Uzimalac je {2} {3} formatter.youAreAsTaker=Vi ste {0} {1} kao uzimalac / Tvorac je {2} {3} diff --git a/common/src/main/resources/i18n/displayStrings_zh.properties b/common/src/main/resources/i18n/displayStrings_zh.properties index a78001b8ef7..dd71a94fe0a 100644 --- a/common/src/main/resources/i18n/displayStrings_zh.properties +++ b/common/src/main/resources/i18n/displayStrings_zh.properties @@ -1250,7 +1250,6 @@ navigation.dao.wallet.receive=\"DAO/BSQ 钱包/接收\" #################################################################### formatter.formatVolumeLabel={0} 数量 {1} -formatter.tradePeriodOver=交易期结束 formatter.makerTaker=卖家{0} {1} / 买家 {2} {3} formatter.youAreAsMaker=您是{0} {1}卖家 / 买家是 {2} {3} formatter.youAreAsTaker=您是{0} {1} 买家 / 卖家是 {2} {3} From 76046c3de6d29462caf9a725cacc1f994dddae02 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 2 Feb 2018 15:27:19 +0100 Subject: [PATCH 07/62] Remove trade date until from max trade period --- common/src/main/resources/i18n/displayStrings.properties | 2 +- common/src/main/resources/i18n/displayStrings_de.properties | 2 +- common/src/main/resources/i18n/displayStrings_el.properties | 2 +- common/src/main/resources/i18n/displayStrings_es.properties | 2 +- common/src/main/resources/i18n/displayStrings_hu.properties | 2 +- common/src/main/resources/i18n/displayStrings_pt.properties | 2 +- common/src/main/resources/i18n/displayStrings_ro.properties | 2 +- common/src/main/resources/i18n/displayStrings_ru.properties | 2 +- common/src/main/resources/i18n/displayStrings_sr.properties | 2 +- common/src/main/resources/i18n/displayStrings_zh.properties | 2 +- .../gui/components/paymentmethods/PaymentMethodForm.java | 5 ++--- .../main/portfolio/pendingtrades/steps/TradeStepView.java | 3 +-- 12 files changed, 13 insertions(+), 15 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index af1651c3f2a..f1adc48e01e 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -1671,7 +1671,7 @@ payment.altcoin.address.dyn={0} address: payment.accountNr=Account number: payment.emailOrMobile=Email or mobile nr: payment.useCustomAccountName=Use custom account name -payment.maxPeriod=Max. allowed trade period / date: +payment.maxPeriod=Max. allowed trade period: payment.maxPeriodAndLimit=Max. trade duration: {0} / Max. trade limit: {1} / Account age: {2} payment.currencyWithSymbol=Currency: {0} payment.nameOfAcceptedBank=Name of accepted bank diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 270f6216435..98fa198a614 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -1414,7 +1414,7 @@ payment.altcoin.address.dyn={0} Adresse: payment.accountNr=Kontonummer: payment.emailOrMobile=E-Mail oder Mobil: payment.useCustomAccountName=Spezifischen Kontonamen nutzen -payment.maxPeriod=Max. erlaubte Handelsdauer / -datum: +payment.maxPeriod=Max. erlaubte Handelsdauer: payment.maxPeriodAndLimit=Max. Handelsdauer: {0} / Max. Handelsgrenze: {1} payment.currencyWithSymbol=Währung: {0} payment.nameOfAcceptedBank=Name der akzeptierten Bank diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index 91b46244721..2005082e258 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -1377,7 +1377,7 @@ payment.altcoin.address.dyn={0} διεύθυνση: payment.accountNr=Αριθμός λογαριασμού: payment.emailOrMobile=Email ή αριθμός κινητού: payment.useCustomAccountName=Χρήση προεπιλεγμένου ονόματος λογαριασμού -payment.maxPeriod=Μέγιστη επιτρεπόμενη χρονική περίοδος συναλλαγής / ημερομηνία: +payment.maxPeriod=Μέγιστη επιτρεπόμενη χρονική περίοδος συναλλαγής: payment.maxPeriodAndLimit=Μέγιστη διάρκεια συναλλαγής: {0} / Μέγιστο όριο συναλλαγής: {1} payment.currencyWithSymbol=Νόμισμα: {0} payment.nameOfAcceptedBank=Όνομα αποδεκτής τράπεζας diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index aefe7f02103..94e389a7640 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -1377,7 +1377,7 @@ payment.altcoin.address.dyn=Su dirección {0}: payment.accountNr=Número de cuenta: payment.emailOrMobile=Email o número de móvil: payment.useCustomAccountName=Utilizar nombre de cuenta personalizado -payment.maxPeriod=Periodo / fecha máximo de intercambio permitido: +payment.maxPeriod=Periodo máximo de intercambio permitido: payment.maxPeriodAndLimit=Duración máxima de intercambio: {0} / Límite máximo de intercambio: {1} payment.currencyWithSymbol=Moneda: {0} payment.nameOfAcceptedBank=Nombre de banco aceptado diff --git a/common/src/main/resources/i18n/displayStrings_hu.properties b/common/src/main/resources/i18n/displayStrings_hu.properties index 986436439e2..d545668209a 100644 --- a/common/src/main/resources/i18n/displayStrings_hu.properties +++ b/common/src/main/resources/i18n/displayStrings_hu.properties @@ -1377,7 +1377,7 @@ payment.altcoin.address.dyn={0} cím: payment.accountNr=Fiókszám: payment.emailOrMobile=E-mail vagy mobil: payment.useCustomAccountName=Használj egyéni fióknevet -payment.maxPeriod=Max. megengedett tranzakció időszak / dátum: +payment.maxPeriod=Max. megengedett tranzakció időszak: payment.maxPeriodAndLimit=Max. tranzakció időtartama: {0} / Max. tranzakció korlátozás: {1} payment.currencyWithSymbol=Valuta: {0} payment.nameOfAcceptedBank=Elfogadott bank neve diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index abb50edd7ca..24ea55fe99f 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -1377,7 +1377,7 @@ payment.altcoin.address.dyn=Endereço {0}: payment.accountNr=Número da conta: payment.emailOrMobile=Email ou celular: payment.useCustomAccountName=Usar número de conta personalizado: -payment.maxPeriod=Período máximo de negociação / data: +payment.maxPeriod=Período máximo de negociação: payment.maxPeriodAndLimit=Duração máxima de negociação: {0} / Limite de negociação: {1} payment.currencyWithSymbol=Moeda: {0} payment.nameOfAcceptedBank=Nome do banco aceito diff --git a/common/src/main/resources/i18n/displayStrings_ro.properties b/common/src/main/resources/i18n/displayStrings_ro.properties index 71556cc63b2..26f53d6f574 100644 --- a/common/src/main/resources/i18n/displayStrings_ro.properties +++ b/common/src/main/resources/i18n/displayStrings_ro.properties @@ -1377,7 +1377,7 @@ payment.altcoin.address.dyn=Adresa {0}: payment.accountNr=Număr cont: payment.emailOrMobile=E-mail sau nr. mobil: payment.useCustomAccountName=Folosește nume de cont preferințial -payment.maxPeriod=Data / perioada maximă de tranzacționare permisă: +payment.maxPeriod=Perioada maximă de tranzacționare permisă: payment.maxPeriodAndLimit=Durata maximă de tranzacționare: {0} / Limita maximă de tranzacționare: {1} payment.currencyWithSymbol=Valuta: {0} payment.nameOfAcceptedBank=Numele băncii acceptate diff --git a/common/src/main/resources/i18n/displayStrings_ru.properties b/common/src/main/resources/i18n/displayStrings_ru.properties index 4660bf8f07b..9240fe20f07 100644 --- a/common/src/main/resources/i18n/displayStrings_ru.properties +++ b/common/src/main/resources/i18n/displayStrings_ru.properties @@ -1377,7 +1377,7 @@ payment.altcoin.address.dyn={0} адрес: payment.accountNr=Номер счёта: payment.emailOrMobile=Электронная почта либо мобильный номер: payment.useCustomAccountName=Использовать собственное название счёта -payment.maxPeriod=Макс. период отведенный на сделку / время: +payment.maxPeriod=Макс. период отведенный на сделку: payment.maxPeriodAndLimit=Макс. продолжительность сделки: {0} / Макс. торговый предел: {1} payment.currencyWithSymbol=Валюта: {0} payment.nameOfAcceptedBank=Название подтверждённого банка diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index 4a1132f5ddf..ccb499ee5b1 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -1377,7 +1377,7 @@ payment.altcoin.address.dyn={0} adresa: payment.accountNr=Broj računa: payment.emailOrMobile=Email ili br. mobilnog: payment.useCustomAccountName=Koristi prilagođeno ime računa -payment.maxPeriod=Maks. dozvoljeni period trgovine / datum: +payment.maxPeriod=Maks. dozvoljeni period trgovine: payment.maxPeriodAndLimit=Maks. trajanje trgovine: {0} / Maks. rok trgovine: {1} payment.currencyWithSymbol=Valuta: {0} payment.nameOfAcceptedBank=Ime prihvaćene banke diff --git a/common/src/main/resources/i18n/displayStrings_zh.properties b/common/src/main/resources/i18n/displayStrings_zh.properties index dd71a94fe0a..9d45d711ee3 100644 --- a/common/src/main/resources/i18n/displayStrings_zh.properties +++ b/common/src/main/resources/i18n/displayStrings_zh.properties @@ -1377,7 +1377,7 @@ payment.altcoin.address.dyn={0} 地址: payment.accountNr=账号: payment.emailOrMobile=电子邮箱或手机号码: payment.useCustomAccountName=使用自定义名称 -payment.maxPeriod=最大允许时限 / 日期: +payment.maxPeriod=最大允许时限: payment.maxPeriodAndLimit=最大交易期限:{0} /最大交易限额:{1} payment.currencyWithSymbol=货币:{0} payment.nameOfAcceptedBank=接受的银行名称 diff --git a/gui/src/main/java/io/bisq/gui/components/paymentmethods/PaymentMethodForm.java b/gui/src/main/java/io/bisq/gui/components/paymentmethods/PaymentMethodForm.java index af29c0db514..56282fc85e9 100644 --- a/gui/src/main/java/io/bisq/gui/components/paymentmethods/PaymentMethodForm.java +++ b/gui/src/main/java/io/bisq/gui/components/paymentmethods/PaymentMethodForm.java @@ -113,11 +113,10 @@ protected void addAccountNameTextFieldWithAutoFillCheckBox() { public static void addOpenTradeDuration(GridPane gridPane, int gridRow, - Offer offer, - String dateFromBlocks) { + Offer offer) { long hours = offer.getMaxTradePeriod() / 3600_000; addLabelTextField(gridPane, gridRow, Res.get("payment.maxPeriod"), - getTimeText(hours) + " / " + dateFromBlocks); + getTimeText(hours)); } protected static String getTimeText(long hours) { diff --git a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java index d0e1e065bde..1f783c46aa9 100644 --- a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -176,8 +176,7 @@ protected void addTradeInfoBlock() { txIdTextField.cleanup(); if (model.dataModel.getTrade() != null) - PaymentMethodForm.addOpenTradeDuration(gridPane, ++gridRow, model.dataModel.getTrade().getOffer(), - model.getDateForOpenDispute()); + PaymentMethodForm.addOpenTradeDuration(gridPane, ++gridRow, model.dataModel.getTrade().getOffer()); timeLeftTextField = addLabelTextField(gridPane, ++gridRow, Res.getWithCol("portfolio.pending.remainingTime")).second; From bd4692f5201f431777c76bfb2b6789a04a3f4143 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 2 Feb 2018 15:31:47 +0100 Subject: [PATCH 08/62] Fix wrong expectation of formatter method and add new translations for handling trade limits --- common/src/main/resources/i18n/displayStrings.properties | 2 ++ .../src/main/resources/i18n/displayStrings_de.properties | 2 ++ .../main/portfolio/pendingtrades/steps/TradeStepView.java | 8 +++++--- gui/src/main/java/io/bisq/gui/util/BSFormatter.java | 2 +- gui/src/test/java/io/bisq/gui/util/BSFormatterTest.java | 4 +++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index f1adc48e01e..acb92d0e136 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -589,6 +589,8 @@ portfolio.pending.step5_seller.received=You have received: portfolio.pending.role=My role portfolio.pending.tradeInformation=Trade information portfolio.pending.remainingTime=Remaining time +portfolio.pending.remainingTimeDetail={0} (until {1}) +portfolio.pending.tradeNotCompleted=Trade not completed in time (until {0}) portfolio.pending.tradeProcess=Trade process portfolio.pending.openAgainDispute.msg=If you are not sure that the message to the arbitrator arrived (e.g. if you did not got a response after 1 day) feel free to open a dispute again. portfolio.pending.openAgainDispute.button=Open dispute again diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 98fa198a614..b4e70c3b804 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -543,6 +543,8 @@ portfolio.pending.step5_seller.received=Sie haben erhalten: portfolio.pending.role=Meine Rolle portfolio.pending.tradeInformation=Handelsinformationen portfolio.pending.remainingTime=Verbleibende Zeit +portfolio.pending.remainingTimeDetail={0} (bis {1}) +portfolio.pending.tradeNotCompleted=Maximale Handelsdauer wurde überschritten (bis {0}) portfolio.pending.tradeProcess=Handelsprozess portfolio.pending.openAgainDispute.msg=Falls Sie nicht sicher sind, ob Ihre Nachricht den Vermittler erreicht hat (z.B. wenn Sie nach einem Tag noch keine Rückmeldung erhalten haben) können Sie gerne erneut einen Konflikt öffnen. portfolio.pending.openAgainDispute.button=Konflikt erneut öffnen diff --git a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java index 1f783c46aa9..3b3c4fc911d 100644 --- a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -211,14 +211,16 @@ private void updateTimeLeft() { if (timeLeftTextField != null) { String remainingTime = model.getRemainingTradeDurationAsWords(); timeLeftProgressBar.setProgress(model.getRemainingTradeDurationAsPercentage()); - if (remainingTime != null) { - timeLeftTextField.setText(remainingTime); + if (!remainingTime.isEmpty()) { + timeLeftTextField.setText(Res.get("portfolio.pending.remainingTimeDetail", + remainingTime, model.getDateForOpenDispute())); if (model.showWarning() || model.showDispute()) { timeLeftTextField.getStyleClass().add("error-text"); timeLeftProgressBar.getStyleClass().add("error"); } } else { - timeLeftTextField.setText("Trade not completed in time (" + model.getDateForOpenDispute() + ")"); + timeLeftTextField.setText(Res.get("portfolio.pending.tradeNotCompleted", + model.getDateForOpenDispute())); timeLeftTextField.getStyleClass().add("error-text"); timeLeftProgressBar.getStyleClass().add("error"); } diff --git a/gui/src/main/java/io/bisq/gui/util/BSFormatter.java b/gui/src/main/java/io/bisq/gui/util/BSFormatter.java index 8d19ca35879..2922ad40cfc 100644 --- a/gui/src/main/java/io/bisq/gui/util/BSFormatter.java +++ b/gui/src/main/java/io/bisq/gui/util/BSFormatter.java @@ -512,7 +512,7 @@ public static String formatDurationAsWords(long durationMillis, boolean showSeco } else format += "H\' " + hours + ", \'m\' " + minutes + "\'"; - String duration = durationMillis > 0 ? DurationFormatUtils.formatDuration(durationMillis, format) : Res.get("formatter.tradePeriodOver"); + String duration = durationMillis > 0 ? DurationFormatUtils.formatDuration(durationMillis, format) : ""; duration = StringUtils.replaceOnce(duration, "1 " + seconds, "1 " + second); duration = StringUtils.replaceOnce(duration, "1 " + minutes, "1 " + minute); diff --git a/gui/src/test/java/io/bisq/gui/util/BSFormatterTest.java b/gui/src/test/java/io/bisq/gui/util/BSFormatterTest.java index c29a9401ce8..42471714dc8 100644 --- a/gui/src/test/java/io/bisq/gui/util/BSFormatterTest.java +++ b/gui/src/test/java/io/bisq/gui/util/BSFormatterTest.java @@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class BSFormatterTest { @@ -63,6 +64,7 @@ public void testFormatDurationAsWords() { assertEquals("1 hour, 0 minutes, 0 seconds", formatter.formatDurationAsWords(oneHour, true)); assertEquals("1 hour, 0 minutes, 1 second", formatter.formatDurationAsWords(oneHour + oneSecond, true)); assertEquals("1 hour, 0 minutes, 2 seconds", formatter.formatDurationAsWords(oneHour + oneSecond * 2, true)); - assertEquals("Trade period is over", formatter.formatDurationAsWords(0)); + assertEquals("", formatter.formatDurationAsWords(0)); + assertTrue(formatter.formatDurationAsWords(0).isEmpty()); } } From 422f5493deff42b51e60af37ec2b560ef74e7f1a Mon Sep 17 00:00:00 2001 From: stevenkain Date: Sat, 3 Feb 2018 00:37:14 +0200 Subject: [PATCH 09/62] Update displayStrings.properties 1. blocked by your internet provider or in your country - by your country 2. sent you -> send you 3. capitalized Bisq 4. I would love me some of that Bitcoin Clashic :) --- common/src/main/resources/i18n/displayStrings.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 99ce4e5a7f1..7741b2750c2 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -533,7 +533,7 @@ The trade ID (\"reason for payment\" text) of the transaction is: \"{2}\" portfolio.pending.step3_seller.cash=\n\nBecause the payment is done via Cash Deposit the BTC buyer has to write \"NO REFUND\" on the paper receipt, tear it in 2 parts and send you a photo by email.\n\n\ To avoid chargeback risk, only confirm if you received the email and if you are sure the paper receipt is valid.\n\ If you are not sure, {0} -portfolio.pending.step3_seller.westernUnion=The buyer has to sent you the MTCN (tracking number) and a photo of the receipt by email.\n\ +portfolio.pending.step3_seller.westernUnion=The buyer has to send you the MTCN (tracking number) and a photo of the receipt by email.\n\ The receipt must clearly show your full name, city, country and the amount. Please check your email if you received the MTCN.\n\n\ After closing that popup you will see the BTC buyer's name and address for picking up the money from Western Union.\n\n\ Only confirm receipt after you have successfully picked up the money! @@ -923,7 +923,7 @@ account.altcoin.popup.ZEC.msg=When using {0} you can only use the transparent ad the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. account.altcoin.popup.XZC.msg=When using {0} you can only use the transparent (traceable) addresses not \ the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. -account.altcoin.popup.bch=Bitcoin Cash and Bitcoin Clashic suffer from replay protection. If you use those coins be sure you take sufficient precautions and understand all implications.\ +account.altcoin.popup.bch=Bitcoin Cash and Bitcoin Classic suffer from replay protection. If you use those coins be sure you take sufficient precautions and understand all implications.\ You can suffer losses by sending one coin and unintentionally send the same coins on the other block chain.\ Because those "airdrop coins" share the same history with the Bitcoin blockchain there are also security risks and a considerable risk for losing privacy.\n\n\ Please read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc @@ -963,7 +963,7 @@ account.seed.warn.noPw.msg=You have not setup a wallet password which would prot Do you want to display the seed words? account.seed.warn.noPw.yes=Yes, and don't ask me again account.seed.enterPw=Enter password to view seed words -account.seed.restore.info=Please note that you cannot import a wallet from an old bisq version (any version before 0.5.0), \ +account.seed.restore.info=Please note that you cannot import a wallet from an old Bisq version (any version before 0.5.0), \ because the wallet format has changed!\n\n\ If you want to move the funds from the old version to the new Bisq application send it with a Bitcoin transaction.\n\n\ Also be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\n\ @@ -1290,7 +1290,7 @@ torNetworkSettingWindow.deleteFiles.button=Delete outdated Tor files and shut do torNetworkSettingWindow.deleteFiles.progress=Shut down Tor in progress torNetworkSettingWindow.deleteFiles.success=Outdated Tor files deleted successfully. Please restart. torNetworkSettingWindow.bridges.header=Is Tor blocked? -torNetworkSettingWindow.bridges.info=If Tor is blocked by your internet provider or in your country you can try to use Tor bridges.\n\ +torNetworkSettingWindow.bridges.info=If Tor is blocked by your internet provider or by your country you can try to use Tor bridges.\n\ Visit the Tor web page at: https://bridges.torproject.org/bridges to learn more about \ bridges and pluggable transports. From b1aa86a57c7701eca5db2181d7281607d03bf6f7 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 5 Feb 2018 10:13:09 +0100 Subject: [PATCH 10/62] Add account age translation --- common/src/main/resources/i18n/displayStrings_de.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index b4e70c3b804..e3d8d0974f6 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -1417,7 +1417,7 @@ payment.accountNr=Kontonummer: payment.emailOrMobile=E-Mail oder Mobil: payment.useCustomAccountName=Spezifischen Kontonamen nutzen payment.maxPeriod=Max. erlaubte Handelsdauer: -payment.maxPeriodAndLimit=Max. Handelsdauer: {0} / Max. Handelsgrenze: {1} +payment.maxPeriodAndLimit=Max. Handelsdauer: {0} / Max. Handelsgrenze: {1} / Alter des Kontos: {2} payment.currencyWithSymbol=Währung: {0} payment.nameOfAcceptedBank=Name der akzeptierten Bank payment.addAcceptedBank=Akzeptierte Bank hinzufügen From a4359e955754575d6491a2527b990266ba1f6339 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 5 Feb 2018 11:07:53 +0100 Subject: [PATCH 11/62] Improve English translation and add German ones --- common/src/main/resources/i18n/displayStrings.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_de.properties | 2 ++ .../java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 99ce4e5a7f1..d915b243dba 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -353,7 +353,7 @@ createOffer.fundsBox.offerFee=Trade fee: createOffer.fundsBox.networkFee=Mining fee: createOffer.fundsBox.placeOfferSpinnerInfo=Offer publishing is in progress ... createOffer.fundsBox.paymentLabel=Bisq trade with ID {0} -createOffer.fundsBox.fundsStructure=({0} deposit, {1} trade fee, {2} mining fee) +createOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee, {2} mining fee) createOffer.success.headline=Your offer has been published createOffer.success.info=You can manage your open offers at \"Portfolio/My open offers\". @@ -411,7 +411,7 @@ takeOffer.fundsBox.offerFee=Trade fee: takeOffer.fundsBox.networkFee=Total mining fees: takeOffer.fundsBox.takeOfferSpinnerInfo=Take offer in progress ... takeOffer.fundsBox.paymentLabel=Bisq trade with ID {0} -takeOffer.fundsBox.fundsStructure=({0} deposit, {1} trade fee, {2} mining fee) +takeOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee, {2} mining fee) takeOffer.success.headline=You have successfully taken an offer. takeOffer.success.info=You can see the status of your trade at \"Portfolio/Open trades\". takeOffer.error.message=An error occurred when taking the offer.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 270fcdeea71..d96eacf3e0c 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -341,6 +341,7 @@ createOffer.fundsBox.offerFee=Erstellergebühr: createOffer.fundsBox.networkFee=Mining-Gebühr: createOffer.fundsBox.placeOfferSpinnerInfo=Das Angebot wird veröffentlicht ... createOffer.fundsBox.paymentLabel=Bisq-Handel mit der ID {0} +createOffer.fundsBox.fundsStructure=({0} Kaution, {1} Erstellergebühr, {2} Mining-Gebühr) createOffer.success.headline=Ihr Angebot wurde veröffentlicht createOffer.success.info=Sie können Ihre offenen Angebote unter \"Portfolio/Meine offenen Angebote\" verwalten. @@ -393,6 +394,7 @@ takeOffer.fundsBox.offerFee=Abnehmergebühr: takeOffer.fundsBox.networkFee=Mining-Gebühren (3x): takeOffer.fundsBox.takeOfferSpinnerInfo=Angebot wird angenommen ... takeOffer.fundsBox.paymentLabel=Bisq-Handel mit der ID {0} +takeOffer.fundsBox.fundsStructure=({0} Kaution, {1} Abnehmergebühr, {2} Mining-Gebühr) takeOffer.success.headline=Sie haben erfolgreich ein Angebot angenommen. takeOffer.success.info=Sie können den Status Ihres Angebots unter \"Portfolio/Offene Angebote\" einsehen. takeOffer.error.message=Bei der Angebotsannahme trat ein Fehler auf.\n\n{0} diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java index f34ca2904d6..66fcc9c08f1 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java @@ -420,7 +420,7 @@ private void onShowPayFundsScreen() { balanceLabel.setVisible(true); balanceTextField.setVisible(true); - totalToPayTextField.setFundsStructure(Res.get("createOffer.fundsBox.fundsStructure", + totalToPayTextField.setFundsStructure(Res.get("takeOffer.fundsBox.fundsStructure", model.getSecurityDepositWithCode(), model.getMakerFeePercentage(), model.getTxFeePercentage())); totalToPayTextField.setContentForInfoPopOver(createInfoPopover()); From 17b33931efa46ad5fe3ac7b0af6a1992fd49c818 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 5 Feb 2018 11:08:38 +0100 Subject: [PATCH 12/62] Prevent flickering on roll over --- .../io/bisq/gui/components/FundsTextField.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/components/FundsTextField.java b/gui/src/main/java/io/bisq/gui/components/FundsTextField.java index adf960120ab..ae81f782b42 100644 --- a/gui/src/main/java/io/bisq/gui/components/FundsTextField.java +++ b/gui/src/main/java/io/bisq/gui/components/FundsTextField.java @@ -19,6 +19,7 @@ import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; +import io.bisq.common.UserThread; import io.bisq.common.locale.Res; import io.bisq.common.util.Utilities; import javafx.beans.binding.Bindings; @@ -33,12 +34,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.TimeUnit; + public class FundsTextField extends AnchorPane { public static final Logger log = LoggerFactory.getLogger(FundsTextField.class); private final StringProperty amount = new SimpleStringProperty(); private final StringProperty fundsStructure = new SimpleStringProperty(); private final Label infoIcon; + private Boolean hidePopover; private PopOver infoPopover; /////////////////////////////////////////////////////////////////////////////////////////// @@ -94,10 +98,19 @@ public FundsTextField() { public void setContentForInfoPopOver(Node node) { // As we don't use binding here we need to recreate it on mouse over to reflect the current state - infoIcon.setOnMouseEntered(e -> showInfoPopOver(node)); + infoIcon.setOnMouseEntered(e -> { + hidePopover = false; + showInfoPopOver(node); + }); infoIcon.setOnMouseExited(e -> { if (infoPopover != null) - infoPopover.hide(); + hidePopover = true; + UserThread.runAfter(() -> { + if (hidePopover) { + infoPopover.hide(); + hidePopover = false; + } + },250, TimeUnit.MILLISECONDS); }); } From 41fd919b59702de30c516951103c58f0ec4a2c98 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 5 Feb 2018 11:10:48 +0100 Subject: [PATCH 13/62] Use brighter green to improve usability --- gui/src/main/java/io/bisq/gui/bisq.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/main/java/io/bisq/gui/bisq.css b/gui/src/main/java/io/bisq/gui/bisq.css index a1021f97356..b7a77e231d0 100644 --- a/gui/src/main/java/io/bisq/gui/bisq.css +++ b/gui/src/main/java/io/bisq/gui/bisq.css @@ -127,7 +127,7 @@ bg color of non edit textFields: fafafa } .info { - -fx-text-fill: -bs-bg-green2; + -fx-text-fill: -bs-green; } .info:hover { From 4b0692c6e03d2bd6016c48dbbb1db9cb14d30d6e Mon Sep 17 00:00:00 2001 From: Bernard Labno Date: Thu, 1 Feb 2018 13:58:36 +0100 Subject: [PATCH 14/62] Use environment locale to select right locale and currency --- common/src/main/java/io/bisq/common/GlobalSettings.java | 2 +- gui/src/main/java/io/bisq/gui/app/BisqAppMain.java | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/common/src/main/java/io/bisq/common/GlobalSettings.java b/common/src/main/java/io/bisq/common/GlobalSettings.java index 82b323b8771..4847b8d7438 100644 --- a/common/src/main/java/io/bisq/common/GlobalSettings.java +++ b/common/src/main/java/io/bisq/common/GlobalSettings.java @@ -26,7 +26,7 @@ public class GlobalSettings { private static boolean useAnimations = true; - private static Locale locale = new Locale("en", "US"); + private static Locale locale = Locale.getDefault(); private static final ObjectProperty localeProperty = new SimpleObjectProperty<>(locale); private static TradeCurrency defaultTradeCurrency; private static String btcDenomination; diff --git a/gui/src/main/java/io/bisq/gui/app/BisqAppMain.java b/gui/src/main/java/io/bisq/gui/app/BisqAppMain.java index c2e2d9f1162..bcfe401768b 100644 --- a/gui/src/main/java/io/bisq/gui/app/BisqAppMain.java +++ b/gui/src/main/java/io/bisq/gui/app/BisqAppMain.java @@ -25,17 +25,12 @@ import joptsimple.OptionParser; import joptsimple.OptionSet; -import java.util.Locale; - import static io.bisq.core.app.BisqEnvironment.DEFAULT_APP_NAME; import static io.bisq.core.app.BisqEnvironment.DEFAULT_USER_DATA_DIR; public class BisqAppMain extends BisqExecutable { static { - // Need to set default locale initially otherwise we get problems at non-english OS - Locale.setDefault(new Locale("en", Locale.getDefault().getCountry())); - Utilities.removeCryptographyRestrictions(); } From f0836381dbca8e75a748e864fbd411a9f481bf92 Mon Sep 17 00:00:00 2001 From: Manbearpixel Date: Mon, 5 Feb 2018 10:22:17 -0600 Subject: [PATCH 15/62] Added ODN Address Validation --- .../statistics/TradeStatisticsManager.java | 2 + .../validation/AltCoinAddressValidator.java | 10 ++- .../gui/util/validation/params/ODNParams.java | 88 +++++++++++++++++++ .../AltCoinAddressValidatorTest.java | 16 ++++ 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index 2840254245a..f2214df3c0e 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -252,6 +252,8 @@ private void printAllCurrencyStats() { newlyAdded.add("MVT"); newlyAdded.add("REF"); + newlyAdded.add("ODN"); + coinsWithValidator.addAll(newlyAdded); diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index 41d0b1266c6..1273a734d18 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -433,9 +433,15 @@ public ValidationResult validate(String input) { return regexTestFailed; else return new ValidationResult(true); + case "ODN": + try { + Address.fromBase58(ODNParams.get(), input); + return new ValidationResult(true); + } catch (AddressFormatException e) { + return new ValidationResult(false, getErrorMessage(e)); + } - - // Add new coins at the end... + // Add new coins at the end... default: log.debug("Validation for AltCoinAddress not implemented yet. currencyCode: " + currencyCode); return validationResult; diff --git a/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java b/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java new file mode 100644 index 00000000000..644a54461a4 --- /dev/null +++ b/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java @@ -0,0 +1,88 @@ +/* + * 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 io.bisq.gui.util.validation.params; + +import org.bitcoinj.core.*; +import org.bitcoinj.store.BlockStore; +import org.bitcoinj.store.BlockStoreException; +import org.bitcoinj.utils.MonetaryFormat; + +public class ODNParams extends NetworkParameters { + + private static ODNParams instance; + + public static synchronized ODNParams get() { + if (instance == null) { + instance = new ODNParams(); + } + return instance; + } + + // We only use the properties needed for address validation + public ODNParams() { + super(); + addressHeader = 75; // networkVersion (0x4b) + p2shHeader = -1; // TODO ??privateKeyPrefix 203 (0xcb) tbd? + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + + // default dummy implementations, not used... + @Override + public String getPaymentProtocolId() { + return PAYMENT_PROTOCOL_ID_MAINNET; + } + + @Override + public void checkDifficultyTransitions(StoredBlock storedPrev, Block next, BlockStore blockStore) throws VerificationException, BlockStoreException { + } + + @Override + public Coin getMaxMoney() { + return null; + } + + @Override + public Coin getMinNonDustOutput() { + return null; + } + + @Override + public MonetaryFormat getMonetaryFormat() { + return null; + } + + @Override + public String getUriScheme() { + return null; + } + + @Override + public boolean hasMaxMoney() { + return false; + } + + @Override + public BitcoinSerializer getSerializer(boolean parseRetain) { + return null; + } + + @Override + public int getProtocolVersionNum(ProtocolVersion version) { + return 0; + } +} diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index 1803217686a..2afe3c4c3f9 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -429,6 +429,22 @@ public void testCAGE() { assertFalse(validator.validate("").isValid); } + @Test + public void testODN() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("ODN"); + + assertTrue(validator.validate("XEfyuzk8yTp5eA9eVUeCW2PFbCFtNb6Jgv").isValid); + assertTrue(validator.validate("XJegzjV2GK9CeQLNNcc5GVSSqTkv1XMxSF").isValid); + assertTrue(validator.validate("XLfvvLuwjUrz2kf5gghEmUPFE3vFvwfEiL").isValid); + assertTrue(validator.validate("XNC1e9TfUApfBsH9JCubioS5XGuwFLbsP4").isValid); + + assertFalse(validator.validate("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq").isValid); + assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266").isValid); + assertFalse(validator.validate("SSnwqFBiyqK1n4BV7kPX86iesev2NobhEo").isValid); + assertFalse(validator.validate("").isValid); + } + @Test public void testCRED() { AltCoinAddressValidator validator = new AltCoinAddressValidator(); From ba1a8c398251d8dc7ffa057a538383ed97e0bda5 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 5 Feb 2018 19:16:56 +0100 Subject: [PATCH 16/62] Add InfoTextField for displaying additional information in a TextField --- .../io/bisq/gui/components/InfoTextField.java | 103 ++++++++++++++++++ .../paymentmethods/PaymentMethodForm.java | 11 +- .../pendingtrades/steps/TradeStepView.java | 7 +- .../java/io/bisq/gui/util/FormBuilder.java | 22 ++++ 4 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 gui/src/main/java/io/bisq/gui/components/InfoTextField.java diff --git a/gui/src/main/java/io/bisq/gui/components/InfoTextField.java b/gui/src/main/java/io/bisq/gui/components/InfoTextField.java new file mode 100644 index 00000000000..30ef51505f0 --- /dev/null +++ b/gui/src/main/java/io/bisq/gui/components/InfoTextField.java @@ -0,0 +1,103 @@ +package io.bisq.gui.components; + +import de.jensd.fx.fontawesome.AwesomeDude; +import de.jensd.fx.fontawesome.AwesomeIcon; +import io.bisq.common.UserThread; +import io.bisq.common.locale.Res; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.layout.AnchorPane; +import org.controlsfx.control.PopOver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.TimeUnit; + +public class InfoTextField extends AnchorPane { + public static final Logger log = LoggerFactory.getLogger(InfoTextField.class); + + private final StringProperty text = new SimpleStringProperty(); + private final Label infoIcon; + private Boolean hidePopover; + private PopOver infoPopover; + + public InfoTextField() { + TextField textField = new TextField(); + // might be removed if no styling is necessary + textField.setEditable(false); + textField.setPromptText(Res.get("createOffer.fundsBox.totalsNeeded.prompt")); + textField.textProperty().bind(text); + textField.setFocusTraversable(false); + + infoIcon = new Label(); + infoIcon.setLayoutY(3); + infoIcon.getStyleClass().addAll("highlight", "show-hand"); + AwesomeDude.setIcon(infoIcon, AwesomeIcon.INFO_SIGN); + + AnchorPane.setRightAnchor(infoIcon, 7.0); + AnchorPane.setRightAnchor(textField, 0.0); + AnchorPane.setLeftAnchor(textField, 0.0); + + getChildren().addAll(textField, infoIcon); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public + /////////////////////////////////////////////////////////////////////////////////////////// + + public void setContentForInfoPopOver(Node node) { + // As we don't use binding here we need to recreate it on mouse over to reflect the current state + infoIcon.setOnMouseEntered(e -> { + hidePopover = false; + showInfoPopOver(node); + }); + infoIcon.setOnMouseExited(e -> { + if (infoPopover != null) + infoPopover.hide(); + hidePopover = true; + UserThread.runAfter(() -> { + if (hidePopover) { + infoPopover.hide(); + hidePopover = false; + } + }, 250, TimeUnit.MILLISECONDS); + }); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void showInfoPopOver(Node node) { + node.getStyleClass().add("default-text"); + + infoPopover = new PopOver(node); + if (infoIcon.getScene() != null) { + infoPopover.setDetachable(false); + infoPopover.setArrowLocation(PopOver.ArrowLocation.RIGHT_TOP); + infoPopover.setArrowIndent(5); + + infoPopover.show(infoIcon, -17); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Getters/Setters + /////////////////////////////////////////////////////////////////////////////////////////// + + public void setText(String text) { + this.text.set(text); + } + + public String getText() { + return text.get(); + } + + public StringProperty amountProperty() { + return text; + } +} diff --git a/gui/src/main/java/io/bisq/gui/components/paymentmethods/PaymentMethodForm.java b/gui/src/main/java/io/bisq/gui/components/paymentmethods/PaymentMethodForm.java index 56282fc85e9..09f22c44ee2 100644 --- a/gui/src/main/java/io/bisq/gui/components/paymentmethods/PaymentMethodForm.java +++ b/gui/src/main/java/io/bisq/gui/components/paymentmethods/PaymentMethodForm.java @@ -26,6 +26,7 @@ import io.bisq.core.payment.AccountAgeWitnessService; import io.bisq.core.payment.CryptoCurrencyAccount; import io.bisq.core.payment.PaymentAccount; +import io.bisq.gui.components.InfoTextField; import io.bisq.gui.components.InputTextField; import io.bisq.gui.main.overlays.popups.Popup; import io.bisq.gui.util.BSFormatter; @@ -111,12 +112,12 @@ protected void addAccountNameTextFieldWithAutoFillCheckBox() { }); } - public static void addOpenTradeDuration(GridPane gridPane, - int gridRow, - Offer offer) { + public static InfoTextField addOpenTradeDuration(GridPane gridPane, + int gridRow, + Offer offer) { long hours = offer.getMaxTradePeriod() / 3600_000; - addLabelTextField(gridPane, gridRow, Res.get("payment.maxPeriod"), - getTimeText(hours)); + return addLabelInfoTextfield(gridPane, gridRow, Res.get("payment.maxPeriod"), + getTimeText(hours)).second; } protected static String getTimeText(long hours) { diff --git a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java index 3b3c4fc911d..a6550ae5e92 100644 --- a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -23,6 +23,7 @@ import io.bisq.core.arbitration.Dispute; import io.bisq.core.trade.Trade; import io.bisq.core.user.Preferences; +import io.bisq.gui.components.InfoTextField; import io.bisq.gui.components.TitledGroupBg; import io.bisq.gui.components.TxIdTextField; import io.bisq.gui.components.paymentmethods.PaymentMethodForm; @@ -43,6 +44,7 @@ import java.util.Optional; import static com.google.common.base.Preconditions.checkNotNull; +import static io.bisq.gui.components.paymentmethods.PaymentMethodForm.addOpenTradeDuration; import static io.bisq.gui.util.FormBuilder.*; public abstract class TradeStepView extends AnchorPane { @@ -175,8 +177,9 @@ protected void addTradeInfoBlock() { else txIdTextField.cleanup(); - if (model.dataModel.getTrade() != null) - PaymentMethodForm.addOpenTradeDuration(gridPane, ++gridRow, model.dataModel.getTrade().getOffer()); + if (model.dataModel.getTrade() != null) { + InfoTextField infoTextField = addOpenTradeDuration(gridPane, ++gridRow, model.dataModel.getTrade().getOffer()); + } timeLeftTextField = addLabelTextField(gridPane, ++gridRow, Res.getWithCol("portfolio.pending.remainingTime")).second; diff --git a/gui/src/main/java/io/bisq/gui/util/FormBuilder.java b/gui/src/main/java/io/bisq/gui/util/FormBuilder.java index 5dd2e6a00f0..0196d2eeb91 100644 --- a/gui/src/main/java/io/bisq/gui/util/FormBuilder.java +++ b/gui/src/main/java/io/bisq/gui/util/FormBuilder.java @@ -802,6 +802,28 @@ public static Tuple2 addLabelFundsTextfield(GridPane grid return new Tuple2<>(label, fundsTextField); } + /////////////////////////////////////////////////////////////////////////////////////////// + // Label + InfoTextField + /////////////////////////////////////////////////////////////////////////////////////////// + public static Tuple2 addLabelInfoTextfield(GridPane gridPane, int rowIndex, String labelText, + String fieldText) { + return addLabelInfoTextfield(gridPane, rowIndex, labelText, fieldText, 0); + } + + public static Tuple2 addLabelInfoTextfield(GridPane gridPane, int rowIndex, String labelText, + String fieldText, double top) { + Label label = addLabel(gridPane, rowIndex, labelText, top); + + InfoTextField infoTextField = new InfoTextField(); + infoTextField.setText(fieldText); + GridPane.setRowIndex(infoTextField, rowIndex); + GridPane.setColumnIndex(infoTextField, 1); + GridPane.setMargin(infoTextField, new Insets(top, 0,0,0)); + gridPane.getChildren().add(infoTextField); + + return new Tuple2<>(label, infoTextField); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Label + BsqAddressTextField /////////////////////////////////////////////////////////////////////////////////////////// From d03dd58144652a57e173350276f36b4ac85c23ec Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Tue, 6 Feb 2018 11:18:57 +0100 Subject: [PATCH 17/62] Add info pop over for trade period --- .../resources/i18n/displayStrings.properties | 2 + .../i18n/displayStrings_de.properties | 2 + gui/src/main/java/io/bisq/gui/bisq.css | 18 ++++++++ .../io/bisq/gui/components/InfoTextField.java | 2 +- .../pendingtrades/steps/TradeStepView.java | 43 +++++++++++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index acb92d0e136..9d9e42fb6c9 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -590,6 +590,8 @@ portfolio.pending.role=My role portfolio.pending.tradeInformation=Trade information portfolio.pending.remainingTime=Remaining time portfolio.pending.remainingTimeDetail={0} (until {1}) +portfolio.pending.tradePeriodInfo=After the first blockchain confirmation, the trade period starts. Based on the payment method used, a different maximum allowed trade period is applied. +portfolio.pending.tradePeriodWarning=If the period is exceeded the trade will go to arbitration automatically. portfolio.pending.tradeNotCompleted=Trade not completed in time (until {0}) portfolio.pending.tradeProcess=Trade process portfolio.pending.openAgainDispute.msg=If you are not sure that the message to the arbitrator arrived (e.g. if you did not got a response after 1 day) feel free to open a dispute again. diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index e3d8d0974f6..ebe68206722 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -544,6 +544,8 @@ portfolio.pending.role=Meine Rolle portfolio.pending.tradeInformation=Handelsinformationen portfolio.pending.remainingTime=Verbleibende Zeit portfolio.pending.remainingTimeDetail={0} (bis {1}) +portfolio.pending.tradePeriodInfo=Die Handelsdauer beginnt mit der ersten Blockchain-Bestätigung. Abhängig von der Zahlungart, wird eine maximale Handesldauer gesetzt. +portfolio.pending.tradePeriodWarning=Beim Überschreiten der Handelsdauer wird automatisch ein Konflikt geöffnet. portfolio.pending.tradeNotCompleted=Maximale Handelsdauer wurde überschritten (bis {0}) portfolio.pending.tradeProcess=Handelsprozess portfolio.pending.openAgainDispute.msg=Falls Sie nicht sicher sind, ob Ihre Nachricht den Vermittler erreicht hat (z.B. wenn Sie nach einem Tag noch keine Rückmeldung erhalten haben) können Sie gerne erneut einen Konflikt öffnen. diff --git a/gui/src/main/java/io/bisq/gui/bisq.css b/gui/src/main/java/io/bisq/gui/bisq.css index fdf6b1dbe25..f680fabad66 100644 --- a/gui/src/main/java/io/bisq/gui/bisq.css +++ b/gui/src/main/java/io/bisq/gui/bisq.css @@ -56,6 +56,7 @@ bg color of non edit textFields: fafafa -bs-orange: #ff8a2b; /* 2 usages */ -bs-orange2: #dd6900; /* 1 usages */ -bs-yellow: #ffb60f; /* 2 usages */ + -bs-yellow-light: derive(-bs-yellow, 81%); -bs-bg-grey8: #E1E9E1; /* 1 usages */ -bs-bg-green2:#619865; /* 2 usages */ -bs-bg-green:#99ba9c; /* 4 usages */ @@ -131,6 +132,23 @@ bg color of non edit textFields: fafafa -fx-font-size: 22; } +.info { + -fx-text-fill: -bs-green; +} + +.info:hover { + -fx-text-fill: -bs-grey; +} + +.warning-box { + -fx-background-color: -bs-yellow-light; + -fx-spacing: 6; + -fx-alignment: center; +} + +.warning { + -fx-text-fill: -bs-yellow; +} /* Other UI Elements */ .separator { diff --git a/gui/src/main/java/io/bisq/gui/components/InfoTextField.java b/gui/src/main/java/io/bisq/gui/components/InfoTextField.java index 30ef51505f0..b39619519e6 100644 --- a/gui/src/main/java/io/bisq/gui/components/InfoTextField.java +++ b/gui/src/main/java/io/bisq/gui/components/InfoTextField.java @@ -34,7 +34,7 @@ public InfoTextField() { infoIcon = new Label(); infoIcon.setLayoutY(3); - infoIcon.getStyleClass().addAll("highlight", "show-hand"); + infoIcon.getStyleClass().addAll("icon", "info"); AwesomeDude.setIcon(infoIcon, AwesomeIcon.INFO_SIGN); AnchorPane.setRightAnchor(infoIcon, 7.0); diff --git a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java index a6550ae5e92..1d08a2abbbd 100644 --- a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -17,12 +17,15 @@ package io.bisq.gui.main.portfolio.pendingtrades.steps; +import de.jensd.fx.fontawesome.AwesomeDude; +import de.jensd.fx.fontawesome.AwesomeIcon; import io.bisq.common.Clock; import io.bisq.common.app.Log; import io.bisq.common.locale.Res; import io.bisq.core.arbitration.Dispute; import io.bisq.core.trade.Trade; import io.bisq.core.user.Preferences; +import io.bisq.gui.components.AutoTooltipLabel; import io.bisq.gui.components.InfoTextField; import io.bisq.gui.components.TitledGroupBg; import io.bisq.gui.components.TxIdTextField; @@ -32,10 +35,15 @@ import io.bisq.gui.main.portfolio.pendingtrades.TradeSubView; import io.bisq.gui.util.Layout; import javafx.beans.value.ChangeListener; +import javafx.geometry.Insets; +import javafx.geometry.Orientation; +import javafx.scene.control.Label; import javafx.scene.control.ProgressBar; +import javafx.scene.control.Separator; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; import org.slf4j.Logger; @@ -179,6 +187,7 @@ protected void addTradeInfoBlock() { if (model.dataModel.getTrade() != null) { InfoTextField infoTextField = addOpenTradeDuration(gridPane, ++gridRow, model.dataModel.getTrade().getOffer()); + infoTextField.setContentForInfoPopOver(createInfoPopover()); } timeLeftTextField = addLabelTextField(gridPane, ++gridRow, Res.getWithCol("portfolio.pending.remainingTime")).second; @@ -425,4 +434,38 @@ private void updateTradePeriodState(Trade.TradePeriodState tradePeriodState) { } } } + + /////////////////////////////////////////////////////////////////////////////////////////// + // TradeDurationLimitInfo + /////////////////////////////////////////////////////////////////////////////////////////// + + private GridPane createInfoPopover() { + GridPane infoGridPane = new GridPane(); + int rowIndex = 0; + infoGridPane.setHgap(5); + infoGridPane.setVgap(5); + infoGridPane.setPadding(new Insets(10, 10, 10, 10)); + Label label = addMultilineLabel(infoGridPane, rowIndex++, Res.get("portfolio.pending.tradePeriodInfo")); + label.setMaxWidth(450); + + HBox warningBox = new HBox(); + warningBox.setMinHeight(30); + warningBox.setPadding(new Insets(5)); + warningBox.getStyleClass().add("warning-box"); + GridPane.setRowIndex(warningBox, rowIndex); + GridPane.setColumnSpan(warningBox, 2); + + Label warningIcon = new Label(); + AwesomeDude.setIcon(warningIcon, AwesomeIcon.WARNING_SIGN); + warningIcon.getStyleClass().add("warning"); + + Label warning = new Label(Res.get("portfolio.pending.tradePeriodWarning")); + warning.setWrapText(true); + warning.setMaxWidth(410); + + warningBox.getChildren().addAll(warningIcon, warning); + infoGridPane.getChildren().add(warningBox); + + return infoGridPane; + } } From a62354a4bf4d618a1d486af75de850df4db0f5ed Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 15:21:03 +0400 Subject: [PATCH 18/62] Copied refactored method as is into a separate class --- .../bisq/core/btc/wallet/ConfigPeerNodes.java | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java new file mode 100644 index 00000000000..aa1324259df --- /dev/null +++ b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java @@ -0,0 +1,160 @@ +package io.bisq.core.btc.wallet; + +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import io.bisq.common.util.Utilities; +import io.bisq.core.btc.BitcoinNodes; +import io.bisq.core.user.Preferences; +import io.bisq.network.DnsLookupTor; +import io.bisq.network.Socks5MultiDiscovery; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.PeerAddress; +import org.bitcoinj.net.OnionCat; +import org.bitcoinj.params.MainNetParams; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class ConfigPeerNodes { + private static final Logger log = LoggerFactory.getLogger(ConfigPeerNodes.class); + + private static final int DEFAULT_CONNECTIONS; + + private final Preferences preferences; + private final WalletConfig walletConfig; + private final NetworkParameters params; + private final int socks5DiscoverMode; + private final boolean useAllProvidedNodes; + private final BitcoinNodes bitcoinNodes; + + private void configPeerNodes(Socks5Proxy socks5Proxy) { + boolean useCustomNodes = false; + List btcNodeList = new ArrayList<>(); + + // We prefer to duplicate the check for CUSTOM here as in case the custom nodes lead to an empty list we fall back to the PROVIDED mode. + if (preferences.getBitcoinNodesOptionOrdinal() == BitcoinNodes.BitcoinNodesOption.CUSTOM.ordinal()) { + btcNodeList = BitcoinNodes.toBtcNodesList(Utilities.commaSeparatedListToSet(preferences.getBitcoinNodes(), false)); + if (btcNodeList.isEmpty()) { + log.warn("Custom nodes is set but no valid nodes are provided. We fall back to provided nodes option."); + preferences.setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.PROVIDED.ordinal()); + } + } + + switch (BitcoinNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]) { + case CUSTOM: + // We have set the btcNodeList already above + walletConfig.setMinBroadcastConnections((int) Math.ceil(btcNodeList.size() * 0.5)); + // If Tor is set we usually only use onion nodes, but if user provides mixed clear net and onion nodes we want to use both + useCustomNodes = true; + break; + case PUBLIC: + // We keep the empty btcNodeList + walletConfig.setMinBroadcastConnections((int) Math.floor(DEFAULT_CONNECTIONS * 0.8)); + break; + default: + case PROVIDED: + btcNodeList = bitcoinNodes.getProvidedBtcNodes(); + // We require only 4 nodes instead of 7 (for 9 max connections) because our provided nodes + // are more reliable than random public nodes. + walletConfig.setMinBroadcastConnections(4); + break; + } + + List peerAddressList = new ArrayList<>(); + final boolean useTorForBitcoinJ = socks5Proxy != null; + // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at + // exit nodes with bloom filters. + if (useTorForBitcoinJ) { + btcNodeList.stream() + .filter(BitcoinNodes.BtcNode::hasOnionAddress) + .forEach(btcNode -> { + // no DNS lookup for onion addresses + log.info("We add a onion node. btcNode={}", btcNode); + final String onionAddress = checkNotNull(btcNode.getOnionAddress()); + try { + // OnionCat.onionHostToInetAddress converts onion to ipv6 representation + // inetAddress is not used but required for wallet persistence. Throws nullPointer if not set. + final InetAddress inetAddress = OnionCat.onionHostToInetAddress(onionAddress); + final PeerAddress peerAddress = new PeerAddress(onionAddress, btcNode.getPort()); + peerAddress.setAddr(inetAddress); + peerAddressList.add(peerAddress); + } catch (UnknownHostException e) { + log.error("OnionCat.onionHostToInetAddress() failed with btcNode={}, error={}", btcNode.toString(), e.toString()); + e.printStackTrace(); + } + }); + if (useAllProvidedNodes || useCustomNodes) { + // We also use the clear net nodes (used for monitor) + btcNodeList.stream() + .filter(BitcoinNodes.BtcNode::hasClearNetAddress) + .forEach(btcNode -> { + try { + // We use DnsLookupTor to not leak with DNS lookup + // Blocking call. takes about 600 ms ;-( + InetSocketAddress address = new InetSocketAddress(DnsLookupTor.lookup(socks5Proxy, btcNode.getHostNameOrAddress()), btcNode.getPort()); + log.info("We add a clear net node (tor is used) with InetAddress={}, btcNode={}", address.getAddress(), btcNode); + peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); + } catch (Exception e) { + if (btcNode.getAddress() != null) { + log.warn("Dns lookup failed. We try with provided IP address. BtcNode: {}", btcNode); + try { + InetSocketAddress address = new InetSocketAddress(DnsLookupTor.lookup(socks5Proxy, btcNode.getAddress()), btcNode.getPort()); + log.info("We add a clear net node (tor is used) with InetAddress={}, BtcNode={}", address.getAddress(), btcNode); + peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); + } catch (Exception e2) { + log.warn("Dns lookup failed for BtcNode: {}", btcNode); + } + } else { + log.warn("Dns lookup failed. No IP address is provided. BtcNode: {}", btcNode); + } + } + }); + } + } else { + btcNodeList.stream() + .filter(BitcoinNodes.BtcNode::hasClearNetAddress) + .forEach(btcNode -> { + try { + InetSocketAddress address = new InetSocketAddress(btcNode.getHostNameOrAddress(), btcNode.getPort()); + log.info("We add a clear net node (no tor is used) with host={}, btcNode.getPort()={}", btcNode.getHostNameOrAddress(), btcNode.getPort()); + peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); + } catch (Throwable t) { + if (btcNode.getAddress() != null) { + log.warn("Dns lookup failed. We try with provided IP address. BtcNode: {}", btcNode); + try { + InetSocketAddress address = new InetSocketAddress(btcNode.getAddress(), btcNode.getPort()); + log.info("We add a clear net node (no tor is used) with host={}, btcNode.getPort()={}", btcNode.getHostNameOrAddress(), btcNode.getPort()); + peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); + } catch (Throwable t2) { + log.warn("Failed to create InetSocketAddress from btcNode {}", btcNode); + } + } else { + log.warn("Dns lookup failed. No IP address is provided. BtcNode: {}", btcNode); + } + } + }); + } + + if (!peerAddressList.isEmpty()) { + final PeerAddress[] peerAddresses = peerAddressList.toArray(new PeerAddress[peerAddressList.size()]); + log.info("You connect with peerAddresses: " + peerAddressList.toString()); + walletConfig.setPeerNodes(peerAddresses); + } else if (useTorForBitcoinJ) { + if (params == MainNetParams.get()) + log.warn("You use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + + "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use the provided nodes."); + // SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time. + walletConfig.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode)); + } else { + log.warn("You don't use gtor and use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + + "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use Tor and the provided nodes."); + } + } + +} From 88e7fddc5814bbfa718de45cd819c745fb9d10c8 Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 16:26:15 +0400 Subject: [PATCH 19/62] Split ConfigPeerNodes into smaller methods --- .../bisq/core/btc/wallet/ConfigPeerNodes.java | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java index aa1324259df..da1afaabd5c 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java @@ -21,7 +21,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -public class ConfigPeerNodes { +class ConfigPeerNodes { private static final Logger log = LoggerFactory.getLogger(ConfigPeerNodes.class); private static final int DEFAULT_CONNECTIONS; @@ -34,7 +34,32 @@ public class ConfigPeerNodes { private final BitcoinNodes bitcoinNodes; private void configPeerNodes(Socks5Proxy socks5Proxy) { - boolean useCustomNodes = false; + List btcNodeList = getBtcNodes(); + + final boolean useTorForBitcoinJ = socks5Proxy != null; + List peerAddressList = getPeerAddresses(socks5Proxy, btcNodeList, useTorForBitcoinJ); + + updateWalletConfig(socks5Proxy, useTorForBitcoinJ, peerAddressList); + } + + private void updateWalletConfig(Socks5Proxy socks5Proxy, boolean useTorForBitcoinJ, List peerAddressList) { + if (!peerAddressList.isEmpty()) { + final PeerAddress[] peerAddresses = peerAddressList.toArray(new PeerAddress[peerAddressList.size()]); + log.info("You connect with peerAddresses: " + peerAddressList.toString()); + walletConfig.setPeerNodes(peerAddresses); + } else if (useTorForBitcoinJ) { + if (params == MainNetParams.get()) + log.warn("You use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + + "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use the provided nodes."); + // SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time. + walletConfig.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode)); + } else { + log.warn("You don't use gtor and use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + + "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use Tor and the provided nodes."); + } + } + + private List getBtcNodes() { List btcNodeList = new ArrayList<>(); // We prefer to duplicate the check for CUSTOM here as in case the custom nodes lead to an empty list we fall back to the PROVIDED mode. @@ -51,7 +76,6 @@ private void configPeerNodes(Socks5Proxy socks5Proxy) { // We have set the btcNodeList already above walletConfig.setMinBroadcastConnections((int) Math.ceil(btcNodeList.size() * 0.5)); // If Tor is set we usually only use onion nodes, but if user provides mixed clear net and onion nodes we want to use both - useCustomNodes = true; break; case PUBLIC: // We keep the empty btcNodeList @@ -65,9 +89,13 @@ private void configPeerNodes(Socks5Proxy socks5Proxy) { walletConfig.setMinBroadcastConnections(4); break; } + return btcNodeList; + } + + private List getPeerAddresses(Socks5Proxy socks5Proxy, List btcNodeList, boolean useTorForBitcoinJ) { + boolean useCustomNodes = BitcoinNodes.BitcoinNodesOption.CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); List peerAddressList = new ArrayList<>(); - final boolean useTorForBitcoinJ = socks5Proxy != null; // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at // exit nodes with bloom filters. if (useTorForBitcoinJ) { @@ -140,21 +168,7 @@ private void configPeerNodes(Socks5Proxy socks5Proxy) { } }); } - - if (!peerAddressList.isEmpty()) { - final PeerAddress[] peerAddresses = peerAddressList.toArray(new PeerAddress[peerAddressList.size()]); - log.info("You connect with peerAddresses: " + peerAddressList.toString()); - walletConfig.setPeerNodes(peerAddresses); - } else if (useTorForBitcoinJ) { - if (params == MainNetParams.get()) - log.warn("You use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + - "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use the provided nodes."); - // SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time. - walletConfig.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode)); - } else { - log.warn("You don't use gtor and use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + - "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use Tor and the provided nodes."); - } + return peerAddressList; } } From b899304613f0382609f8d57a7054d41d27ff4b0b Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 17:17:10 +0400 Subject: [PATCH 20/62] Added initial implementation of BtcNodeConverter class --- .../core/btc/wallet/BtcNodeConverter.java | 41 +++++++++++++++++++ .../bisq/core/btc/wallet/ConfigPeerNodes.java | 36 +++++++--------- 2 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java new file mode 100644 index 00000000000..75bea74b6ca --- /dev/null +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java @@ -0,0 +1,41 @@ +package io.bisq.core.btc.wallet; + +import io.bisq.core.btc.BitcoinNodes.BtcNode; +import org.bitcoinj.core.PeerAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.net.InetSocketAddress; + +class BtcNodeConverter { + private static final Logger log = LoggerFactory.getLogger(BtcNodeConverter.class); + + @Nullable + PeerAddress convertClearNode(BtcNode node) { + int port = node.getPort(); + + PeerAddress result = create(node.getHostNameOrAddress(), port); + if (result == null) { + String address = node.getAddress(); + if (address != null) { + result = create(address, port); + } else { + log.warn("Lookup failed, no ip address for node", node); + } + } + return result; + } + + @Nullable + private static PeerAddress create(String hostName, int port) { + try { + InetSocketAddress address = new InetSocketAddress(hostName, port); + return new PeerAddress(address.getAddress(), address.getPort()); + } catch (Throwable t) { + log.error("Failed to create peer address", t); + return null; + } + } +} + diff --git a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java index da1afaabd5c..72e3779537e 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java @@ -13,11 +13,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkNotNull; @@ -32,6 +36,7 @@ class ConfigPeerNodes { private final int socks5DiscoverMode; private final boolean useAllProvidedNodes; private final BitcoinNodes bitcoinNodes; + private final BtcNodeConverter btcNodeConverter; private void configPeerNodes(Socks5Proxy socks5Proxy) { List btcNodeList = getBtcNodes(); @@ -145,30 +150,17 @@ private List getPeerAddresses(Socks5Proxy socks5Proxy, List { - try { - InetSocketAddress address = new InetSocketAddress(btcNode.getHostNameOrAddress(), btcNode.getPort()); - log.info("We add a clear net node (no tor is used) with host={}, btcNode.getPort()={}", btcNode.getHostNameOrAddress(), btcNode.getPort()); - peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); - } catch (Throwable t) { - if (btcNode.getAddress() != null) { - log.warn("Dns lookup failed. We try with provided IP address. BtcNode: {}", btcNode); - try { - InetSocketAddress address = new InetSocketAddress(btcNode.getAddress(), btcNode.getPort()); - log.info("We add a clear net node (no tor is used) with host={}, btcNode.getPort()={}", btcNode.getHostNameOrAddress(), btcNode.getPort()); - peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); - } catch (Throwable t2) { - log.warn("Failed to create InetSocketAddress from btcNode {}", btcNode); - } - } else { - log.warn("Dns lookup failed. No IP address is provided. BtcNode: {}", btcNode); - } - } - }); + peerAddressList = btcNodeList.stream() + .flatMap(btcNode -> nullableAsStream(btcNodeConverter.convertClearNode(btcNode))) + .collect(Collectors.toList()); } return peerAddressList; } + private static Stream nullableAsStream(@Nullable T item) { + return Optional.ofNullable(item) + .map(Stream::of) + .orElse(Stream.empty()); + + } } From 9a621d064f787158f0d673a02fd44911fe76be27 Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 17:36:40 +0400 Subject: [PATCH 21/62] Extracted tor nodes conversion into BtcNodeConverter --- .../core/btc/wallet/BtcNodeConverter.java | 40 +++++++++++++++++-- .../bisq/core/btc/wallet/ConfigPeerNodes.java | 29 +++----------- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java index 75bea74b6ca..4cb4f2c0d4d 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java @@ -1,13 +1,17 @@ package io.bisq.core.btc.wallet; +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; import io.bisq.core.btc.BitcoinNodes.BtcNode; +import io.bisq.network.DnsLookupTor; import org.bitcoinj.core.PeerAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; +import java.net.InetAddress; import java.net.InetSocketAddress; +// TODO refactor class BtcNodeConverter { private static final Logger log = LoggerFactory.getLogger(BtcNodeConverter.class); @@ -21,19 +25,49 @@ PeerAddress convertClearNode(BtcNode node) { if (address != null) { result = create(address, port); } else { - log.warn("Lookup failed, no ip address for node", node); + log.warn("Lookup failed, no address for node", node); } } return result; } + @Nullable + PeerAddress convertWithTor(BtcNode node, Socks5Proxy proxy) { + int port = node.getPort(); + + PeerAddress result = create(proxy, node.getHostNameOrAddress(), port); + if (result == null) { + String address = node.getAddress(); + if (address != null) { + result = create(proxy, address, port); + } else { + log.warn("Lookup failed, no address for node", node); + } + } + return result; + } + + @Nullable + private static PeerAddress create(Socks5Proxy proxy, String host, int port) { + try { + // We use DnsLookupTor to not leak with DNS lookup + // Blocking call. takes about 600 ms ;-( + InetAddress lookupAddress = DnsLookupTor.lookup(proxy, host); + InetSocketAddress address = new InetSocketAddress(lookupAddress, port); + return new PeerAddress(address.getAddress(), address.getPort()); + } catch (Exception e) { + log.error("Failed to create peer address", e); + return null; + } + } + @Nullable private static PeerAddress create(String hostName, int port) { try { InetSocketAddress address = new InetSocketAddress(hostName, port); return new PeerAddress(address.getAddress(), address.getPort()); - } catch (Throwable t) { - log.error("Failed to create peer address", t); + } catch (Exception e) { + log.error("Failed to create peer address", e); return null; } } diff --git a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java index 72e3779537e..04539f77296 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java @@ -124,33 +124,16 @@ private List getPeerAddresses(Socks5Proxy socks5Proxy, List torAddresses = btcNodeList.stream() .filter(BitcoinNodes.BtcNode::hasClearNetAddress) - .forEach(btcNode -> { - try { - // We use DnsLookupTor to not leak with DNS lookup - // Blocking call. takes about 600 ms ;-( - InetSocketAddress address = new InetSocketAddress(DnsLookupTor.lookup(socks5Proxy, btcNode.getHostNameOrAddress()), btcNode.getPort()); - log.info("We add a clear net node (tor is used) with InetAddress={}, btcNode={}", address.getAddress(), btcNode); - peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); - } catch (Exception e) { - if (btcNode.getAddress() != null) { - log.warn("Dns lookup failed. We try with provided IP address. BtcNode: {}", btcNode); - try { - InetSocketAddress address = new InetSocketAddress(DnsLookupTor.lookup(socks5Proxy, btcNode.getAddress()), btcNode.getPort()); - log.info("We add a clear net node (tor is used) with InetAddress={}, BtcNode={}", address.getAddress(), btcNode); - peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); - } catch (Exception e2) { - log.warn("Dns lookup failed for BtcNode: {}", btcNode); - } - } else { - log.warn("Dns lookup failed. No IP address is provided. BtcNode: {}", btcNode); - } - } - }); + .flatMap(btcNode -> nullableAsStream(btcNodeConverter.convertWithTor(btcNode, socks5Proxy))) + .collect(Collectors.toList()); + + peerAddressList.addAll(torAddresses); } } else { peerAddressList = btcNodeList.stream() + .filter(BitcoinNodes.BtcNode::hasClearNetAddress) .flatMap(btcNode -> nullableAsStream(btcNodeConverter.convertClearNode(btcNode))) .collect(Collectors.toList()); } From 913873991ca279cd1e2852fd4bbef94a9f0b0b14 Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 18:23:57 +0400 Subject: [PATCH 22/62] Implemented PeerAddressesRepository class --- .../core/btc/wallet/BtcNodeConverter.java | 20 ++++++ .../bisq/core/btc/wallet/ConfigPeerNodes.java | 67 +++++++------------ .../btc/wallet/PeerAddressesRepository.java | 48 +++++++++++++ 3 files changed, 93 insertions(+), 42 deletions(-) create mode 100644 core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java index 4cb4f2c0d4d..30fbc4e4650 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java @@ -4,17 +4,37 @@ import io.bisq.core.btc.BitcoinNodes.BtcNode; import io.bisq.network.DnsLookupTor; import org.bitcoinj.core.PeerAddress; +import org.bitcoinj.net.OnionCat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.Objects; // TODO refactor class BtcNodeConverter { private static final Logger log = LoggerFactory.getLogger(BtcNodeConverter.class); + @Nullable + PeerAddress convertOnionHost(BtcNode node) { + // no DNS lookup for onion addresses + String onionAddress = Objects.requireNonNull(node.getOnionAddress()); + try { + // OnionCat.onionHostToInetAddress converts onion to ipv6 representation + // inetAddress is not used but required for wallet persistence. Throws nullPointer if not set. + InetAddress inetAddress = OnionCat.onionHostToInetAddress(onionAddress); + PeerAddress result = new PeerAddress(onionAddress, node.getPort()); + result.setAddr(inetAddress); + return result; + } catch (UnknownHostException e) { + log.error("Failed to convert node", e); + return null; + } + } + @Nullable PeerAddress convertClearNode(BtcNode node) { int port = node.getPort(); diff --git a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java index 04539f77296..4f1d8fea5a6 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java @@ -3,27 +3,23 @@ import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; import io.bisq.common.util.Utilities; import io.bisq.core.btc.BitcoinNodes; +import io.bisq.core.btc.BitcoinNodes.BtcNode; import io.bisq.core.user.Preferences; -import io.bisq.network.DnsLookupTor; import io.bisq.network.Socks5MultiDiscovery; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.PeerAddress; -import org.bitcoinj.net.OnionCat; import org.bitcoinj.params.MainNetParams; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkNotNull; +import static io.bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM; class ConfigPeerNodes { private static final Logger log = LoggerFactory.getLogger(ConfigPeerNodes.class); @@ -39,7 +35,7 @@ class ConfigPeerNodes { private final BtcNodeConverter btcNodeConverter; private void configPeerNodes(Socks5Proxy socks5Proxy) { - List btcNodeList = getBtcNodes(); + List btcNodeList = getBtcNodes(); final boolean useTorForBitcoinJ = socks5Proxy != null; List peerAddressList = getPeerAddresses(socks5Proxy, btcNodeList, useTorForBitcoinJ); @@ -64,11 +60,11 @@ private void updateWalletConfig(Socks5Proxy socks5Proxy, boolean useTorForBitcoi } } - private List getBtcNodes() { - List btcNodeList = new ArrayList<>(); + private List getBtcNodes() { + List btcNodeList = new ArrayList<>(); // We prefer to duplicate the check for CUSTOM here as in case the custom nodes lead to an empty list we fall back to the PROVIDED mode. - if (preferences.getBitcoinNodesOptionOrdinal() == BitcoinNodes.BitcoinNodesOption.CUSTOM.ordinal()) { + if (preferences.getBitcoinNodesOptionOrdinal() == CUSTOM.ordinal()) { btcNodeList = BitcoinNodes.toBtcNodesList(Utilities.commaSeparatedListToSet(preferences.getBitcoinNodes(), false)); if (btcNodeList.isEmpty()) { log.warn("Custom nodes is set but no valid nodes are provided. We fall back to provided nodes option."); @@ -97,53 +93,40 @@ private List getBtcNodes() { return btcNodeList; } - private List getPeerAddresses(Socks5Proxy socks5Proxy, List btcNodeList, boolean useTorForBitcoinJ) { - boolean useCustomNodes = BitcoinNodes.BitcoinNodesOption.CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); - - List peerAddressList = new ArrayList<>(); + private List getPeerAddresses(@Nullable Socks5Proxy socks5Proxy, List nodes) { + List result; // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at // exit nodes with bloom filters. - if (useTorForBitcoinJ) { - btcNodeList.stream() - .filter(BitcoinNodes.BtcNode::hasOnionAddress) - .forEach(btcNode -> { - // no DNS lookup for onion addresses - log.info("We add a onion node. btcNode={}", btcNode); - final String onionAddress = checkNotNull(btcNode.getOnionAddress()); - try { - // OnionCat.onionHostToInetAddress converts onion to ipv6 representation - // inetAddress is not used but required for wallet persistence. Throws nullPointer if not set. - final InetAddress inetAddress = OnionCat.onionHostToInetAddress(onionAddress); - final PeerAddress peerAddress = new PeerAddress(onionAddress, btcNode.getPort()); - peerAddress.setAddr(inetAddress); - peerAddressList.add(peerAddress); - } catch (UnknownHostException e) { - log.error("OnionCat.onionHostToInetAddress() failed with btcNode={}, error={}", btcNode.toString(), e.toString()); - e.printStackTrace(); - } - }); + if (socks5Proxy != null) { + List onionHosts = nodes.stream() + .filter(BtcNode::hasOnionAddress) + .flatMap(node -> nullableAsStream(btcNodeConverter.convertOnionHost(node))) + .collect(Collectors.toList()); + + boolean useCustomNodes = CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); if (useAllProvidedNodes || useCustomNodes) { // We also use the clear net nodes (used for monitor) - List torAddresses = btcNodeList.stream() - .filter(BitcoinNodes.BtcNode::hasClearNetAddress) - .flatMap(btcNode -> nullableAsStream(btcNodeConverter.convertWithTor(btcNode, socks5Proxy))) + List torAddresses = nodes.stream() + .filter(BtcNode::hasClearNetAddress) + .flatMap(node -> nullableAsStream(btcNodeConverter.convertWithTor(node, socks5Proxy))) .collect(Collectors.toList()); - peerAddressList.addAll(torAddresses); + result = new ArrayList<>(); + result.addAll(onionHosts); + result.addAll(torAddresses); } } else { - peerAddressList = btcNodeList.stream() - .filter(BitcoinNodes.BtcNode::hasClearNetAddress) - .flatMap(btcNode -> nullableAsStream(btcNodeConverter.convertClearNode(btcNode))) + result = nodes.stream() + .filter(BtcNode::hasClearNetAddress) + .flatMap(node -> nullableAsStream(btcNodeConverter.convertClearNode(node))) .collect(Collectors.toList()); } - return peerAddressList; + return result; } private static Stream nullableAsStream(@Nullable T item) { return Optional.ofNullable(item) .map(Stream::of) .orElse(Stream.empty()); - } } diff --git a/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java b/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java new file mode 100644 index 00000000000..08b8d2cf714 --- /dev/null +++ b/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java @@ -0,0 +1,48 @@ +package io.bisq.core.btc.wallet; + +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import io.bisq.core.btc.BitcoinNodes.BtcNode; +import org.bitcoinj.core.PeerAddress; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class PeerAddressesRepository { + private final BtcNodeConverter converter; + private final List nodes; + + PeerAddressesRepository(BtcNodeConverter converter, List nodes) { + this.converter = converter; + this.nodes = nodes; + } + + List getClearNodes() { + return nodes.stream() + .filter(BtcNode::hasClearNetAddress) + .flatMap(node -> nullableAsStream(converter.convertClearNode(node))) + .collect(Collectors.toList()); + } + + List getOnionHosts() { + return nodes.stream() + .filter(BtcNode::hasOnionAddress) + .flatMap(node -> nullableAsStream(converter.convertOnionHost(node))) + .collect(Collectors.toList()); + } + + List getProxifiedClearNodes(Socks5Proxy proxy) { + return nodes.stream() + .filter(BtcNode::hasClearNetAddress) + .flatMap(node -> nullableAsStream(converter.convertWithTor(node, proxy))) + .collect(Collectors.toList()); + } + + private static Stream nullableAsStream(@Nullable T item) { + return Optional.ofNullable(item) + .map(Stream::of) + .orElse(Stream.empty()); + } +} From f68a997d04e4a37ad7600be9e35f72533c3bca07 Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 19:01:00 +0400 Subject: [PATCH 23/62] Extracted ConfigPeerNodes.setUpMinBroadcastConnections method --- .../bisq/core/btc/wallet/ConfigPeerNodes.java | 132 ++++++++++-------- 1 file changed, 72 insertions(+), 60 deletions(-) diff --git a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java index 4f1d8fea5a6..86b7a4b752f 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java @@ -14,10 +14,8 @@ import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static io.bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM; @@ -32,101 +30,115 @@ class ConfigPeerNodes { private final int socks5DiscoverMode; private final boolean useAllProvidedNodes; private final BitcoinNodes bitcoinNodes; - private final BtcNodeConverter btcNodeConverter; - private void configPeerNodes(Socks5Proxy socks5Proxy) { - List btcNodeList = getBtcNodes(); - - final boolean useTorForBitcoinJ = socks5Proxy != null; - List peerAddressList = getPeerAddresses(socks5Proxy, btcNodeList, useTorForBitcoinJ); + ConfigPeerNodes(Preferences preferences, WalletConfig walletConfig, NetworkParameters params, + int socks5DiscoverMode, boolean useAllProvidedNodes, BitcoinNodes bitcoinNodes) { + this.preferences = preferences; + this.walletConfig = walletConfig; + this.params = params; + this.socks5DiscoverMode = socks5DiscoverMode; + this.useAllProvidedNodes = useAllProvidedNodes; + this.bitcoinNodes = bitcoinNodes; + } - updateWalletConfig(socks5Proxy, useTorForBitcoinJ, peerAddressList); + public void configPeerNodes(@Nullable Socks5Proxy proxy) { + List nodes = getBtcNodes(); + setUpMinBroadcastConnections(nodes); + List peers = getPeerAddresses(proxy, nodes); + updateWalletConfig(proxy, peers); } - private void updateWalletConfig(Socks5Proxy socks5Proxy, boolean useTorForBitcoinJ, List peerAddressList) { - if (!peerAddressList.isEmpty()) { - final PeerAddress[] peerAddresses = peerAddressList.toArray(new PeerAddress[peerAddressList.size()]); - log.info("You connect with peerAddresses: " + peerAddressList.toString()); + private void updateWalletConfig(@Nullable Socks5Proxy socks5Proxy, List peers) { + if (!peers.isEmpty()) { + log.info("You connect with peerAddresses: {}", peers); + PeerAddress[] peerAddresses = peers.toArray(new PeerAddress[peers.size()]); walletConfig.setPeerNodes(peerAddresses); - } else if (useTorForBitcoinJ) { - if (params == MainNetParams.get()) - log.warn("You use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + - "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use the provided nodes."); + } else if (socks5Proxy != null) { + MainNetParams mainNetParams = MainNetParams.get(); + if (log.isWarnEnabled() && params.equals(mainNetParams)) { + log.warn("You use the public Bitcoin network and are exposed to privacy issues " + + "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + + "for more info. It is recommended to use the provided nodes."); + } // SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time. walletConfig.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode)); } else { - log.warn("You don't use gtor and use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + - "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use Tor and the provided nodes."); + log.warn("You don't use tor and use the public Bitcoin network and are exposed to privacy issues " + + "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + + "for more info. It is recommended to use Tor and the provided nodes."); } } private List getBtcNodes() { - List btcNodeList = new ArrayList<>(); - - // We prefer to duplicate the check for CUSTOM here as in case the custom nodes lead to an empty list we fall back to the PROVIDED mode. - if (preferences.getBitcoinNodesOptionOrdinal() == CUSTOM.ordinal()) { - btcNodeList = BitcoinNodes.toBtcNodesList(Utilities.commaSeparatedListToSet(preferences.getBitcoinNodes(), false)); - if (btcNodeList.isEmpty()) { - log.warn("Custom nodes is set but no valid nodes are provided. We fall back to provided nodes option."); - preferences.setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.PROVIDED.ordinal()); - } + List btcNodeList; + + switch (BitcoinNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]) { + case CUSTOM: + btcNodeList = BitcoinNodes.toBtcNodesList(Utilities.commaSeparatedListToSet(preferences.getBitcoinNodes(), false)); + if (btcNodeList.isEmpty()) { + log.warn("Custom nodes is set but no valid nodes are provided. We fall back to provided nodes option."); + preferences.setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.PROVIDED.ordinal()); + + // TODO refactor as "PROVIDED" + btcNodeList = bitcoinNodes.getProvidedBtcNodes(); + } + break; + case PUBLIC: + btcNodeList = Collections.emptyList(); + break; + case PROVIDED: + default: + btcNodeList = bitcoinNodes.getProvidedBtcNodes(); + break; } + return btcNodeList; + } + + private void setUpMinBroadcastConnections(List nodes) { switch (BitcoinNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]) { case CUSTOM: - // We have set the btcNodeList already above - walletConfig.setMinBroadcastConnections((int) Math.ceil(btcNodeList.size() * 0.5)); - // If Tor is set we usually only use onion nodes, but if user provides mixed clear net and onion nodes we want to use both + // We have set the nodes already above + walletConfig.setMinBroadcastConnections((int) Math.ceil(nodes.size() * 0.5)); + // If Tor is set we usually only use onion nodes, + // but if user provides mixed clear net and onion nodes we want to use both break; case PUBLIC: - // We keep the empty btcNodeList + // We keep the empty nodes walletConfig.setMinBroadcastConnections((int) Math.floor(DEFAULT_CONNECTIONS * 0.8)); break; - default: case PROVIDED: - btcNodeList = bitcoinNodes.getProvidedBtcNodes(); + default: // We require only 4 nodes instead of 7 (for 9 max connections) because our provided nodes // are more reliable than random public nodes. walletConfig.setMinBroadcastConnections(4); break; } - return btcNodeList; } - private List getPeerAddresses(@Nullable Socks5Proxy socks5Proxy, List nodes) { + private List getPeerAddresses(@Nullable Socks5Proxy proxy, List nodes) { + // TODO factory + PeerAddressesRepository repository = new PeerAddressesRepository(new BtcNodeConverter(), nodes); + List result; // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at // exit nodes with bloom filters. - if (socks5Proxy != null) { - List onionHosts = nodes.stream() - .filter(BtcNode::hasOnionAddress) - .flatMap(node -> nullableAsStream(btcNodeConverter.convertOnionHost(node))) - .collect(Collectors.toList()); - - boolean useCustomNodes = CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); - if (useAllProvidedNodes || useCustomNodes) { - // We also use the clear net nodes (used for monitor) - List torAddresses = nodes.stream() - .filter(BtcNode::hasClearNetAddress) - .flatMap(node -> nullableAsStream(btcNodeConverter.convertWithTor(node, socks5Proxy))) - .collect(Collectors.toList()); + if (proxy != null) { + List onionHosts = repository.getOnionHosts(); + result = new ArrayList<>(onionHosts); - result = new ArrayList<>(); - result.addAll(onionHosts); + if (useAllProvidedNodes || isUseCustomNodes()) { + // We also use the clear net nodes (used for monitor) + List torAddresses = repository.getProxifiedClearNodes(proxy); result.addAll(torAddresses); } } else { - result = nodes.stream() - .filter(BtcNode::hasClearNetAddress) - .flatMap(node -> nullableAsStream(btcNodeConverter.convertClearNode(node))) - .collect(Collectors.toList()); + result = repository.getClearNodes(); } return result; } - private static Stream nullableAsStream(@Nullable T item) { - return Optional.ofNullable(item) - .map(Stream::of) - .orElse(Stream.empty()); + private boolean isUseCustomNodes() { + return CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); } } From bfb380e3053c44ec481cfadef1c96cbadc37f1f7 Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 21:29:12 +0400 Subject: [PATCH 24/62] Replaced WalletsSetup#configPeerNodes method --- .../io/bisq/core/btc/wallet/BtcNodes.java | 4 + .../bisq/core/btc/wallet/ConfigPeerNodes.java | 144 ------------------ .../btc/wallet/PeerAddressesRepository.java | 27 +++- .../core/btc/wallet/WalletNetworkConfig.java | 51 +++++++ .../btc/wallet/WalletSetupPreferences.java | 80 ++++++++++ .../io/bisq/core/btc/wallet/WalletsSetup.java | 136 ++--------------- 6 files changed, 168 insertions(+), 274 deletions(-) create mode 100644 core/src/main/java/io/bisq/core/btc/wallet/BtcNodes.java delete mode 100644 core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java create mode 100644 core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java create mode 100644 core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodes.java new file mode 100644 index 00000000000..226e1ec3476 --- /dev/null +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodes.java @@ -0,0 +1,4 @@ +package io.bisq.core.btc.wallet; + +class BtcNodes { +} diff --git a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java deleted file mode 100644 index 86b7a4b752f..00000000000 --- a/core/src/main/java/io/bisq/core/btc/wallet/ConfigPeerNodes.java +++ /dev/null @@ -1,144 +0,0 @@ -package io.bisq.core.btc.wallet; - -import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; -import io.bisq.common.util.Utilities; -import io.bisq.core.btc.BitcoinNodes; -import io.bisq.core.btc.BitcoinNodes.BtcNode; -import io.bisq.core.user.Preferences; -import io.bisq.network.Socks5MultiDiscovery; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.PeerAddress; -import org.bitcoinj.params.MainNetParams; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static io.bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM; - -class ConfigPeerNodes { - private static final Logger log = LoggerFactory.getLogger(ConfigPeerNodes.class); - - private static final int DEFAULT_CONNECTIONS; - - private final Preferences preferences; - private final WalletConfig walletConfig; - private final NetworkParameters params; - private final int socks5DiscoverMode; - private final boolean useAllProvidedNodes; - private final BitcoinNodes bitcoinNodes; - - ConfigPeerNodes(Preferences preferences, WalletConfig walletConfig, NetworkParameters params, - int socks5DiscoverMode, boolean useAllProvidedNodes, BitcoinNodes bitcoinNodes) { - this.preferences = preferences; - this.walletConfig = walletConfig; - this.params = params; - this.socks5DiscoverMode = socks5DiscoverMode; - this.useAllProvidedNodes = useAllProvidedNodes; - this.bitcoinNodes = bitcoinNodes; - } - - public void configPeerNodes(@Nullable Socks5Proxy proxy) { - List nodes = getBtcNodes(); - setUpMinBroadcastConnections(nodes); - List peers = getPeerAddresses(proxy, nodes); - updateWalletConfig(proxy, peers); - } - - private void updateWalletConfig(@Nullable Socks5Proxy socks5Proxy, List peers) { - if (!peers.isEmpty()) { - log.info("You connect with peerAddresses: {}", peers); - PeerAddress[] peerAddresses = peers.toArray(new PeerAddress[peers.size()]); - walletConfig.setPeerNodes(peerAddresses); - } else if (socks5Proxy != null) { - MainNetParams mainNetParams = MainNetParams.get(); - if (log.isWarnEnabled() && params.equals(mainNetParams)) { - log.warn("You use the public Bitcoin network and are exposed to privacy issues " + - "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + - "for more info. It is recommended to use the provided nodes."); - } - // SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time. - walletConfig.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode)); - } else { - log.warn("You don't use tor and use the public Bitcoin network and are exposed to privacy issues " + - "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + - "for more info. It is recommended to use Tor and the provided nodes."); - } - } - - private List getBtcNodes() { - List btcNodeList; - - switch (BitcoinNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]) { - case CUSTOM: - btcNodeList = BitcoinNodes.toBtcNodesList(Utilities.commaSeparatedListToSet(preferences.getBitcoinNodes(), false)); - if (btcNodeList.isEmpty()) { - log.warn("Custom nodes is set but no valid nodes are provided. We fall back to provided nodes option."); - preferences.setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.PROVIDED.ordinal()); - - // TODO refactor as "PROVIDED" - btcNodeList = bitcoinNodes.getProvidedBtcNodes(); - } - break; - case PUBLIC: - btcNodeList = Collections.emptyList(); - break; - case PROVIDED: - default: - btcNodeList = bitcoinNodes.getProvidedBtcNodes(); - break; - } - - return btcNodeList; - } - - private void setUpMinBroadcastConnections(List nodes) { - switch (BitcoinNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]) { - case CUSTOM: - // We have set the nodes already above - walletConfig.setMinBroadcastConnections((int) Math.ceil(nodes.size() * 0.5)); - // If Tor is set we usually only use onion nodes, - // but if user provides mixed clear net and onion nodes we want to use both - break; - case PUBLIC: - // We keep the empty nodes - walletConfig.setMinBroadcastConnections((int) Math.floor(DEFAULT_CONNECTIONS * 0.8)); - break; - case PROVIDED: - default: - // We require only 4 nodes instead of 7 (for 9 max connections) because our provided nodes - // are more reliable than random public nodes. - walletConfig.setMinBroadcastConnections(4); - break; - } - } - - private List getPeerAddresses(@Nullable Socks5Proxy proxy, List nodes) { - // TODO factory - PeerAddressesRepository repository = new PeerAddressesRepository(new BtcNodeConverter(), nodes); - - List result; - // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at - // exit nodes with bloom filters. - if (proxy != null) { - List onionHosts = repository.getOnionHosts(); - result = new ArrayList<>(onionHosts); - - if (useAllProvidedNodes || isUseCustomNodes()) { - // We also use the clear net nodes (used for monitor) - List torAddresses = repository.getProxifiedClearNodes(proxy); - result.addAll(torAddresses); - } - } else { - result = repository.getClearNodes(); - } - return result; - } - - private boolean isUseCustomNodes() { - return CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); - } -} diff --git a/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java b/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java index 08b8d2cf714..626be6fe330 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java @@ -5,6 +5,7 @@ import org.bitcoinj.core.PeerAddress; import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -19,21 +20,41 @@ class PeerAddressesRepository { this.nodes = nodes; } - List getClearNodes() { + // TODO remove flag + List getPeerAddresses(@Nullable Socks5Proxy proxy, boolean isUseProxifiedClearNodes) { + List result; + // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at + // exit nodes with bloom filters. + if (proxy != null) { + List onionHosts = getOnionHosts(); + result = new ArrayList<>(onionHosts); + + if (isUseProxifiedClearNodes) { + // We also use the clear net nodes (used for monitor) + List torAddresses = getProxifiedClearNodes(proxy); + result.addAll(torAddresses); + } + } else { + result = getClearNodes(); + } + return result; + } + + private List getClearNodes() { return nodes.stream() .filter(BtcNode::hasClearNetAddress) .flatMap(node -> nullableAsStream(converter.convertClearNode(node))) .collect(Collectors.toList()); } - List getOnionHosts() { + private List getOnionHosts() { return nodes.stream() .filter(BtcNode::hasOnionAddress) .flatMap(node -> nullableAsStream(converter.convertOnionHost(node))) .collect(Collectors.toList()); } - List getProxifiedClearNodes(Socks5Proxy proxy) { + private List getProxifiedClearNodes(Socks5Proxy proxy) { return nodes.stream() .filter(BtcNode::hasClearNetAddress) .flatMap(node -> nullableAsStream(converter.convertWithTor(node, proxy))) diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java new file mode 100644 index 00000000000..73559b80b3a --- /dev/null +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java @@ -0,0 +1,51 @@ +package io.bisq.core.btc.wallet; + +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import io.bisq.network.Socks5MultiDiscovery; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.PeerAddress; +import org.bitcoinj.params.MainNetParams; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.List; + +class WalletNetworkConfig { + private static final Logger log = LoggerFactory.getLogger(WalletNetworkConfig.class); + + @Nullable + private final Socks5Proxy socks5Proxy; + private final WalletConfig walletConfig; + private final NetworkParameters params; + private final int socks5DiscoverMode; + + WalletNetworkConfig(WalletConfig walletConfig, NetworkParameters params, int socks5DiscoverMode, + @Nullable Socks5Proxy socks5Proxy) { + this.walletConfig = walletConfig; + this.params = params; + this.socks5DiscoverMode = socks5DiscoverMode; + this.socks5Proxy = socks5Proxy; + } + + void setPeers(List peers) { + if (!peers.isEmpty()) { + log.info("You connect with peerAddresses: {}", peers); + PeerAddress[] peerAddresses = peers.toArray(new PeerAddress[peers.size()]); + walletConfig.setPeerNodes(peerAddresses); + } else if (socks5Proxy != null) { + MainNetParams mainNetParams = MainNetParams.get(); + if (log.isWarnEnabled() && params.equals(mainNetParams)) { + log.warn("You use the public Bitcoin network and are exposed to privacy issues " + + "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + + "for more info. It is recommended to use the provided nodes."); + } + // SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time. + walletConfig.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode)); + } else { + log.warn("You don't use tor and use the public Bitcoin network and are exposed to privacy issues " + + "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + + "for more info. It is recommended to use Tor and the provided nodes."); + } + } +} diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java new file mode 100644 index 00000000000..fe1cb7a9b4c --- /dev/null +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java @@ -0,0 +1,80 @@ +package io.bisq.core.btc.wallet; + +import io.bisq.common.util.Utilities; +import io.bisq.core.btc.BitcoinNodes; +import io.bisq.core.btc.BitcoinNodes.BitcoinNodesOption; +import io.bisq.core.btc.BitcoinNodes.BtcNode; +import io.bisq.core.user.Preferences; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; + +import static io.bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM; +import static io.bisq.core.btc.wallet.WalletsSetup.DEFAULT_CONNECTIONS; + + +class WalletSetupPreferences { + private static final Logger log = LoggerFactory.getLogger(WalletSetupPreferences.class); + + private final Preferences preferences; + + WalletSetupPreferences(Preferences preferences) { + this.preferences = preferences; + } + + List selectPreferredNodes(BitcoinNodes bitcoinNodes) { + List result; + + BitcoinNodesOption nodesOption = BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]; + switch (nodesOption) { + case CUSTOM: + result = BitcoinNodes.toBtcNodesList(Utilities.commaSeparatedListToSet(preferences.getBitcoinNodes(), + false)); + if (result.isEmpty()) { + log.warn("Custom nodes is set but no valid nodes are provided. " + + "We fall back to provided nodes option."); + preferences.setBitcoinNodesOptionOrdinal(BitcoinNodesOption.PROVIDED.ordinal()); + result = bitcoinNodes.getProvidedBtcNodes(); + } + break; + case PUBLIC: + result = Collections.emptyList(); + break; + case PROVIDED: + default: + result = bitcoinNodes.getProvidedBtcNodes(); + break; + } + + return result; + } + + boolean isUseCustomNodes() { + return CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); + } + + void calculateMinBroadcastConnections(WalletConfig walletConfig, List nodes) { + BitcoinNodesOption nodesOption = BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]; + switch (nodesOption) { + case CUSTOM: + // We have set the nodes already above + walletConfig.setMinBroadcastConnections((int) Math.ceil(nodes.size() * 0.5)); + // If Tor is set we usually only use onion nodes, + // but if user provides mixed clear net and onion nodes we want to use both + break; + case PUBLIC: + // We keep the empty nodes + walletConfig.setMinBroadcastConnections((int) Math.floor(DEFAULT_CONNECTIONS * 0.8)); + break; + case PROVIDED: + default: + // We require only 4 nodes instead of 7 (for 9 max connections) because our provided nodes + // are more reliable than random public nodes. + walletConfig.setMinBroadcastConnections(4); + break; + } + } + +} diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java index c621492a2de..0e09e0b3994 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java @@ -28,11 +28,10 @@ import io.bisq.common.handlers.ExceptionHandler; import io.bisq.common.handlers.ResultHandler; import io.bisq.common.storage.FileUtil; -import io.bisq.common.util.Utilities; import io.bisq.core.app.BisqEnvironment; import io.bisq.core.btc.*; +import io.bisq.core.btc.BitcoinNodes.BtcNode; import io.bisq.core.user.Preferences; -import io.bisq.network.DnsLookupTor; import io.bisq.network.Socks5MultiDiscovery; import io.bisq.network.Socks5ProxyProvider; import javafx.beans.property.*; @@ -40,8 +39,6 @@ import org.apache.commons.lang3.StringUtils; import org.bitcoinj.core.*; import org.bitcoinj.core.listeners.DownloadProgressTracker; -import org.bitcoinj.net.OnionCat; -import org.bitcoinj.params.MainNetParams; import org.bitcoinj.params.RegTestParams; import org.bitcoinj.utils.Threading; import org.bitcoinj.wallet.DeterministicSeed; @@ -53,7 +50,6 @@ import java.io.File; import java.io.IOException; import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.nio.file.Paths; import java.util.ArrayList; @@ -72,7 +68,7 @@ @Slf4j public class WalletsSetup { // We reduce defaultConnections from 12 (PeerGroup.DEFAULT_CONNECTIONS) to 9 nodes - private static final int DEFAULT_CONNECTIONS = 9; + static final int DEFAULT_CONNECTIONS = 9; private static final long STARTUP_TIMEOUT = 180; private static final String BSQ_WALLET_FILE_NAME = "bisq_BSQ.wallet"; @@ -292,128 +288,14 @@ private void configPeerNodesForLocalHostBitcoinNode() { walletConfig.setPeerNodesForLocalHost(); } - private void configPeerNodes(Socks5Proxy socks5Proxy) { - boolean useCustomNodes = false; - List btcNodeList = new ArrayList<>(); + private void configPeerNodes(@Nullable Socks5Proxy proxy) { + WalletSetupPreferences walletSetupPreferences = new WalletSetupPreferences(preferences); + List nodes = walletSetupPreferences.selectPreferredNodes(bitcoinNodes); + walletSetupPreferences.calculateMinBroadcastConnections(walletConfig, nodes); + List peers = new PeerAddressesRepository(new BtcNodeConverter(), nodes).getPeerAddresses(proxy, + useAllProvidedNodes || walletSetupPreferences.isUseCustomNodes()); + new WalletNetworkConfig(walletConfig, params, socks5DiscoverMode, proxy).setPeers(peers); - // We prefer to duplicate the check for CUSTOM here as in case the custom nodes lead to an empty list we fall back to the PROVIDED mode. - if (preferences.getBitcoinNodesOptionOrdinal() == BitcoinNodes.BitcoinNodesOption.CUSTOM.ordinal()) { - btcNodeList = BitcoinNodes.toBtcNodesList(Utilities.commaSeparatedListToSet(preferences.getBitcoinNodes(), false)); - if (btcNodeList.isEmpty()) { - log.warn("Custom nodes is set but no valid nodes are provided. We fall back to provided nodes option."); - preferences.setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.PROVIDED.ordinal()); - } - } - - switch (BitcoinNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]) { - case CUSTOM: - // We have set the btcNodeList already above - walletConfig.setMinBroadcastConnections((int) Math.ceil(btcNodeList.size() * 0.5)); - // If Tor is set we usually only use onion nodes, but if user provides mixed clear net and onion nodes we want to use both - useCustomNodes = true; - break; - case PUBLIC: - // We keep the empty btcNodeList - walletConfig.setMinBroadcastConnections((int) Math.floor(DEFAULT_CONNECTIONS * 0.8)); - break; - default: - case PROVIDED: - btcNodeList = bitcoinNodes.getProvidedBtcNodes(); - // We require only 4 nodes instead of 7 (for 9 max connections) because our provided nodes - // are more reliable than random public nodes. - walletConfig.setMinBroadcastConnections(4); - break; - } - - List peerAddressList = new ArrayList<>(); - final boolean useTorForBitcoinJ = socks5Proxy != null; - // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at - // exit nodes with bloom filters. - if (useTorForBitcoinJ) { - btcNodeList.stream() - .filter(BitcoinNodes.BtcNode::hasOnionAddress) - .forEach(btcNode -> { - // no DNS lookup for onion addresses - log.info("We add a onion node. btcNode={}", btcNode); - final String onionAddress = checkNotNull(btcNode.getOnionAddress()); - try { - // OnionCat.onionHostToInetAddress converts onion to ipv6 representation - // inetAddress is not used but required for wallet persistence. Throws nullPointer if not set. - final InetAddress inetAddress = OnionCat.onionHostToInetAddress(onionAddress); - final PeerAddress peerAddress = new PeerAddress(onionAddress, btcNode.getPort()); - peerAddress.setAddr(inetAddress); - peerAddressList.add(peerAddress); - } catch (UnknownHostException e) { - log.error("OnionCat.onionHostToInetAddress() failed with btcNode={}, error={}", btcNode.toString(), e.toString()); - e.printStackTrace(); - } - }); - if (useAllProvidedNodes || useCustomNodes) { - // We also use the clear net nodes (used for monitor) - btcNodeList.stream() - .filter(BitcoinNodes.BtcNode::hasClearNetAddress) - .forEach(btcNode -> { - try { - // We use DnsLookupTor to not leak with DNS lookup - // Blocking call. takes about 600 ms ;-( - InetSocketAddress address = new InetSocketAddress(DnsLookupTor.lookup(socks5Proxy, btcNode.getHostNameOrAddress()), btcNode.getPort()); - log.info("We add a clear net node (tor is used) with InetAddress={}, btcNode={}", address.getAddress(), btcNode); - peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); - } catch (Exception e) { - if (btcNode.getAddress() != null) { - log.warn("Dns lookup failed. We try with provided IP address. BtcNode: {}", btcNode); - try { - InetSocketAddress address = new InetSocketAddress(DnsLookupTor.lookup(socks5Proxy, btcNode.getAddress()), btcNode.getPort()); - log.info("We add a clear net node (tor is used) with InetAddress={}, BtcNode={}", address.getAddress(), btcNode); - peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); - } catch (Exception e2) { - log.warn("Dns lookup failed for BtcNode: {}", btcNode); - } - } else { - log.warn("Dns lookup failed. No IP address is provided. BtcNode: {}", btcNode); - } - } - }); - } - } else { - btcNodeList.stream() - .filter(BitcoinNodes.BtcNode::hasClearNetAddress) - .forEach(btcNode -> { - try { - InetSocketAddress address = new InetSocketAddress(btcNode.getHostNameOrAddress(), btcNode.getPort()); - log.info("We add a clear net node (no tor is used) with host={}, btcNode.getPort()={}", btcNode.getHostNameOrAddress(), btcNode.getPort()); - peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); - } catch (Throwable t) { - if (btcNode.getAddress() != null) { - log.warn("Dns lookup failed. We try with provided IP address. BtcNode: {}", btcNode); - try { - InetSocketAddress address = new InetSocketAddress(btcNode.getAddress(), btcNode.getPort()); - log.info("We add a clear net node (no tor is used) with host={}, btcNode.getPort()={}", btcNode.getHostNameOrAddress(), btcNode.getPort()); - peerAddressList.add(new PeerAddress(address.getAddress(), address.getPort())); - } catch (Throwable t2) { - log.warn("Failed to create InetSocketAddress from btcNode {}", btcNode); - } - } else { - log.warn("Dns lookup failed. No IP address is provided. BtcNode: {}", btcNode); - } - } - }); - } - - if (!peerAddressList.isEmpty()) { - final PeerAddress[] peerAddresses = peerAddressList.toArray(new PeerAddress[peerAddressList.size()]); - log.info("You connect with peerAddresses: " + peerAddressList.toString()); - walletConfig.setPeerNodes(peerAddresses); - } else if (useTorForBitcoinJ) { - if (params == MainNetParams.get()) - log.warn("You use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + - "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use the provided nodes."); - // SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time. - walletConfig.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode)); - } else { - log.warn("You don't use gtor and use the public Bitcoin network and are exposed to privacy issues caused by the broken bloom filters." + - "See https://bisq.network/blog/privacy-in-bitsquare/ for more info. It is recommended to use Tor and the provided nodes."); - } } From a6341ec7f1f251665dc53a084a608bed72150fc3 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 6 Feb 2018 19:20:41 +0100 Subject: [PATCH 25/62] Test parseBlocks and BsqBlockChain population --- .../core/dao/blockchain/parse/BsqParser.java | 2 + .../dao/blockchain/parse/BsqParserTest.java | 108 +++++++++++++++++- 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java index d4949b0ba57..22b963cf36f 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java @@ -102,6 +102,7 @@ void parseBsqBlock(BsqBlock bsqBlock, // Parsing with data requested from bsqBlockchainService /////////////////////////////////////////////////////////////////////////////////////////// + @VisibleForTesting void parseBlocks(int startBlockHeight, int chainHeadHeight, int genesisBlockHeight, @@ -112,6 +113,7 @@ void parseBlocks(int startBlockHeight, for (int blockHeight = startBlockHeight; blockHeight <= chainHeadHeight; blockHeight++) { long startTs = System.currentTimeMillis(); Block btcdBlock = rpcService.requestBlock(blockHeight); + List bsqTxsInBlock = findBsqTxsInBlock(btcdBlock, genesisBlockHeight, genesisTxId); diff --git a/core/src/test/java/io/bisq/core/dao/blockchain/parse/BsqParserTest.java b/core/src/test/java/io/bisq/core/dao/blockchain/parse/BsqParserTest.java index 29a065e92cf..e4c93d5adf6 100644 --- a/core/src/test/java/io/bisq/core/dao/blockchain/parse/BsqParserTest.java +++ b/core/src/test/java/io/bisq/core/dao/blockchain/parse/BsqParserTest.java @@ -1,20 +1,25 @@ package io.bisq.core.dao.blockchain.parse; +import com.neemre.btcdcli4j.core.BitcoindException; +import com.neemre.btcdcli4j.core.CommunicationException; +import com.neemre.btcdcli4j.core.domain.Block; import io.bisq.common.proto.persistable.PersistenceProtoResolver; +import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException; +import io.bisq.core.dao.blockchain.exceptions.BsqBlockchainException; import io.bisq.core.dao.blockchain.vo.*; import mockit.Expectations; import mockit.Injectable; import mockit.Tested; import mockit.integration.junit4.JMockit; +import org.bitcoinj.core.Coin; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Optional; +import java.math.BigDecimal; +import java.util.*; +import static java.util.Arrays.asList; import static org.junit.Assert.*; @RunWith(JMockit.class) @@ -75,4 +80,99 @@ public void testIsBsqTx() { // Third time there is BSQ in the second txout assertTrue(bsqParser.isBsqTx(height, tx)); } + + @Test + public void testParseBlocks() throws BitcoindException, CommunicationException, BlockNotConnectingException, BsqBlockchainException { + // Setup blocks to test, starting before genesis + // Only the transactions related to bsq are relevant, no checks are done on correctness of blocks or other txs + // so hashes and most other data don't matter + long time = new Date().getTime(); + int genesisHeight = 200; + int startHeight = 199; + int headHeight = 201; + Coin issuance = Coin.parseCoin("25"); + + // Blockhashes + String bh199 = "blockhash199"; + String bh200 = "blockhash200"; + String bh201 = "blockhash201"; + + // Block 199 + String cbId199 = "cbid199"; + Tx cbTx199 = new Tx(new TxVo(cbId199, 199, bh199, time), + new ArrayList(), + asList(new TxOutput(0, 25, cbId199, null, null, null, 199))); + Block block199 = new Block(bh199, 10, 10, 199, 2, "root", asList(cbId199), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", "previousBlockHash", bh200); + + // Genesis Block + String cbId200 = "cbid200"; + String genesisId = "genesisId"; + Tx cbTx200 = new Tx(new TxVo(cbId200, 200, bh200, time), + new ArrayList(), + asList(new TxOutput(0, 25, cbId200, null, null, null, 200))); + Tx genesisTx = new Tx(new TxVo(genesisId, 200, bh200, time), + asList(new TxInput("someoldtx", 0)), + asList(new TxOutput(0, issuance.getValue(), genesisId, null, null, null, 200))); + Block block200 = new Block(bh200, 10, 10, 200, 2, "root", asList(cbId200, genesisId), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", bh199, bh201); + + // Block 201 + // Make a bsq transaction + String cbId201 = "cbid201"; + String bsqTx1Id = "bsqtx1"; + long bsqTx1Value1 = Coin.parseCoin("24").getValue(); + long bsqTx1Value2 = Coin.parseCoin("0.4").getValue(); + Tx cbTx201 = new Tx(new TxVo(cbId201, 201, bh201, time), + new ArrayList(), + asList(new TxOutput(0, 25, cbId201, null, null, null, 201))); + Tx bsqTx1 = new Tx(new TxVo(bsqTx1Id, 201, bh201, time), + asList(new TxInput(genesisId, 0)), + asList(new TxOutput(0, bsqTx1Value1, bsqTx1Id, null, null, null, 201), + new TxOutput(1, bsqTx1Value2, bsqTx1Id, null, null, null, 201))); + Block block201 = new Block(bh201, 10, 10, 201, 2, "root", asList(cbId201, bsqTx1Id), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", bh200, "nextBlockHash"); + + new Expectations(rpcService) {{ + rpcService.requestBlock(199); + result = block199; + rpcService.requestBlock(200); + result = block200; + rpcService.requestBlock(201); + result = block201; + + rpcService.requestTx(cbId199, 199); + result = cbTx199; + rpcService.requestTx(cbId200, genesisHeight); + result = cbTx200; + rpcService.requestTx(genesisId, genesisHeight); + result = genesisTx; + rpcService.requestTx(cbId201, 201); + result = cbTx201; + rpcService.requestTx(bsqTx1Id, 201); + result = bsqTx1; + }}; + + // Running parseBlocks to build the bsq blockchain + bsqParser.parseBlocks(startHeight, headHeight, genesisHeight, genesisId, block -> { + }); + + // Verify that the the genesis tx has been added to the bsq blockchain with the correct issuance amount + assertTrue(bsqBlockChain.getGenesisTx() == genesisTx); + assertTrue(bsqBlockChain.getIssuedAmount().getValue() == issuance.getValue()); + + // And that other txs are not added + assertFalse(bsqBlockChain.containsTx(cbId199)); + assertFalse(bsqBlockChain.containsTx(cbId200)); + assertFalse(bsqBlockChain.containsTx(cbId201)); + + // But bsq txs are added + assertTrue(bsqBlockChain.containsTx(bsqTx1Id)); + TxOutput bsqOut1 = bsqBlockChain.getSpendableTxOutput(bsqTx1Id, 0).get(); + assertTrue(bsqOut1.isUnspent()); + assertTrue(bsqOut1.getValue() == bsqTx1Value1); + TxOutput bsqOut2 = bsqBlockChain.getSpendableTxOutput(bsqTx1Id, 1).get(); + assertTrue(bsqOut2.isUnspent()); + assertTrue(bsqOut2.getValue() == bsqTx1Value2); + assertFalse(bsqBlockChain.isTxOutputSpendable(genesisId, 0)); + assertTrue(bsqBlockChain.isTxOutputSpendable(bsqTx1Id, 0)); + assertTrue(bsqBlockChain.isTxOutputSpendable(bsqTx1Id, 1)); + } } From 8ec499888c2afdedfab706f8e7ef81c07c9b1803 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 6 Feb 2018 19:25:48 +0100 Subject: [PATCH 26/62] Simplyfy BsqParser test case setup --- .../bisq/core/dao/blockchain/parse/BsqParserTest.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/test/java/io/bisq/core/dao/blockchain/parse/BsqParserTest.java b/core/src/test/java/io/bisq/core/dao/blockchain/parse/BsqParserTest.java index e4c93d5adf6..05222c6aada 100644 --- a/core/src/test/java/io/bisq/core/dao/blockchain/parse/BsqParserTest.java +++ b/core/src/test/java/io/bisq/core/dao/blockchain/parse/BsqParserTest.java @@ -49,13 +49,10 @@ public void testIsBsqTx() { int height = 200; String hash = "abc123"; long time = new Date().getTime(); - List inputs = new ArrayList<>(); - inputs.add(new TxInput("tx1", 0)); - inputs.add(new TxInput("tx1", 1)); - List outputs = new ArrayList<>(); - outputs.add(new TxOutput(0, 101, "tx1", null, null, null, height)); - TxVo txVo = new TxVo("vo", height, hash, time); - Tx tx = new Tx(txVo, inputs, outputs); + Tx tx = new Tx(new TxVo("vo", height, hash, time), + asList(new TxInput("tx1", 0), + new TxInput("tx1", 1)), + asList(new TxOutput(0, 101, "tx1", null, null, null, height))); // Return one spendable txoutputs with value, for three test cases // 1) - null, 0 -> not BSQ transaction From 6c250b135a8831491024a0731c26576621751096 Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 23:09:16 +0400 Subject: [PATCH 27/62] Minor refactorings in new code --- .../core/btc/wallet/BtcNodeConverter.java | 1 - .../io/bisq/core/btc/wallet/BtcNodes.java | 4 --- .../btc/wallet/PeerAddressesRepository.java | 13 ++++--- .../core/btc/wallet/WalletNetworkConfig.java | 36 ++++++++++--------- .../btc/wallet/WalletSetupPreferences.java | 22 +++++++----- .../io/bisq/core/btc/wallet/WalletsSetup.java | 13 ++++--- 6 files changed, 49 insertions(+), 40 deletions(-) delete mode 100644 core/src/main/java/io/bisq/core/btc/wallet/BtcNodes.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java index 30fbc4e4650..14622a291d0 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java @@ -14,7 +14,6 @@ import java.net.UnknownHostException; import java.util.Objects; -// TODO refactor class BtcNodeConverter { private static final Logger log = LoggerFactory.getLogger(BtcNodeConverter.class); diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodes.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodes.java deleted file mode 100644 index 226e1ec3476..00000000000 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodes.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.bisq.core.btc.wallet; - -class BtcNodes { -} diff --git a/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java b/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java index 626be6fe330..f411295979a 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java @@ -20,8 +20,11 @@ class PeerAddressesRepository { this.nodes = nodes; } - // TODO remove flag - List getPeerAddresses(@Nullable Socks5Proxy proxy, boolean isUseProxifiedClearNodes) { + PeerAddressesRepository(List nodes) { + this(new BtcNodeConverter(), nodes); + } + + List getPeerAddresses(@Nullable Socks5Proxy proxy, boolean isUseClearNodesWithProxies) { List result; // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at // exit nodes with bloom filters. @@ -29,9 +32,9 @@ List getPeerAddresses(@Nullable Socks5Proxy proxy, boolean isUsePro List onionHosts = getOnionHosts(); result = new ArrayList<>(onionHosts); - if (isUseProxifiedClearNodes) { + if (isUseClearNodesWithProxies) { // We also use the clear net nodes (used for monitor) - List torAddresses = getProxifiedClearNodes(proxy); + List torAddresses = getClearNodesBehindProxy(proxy); result.addAll(torAddresses); } } else { @@ -54,7 +57,7 @@ private List getOnionHosts() { .collect(Collectors.toList()); } - private List getProxifiedClearNodes(Socks5Proxy proxy) { + private List getClearNodesBehindProxy(Socks5Proxy proxy) { return nodes.stream() .filter(BtcNode::hasClearNetAddress) .flatMap(node -> nullableAsStream(converter.convertWithTor(node, proxy))) diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java index 73559b80b3a..76f5d5a0e9a 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java @@ -15,33 +15,35 @@ class WalletNetworkConfig { private static final Logger log = LoggerFactory.getLogger(WalletNetworkConfig.class); @Nullable - private final Socks5Proxy socks5Proxy; - private final WalletConfig walletConfig; - private final NetworkParameters params; + private final Socks5Proxy proxy; + private final WalletConfig delegate; + private final NetworkParameters parameters; private final int socks5DiscoverMode; - WalletNetworkConfig(WalletConfig walletConfig, NetworkParameters params, int socks5DiscoverMode, - @Nullable Socks5Proxy socks5Proxy) { - this.walletConfig = walletConfig; - this.params = params; + WalletNetworkConfig(WalletConfig delegate, NetworkParameters parameters, int socks5DiscoverMode, + @Nullable Socks5Proxy proxy) { + this.delegate = delegate; + this.parameters = parameters; this.socks5DiscoverMode = socks5DiscoverMode; - this.socks5Proxy = socks5Proxy; + this.proxy = proxy; } - void setPeers(List peers) { + void proposePeers(List peers) { if (!peers.isEmpty()) { log.info("You connect with peerAddresses: {}", peers); PeerAddress[] peerAddresses = peers.toArray(new PeerAddress[peers.size()]); - walletConfig.setPeerNodes(peerAddresses); - } else if (socks5Proxy != null) { - MainNetParams mainNetParams = MainNetParams.get(); - if (log.isWarnEnabled() && params.equals(mainNetParams)) { - log.warn("You use the public Bitcoin network and are exposed to privacy issues " + - "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + - "for more info. It is recommended to use the provided nodes."); + delegate.setPeerNodes(peerAddresses); + } else if (proxy != null) { + if (log.isWarnEnabled()) { + MainNetParams mainNetParams = MainNetParams.get(); + if (parameters.equals(mainNetParams)) { + log.warn("You use the public Bitcoin network and are exposed to privacy issues " + + "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + + "for more info. It is recommended to use the provided nodes."); + } } // SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time. - walletConfig.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode)); + delegate.setDiscovery(new Socks5MultiDiscovery(proxy, parameters, socks5DiscoverMode)); } else { log.warn("You don't use tor and use the public Bitcoin network and are exposed to privacy issues " + "caused by the broken bloom filters. See https://bisq.network/blog/privacy-in-bitsquare/ " + diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java index fe1cb7a9b4c..dbc5349ed70 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java @@ -10,6 +10,7 @@ import java.util.Collections; import java.util.List; +import java.util.Set; import static io.bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM; import static io.bisq.core.btc.wallet.WalletsSetup.DEFAULT_CONNECTIONS; @@ -24,19 +25,20 @@ class WalletSetupPreferences { this.preferences = preferences; } - List selectPreferredNodes(BitcoinNodes bitcoinNodes) { + List selectPreferredNodes(BitcoinNodes nodes) { List result; BitcoinNodesOption nodesOption = BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]; switch (nodesOption) { case CUSTOM: - result = BitcoinNodes.toBtcNodesList(Utilities.commaSeparatedListToSet(preferences.getBitcoinNodes(), - false)); + String bitcoinNodes = preferences.getBitcoinNodes(); + Set distinctNodes = Utilities.commaSeparatedListToSet(bitcoinNodes, false); + result = BitcoinNodes.toBtcNodesList(distinctNodes); if (result.isEmpty()) { log.warn("Custom nodes is set but no valid nodes are provided. " + "We fall back to provided nodes option."); preferences.setBitcoinNodesOptionOrdinal(BitcoinNodesOption.PROVIDED.ordinal()); - result = bitcoinNodes.getProvidedBtcNodes(); + result = nodes.getProvidedBtcNodes(); } break; case PUBLIC: @@ -44,7 +46,7 @@ List selectPreferredNodes(BitcoinNodes bitcoinNodes) { break; case PROVIDED: default: - result = bitcoinNodes.getProvidedBtcNodes(); + result = nodes.getProvidedBtcNodes(); break; } @@ -55,26 +57,28 @@ boolean isUseCustomNodes() { return CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); } - void calculateMinBroadcastConnections(WalletConfig walletConfig, List nodes) { + int calculateMinBroadcastConnections(List nodes) { BitcoinNodesOption nodesOption = BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]; + int result; switch (nodesOption) { case CUSTOM: // We have set the nodes already above - walletConfig.setMinBroadcastConnections((int) Math.ceil(nodes.size() * 0.5)); + result = (int) Math.ceil(nodes.size() * 0.5); // If Tor is set we usually only use onion nodes, // but if user provides mixed clear net and onion nodes we want to use both break; case PUBLIC: // We keep the empty nodes - walletConfig.setMinBroadcastConnections((int) Math.floor(DEFAULT_CONNECTIONS * 0.8)); + result = (int) Math.floor(DEFAULT_CONNECTIONS * 0.8); break; case PROVIDED: default: // We require only 4 nodes instead of 7 (for 9 max connections) because our provided nodes // are more reliable than random public nodes. - walletConfig.setMinBroadcastConnections(4); + result = 4; break; } + return result; } } diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java index 0e09e0b3994..9edbfaa3857 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java @@ -290,12 +290,17 @@ private void configPeerNodesForLocalHostBitcoinNode() { private void configPeerNodes(@Nullable Socks5Proxy proxy) { WalletSetupPreferences walletSetupPreferences = new WalletSetupPreferences(preferences); + List nodes = walletSetupPreferences.selectPreferredNodes(bitcoinNodes); - walletSetupPreferences.calculateMinBroadcastConnections(walletConfig, nodes); - List peers = new PeerAddressesRepository(new BtcNodeConverter(), nodes).getPeerAddresses(proxy, - useAllProvidedNodes || walletSetupPreferences.isUseCustomNodes()); - new WalletNetworkConfig(walletConfig, params, socks5DiscoverMode, proxy).setPeers(peers); + int minBroadcastConnections = walletSetupPreferences.calculateMinBroadcastConnections(nodes); + walletConfig.setMinBroadcastConnections(minBroadcastConnections); + + PeerAddressesRepository repository = new PeerAddressesRepository(nodes); + boolean isUseClearNodesWithProxies = (useAllProvidedNodes || walletSetupPreferences.isUseCustomNodes()); + List peers = repository.getPeerAddresses(proxy, isUseClearNodesWithProxies); + WalletNetworkConfig networkConfig = new WalletNetworkConfig(walletConfig, params, socks5DiscoverMode, proxy); + networkConfig.proposePeers(peers); } From cd05c8767c0347405d7f569536997aa68ebd109d Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 23:27:31 +0400 Subject: [PATCH 28/62] Implemented unit test for WalletNetworkConfig --- .../core/btc/wallet/WalletNetworkConfig.java | 17 ++++++ .../btc/wallet/WalletNetworkConfigTest.java | 54 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 core/src/test/java/io/bisq/core/btc/wallet/WalletNetworkConfigTest.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java index 76f5d5a0e9a..b0ffbda92ef 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletNetworkConfig.java @@ -1,3 +1,20 @@ +/* + * 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 io.bisq.core.btc.wallet; import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; diff --git a/core/src/test/java/io/bisq/core/btc/wallet/WalletNetworkConfigTest.java b/core/src/test/java/io/bisq/core/btc/wallet/WalletNetworkConfigTest.java new file mode 100644 index 00000000000..142a57b1b18 --- /dev/null +++ b/core/src/test/java/io/bisq/core/btc/wallet/WalletNetworkConfigTest.java @@ -0,0 +1,54 @@ +package io.bisq.core.btc.wallet; + +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import io.bisq.network.Socks5MultiDiscovery; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.PeerAddress; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +public class WalletNetworkConfigTest { + private static final int MODE = 0; + + private WalletConfig delegate; + + @Before + public void setUp() { + delegate = mock(WalletConfig.class); + } + + @Test + public void testProposePeersWhenProxyPresentAndNoPeers() { + WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE, + mock(Socks5Proxy.class)); + config.proposePeers(Collections.emptyList()); + + verify(delegate, never()).setPeerNodes(any()); + verify(delegate).setDiscovery(any(Socks5MultiDiscovery.class)); + } + + @Test + public void testProposePeersWhenProxyNotPresentAndNoPeers() { + WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE, + null); + config.proposePeers(Collections.emptyList()); + + verify(delegate, never()).setDiscovery(any(Socks5MultiDiscovery.class)); + verify(delegate, never()).setPeerNodes(any()); + } + + @Test + public void testProposePeersWhenPeersPresent() { + WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE, + null); + config.proposePeers(Collections.singletonList(mock(PeerAddress.class))); + + verify(delegate, never()).setDiscovery(any(Socks5MultiDiscovery.class)); + verify(delegate).setPeerNodes(any()); + } +} From 862de72666bfb9e871f5f98130df0e00fb6b971a Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Tue, 6 Feb 2018 23:38:00 +0400 Subject: [PATCH 29/62] Implemented unit test for WalletSetupPreferences --- .../btc/wallet/WalletSetupPreferences.java | 17 +++++++ .../wallet/WalletSetupPreferencesTest.java | 45 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 core/src/test/java/io/bisq/core/btc/wallet/WalletSetupPreferencesTest.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java index dbc5349ed70..29b460f2e91 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletSetupPreferences.java @@ -1,3 +1,20 @@ +/* + * 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 io.bisq.core.btc.wallet; import io.bisq.common.util.Utilities; diff --git a/core/src/test/java/io/bisq/core/btc/wallet/WalletSetupPreferencesTest.java b/core/src/test/java/io/bisq/core/btc/wallet/WalletSetupPreferencesTest.java new file mode 100644 index 00000000000..2d114b7dba2 --- /dev/null +++ b/core/src/test/java/io/bisq/core/btc/wallet/WalletSetupPreferencesTest.java @@ -0,0 +1,45 @@ +package io.bisq.core.btc.wallet; + +import io.bisq.core.btc.BitcoinNodes; +import io.bisq.core.btc.BitcoinNodes.BtcNode; +import io.bisq.core.user.Preferences; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.List; + +import static io.bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM; +import static io.bisq.core.btc.BitcoinNodes.BitcoinNodesOption.PUBLIC; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(Preferences.class) +public class WalletSetupPreferencesTest { + @Test + public void testSelectPreferredNodesWhenPublicOption() { + Preferences delegate = mock(Preferences.class); + when(delegate.getBitcoinNodesOptionOrdinal()).thenReturn(PUBLIC.ordinal()); + + WalletSetupPreferences preferences = new WalletSetupPreferences(delegate); + List nodes = preferences.selectPreferredNodes(mock(BitcoinNodes.class)); + + assertTrue(nodes.isEmpty()); + } + + @Test + public void testSelectPreferredNodesWhenCustomOption() { + Preferences delegate = mock(Preferences.class); + when(delegate.getBitcoinNodesOptionOrdinal()).thenReturn(CUSTOM.ordinal()); + when(delegate.getBitcoinNodes()).thenReturn("aaa.onion,bbb.onion"); + + WalletSetupPreferences preferences = new WalletSetupPreferences(delegate); + List nodes = preferences.selectPreferredNodes(mock(BitcoinNodes.class)); + + assertEquals(2, nodes.size()); + } +} From f9ab2614a80d2895770930832856269fd76709a9 Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Wed, 7 Feb 2018 00:15:38 +0400 Subject: [PATCH 30/62] Implemented unit test for PeerAddressRepository --- .../btc/wallet/PeerAddressesRepository.java | 17 ++++ .../wallet/PeerAddressesRepositoryTest.java | 83 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 core/src/test/java/io/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java b/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java index f411295979a..c2dac3e2188 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/PeerAddressesRepository.java @@ -1,3 +1,20 @@ +/* + * 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 io.bisq.core.btc.wallet; import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; diff --git a/core/src/test/java/io/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java b/core/src/test/java/io/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java new file mode 100644 index 00000000000..95050ccd2fa --- /dev/null +++ b/core/src/test/java/io/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java @@ -0,0 +1,83 @@ +package io.bisq.core.btc.wallet; + +import com.google.common.collect.Lists; +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import io.bisq.core.btc.BitcoinNodes.BtcNode; +import org.bitcoinj.core.PeerAddress; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +public class PeerAddressesRepositoryTest { + @Test + public void testGetPeerAddressesWhenClearNodes() { + BtcNode node = mock(BtcNode.class); + when(node.hasClearNetAddress()).thenReturn(true); + + BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS); + PeerAddressesRepository repository = new PeerAddressesRepository(converter, + Collections.singletonList(node)); + + List peers = repository.getPeerAddresses(null, false); + + assertFalse(peers.isEmpty()); + } + + @Test + public void testGetPeerAddressesWhenConverterReturnsNull() { + BtcNodeConverter converter = mock(BtcNodeConverter.class); + when(converter.convertClearNode(any())).thenReturn(null); + + BtcNode node = mock(BtcNode.class); + when(node.hasClearNetAddress()).thenReturn(true); + + PeerAddressesRepository repository = new PeerAddressesRepository(converter, + Collections.singletonList(node)); + + List peers = repository.getPeerAddresses(null, false); + + verify(converter).convertClearNode(any()); + assertTrue(peers.isEmpty()); + } + + @Test + public void testGetPeerAddressesWhenProxyAndClearNodes() { + BtcNode node = mock(BtcNode.class); + when(node.hasClearNetAddress()).thenReturn(true); + + BtcNode onionNode = mock(BtcNode.class); + when(node.hasOnionAddress()).thenReturn(true); + + BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS); + PeerAddressesRepository repository = new PeerAddressesRepository(converter, + Lists.newArrayList(node, onionNode)); + + List peers = repository.getPeerAddresses(mock(Socks5Proxy.class), true); + + assertEquals(2, peers.size()); + } + + @Test + public void testGetPeerAddressesWhenOnionNodesOnly() { + BtcNode node = mock(BtcNode.class); + when(node.hasClearNetAddress()).thenReturn(true); + + BtcNode onionNode = mock(BtcNode.class); + when(node.hasOnionAddress()).thenReturn(true); + + BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS); + PeerAddressesRepository repository = new PeerAddressesRepository(converter, + Lists.newArrayList(node, onionNode)); + + List peers = repository.getPeerAddresses(mock(Socks5Proxy.class), false); + + assertEquals(1, peers.size()); + } +} From 707fd5c9869837a7877f7db29170752c979acb6d Mon Sep 17 00:00:00 2001 From: Sergey Rozhnov Date: Wed, 7 Feb 2018 01:19:14 +0400 Subject: [PATCH 31/62] Implemented unit test for BtcNodeConverter --- .../core/btc/wallet/BtcNodeConverter.java | 44 ++++++++++- .../core/btc/wallet/BtcNodeConverterTest.java | 76 +++++++++++++++++++ 2 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/io/bisq/core/btc/wallet/BtcNodeConverterTest.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java index 14622a291d0..b15aa5312ac 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcNodeConverter.java @@ -1,7 +1,25 @@ +/* + * 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 io.bisq.core.btc.wallet; import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; import io.bisq.core.btc.BitcoinNodes.BtcNode; +import io.bisq.network.DnsLookupException; import io.bisq.network.DnsLookupTor; import org.bitcoinj.core.PeerAddress; import org.bitcoinj.net.OnionCat; @@ -17,6 +35,16 @@ class BtcNodeConverter { private static final Logger log = LoggerFactory.getLogger(BtcNodeConverter.class); + private final Facade facade; + + BtcNodeConverter() { + this.facade = new Facade(); + } + + BtcNodeConverter(Facade facade) { + this.facade = facade; + } + @Nullable PeerAddress convertOnionHost(BtcNode node) { // no DNS lookup for onion addresses @@ -24,7 +52,7 @@ PeerAddress convertOnionHost(BtcNode node) { try { // OnionCat.onionHostToInetAddress converts onion to ipv6 representation // inetAddress is not used but required for wallet persistence. Throws nullPointer if not set. - InetAddress inetAddress = OnionCat.onionHostToInetAddress(onionAddress); + InetAddress inetAddress = facade.onionHostToInetAddress(onionAddress); PeerAddress result = new PeerAddress(onionAddress, node.getPort()); result.setAddr(inetAddress); return result; @@ -67,11 +95,11 @@ PeerAddress convertWithTor(BtcNode node, Socks5Proxy proxy) { } @Nullable - private static PeerAddress create(Socks5Proxy proxy, String host, int port) { + private PeerAddress create(Socks5Proxy proxy, String host, int port) { try { // We use DnsLookupTor to not leak with DNS lookup // Blocking call. takes about 600 ms ;-( - InetAddress lookupAddress = DnsLookupTor.lookup(proxy, host); + InetAddress lookupAddress = facade.torLookup(proxy, host); InetSocketAddress address = new InetSocketAddress(lookupAddress, port); return new PeerAddress(address.getAddress(), address.getPort()); } catch (Exception e) { @@ -90,5 +118,15 @@ private static PeerAddress create(String hostName, int port) { return null; } } + + static class Facade { + InetAddress onionHostToInetAddress(String onionAddress) throws UnknownHostException { + return OnionCat.onionHostToInetAddress(onionAddress); + } + + InetAddress torLookup(Socks5Proxy proxy, String host) throws DnsLookupException { + return DnsLookupTor.lookup(proxy, host); + } + } } diff --git a/core/src/test/java/io/bisq/core/btc/wallet/BtcNodeConverterTest.java b/core/src/test/java/io/bisq/core/btc/wallet/BtcNodeConverterTest.java new file mode 100644 index 00000000000..84feafd9f4c --- /dev/null +++ b/core/src/test/java/io/bisq/core/btc/wallet/BtcNodeConverterTest.java @@ -0,0 +1,76 @@ +package io.bisq.core.btc.wallet; + +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import io.bisq.core.btc.BitcoinNodes.BtcNode; +import io.bisq.core.btc.wallet.BtcNodeConverter.Facade; +import io.bisq.network.DnsLookupException; +import org.bitcoinj.core.PeerAddress; +import org.junit.Test; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BtcNodeConverterTest { + @Test + public void testConvertOnionHost() throws UnknownHostException { + BtcNode node = mock(BtcNode.class); + when(node.getOnionAddress()).thenReturn("aaa.onion"); + + InetAddress inetAddress = mock(InetAddress.class); + + Facade facade = mock(Facade.class); + when(facade.onionHostToInetAddress(any())).thenReturn(inetAddress); + + PeerAddress peerAddress = new BtcNodeConverter(facade).convertOnionHost(node); + // noinspection ConstantConditions + assertEquals(inetAddress, peerAddress.getAddr()); + } + + @Test + public void testConvertOnionHostOnFailure() throws UnknownHostException { + BtcNode node = mock(BtcNode.class); + when(node.getOnionAddress()).thenReturn("aaa.onion"); + + Facade facade = mock(Facade.class); + when(facade.onionHostToInetAddress(any())).thenThrow(UnknownHostException.class); + + PeerAddress peerAddress = new BtcNodeConverter(facade).convertOnionHost(node); + assertNull(peerAddress); + } + + @Test + public void testConvertClearNode() { + final String ip = "192.168.0.1"; + + BtcNode node = mock(BtcNode.class); + when(node.getHostNameOrAddress()).thenReturn(ip); + + PeerAddress peerAddress = new BtcNodeConverter().convertClearNode(node); + // noinspection ConstantConditions + InetAddress inetAddress = peerAddress.getAddr(); + assertEquals(ip, inetAddress.getHostName()); + } + + @Test + public void testConvertWithTor() throws DnsLookupException { + InetAddress expected = mock(InetAddress.class); + + Facade facade = mock(Facade.class); + when(facade.torLookup(any(), anyString())).thenReturn(expected); + + BtcNode node = mock(BtcNode.class); + when(node.getHostNameOrAddress()).thenReturn("aaa.onion"); + + PeerAddress peerAddress = new BtcNodeConverter(facade).convertWithTor(node, mock(Socks5Proxy.class)); + + // noinspection ConstantConditions + assertEquals(expected, peerAddress.getAddr()); + } +} From 65d2c03eac0cca40f1df09003243d81d208451a2 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 7 Feb 2018 16:28:24 +0100 Subject: [PATCH 32/62] Change path after completed trade --- common/src/main/resources/i18n/displayStrings.properties | 1 + .../src/main/resources/i18n/displayStrings_de.properties | 1 + .../pendingtrades/steps/buyer/BuyerStep4View.java | 8 ++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 99ce4e5a7f1..3dc2bbfae8a 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -1514,6 +1514,7 @@ navigation.arbitratorSelection=\"Arbitrator selection\" navigation.funds.availableForWithdrawal=\"Fund/Send funds\" navigation.portfolio.myOpenOffers=\"Portfolio/My open offers\" navigation.portfolio.pending=\"Portfolio/Open trades\" +navigation.portfolio.closedTrades=\"Portfolio/History\" navigation.funds.depositFunds=\"Funds/Receive funds\" navigation.settings.preferences=\"Settings/Preferences\" navigation.funds.transactions=\"Funds/Transactions\" diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 270fcdeea71..dff497a7b67 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -1275,6 +1275,7 @@ navigation.arbitratorSelection=\"Vermittlerauswahl\" navigation.funds.availableForWithdrawal=\"Gelder/Gelder senden\" navigation.portfolio.myOpenOffers=\"Portfolio/Meine offenen Angebote\" navigation.portfolio.pending=\"Portfolio/Offene Händel\" +navigation.portfolio.closedTrades=\"Portfolio/Verlauf\" navigation.funds.depositFunds=\"Gelder/Gelder erhalten\" navigation.settings.preferences=\"Einstellungen/Voreinstellungen\" navigation.funds.transactions=\"Gelder/Transaktionen\" diff --git a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java index d33eee9f874..46114670eb6 100644 --- a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java +++ b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java @@ -36,10 +36,10 @@ import io.bisq.gui.components.InputTextField; import io.bisq.gui.components.TitledGroupBg; import io.bisq.gui.main.MainView; -import io.bisq.gui.main.funds.FundsView; -import io.bisq.gui.main.funds.transactions.TransactionsView; import io.bisq.gui.main.overlays.notifications.Notification; import io.bisq.gui.main.overlays.popups.Popup; +import io.bisq.gui.main.portfolio.PortfolioView; +import io.bisq.gui.main.portfolio.closedtrades.ClosedTradesView; import io.bisq.gui.main.portfolio.pendingtrades.PendingTradesViewModel; import io.bisq.gui.main.portfolio.pendingtrades.steps.TradeStepView; import io.bisq.gui.util.BSFormatter; @@ -286,8 +286,8 @@ private void handleTradeCompleted() { //noinspection unchecked new Popup<>().headLine(Res.get("portfolio.pending.step5_buyer.withdrawalCompleted.headline")) .feedback(Res.get("portfolio.pending.step5_buyer.withdrawalCompleted.msg")) - .actionButtonTextWithGoTo("navigation.funds.transactions") - .onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class)) + .actionButtonTextWithGoTo("navigation.portfolio.closedTrades") + .onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, PortfolioView.class, ClosedTradesView.class)) .dontShowAgainId(key) .show(); } From 55201fedba0084e1347bc5f0b0a4ac0fe3dd620f Mon Sep 17 00:00:00 2001 From: Hein Bloed Date: Thu, 8 Feb 2018 18:25:24 +0100 Subject: [PATCH 33/62] correct settings for p2shHeader --- .../java/io/bisq/gui/util/validation/params/ODNParams.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java b/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java index 644a54461a4..ebc14978f67 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java @@ -36,8 +36,8 @@ public static synchronized ODNParams get() { // We only use the properties needed for address validation public ODNParams() { super(); - addressHeader = 75; // networkVersion (0x4b) - p2shHeader = -1; // TODO ??privateKeyPrefix 203 (0xcb) tbd? + addressHeader = 75; + p2shHeader = 125; acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } From 00f6f8ea4b134b470ec97347f18bbeeeb7430250 Mon Sep 17 00:00:00 2001 From: Hein Bloed Date: Thu, 8 Feb 2018 18:26:45 +0100 Subject: [PATCH 34/62] added ODN to createAllSortedCryptoCurrenciesList --- common/src/main/java/io/bisq/common/locale/CurrencyUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index 9754f336662..7dde834b245 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -131,6 +131,7 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("NMC", "Namecoin")); result.add(new CryptoCurrency("NBT", "NuBits")); result.add(new CryptoCurrency("NXT", "Nxt")); + result.add(new CryptoCurrency("ODN", "Obsidian")); result.add(new CryptoCurrency("888", "OctoCoin")); result.add(new CryptoCurrency("PART", "Particl")); result.add(new CryptoCurrency("PASC", "Pascal Coin", true)); From 0b83d9de23d14bc7cbd53961069338693760c043 Mon Sep 17 00:00:00 2001 From: reverendus Date: Sat, 10 Feb 2018 11:34:40 +0100 Subject: [PATCH 35/62] Add DAI Token --- .../java/io/bisq/common/locale/CurrencyUtil.java | 1 + .../trade/statistics/TradeStatisticsManager.java | 1 + .../util/validation/AltCoinAddressValidator.java | 6 ++++++ .../validation/AltCoinAddressValidatorTest.java | 14 ++++++++++++++ 4 files changed, 22 insertions(+) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index f4461597321..5f7bb102754 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -102,6 +102,7 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("XCP", "Counterparty")); result.add(new CryptoCurrency("CREA", "Creativecoin")); result.add(new CryptoCurrency("XCN", "Cryptonite")); + result.add(new CryptoCurrency("DAI", "Dai Stablecoin", true)); result.add(new CryptoCurrency("DNET", "DarkNet")); if (!baseCurrencyCode.equals("DASH")) result.add(new CryptoCurrency("DASH", "Dash")); diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index 2840254245a..5b0270222f8 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -251,6 +251,7 @@ private void printAllCurrencyStats() { newlyAdded.add("BETR"); newlyAdded.add("MVT"); newlyAdded.add("REF"); + newlyAdded.add("DAI"); coinsWithValidator.addAll(newlyAdded); diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index 686897fd7d9..c1b23cc9bca 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -196,6 +196,12 @@ public ValidationResult validate(String input) { return regexTestFailed; else return new ValidationResult(true); + case "DAI": + // https://github.com/ethereum/web3.js/blob/master/lib/utils/utils.js#L403 + if (!input.matches("^(0x)?[0-9a-fA-F]{40}$")) + return regexTestFailed; + else + return new ValidationResult(true); case "PIVX": if (input.matches("^[D][a-km-zA-HJ-NP-Z1-9]{25,34}$")) { //noinspection ConstantConditions diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index 59b6791ac84..11b086a1696 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -128,6 +128,20 @@ public void testETH() { assertFalse(validator.validate("").isValid); } + @Test + public void testDAI() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("DAI"); + + assertTrue(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d164135398226").isValid); + assertTrue(validator.validate("2a65Aca4D5fC5B5C859090a6c34d164135398226").isValid); + + assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266").isValid); + assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d16413539822g").isValid); + assertFalse(validator.validate("2a65Aca4D5fC5B5C859090a6c34d16413539822g").isValid); + assertFalse(validator.validate("").isValid); + } + @Test public void testPIVX() { AltCoinAddressValidator validator = new AltCoinAddressValidator(); From fc622372340c59265ab364a086137f16d690fa1a Mon Sep 17 00:00:00 2001 From: Poompon Date: Sat, 10 Feb 2018 14:35:35 +0300 Subject: [PATCH 36/62] Add YENTEN [YTN] validaion --- .../validation/AltCoinAddressValidator.java | 2 + .../altcoins/YTNAddressValidator.java | 83 +++++++++++++++++++ .../AltCoinAddressValidatorTest.java | 16 ++++ 3 files changed, 101 insertions(+) create mode 100644 gui/src/main/java/io/bisq/gui/util/validation/altcoins/YTNAddressValidator.java diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index 686897fd7d9..966c59c2b06 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -438,6 +438,8 @@ public ValidationResult validate(String input) { return regexTestFailed; else return new ValidationResult(true); + case "YTN": + return YTNAddressValidator.ValidateAddress(input); // Add new coins at the end... default: diff --git a/gui/src/main/java/io/bisq/gui/util/validation/altcoins/YTNAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/altcoins/YTNAddressValidator.java new file mode 100644 index 00000000000..645e20e95f0 --- /dev/null +++ b/gui/src/main/java/io/bisq/gui/util/validation/altcoins/YTNAddressValidator.java @@ -0,0 +1,83 @@ +/* + * 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 io.bisq.gui.util.validation.altcoins; + +import io.bisq.gui.util.validation.InputValidator.ValidationResult; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public class YTNAddressValidator +{ + private final static String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + public static ValidationResult ValidateAddress(String addr) + { + if (addr.length() != 34) + return new ValidationResult(false, "YTN_Addr_Invalid: Length must be 34!"); + if (!addr.startsWith("Y")) + return new ValidationResult(false, "YTN_Addr_Invalid: must start with 'Y'!"); + byte[] decoded = decodeBase58(addr, 58, 25); + if (decoded == null) + return new ValidationResult(false, "YTN_Addr_Invalid: Base58 decoder error!"); + + byte[] hash = getSha256(decoded, 0, 21, 2); + if(hash == null || !Arrays.equals(Arrays.copyOfRange(hash, 0, 4), Arrays.copyOfRange(decoded, 21, 25))) + return new ValidationResult(false, "YTN_Addr_Invalid: Checksum error!"); + return new ValidationResult(true); + } + + private static byte[] decodeBase58(String input, int base, int len) + { + byte[] output = new byte[len]; + for (int i = 0; i < input.length(); i++) + { + char t = input.charAt(i); + + int p = ALPHABET.indexOf(t); + if (p == -1) + return null; + for (int j = len - 1; j >= 0; j--, p /= 256) + { + p += base * (output[j] & 0xFF); + output[j] = (byte) (p % 256); + } + if (p != 0) + return null; + } + + return output; + } + + private static byte[] getSha256(byte[] data, int start, int len, int recursion) + { + if (recursion == 0) + return data; + + try + { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(Arrays.copyOfRange(data, start, start + len)); + return getSha256(md.digest(), 0, 32, recursion - 1); + } catch (NoSuchAlgorithmException e) + { + return null; + } + } +} diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index 59b6791ac84..db79c1f5012 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -582,4 +582,20 @@ public void testSTL() { assertFalse(validator.validate("Se3F51UzpbVVnQRx2VNbcjfBoQJfeuyFF353i1jLnCZda9yVN3vy8csbYCESBvf38TFkchH1C1tMY6XHkC8L678K2vLsVZVMUII").isValid); //99 Charecters, expected is 97 assertFalse(validator.validate("").isValid); } + + @Test + public void testYTN() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("YTN"); + assertTrue(validator.validate("YTgSv7bk5x5p6te3uf3HbUwgnf7zEJM4Jn").isValid); + assertTrue(validator.validate("YVz19KtQUfyTP4AJS8sbRBqi7dkGTL2ovd").isValid); + + assertFalse(validator.validate("YiTwGuv3opowtPF5w8LUWBXFmaxc9S68ha").isValid); + assertFalse(validator.validate("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq").isValid); + assertFalse(validator.validate("YVZNX1SN5NtKa8UQFxwQbFeFc3iqRYheO").isValid); + assertFalse(validator.validate("YiTwGuv3opowtPF5w8LUWBlFmaxc9S68hz").isValid); + assertFalse(validator.validate("YiTwGuv3opowtPF5w8LUWB0Fmaxc9S68hz").isValid); + assertFalse(validator.validate("YiTwGuv3opowtPF5w8LUWBIFmaxc9S68hz").isValid); + assertFalse(validator.validate("").isValid); + } } From fc94fb564e725d844d363c1a96a409b695d258b5 Mon Sep 17 00:00:00 2001 From: Poompon Date: Sat, 10 Feb 2018 15:34:52 +0300 Subject: [PATCH 37/62] Add YENTEN [YTN] validaion --- common/src/main/java/io/bisq/common/locale/CurrencyUtil.java | 1 + .../io/bisq/core/trade/statistics/TradeStatisticsManager.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index f4461597321..5b8fcbd2b68 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -155,6 +155,7 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("WAC", "WACoins")); result.add(new CryptoCurrency("WILD", "WILD Token", true)); result.add(new CryptoCurrency("XZC", "Zcoin")); + result.add(new CryptoCurrency("YTN", "Yenten")); result.add(new CryptoCurrency("ZEC", "Zcash")); result.add(new CryptoCurrency("ZEN", "ZenCash")); result.sort(TradeCurrency::compareTo); diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index 2840254245a..d2bb4eb2d34 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -251,7 +251,7 @@ private void printAllCurrencyStats() { newlyAdded.add("BETR"); newlyAdded.add("MVT"); newlyAdded.add("REF"); - + newlyAdded.add("YTN"); coinsWithValidator.addAll(newlyAdded); From 51e2d89e5ce449b92de0acfa2ee05897f1201086 Mon Sep 17 00:00:00 2001 From: bitdaric <35429817+bitdaric@users.noreply.github.com> Date: Sun, 11 Feb 2018 10:43:24 +0330 Subject: [PATCH 38/62] add BitDaric --- common/src/main/java/io/bisq/common/locale/CurrencyUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index f4461597321..5816a39957f 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -157,6 +157,7 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("XZC", "Zcoin")); result.add(new CryptoCurrency("ZEC", "Zcash")); result.add(new CryptoCurrency("ZEN", "ZenCash")); + result.add(new CryptoCurrency("DARX", "BitDaric")); result.sort(TradeCurrency::compareTo); // Util for printing all altcoins for adding to FAQ page From 9ca3876472afa433232912636487151befd2d2f8 Mon Sep 17 00:00:00 2001 From: bitdaric <35429817+bitdaric@users.noreply.github.com> Date: Sun, 11 Feb 2018 10:45:26 +0330 Subject: [PATCH 39/62] add BitDaric --- .../io/bisq/core/trade/statistics/TradeStatisticsManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index 2840254245a..ae745faf748 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -251,6 +251,7 @@ private void printAllCurrencyStats() { newlyAdded.add("BETR"); newlyAdded.add("MVT"); newlyAdded.add("REF"); + newlyAdded.add("DARX"); coinsWithValidator.addAll(newlyAdded); From 9204c3b8c212fcac4e803d11d3503c7b4fc975dc Mon Sep 17 00:00:00 2001 From: bitdaric <35429817+bitdaric@users.noreply.github.com> Date: Sun, 11 Feb 2018 10:47:34 +0330 Subject: [PATCH 40/62] add BitDaric --- .../io/bisq/gui/util/validation/AltCoinAddressValidator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index 686897fd7d9..e8f8ae17e9b 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -438,6 +438,11 @@ public ValidationResult validate(String input) { return regexTestFailed; else return new ValidationResult(true); + case "DARX": + if (!input.matches("^[R][a-km-zA-HJ-NP-Z1-9]{25,34}$")) + return regexTestFailed; + else + return new ValidationResult(true); // Add new coins at the end... default: From a22bea3a80f067f90015be76303ed736c9acb8ec Mon Sep 17 00:00:00 2001 From: bitdaric <35429817+bitdaric@users.noreply.github.com> Date: Sun, 11 Feb 2018 10:51:49 +0330 Subject: [PATCH 41/62] add BitDaric --- .../validation/AltCoinAddressValidatorTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index 59b6791ac84..eae43f76caf 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -582,4 +582,18 @@ public void testSTL() { assertFalse(validator.validate("Se3F51UzpbVVnQRx2VNbcjfBoQJfeuyFF353i1jLnCZda9yVN3vy8csbYCESBvf38TFkchH1C1tMY6XHkC8L678K2vLsVZVMUII").isValid); //99 Charecters, expected is 97 assertFalse(validator.validate("").isValid); } + + @Test + public void testDARX() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("DARX"); + + assertTrue(validator.validate("RN8spHmkV6ZtRsquaTJMRZJujRQkkDNh2G").isValid); + assertTrue(validator.validate("RTD9jtKybd7TeM597t5MkNof84GPka34R7").isValid); + + assertFalse(validator.validate("LAPc1FumbYifKpfBpGbRLuPcLAJwHUxeyu").isValid); + assertFalse(validator.validate("ROPc1FumbYifKpfBpGbRLuPcLAJwHUxeyu").isValid); + assertFalse(validator.validate("rN8spHmkV6ZtROquaTJMRZJujRQkkDNh2G").isValid); + assertFalse(validator.validate("1NxrMzHCjG8X9kqTEZBXUNB5PC58DSXAht").isValid); + } } From abe20f1b975e6e6bb4af2dccbc87ab28794140f5 Mon Sep 17 00:00:00 2001 From: Manbearpixel Date: Mon, 5 Feb 2018 10:22:17 -0600 Subject: [PATCH 42/62] Added ODN Address Validation --- .../statistics/TradeStatisticsManager.java | 2 + .../validation/AltCoinAddressValidator.java | 9 +- .../gui/util/validation/params/ODNParams.java | 88 +++++++++++++++++++ .../AltCoinAddressValidatorTest.java | 16 ++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index 2840254245a..f2214df3c0e 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -252,6 +252,8 @@ private void printAllCurrencyStats() { newlyAdded.add("MVT"); newlyAdded.add("REF"); + newlyAdded.add("ODN"); + coinsWithValidator.addAll(newlyAdded); diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index 686897fd7d9..3c5935ac31b 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -438,8 +438,15 @@ public ValidationResult validate(String input) { return regexTestFailed; else return new ValidationResult(true); + case "ODN": + try { + Address.fromBase58(ODNParams.get(), input); + return new ValidationResult(true); + } catch (AddressFormatException e) { + return new ValidationResult(false, getErrorMessage(e)); + } - // Add new coins at the end... + // Add new coins at the end... default: log.debug("Validation for AltCoinAddress not implemented yet. currencyCode: " + currencyCode); return validationResult; diff --git a/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java b/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java new file mode 100644 index 00000000000..644a54461a4 --- /dev/null +++ b/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java @@ -0,0 +1,88 @@ +/* + * 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 io.bisq.gui.util.validation.params; + +import org.bitcoinj.core.*; +import org.bitcoinj.store.BlockStore; +import org.bitcoinj.store.BlockStoreException; +import org.bitcoinj.utils.MonetaryFormat; + +public class ODNParams extends NetworkParameters { + + private static ODNParams instance; + + public static synchronized ODNParams get() { + if (instance == null) { + instance = new ODNParams(); + } + return instance; + } + + // We only use the properties needed for address validation + public ODNParams() { + super(); + addressHeader = 75; // networkVersion (0x4b) + p2shHeader = -1; // TODO ??privateKeyPrefix 203 (0xcb) tbd? + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + + // default dummy implementations, not used... + @Override + public String getPaymentProtocolId() { + return PAYMENT_PROTOCOL_ID_MAINNET; + } + + @Override + public void checkDifficultyTransitions(StoredBlock storedPrev, Block next, BlockStore blockStore) throws VerificationException, BlockStoreException { + } + + @Override + public Coin getMaxMoney() { + return null; + } + + @Override + public Coin getMinNonDustOutput() { + return null; + } + + @Override + public MonetaryFormat getMonetaryFormat() { + return null; + } + + @Override + public String getUriScheme() { + return null; + } + + @Override + public boolean hasMaxMoney() { + return false; + } + + @Override + public BitcoinSerializer getSerializer(boolean parseRetain) { + return null; + } + + @Override + public int getProtocolVersionNum(ProtocolVersion version) { + return 0; + } +} diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index 59b6791ac84..4d9e17250de 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -429,6 +429,22 @@ public void testCAGE() { assertFalse(validator.validate("").isValid); } + @Test + public void testODN() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("ODN"); + + assertTrue(validator.validate("XEfyuzk8yTp5eA9eVUeCW2PFbCFtNb6Jgv").isValid); + assertTrue(validator.validate("XJegzjV2GK9CeQLNNcc5GVSSqTkv1XMxSF").isValid); + assertTrue(validator.validate("XLfvvLuwjUrz2kf5gghEmUPFE3vFvwfEiL").isValid); + assertTrue(validator.validate("XNC1e9TfUApfBsH9JCubioS5XGuwFLbsP4").isValid); + + assertFalse(validator.validate("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq").isValid); + assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266").isValid); + assertFalse(validator.validate("SSnwqFBiyqK1n4BV7kPX86iesev2NobhEo").isValid); + assertFalse(validator.validate("").isValid); + } + @Test public void testCRED() { AltCoinAddressValidator validator = new AltCoinAddressValidator(); From b067831e5e5740d3e0442d6c4a1bb7e402275334 Mon Sep 17 00:00:00 2001 From: Hein Bloed Date: Thu, 8 Feb 2018 18:25:24 +0100 Subject: [PATCH 43/62] correct settings for p2shHeader --- .../java/io/bisq/gui/util/validation/params/ODNParams.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java b/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java index 644a54461a4..ebc14978f67 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/params/ODNParams.java @@ -36,8 +36,8 @@ public static synchronized ODNParams get() { // We only use the properties needed for address validation public ODNParams() { super(); - addressHeader = 75; // networkVersion (0x4b) - p2shHeader = -1; // TODO ??privateKeyPrefix 203 (0xcb) tbd? + addressHeader = 75; + p2shHeader = 125; acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } From 45b269304895af0bca4dafe3d6619a26d6280232 Mon Sep 17 00:00:00 2001 From: Hein Bloed Date: Thu, 8 Feb 2018 18:26:45 +0100 Subject: [PATCH 44/62] added ODN to createAllSortedCryptoCurrenciesList --- common/src/main/java/io/bisq/common/locale/CurrencyUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index f4461597321..aa56c8031d8 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -131,6 +131,7 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("NMC", "Namecoin")); result.add(new CryptoCurrency("NBT", "NuBits")); result.add(new CryptoCurrency("NXT", "Nxt")); + result.add(new CryptoCurrency("ODN", "Obsidian")); result.add(new CryptoCurrency("888", "OctoCoin")); result.add(new CryptoCurrency("PART", "Particl")); result.add(new CryptoCurrency("PASC", "Pascal Coin", true)); From 313090b82545e583765b2ab140b2052fdb719540 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 12 Feb 2018 11:17:31 -0500 Subject: [PATCH 45/62] Improve and fix logs. --- .../network/p2p/network/LocalhostNetworkNode.java | 7 +++---- .../p2p/network/SynchronizedProtoOutputStream.java | 12 +++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/network/src/main/java/io/bisq/network/p2p/network/LocalhostNetworkNode.java b/network/src/main/java/io/bisq/network/p2p/network/LocalhostNetworkNode.java index 79057693669..4cec5eaa861 100644 --- a/network/src/main/java/io/bisq/network/p2p/network/LocalhostNetworkNode.java +++ b/network/src/main/java/io/bisq/network/p2p/network/LocalhostNetworkNode.java @@ -67,10 +67,9 @@ public void start(@Nullable SetupListener setupListener) { e.printStackTrace(); log.error("Exception at startServer: " + e.getMessage()); } - - final NodeAddress nodeAddress; - if (null == address) nodeAddress = new NodeAddress("localhost", servicePort); - else nodeAddress = new NodeAddress(address); + final NodeAddress nodeAddress = address == null ? + new NodeAddress("localhost", servicePort) : + new NodeAddress(address); nodeAddressProperty.set(nodeAddress); setupListeners.stream().forEach(SetupListener::onHiddenServicePublished); }, simulateTorDelayTorNode, TimeUnit.MILLISECONDS); diff --git a/network/src/main/java/io/bisq/network/p2p/network/SynchronizedProtoOutputStream.java b/network/src/main/java/io/bisq/network/p2p/network/SynchronizedProtoOutputStream.java index dfc7bbf6028..51a2c65d27b 100644 --- a/network/src/main/java/io/bisq/network/p2p/network/SynchronizedProtoOutputStream.java +++ b/network/src/main/java/io/bisq/network/p2p/network/SynchronizedProtoOutputStream.java @@ -47,11 +47,13 @@ void writeEnvelope(NetworkEnvelope envelope) { } catch (InterruptedException e) { Thread currentThread = Thread.currentThread(); currentThread.interrupt(); - log.error("Thread " + currentThread + " was interrupted", e); - throw new BisqRuntimeException("Failed to write envelope", e); + final String msg = "Thread " + currentThread + " was interrupted. InterruptedException=" + e; + log.error(msg); + throw new BisqRuntimeException(msg, e); } catch (ExecutionException e) { - log.error("Failed to write envelope", e); - throw new BisqRuntimeException("Failed to write envelope", e); + final String msg = "Failed to write envelope. ExecutionException " + e; + log.error(msg); + throw new BisqRuntimeException(msg, e); } } @@ -60,7 +62,7 @@ void onConnectionShutdown() { executorService.shutdownNow(); super.onConnectionShutdown(); } catch (Throwable t) { - log.error("Failed to handle connection shutdown", t); + log.error("Failed to handle connection shutdown. Throwable={}", t); } } } From ceb8e857d5ac8c13c578de26cc523829cb50c521 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Tue, 13 Feb 2018 09:36:08 +0100 Subject: [PATCH 46/62] Unify order of columns in offer book --- .../main/market/offerbook/OfferBookChartView.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java b/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java index 1cb2e965916..b833e4eb069 100644 --- a/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java +++ b/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java @@ -438,17 +438,9 @@ public void updateItem(final OfferListItem offerListItem, boolean empty) { } }); */ - if (direction == OfferPayload.Direction.BUY) { - // tableView.getColumns().add(accumulatedColumn); - tableView.getColumns().add(volumeColumn); - tableView.getColumns().add(amountColumn); - tableView.getColumns().add(priceColumn); - } else { - tableView.getColumns().add(priceColumn); - tableView.getColumns().add(amountColumn); - tableView.getColumns().add(volumeColumn); - //tableView.getColumns().add(accumulatedColumn); - } + tableView.getColumns().add(volumeColumn); + tableView.getColumns().add(amountColumn); + tableView.getColumns().add(priceColumn); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); Label placeholder = new AutoTooltipLabel(Res.get("table.placeholder.noItems", Res.get("shared.offers"))); From b930e18ce076c4e5345742460f9ba4928f6fdedf Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Tue, 13 Feb 2018 10:00:46 +0100 Subject: [PATCH 47/62] Increase vertical gap --- .../gui/main/portfolio/pendingtrades/steps/TradeStepView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java index 1d08a2abbbd..ee71cdfb184 100644 --- a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -443,7 +443,7 @@ private GridPane createInfoPopover() { GridPane infoGridPane = new GridPane(); int rowIndex = 0; infoGridPane.setHgap(5); - infoGridPane.setVgap(5); + infoGridPane.setVgap(10); infoGridPane.setPadding(new Insets(10, 10, 10, 10)); Label label = addMultilineLabel(infoGridPane, rowIndex++, Res.get("portfolio.pending.tradePeriodInfo")); label.setMaxWidth(450); From 9c58c3069620c1e8a366732799046c2c8c8b637c Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Tue, 13 Feb 2018 12:22:02 +0100 Subject: [PATCH 48/62] Add missing active text color --- gui/src/main/java/io/bisq/gui/bisq.css | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/src/main/java/io/bisq/gui/bisq.css b/gui/src/main/java/io/bisq/gui/bisq.css index e6dfe46ea11..4167f3d606a 100644 --- a/gui/src/main/java/io/bisq/gui/bisq.css +++ b/gui/src/main/java/io/bisq/gui/bisq.css @@ -826,6 +826,7 @@ textfield */ #titled-group-bg-label-active { -fx-font-weight: bold; -fx-font-size: 14; + -fx-text-fill: -fx-accent; -fx-background-color: -bs-content-bg-grey; } From dc2d94b4867a4e830603e7ff277e54deee8fdce7 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Tue, 13 Feb 2018 12:29:37 +0100 Subject: [PATCH 49/62] Use header label for buy and sell offers that is easier to understand --- common/src/main/resources/i18n/displayStrings.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_de.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_el.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_es.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_hu.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_pt.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_ro.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_ru.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_sr.properties | 4 ++-- .../src/main/resources/i18n/displayStrings_zh.properties | 4 ++-- .../resources/i18n/in_dev/displayStrings_fr.properties | 4 ++-- .../gui/main/market/offerbook/OfferBookChartView.java | 8 ++++---- 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 99ce4e5a7f1..b0ae08eff1d 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -266,8 +266,8 @@ market.offerBook.leftButtonAltcoin=I want to buy {0} (sell {1}) market.offerBook.rightButtonAltcoin=I want to sell {0} (buy {1}) market.offerBook.leftButtonFiat=I want to buy {0} with {1} market.offerBook.rightButtonFiat=I want to sell {0} for {1} -market.offerBook.leftHeaderLabel=Offers to sell {0} for {1} -market.offerBook.rightHeaderLabel=Offers to buy {0} with {1} +market.offerBook.sellOffersHeaderLabel=Sell {0} to +market.offerBook.buyOffersHeaderLabel=Buy {0} from market.offerBook.buy=I want to buy bitcoin market.offerBook.sell=I want to sell bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 270fcdeea71..a72f791a741 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Ich möchte {0} kaufen ({1} verkaufen) market.offerBook.rightButtonAltcoin=Ich möchte {0} verkaufen ({1} kaufen) market.offerBook.leftButtonFiat=Ich möchte {0} mit {1} kaufen market.offerBook.rightButtonFiat=Ich möchte {0} für {1} verkaufen -market.offerBook.leftHeaderLabel=Angebote {0} für {1} zu verkaufen -market.offerBook.rightHeaderLabel=Angebote {0} für {1} zu kaufen +market.offerBook.sellOffersHeaderLabel=Angebote {0} für {1} zu verkaufen +market.offerBook.buyOffersHeaderLabel=Angebote {0} für {1} zu kaufen market.offerBook.buy=Ich möchte Bitcoins kaufen market.offerBook.sell=Ich möchte Bitcoins verkaufen diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index bf2a11e7d82..aac4269f0a0 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Θέλω να αγοράσω {0} (πώλησ market.offerBook.rightButtonAltcoin=Θέλω να πουλήσω {0} (αγορά {1}) market.offerBook.leftButtonFiat=Θέλω να αγοράσω {0} με {1} market.offerBook.rightButtonFiat=Θέλω να πουλήσω {0} με {1} -market.offerBook.leftHeaderLabel=Προσφορές πώλησης {0} έναντι {1} -market.offerBook.rightHeaderLabel=Προσφορές αγοράς {0} έναντι {1} +market.offerBook.sellOffersHeaderLabel=Προσφορές πώλησης {0} έναντι {1} +market.offerBook.buyOffersHeaderLabel=Προσφορές αγοράς {0} έναντι {1} market.offerBook.buy=Θέλω να αγοράσω bitcoin market.offerBook.sell=Θέλω να πουλήσω bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index 6e4bd4e215f..889a628ed1e 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Quiero comprar {0} (vender {1}) market.offerBook.rightButtonAltcoin=Quiero vender {0} (comprar {1}) market.offerBook.leftButtonFiat=Quiero comprar {0} con {1} market.offerBook.rightButtonFiat=Quiero vender {0} por {1} -market.offerBook.leftHeaderLabel=Ofrece vender {0} por {1} -market.offerBook.rightHeaderLabel=Ofrece comrpar {0} con {1} +market.offerBook.sellOffersHeaderLabel=Ofrece vender {0} por {1} +market.offerBook.buyOffersHeaderLabel=Ofrece comrpar {0} con {1} market.offerBook.buy=Quiero comprar bitcoin market.offerBook.sell=Quiero vender bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_hu.properties b/common/src/main/resources/i18n/displayStrings_hu.properties index 1289b95070c..9192370d358 100644 --- a/common/src/main/resources/i18n/displayStrings_hu.properties +++ b/common/src/main/resources/i18n/displayStrings_hu.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Vásárolni szeretnék {0} (eladni való {1}) market.offerBook.rightButtonAltcoin=Eladni szeretnék {0} ({1} vétel) market.offerBook.leftButtonFiat=Vásárolni szeretnék {0}-ot {1}-ért market.offerBook.rightButtonFiat=Eladni szeretnék {0} cserébe {1}-ért -market.offerBook.leftHeaderLabel=Eladási ajánlatok {0} cserében {1} -market.offerBook.rightHeaderLabel=Vételi ajánlatok {0} cserében {1} +market.offerBook.sellOffersHeaderLabel=Eladási ajánlatok {0} cserében {1} +market.offerBook.buyOffersHeaderLabel=Vételi ajánlatok {0} cserében {1} market.offerBook.buy=Vásárolni szeretnék bitcoinot market.offerBook.sell=Eladni szeretnék bitcoinot diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index 7ec55e74b7d..abc7e1f97cd 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Eu quero comprar {0} (vender {1}) market.offerBook.rightButtonAltcoin=Eu quero vender {0} (comprar {1}) market.offerBook.leftButtonFiat=Eu quero comprar {0} com {1} market.offerBook.rightButtonFiat=Eu quero vender {0} por {1} -market.offerBook.leftHeaderLabel=Ofertas para vender {0} por {1} -market.offerBook.rightHeaderLabel=Ofertas para comprar {0} com {1} +market.offerBook.sellOffersHeaderLabel=Ofertas para vender {0} por {1} +market.offerBook.buyOffersHeaderLabel=Ofertas para comprar {0} com {1} market.offerBook.buy=Eu quero comprar bitcoin market.offerBook.sell=Eu quero vender bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_ro.properties b/common/src/main/resources/i18n/displayStrings_ro.properties index b76231ff83e..be3a0dfa366 100644 --- a/common/src/main/resources/i18n/displayStrings_ro.properties +++ b/common/src/main/resources/i18n/displayStrings_ro.properties @@ -264,8 +264,8 @@ market.offerBook.leftButtonAltcoin=Doresc să cumpăr {0} (vând {1}) market.offerBook.rightButtonAltcoin=Doresc să vând {0} (cumpăr {1}) market.offerBook.leftButtonFiat=Doresc să cumpăr {0} cu {1} market.offerBook.rightButtonFiat=Doresc să vând {0} contra {1} -market.offerBook.leftHeaderLabel=Oferte de vânzare {0} contra {1} -market.offerBook.rightHeaderLabel=Oferte de cumpărare {0} cu {1} +market.offerBook.sellOffersHeaderLabel=Oferte de vânzare {0} contra {1} +market.offerBook.buyOffersHeaderLabel=Oferte de cumpărare {0} cu {1} market.offerBook.buy=Doresc să cumpăr bitcoin market.offerBook.sell=Doresc să vând bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_ru.properties b/common/src/main/resources/i18n/displayStrings_ru.properties index 9350dfaa541..8935e98da6c 100644 --- a/common/src/main/resources/i18n/displayStrings_ru.properties +++ b/common/src/main/resources/i18n/displayStrings_ru.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Я хочу купить {0} (продать market.offerBook.rightButtonAltcoin=Я хочу продать {0} (купить {1}) market.offerBook.leftButtonFiat=Я хочу купить {0} за {1} market.offerBook.rightButtonFiat=Я хочу продать {0} за {1} -market.offerBook.leftHeaderLabel=Предложения по продаже {0} за {1} -market.offerBook.rightHeaderLabel=Предложения по покупке {0} за {1} +market.offerBook.sellOffersHeaderLabel=Предложения по продаже {0} за {1} +market.offerBook.buyOffersHeaderLabel=Предложения по покупке {0} за {1} market.offerBook.buy=Я хочу купить Биткоин market.offerBook.sell=Я хочу продать Биткоин diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index a48805ba13d..3ea1b337173 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Želim da kupim {0} (prodaja {1}) market.offerBook.rightButtonAltcoin=Želim da prodam {0} (kupovina {1}) market.offerBook.leftButtonFiat=Želim da kupim {0} sa {1} market.offerBook.rightButtonFiat=Želim da prodam {0} za {1} -market.offerBook.leftHeaderLabel=Ponude prodaje {0} za {1} -market.offerBook.rightHeaderLabel=Ponude kupovine {0} sa {1} +market.offerBook.sellOffersHeaderLabel=Ponude prodaje {0} za {1} +market.offerBook.buyOffersHeaderLabel=Ponude kupovine {0} sa {1} market.offerBook.buy=Želim da kupim bitkoin market.offerBook.sell=Želim da prodam bitkoin diff --git a/common/src/main/resources/i18n/displayStrings_zh.properties b/common/src/main/resources/i18n/displayStrings_zh.properties index a78001b8ef7..64ce2307c32 100644 --- a/common/src/main/resources/i18n/displayStrings_zh.properties +++ b/common/src/main/resources/i18n/displayStrings_zh.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=我想要买入 {0} (卖出 {1}) market.offerBook.rightButtonAltcoin=我想要卖出 {0} (买入 {1}) market.offerBook.leftButtonFiat=我想要用 {1} 买入 {0} market.offerBook.rightButtonFiat=我想要用 {1} 卖出 {0} -market.offerBook.leftHeaderLabel={1} 卖出 {0} 列表 -market.offerBook.rightHeaderLabel={1} 买入 {0} 列表 +market.offerBook.sellOffersHeaderLabel={1} 卖出 {0} 列表 +market.offerBook.buyOffersHeaderLabel={1} 买入 {0} 列表 market.offerBook.buy=我想要买入比特币 market.offerBook.sell=我想要卖出比特币 diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties index e7a7a61bcd4..d19f3dd59d4 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties @@ -434,8 +434,8 @@ market.offerBook.leftButtonAltcoin=Je veux acheter {0} (vendre {1}) market.offerBook.rightButtonAltcoin=Je veux vendre {0} (buy {1}) market.offerBook.leftButtonFiat=Je veux acheter {0} avec {1} market.offerBook.rightButtonFiat=Je veux vendre {0} pour {1} -market.offerBook.leftHeaderLabel=Offres de vente {0} pour {1} -market.offerBook.rightHeaderLabel=Offres pour acheter {0} avec {1} +market.offerBook.sellOffersHeaderLabel=Offres de vente {0} pour {1} +market.offerBook.buyOffersHeaderLabel=Offres pour acheter {0} avec {1} market.offerBook.buy=Je veux acheter du bitcoin market.offerBook.sell=Je veux vendre du bitcoin market.spread.numberOfOffersColumn=Toutes les offres ({0}) diff --git a/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java b/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java index 1cb2e965916..d300c94fdff 100644 --- a/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java +++ b/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java @@ -207,10 +207,10 @@ public Number fromString(String string) { reverseTableColumns(); } - leftHeaderLabel.setText(Res.get("market.offerBook.leftHeaderLabel", code, Res.getBaseCurrencyCode())); + leftHeaderLabel.setText(Res.get("market.offerBook.buyOffersHeaderLabel", code)); leftButton.setText(Res.get("market.offerBook.leftButtonAltcoin", code, Res.getBaseCurrencyCode())); - rightHeaderLabel.setText(Res.get("market.offerBook.rightHeaderLabel", code, Res.getBaseCurrencyCode())); + rightHeaderLabel.setText(Res.get("market.offerBook.sellOffersHeaderLabel", code)); rightButton.setText(Res.get("market.offerBook.rightButtonAltcoin", code, Res.getBaseCurrencyCode())); priceColumnLabel.set(Res.get("shared.priceWithCur", Res.getBaseCurrencyCode())); @@ -220,10 +220,10 @@ public Number fromString(String string) { reverseTableColumns(); } - leftHeaderLabel.setText(Res.get("market.offerBook.rightHeaderLabel", Res.getBaseCurrencyCode(), code)); + leftHeaderLabel.setText(Res.get("market.offerBook.sellOffersHeaderLabel", Res.getBaseCurrencyCode())); leftButton.setText(Res.get("market.offerBook.rightButtonFiat", Res.getBaseCurrencyCode(), code)); - rightHeaderLabel.setText(Res.get("market.offerBook.leftHeaderLabel", Res.getBaseCurrencyCode(), code)); + rightHeaderLabel.setText(Res.get("market.offerBook.buyOffersHeaderLabel", Res.getBaseCurrencyCode())); rightButton.setText(Res.get("market.offerBook.leftButtonFiat", Res.getBaseCurrencyCode(), code)); priceColumnLabel.set(Res.get("shared.priceWithCur", code)); From 6de488bcc1e994a8847b56d6b15be2ac0ca3e7ae Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Tue, 13 Feb 2018 12:46:18 +0100 Subject: [PATCH 50/62] Add translations for changed string --- common/src/main/resources/i18n/displayStrings_de.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_el.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_es.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_hu.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_pt.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_ro.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_ru.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_sr.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_zh.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_fr.properties | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index a72f791a741..1ad38bb0d4d 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Ich möchte {0} kaufen ({1} verkaufen) market.offerBook.rightButtonAltcoin=Ich möchte {0} verkaufen ({1} kaufen) market.offerBook.leftButtonFiat=Ich möchte {0} mit {1} kaufen market.offerBook.rightButtonFiat=Ich möchte {0} für {1} verkaufen -market.offerBook.sellOffersHeaderLabel=Angebote {0} für {1} zu verkaufen -market.offerBook.buyOffersHeaderLabel=Angebote {0} für {1} zu kaufen +market.offerBook.sellOffersHeaderLabel=Verkaufe {0} an +market.offerBook.buyOffersHeaderLabel=Kaufe {0} von market.offerBook.buy=Ich möchte Bitcoins kaufen market.offerBook.sell=Ich möchte Bitcoins verkaufen diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index aac4269f0a0..af24f23498f 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Θέλω να αγοράσω {0} (πώλησ market.offerBook.rightButtonAltcoin=Θέλω να πουλήσω {0} (αγορά {1}) market.offerBook.leftButtonFiat=Θέλω να αγοράσω {0} με {1} market.offerBook.rightButtonFiat=Θέλω να πουλήσω {0} με {1} -market.offerBook.sellOffersHeaderLabel=Προσφορές πώλησης {0} έναντι {1} -market.offerBook.buyOffersHeaderLabel=Προσφορές αγοράς {0} έναντι {1} +market.offerBook.sellOffersHeaderLabel=Πουλήστε το {0} στο +market.offerBook.buyOffersHeaderLabel=Αγοράστε {0} από market.offerBook.buy=Θέλω να αγοράσω bitcoin market.offerBook.sell=Θέλω να πουλήσω bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index 889a628ed1e..47edc648bfe 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Quiero comprar {0} (vender {1}) market.offerBook.rightButtonAltcoin=Quiero vender {0} (comprar {1}) market.offerBook.leftButtonFiat=Quiero comprar {0} con {1} market.offerBook.rightButtonFiat=Quiero vender {0} por {1} -market.offerBook.sellOffersHeaderLabel=Ofrece vender {0} por {1} -market.offerBook.buyOffersHeaderLabel=Ofrece comrpar {0} con {1} +market.offerBook.sellOffersHeaderLabel=Vender {0} a +market.offerBook.buyOffersHeaderLabel=Compre {0} desde market.offerBook.buy=Quiero comprar bitcoin market.offerBook.sell=Quiero vender bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_hu.properties b/common/src/main/resources/i18n/displayStrings_hu.properties index 9192370d358..04726650b3f 100644 --- a/common/src/main/resources/i18n/displayStrings_hu.properties +++ b/common/src/main/resources/i18n/displayStrings_hu.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Vásárolni szeretnék {0} (eladni való {1}) market.offerBook.rightButtonAltcoin=Eladni szeretnék {0} ({1} vétel) market.offerBook.leftButtonFiat=Vásárolni szeretnék {0}-ot {1}-ért market.offerBook.rightButtonFiat=Eladni szeretnék {0} cserébe {1}-ért -market.offerBook.sellOffersHeaderLabel=Eladási ajánlatok {0} cserében {1} -market.offerBook.buyOffersHeaderLabel=Vételi ajánlatok {0} cserében {1} +market.offerBook.sellOffersHeaderLabel={0} eladni +market.offerBook.buyOffersHeaderLabel=Vásároljon {0} -tól market.offerBook.buy=Vásárolni szeretnék bitcoinot market.offerBook.sell=Eladni szeretnék bitcoinot diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index abc7e1f97cd..8fc5de201c9 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Eu quero comprar {0} (vender {1}) market.offerBook.rightButtonAltcoin=Eu quero vender {0} (comprar {1}) market.offerBook.leftButtonFiat=Eu quero comprar {0} com {1} market.offerBook.rightButtonFiat=Eu quero vender {0} por {1} -market.offerBook.sellOffersHeaderLabel=Ofertas para vender {0} por {1} -market.offerBook.buyOffersHeaderLabel=Ofertas para comprar {0} com {1} +market.offerBook.sellOffersHeaderLabel=Vender {0} para +market.offerBook.buyOffersHeaderLabel=Compre {0} de market.offerBook.buy=Eu quero comprar bitcoin market.offerBook.sell=Eu quero vender bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_ro.properties b/common/src/main/resources/i18n/displayStrings_ro.properties index be3a0dfa366..f81b5246292 100644 --- a/common/src/main/resources/i18n/displayStrings_ro.properties +++ b/common/src/main/resources/i18n/displayStrings_ro.properties @@ -264,8 +264,8 @@ market.offerBook.leftButtonAltcoin=Doresc să cumpăr {0} (vând {1}) market.offerBook.rightButtonAltcoin=Doresc să vând {0} (cumpăr {1}) market.offerBook.leftButtonFiat=Doresc să cumpăr {0} cu {1} market.offerBook.rightButtonFiat=Doresc să vând {0} contra {1} -market.offerBook.sellOffersHeaderLabel=Oferte de vânzare {0} contra {1} -market.offerBook.buyOffersHeaderLabel=Oferte de cumpărare {0} cu {1} +market.offerBook.sellOffersHeaderLabel=Vindem {0} la +market.offerBook.buyOffersHeaderLabel=Cumpărați {0} de la market.offerBook.buy=Doresc să cumpăr bitcoin market.offerBook.sell=Doresc să vând bitcoin diff --git a/common/src/main/resources/i18n/displayStrings_ru.properties b/common/src/main/resources/i18n/displayStrings_ru.properties index 8935e98da6c..dee2745b2ae 100644 --- a/common/src/main/resources/i18n/displayStrings_ru.properties +++ b/common/src/main/resources/i18n/displayStrings_ru.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Я хочу купить {0} (продать market.offerBook.rightButtonAltcoin=Я хочу продать {0} (купить {1}) market.offerBook.leftButtonFiat=Я хочу купить {0} за {1} market.offerBook.rightButtonFiat=Я хочу продать {0} за {1} -market.offerBook.sellOffersHeaderLabel=Предложения по продаже {0} за {1} -market.offerBook.buyOffersHeaderLabel=Предложения по покупке {0} за {1} +market.offerBook.sellOffersHeaderLabel=Продать {0} до +market.offerBook.buyOffersHeaderLabel=Купите {0} из market.offerBook.buy=Я хочу купить Биткоин market.offerBook.sell=Я хочу продать Биткоин diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index 3ea1b337173..6e1d20d1435 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=Želim da kupim {0} (prodaja {1}) market.offerBook.rightButtonAltcoin=Želim da prodam {0} (kupovina {1}) market.offerBook.leftButtonFiat=Želim da kupim {0} sa {1} market.offerBook.rightButtonFiat=Želim da prodam {0} za {1} -market.offerBook.sellOffersHeaderLabel=Ponude prodaje {0} za {1} -market.offerBook.buyOffersHeaderLabel=Ponude kupovine {0} sa {1} +market.offerBook.sellOffersHeaderLabel=Prodaj {0} u +market.offerBook.buyOffersHeaderLabel=Kupite {0} iz market.offerBook.buy=Želim da kupim bitkoin market.offerBook.sell=Želim da prodam bitkoin diff --git a/common/src/main/resources/i18n/displayStrings_zh.properties b/common/src/main/resources/i18n/displayStrings_zh.properties index 64ce2307c32..0bb4692a375 100644 --- a/common/src/main/resources/i18n/displayStrings_zh.properties +++ b/common/src/main/resources/i18n/displayStrings_zh.properties @@ -258,8 +258,8 @@ market.offerBook.leftButtonAltcoin=我想要买入 {0} (卖出 {1}) market.offerBook.rightButtonAltcoin=我想要卖出 {0} (买入 {1}) market.offerBook.leftButtonFiat=我想要用 {1} 买入 {0} market.offerBook.rightButtonFiat=我想要用 {1} 卖出 {0} -market.offerBook.sellOffersHeaderLabel={1} 卖出 {0} 列表 -market.offerBook.buyOffersHeaderLabel={1} 买入 {0} 列表 +market.offerBook.sellOffersHeaderLabel=出售 {0} 到 +market.offerBook.buyOffersHeaderLabel=从中购买 {0} market.offerBook.buy=我想要买入比特币 market.offerBook.sell=我想要卖出比特币 diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties index d19f3dd59d4..171f740277d 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties @@ -434,8 +434,8 @@ market.offerBook.leftButtonAltcoin=Je veux acheter {0} (vendre {1}) market.offerBook.rightButtonAltcoin=Je veux vendre {0} (buy {1}) market.offerBook.leftButtonFiat=Je veux acheter {0} avec {1} market.offerBook.rightButtonFiat=Je veux vendre {0} pour {1} -market.offerBook.sellOffersHeaderLabel=Offres de vente {0} pour {1} -market.offerBook.buyOffersHeaderLabel=Offres pour acheter {0} avec {1} +market.offerBook.sellOffersHeaderLabel=Vendre {0} à +market.offerBook.buyOffersHeaderLabel=Acheter {0} à partir de market.offerBook.buy=Je veux acheter du bitcoin market.offerBook.sell=Je veux vendre du bitcoin market.spread.numberOfOffersColumn=Toutes les offres ({0}) From d06746e78b35cc3ae0c90ebe66af04c7091b295e Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 13 Feb 2018 13:48:48 +0100 Subject: [PATCH 51/62] Change warning log to info --- .../bisq/core/dao/compensation/CompensationRequestManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java index 72f67aa5eed..13a6044ed15 100644 --- a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java +++ b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java @@ -230,11 +230,12 @@ private void createCompensationRequest(CompensationRequestPayload compensationRe if (storeLocally) compensationRequestsStorage.queueUpForSave(new CompensationRequestList(getAllRequests()), 500); } else { - log.warn("We have already an item with the same CompensationRequest."); + log.info("We already have an item with the same CompensationRequest."); } } private void updateFilteredLists() { + // TODO: Does this only need to be set once to keep the list updated? pastRequests.setPredicate(daoPeriodService::isInPastCycle); activeRequests.setPredicate(compensationRequest -> { return daoPeriodService.isInCurrentCycle(compensationRequest) || From 3fdb4fb860d886519cbabb26ad3316aa503ea0ba Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 13 Feb 2018 14:02:40 +0100 Subject: [PATCH 52/62] Only warn on duplicate foreign compensation requests --- .../bisq/core/dao/compensation/CompensationRequestManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java index 13a6044ed15..f255b9faaea 100644 --- a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java +++ b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java @@ -230,7 +230,8 @@ private void createCompensationRequest(CompensationRequestPayload compensationRe if (storeLocally) compensationRequestsStorage.queueUpForSave(new CompensationRequestList(getAllRequests()), 500); } else { - log.info("We already have an item with the same CompensationRequest."); + if (!isMine(compensationRequestPayload)) + log.warn("We already have an item with the same CompensationRequest."); } } From f73395f6f1e578909f81d4f3b5be80211ae3e01a Mon Sep 17 00:00:00 2001 From: sqrrm Date: Sat, 10 Feb 2018 16:42:49 +0100 Subject: [PATCH 53/62] Fix log spelling --- .../main/java/io/bisq/core/provider/ProvidersRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/io/bisq/core/provider/ProvidersRepository.java b/core/src/main/java/io/bisq/core/provider/ProvidersRepository.java index 2142f039c70..166a90103c3 100644 --- a/core/src/main/java/io/bisq/core/provider/ProvidersRepository.java +++ b/core/src/main/java/io/bisq/core/provider/ProvidersRepository.java @@ -91,7 +91,7 @@ public void selectNextProviderBaseUrl() { index++; if (providerList.size() == 1) - log.warn("We oly have one provider"); + log.warn("We only have one provider"); } else { baseUrl = ""; log.warn("We do not have any providers. That can be if all providers are filtered or providersFromProgramArgs is set but empty. " + From ab47148079a018c328a0709ca6c14df51adfcfde Mon Sep 17 00:00:00 2001 From: sqrrm Date: Sat, 10 Feb 2018 16:55:38 +0100 Subject: [PATCH 54/62] Move compensation request domain specifics out of gui --- .../core/btc/wallet/BsqWalletService.java | 22 ++-- .../btc/wallet/InsufficientBsqException.java | 10 ++ .../dao/compensation/CompensationRequest.java | 27 +++++ .../CompensationRequestManager.java | 103 +++++++++++++++++- .../create/CreateCompensationRequestView.java | 78 ++----------- 5 files changed, 162 insertions(+), 78 deletions(-) create mode 100644 core/src/main/java/io/bisq/core/btc/wallet/InsufficientBsqException.java diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java index 3b53566cba9..f19467b1a2b 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java @@ -17,8 +17,6 @@ package io.bisq.core.btc.wallet; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; import io.bisq.core.app.BisqEnvironment; import io.bisq.core.btc.Restrictions; import io.bisq.core.btc.exceptions.TransactionVerificationException; @@ -365,7 +363,7 @@ public void commitTx(Transaction tx) { public Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount) throws AddressFormatException, - InsufficientMoneyException, WalletException, TransactionVerificationException { + InsufficientBsqException, WalletException, TransactionVerificationException { Transaction tx = new Transaction(params); checkArgument(Restrictions.isAboveDust(receiverAmount), @@ -381,7 +379,11 @@ public Transaction getPreparedSendTx(String receiverAddress, sendRequest.signInputs = false; sendRequest.ensureMinRequiredFee = false; sendRequest.changeAddress = getUnusedAddress(); - wallet.completeTx(sendRequest); + try { + wallet.completeTx(sendRequest); + } catch (InsufficientMoneyException e) { + throw new InsufficientBsqException(e.missing); + } checkWalletConsistency(wallet); verifyTransaction(tx); // printTx("prepareSendTx", tx); @@ -394,7 +396,7 @@ public Transaction getPreparedSendTx(String receiverAddress, /////////////////////////////////////////////////////////////////////////////////////////// public Transaction getPreparedBurnFeeTx(Coin fee) throws - InsufficientMoneyException, ChangeBelowDustException { + InsufficientBsqException, ChangeBelowDustException { Transaction tx = new Transaction(params); // We might have no output if inputs match fee. @@ -404,9 +406,13 @@ public Transaction getPreparedBurnFeeTx(Coin fee) throws // TODO check dust output CoinSelection coinSelection = bsqCoinSelector.select(fee, wallet.calculateAllSpendCandidates()); coinSelection.gathered.stream().forEach(tx::addInput); - Coin change = bsqCoinSelector.getChange(fee, coinSelection); - if (change.isPositive()) - tx.addOutput(change, getUnusedAddress()); + try { + Coin change = bsqCoinSelector.getChange(fee, coinSelection); + if (change.isPositive()) + tx.addOutput(change, getUnusedAddress()); + } catch (InsufficientMoneyException e) { + throw new InsufficientBsqException(e.missing); + } //printTx("getPreparedBurnFeeTx", tx); return tx; diff --git a/core/src/main/java/io/bisq/core/btc/wallet/InsufficientBsqException.java b/core/src/main/java/io/bisq/core/btc/wallet/InsufficientBsqException.java new file mode 100644 index 00000000000..9c5246bc928 --- /dev/null +++ b/core/src/main/java/io/bisq/core/btc/wallet/InsufficientBsqException.java @@ -0,0 +1,10 @@ +package io.bisq.core.btc.wallet; + +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.InsufficientMoneyException; + +public class InsufficientBsqException extends InsufficientMoneyException { + public InsufficientBsqException(Coin missing) { + super(missing); + } +} diff --git a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequest.java b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequest.java index 8ef0cd12982..5ba3eabab54 100644 --- a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequest.java +++ b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequest.java @@ -18,17 +18,23 @@ package io.bisq.core.dao.compensation; import io.bisq.common.proto.persistable.PersistablePayload; +import io.bisq.core.btc.wallet.BsqWalletService; import io.bisq.generated.protobuffer.PB; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.bitcoinj.core.Address; +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.Transaction; import org.springframework.util.CollectionUtils; import javax.annotation.Nullable; import java.util.Map; import java.util.Optional; +import static com.google.common.base.Preconditions.checkNotNull; + // Represents the state of the CompensationRequest data // TODO cleanup @Getter @@ -51,6 +57,14 @@ public final class CompensationRequest implements PersistablePayload { private boolean closed; @Setter //TODO private boolean waitingForVotingPeriod; + @Setter + private Coin compensationRequestFee; + @Setter + private Transaction feeTx; + @Setter + Transaction txWithBtcFee; + @Setter + private Transaction signedTx; @Nullable private Map extraDataMap; @@ -108,4 +122,17 @@ public static CompensationRequest fromProto(PB.CompensationRequest proto) { proto.getWaitingForVotingPeriod(), CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap()); } + + /// API + public Coin getRequestedBsq() { + checkNotNull(payload); + return payload.getRequestedBsq(); + } + + public Address getIssuanceAddress (BsqWalletService bsqWalletService) { + checkNotNull(payload); + // Remove leading 'B' + String underlying_btc_address = payload.getBsqAddress().substring(1, payload.getBsqAddress().length()); + return Address.fromBase58(bsqWalletService.getParams(), underlying_btc_address); + } } diff --git a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java index f255b9faaea..2bc5c0f2a26 100644 --- a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java +++ b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequestManager.java @@ -17,18 +17,28 @@ package io.bisq.core.dao.compensation; +import com.google.common.util.concurrent.FutureCallback; import com.google.inject.Inject; import io.bisq.common.UserThread; import io.bisq.common.app.DevEnv; +import io.bisq.common.app.Version; +import io.bisq.common.crypto.Hash; import io.bisq.common.crypto.KeyRing; import io.bisq.common.proto.persistable.PersistedDataHost; import io.bisq.common.storage.Storage; +import io.bisq.common.util.Utilities; import io.bisq.core.app.BisqEnvironment; +import io.bisq.core.btc.exceptions.TransactionVerificationException; +import io.bisq.core.btc.exceptions.WalletException; import io.bisq.core.btc.wallet.BsqWalletService; +import io.bisq.core.btc.wallet.BtcWalletService; +import io.bisq.core.btc.wallet.ChangeBelowDustException; +import io.bisq.core.dao.DaoConstants; import io.bisq.core.dao.DaoPeriodService; import io.bisq.core.dao.blockchain.BsqBlockChainChangeDispatcher; import io.bisq.core.dao.blockchain.BsqBlockChainListener; import io.bisq.core.dao.blockchain.parse.BsqBlockChain; +import io.bisq.core.provider.fee.FeeService; import io.bisq.network.p2p.P2PService; import io.bisq.network.p2p.storage.HashMapChangedListener; import io.bisq.network.p2p.storage.payload.ProtectedStorageEntry; @@ -37,21 +47,33 @@ import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.bitcoinj.core.*; +import org.bitcoinj.crypto.DeterministicKey; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.security.PublicKey; import java.util.Optional; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + public class CompensationRequestManager implements PersistedDataHost, BsqBlockChainListener, HashMapChangedListener { private static final Logger log = LoggerFactory.getLogger(CompensationRequestManager.class); private final P2PService p2PService; private final DaoPeriodService daoPeriodService; private final BsqWalletService bsqWalletService; + private final BtcWalletService btcWalletService; private final BsqBlockChain bsqBlockChain; private final Storage compensationRequestsStorage; private final PublicKey signaturePubKey; + private final FeeService feeService; @Getter private final ObservableList allRequests = FXCollections.observableArrayList(); @@ -68,16 +90,20 @@ public class CompensationRequestManager implements PersistedDataHost, BsqBlockCh @Inject public CompensationRequestManager(P2PService p2PService, BsqWalletService bsqWalletService, + BtcWalletService btcWalletService, DaoPeriodService daoPeriodService, BsqBlockChain bsqBlockChain, BsqBlockChainChangeDispatcher bsqBlockChainChangeDispatcher, KeyRing keyRing, - Storage compensationRequestsStorage) { + Storage compensationRequestsStorage, + FeeService feeService) { this.p2PService = p2PService; - this.daoPeriodService = daoPeriodService; this.bsqWalletService = bsqWalletService; + this.btcWalletService = btcWalletService; + this.daoPeriodService = daoPeriodService; this.bsqBlockChain = bsqBlockChain; this.compensationRequestsStorage = compensationRequestsStorage; + this.feeService = feeService; signaturePubKey = keyRing.getPubKeyRing().getSignaturePubKey(); bsqBlockChainChangeDispatcher.addBsqBlockChainListener(this); @@ -114,6 +140,79 @@ public void addToP2PNetwork(CompensationRequestPayload compensationRequestPayloa p2PService.addProtectedStorageEntry(compensationRequestPayload, true); } + public CompensationRequest prepareCompensationRequest(CompensationRequestPayload compensationRequestPayload) + throws InsufficientMoneyException, ChangeBelowDustException, TransactionVerificationException, WalletException, IOException { + CompensationRequest compensationRequest = new CompensationRequest(compensationRequestPayload); + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + compensationRequest.setCompensationRequestFee(feeService.getCreateCompensationRequestFee()); + compensationRequest.setFeeTx(bsqWalletService.getPreparedBurnFeeTx(compensationRequest.getCompensationRequestFee())); + + String bsqAddress = compensationRequestPayload.getBsqAddress(); + // Remove initial B + bsqAddress = bsqAddress.substring(1, bsqAddress.length()); + checkArgument(!compensationRequest.getFeeTx().getInputs().isEmpty(), "preparedTx inputs must not be empty"); + + // We use the key of the first BSQ input for signing the data + TransactionOutput connectedOutput = compensationRequest.getFeeTx().getInputs().get(0).getConnectedOutput(); + checkNotNull(connectedOutput, "connectedOutput must not be null"); + DeterministicKey bsqKeyPair = bsqWalletService.findKeyFromPubKeyHash(connectedOutput.getScriptPubKey().getPubKeyHash()); + checkNotNull(bsqKeyPair, "bsqKeyPair must not be null"); + + // We get the JSON of the object excluding signature and feeTxId + String payloadAsJson = StringUtils.deleteWhitespace(Utilities.objectToJson(compensationRequestPayload)); + // Signs a text message using the standard Bitcoin messaging signing format and returns the signature as a base64 + // encoded string. + String signature = bsqKeyPair.signMessage(payloadAsJson); + compensationRequestPayload.setSignature(signature); + + String dataAndSig = payloadAsJson + signature; + byte[] dataAndSigAsBytes = dataAndSig.getBytes(); + outputStream.write(DaoConstants.OP_RETURN_TYPE_COMPENSATION_REQUEST); + outputStream.write(Version.COMPENSATION_REQUEST_VERSION); + outputStream.write(Hash.getSha256Ripemd160hash(dataAndSigAsBytes)); + byte opReturnData[] = outputStream.toByteArray(); + + //TODO should we store the hash in the compensationRequestPayload object? + + //TODO 1 Btc output (small payment to own compensation receiving address) + compensationRequest.setTxWithBtcFee( + btcWalletService.completePreparedCompensationRequestTx( + compensationRequest.getRequestedBsq(), + compensationRequest.getIssuanceAddress(bsqWalletService), + compensationRequest.getFeeTx(), + opReturnData)); + if (contains(compensationRequestPayload)) {log.error("Req found");} + compensationRequest.setSignedTx(bsqWalletService.signTx(compensationRequest.getTxWithBtcFee())); + if (contains(compensationRequestPayload)) {log.error("Req found");} + } + if (contains(compensationRequestPayload)) {log.error("Req found");} + return compensationRequest; + } + + public void commitCompensationRequest(CompensationRequest compensationRequest, FutureCallback callback) { + // We need to create another instance, otherwise the tx would trigger an invalid state exception + // if it gets committed 2 times + // We clone before commit to avoid unwanted side effects + final Transaction clonedTransaction = btcWalletService.getClonedTransaction(compensationRequest.getTxWithBtcFee()); + bsqWalletService.commitTx(compensationRequest.getTxWithBtcFee()); + btcWalletService.commitTx(clonedTransaction); + bsqWalletService.broadcastTx(compensationRequest.getSignedTx(), new FutureCallback() { + @Override + public void onSuccess(@Nullable Transaction transaction) { + checkNotNull(transaction, "Transaction must not be null at broadcastTx callback."); + compensationRequest.getPayload().setTxId(transaction.getHashAsString()); + addToP2PNetwork(compensationRequest.getPayload()); + + callback.onSuccess(transaction); + } + + @Override + public void onFailure(@NotNull Throwable t) { + callback.onFailure(t); + } + }); + } + public boolean removeCompensationRequest(CompensationRequest compensationRequest) { final CompensationRequestPayload payload = compensationRequest.getPayload(); // We allow removal which are not confirmed yet or if it we are in the right phase diff --git a/gui/src/main/java/io/bisq/gui/main/dao/compensation/create/CreateCompensationRequestView.java b/gui/src/main/java/io/bisq/gui/main/dao/compensation/create/CreateCompensationRequestView.java index 68f66d411fc..3cbd54c4c3b 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/compensation/create/CreateCompensationRequestView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/compensation/create/CreateCompensationRequestView.java @@ -18,18 +18,12 @@ package io.bisq.gui.main.dao.compensation.create; import com.google.common.util.concurrent.FutureCallback; -import io.bisq.common.app.Version; -import io.bisq.common.crypto.Hash; import io.bisq.common.crypto.KeyRing; import io.bisq.common.locale.Res; -import io.bisq.common.util.Utilities; import io.bisq.core.btc.exceptions.TransactionVerificationException; import io.bisq.core.btc.exceptions.WalletException; -import io.bisq.core.btc.wallet.BsqWalletService; -import io.bisq.core.btc.wallet.BtcWalletService; -import io.bisq.core.btc.wallet.ChangeBelowDustException; -import io.bisq.core.btc.wallet.WalletsSetup; -import io.bisq.core.dao.DaoConstants; +import io.bisq.core.btc.wallet.*; +import io.bisq.core.dao.compensation.CompensationRequest; import io.bisq.core.dao.compensation.CompensationRequestManager; import io.bisq.core.dao.compensation.CompensationRequestPayload; import io.bisq.core.provider.fee.FeeService; @@ -45,20 +39,16 @@ import io.bisq.network.p2p.P2PService; import javafx.scene.control.Button; import javafx.scene.layout.GridPane; -import org.apache.commons.lang3.StringUtils; import org.bitcoinj.core.*; -import org.bitcoinj.crypto.DeterministicKey; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import javax.inject.Inject; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.PublicKey; import java.util.Date; import java.util.UUID; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static io.bisq.gui.util.FormBuilder.addButtonAfterGroup; @@ -136,70 +126,22 @@ protected void activate() { ); boolean walletExceptionMightBeCausedByBtCWallet = false; - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - // TODO move to domain - final Coin compensationRequestFee = feeService.getCreateCompensationRequestFee(); - final Transaction feeTx = bsqWalletService.getPreparedBurnFeeTx(compensationRequestFee); - String bsqAddress = compensationRequestPayload.getBsqAddress(); - // Remove initial B - bsqAddress = bsqAddress.substring(1, bsqAddress.length()); - final Address issuanceAddress = Address.fromBase58(bsqWalletService.getParams(), bsqAddress); - final Coin issuanceAmount = compensationRequestPayload.getRequestedBsq(); - walletExceptionMightBeCausedByBtCWallet = true; - checkArgument(!feeTx.getInputs().isEmpty(), "preparedTx inputs must not be empty"); - - // We use the key of the first BSQ input for signing the data - TransactionOutput connectedOutput = feeTx.getInputs().get(0).getConnectedOutput(); - checkNotNull(connectedOutput, "connectedOutput must not be null"); - DeterministicKey bsqKeyPair = bsqWalletService.findKeyFromPubKeyHash(connectedOutput.getScriptPubKey().getPubKeyHash()); - checkNotNull(bsqKeyPair, "bsqKeyPair must not be null"); - - // We get the JSON of the object excluding signature and feeTxId - String payloadAsJson = StringUtils.deleteWhitespace(Utilities.objectToJson(compensationRequestPayload)); - // Signs a text message using the standard Bitcoin messaging signing format and returns the signature as a base64 - // encoded string. - String signature = bsqKeyPair.signMessage(payloadAsJson); - compensationRequestPayload.setSignature(signature); - - String dataAndSig = payloadAsJson + signature; - byte[] dataAndSigAsBytes = dataAndSig.getBytes(); - outputStream.write(DaoConstants.OP_RETURN_TYPE_COMPENSATION_REQUEST); - outputStream.write(Version.COMPENSATION_REQUEST_VERSION); - outputStream.write(Hash.getSha256Ripemd160hash(dataAndSigAsBytes)); - byte opReturnData[] = outputStream.toByteArray(); - //TODO should we store the hash in the compensationRequestPayload object? - - //TODO 1 Btc output (small payment to own compensation receiving address) - walletExceptionMightBeCausedByBtCWallet = true; - Transaction txWithBtcFee = btcWalletService.completePreparedCompensationRequestTx(issuanceAmount, - issuanceAddress, - feeTx, - opReturnData); - walletExceptionMightBeCausedByBtCWallet = false; - Transaction signedTx = bsqWalletService.signTx(txWithBtcFee); - Coin miningFee = signedTx.getFee(); - int txSize = signedTx.bitcoinSerialize().length; + try { + CompensationRequest compensationRequest = compensationRequestManager.prepareCompensationRequest(compensationRequestPayload); + Coin miningFee = compensationRequest.getSignedTx().getFee(); + int txSize = compensationRequest.getSignedTx().bitcoinSerialize().length; new Popup<>().headLine(Res.get("dao.compensation.create.confirm")) .confirmation(Res.get("dao.compensation.create.confirm.info", - bsqFormatter.formatCoinWithCode(issuanceAmount), - bsqFormatter.formatCoinWithCode(compensationRequestFee), + bsqFormatter.formatCoinWithCode(compensationRequest.getRequestedBsq()), + bsqFormatter.formatCoinWithCode(compensationRequest.getCompensationRequestFee()), btcFormatter.formatCoinWithCode(miningFee), CoinUtil.getFeePerByte(miningFee, txSize), (txSize / 1000d))) .actionButtonText(Res.get("shared.yes")) .onAction(() -> { - // We need to create another instance, otherwise the tx would trigger an invalid state exception - // if it gets committed 2 times - // We clone before commit to avoid unwanted side effects - final Transaction clonedTransaction = btcWalletService.getClonedTransaction(txWithBtcFee); - bsqWalletService.commitTx(txWithBtcFee); - btcWalletService.commitTx(clonedTransaction); - bsqWalletService.broadcastTx(signedTx, new FutureCallback() { + compensationRequestManager.commitCompensationRequest(compensationRequest, new FutureCallback() { @Override public void onSuccess(@Nullable Transaction transaction) { - checkNotNull(transaction, "Transaction must not be null at broadcastTx callback."); - compensationRequestPayload.setTxId(transaction.getHashAsString()); - compensationRequestManager.addToP2PNetwork(compensationRequestPayload); compensationRequestDisplay.clearForm(); new Popup<>().confirmation(Res.get("dao.tx.published.success")).show(); } @@ -214,7 +156,7 @@ public void onFailure(@NotNull Throwable t) { .closeButtonText(Res.get("shared.cancel")) .show(); } catch (InsufficientMoneyException e) { - BSFormatter formatter = walletExceptionMightBeCausedByBtCWallet ? btcFormatter : bsqFormatter; + BSFormatter formatter = e instanceof InsufficientBsqException ? bsqFormatter : btcFormatter; new Popup<>().warning(Res.get("dao.compensation.create.missingFunds", formatter.formatCoinWithCode(e.missing))).show(); } catch (IOException | TransactionVerificationException | WalletException | ChangeBelowDustException e) { log.error(e.toString()); From f29f0e03d73afeeab56f869c7842d9606a0463d0 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 13 Feb 2018 12:28:17 -0500 Subject: [PATCH 55/62] Change Bitcoin Classic to Bitcoin Clashic --- common/src/main/resources/i18n/displayStrings.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 5ecfb2b3a78..529707ff2ca 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -923,7 +923,7 @@ account.altcoin.popup.ZEC.msg=When using {0} you can only use the transparent ad the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. account.altcoin.popup.XZC.msg=When using {0} you can only use the transparent (traceable) addresses not \ the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. -account.altcoin.popup.bch=Bitcoin Cash and Bitcoin Classic suffer from replay protection. If you use those coins be sure you take sufficient precautions and understand all implications.\ +account.altcoin.popup.bch=Bitcoin Cash and Bitcoin Clashic suffer from replay protection. If you use those coins be sure you take sufficient precautions and understand all implications.\ You can suffer losses by sending one coin and unintentionally send the same coins on the other block chain.\ Because those "airdrop coins" share the same history with the Bitcoin blockchain there are also security risks and a considerable risk for losing privacy.\n\n\ Please read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc From d653e8263db108989a241f53c1efa4e1b0cfc768 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 13 Feb 2018 12:37:06 -0500 Subject: [PATCH 56/62] Change text for portfolio.pending.tradePeriodWarning --- common/src/main/resources/i18n/displayStrings.properties | 2 +- common/src/main/resources/i18n/displayStrings_de.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index e143f7a907e..d30e62d2c3a 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -591,7 +591,7 @@ portfolio.pending.tradeInformation=Trade information portfolio.pending.remainingTime=Remaining time portfolio.pending.remainingTimeDetail={0} (until {1}) portfolio.pending.tradePeriodInfo=After the first blockchain confirmation, the trade period starts. Based on the payment method used, a different maximum allowed trade period is applied. -portfolio.pending.tradePeriodWarning=If the period is exceeded the trade will go to arbitration automatically. +portfolio.pending.tradePeriodWarning=If the period is exceeded both trades can open a dispute. portfolio.pending.tradeNotCompleted=Trade not completed in time (until {0}) portfolio.pending.tradeProcess=Trade process portfolio.pending.openAgainDispute.msg=If you are not sure that the message to the arbitrator arrived (e.g. if you did not got a response after 1 day) feel free to open a dispute again. diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index fae0cd7a178..b003ebaa92c 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -547,7 +547,7 @@ portfolio.pending.tradeInformation=Handelsinformationen portfolio.pending.remainingTime=Verbleibende Zeit portfolio.pending.remainingTimeDetail={0} (bis {1}) portfolio.pending.tradePeriodInfo=Die Handelsdauer beginnt mit der ersten Blockchain-Bestätigung. Abhängig von der Zahlungart, wird eine maximale Handesldauer gesetzt. -portfolio.pending.tradePeriodWarning=Beim Überschreiten der Handelsdauer wird automatisch ein Konflikt geöffnet. +portfolio.pending.tradePeriodWarning=Beim Überschreiten der Handelsdauer kann eine Anfrage auf Konfliktlösung geöffnet werden. portfolio.pending.tradeNotCompleted=Maximale Handelsdauer wurde überschritten (bis {0}) portfolio.pending.tradeProcess=Handelsprozess portfolio.pending.openAgainDispute.msg=Falls Sie nicht sicher sind, ob Ihre Nachricht den Vermittler erreicht hat (z.B. wenn Sie nach einem Tag noch keine Rückmeldung erhalten haben) können Sie gerne erneut einen Konflikt öffnen. From c22f52477f708170c87b00ccbcb5b2f445a452f0 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 13 Feb 2018 20:17:09 -0500 Subject: [PATCH 57/62] Add Yenten --- .../io/bisq/common/locale/CurrencyUtil.java | 2 +- .../validation/AltCoinAddressValidator.java | 16 ++-- .../altcoins/YTNAddressValidator.java | 92 +++++++++---------- .../AltCoinAddressValidatorTest.java | 29 +++--- 4 files changed, 66 insertions(+), 73 deletions(-) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index d149d8be3ff..65de3420b42 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -155,8 +155,8 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("CRED", "Verify", true)); result.add(new CryptoCurrency("WAC", "WACoins")); result.add(new CryptoCurrency("WILD", "WILD Token", true)); - result.add(new CryptoCurrency("XZC", "Zcoin")); result.add(new CryptoCurrency("YTN", "Yenten")); + result.add(new CryptoCurrency("XZC", "Zcoin")); result.add(new CryptoCurrency("ZEC", "Zcash")); result.add(new CryptoCurrency("ZEN", "ZenCash")); result.sort(TradeCurrency::compareTo); diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index ad866734ea8..5d593024176 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -196,12 +196,6 @@ public ValidationResult validate(String input) { return regexTestFailed; else return new ValidationResult(true); - case "DAI": - // https://github.com/ethereum/web3.js/blob/master/lib/utils/utils.js#L403 - if (!input.matches("^(0x)?[0-9a-fA-F]{40}$")) - return regexTestFailed; - else - return new ValidationResult(true); case "PIVX": if (input.matches("^[D][a-km-zA-HJ-NP-Z1-9]{25,34}$")) { //noinspection ConstantConditions @@ -440,14 +434,20 @@ public ValidationResult validate(String input) { else return new ValidationResult(true); case "STL": - if(!input.matches("^(Se)\\d[0-9A-Za-z]{94}$")) + if (!input.matches("^(Se)\\d[0-9A-Za-z]{94}$")) + return regexTestFailed; + else + return new ValidationResult(true); + case "DAI": + // https://github.com/ethereum/web3.js/blob/master/lib/utils/utils.js#L403 + if (!input.matches("^(0x)?[0-9a-fA-F]{40}$")) return regexTestFailed; else return new ValidationResult(true); case "YTN": return YTNAddressValidator.ValidateAddress(input); - // Add new coins at the end... + // Add new coins at the end... default: log.debug("Validation for AltCoinAddress not implemented yet. currencyCode: " + currencyCode); return validationResult; diff --git a/gui/src/main/java/io/bisq/gui/util/validation/altcoins/YTNAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/altcoins/YTNAddressValidator.java index 645e20e95f0..da8d08b0508 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/altcoins/YTNAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/altcoins/YTNAddressValidator.java @@ -23,61 +23,53 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; -public class YTNAddressValidator -{ - private final static String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +public class YTNAddressValidator { + private final static String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - public static ValidationResult ValidateAddress(String addr) - { - if (addr.length() != 34) - return new ValidationResult(false, "YTN_Addr_Invalid: Length must be 34!"); - if (!addr.startsWith("Y")) - return new ValidationResult(false, "YTN_Addr_Invalid: must start with 'Y'!"); - byte[] decoded = decodeBase58(addr, 58, 25); - if (decoded == null) - return new ValidationResult(false, "YTN_Addr_Invalid: Base58 decoder error!"); + public static ValidationResult ValidateAddress(String addr) { + if (addr.length() != 34) + return new ValidationResult(false, "YTN_Addr_Invalid: Length must be 34!"); + if (!addr.startsWith("Y")) + return new ValidationResult(false, "YTN_Addr_Invalid: must start with 'Y'!"); + byte[] decoded = decodeBase58(addr, 58, 25); + if (decoded == null) + return new ValidationResult(false, "YTN_Addr_Invalid: Base58 decoder error!"); - byte[] hash = getSha256(decoded, 0, 21, 2); - if(hash == null || !Arrays.equals(Arrays.copyOfRange(hash, 0, 4), Arrays.copyOfRange(decoded, 21, 25))) - return new ValidationResult(false, "YTN_Addr_Invalid: Checksum error!"); - return new ValidationResult(true); - } + byte[] hash = getSha256(decoded, 0, 21, 2); + if (hash == null || !Arrays.equals(Arrays.copyOfRange(hash, 0, 4), Arrays.copyOfRange(decoded, 21, 25))) + return new ValidationResult(false, "YTN_Addr_Invalid: Checksum error!"); + return new ValidationResult(true); + } - private static byte[] decodeBase58(String input, int base, int len) - { - byte[] output = new byte[len]; - for (int i = 0; i < input.length(); i++) - { - char t = input.charAt(i); + private static byte[] decodeBase58(String input, int base, int len) { + byte[] output = new byte[len]; + for (int i = 0; i < input.length(); i++) { + char t = input.charAt(i); - int p = ALPHABET.indexOf(t); - if (p == -1) - return null; - for (int j = len - 1; j >= 0; j--, p /= 256) - { - p += base * (output[j] & 0xFF); - output[j] = (byte) (p % 256); - } - if (p != 0) - return null; - } + int p = ALPHABET.indexOf(t); + if (p == -1) + return null; + for (int j = len - 1; j >= 0; j--, p /= 256) { + p += base * (output[j] & 0xFF); + output[j] = (byte) (p % 256); + } + if (p != 0) + return null; + } - return output; - } + return output; + } - private static byte[] getSha256(byte[] data, int start, int len, int recursion) - { - if (recursion == 0) - return data; + private static byte[] getSha256(byte[] data, int start, int len, int recursion) { + if (recursion == 0) + return data; - try - { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - md.update(Arrays.copyOfRange(data, start, start + len)); - return getSha256(md.digest(), 0, 32, recursion - 1); - } catch (NoSuchAlgorithmException e) - { - return null; - } - } + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(Arrays.copyOfRange(data, start, start + len)); + return getSha256(md.digest(), 0, 32, recursion - 1); + } catch (NoSuchAlgorithmException e) { + return null; + } + } } diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index ccf2118967e..9f185a8edaf 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -128,20 +128,6 @@ public void testETH() { assertFalse(validator.validate("").isValid); } - @Test - public void testDAI() { - AltCoinAddressValidator validator = new AltCoinAddressValidator(); - validator.setCurrencyCode("DAI"); - - assertTrue(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d164135398226").isValid); - assertTrue(validator.validate("2a65Aca4D5fC5B5C859090a6c34d164135398226").isValid); - - assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266").isValid); - assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d16413539822g").isValid); - assertFalse(validator.validate("2a65Aca4D5fC5B5C859090a6c34d16413539822g").isValid); - assertFalse(validator.validate("").isValid); - } - @Test public void testPIVX() { AltCoinAddressValidator validator = new AltCoinAddressValidator(); @@ -597,6 +583,21 @@ public void testSTL() { assertFalse(validator.validate("").isValid); } + // Added 0.6.6 + @Test + public void testDAI() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("DAI"); + + assertTrue(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d164135398226").isValid); + assertTrue(validator.validate("2a65Aca4D5fC5B5C859090a6c34d164135398226").isValid); + + assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266").isValid); + assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d16413539822g").isValid); + assertFalse(validator.validate("2a65Aca4D5fC5B5C859090a6c34d16413539822g").isValid); + assertFalse(validator.validate("").isValid); + } + @Test public void testYTN() { AltCoinAddressValidator validator = new AltCoinAddressValidator(); From 2dd6a91be8dc942c1877066daaa677cc985571dc Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 13 Feb 2018 20:23:25 -0500 Subject: [PATCH 58/62] Merge Obsidian --- .../validation/AltCoinAddressValidator.java | 2 +- .../AltCoinAddressValidatorTest.java | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index d5ad1913771..32c7c4076b5 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -459,7 +459,7 @@ public ValidationResult validate(String input) { return new ValidationResult(false, getErrorMessage(e)); } - // Add new coins at the end... + // Add new coins at the end... default: log.debug("Validation for AltCoinAddress not implemented yet. currencyCode: " + currencyCode); return validationResult; diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index f59d4bc6f61..6d7b4bdaedc 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -429,22 +429,6 @@ public void testCAGE() { assertFalse(validator.validate("").isValid); } - @Test - public void testODN() { - AltCoinAddressValidator validator = new AltCoinAddressValidator(); - validator.setCurrencyCode("ODN"); - - assertTrue(validator.validate("XEfyuzk8yTp5eA9eVUeCW2PFbCFtNb6Jgv").isValid); - assertTrue(validator.validate("XJegzjV2GK9CeQLNNcc5GVSSqTkv1XMxSF").isValid); - assertTrue(validator.validate("XLfvvLuwjUrz2kf5gghEmUPFE3vFvwfEiL").isValid); - assertTrue(validator.validate("XNC1e9TfUApfBsH9JCubioS5XGuwFLbsP4").isValid); - - assertFalse(validator.validate("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq").isValid); - assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266").isValid); - assertFalse(validator.validate("SSnwqFBiyqK1n4BV7kPX86iesev2NobhEo").isValid); - assertFalse(validator.validate("").isValid); - } - @Test public void testCRED() { AltCoinAddressValidator validator = new AltCoinAddressValidator(); @@ -643,4 +627,20 @@ public void testDARX() { assertFalse(validator.validate("rN8spHmkV6ZtROquaTJMRZJujRQkkDNh2G").isValid); assertFalse(validator.validate("1NxrMzHCjG8X9kqTEZBXUNB5PC58DSXAht").isValid); } + + @Test + public void testODN() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("ODN"); + + assertTrue(validator.validate("XEfyuzk8yTp5eA9eVUeCW2PFbCFtNb6Jgv").isValid); + assertTrue(validator.validate("XJegzjV2GK9CeQLNNcc5GVSSqTkv1XMxSF").isValid); + assertTrue(validator.validate("XLfvvLuwjUrz2kf5gghEmUPFE3vFvwfEiL").isValid); + assertTrue(validator.validate("XNC1e9TfUApfBsH9JCubioS5XGuwFLbsP4").isValid); + + assertFalse(validator.validate("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq").isValid); + assertFalse(validator.validate("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266").isValid); + assertFalse(validator.validate("SSnwqFBiyqK1n4BV7kPX86iesev2NobhEo").isValid); + assertFalse(validator.validate("").isValid); + } } From 0a1dc70ec055b4361186f40efe5320b49e962772 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 13 Feb 2018 20:37:26 -0500 Subject: [PATCH 59/62] Fix sorting of altcoins --- .../src/main/java/io/bisq/common/locale/CurrencyUtil.java | 6 +++--- .../bisq/core/trade/statistics/TradeStatisticsManager.java | 2 ++ .../gui/util/validation/AltCoinAddressValidatorTest.java | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index f61e7e5c2cb..9e3887f564c 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -96,6 +96,7 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("BCH", "Bitcoin Cash")); result.add(new CryptoCurrency("BCHC", "Bitcoin Clashic")); result.add(new CryptoCurrency("BTG", "Bitcoin Gold")); + result.add(new CryptoCurrency("DARX", "BitDaric")); result.add(new CryptoCurrency("BURST", "Burstcoin")); result.add(new CryptoCurrency("GBYTE", "Byte")); result.add(new CryptoCurrency("CAGE", "Cagecoin")); @@ -148,8 +149,8 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("SIB", "Sibcoin")); result.add(new CryptoCurrency("XSPEC", "Spectrecoin")); result.add(new CryptoCurrency("STEEM", "STEEM")); - result.add(new CryptoCurrency("STL", "Stellite")); - result.add(new CryptoCurrency("TRC", "Terracoin")); + result.add(new CryptoCurrency("STL", "Stellite")); + result.add(new CryptoCurrency("TRC", "Terracoin")); result.add(new CryptoCurrency("MVT", "The Movement", true)); result.add(new CryptoCurrency("UNO", "Unobtanium")); @@ -160,7 +161,6 @@ public static List createAllSortedCryptoCurrenciesList() { result.add(new CryptoCurrency("XZC", "Zcoin")); result.add(new CryptoCurrency("ZEC", "Zcash")); result.add(new CryptoCurrency("ZEN", "ZenCash")); - result.add(new CryptoCurrency("DARX", "BitDaric")); result.sort(TradeCurrency::compareTo); // Util for printing all altcoins for adding to FAQ page diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index cbb51bf90b5..aed627af8fa 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -251,6 +251,8 @@ private void printAllCurrencyStats() { newlyAdded.add("BETR"); newlyAdded.add("MVT"); newlyAdded.add("REF"); + // v0.6.6 + newlyAdded.add("STL"); newlyAdded.add("DAI"); newlyAdded.add("YTN"); newlyAdded.add("DARX"); diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index 6d7b4bdaedc..35c5bff1cf3 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -569,6 +569,7 @@ public void testREF() { assertFalse(validator.validate("").isValid); } + // Added 0.6.6 @Test public void testSTL() { AltCoinAddressValidator validator = new AltCoinAddressValidator(); @@ -583,7 +584,6 @@ public void testSTL() { assertFalse(validator.validate("").isValid); } - // Added 0.6.6 @Test public void testDAI() { AltCoinAddressValidator validator = new AltCoinAddressValidator(); From ff629542e7e83c70202078c20edec6a37f5f5878 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 13 Feb 2018 20:56:23 -0500 Subject: [PATCH 60/62] Use camelCase --- .../io/bisq/core/dao/compensation/CompensationRequest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequest.java b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequest.java index 5ba3eabab54..33284b0e1d0 100644 --- a/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequest.java +++ b/core/src/main/java/io/bisq/core/dao/compensation/CompensationRequest.java @@ -132,7 +132,7 @@ public Coin getRequestedBsq() { public Address getIssuanceAddress (BsqWalletService bsqWalletService) { checkNotNull(payload); // Remove leading 'B' - String underlying_btc_address = payload.getBsqAddress().substring(1, payload.getBsqAddress().length()); - return Address.fromBase58(bsqWalletService.getParams(), underlying_btc_address); + String underlyingBtcAddress = payload.getBsqAddress().substring(1, payload.getBsqAddress().length()); + return Address.fromBase58(bsqWalletService.getParams(), underlyingBtcAddress); } } From 899d6aef0261ab89a5b0fa7eb8fc09168ff51c68 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 14 Feb 2018 11:27:05 +0100 Subject: [PATCH 61/62] Add german translation --- common/src/main/resources/i18n/displayStrings_de.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 8c383e9e36e..1443c115858 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -166,6 +166,7 @@ shared.viewContractAsJson=Vertrag im JSON-Format ansehen shared.contract.title=Vertrag für den Handel mit der ID: {0} shared.paymentDetails=Zahlungsdetails des BTC-{0}: shared.securityDeposit=Kaution +shared.yourSecurityDeposit=Deine Kaution shared.contract=Vertrag shared.messageArrived=Nachricht angekommen. shared.messageStoredInMailbox=Nachricht in Postfach gespeichert. From 1075298dc068f3fe64c8b7cac826bfe72d7ea02b Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Wed, 14 Feb 2018 11:29:39 +0100 Subject: [PATCH 62/62] DRYing up code after recent PR merges --- .../bisq/gui/components/FundsTextField.java | 79 ++----------------- .../io/bisq/gui/components/InfoTextField.java | 9 +-- .../offer/createoffer/CreateOfferView.java | 4 +- .../main/offer/takeoffer/TakeOfferView.java | 4 +- 4 files changed, 14 insertions(+), 82 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/components/FundsTextField.java b/gui/src/main/java/io/bisq/gui/components/FundsTextField.java index ae81f782b42..eacfee5e6c9 100644 --- a/gui/src/main/java/io/bisq/gui/components/FundsTextField.java +++ b/gui/src/main/java/io/bisq/gui/components/FundsTextField.java @@ -36,14 +36,10 @@ import java.util.concurrent.TimeUnit; -public class FundsTextField extends AnchorPane { +public class FundsTextField extends InfoTextField { public static final Logger log = LoggerFactory.getLogger(FundsTextField.class); - private final StringProperty amount = new SimpleStringProperty(); private final StringProperty fundsStructure = new SimpleStringProperty(); - private final Label infoIcon; - private Boolean hidePopover; - private PopOver infoPopover; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -51,19 +47,9 @@ public class FundsTextField extends AnchorPane { public FundsTextField() { - - TextField textField = new TextField(); - // might be removed if no styling is necessary - textField.setId("amount-text-field"); - textField.setEditable(false); - textField.setPromptText(Res.get("createOffer.fundsBox.totalsNeeded.prompt")); - textField.textProperty().bind(Bindings.concat(amount, " ", fundsStructure)); - textField.setFocusTraversable(false); - - infoIcon = new Label(); - infoIcon.setLayoutY(3); - infoIcon.getStyleClass().addAll("icon", "info"); - AwesomeDude.setIcon(infoIcon, AwesomeIcon.INFO_SIGN); + super(); + textField.textProperty().unbind(); + textField.textProperty().bind(Bindings.concat(textProperty(), " ", fundsStructure)); Label copyIcon = new Label(); copyIcon.setLayoutY(3); @@ -71,7 +57,7 @@ public FundsTextField() { Tooltip.install(copyIcon, new Tooltip(Res.get("shared.copyToClipboard"))); AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); copyIcon.setOnMouseClicked(e -> { - String text = getAmount(); + String text = getText(); if (text != null && text.length() > 0) { String copyText; String[] strings = text.split(" "); @@ -87,67 +73,14 @@ public FundsTextField() { AnchorPane.setRightAnchor(copyIcon, 30.0); AnchorPane.setRightAnchor(infoIcon, 62.0); AnchorPane.setRightAnchor(textField, 55.0); - AnchorPane.setLeftAnchor(textField, 0.0); - - getChildren().addAll(textField, infoIcon, copyIcon); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Public - /////////////////////////////////////////////////////////////////////////////////////////// - public void setContentForInfoPopOver(Node node) { - // As we don't use binding here we need to recreate it on mouse over to reflect the current state - infoIcon.setOnMouseEntered(e -> { - hidePopover = false; - showInfoPopOver(node); - }); - infoIcon.setOnMouseExited(e -> { - if (infoPopover != null) - hidePopover = true; - UserThread.runAfter(() -> { - if (hidePopover) { - infoPopover.hide(); - hidePopover = false; - } - },250, TimeUnit.MILLISECONDS); - }); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void showInfoPopOver(Node node) { - node.getStyleClass().add("default-text"); - - if (infoPopover == null) infoPopover = new PopOver(node); - - if (infoIcon.getScene() != null) { - infoPopover.setDetachable(false); - infoPopover.setArrowLocation(PopOver.ArrowLocation.RIGHT_TOP); - infoPopover.setArrowIndent(5); - - infoPopover.show(infoIcon, -17); - } + getChildren().add(copyIcon); } /////////////////////////////////////////////////////////////////////////////////////////// // Getters/Setters /////////////////////////////////////////////////////////////////////////////////////////// - public void setAmount(String amount) { - this.amount.set(amount); - } - - public String getAmount() { - return amount.get(); - } - - public StringProperty amountProperty() { - return amount; - } - public void setFundsStructure(String fundsStructure) { this.fundsStructure.set(fundsStructure); } diff --git a/gui/src/main/java/io/bisq/gui/components/InfoTextField.java b/gui/src/main/java/io/bisq/gui/components/InfoTextField.java index b39619519e6..8e69770d597 100644 --- a/gui/src/main/java/io/bisq/gui/components/InfoTextField.java +++ b/gui/src/main/java/io/bisq/gui/components/InfoTextField.java @@ -20,15 +20,14 @@ public class InfoTextField extends AnchorPane { public static final Logger log = LoggerFactory.getLogger(InfoTextField.class); private final StringProperty text = new SimpleStringProperty(); - private final Label infoIcon; + protected final Label infoIcon; + protected final TextField textField; private Boolean hidePopover; private PopOver infoPopover; public InfoTextField() { - TextField textField = new TextField(); - // might be removed if no styling is necessary + textField = new TextField(); textField.setEditable(false); - textField.setPromptText(Res.get("createOffer.fundsBox.totalsNeeded.prompt")); textField.textProperty().bind(text); textField.setFocusTraversable(false); @@ -97,7 +96,7 @@ public String getText() { return text.get(); } - public StringProperty amountProperty() { + public StringProperty textProperty() { return text; } } diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java index 2a65908cfab..62ba1e8ff9d 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java @@ -496,7 +496,7 @@ private void addBindings() { marketBasedPriceTextField.textProperty().bindBidirectional(model.marketPriceMargin); volumeTextField.textProperty().bindBidirectional(model.volume); volumeTextField.promptTextProperty().bind(model.volumePromptLabel); - totalToPayTextField.amountProperty().bind(model.totalToPay); + totalToPayTextField.textProperty().bind(model.totalToPay); addressTextField.amountAsCoinProperty().bind(model.dataModel.getMissingCoin()); buyerSecurityDepositInputTextField.textProperty().bindBidirectional(model.buyerSecurityDeposit); @@ -544,7 +544,7 @@ private void removeBindings() { marketBasedPriceLabel.prefWidthProperty().unbind(); volumeTextField.textProperty().unbindBidirectional(model.volume); volumeTextField.promptTextProperty().unbindBidirectional(model.volume); - totalToPayTextField.amountProperty().unbind(); + totalToPayTextField.textProperty().unbind(); addressTextField.amountAsCoinProperty().unbind(); buyerSecurityDepositInputTextField.textProperty().unbindBidirectional(model.buyerSecurityDeposit); diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java index 66fcc9c08f1..34da719ae41 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java @@ -462,7 +462,7 @@ private void close() { private void addBindings() { amountTextField.textProperty().bindBidirectional(model.amount); volumeTextField.textProperty().bindBidirectional(model.volume); - totalToPayTextField.amountProperty().bind(model.totalToPay); + totalToPayTextField.textProperty().bind(model.totalToPay); addressTextField.amountAsCoinProperty().bind(model.dataModel.missingCoin); amountTextField.validationResultProperty().bind(model.amountValidationResult); priceCurrencyLabel.textProperty().bind(createStringBinding(() -> formatter.getCurrencyPair(model.dataModel.getCurrencyCode()))); @@ -481,7 +481,7 @@ private void addBindings() { private void removeBindings() { amountTextField.textProperty().unbindBidirectional(model.amount); volumeTextField.textProperty().unbindBidirectional(model.volume); - totalToPayTextField.amountProperty().unbind(); + totalToPayTextField.textProperty().unbind(); addressTextField.amountAsCoinProperty().unbind(); amountTextField.validationResultProperty().unbind(); priceCurrencyLabel.textProperty().unbind();