From 25ee98dbce15d2510a45599dc2438a65ca939725 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 14 May 2019 15:05:12 +0200 Subject: [PATCH] Fixes https://github.com/bisq-network/bisq/issues/2804 and https://github.com/bisq-network/bisq/issues/2826 --- .../BsqChangeBelowDustException.java | 33 +++++++++++++++++++ .../core/btc/wallet/BsqWalletService.java | 19 +++++++++-- .../resources/i18n/displayStrings.properties | 9 +++++ .../main/dao/wallet/send/BsqSendView.java | 16 ++++++--- 4 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/bisq/core/btc/exceptions/BsqChangeBelowDustException.java diff --git a/core/src/main/java/bisq/core/btc/exceptions/BsqChangeBelowDustException.java b/core/src/main/java/bisq/core/btc/exceptions/BsqChangeBelowDustException.java new file mode 100644 index 0000000000..d0bf325151 --- /dev/null +++ b/core/src/main/java/bisq/core/btc/exceptions/BsqChangeBelowDustException.java @@ -0,0 +1,33 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.btc.exceptions; + +import org.bitcoinj.core.Coin; + +import lombok.Getter; + +public class BsqChangeBelowDustException extends Exception { + @Getter + private final Coin outputValue; + + public BsqChangeBelowDustException(String message, Coin outputValue) { + super(message); + + this.outputValue = outputValue; + } +} diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java index 43fea1bfb7..49018f8bca 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java @@ -17,6 +17,7 @@ package bisq.core.btc.wallet; +import bisq.core.btc.exceptions.BsqChangeBelowDustException; import bisq.core.btc.exceptions.InsufficientBsqException; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; @@ -475,7 +476,7 @@ public class BsqWalletService extends WalletService implements DaoStateListener /////////////////////////////////////////////////////////////////////////////////////////// public Transaction getPreparedSendBsqTx(String receiverAddress, Coin receiverAmount) - throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException { + throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException { return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector); } @@ -484,12 +485,12 @@ public class BsqWalletService extends WalletService implements DaoStateListener /////////////////////////////////////////////////////////////////////////////////////////// public Transaction getPreparedSendBtcTx(String receiverAddress, Coin receiverAmount) - throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException { + throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException { return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector); } private Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount, CoinSelector coinSelector) - throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException { + throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException { DaoKillSwitch.assertDaoIsNotDisabled(); Transaction tx = new Transaction(params); checkArgument(Restrictions.isAboveDust(receiverAmount), @@ -510,6 +511,18 @@ public class BsqWalletService extends WalletService implements DaoStateListener checkWalletConsistency(wallet); verifyTransaction(tx); // printTx("prepareSendTx", tx); + + // Tx has as first output BSQ and an optional second BSQ change output. + // At that stage we do not have added the BTC inputs so there is no BTC change output here. + if (tx.getOutputs().size() == 2) { + TransactionOutput bsqChangeOutput = tx.getOutputs().get(1); + if (!Restrictions.isAboveDust(bsqChangeOutput.getValue())) { + String msg = "BSQ change output is below dust limit. outputValue=" + bsqChangeOutput.getValue().toFriendlyString(); + log.warn(msg); + throw new BsqChangeBelowDustException(msg, bsqChangeOutput.getValue()); + } + } + return tx; } catch (InsufficientMoneyException e) { log.error(e.toString()); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index fe75da695f..f27c4fc855 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2278,6 +2278,15 @@ popup.warning.noPriceFeedAvailable=There is no price feed available for that cur popup.warning.sendMsgFailed=Sending message to your trading partner failed.\nPlease try again and if it continue to fail report a bug. popup.warning.insufficientBtcFundsForBsqTx=You don''t have sufficient BTC funds for paying the miner fee for that transaction.\n\ Please fund your BTC wallet.\nMissing funds: {0} +popup.warning.bsqChangeBelowDustException=This transaction creates a BSQ change output which is below dust \ + limit (5.46 BSQ) and would be rejected by the bitcoin network.\n\n\ + You need to either send a higher amount to avoid the change output (e.g. by adding the dust amount to your \ + sending amount) or add more BSQ funds to your wallet so you avoid to generate a dust output.\n\n\ + The dust output is {0}. +popup.warning.btcChangeBelowDustException=This transaction creates a change output which is below dust \ + limit (546 Satoshi) and would be rejected by the bitcoin network.\n\n\ + You need to add the dust amount to your sending amount to avoid to generate a dust output.\n\n\ + The dust output is {0}. popup.warning.insufficientBsqFundsForBtcFeePayment=You don''t have sufficient BSQ funds for paying the trade fee in BSQ. \ You can pay the fee in BTC or you need to fund your BSQ wallet. You can buy BSQ in Bisq.\n\n\ diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/send/BsqSendView.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/send/BsqSendView.java index a179c7c64e..24de472f15 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/send/BsqSendView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/send/BsqSendView.java @@ -33,6 +33,7 @@ import bisq.desktop.util.validation.BsqAddressValidator; import bisq.desktop.util.validation.BsqValidator; import bisq.desktop.util.validation.BtcValidator; +import bisq.core.btc.exceptions.BsqChangeBelowDustException; import bisq.core.btc.exceptions.TxBroadcastException; import bisq.core.btc.listeners.BsqBalanceListener; import bisq.core.btc.setup.WalletsSetup; @@ -85,7 +86,7 @@ public class BsqSendView extends ActivatableView implements BsqB private int gridRow = 0; private InputTextField amountInputTextField, btcAmountInputTextField; - private Button sendButton, sendBtcButton; + private Button sendBsqButton, sendBtcButton; private InputTextField receiversAddressInputTextField, receiversBtcAddressInputTextField; private ChangeListener focusOutListener; private TitledGroupBg btcTitledGroupBg; @@ -200,7 +201,7 @@ public class BsqSendView extends ActivatableView implements BsqB bsqValidator.setAvailableBalance(availableConfirmedBalance); boolean isValid = bsqAddressValidator.validate(receiversAddressInputTextField.getText()).isValid && bsqValidator.validate(amountInputTextField.getText()).isValid; - sendButton.setDisable(!isValid); + sendBsqButton.setDisable(!isValid); boolean isBtcValid = btcAddressValidator.validate(receiversBtcAddressInputTextField.getText()).isValid && btcValidator.validate(btcAmountInputTextField.getText()).isValid; @@ -227,9 +228,9 @@ public class BsqSendView extends ActivatableView implements BsqB onUpdateBalances(); }; - sendButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.wallet.send.send")); + sendBsqButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.wallet.send.send")); - sendButton.setOnAction((event) -> { + sendBsqButton.setOnAction((event) -> { // TODO break up in methods if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) { String receiversAddressString = bsqFormatter.getAddressFromBsqAddress(receiversAddressInputTextField.getText()).toString(); @@ -252,6 +253,9 @@ public class BsqSendView extends ActivatableView implements BsqB receiversAddressInputTextField.setText(""); amountInputTextField.setText(""); }); + } catch (BsqChangeBelowDustException e) { + String msg = Res.get("popup.warning.bsqChangeBelowDustException", bsqFormatter.formatCoinWithCode(e.getOutputValue())); + new Popup<>().warning(msg).show(); } catch (Throwable t) { handleError(t); } @@ -314,7 +318,9 @@ public class BsqSendView extends ActivatableView implements BsqB }); } - + } catch (BsqChangeBelowDustException e) { + String msg = Res.get("popup.warning.btcChangeBelowDustException", btcFormatter.formatCoinWithCode(e.getOutputValue())); + new Popup<>().warning(msg).show(); } catch (Throwable t) { handleError(t); }