From 8ab348c49531192a535c79366e4f11115e9e218c Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 13:06:46 -0500 Subject: [PATCH 01/15] Add check for account age to apply restrictions --- .../payment/AccountAgeWitnessService.java | 1 + .../bisq/core/payment/PaymentAccountUtil.java | 63 +++++++++++++++++-- .../bisq/core/payment/PaymentAccounts.java | 2 +- .../core/payment/payload/PaymentMethod.java | 16 +++++ .../trade/protocol/SellerAsMakerProtocol.java | 2 + .../trade/protocol/SellerAsTakerProtocol.java | 2 + .../tasks/VerifyPeersAccountAgeWitness.java | 2 +- .../seller/SellerVerifiesPeersAccountAge.java | 62 ++++++++++++++++++ .../resources/i18n/displayStrings.properties | 10 +++ .../main/offer/MutableOfferDataModel.java | 32 +++++++--- .../main/offer/offerbook/OfferBookView.java | 19 +++++- .../offer/offerbook/OfferBookViewModel.java | 18 +++++- .../offer/takeoffer/TakeOfferDataModel.java | 2 +- .../pendingtrades/PendingTradesDataModel.java | 7 +++ .../steps/buyer/BuyerStep2View.java | 2 +- .../offerbook/OfferBookViewModelTest.java | 44 ++++++------- 16 files changed, 242 insertions(+), 42 deletions(-) create mode 100644 core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java diff --git a/core/src/main/java/bisq/core/payment/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/payment/AccountAgeWitnessService.java index 405bd694c19..547a9401a35 100644 --- a/core/src/main/java/bisq/core/payment/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/payment/AccountAgeWitnessService.java @@ -63,6 +63,7 @@ public class AccountAgeWitnessService { private static final Date RELEASE = Utilities.getUTCDate(2017, GregorianCalendar.NOVEMBER, 11); public static final Date FULL_ACTIVATION = Utilities.getUTCDate(2018, GregorianCalendar.FEBRUARY, 15); + public static final long SAFE_ACCOUNT_AGE_DATE = Utilities.getUTCDate(2019, GregorianCalendar.MARCH, 15).getTime(); public enum AccountAge { LESS_ONE_MONTH, diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java b/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java index 4b0a08d9092..e30acb63597 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Optional; import java.util.Set; @@ -37,18 +38,68 @@ @Slf4j public class PaymentAccountUtil { - public static boolean isAnyPaymentAccountValidForOffer(Offer offer, Collection paymentAccounts) { - for (PaymentAccount paymentAccount : paymentAccounts) { - if (isPaymentAccountValidForOffer(offer, paymentAccount)) + + public static boolean isRiskyBuyOfferWithImmatureAccountAge(Offer offer, AccountAgeWitnessService accountAgeWitnessService) { + long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMakersAccountAge(offer, new Date()); + return offer.isBuyOffer() && + PaymentMethod.hasChargebackRisk(offer.getPaymentMethod()) && + accountCreationDate > AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE; + } + + public static boolean isSellOfferAndAnyTakerPaymentAccountForOfferMature(Offer offer, + Collection takerPaymentAccounts, + AccountAgeWitnessService accountAgeWitnessService) { + if (offer.isBuyOffer() || !PaymentMethod.hasChargebackRisk(offer.getPaymentMethod())) + return true; + + for (PaymentAccount takerPaymentAccount : takerPaymentAccounts) { + if (isTakerPaymentAccountForOfferMature(offer, takerPaymentAccount, accountAgeWitnessService)) + return true; + } + return false; + } + + private static boolean isTakerPaymentAccountForOfferMature(Offer offer, + PaymentAccount takerPaymentAccount, + AccountAgeWitnessService accountAgeWitnessService) { + long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMyAccountAge(takerPaymentAccount.getPaymentAccountPayload()); + return isTakerPaymentAccountValidForOffer(offer, takerPaymentAccount) && + accountCreationDate <= AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE; + } + + public static boolean hasMakerAnyMatureAccountForBuyOffer(Collection makerPaymentAccounts, + AccountAgeWitnessService accountAgeWitnessService) { + for (PaymentAccount makerPaymentAccount : makerPaymentAccounts) { + if (hasMakerMatureAccountForBuyOffer(makerPaymentAccount, accountAgeWitnessService)) + return true; + } + return false; + } + + private static boolean hasMakerMatureAccountForBuyOffer(PaymentAccount makerPaymentAccount, + AccountAgeWitnessService accountAgeWitnessService) { + if (!PaymentMethod.hasChargebackRisk(makerPaymentAccount.getPaymentMethod())) + return true; + + long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMyAccountAge(makerPaymentAccount.getPaymentAccountPayload()); + return accountCreationDate <= AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE; + } + + public static boolean isAnyTakerPaymentAccountValidForOffer(Offer offer, Collection takerPaymentAccounts) { + for (PaymentAccount takerPaymentAccount : takerPaymentAccounts) { + if (isTakerPaymentAccountValidForOffer(offer, takerPaymentAccount)) return true; } return false; } - public static ObservableList getPossiblePaymentAccounts(Offer offer, Set paymentAccounts) { + public static ObservableList getPossiblePaymentAccounts(Offer offer, + Set paymentAccounts, + AccountAgeWitnessService accountAgeWitnessService) { ObservableList result = FXCollections.observableArrayList(); result.addAll(paymentAccounts.stream() - .filter(paymentAccount -> isPaymentAccountValidForOffer(offer, paymentAccount)) + .filter(paymentAccount -> isTakerPaymentAccountValidForOffer(offer, paymentAccount)) + .filter(paymentAccount -> isTakerPaymentAccountForOfferMature(offer, paymentAccount, accountAgeWitnessService)) .collect(Collectors.toList())); return result; } @@ -61,7 +112,7 @@ public static String getInfoForMismatchingPaymentMethodLimits(Offer offer, Payme "Payment method from offer: " + offer.getPaymentMethod().toString(); } - public static boolean isPaymentAccountValidForOffer(Offer offer, PaymentAccount paymentAccount) { + public static boolean isTakerPaymentAccountValidForOffer(Offer offer, PaymentAccount paymentAccount) { return new ReceiptValidator(offer, paymentAccount).isValid(); } diff --git a/core/src/main/java/bisq/core/payment/PaymentAccounts.java b/core/src/main/java/bisq/core/payment/PaymentAccounts.java index e91c26c72ce..817ba27181f 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccounts.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccounts.java @@ -39,7 +39,7 @@ class PaymentAccounts { private final BiFunction validator; PaymentAccounts(Set accounts, AccountAgeWitnessService service) { - this(accounts, service, PaymentAccountUtil::isPaymentAccountValidForOffer); + this(accounts, service, PaymentAccountUtil::isTakerPaymentAccountValidForOffer); } PaymentAccounts(Set accounts, AccountAgeWitnessService service, diff --git a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java index 23066d18c26..8ba2594ae97 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -316,4 +316,20 @@ public int compareTo(@NotNull Object other) { public boolean isAsset() { return this.equals(BLOCK_CHAINS_INSTANT) || this.equals(BLOCK_CHAINS); } + + public static boolean hasChargebackRisk(PaymentMethod paymentMethod) { + String id = paymentMethod.getId(); + return id.equals(PaymentMethod.SEPA_ID) || + id.equals(PaymentMethod.SEPA_INSTANT_ID) || + id.equals(PaymentMethod.INTERAC_E_TRANSFER_ID) || + id.equals(PaymentMethod.CLEAR_X_CHANGE_ID) || + id.equals(PaymentMethod.REVOLUT_ID) || + id.equals(PaymentMethod.NATIONAL_BANK_ID) || + id.equals(PaymentMethod.SAME_BANK_ID) || + id.equals(PaymentMethod.SPECIFIC_BANKS_ID) || + id.equals(PaymentMethod.CHASE_QUICK_PAY_ID) || + id.equals(PaymentMethod.POPMONEY_ID) || + id.equals(PaymentMethod.MONEY_BEAM_ID) || + id.equals(PaymentMethod.UPHOLD_ID); + } } diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerAsMakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerAsMakerProtocol.java index 1bcd137643c..d7b67649cfe 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerAsMakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerAsMakerProtocol.java @@ -38,6 +38,7 @@ import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage; import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage; import bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx; +import bisq.core.trade.protocol.tasks.seller.SellerVerifiesPeersAccountAge; import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerCreatesAndSignsDepositTx; import bisq.core.util.Validator; @@ -127,6 +128,7 @@ public void handleTakeOfferRequest(TradeMessage tradeMessage, NodeAddress sender CheckIfPeerIsBanned.class, MakerVerifyTakerAccount.class, VerifyPeersAccountAgeWitness.class, + SellerVerifiesPeersAccountAge.class, MakerVerifyTakerFeePayment.class, MakerCreateAndSignContract.class, SellerAsMakerCreatesAndSignsDepositTx.class, diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java index 3a1a963d472..13cf6b19e29 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java @@ -30,6 +30,7 @@ import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage; import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage; import bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx; +import bisq.core.trade.protocol.tasks.seller.SellerVerifiesPeersAccountAge; import bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerCreatesDepositTxInputs; import bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerSignAndPublishDepositTx; import bisq.core.trade.protocol.tasks.taker.CreateTakerFeeTx; @@ -140,6 +141,7 @@ private void handle(PublishDepositTxRequest tradeMessage, NodeAddress sender) { CheckIfPeerIsBanned.class, TakerVerifyMakerAccount.class, VerifyPeersAccountAgeWitness.class, + SellerVerifiesPeersAccountAge.class, TakerVerifyMakerFeePayment.class, TakerVerifyAndSignContract.class, TakerPublishFeeTx.class, diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/VerifyPeersAccountAgeWitness.java b/core/src/main/java/bisq/core/trade/protocol/tasks/VerifyPeersAccountAgeWitness.java index 1b1b4cc9b42..379512ca61d 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/VerifyPeersAccountAgeWitness.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/VerifyPeersAccountAgeWitness.java @@ -45,7 +45,7 @@ protected void run() { try { runInterceptHook(); - if (CurrencyUtil.isFiatCurrency(trade.getOffer().getCurrencyCode())) { + if (trade.getOffer() != null && CurrencyUtil.isFiatCurrency(trade.getOffer().getCurrencyCode())) { final AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); final TradingPeer tradingPeer = processModel.getTradingPeer(); final PaymentAccountPayload peersPaymentAccountPayload = checkNotNull(tradingPeer.getPaymentAccountPayload(), diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java new file mode 100644 index 00000000000..5f763843822 --- /dev/null +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java @@ -0,0 +1,62 @@ +/* + * 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.seller; + +import bisq.core.offer.Offer; +import bisq.core.payment.AccountAgeWitnessService; +import bisq.core.payment.payload.PaymentMethod; +import bisq.core.trade.Trade; +import bisq.core.trade.protocol.tasks.TradeTask; + +import bisq.common.taskrunner.TaskRunner; + +import java.util.Date; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class SellerVerifiesPeersAccountAge extends TradeTask { + + @SuppressWarnings({"WeakerAccess", "unused"}) + public SellerVerifiesPeersAccountAge(TaskRunner taskHandler, Trade trade) { + super(taskHandler, trade); + } + + @Override + protected void run() { + try { + runInterceptHook(); + + Offer offer = trade.getOffer(); + if (offer != null && PaymentMethod.hasChargebackRisk(offer.getPaymentMethod())) { + AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); + long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getTradingPeersAccountAge(trade); + if (accountCreationDate <= AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE) { + complete(); + } else { + failed("Trade process failed because the buyer's payment account was created after March 15th 2019 and the payment method is considered " + + "risky regarding chargeback risk."); + } + } else { + complete(); + } + } catch (Throwable t) { + failed(t); + } + } +} diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 9d5e9d78a86..579fe50b263 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -344,6 +344,16 @@ offerbook.warning.noTradingAccountForCurrency.headline=No trading account for se offerbook.warning.noTradingAccountForCurrency.msg=You don't have a trading account for the selected currency.\nDo you want to create an offer with one of your existing trading accounts? offerbook.warning.noMatchingAccount.headline=No matching trading account. offerbook.warning.noMatchingAccount.msg=You don't have a trading account with the payment method required for that offer.\nYou need to setup a trading account with that payment method if you want to take this offer.\nDo you want to do this now? +offerbook.warning.makerHasNoMatureAccountForBuyOffer=You cannot create an offer because you do not have a payment account which was created before March 15th 2019. \ + The selected payment method is considered risky regarding bank chargeback and we needed to deploy those restrictions for enhanced security.\n\n\ + With the next software release we will provide more protection tools and this restriction for new accounts will be removed. +offerbook.warning.riskyBuyOfferWithImmatureAccountAge=This offer cannot be taken because the maker's payment account \ + was created after March 15th 2019 and the payment method is considered risky regarding bank chargeback and we needed to deploy those restrictions for enhanced security.\n\n\ + With the next software release we will provide more protection tools so that offers with that risk profile can be traded as well. +offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer cannot be taken because your payment account \ + was created after March 15th 2019 and the payment method is considered risky regarding bank chargeback and we needed to deploy those restrictions for enhanced security.\n\n\ + With the next software release we will provide more protection tools so that offers with that risk profile can be traded as well. + offerbook.warning.wrongTradeProtocol=That offer requires a different protocol version as the one used in your version of the software.\n\nPlease check if you have the latest version installed, otherwise the user who created the offer has used an older version.\n\nUsers cannot trade with an incompatible trade protocol version. offerbook.warning.userIgnored=You have added that user's onion address to your ignore list. offerbook.warning.offerBlocked=That offer was blocked by the Bisq developers.\nProbably there is an unhandled bug causing issues when taking that offer. diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java index b153cda451f..bed0d340d73 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -39,6 +39,7 @@ import bisq.core.payment.HalCashAccount; import bisq.core.payment.PaymentAccount; import bisq.core.payment.PaymentAccountUtil; +import bisq.core.payment.payload.PaymentMethod; import bisq.core.provider.fee.FeeService; import bisq.core.provider.price.PriceFeedService; import bisq.core.trade.handlers.TransactionResultHandler; @@ -85,6 +86,7 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -254,16 +256,20 @@ public boolean initWithData(OfferPayload.Direction direction, TradeCurrency trad fillPaymentAccounts(); - PaymentAccount account; + PaymentAccount account = null; PaymentAccount lastSelectedPaymentAccount = getPreselectedPaymentAccount(); if (lastSelectedPaymentAccount != null && lastSelectedPaymentAccount.getTradeCurrencies().contains(tradeCurrency) && user.getPaymentAccounts() != null && - user.getPaymentAccounts().stream().anyMatch(paymentAccount -> paymentAccount.getId().equals(lastSelectedPaymentAccount.getId()))) { + user.getPaymentAccounts().stream().anyMatch(paymentAccount -> paymentAccount.getId().equals(lastSelectedPaymentAccount.getId())) && + isPaymentAccountMatureForBuyOffer(lastSelectedPaymentAccount)) { account = lastSelectedPaymentAccount; } else { - account = user.findFirstPaymentAccountWithCurrency(tradeCurrency); + PaymentAccount firstPaymentAccountWithCurrency = user.findFirstPaymentAccountWithCurrency(tradeCurrency); + if (firstPaymentAccountWithCurrency != null && isPaymentAccountMatureForBuyOffer(firstPaymentAccountWithCurrency)) { + account = firstPaymentAccountWithCurrency; + } } if (account != null) { @@ -271,8 +277,9 @@ public boolean initWithData(OfferPayload.Direction direction, TradeCurrency trad } else { Optional paymentAccountOptional = paymentAccounts.stream().findAny(); if (paymentAccountOptional.isPresent()) { - this.paymentAccount = paymentAccountOptional.get(); - + PaymentAccount anyPaymentAccount = paymentAccountOptional.get(); + if (isPaymentAccountMatureForBuyOffer(anyPaymentAccount)) + this.paymentAccount = anyPaymentAccount; } else { log.warn("PaymentAccount not available. Should never get called as in offer view you should not be able to open a create offer view"); return false; @@ -426,7 +433,7 @@ void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) { } void onPaymentAccountSelected(PaymentAccount paymentAccount) { - if (paymentAccount != null && !this.paymentAccount.equals(paymentAccount)) { + if (paymentAccount != null && !this.paymentAccount.equals(paymentAccount) && isPaymentAccountMatureForBuyOffer(paymentAccount)) { volume.set(null); minVolume.set(null); price.set(null); @@ -444,6 +451,12 @@ void onPaymentAccountSelected(PaymentAccount paymentAccount) { } } + private boolean isPaymentAccountMatureForBuyOffer(PaymentAccount paymentAccount) { + return direction == OfferPayload.Direction.SELL || + !PaymentMethod.hasChargebackRisk(paymentAccount.getPaymentMethod()) || + new Date().getTime() - accountAgeWitnessService.getMyAccountAge(paymentAccount.getPaymentAccountPayload()) <= AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE; + } + private void setTradeCurrencyFromPaymentAccount(PaymentAccount paymentAccount) { if (!paymentAccount.getTradeCurrencies().contains(tradeCurrency)) { if (paymentAccount.getSelectedTradeCurrency() != null) @@ -694,8 +707,11 @@ public void swapTradeToSavings() { } private void fillPaymentAccounts() { - if (user.getPaymentAccounts() != null) - paymentAccounts.setAll(new HashSet<>(user.getPaymentAccounts())); + if (user.getPaymentAccounts() != null) { + paymentAccounts.setAll(new HashSet<>(user.getPaymentAccounts().stream() + .filter(this::isPaymentAccountMatureForBuyOffer) + .collect(Collectors.toSet()))); + } } protected void setAmount(Coin amount) { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index ab533aa6785..c4032527f99 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -425,6 +425,8 @@ private void onCreateOffer() { }) .width(725) .show(); + } else if (!model.hasMakerAnyMatureAccountForBuyOffer()) { + new Popup<>().warning(Res.get("offerbook.warning.makerHasNoMatureAccountForBuyOffer")).show(); } else if (!model.hasAcceptedArbitrators()) { new Popup<>().warning(Res.get("popup.warning.noArbitratorsAvailable")).show(); } else { @@ -435,6 +437,8 @@ private void onCreateOffer() { private void onShowInfo(Offer offer, boolean isPaymentAccountValidForOffer, + boolean isRiskyBuyOfferWithImmatureAccountAge, + boolean isSellOfferAndAnyTakerPaymentAccountForOfferMature, boolean hasSameProtocolVersion, boolean isIgnored, boolean isOfferBanned, @@ -448,6 +452,10 @@ private void onShowInfo(Offer offer, Res.get("offerbook.warning.noMatchingAccount.msg"), FiatAccountsView.class, "navigation.account"); + } else if (isRiskyBuyOfferWithImmatureAccountAge) { + new Popup<>().warning(Res.get("offerbook.warning.riskyBuyOfferWithImmatureAccountAge")).show(); + } else if (!isSellOfferAndAnyTakerPaymentAccountForOfferMature) { + new Popup<>().warning(Res.get("offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature")).show(); } else if (!hasSameProtocolVersion) { new Popup<>().warning(Res.get("offerbook.warning.wrongTradeProtocol")).show(); } else if (isIgnored) { @@ -812,7 +820,8 @@ public TableCell call(TableColumn() { final ImageView iconView = new ImageView(); final AutoTooltipButton button = new AutoTooltipButton(); - boolean isTradable, isPaymentAccountValidForOffer, + boolean isTradable, isPaymentAccountValidForOffer, isRiskyBuyOfferWithImmatureAccountAge, + isSellOfferAndAnyTakerPaymentAccountForOfferMature, hasSameProtocolVersion, isIgnored, isOfferBanned, isCurrencyBanned, isPaymentMethodBanned, isNodeAddressBanned, isInsufficientTradeLimit, requireUpdateToNewVersion; @@ -834,6 +843,8 @@ public void updateItem(final OfferBookListItem newItem, boolean empty) { boolean myOffer = model.isMyOffer(offer); if (tableRow != null) { isPaymentAccountValidForOffer = model.isAnyPaymentAccountValidForOffer(offer); + isRiskyBuyOfferWithImmatureAccountAge = model.isRiskyBuyOfferWithImmatureAccountAge(offer); + isSellOfferAndAnyTakerPaymentAccountForOfferMature = model.isSellOfferAndAnyTakerPaymentAccountForOfferMature(offer); hasSameProtocolVersion = model.hasSameProtocolVersion(offer); isIgnored = model.isIgnored(offer); isOfferBanned = model.isOfferBanned(offer); @@ -843,6 +854,8 @@ public void updateItem(final OfferBookListItem newItem, boolean empty) { requireUpdateToNewVersion = model.requireUpdateToNewVersion(); isInsufficientTradeLimit = model.isInsufficientTradeLimit(offer); isTradable = isPaymentAccountValidForOffer && + !isRiskyBuyOfferWithImmatureAccountAge && + isSellOfferAndAnyTakerPaymentAccountForOfferMature && hasSameProtocolVersion && !isIgnored && !isOfferBanned && @@ -865,6 +878,8 @@ public void updateItem(final OfferBookListItem newItem, boolean empty) { if (!(e.getTarget() instanceof ImageView || e.getTarget() instanceof Canvas)) onShowInfo(offer, isPaymentAccountValidForOffer, + isRiskyBuyOfferWithImmatureAccountAge, + isSellOfferAndAnyTakerPaymentAccountForOfferMature, hasSameProtocolVersion, isIgnored, isOfferBanned, @@ -905,6 +920,8 @@ public void updateItem(final OfferBookListItem newItem, boolean empty) { if (!myOffer && !isTradable) button.setOnAction(e -> onShowInfo(offer, isPaymentAccountValidForOffer, + isRiskyBuyOfferWithImmatureAccountAge, + isSellOfferAndAnyTakerPaymentAccountForOfferMature, hasSameProtocolVersion, isIgnored, isOfferBanned, diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java index e20ab3d03df..cc41f60be9c 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java @@ -505,7 +505,17 @@ boolean hasPaymentAccount() { } boolean isAnyPaymentAccountValidForOffer(Offer offer) { - return user.getPaymentAccounts() != null && PaymentAccountUtil.isAnyPaymentAccountValidForOffer(offer, user.getPaymentAccounts()); + return user.getPaymentAccounts() != null && + PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer(offer, user.getPaymentAccounts()); + } + + boolean isSellOfferAndAnyTakerPaymentAccountForOfferMature(Offer offer) { + return user.getPaymentAccounts() != null && + PaymentAccountUtil.isSellOfferAndAnyTakerPaymentAccountForOfferMature(offer, user.getPaymentAccounts(), accountAgeWitnessService); + } + + boolean isRiskyBuyOfferWithImmatureAccountAge(Offer offer) { + return user.getPaymentAccounts() != null && PaymentAccountUtil.isRiskyBuyOfferWithImmatureAccountAge(offer, accountAgeWitnessService); } boolean hasPaymentAccountForCurrency() { @@ -515,6 +525,12 @@ boolean hasPaymentAccountForCurrency() { user.hasPaymentAccountForCurrency(selectedTradeCurrency); } + boolean hasMakerAnyMatureAccountForBuyOffer() { + return direction == OfferPayload.Direction.SELL || + (user.getPaymentAccounts() != null && + PaymentAccountUtil.hasMakerAnyMatureAccountForBuyOffer(user.getPaymentAccounts(), accountAgeWitnessService)); + } + boolean hasAcceptedArbitrators() { return user.getAcceptedArbitrators() != null && !user.getAcceptedArbitrators().isEmpty(); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java index 46f776a6727..f670eba9de3 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -405,7 +405,7 @@ public Offer getOffer() { ObservableList getPossiblePaymentAccounts() { Set paymentAccounts = user.getPaymentAccounts(); checkNotNull(paymentAccounts, "paymentAccounts must not be null"); - return PaymentAccountUtil.getPossiblePaymentAccounts(offer, paymentAccounts); + return PaymentAccountUtil.getPossiblePaymentAccounts(offer, paymentAccounts, accountAgeWitnessService); } public PaymentAccount getLastSelectedPaymentAccount() { 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 d487aa0e5f0..bddd066fd1a 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 @@ -35,6 +35,7 @@ import bisq.core.locale.Res; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; +import bisq.core.payment.AccountAgeWitnessService; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.trade.BuyerTrade; import bisq.core.trade.SellerTrade; @@ -71,6 +72,8 @@ import java.util.List; import java.util.stream.Collectors; +import lombok.Getter; + import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkArgument; @@ -83,6 +86,8 @@ public class PendingTradesDataModel extends ActivatableDataModel { public final DisputeManager disputeManager; private final P2PService p2PService; private final WalletsSetup walletsSetup; + @Getter + private final AccountAgeWitnessService accountAgeWitnessService; public final Navigation navigation; public final WalletPasswordWindow walletPasswordWindow; private final NotificationCenter notificationCenter; @@ -110,6 +115,7 @@ public PendingTradesDataModel(TradeManager tradeManager, Preferences preferences, P2PService p2PService, WalletsSetup walletsSetup, + AccountAgeWitnessService accountAgeWitnessService, Navigation navigation, WalletPasswordWindow walletPasswordWindow, NotificationCenter notificationCenter) { @@ -120,6 +126,7 @@ public PendingTradesDataModel(TradeManager tradeManager, this.preferences = preferences; this.p2PService = p2PService; this.walletsSetup = walletsSetup; + this.accountAgeWitnessService = accountAgeWitnessService; this.navigation = navigation; this.walletPasswordWindow = walletPasswordWindow; this.notificationCenter = notificationCenter; 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 36b692cec08..6951bdf7c21 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 @@ -301,7 +301,7 @@ protected void addContent() { if (trade != null && model.getUser().getPaymentAccounts() != null) { Offer offer = trade.getOffer(); List possiblePaymentAccounts = PaymentAccountUtil.getPossiblePaymentAccounts(offer, - model.getUser().getPaymentAccounts()); + model.getUser().getPaymentAccounts(), model.dataModel.getAccountAgeWitnessService()); PaymentAccountPayload buyersPaymentAccountPayload = model.dataModel.getBuyersPaymentAccountPayload(); if (buyersPaymentAccountPayload != null && possiblePaymentAccounts.size() > 1) { String id = buyersPaymentAccountPayload.getId(); diff --git a/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java index 507fd409246..bd3338aa2b7 100644 --- a/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java @@ -99,46 +99,46 @@ public void testIsAnyPaymentAccountValidForOffer() { Collection paymentAccounts; paymentAccounts = new ArrayList<>(Collections.singletonList(getSepaAccount("EUR", "DE", "1212324", new ArrayList<>(Arrays.asList("AT", "DE"))))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getSEPAPaymentMethod("EUR", "AT", new ArrayList<>(Arrays.asList("AT", "DE")), "PSK"), paymentAccounts)); // empty paymentAccounts paymentAccounts = new ArrayList<>(); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer(getSEPAPaymentMethod("EUR", "AT", + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer(getSEPAPaymentMethod("EUR", "AT", new ArrayList<>(Arrays.asList("AT", "DE")), "PSK"), paymentAccounts)); // simple cases: same payment methods // offer: alipay paymentAccount: alipay - same country, same currency paymentAccounts = new ArrayList<>(Collections.singletonList(getAliPayAccount("CNY"))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getAliPayPaymentMethod("EUR"), paymentAccounts)); // offer: ether paymentAccount: ether - same country, same currency paymentAccounts = new ArrayList<>(Collections.singletonList(getCryptoAccount("ETH"))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getBlockChainsPaymentMethod("ETH"), paymentAccounts)); // offer: sepa paymentAccount: sepa - same country, same currency paymentAccounts = new ArrayList<>(Collections.singletonList(getSepaAccount("EUR", "AT", "1212324", new ArrayList<>(Arrays.asList("AT", "DE"))))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getSEPAPaymentMethod("EUR", "AT", new ArrayList<>(Arrays.asList("AT", "DE")), "PSK"), paymentAccounts)); // offer: nationalBank paymentAccount: nationalBank - same country, same currency paymentAccounts = new ArrayList<>(Collections.singletonList(getNationalBankAccount("EUR", "AT", "PSK"))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // offer: SameBank paymentAccount: SameBank - same country, same currency paymentAccounts = new ArrayList<>(Collections.singletonList(getSameBankAccount("EUR", "AT", "PSK"))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getSameBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // offer: sepa paymentAccount: sepa - diff. country, same currency paymentAccounts = new ArrayList<>(Collections.singletonList(getSepaAccount("EUR", "DE", "1212324", new ArrayList<>(Arrays.asList("AT", "DE"))))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getSEPAPaymentMethod("EUR", "AT", new ArrayList<>(Arrays.asList("AT", "DE")), "PSK"), paymentAccounts)); @@ -146,79 +146,79 @@ public void testIsAnyPaymentAccountValidForOffer() { // offer: sepa paymentAccount: sepa - same country, same currency paymentAccounts = new ArrayList<>(Collections.singletonList(getSepaAccount("EUR", "AT", "1212324", new ArrayList<>(Arrays.asList("AT", "DE"))))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getSEPAPaymentMethod("EUR", "AT", new ArrayList<>(Arrays.asList("AT", "DE")), "PSK"), paymentAccounts)); // offer: sepa paymentAccount: nationalBank - same country, same currency // wrong method paymentAccounts = new ArrayList<>(Collections.singletonList(getNationalBankAccount("EUR", "AT", "PSK"))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getSEPAPaymentMethod("EUR", "AT", new ArrayList<>(Arrays.asList("AT", "DE")), "PSK"), paymentAccounts)); // wrong currency paymentAccounts = new ArrayList<>(Collections.singletonList(getNationalBankAccount("USD", "US", "XXX"))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // wrong country paymentAccounts = new ArrayList<>(Collections.singletonList(getNationalBankAccount("EUR", "FR", "PSK"))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // sepa wrong country paymentAccounts = new ArrayList<>(Collections.singletonList(getNationalBankAccount("EUR", "CH", "PSK"))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getSEPAPaymentMethod("EUR", "AT", new ArrayList<>(Arrays.asList("AT", "DE")), "PSK"), paymentAccounts)); // sepa wrong currency paymentAccounts = new ArrayList<>(Collections.singletonList(getNationalBankAccount("CHF", "DE", "PSK"))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getSEPAPaymentMethod("EUR", "AT", new ArrayList<>(Arrays.asList("AT", "DE")), "PSK"), paymentAccounts)); // same bank paymentAccounts = new ArrayList<>(Collections.singletonList(getSameBankAccount("EUR", "AT", "PSK"))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // not same bank paymentAccounts = new ArrayList<>(Collections.singletonList(getSameBankAccount("EUR", "AT", "Raika"))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // same bank, wrong country paymentAccounts = new ArrayList<>(Collections.singletonList(getSameBankAccount("EUR", "DE", "PSK"))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // same bank, wrong currency paymentAccounts = new ArrayList<>(Collections.singletonList(getSameBankAccount("USD", "AT", "PSK"))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // spec. bank paymentAccounts = new ArrayList<>(Collections.singletonList(getSpecificBanksAccount("EUR", "AT", "PSK", new ArrayList<>(Arrays.asList("PSK", "Raika"))))); - assertTrue(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertTrue(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // spec. bank, missing bank paymentAccounts = new ArrayList<>(Collections.singletonList(getSpecificBanksAccount("EUR", "AT", "PSK", new ArrayList<>(Collections.singletonList("Raika"))))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // spec. bank, wrong country paymentAccounts = new ArrayList<>(Collections.singletonList(getSpecificBanksAccount("EUR", "FR", "PSK", new ArrayList<>(Arrays.asList("PSK", "Raika"))))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); // spec. bank, wrong currency paymentAccounts = new ArrayList<>(Collections.singletonList(getSpecificBanksAccount("USD", "AT", "PSK", new ArrayList<>(Arrays.asList("PSK", "Raika"))))); - assertFalse(PaymentAccountUtil.isAnyPaymentAccountValidForOffer( + assertFalse(PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer( getNationalBankPaymentMethod("EUR", "AT", "PSK"), paymentAccounts)); //TODO add more tests From 6d6e869eef2c9b532f65632b5788e8a675bbd4b1 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 13:41:26 -0500 Subject: [PATCH 02/15] Revert changes with filtering not mature accounts - We will allow small amounts so we keep all accounts and add the check later when the user click next --- .../main/offer/MutableOfferDataModel.java | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java index bed0d340d73..8c636896d78 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -86,7 +86,6 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; -import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -256,20 +255,16 @@ public boolean initWithData(OfferPayload.Direction direction, TradeCurrency trad fillPaymentAccounts(); - PaymentAccount account = null; + PaymentAccount account; PaymentAccount lastSelectedPaymentAccount = getPreselectedPaymentAccount(); if (lastSelectedPaymentAccount != null && lastSelectedPaymentAccount.getTradeCurrencies().contains(tradeCurrency) && user.getPaymentAccounts() != null && - user.getPaymentAccounts().stream().anyMatch(paymentAccount -> paymentAccount.getId().equals(lastSelectedPaymentAccount.getId())) && - isPaymentAccountMatureForBuyOffer(lastSelectedPaymentAccount)) { + user.getPaymentAccounts().stream().anyMatch(paymentAccount -> paymentAccount.getId().equals(lastSelectedPaymentAccount.getId()))) { account = lastSelectedPaymentAccount; } else { - PaymentAccount firstPaymentAccountWithCurrency = user.findFirstPaymentAccountWithCurrency(tradeCurrency); - if (firstPaymentAccountWithCurrency != null && isPaymentAccountMatureForBuyOffer(firstPaymentAccountWithCurrency)) { - account = firstPaymentAccountWithCurrency; - } + account = user.findFirstPaymentAccountWithCurrency(tradeCurrency); } if (account != null) { @@ -277,9 +272,8 @@ public boolean initWithData(OfferPayload.Direction direction, TradeCurrency trad } else { Optional paymentAccountOptional = paymentAccounts.stream().findAny(); if (paymentAccountOptional.isPresent()) { - PaymentAccount anyPaymentAccount = paymentAccountOptional.get(); - if (isPaymentAccountMatureForBuyOffer(anyPaymentAccount)) - this.paymentAccount = anyPaymentAccount; + this.paymentAccount = paymentAccountOptional.get(); + } else { log.warn("PaymentAccount not available. Should never get called as in offer view you should not be able to open a create offer view"); return false; @@ -433,7 +427,7 @@ void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) { } void onPaymentAccountSelected(PaymentAccount paymentAccount) { - if (paymentAccount != null && !this.paymentAccount.equals(paymentAccount) && isPaymentAccountMatureForBuyOffer(paymentAccount)) { + if (paymentAccount != null && !this.paymentAccount.equals(paymentAccount)) { volume.set(null); minVolume.set(null); price.set(null); @@ -707,11 +701,8 @@ public void swapTradeToSavings() { } private void fillPaymentAccounts() { - if (user.getPaymentAccounts() != null) { - paymentAccounts.setAll(new HashSet<>(user.getPaymentAccounts().stream() - .filter(this::isPaymentAccountMatureForBuyOffer) - .collect(Collectors.toSet()))); - } + if (user.getPaymentAccounts() != null) + paymentAccounts.setAll(new HashSet<>(user.getPaymentAccounts())); } protected void setAmount(Coin amount) { From 4947352c0391c1fc1aa0ec9d1717e05453bb5d40 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 13:44:02 -0500 Subject: [PATCH 03/15] Update text --- .../resources/i18n/displayStrings.properties | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 579fe50b263..f01a3489026 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -343,16 +343,16 @@ offerbook.withdrawFundsHint=You can withdraw the funds you paid in from the {0} offerbook.warning.noTradingAccountForCurrency.headline=No trading account for selected currency offerbook.warning.noTradingAccountForCurrency.msg=You don't have a trading account for the selected currency.\nDo you want to create an offer with one of your existing trading accounts? offerbook.warning.noMatchingAccount.headline=No matching trading account. -offerbook.warning.noMatchingAccount.msg=You don't have a trading account with the payment method required for that offer.\nYou need to setup a trading account with that payment method if you want to take this offer.\nDo you want to do this now? -offerbook.warning.makerHasNoMatureAccountForBuyOffer=You cannot create an offer because you do not have a payment account which was created before March 15th 2019. \ - The selected payment method is considered risky regarding bank chargeback and we needed to deploy those restrictions for enhanced security.\n\n\ - With the next software release we will provide more protection tools and this restriction for new accounts will be removed. + +offerbook.warning.noMatchingAccount.msg=To take this offer, you will need to set up a payment account using this payment method.\n\nWould you like to do this now? offerbook.warning.riskyBuyOfferWithImmatureAccountAge=This offer cannot be taken because the maker's payment account \ - was created after March 15th 2019 and the payment method is considered risky regarding bank chargeback and we needed to deploy those restrictions for enhanced security.\n\n\ - With the next software release we will provide more protection tools so that offers with that risk profile can be traded as well. + was created after March 15th 2019, and the payment method is considered risky for bank chargebacks. We needed to deploy this restriction as a \ + short-term measure for enhanced security.\n\n\ + The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer cannot be taken because your payment account \ - was created after March 15th 2019 and the payment method is considered risky regarding bank chargeback and we needed to deploy those restrictions for enhanced security.\n\n\ - With the next software release we will provide more protection tools so that offers with that risk profile can be traded as well. + was created after March 15th 2019 and the payment method is considered risky for bank chargebacks. We needed to deploy this restriction as a \ + short-term measure for enhanced security.\n\n\ + The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. offerbook.warning.wrongTradeProtocol=That offer requires a different protocol version as the one used in your version of the software.\n\nPlease check if you have the latest version installed, otherwise the user who created the offer has used an older version.\n\nUsers cannot trade with an incompatible trade protocol version. offerbook.warning.userIgnored=You have added that user's onion address to your ignore list. From c74025b409357edbfafdb42f15d49f55c6024f32 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 13:44:14 -0500 Subject: [PATCH 04/15] Remove check for makerHasNoMatureAccountForBuyOffer --- .../java/bisq/desktop/main/offer/offerbook/OfferBookView.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index c4032527f99..0f58615aeab 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -425,8 +425,6 @@ private void onCreateOffer() { }) .width(725) .show(); - } else if (!model.hasMakerAnyMatureAccountForBuyOffer()) { - new Popup<>().warning(Res.get("offerbook.warning.makerHasNoMatureAccountForBuyOffer")).show(); } else if (!model.hasAcceptedArbitrators()) { new Popup<>().warning(Res.get("popup.warning.noArbitratorsAvailable")).show(); } else { From 8e0ec54fb1ef078cd56c4912a2264c4700aca50c Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 15:15:13 -0500 Subject: [PATCH 05/15] Refactor restriction handling --- .../bisq/core/offer/OfferRestrictions.java | 47 +++++++++++++++++ .../core/payment/AccountAgeRestrictions.java | 50 +++++++++++++++++++ .../bisq/core/payment/PaymentAccountUtil.java | 40 +++++++-------- .../seller/SellerVerifiesPeersAccountAge.java | 19 +++---- .../resources/i18n/displayStrings.properties | 20 ++++---- .../main/offer/MutableOfferDataModel.java | 7 --- .../main/offer/offerbook/OfferBookView.java | 6 ++- 7 files changed, 136 insertions(+), 53 deletions(-) create mode 100644 core/src/main/java/bisq/core/offer/OfferRestrictions.java create mode 100644 core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java diff --git a/core/src/main/java/bisq/core/offer/OfferRestrictions.java b/core/src/main/java/bisq/core/offer/OfferRestrictions.java new file mode 100644 index 00000000000..e3f415163b1 --- /dev/null +++ b/core/src/main/java/bisq/core/offer/OfferRestrictions.java @@ -0,0 +1,47 @@ +/* + * 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.offer; + +import bisq.core.payment.payload.PaymentMethod; + +import org.bitcoinj.core.Coin; + +public class OfferRestrictions { + public static Coin TOLERATED_SMALL_TRADE_AMOUNT = Coin.parseCoin("0.01"); + + public static boolean isOfferRisky(Offer offer) { + return offer != null && + offer.isBuyOffer() && + PaymentMethod.hasChargebackRisk(offer.getPaymentMethod()) && + isMinTradeAmountRisky(offer); + } + + public static boolean isSellOfferRisky(Offer offer) { + return offer != null && + PaymentMethod.hasChargebackRisk(offer.getPaymentMethod()) && + isMinTradeAmountRisky(offer); + } + + public static boolean isMinTradeAmountRisky(Offer offer) { + return isAmountRisky(offer.getMinAmount()); + } + + public static boolean isAmountRisky(Coin amount) { + return amount.isGreaterThan(TOLERATED_SMALL_TRADE_AMOUNT); + } +} diff --git a/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java b/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java new file mode 100644 index 00000000000..02715acbbc0 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java @@ -0,0 +1,50 @@ +/* + * 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.payment; + +import bisq.core.offer.Offer; +import bisq.core.trade.Trade; + +import bisq.common.util.Utilities; + +import java.util.Date; +import java.util.GregorianCalendar; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class AccountAgeRestrictions { + public static final long SAFE_ACCOUNT_AGE_DATE = Utilities.getUTCDate(2019, GregorianCalendar.MARCH, 15).getTime(); + + public static boolean isMakersAccountAgeImmature(AccountAgeWitnessService accountAgeWitnessService, Offer offer) { + long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMakersAccountAge(offer, new Date()); + return accountCreationDate > SAFE_ACCOUNT_AGE_DATE; + } + + public static boolean isTradePeersAccountAgeImmature(AccountAgeWitnessService accountAgeWitnessService, Trade trade) { + long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getTradingPeersAccountAge(trade); + return accountCreationDate > SAFE_ACCOUNT_AGE_DATE; + } + + public static boolean isMyAccountAgeImmature(AccountAgeWitnessService accountAgeWitnessService, PaymentAccount myPaymentAccount) { + long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMyAccountAge(myPaymentAccount.getPaymentAccountPayload()); + log.error("isMyAccountAgeImmature {}", accountCreationDate > SAFE_ACCOUNT_AGE_DATE); + + return accountCreationDate > SAFE_ACCOUNT_AGE_DATE; + } +} diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java b/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java index e30acb63597..39007e54c34 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java @@ -19,6 +19,7 @@ import bisq.core.locale.Country; import bisq.core.offer.Offer; +import bisq.core.offer.OfferRestrictions; import bisq.core.payment.payload.PaymentMethod; import javafx.collections.FXCollections; @@ -26,7 +27,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.List; import java.util.Optional; import java.util.Set; @@ -40,49 +40,45 @@ public class PaymentAccountUtil { public static boolean isRiskyBuyOfferWithImmatureAccountAge(Offer offer, AccountAgeWitnessService accountAgeWitnessService) { - long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMakersAccountAge(offer, new Date()); - return offer.isBuyOffer() && - PaymentMethod.hasChargebackRisk(offer.getPaymentMethod()) && - accountCreationDate > AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE; + return OfferRestrictions.isOfferRisky(offer) && + AccountAgeRestrictions.isMakersAccountAgeImmature(accountAgeWitnessService, offer); } public static boolean isSellOfferAndAnyTakerPaymentAccountForOfferMature(Offer offer, Collection takerPaymentAccounts, AccountAgeWitnessService accountAgeWitnessService) { - if (offer.isBuyOffer() || !PaymentMethod.hasChargebackRisk(offer.getPaymentMethod())) + if (!OfferRestrictions.isSellOfferRisky(offer)) { return true; + } for (PaymentAccount takerPaymentAccount : takerPaymentAccounts) { - if (isTakerPaymentAccountForOfferMature(offer, takerPaymentAccount, accountAgeWitnessService)) + if (isTakerAccountForOfferMature(offer, takerPaymentAccount, accountAgeWitnessService)) return true; } return false; } - private static boolean isTakerPaymentAccountForOfferMature(Offer offer, - PaymentAccount takerPaymentAccount, - AccountAgeWitnessService accountAgeWitnessService) { - long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMyAccountAge(takerPaymentAccount.getPaymentAccountPayload()); - return isTakerPaymentAccountValidForOffer(offer, takerPaymentAccount) && - accountCreationDate <= AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE; + private static boolean isTakerAccountForOfferMature(Offer offer, + PaymentAccount takerPaymentAccount, + AccountAgeWitnessService accountAgeWitnessService) { + return !OfferRestrictions.isMinTradeAmountRisky(offer) || + (isTakerPaymentAccountValidForOffer(offer, takerPaymentAccount) && + !AccountAgeRestrictions.isMyAccountAgeImmature(accountAgeWitnessService, takerPaymentAccount)); } public static boolean hasMakerAnyMatureAccountForBuyOffer(Collection makerPaymentAccounts, AccountAgeWitnessService accountAgeWitnessService) { for (PaymentAccount makerPaymentAccount : makerPaymentAccounts) { - if (hasMakerMatureAccountForBuyOffer(makerPaymentAccount, accountAgeWitnessService)) + if (hasMyMatureAccountForBuyOffer(makerPaymentAccount, accountAgeWitnessService)) return true; } return false; } - private static boolean hasMakerMatureAccountForBuyOffer(PaymentAccount makerPaymentAccount, - AccountAgeWitnessService accountAgeWitnessService) { - if (!PaymentMethod.hasChargebackRisk(makerPaymentAccount.getPaymentMethod())) - return true; - - long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMyAccountAge(makerPaymentAccount.getPaymentAccountPayload()); - return accountCreationDate <= AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE; + private static boolean hasMyMatureAccountForBuyOffer(PaymentAccount myPaymentAccount, + AccountAgeWitnessService accountAgeWitnessService) { + return !PaymentMethod.hasChargebackRisk(myPaymentAccount.getPaymentMethod()) || + !AccountAgeRestrictions.isMyAccountAgeImmature(accountAgeWitnessService, myPaymentAccount); } public static boolean isAnyTakerPaymentAccountValidForOffer(Offer offer, Collection takerPaymentAccounts) { @@ -99,7 +95,7 @@ public static ObservableList getPossiblePaymentAccounts(Offer of ObservableList result = FXCollections.observableArrayList(); result.addAll(paymentAccounts.stream() .filter(paymentAccount -> isTakerPaymentAccountValidForOffer(offer, paymentAccount)) - .filter(paymentAccount -> isTakerPaymentAccountForOfferMature(offer, paymentAccount, accountAgeWitnessService)) + .filter(paymentAccount -> isTakerAccountForOfferMature(offer, paymentAccount, accountAgeWitnessService)) .collect(Collectors.toList())); return result; } diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java index 5f763843822..b95d3e4deb1 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java @@ -18,15 +18,13 @@ package bisq.core.trade.protocol.tasks.seller; import bisq.core.offer.Offer; -import bisq.core.payment.AccountAgeWitnessService; -import bisq.core.payment.payload.PaymentMethod; +import bisq.core.offer.OfferRestrictions; +import bisq.core.payment.AccountAgeRestrictions; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; import bisq.common.taskrunner.TaskRunner; -import java.util.Date; - import lombok.extern.slf4j.Slf4j; @Slf4j @@ -43,15 +41,10 @@ protected void run() { runInterceptHook(); Offer offer = trade.getOffer(); - if (offer != null && PaymentMethod.hasChargebackRisk(offer.getPaymentMethod())) { - AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); - long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getTradingPeersAccountAge(trade); - if (accountCreationDate <= AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE) { - complete(); - } else { - failed("Trade process failed because the buyer's payment account was created after March 15th 2019 and the payment method is considered " + - "risky regarding chargeback risk."); - } + if (OfferRestrictions.isOfferRisky(offer) && + AccountAgeRestrictions.isTradePeersAccountAgeImmature(processModel.getAccountAgeWitnessService(), trade)) { + failed("Trade process failed because the buyer's payment account was created after March 15th 2019 and the payment method is considered " + + "risky regarding chargeback risk."); } else { complete(); } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index f01a3489026..c95331f91de 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -343,16 +343,18 @@ offerbook.withdrawFundsHint=You can withdraw the funds you paid in from the {0} offerbook.warning.noTradingAccountForCurrency.headline=No trading account for selected currency offerbook.warning.noTradingAccountForCurrency.msg=You don't have a trading account for the selected currency.\nDo you want to create an offer with one of your existing trading accounts? offerbook.warning.noMatchingAccount.headline=No matching trading account. - offerbook.warning.noMatchingAccount.msg=To take this offer, you will need to set up a payment account using this payment method.\n\nWould you like to do this now? -offerbook.warning.riskyBuyOfferWithImmatureAccountAge=This offer cannot be taken because the maker's payment account \ - was created after March 15th 2019, and the payment method is considered risky for bank chargebacks. We needed to deploy this restriction as a \ - short-term measure for enhanced security.\n\n\ - The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. -offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer cannot be taken because your payment account \ - was created after March 15th 2019 and the payment method is considered risky for bank chargebacks. We needed to deploy this restriction as a \ - short-term measure for enhanced security.\n\n\ - The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. + +offerbook.warning.riskyBuyOfferWithImmatureAccountAge=This offer cannot be taken because of security restrictions:\n\ + - The maker''s payment account was created after March 15th 2019\n\ + - The min. trade amount is above 0.01 BTC\n\ + - The payment method for that offer is considered risky for bank chargebacks\n\n{0} +offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer cannot be taken because of security restrictions:\n\ + - Your payment account was created after March 15th 2019\n\ + - The min. trade amount is above 0.01 BTC\n\ + - The payment method for that offer is considered risky for bank chargebacks\n\n{0}\ +offerbook.warning.newVersionAnnouncement=We needed to deploy this restriction as a short-term measure for enhanced security.\n\n\ + The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. offerbook.warning.wrongTradeProtocol=That offer requires a different protocol version as the one used in your version of the software.\n\nPlease check if you have the latest version installed, otherwise the user who created the offer has used an older version.\n\nUsers cannot trade with an incompatible trade protocol version. offerbook.warning.userIgnored=You have added that user's onion address to your ignore list. diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java index 8c636896d78..b153cda451f 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -39,7 +39,6 @@ import bisq.core.payment.HalCashAccount; import bisq.core.payment.PaymentAccount; import bisq.core.payment.PaymentAccountUtil; -import bisq.core.payment.payload.PaymentMethod; import bisq.core.provider.fee.FeeService; import bisq.core.provider.price.PriceFeedService; import bisq.core.trade.handlers.TransactionResultHandler; @@ -445,12 +444,6 @@ void onPaymentAccountSelected(PaymentAccount paymentAccount) { } } - private boolean isPaymentAccountMatureForBuyOffer(PaymentAccount paymentAccount) { - return direction == OfferPayload.Direction.SELL || - !PaymentMethod.hasChargebackRisk(paymentAccount.getPaymentMethod()) || - new Date().getTime() - accountAgeWitnessService.getMyAccountAge(paymentAccount.getPaymentAccountPayload()) <= AccountAgeWitnessService.SAFE_ACCOUNT_AGE_DATE; - } - private void setTradeCurrencyFromPaymentAccount(PaymentAccount paymentAccount) { if (!paymentAccount.getTradeCurrencies().contains(tradeCurrency)) { if (paymentAccount.getSelectedTradeCurrency() != null) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 0f58615aeab..e9d7db1716d 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -451,9 +451,11 @@ private void onShowInfo(Offer offer, FiatAccountsView.class, "navigation.account"); } else if (isRiskyBuyOfferWithImmatureAccountAge) { - new Popup<>().warning(Res.get("offerbook.warning.riskyBuyOfferWithImmatureAccountAge")).show(); + new Popup<>().warning(Res.get("offerbook.warning.riskyBuyOfferWithImmatureAccountAge", + Res.get("offerbook.warning.newVersionAnnouncement"))).show(); } else if (!isSellOfferAndAnyTakerPaymentAccountForOfferMature) { - new Popup<>().warning(Res.get("offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature")).show(); + new Popup<>().warning(Res.get("offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature", + Res.get("offerbook.warning.newVersionAnnouncement"))).show(); } else if (!hasSameProtocolVersion) { new Popup<>().warning(Res.get("offerbook.warning.wrongTradeProtocol")).show(); } else if (isIgnored) { From 9df54372dc02f0cd4039cc412129c2b34c58d464 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 15:35:02 -0500 Subject: [PATCH 06/15] Fix verify task --- .../main/java/bisq/core/offer/OfferRestrictions.java | 12 ++++++++++++ .../tasks/seller/SellerVerifiesPeersAccountAge.java | 11 ++++++----- .../main/resources/i18n/displayStrings.properties | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/offer/OfferRestrictions.java b/core/src/main/java/bisq/core/offer/OfferRestrictions.java index e3f415163b1..53905478992 100644 --- a/core/src/main/java/bisq/core/offer/OfferRestrictions.java +++ b/core/src/main/java/bisq/core/offer/OfferRestrictions.java @@ -18,6 +18,7 @@ package bisq.core.offer; import bisq.core.payment.payload.PaymentMethod; +import bisq.core.trade.Trade; import org.bitcoinj.core.Coin; @@ -37,6 +38,17 @@ public static boolean isSellOfferRisky(Offer offer) { isMinTradeAmountRisky(offer); } + public static boolean isTradeRisky(Trade trade) { + if (trade == null) + return false; + + Offer offer = trade.getOffer(); + return offer != null && + PaymentMethod.hasChargebackRisk(offer.getPaymentMethod()) && + trade.getTradeAmount() != null && + isAmountRisky(trade.getTradeAmount()); + } + public static boolean isMinTradeAmountRisky(Offer offer) { return isAmountRisky(offer.getMinAmount()); } diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java index b95d3e4deb1..dc3fdd941e7 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerVerifiesPeersAccountAge.java @@ -17,7 +17,6 @@ package bisq.core.trade.protocol.tasks.seller; -import bisq.core.offer.Offer; import bisq.core.offer.OfferRestrictions; import bisq.core.payment.AccountAgeRestrictions; import bisq.core.trade.Trade; @@ -40,11 +39,13 @@ protected void run() { try { runInterceptHook(); - Offer offer = trade.getOffer(); - if (OfferRestrictions.isOfferRisky(offer) && + log.error("SellerVerifiesPeersAccountAge isOfferRisky={} isTradePeersAccountAgeImmature={}", OfferRestrictions.isTradeRisky(trade), AccountAgeRestrictions.isTradePeersAccountAgeImmature(processModel.getAccountAgeWitnessService(), trade)); + if (OfferRestrictions.isTradeRisky(trade) && AccountAgeRestrictions.isTradePeersAccountAgeImmature(processModel.getAccountAgeWitnessService(), trade)) { - failed("Trade process failed because the buyer's payment account was created after March 15th 2019 and the payment method is considered " + - "risky regarding chargeback risk."); + failed("Violation of security restrictions:\n" + + " - The peer's account was created after March 15th 2019\n" + + " - The trade amount is above 0.01 BTC\n" + + " - The payment method for that offer is considered risky for bank chargebacks\n"); } else { complete(); } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index c95331f91de..d119bd21909 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -491,7 +491,7 @@ takeOffer.failed.offererOffline=You cannot take that offer because the maker is takeOffer.warning.connectionToPeerLost=You lost connection to the maker.\nHe might have gone offline or has closed the connection to you because of too many open connections.\n\nIf you can still see his offer in the offerbook you can try to take the offer again. takeOffer.error.noFundsLost=\n\nNo funds have left your wallet yet.\nPlease try to restart your application and check your network connection to see if you can resolve the issue. -takeOffer.error.feePaid=\n\nThe taker fee is already paid. In the worst case you have lost that fee.\nPlease try to restart your application and check your network connection to see if you can resolve the issue. +takeOffer.error.feePaid=\n\nPlease try to restart your application and check your network connection to see if you can resolve the issue. takeOffer.error.depositPublished=\n\nThe deposit transaction is already published.\nPlease try to restart your application and check your network connection to see if you can resolve the issue.\nIf the problem still remains please contact the developers for support. takeOffer.error.payoutPublished=\n\nThe payout transaction is already published.\nPlease try to restart your application and check your network connection to see if you can resolve the issue.\nIf the problem still remains please contact the developers for support. takeOffer.tac=With taking this offer I agree to the trade conditions as defined in this screen. From 08cf993d0f97d17b70197bc803ba3d44aa2286f4 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 16:22:20 -0500 Subject: [PATCH 07/15] Add amount limit to take offer view --- .../bisq/core/payment/AccountAgeRestrictions.java | 11 +++++++++-- .../main/resources/i18n/displayStrings.properties | 12 +++++++++--- .../main/offer/takeoffer/TakeOfferDataModel.java | 13 +++++++------ .../main/offer/takeoffer/TakeOfferViewModel.java | 6 ++++++ .../bisq/desktop/util/validation/BtcValidator.java | 2 ++ 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java b/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java index 02715acbbc0..6b431ae3f13 100644 --- a/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java +++ b/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java @@ -18,6 +18,7 @@ package bisq.core.payment; import bisq.core.offer.Offer; +import bisq.core.offer.OfferRestrictions; import bisq.core.trade.Trade; import bisq.common.util.Utilities; @@ -43,8 +44,14 @@ public static boolean isTradePeersAccountAgeImmature(AccountAgeWitnessService ac public static boolean isMyAccountAgeImmature(AccountAgeWitnessService accountAgeWitnessService, PaymentAccount myPaymentAccount) { long accountCreationDate = new Date().getTime() - accountAgeWitnessService.getMyAccountAge(myPaymentAccount.getPaymentAccountPayload()); - log.error("isMyAccountAgeImmature {}", accountCreationDate > SAFE_ACCOUNT_AGE_DATE); - return accountCreationDate > SAFE_ACCOUNT_AGE_DATE; } + + public static long getMyTradeLimitAtTakeOffer(AccountAgeWitnessService accountAgeWitnessService, PaymentAccount paymentAccount, String currencyCode) { + if (AccountAgeRestrictions.isMyAccountAgeImmature(accountAgeWitnessService, paymentAccount)) { + return OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value; + } else { + return accountAgeWitnessService.getMyTradeLimit(paymentAccount, currencyCode); + } + } } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index d119bd21909..d1d80b1b9ee 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -345,17 +345,23 @@ offerbook.warning.noTradingAccountForCurrency.msg=You don't have a trading accou offerbook.warning.noMatchingAccount.headline=No matching trading account. offerbook.warning.noMatchingAccount.msg=To take this offer, you will need to set up a payment account using this payment method.\n\nWould you like to do this now? -offerbook.warning.riskyBuyOfferWithImmatureAccountAge=This offer cannot be taken because of security restrictions:\n\ +offerbook.warning.riskyBuyOfferWithImmatureAccountAge=This offer cannot be taken because of security restrictions based on those criteria:\n\ - The maker''s payment account was created after March 15th 2019\n\ - The min. trade amount is above 0.01 BTC\n\ - The payment method for that offer is considered risky for bank chargebacks\n\n{0} -offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer cannot be taken because of security restrictions:\n\ + +offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer cannot be taken because of security restrictions based on those criteria:\n\ - Your payment account was created after March 15th 2019\n\ - The min. trade amount is above 0.01 BTC\n\ - - The payment method for that offer is considered risky for bank chargebacks\n\n{0}\ + - The payment method for that offer is considered risky for bank chargebacks\n\n{0} + offerbook.warning.newVersionAnnouncement=We needed to deploy this restriction as a short-term measure for enhanced security.\n\n\ The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. +takeOffer.popup.tradeLimitDueAccountAgeRestriction=The allowed trade amount is limited because of security restrictions based on those criteria:\n\ + - Your payment account was created after March 15th 2019\n\ + - The payment method for that offer is considered risky for bank chargebacks\n\n{0} + offerbook.warning.wrongTradeProtocol=That offer requires a different protocol version as the one used in your version of the software.\n\nPlease check if you have the latest version installed, otherwise the user who created the offer has used an older version.\n\nUsers cannot trade with an incompatible trade protocol version. offerbook.warning.userIgnored=You have added that user's onion address to your ignore list. offerbook.warning.offerBlocked=That offer was blocked by the Bisq developers.\nProbably there is an unhandled bug causing issues when taking that offer. diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java index f670eba9de3..5592e635fa9 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -35,6 +35,7 @@ import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; import bisq.core.offer.OfferUtil; +import bisq.core.payment.AccountAgeRestrictions; import bisq.core.payment.AccountAgeWitnessService; import bisq.core.payment.HalCashAccount; import bisq.core.payment.PaymentAccount; @@ -185,8 +186,7 @@ void initWithData(Offer offer) { checkArgument(!possiblePaymentAccounts.isEmpty(), "possiblePaymentAccounts.isEmpty()"); paymentAccount = getLastSelectedPaymentAccount(); - long myLimit = accountAgeWitnessService.getMyTradeLimit(paymentAccount, getCurrencyCode()); - this.amount.set(Coin.valueOf(Math.min(offer.getAmount().value, myLimit))); + this.amount.set(Coin.valueOf(Math.min(offer.getAmount().value, getMaxTradeLimit()))); securityDeposit = offer.getDirection() == OfferPayload.Direction.SELL ? getBuyerSecurityDeposit() : @@ -373,7 +373,7 @@ public void onPaymentAccountSelected(PaymentAccount paymentAccount) { if (paymentAccount != null) { this.paymentAccount = paymentAccount; - long myLimit = accountAgeWitnessService.getMyTradeLimit(paymentAccount, getCurrencyCode()); + long myLimit = getMaxTradeLimit(); this.amount.set(Coin.valueOf(Math.max(offer.getMinAmount().value, Math.min(amount.get().value, myLimit)))); preferences.setTakeOfferSelectedPaymentAccountId(paymentAccount.getId()); @@ -430,11 +430,12 @@ boolean hasAcceptedArbitrators() { long getMaxTradeLimit() { if (paymentAccount != null) - return accountAgeWitnessService.getMyTradeLimit(paymentAccount, getCurrencyCode()); + return AccountAgeRestrictions.getMyTradeLimitAtTakeOffer(accountAgeWitnessService, paymentAccount, getCurrencyCode()); else return 0; } + /////////////////////////////////////////////////////////////////////////////////////////// // Bindings, listeners /////////////////////////////////////////////////////////////////////////////////////////// @@ -447,6 +448,7 @@ private void removeListeners() { btcWalletService.removeBalanceListener(balanceListener); } + /////////////////////////////////////////////////////////////////////////////////////////// // Utils /////////////////////////////////////////////////////////////////////////////////////////// @@ -468,8 +470,7 @@ else if (CurrencyUtil.isFiatCurrency(getCurrencyCode())) } void applyAmount(Coin amount) { - long myLimit = accountAgeWitnessService.getMyTradeLimit(paymentAccount, getCurrencyCode()); - this.amount.set(Coin.valueOf(Math.min(amount.value, myLimit))); + this.amount.set(Coin.valueOf(Math.min(amount.value, getMaxTradeLimit()))); calculateTotalToPay(); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java index ae96ae4e39a..d1b766d7c5f 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java @@ -34,6 +34,7 @@ import bisq.core.monetary.Volume; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; +import bisq.core.offer.OfferRestrictions; import bisq.core.offer.OfferUtil; import bisq.core.payment.PaymentAccount; import bisq.core.payment.payload.PaymentMethod; @@ -371,6 +372,11 @@ void onFocusOutAmountTextField(boolean oldValue, boolean newValue, String userIn if (dataModel.wouldCreateDustForMaker()) amountValidationResult.set(new InputValidator.ValidationResult(false, Res.get("takeOffer.validation.amountLargerThanOfferAmountMinusFee"))); + } else if (btcValidator.getMaxTradeLimit() != null && btcValidator.getMaxTradeLimit().value == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value) { + new Popup<>().information(Res.get("takeOffer.popup.tradeLimitDueAccountAgeRestriction", + Res.get("offerbook.warning.newVersionAnnouncement"))) + .width(900) + .show(); } } } diff --git a/desktop/src/main/java/bisq/desktop/util/validation/BtcValidator.java b/desktop/src/main/java/bisq/desktop/util/validation/BtcValidator.java index f7586fa4db1..d1b8d3d8173 100644 --- a/desktop/src/main/java/bisq/desktop/util/validation/BtcValidator.java +++ b/desktop/src/main/java/bisq/desktop/util/validation/BtcValidator.java @@ -27,6 +27,7 @@ import java.math.BigDecimal; +import lombok.Getter; import lombok.Setter; import javax.annotation.Nullable; @@ -45,6 +46,7 @@ public class BtcValidator extends NumberValidator { @Nullable @Setter + @Getter protected Coin maxTradeLimit; @Inject From e51844a7f1f355d05a9cf4bc8eba3b9c1f89ac3d Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 17:39:58 -0500 Subject: [PATCH 08/15] Improve dispute handling of failed trades --- .../bisq/core/arbitration/DisputeManager.java | 22 ++++++++++++++++--- .../windows/SelectDepositTxWindow.java | 6 +---- .../pendingtrades/PendingTradesDataModel.java | 16 +++++++++----- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/bisq/core/arbitration/DisputeManager.java b/core/src/main/java/bisq/core/arbitration/DisputeManager.java index 3110b5e2f7b..24d1898c801 100644 --- a/core/src/main/java/bisq/core/arbitration/DisputeManager.java +++ b/core/src/main/java/bisq/core/arbitration/DisputeManager.java @@ -878,10 +878,9 @@ private void onDisputeResultMessage(DisputeResultMessage disputeResultMessage) { } return; } - + Dispute dispute = disputeOptional.get(); try { cleanupRetryMap(uid); - Dispute dispute = disputeOptional.get(); arbitratorsPubKeyRing = dispute.getArbitratorPubKeyRing(); DisputeCommunicationMessage disputeCommunicationMessage = disputeResult.getDisputeCommunicationMessage(); if (!dispute.getDisputeCommunicationMessages().contains(disputeCommunicationMessage)) @@ -997,7 +996,24 @@ public void onFailure(TxBroadcastException exception) { success = true; } - } catch (AddressFormatException | WalletException | TransactionVerificationException e) { + } catch (TransactionVerificationException e) { + e.printStackTrace(); + errorMessage = "Error at traderSignAndFinalizeDisputedPayoutTx " + e.toString(); + log.error(errorMessage); + success = false; + + // We prefer to close the dispute in that case. If there was no deposit tx and a random tx was used + // we get a TransactionVerificationException. No reason to keep that dispute open... + if (tradeManager.getTradeById(dispute.getTradeId()).isPresent()) + tradeManager.closeDisputedTrade(dispute.getTradeId()); + else { + Optional openOfferOptional = openOfferManager.getOpenOfferById(dispute.getTradeId()); + openOfferOptional.ifPresent(openOffer -> openOfferManager.closeOpenOffer(openOffer.getOffer())); + } + dispute.setIsClosed(true); + + throw new RuntimeException(errorMessage); + } catch (AddressFormatException | WalletException e) { e.printStackTrace(); errorMessage = "Error at traderSignAndFinalizeDisputedPayoutTx " + e.toString(); log.error(errorMessage); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectDepositTxWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectDepositTxWindow.java index e40cb72f592..25903a8ae20 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectDepositTxWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectDepositTxWindow.java @@ -40,14 +40,10 @@ import java.util.Optional; import java.util.function.Consumer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import static bisq.desktop.util.FormBuilder.addMultilineLabel; +//TODO might be removed, but leave it for now until sure we will not use it anymore. public class SelectDepositTxWindow extends Overlay { - private static final Logger log = LoggerFactory.getLogger(SelectDepositTxWindow.class); - private ComboBox transactionsComboBox; private List transactions; private Optional> selectHandlerOptional; 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 bddd066fd1a..319f8dd8442 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 @@ -23,7 +23,6 @@ import bisq.desktop.main.disputes.DisputesView; import bisq.desktop.main.overlays.notifications.NotificationCenter; import bisq.desktop.main.overlays.popups.Popup; -import bisq.desktop.main.overlays.windows.SelectDepositTxWindow; import bisq.desktop.main.overlays.windows.WalletPasswordWindow; import bisq.desktop.util.GUIUtil; @@ -425,15 +424,22 @@ private void tryOpenDispute(boolean isSupportTicket) { } }); - if (candidates.size() == 1) + if (candidates.size() > 0) { + log.error("Trade.depositTx is null. We take the first possible MultiSig tx just to be able to open a dispute. " + + "candidates={}", candidates); doOpenDispute(isSupportTicket, candidates.get(0)); - else if (candidates.size() > 1) + }/* else if (candidates.size() > 1) { + // Let remove that as it confused users and was from little help new SelectDepositTxWindow().transactions(candidates) .onSelect(transaction -> doOpenDispute(isSupportTicket, transaction)) .closeButtonText(Res.get("shared.cancel")) .show(); - else - log.error("Trade.depositTx is null and we did not find any MultiSig transaction."); + }*/ else if (transactions.size() > 0) { + doOpenDispute(isSupportTicket, transactions.get(0)); + log.error("Trade.depositTx is null and we did not find any MultiSig transaction. We take any random tx just to be able to open a dispute"); + } else { + log.error("Trade.depositTx is null and we did not find any transaction."); + } } } else { log.error("Trade is null"); From e0cf68381803f5efa30d957ab09fe10686497c9f Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 17:46:25 -0500 Subject: [PATCH 09/15] Update text --- .../resources/i18n/displayStrings.properties | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index d1d80b1b9ee..283cc9f877d 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -345,22 +345,22 @@ offerbook.warning.noTradingAccountForCurrency.msg=You don't have a trading accou offerbook.warning.noMatchingAccount.headline=No matching trading account. offerbook.warning.noMatchingAccount.msg=To take this offer, you will need to set up a payment account using this payment method.\n\nWould you like to do this now? -offerbook.warning.riskyBuyOfferWithImmatureAccountAge=This offer cannot be taken because of security restrictions based on those criteria:\n\ - - The maker''s payment account was created after March 15th 2019\n\ - - The min. trade amount is above 0.01 BTC\n\ - - The payment method for that offer is considered risky for bank chargebacks\n\n{0} +offerbook.warning.riskyBuyOfferWithImmatureAccountAge=This offer cannot be taken because of security restrictions based on the following criteria:\n\ + - The maker''s payment account was created after March 15th 2019\n\ + - The minimum trade amount is above 0.01 BTC\n\ + - The payment method for the offer is considered risky for bank chargebacks\n\n{0} -offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer cannot be taken because of security restrictions based on those criteria:\n\ - - Your payment account was created after March 15th 2019\n\ - - The min. trade amount is above 0.01 BTC\n\ - - The payment method for that offer is considered risky for bank chargebacks\n\n{0} +offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer cannot be taken because of security restrictions based on the following criteria:\n\ + - Your payment account was created after March 15th 2019\n\ + - The minimum trade amount is above 0.01 BTC\n\ + - The payment method for this offer is considered risky for bank chargebacks\n\n{0} offerbook.warning.newVersionAnnouncement=We needed to deploy this restriction as a short-term measure for enhanced security.\n\n\ - The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. + The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. -takeOffer.popup.tradeLimitDueAccountAgeRestriction=The allowed trade amount is limited because of security restrictions based on those criteria:\n\ - - Your payment account was created after March 15th 2019\n\ - - The payment method for that offer is considered risky for bank chargebacks\n\n{0} +takeOffer.popup.tradeLimitDueAccountAgeRestriction=The allowed trade amount is limited because of security restrictions based on the following criteria:\n\ + - Your payment account was created after March 15th 2019\n\ + - The payment method for this offer is considered risky for bank chargebacks\n\n{0} offerbook.warning.wrongTradeProtocol=That offer requires a different protocol version as the one used in your version of the software.\n\nPlease check if you have the latest version installed, otherwise the user who created the offer has used an older version.\n\nUsers cannot trade with an incompatible trade protocol version. offerbook.warning.userIgnored=You have added that user's onion address to your ignore list. From c4e30e5aa9afa20f817f94fb6b2c131533a1d814 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 20:54:31 -0500 Subject: [PATCH 10/15] Fix limitations --- .../core/payment/AccountAgeRestrictions.java | 29 +++++++++++++++++-- .../bisq/core/payment/PaymentAccountUtil.java | 3 +- .../resources/i18n/displayStrings.properties | 5 +++- .../main/offer/MutableOfferDataModel.java | 6 ++-- .../main/offer/MutableOfferViewModel.java | 6 ++++ .../offer/offerbook/OfferBookViewModel.java | 2 +- .../offer/takeoffer/TakeOfferDataModel.java | 2 +- .../offer/takeoffer/TakeOfferViewModel.java | 16 +++++++--- 8 files changed, 56 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java b/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java index 6b431ae3f13..6458bc08ddb 100644 --- a/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java +++ b/core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java @@ -18,7 +18,9 @@ package bisq.core.payment; import bisq.core.offer.Offer; +import bisq.core.offer.OfferPayload; import bisq.core.offer.OfferRestrictions; +import bisq.core.payment.payload.PaymentMethod; import bisq.core.trade.Trade; import bisq.common.util.Utilities; @@ -47,8 +49,31 @@ public static boolean isMyAccountAgeImmature(AccountAgeWitnessService accountAge return accountCreationDate > SAFE_ACCOUNT_AGE_DATE; } - public static long getMyTradeLimitAtTakeOffer(AccountAgeWitnessService accountAgeWitnessService, PaymentAccount paymentAccount, String currencyCode) { - if (AccountAgeRestrictions.isMyAccountAgeImmature(accountAgeWitnessService, paymentAccount)) { + public static long getMyTradeLimitAtCreateOffer(AccountAgeWitnessService accountAgeWitnessService, + PaymentAccount paymentAccount, + String currencyCode, + OfferPayload.Direction direction) { + if (direction == OfferPayload.Direction.BUY && + PaymentMethod.hasChargebackRisk(paymentAccount.getPaymentMethod()) && + AccountAgeRestrictions.isMyAccountAgeImmature(accountAgeWitnessService, paymentAccount)) { + return OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value; + } else { + return accountAgeWitnessService.getMyTradeLimit(paymentAccount, currencyCode); + } + } + + public static long getMyTradeLimitAtTakeOffer(AccountAgeWitnessService accountAgeWitnessService, + PaymentAccount paymentAccount, + Offer offer, + String currencyCode, + OfferPayload.Direction direction) { + if (direction == OfferPayload.Direction.BUY && PaymentMethod.hasChargebackRisk(paymentAccount.getPaymentMethod()) && + AccountAgeRestrictions.isMakersAccountAgeImmature(accountAgeWitnessService, offer)) { + // Taker is seller + return OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value; + } else if (direction == OfferPayload.Direction.SELL && PaymentMethod.hasChargebackRisk(paymentAccount.getPaymentMethod()) && + AccountAgeRestrictions.isMyAccountAgeImmature(accountAgeWitnessService, paymentAccount)) { + // Taker is buyer return OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value; } else { return accountAgeWitnessService.getMyTradeLimit(paymentAccount, currencyCode); diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java b/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java index 39007e54c34..96a9e051de4 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccountUtil.java @@ -61,7 +61,8 @@ public static boolean isSellOfferAndAnyTakerPaymentAccountForOfferMature(Offer o private static boolean isTakerAccountForOfferMature(Offer offer, PaymentAccount takerPaymentAccount, AccountAgeWitnessService accountAgeWitnessService) { - return !OfferRestrictions.isMinTradeAmountRisky(offer) || + return !PaymentMethod.hasChargebackRisk(offer.getPaymentMethod()) || + !OfferRestrictions.isMinTradeAmountRisky(offer) || (isTakerPaymentAccountValidForOffer(offer, takerPaymentAccount) && !AccountAgeRestrictions.isMyAccountAgeImmature(accountAgeWitnessService, takerPaymentAccount)); } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 283cc9f877d..a228f56cecd 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -358,7 +358,10 @@ offerbook.warning.sellOfferAndAnyTakerPaymentAccountForOfferMature=This offer ca offerbook.warning.newVersionAnnouncement=We needed to deploy this restriction as a short-term measure for enhanced security.\n\n\ The next software release will provide more robust protection tools so that offers with this risk profile can be traded again. -takeOffer.popup.tradeLimitDueAccountAgeRestriction=The allowed trade amount is limited because of security restrictions based on the following criteria:\n\ +popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to 0.01 BTC because of security restrictions based on the following criteria:\n\ + - The buyers account was created after March 15th 2019\n\ + - The payment method for this offer is considered risky for bank chargebacks\n\n{0} +popup.warning.tradeLimitDueAccountAgeRestriction.buyer=The allowed trade amount is limited to 0.01 BTC because of security restrictions based on the following criteria:\n\ - Your payment account was created after March 15th 2019\n\ - The payment method for this offer is considered risky for bank chargebacks\n\n{0} diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java index b153cda451f..67f5188062d 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -35,6 +35,7 @@ import bisq.core.offer.OfferPayload; import bisq.core.offer.OfferUtil; import bisq.core.offer.OpenOfferManager; +import bisq.core.payment.AccountAgeRestrictions; import bisq.core.payment.AccountAgeWitnessService; import bisq.core.payment.HalCashAccount; import bisq.core.payment.PaymentAccount; @@ -438,9 +439,8 @@ void onPaymentAccountSelected(PaymentAccount paymentAccount) { buyerSecurityDeposit.set(preferences.getBuyerSecurityDepositAsPercent(getPaymentAccount())); - long myLimit = accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get()); if (amount.get() != null) - this.amount.set(Coin.valueOf(Math.min(amount.get().value, myLimit))); + this.amount.set(Coin.valueOf(Math.min(amount.get().value, getMaxTradeLimit()))); } } @@ -581,7 +581,7 @@ boolean isMakerFeeValid() { long getMaxTradeLimit() { if (paymentAccount != null) - return accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get()); + return AccountAgeRestrictions.getMyTradeLimitAtCreateOffer(accountAgeWitnessService, paymentAccount, tradeCurrencyCode.get(), direction); else return 0; } 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 ce3ebc972b5..775d41743d6 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -44,6 +44,7 @@ import bisq.core.monetary.Volume; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; +import bisq.core.offer.OfferRestrictions; import bisq.core.offer.OfferUtil; import bisq.core.payment.PaymentAccount; import bisq.core.provider.price.MarketPrice; @@ -728,6 +729,11 @@ void onFocusOutAmountTextField(boolean oldValue, boolean newValue) { if (minAmount.get() != null) minAmountValidationResult.set(isBtcInputValid(minAmount.get())); + } else if (btcValidator.getMaxTradeLimit() != null && btcValidator.getMaxTradeLimit().value == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value) { + new Popup<>().information(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.buyer", + Res.get("offerbook.warning.newVersionAnnouncement"))) + .width(900) + .show(); } // We want to trigger a recalculation of the volume UserThread.execute(() -> { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java index cc41f60be9c..206ca1c6075 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java @@ -515,7 +515,7 @@ boolean isSellOfferAndAnyTakerPaymentAccountForOfferMature(Offer offer) { } boolean isRiskyBuyOfferWithImmatureAccountAge(Offer offer) { - return user.getPaymentAccounts() != null && PaymentAccountUtil.isRiskyBuyOfferWithImmatureAccountAge(offer, accountAgeWitnessService); + return PaymentAccountUtil.isRiskyBuyOfferWithImmatureAccountAge(offer, accountAgeWitnessService); } boolean hasPaymentAccountForCurrency() { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java index 5592e635fa9..f933d4b624a 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -430,7 +430,7 @@ boolean hasAcceptedArbitrators() { long getMaxTradeLimit() { if (paymentAccount != null) - return AccountAgeRestrictions.getMyTradeLimitAtTakeOffer(accountAgeWitnessService, paymentAccount, getCurrencyCode()); + return AccountAgeRestrictions.getMyTradeLimitAtTakeOffer(accountAgeWitnessService, paymentAccount, offer, getCurrencyCode(), getDirection()); else return 0; } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java index d1b766d7c5f..55dcbe0b2e5 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java @@ -373,10 +373,18 @@ void onFocusOutAmountTextField(boolean oldValue, boolean newValue, String userIn amountValidationResult.set(new InputValidator.ValidationResult(false, Res.get("takeOffer.validation.amountLargerThanOfferAmountMinusFee"))); } else if (btcValidator.getMaxTradeLimit() != null && btcValidator.getMaxTradeLimit().value == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value) { - new Popup<>().information(Res.get("takeOffer.popup.tradeLimitDueAccountAgeRestriction", - Res.get("offerbook.warning.newVersionAnnouncement"))) - .width(900) - .show(); + if (dataModel.getDirection() == OfferPayload.Direction.BUY) { + new Popup<>().information(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.seller", + Res.get("offerbook.warning.newVersionAnnouncement"))) + .width(900) + .show(); + } else { + new Popup<>().information(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.buyer", + Res.get("offerbook.warning.newVersionAnnouncement"))) + .width(900) + .show(); + } + } } } From 3b613bebce27651e1942dc13bab3c67e24c8b657 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 21:48:13 -0500 Subject: [PATCH 11/15] Improve handling of filter --- .../main/java/bisq/common/app/Version.java | 1 + .../main/java/bisq/core/app/BisqSetup.java | 25 ++++++++++++++++--- .../java/bisq/core/filter/FilterManager.java | 16 +++++++++++- .../bisq/core/offer/OfferBookService.java | 15 +++++++++++ .../protocol/tasks/CheckIfPeerIsBanned.java | 2 +- .../resources/i18n/displayStrings.properties | 9 ++++++- .../java/bisq/desktop/main/MainViewModel.java | 3 +++ .../offer/offerbook/OfferBookViewModel.java | 2 +- .../offer/takeoffer/TakeOfferDataModel.java | 2 +- 9 files changed, 66 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/bisq/common/app/Version.java b/common/src/main/java/bisq/common/app/Version.java index 00195234ccf..3c7d7aefcf7 100644 --- a/common/src/main/java/bisq/common/app/Version.java +++ b/common/src/main/java/bisq/common/app/Version.java @@ -82,6 +82,7 @@ private static int getSubVersion(String version, int index) { // The version no. of the current protocol. The offer holds that version. // A taker will check the version of the offers to see if his version is compatible. + // Offers created with the old version will become invalid and have to be canceled. // VERSION = 0.5.0 -> TRADE_PROTOCOL_VERSION = 1 public static final int TRADE_PROTOCOL_VERSION = 1; private static int p2pMessageVersion; diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 07668b9b241..26cc912e6c7 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -649,11 +649,28 @@ private void initDomainServices() { filterManager.onAllServicesInitialized(); filterManager.addListener(filter -> { if (filter != null && filterWarningHandler != null) { - if (filter.getSeedNodes() != null && !filter.getSeedNodes().isEmpty()) - filterWarningHandler.accept(Res.get("popup.warning.nodeBanned", Res.get("popup.warning.seed"))); + if (filter.getSeedNodes() != null && !filter.getSeedNodes().isEmpty()) { + log.warn(Res.get("popup.warning.nodeBanned", Res.get("popup.warning.seed"))); + // Lets keep that more silent. Might be used in case a node is unstable and we don't want to confuse users. + // filterWarningHandler.accept(Res.get("popup.warning.nodeBanned", Res.get("popup.warning.seed"))); + } + + if (filter.getPriceRelayNodes() != null && !filter.getPriceRelayNodes().isEmpty()) { + log.warn(Res.get("popup.warning.nodeBanned", Res.get("popup.warning.priceRelay"))); + // Lets keep that more silent. Might be used in case a node is unstable and we don't want to confuse users. + // filterWarningHandler.accept(Res.get("popup.warning.nodeBanned", Res.get("popup.warning.priceRelay"))); + } - if (filter.getPriceRelayNodes() != null && !filter.getPriceRelayNodes().isEmpty()) - filterWarningHandler.accept(Res.get("popup.warning.nodeBanned", Res.get("popup.warning.priceRelay"))); + if (filterManager.requireUpdateToNewVersionForTrading()) { + filterWarningHandler.accept(Res.get("popup.warning.mandatoryUpdate.trading")); + } + + if (filterManager.requireUpdateToNewVersionForDAO()) { + filterWarningHandler.accept(Res.get("popup.warning.mandatoryUpdate.dao")); + } + if (filter.isDisableDao()) { + filterWarningHandler.accept(Res.get("popup.warning.disable.dao")); + } } }); diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 91ed347cce9..f185f11c6ce 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -350,7 +350,7 @@ public boolean isNodeAddressBanned(NodeAddress nodeAddress) { .anyMatch(e -> e.equals(nodeAddress.getFullAddress())); } - public boolean requireUpdateToNewVersion() { + public boolean requireUpdateToNewVersionForTrading() { if (getFilter() == null) { return false; } @@ -364,6 +364,20 @@ public boolean requireUpdateToNewVersion() { return requireUpdateToNewVersion; } + public boolean requireUpdateToNewVersionForDAO() { + if (getFilter() == null) { + return false; + } + + boolean requireUpdateToNewVersion = false; + String disableDaoBelowVersion = getFilter().getDisableDaoBelowVersion(); + if (disableDaoBelowVersion != null && !disableDaoBelowVersion.isEmpty()) { + requireUpdateToNewVersion = Version.isNewVersion(disableDaoBelowVersion); + } + + return requireUpdateToNewVersion; + } + public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload, PaymentAccountFilter[] appliedPaymentAccountFilter) { return getFilter() != null && diff --git a/core/src/main/java/bisq/core/offer/OfferBookService.java b/core/src/main/java/bisq/core/offer/OfferBookService.java index 4865876d260..71b9b9d6e3c 100644 --- a/core/src/main/java/bisq/core/offer/OfferBookService.java +++ b/core/src/main/java/bisq/core/offer/OfferBookService.java @@ -18,6 +18,8 @@ package bisq.core.offer; import bisq.core.app.AppOptionKeys; +import bisq.core.filter.FilterManager; +import bisq.core.locale.Res; import bisq.core.provider.price.PriceFeedService; import bisq.network.p2p.BootstrapListener; @@ -63,6 +65,7 @@ public interface OfferBookChangedListener { private final P2PService p2PService; private final PriceFeedService priceFeedService; private final List offerBookChangedListeners = new LinkedList<>(); + private final FilterManager filterManager; private final JsonFileManager jsonFileManager; @@ -73,10 +76,12 @@ public interface OfferBookChangedListener { @Inject public OfferBookService(P2PService p2PService, PriceFeedService priceFeedService, + FilterManager filterManager, @Named(Storage.STORAGE_DIR) File storageDir, @Named(AppOptionKeys.DUMP_STATISTICS) boolean dumpStatistics) { this.p2PService = p2PService; this.priceFeedService = priceFeedService; + this.filterManager = filterManager; jsonFileManager = new JsonFileManager(storageDir); p2PService.addHashSetChangedListener(new HashMapChangedListener() { @@ -132,6 +137,11 @@ public void onRemoved(Offer offer) { /////////////////////////////////////////////////////////////////////////////////////////// public void addOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { + if (filterManager.requireUpdateToNewVersionForTrading()) { + errorMessageHandler.handleErrorMessage(Res.get("popup.warning.mandatoryUpdate.trading")); + return; + } + boolean result = p2PService.addProtectedStorageEntry(offer.getOfferPayload(), true); if (result) { resultHandler.handleResult(); @@ -141,6 +151,11 @@ public void addOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandl } public void refreshTTL(OfferPayload offerPayload, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { + if (filterManager.requireUpdateToNewVersionForTrading()) { + errorMessageHandler.handleErrorMessage(Res.get("popup.warning.mandatoryUpdate.trading")); + return; + } + boolean result = p2PService.refreshTTL(offerPayload, true); if (result) { resultHandler.handleResult(); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java b/core/src/main/java/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java index 0e1c0af3115..2455ed9a8e9 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java @@ -61,7 +61,7 @@ protected void run() { failed("Other trader is banned by his trading account data.\n" + "paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails() + "\n" + "banFilter=" + appliedPaymentAccountFilter[0].toString()); - } else if (processModel.getFilterManager().requireUpdateToNewVersion()) { + } else if (processModel.getFilterManager().requireUpdateToNewVersionForTrading()) { failed("Your version of Bisq is not compatible for trading anymore. " + "Please update to the latest Bisq version at https://bisq.network/downloads."); } else { diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index a228f56cecd..61fbadf8e6b 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2293,7 +2293,14 @@ popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\n\ popup.warning.nodeBanned=One of the {0} nodes got banned. Please restart your application to be sure to not be connected to the banned node. popup.warning.priceRelay=price relay popup.warning.seed=seed - +popup.warning.mandatoryUpdate.trading=Please update to the latest Bisq version. \ + A mandatory update was released which disables trading for old versions. \ + Please check out the Bisq Forum for more information. +popup.warning.mandatoryUpdate.dao=Please update to the latest Bisq version. \ + A mandatory update was released which disables the Bisq DAO and BSQ for old versions. \ + Please check out the Bisq Forum for more information. +popup.warning.disable.dao=The Bisq DAO and BSQ are temporary disabled. \ + Please check out the Bisq Forum for more information. popup.warning.burnBTC=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. \ Please wait until the mining fees are low again or until you''ve accumulated more BTC to transfer. diff --git a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java index 11b85f60cb5..bc180a27d66 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java @@ -378,6 +378,9 @@ private void setupHandlers() { bisqSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress()); daoPresentation.getBsqSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress()); + bisqSetup.setFilterWarningHandler(warning -> { + new Popup<>().warning(warning).show(); + }); } private void setupP2PNumPeersWatcher() { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java index 206ca1c6075..fed5625ac56 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java @@ -574,7 +574,7 @@ boolean isNodeAddressBanned(Offer offer) { } boolean requireUpdateToNewVersion() { - return filterManager.requireUpdateToNewVersion(); + return filterManager.requireUpdateToNewVersionForTrading(); } boolean isInsufficientTradeLimit(Offer offer) { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java index f933d4b624a..5d2dfabbc0f 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -304,7 +304,7 @@ void onTakeOffer(TradeResultHandler tradeResultHandler) { new Popup<>().warning(Res.get("offerbook.warning.offerBlocked")).show(); } else if (filterManager.isNodeAddressBanned(offer.getMakerNodeAddress())) { new Popup<>().warning(Res.get("offerbook.warning.nodeBlocked")).show(); - } else if (filterManager.requireUpdateToNewVersion()) { + } else if (filterManager.requireUpdateToNewVersionForTrading()) { new Popup<>().warning(Res.get("offerbook.warning.requireUpdateToNewVersion")).show(); } else { tradeManager.onTakeOffer(amount.get(), From 830e693acec7149060992f3f1bfcc3eb196e5906 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 21:48:51 -0500 Subject: [PATCH 12/15] Refactoring: Rename CheckIfPeerIsBanned to ApplyFilter --- .../trade/protocol/BuyerAsMakerProtocol.java | 6 +++--- .../trade/protocol/BuyerAsTakerProtocol.java | 6 +++--- .../trade/protocol/SellerAsMakerProtocol.java | 8 ++++---- .../trade/protocol/SellerAsTakerProtocol.java | 8 ++++---- ...eckIfPeerIsBanned.java => ApplyFilter.java} | 4 ++-- .../bisq/desktop/main/debug/DebugView.java | 18 +++++++++--------- 6 files changed, 25 insertions(+), 25 deletions(-) rename core/src/main/java/bisq/core/trade/protocol/tasks/{CheckIfPeerIsBanned.java => ApplyFilter.java} (96%) 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 9f50d309517..54da5279165 100644 --- a/core/src/main/java/bisq/core/trade/protocol/BuyerAsMakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/BuyerAsMakerProtocol.java @@ -23,7 +23,7 @@ import bisq.core.trade.messages.PayDepositRequest; import bisq.core.trade.messages.PayoutTxPublishedMessage; import bisq.core.trade.messages.TradeMessage; -import bisq.core.trade.protocol.tasks.CheckIfPeerIsBanned; +import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.tasks.buyer.BuyerProcessPayoutTxPublishedMessage; @@ -129,7 +129,7 @@ public void handleTakeOfferRequest(TradeMessage tradeMessage, NodeAddress peerNo }); taskRunner.addTasks( MakerProcessPayDepositRequest.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, VerifyPeersAccountAgeWitness.class, MakerVerifyTakerFeePayment.class, @@ -186,7 +186,7 @@ public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandle handleTaskRunnerFault(errorMessage); }); taskRunner.addTasks( - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, MakerVerifyTakerFeePayment.class, BuyerAsMakerSignPayoutTx.class, 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 740d187fa4f..ae7d9b09bab 100644 --- a/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java @@ -23,7 +23,7 @@ import bisq.core.trade.messages.PayoutTxPublishedMessage; import bisq.core.trade.messages.PublishDepositTxRequest; import bisq.core.trade.messages.TradeMessage; -import bisq.core.trade.protocol.tasks.CheckIfPeerIsBanned; +import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.tasks.buyer.BuyerProcessPayoutTxPublishedMessage; @@ -145,7 +145,7 @@ private void handle(PublishDepositTxRequest tradeMessage, NodeAddress sender) { errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); taskRunner.addTasks( TakerProcessPublishDepositTxRequest.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, VerifyPeersAccountAgeWitness.class, TakerVerifyMakerFeePayment.class, @@ -179,7 +179,7 @@ public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandle handleTaskRunnerFault(errorMessage); }); taskRunner.addTasks( - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, TakerVerifyMakerFeePayment.class, BuyerAsMakerSignPayoutTx.class, diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerAsMakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerAsMakerProtocol.java index d7b67649cfe..e56beaff131 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerAsMakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerAsMakerProtocol.java @@ -24,7 +24,7 @@ import bisq.core.trade.messages.DepositTxPublishedMessage; import bisq.core.trade.messages.PayDepositRequest; import bisq.core.trade.messages.TradeMessage; -import bisq.core.trade.protocol.tasks.CheckIfPeerIsBanned; +import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.tasks.maker.MakerCreateAndSignContract; @@ -125,7 +125,7 @@ public void handleTakeOfferRequest(TradeMessage tradeMessage, NodeAddress sender taskRunner.addTasks( MakerProcessPayDepositRequest.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, VerifyPeersAccountAgeWitness.class, SellerVerifiesPeersAccountAge.class, @@ -207,7 +207,7 @@ public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandl }); taskRunner.addTasks( - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, MakerVerifyTakerFeePayment.class, SellerSignAndFinalizePayoutTx.class, @@ -232,7 +232,7 @@ public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandl }); taskRunner.addTasks( - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, MakerVerifyTakerFeePayment.class, SellerSendPayoutTxPublishedMessage.class diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java index 13cf6b19e29..ce768c957a0 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java @@ -23,7 +23,7 @@ import bisq.core.trade.messages.CounterCurrencyTransferStartedMessage; import bisq.core.trade.messages.PublishDepositTxRequest; import bisq.core.trade.messages.TradeMessage; -import bisq.core.trade.protocol.tasks.CheckIfPeerIsBanned; +import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx; @@ -138,7 +138,7 @@ private void handle(PublishDepositTxRequest tradeMessage, NodeAddress sender) { taskRunner.addTasks( TakerProcessPublishDepositTxRequest.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, VerifyPeersAccountAgeWitness.class, SellerVerifiesPeersAccountAge.class, @@ -194,7 +194,7 @@ public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandl }); taskRunner.addTasks( - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, TakerVerifyMakerFeePayment.class, SellerSignAndFinalizePayoutTx.class, @@ -219,7 +219,7 @@ public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandl }); taskRunner.addTasks( - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, TakerVerifyMakerFeePayment.class, SellerSendPayoutTxPublishedMessage.class diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java similarity index 96% rename from core/src/main/java/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java rename to core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java index 2455ed9a8e9..e7a5cbf6462 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java @@ -30,9 +30,9 @@ import static com.google.common.base.Preconditions.checkNotNull; @Slf4j -public class CheckIfPeerIsBanned extends TradeTask { +public class ApplyFilter extends TradeTask { @SuppressWarnings({"WeakerAccess", "unused"}) - public CheckIfPeerIsBanned(TaskRunner taskHandler, Trade trade) { + public ApplyFilter(TaskRunner taskHandler, Trade trade) { super(taskHandler, trade); } diff --git a/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java b/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java index a2418463245..afa3c68d1a0 100644 --- a/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java +++ b/desktop/src/main/java/bisq/desktop/main/debug/DebugView.java @@ -26,7 +26,7 @@ import bisq.core.offer.placeoffer.tasks.AddToOfferBook; import bisq.core.offer.placeoffer.tasks.CreateMakerFeeTx; import bisq.core.offer.placeoffer.tasks.ValidateOffer; -import bisq.core.trade.protocol.tasks.CheckIfPeerIsBanned; +import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.PublishTradeStatistics; import bisq.core.trade.protocol.tasks.buyer.BuyerSendCounterCurrencyTransferStartedMessage; import bisq.core.trade.protocol.tasks.buyer.BuyerSetupPayoutTxListener; @@ -107,7 +107,7 @@ public void initialize() { addGroup("BuyerAsMakerProtocol", FXCollections.observableArrayList(Arrays.asList( MakerProcessPayDepositRequest.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, MakerVerifyTakerFeePayment.class, MakerCreateAndSignContract.class, @@ -120,7 +120,7 @@ public void initialize() { MakerVerifyTakerFeePayment.class, PublishTradeStatistics.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, MakerVerifyTakerFeePayment.class, BuyerAsMakerSignPayoutTx.class, @@ -137,7 +137,7 @@ public void initialize() { TakerSendPayDepositRequest.class, TakerProcessPublishDepositTxRequest.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, TakerVerifyMakerFeePayment.class, TakerVerifyAndSignContract.class, @@ -148,7 +148,7 @@ public void initialize() { TakerVerifyMakerAccount.class, TakerVerifyMakerFeePayment.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, TakerVerifyMakerFeePayment.class, SellerSignAndFinalizePayoutTx.class, @@ -164,14 +164,14 @@ public void initialize() { TakerSendPayDepositRequest.class, TakerProcessPublishDepositTxRequest.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, TakerVerifyMakerFeePayment.class, TakerVerifyAndSignContract.class, BuyerAsTakerSignAndPublishDepositTx.class, TakerSendDepositTxPublishedMessage.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, TakerVerifyMakerAccount.class, TakerVerifyMakerFeePayment.class, BuyerAsMakerSignPayoutTx.class, @@ -181,7 +181,7 @@ public void initialize() { addGroup("SellerAsMakerProtocol", FXCollections.observableArrayList(Arrays.asList( MakerProcessPayDepositRequest.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, MakerVerifyTakerFeePayment.class, MakerCreateAndSignContract.class, @@ -198,7 +198,7 @@ public void initialize() { MakerVerifyTakerAccount.class, MakerVerifyTakerFeePayment.class, - CheckIfPeerIsBanned.class, + ApplyFilter.class, MakerVerifyTakerAccount.class, MakerVerifyTakerFeePayment.class, SellerSignAndFinalizePayoutTx.class, From 8f56f4a2625b5b3ee985fb21cfe1a138fe0ec5a4 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 21:49:47 -0500 Subject: [PATCH 13/15] Refactoring: Extract variable --- .../core/trade/protocol/tasks/ApplyFilter.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java index e7a5cbf6462..92c1185d950 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java @@ -17,6 +17,7 @@ package bisq.core.trade.protocol.tasks; +import bisq.core.filter.FilterManager; import bisq.core.filter.PaymentAccountFilter; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.trade.Trade; @@ -45,23 +46,24 @@ protected void run() { PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload()); final PaymentAccountFilter[] appliedPaymentAccountFilter = new PaymentAccountFilter[1]; - if (nodeAddress != null && processModel.getFilterManager().isNodeAddressBanned(nodeAddress)) { + FilterManager filterManager = processModel.getFilterManager(); + if (nodeAddress != null && filterManager.isNodeAddressBanned(nodeAddress)) { failed("Other trader is banned by his node address.\n" + "tradingPeerNodeAddress=" + nodeAddress); - } else if (processModel.getFilterManager().isOfferIdBanned(trade.getId())) { + } else if (filterManager.isOfferIdBanned(trade.getId())) { failed("Offer ID is banned.\n" + "Offer ID=" + trade.getId()); - } else if (processModel.getFilterManager().isCurrencyBanned(trade.getOffer().getCurrencyCode())) { + } else if (trade.getOffer() != null && filterManager.isCurrencyBanned(trade.getOffer().getCurrencyCode())) { failed("Currency is banned.\n" + "Currency code=" + trade.getOffer().getCurrencyCode()); - } else if (processModel.getFilterManager().isPaymentMethodBanned(trade.getOffer().getPaymentMethod())) { + } else if (filterManager.isPaymentMethodBanned(trade.getOffer().getPaymentMethod())) { failed("Payment method is banned.\n" + "Payment method=" + trade.getOffer().getPaymentMethod().getId()); - } else if (processModel.getFilterManager().isPeersPaymentAccountDataAreBanned(paymentAccountPayload, appliedPaymentAccountFilter)) { + } else if (filterManager.isPeersPaymentAccountDataAreBanned(paymentAccountPayload, appliedPaymentAccountFilter)) { failed("Other trader is banned by his trading account data.\n" + "paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails() + "\n" + "banFilter=" + appliedPaymentAccountFilter[0].toString()); - } else if (processModel.getFilterManager().requireUpdateToNewVersionForTrading()) { + } else if (filterManager.requireUpdateToNewVersionForTrading()) { failed("Your version of Bisq is not compatible for trading anymore. " + "Please update to the latest Bisq version at https://bisq.network/downloads."); } else { From 87358927a66292e5bf022a6685447422f51043cb Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 2 May 2019 22:40:54 -0500 Subject: [PATCH 14/15] Add null check --- .../src/main/java/bisq/core/payment/payload/PaymentMethod.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java index 8ba2594ae97..f125fcc29aa 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -318,6 +318,9 @@ public boolean isAsset() { } public static boolean hasChargebackRisk(PaymentMethod paymentMethod) { + if (paymentMethod == null) + return false; + String id = paymentMethod.getId(); return id.equals(PaymentMethod.SEPA_ID) || id.equals(PaymentMethod.SEPA_INSTANT_ID) || From 364f666a27229794a0869bedcf82aeba3f21da4e Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 3 May 2019 11:57:38 +0200 Subject: [PATCH 15/15] Only show information popup when actual amount was set --- .../java/bisq/desktop/main/offer/MutableOfferViewModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 775d41743d6..1763a9ef3a9 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -729,7 +729,7 @@ void onFocusOutAmountTextField(boolean oldValue, boolean newValue) { if (minAmount.get() != null) minAmountValidationResult.set(isBtcInputValid(minAmount.get())); - } else if (btcValidator.getMaxTradeLimit() != null && btcValidator.getMaxTradeLimit().value == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value) { + } else if (amount.get() != null && btcValidator.getMaxTradeLimit() != null && btcValidator.getMaxTradeLimit().value == OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value) { new Popup<>().information(Res.get("popup.warning.tradeLimitDueAccountAgeRestriction.buyer", Res.get("offerbook.warning.newVersionAnnouncement"))) .width(900)