diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index d6f4a8b4f96..64756692869 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -675,7 +675,7 @@ public Transaction createDelayedUnsignedPayoutTx(Transaction depositTx, Transaction delayedPayoutTx = new Transaction(params); delayedPayoutTx.addInput(p2SHMultiSigOutput); applyLockTime(lockTime, delayedPayoutTx); - Coin outputAmount = depositTx.getOutputSum().subtract(minerFee); + Coin outputAmount = p2SHMultiSigOutput.getValue().subtract(minerFee); delayedPayoutTx.addOutput(outputAmount, Address.fromBase58(params, donationAddressString)); WalletService.printTx("Unsigned delayedPayoutTx ToDonationAddress", delayedPayoutTx); WalletService.verifyTransaction(delayedPayoutTx); @@ -724,11 +724,29 @@ public Transaction finalizeDelayedPayoutTx(Transaction delayedPayoutTx, return delayedPayoutTx; } - public boolean verifiesDepositTxAndDelayedPayoutTx(@SuppressWarnings("unused") Transaction depositTx, - Transaction delayedPayoutTx) { - // todo add more checks - if (delayedPayoutTx.getLockTime() == 0) { - log.error("Time lock is not set"); + public boolean verifiesDepositTxAndDelayedPayoutTx(Transaction depositTx, + Transaction delayedPayoutTx, + long lockTime) { + + TransactionOutput p2SHMultiSigOutput = depositTx.getOutput(0); + if (delayedPayoutTx.getInputs().size() != 1) { + log.error("Number of inputs must be 1"); + return false; + } + + TransactionOutput connectedOutput = delayedPayoutTx.getInput(0).getConnectedOutput(); + if (connectedOutput == null) { + log.error("connectedOutput must not be null"); + return false; + } + + if (!connectedOutput.equals(p2SHMultiSigOutput)) { + log.error("connectedOutput must be p2SHMultiSigOutput"); + return false; + } + + if (delayedPayoutTx.getLockTime() != lockTime) { + log.error("LockTime must match trades lockTime"); return false; } @@ -1228,11 +1246,10 @@ private void applyLockTime(long lockTime, Transaction tx) { private boolean removeDust(Transaction transaction) { List originalTransactionOutputs = transaction.getOutputs(); List keepTransactionOutputs = new ArrayList<>(); - for (TransactionOutput transactionOutput: originalTransactionOutputs) { + for (TransactionOutput transactionOutput : originalTransactionOutputs) { if (transactionOutput.getValue().isLessThan(Restrictions.getMinNonDustOutput())) { log.info("your transaction would have contained a dust output of {}", transactionOutput.toString()); - } - else { + } else { keepTransactionOutputs.add(transactionOutput); } } diff --git a/core/src/main/java/bisq/core/trade/DonationAddressValidation.java b/core/src/main/java/bisq/core/trade/DonationAddressValidation.java new file mode 100644 index 00000000000..1dc8236d032 --- /dev/null +++ b/core/src/main/java/bisq/core/trade/DonationAddressValidation.java @@ -0,0 +1,98 @@ +/* + * 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.trade; + +import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.dao.DaoFacade; +import bisq.core.dao.governance.param.Param; + +import org.bitcoinj.core.Address; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionOutput; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +@Slf4j +public class DonationAddressValidation { + + @Getter + public static class DonationAddressException extends Exception { + private final String addressAsString; + private final String recentDonationAddressString; + private final String defaultDonationAddressString; + + DonationAddressException(String addressAsString, + String recentDonationAddressString, + String defaultDonationAddressString) { + + this.addressAsString = addressAsString; + this.recentDonationAddressString = recentDonationAddressString; + this.defaultDonationAddressString = defaultDonationAddressString; + } + } + + public static class MissingDelayedPayoutTxException extends Exception { + public MissingDelayedPayoutTxException(String msg) { + super(msg); + } + } + + public static void validate(Transaction delayedPayoutTx, + DaoFacade daoFacade, + BtcWalletService btcWalletService) throws DonationAddressException, MissingDelayedPayoutTxException { + if (delayedPayoutTx == null) { + throw new MissingDelayedPayoutTxException("DelayedPayoutTx must not be null"); + } + + // Get most recent donation address. + // We do not support past DAO param addresses to avoid that those receive funds (no bond set up anymore). + // Users who have not synced the DAO cannot trade. + String recentDonationAddressString = daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS); + + // In case the seller has deactivated the DAO the default address will be used. + String defaultDonationAddressString = Param.RECIPIENT_BTC_ADDRESS.getDefaultValue(); + + checkArgument(delayedPayoutTx.getOutputs().size() == 1, + "preparedDelayedPayoutTx must have 1 output"); + + TransactionOutput output = delayedPayoutTx.getOutput(0); + NetworkParameters params = btcWalletService.getParams(); + Address address = output.getAddressFromP2PKHScript(params); + if (address == null) { + // The donation address can be as well be a multisig address. + address = output.getAddressFromP2SH(params); + checkNotNull(address, "address must not be null"); + } + + String addressAsString = address.toString(); + boolean isValid = recentDonationAddressString.equals(addressAsString) || + defaultDonationAddressString.equals(addressAsString); + if (!isValid) { + log.warn("Donation address is invalid." + + "\nAddress used by BTC seller: " + addressAsString + + "\nRecent donation address:" + recentDonationAddressString + + "\nDefault donation address: " + defaultDonationAddressString); + throw new DonationAddressException(addressAsString, recentDonationAddressString, defaultDonationAddressString); + } + } +} diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index f55b8bd0d0c..a981895b0d2 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -297,6 +297,16 @@ private void initPendingTrades() { trade.getId()); tradesWithoutDepositTx.add(trade); } + + try { + DonationAddressValidation.validate(trade.getDelayedPayoutTx(), + daoFacade, + btcWalletService); + } catch (DonationAddressValidation.DonationAddressException | + DonationAddressValidation.MissingDelayedPayoutTxException e) { + // We move it to failed trades so it cannot be continued. + addTradeToFailedTradesList.add(trade); + } } ); diff --git a/core/src/main/java/bisq/core/trade/protocol/BuyerAsMakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/BuyerAsMakerProtocol.java index 7bb9f25cd19..ec5d5547937 100644 --- a/core/src/main/java/bisq/core/trade/protocol/BuyerAsMakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/BuyerAsMakerProtocol.java @@ -37,8 +37,8 @@ import bisq.core.trade.protocol.tasks.buyer.BuyerSetupPayoutTxListener; import bisq.core.trade.protocol.tasks.buyer.BuyerSignPayoutTx; import bisq.core.trade.protocol.tasks.buyer.BuyerSignsDelayedPayoutTx; -import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesDelayedPayoutTx; -import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesDonationAddress; +import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesFinalDelayedPayoutTx; +import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesPreparedDelayedPayoutTx; import bisq.core.trade.protocol.tasks.buyer_as_maker.BuyerAsMakerCreatesAndSignsDepositTx; import bisq.core.trade.protocol.tasks.buyer_as_maker.BuyerAsMakerSendsInputsForDepositTxResponse; import bisq.core.trade.protocol.tasks.maker.MakerCreateAndSignContract; @@ -155,7 +155,7 @@ private void handle(DelayedPayoutTxSignatureRequest tradeMessage, NodeAddress pe errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); taskRunner.addTasks( BuyerProcessDelayedPayoutTxSignatureRequest.class, - BuyerVerifiesDonationAddress.class, + BuyerVerifiesPreparedDelayedPayoutTx.class, BuyerSignsDelayedPayoutTx.class, BuyerSendsDelayedPayoutTxSignatureResponse.class ); @@ -171,7 +171,7 @@ private void handle(DepositTxAndDelayedPayoutTxMessage tradeMessage, NodeAddress errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); taskRunner.addTasks( BuyerProcessDepositTxAndDelayedPayoutTxMessage.class, - BuyerVerifiesDelayedPayoutTx.class, + BuyerVerifiesFinalDelayedPayoutTx.class, PublishTradeStatistics.class ); taskRunner.run(); diff --git a/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java index c9e41e669ae..6665c001ec6 100644 --- a/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java @@ -39,8 +39,8 @@ import bisq.core.trade.protocol.tasks.buyer.BuyerSetupPayoutTxListener; import bisq.core.trade.protocol.tasks.buyer.BuyerSignPayoutTx; import bisq.core.trade.protocol.tasks.buyer.BuyerSignsDelayedPayoutTx; -import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesDelayedPayoutTx; -import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesDonationAddress; +import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesFinalDelayedPayoutTx; +import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesPreparedDelayedPayoutTx; import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerCreatesDepositTxInputs; import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSendsDepositTxMessage; import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSignsDepositTx; @@ -178,7 +178,7 @@ private void handle(DelayedPayoutTxSignatureRequest tradeMessage, NodeAddress se errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); taskRunner.addTasks( BuyerProcessDelayedPayoutTxSignatureRequest.class, - BuyerVerifiesDonationAddress.class, + BuyerVerifiesPreparedDelayedPayoutTx.class, BuyerSignsDelayedPayoutTx.class, BuyerSendsDelayedPayoutTxSignatureResponse.class ); @@ -195,7 +195,7 @@ private void handle(DepositTxAndDelayedPayoutTxMessage tradeMessage, NodeAddress errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); taskRunner.addTasks( BuyerProcessDepositTxAndDelayedPayoutTxMessage.class, - BuyerVerifiesDelayedPayoutTx.class, + BuyerVerifiesFinalDelayedPayoutTx.class, PublishTradeStatistics.class ); taskRunner.run(); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesDonationAddress.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesDonationAddress.java deleted file mode 100644 index c9c19ae3d59..00000000000 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesDonationAddress.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.trade.protocol.tasks.buyer; - -import bisq.core.dao.governance.param.Param; -import bisq.core.trade.Trade; -import bisq.core.trade.protocol.tasks.TradeTask; - -import bisq.common.taskrunner.TaskRunner; - -import org.bitcoinj.core.Address; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionOutput; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkNotNull; - -@Slf4j -public class BuyerVerifiesDonationAddress extends TradeTask { - @SuppressWarnings({"unused"}) - public BuyerVerifiesDonationAddress(TaskRunner taskHandler, Trade trade) { - super(taskHandler, trade); - } - - @Override - protected void run() { - try { - runInterceptHook(); - - Transaction preparedDelayedPayoutTx = checkNotNull(processModel.getPreparedDelayedPayoutTx(), "preparedDelayedPayoutTx must not be null"); - - // Get most recent donation address. - // We do not support past DAO param addresses to avoid that those receive funds (no bond set up anymore). - // Users who have not synced the DAO cannot trade. - String recentDonationAddressString = processModel.getDaoFacade().getParamValue(Param.RECIPIENT_BTC_ADDRESS); - - // In case the seller has deactivated the DAO the default address will be used. - String defaultDonationAddressString = Param.RECIPIENT_BTC_ADDRESS.getDefaultValue(); - - TransactionOutput output = preparedDelayedPayoutTx.getOutput(0); - NetworkParameters params = processModel.getBtcWalletService().getParams(); - Address address = output.getAddressFromP2PKHScript(params); - if (address == null) { - // The donation address can be as well be a multisig address. - address = output.getAddressFromP2SH(params); - checkNotNull(address, "address must not be null"); - } - - String addressAsString = address.toString(); - if (recentDonationAddressString.equals(addressAsString) || - defaultDonationAddressString.equals(addressAsString)) { - complete(); - } else { - failed("Sellers donation address not recognized." + - "\nAddress used by BTC seller: " + addressAsString + - "\nRecent donation address:" + recentDonationAddressString + - "\nDefault donation address: " + defaultDonationAddressString); - } - } catch (Throwable t) { - failed(t); - } - } -} diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java similarity index 85% rename from core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesDelayedPayoutTx.java rename to core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java index fb98001183a..7e643f7bf94 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java @@ -29,9 +29,9 @@ import static com.google.common.base.Preconditions.checkNotNull; @Slf4j -public class BuyerVerifiesDelayedPayoutTx extends TradeTask { +public class BuyerVerifiesFinalDelayedPayoutTx extends TradeTask { @SuppressWarnings({"unused"}) - public BuyerVerifiesDelayedPayoutTx(TaskRunner taskHandler, Trade trade) { + public BuyerVerifiesFinalDelayedPayoutTx(TaskRunner taskHandler, Trade trade) { super(taskHandler, trade); } @@ -42,7 +42,9 @@ protected void run() { Transaction depositTx = checkNotNull(trade.getDepositTx()); Transaction delayedPayoutTx = checkNotNull(trade.getDelayedPayoutTx()); - if (processModel.getTradeWalletService().verifiesDepositTxAndDelayedPayoutTx(depositTx, delayedPayoutTx)) { + if (processModel.getTradeWalletService().verifiesDepositTxAndDelayedPayoutTx(depositTx, + delayedPayoutTx, + trade.getLockTime())) { complete(); } else { failed("DelayedPayoutTx is not spending depositTx correctly"); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesPreparedDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesPreparedDelayedPayoutTx.java new file mode 100644 index 00000000000..bf226a000ac --- /dev/null +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesPreparedDelayedPayoutTx.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 bisq.core.trade.protocol.tasks.buyer; + +import bisq.core.offer.Offer; +import bisq.core.trade.DonationAddressValidation; +import bisq.core.trade.Trade; +import bisq.core.trade.protocol.tasks.TradeTask; + +import bisq.common.taskrunner.TaskRunner; + +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionInput; + +import lombok.extern.slf4j.Slf4j; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +@Slf4j +public class BuyerVerifiesPreparedDelayedPayoutTx extends TradeTask { + @SuppressWarnings({"unused"}) + public BuyerVerifiesPreparedDelayedPayoutTx(TaskRunner taskHandler, Trade trade) { + super(taskHandler, trade); + } + + @Override + protected void run() { + try { + runInterceptHook(); + + Transaction preparedDelayedPayoutTx = processModel.getPreparedDelayedPayoutTx(); + + // Check donation address + DonationAddressValidation.validate(preparedDelayedPayoutTx, + processModel.getDaoFacade(), + processModel.getBtcWalletService()); + + // Check amount + Offer offer = checkNotNull(trade.getOffer()); + Coin msOutputAmount = offer.getBuyerSecurityDeposit() + .add(offer.getSellerSecurityDeposit()) + .add(checkNotNull(trade.getTradeAmount())); + checkArgument(preparedDelayedPayoutTx.getOutput(0).getValue().equals(msOutputAmount), + "output value of deposit tx and delayed payout tx must match"); + + // Check lock time + checkArgument(preparedDelayedPayoutTx.getLockTime() == trade.getLockTime(), + "preparedDelayedPayoutTx lock time must match trade.getLockTime()"); + + // Check seq num + checkArgument(preparedDelayedPayoutTx.getInputs().stream().anyMatch(e -> e.getSequenceNumber() == TransactionInput.NO_SEQUENCE - 1), + "Sequence number must be 0xFFFFFFFE"); + + complete(); + } catch (DonationAddressValidation.DonationAddressException e) { + failed("Sellers donation address is invalid." + + "\nAddress used by BTC seller: " + e.getAddressAsString() + + "\nRecent donation address:" + e.getRecentDonationAddressString() + + "\nDefault donation address: " + e.getDefaultDonationAddressString()); + } catch (DonationAddressValidation.MissingDelayedPayoutTxException e) { + failed(e.getMessage()); + } catch (Throwable t) { + failed(t); + } + } +} diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 47764e19d3a..f6e9f2ef4b1 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -555,6 +555,13 @@ portfolio.tab.history=History portfolio.tab.failed=Failed portfolio.tab.editOpenOffer=Edit offer +portfolio.pending.invalidDonationAddress=The donation address in the delayed payout transaction is invalid.\n\n\ + Please do NOT send the Altcoin or Fiat payment but contact the Bisq developers at 'https://bisq.community' or \ + the Keybase channel.\n\n\ + Address used by BTC seller: {0}\n\ + Recent donation address: {1}\n\ + Default donation address: {2}; + portfolio.pending.step1.waitForConf=Wait for blockchain confirmation portfolio.pending.step2_buyer.startPayment=Start payment portfolio.pending.step2_seller.waitPaymentStarted=Wait until payment has started diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java index cf3aa27dbaf..7d4125a3e2c 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -231,7 +231,7 @@ public void activate() { if (DevEnv.isDevMode()) { UserThread.runAfter(() -> { amount.set("0.001"); - price.set("75000"); // for CNY + price.set("0.0001"); // for BSQ minAmount.set(amount.get()); onFocusOutPriceAsPercentageTextField(true, false); applyMakerFee(); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index 88f45602f13..49d79a0874a 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -31,6 +31,7 @@ import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.dao.DaoFacade; import bisq.core.locale.Res; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; @@ -99,6 +100,7 @@ public class PendingTradesDataModel extends ActivatableDataModel { private final WalletsSetup walletsSetup; @Getter private final AccountAgeWitnessService accountAgeWitnessService; + public final DaoFacade daoFacade; public final Navigation navigation; public final WalletPasswordWindow walletPasswordWindow; private final NotificationCenter notificationCenter; @@ -134,6 +136,7 @@ public PendingTradesDataModel(TradeManager tradeManager, P2PService p2PService, WalletsSetup walletsSetup, AccountAgeWitnessService accountAgeWitnessService, + DaoFacade daoFacade, Navigation navigation, WalletPasswordWindow walletPasswordWindow, NotificationCenter notificationCenter) { @@ -147,6 +150,7 @@ public PendingTradesDataModel(TradeManager tradeManager, this.p2PService = p2PService; this.walletsSetup = walletsSetup; this.accountAgeWitnessService = accountAgeWitnessService; + this.daoFacade = daoFacade; this.navigation = navigation; this.walletPasswordWindow = walletPasswordWindow; this.notificationCenter = notificationCenter; diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep1View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep1View.java index 2c1d75fa501..3d74690eaf6 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep1View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep1View.java @@ -17,10 +17,12 @@ package bisq.desktop.main.portfolio.pendingtrades.steps.buyer; +import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel; import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView; import bisq.core.locale.Res; +import bisq.core.trade.DonationAddressValidation; public class BuyerStep1View extends TradeStepView { @@ -32,6 +34,27 @@ public BuyerStep1View(PendingTradesViewModel model) { super(model); } + @Override + public void activate() { + super.activate(); + + try { + DonationAddressValidation.validate(trade.getDelayedPayoutTx(), + model.dataModel.daoFacade, + model.dataModel.btcWalletService); + } catch (DonationAddressValidation.DonationAddressException e) { + new Popup().warning(Res.get("portfolio.pending.invalidDonationAddress", + e.getAddressAsString(), + e.getRecentDonationAddressString(), + e.getDefaultDonationAddressString())) + .show(); + } catch (DonationAddressValidation.MissingDelayedPayoutTxException e) { + // We don't react on that error as a failed trade might get listed initially but getting removed from the + // trade manager after initPendingTrades which happens after activate might be called. + } + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Info /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java index 80cb21526b5..367e3852610 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java @@ -69,6 +69,7 @@ import bisq.core.payment.payload.PaymentMethod; import bisq.core.payment.payload.USPostalMoneyOrderAccountPayload; import bisq.core.payment.payload.WesternUnionAccountPayload; +import bisq.core.trade.DonationAddressValidation; import bisq.core.trade.Trade; import bisq.core.user.DontShowAgainLookup; @@ -114,6 +115,21 @@ public BuyerStep2View(PendingTradesViewModel model) { public void activate() { super.activate(); + try { + DonationAddressValidation.validate(trade.getDelayedPayoutTx(), + model.dataModel.daoFacade, + model.dataModel.btcWalletService); + } catch (DonationAddressValidation.DonationAddressException e) { + new Popup().warning(Res.get("portfolio.pending.invalidDonationAddress", + e.getAddressAsString(), + e.getRecentDonationAddressString(), + e.getDefaultDonationAddressString())) + .show(); + } catch (DonationAddressValidation.MissingDelayedPayoutTxException ignore) { + // We don't react on that error as a failed trade might get listed initially but getting removed from the + // trade manager after initPendingTrades which happens after activate might be called. + } + if (timeoutTimer != null) timeoutTimer.stop();