mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 06:41:41 +01:00
Add check for account age to apply restrictions
This commit is contained in:
parent
df7b81138f
commit
8ab348c495
16 changed files with 242 additions and 42 deletions
|
@ -63,6 +63,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
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,
|
||||
|
|
|
@ -26,6 +26,7 @@ import javafx.collections.ObservableList;
|
|||
|
||||
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 @@ import javax.annotation.Nullable;
|
|||
|
||||
@Slf4j
|
||||
public class PaymentAccountUtil {
|
||||
public static boolean isAnyPaymentAccountValidForOffer(Offer offer, Collection<PaymentAccount> 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<PaymentAccount> 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;
|
||||
}
|
||||
|
||||
public static ObservableList<PaymentAccount> getPossiblePaymentAccounts(Offer offer, Set<PaymentAccount> paymentAccounts) {
|
||||
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<PaymentAccount> 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<PaymentAccount> takerPaymentAccounts) {
|
||||
for (PaymentAccount takerPaymentAccount : takerPaymentAccounts) {
|
||||
if (isTakerPaymentAccountValidForOffer(offer, takerPaymentAccount))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ObservableList<PaymentAccount> getPossiblePaymentAccounts(Offer offer,
|
||||
Set<PaymentAccount> paymentAccounts,
|
||||
AccountAgeWitnessService accountAgeWitnessService) {
|
||||
ObservableList<PaymentAccount> 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 class PaymentAccountUtil {
|
|||
"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();
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class PaymentAccounts {
|
|||
private final BiFunction<Offer, PaymentAccount, Boolean> validator;
|
||||
|
||||
PaymentAccounts(Set<PaymentAccount> accounts, AccountAgeWitnessService service) {
|
||||
this(accounts, service, PaymentAccountUtil::isPaymentAccountValidForOffer);
|
||||
this(accounts, service, PaymentAccountUtil::isTakerPaymentAccountValidForOffer);
|
||||
}
|
||||
|
||||
PaymentAccounts(Set<PaymentAccount> accounts, AccountAgeWitnessService service,
|
||||
|
|
|
@ -316,4 +316,20 @@ public final class PaymentMethod implements PersistablePayload, Comparable {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx;
|
|||
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 class SellerAsMakerProtocol extends TradeProtocol implements SellerProtoc
|
|||
CheckIfPeerIsBanned.class,
|
||||
MakerVerifyTakerAccount.class,
|
||||
VerifyPeersAccountAgeWitness.class,
|
||||
SellerVerifiesPeersAccountAge.class,
|
||||
MakerVerifyTakerFeePayment.class,
|
||||
MakerCreateAndSignContract.class,
|
||||
SellerAsMakerCreatesAndSignsDepositTx.class,
|
||||
|
|
|
@ -30,6 +30,7 @@ import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx;
|
|||
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 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
|
|||
CheckIfPeerIsBanned.class,
|
||||
TakerVerifyMakerAccount.class,
|
||||
VerifyPeersAccountAgeWitness.class,
|
||||
SellerVerifiesPeersAccountAge.class,
|
||||
TakerVerifyMakerFeePayment.class,
|
||||
TakerVerifyAndSignContract.class,
|
||||
TakerPublishFeeTx.class,
|
||||
|
|
|
@ -45,7 +45,7 @@ public class VerifyPeersAccountAgeWitness extends TradeTask {
|
|||
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(),
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -39,6 +39,7 @@ import bisq.core.payment.AccountAgeWitnessService;
|
|||
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.List;
|
|||
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 abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
|
||||
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 abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
} else {
|
||||
Optional<PaymentAccount> 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 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
}
|
||||
|
||||
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 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
}
|
||||
}
|
||||
|
||||
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 abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -425,6 +425,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
})
|
||||
.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 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
|
||||
private void onShowInfo(Offer offer,
|
||||
boolean isPaymentAccountValidForOffer,
|
||||
boolean isRiskyBuyOfferWithImmatureAccountAge,
|
||||
boolean isSellOfferAndAnyTakerPaymentAccountForOfferMature,
|
||||
boolean hasSameProtocolVersion,
|
||||
boolean isIgnored,
|
||||
boolean isOfferBanned,
|
||||
|
@ -448,6 +452,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
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 class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
return new TableCell<>() {
|
||||
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 class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
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 class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
requireUpdateToNewVersion = model.requireUpdateToNewVersion();
|
||||
isInsufficientTradeLimit = model.isInsufficientTradeLimit(offer);
|
||||
isTradable = isPaymentAccountValidForOffer &&
|
||||
!isRiskyBuyOfferWithImmatureAccountAge &&
|
||||
isSellOfferAndAnyTakerPaymentAccountForOfferMature &&
|
||||
hasSameProtocolVersion &&
|
||||
!isIgnored &&
|
||||
!isOfferBanned &&
|
||||
|
@ -865,6 +878,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
if (!(e.getTarget() instanceof ImageView || e.getTarget() instanceof Canvas))
|
||||
onShowInfo(offer,
|
||||
isPaymentAccountValidForOffer,
|
||||
isRiskyBuyOfferWithImmatureAccountAge,
|
||||
isSellOfferAndAnyTakerPaymentAccountForOfferMature,
|
||||
hasSameProtocolVersion,
|
||||
isIgnored,
|
||||
isOfferBanned,
|
||||
|
@ -905,6 +920,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
if (!myOffer && !isTradable)
|
||||
button.setOnAction(e -> onShowInfo(offer,
|
||||
isPaymentAccountValidForOffer,
|
||||
isRiskyBuyOfferWithImmatureAccountAge,
|
||||
isSellOfferAndAnyTakerPaymentAccountForOfferMature,
|
||||
hasSameProtocolVersion,
|
||||
isIgnored,
|
||||
isOfferBanned,
|
||||
|
|
|
@ -505,7 +505,17 @@ class OfferBookViewModel extends ActivatableViewModel {
|
|||
}
|
||||
|
||||
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 @@ class OfferBookViewModel extends ActivatableViewModel {
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -405,7 +405,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
ObservableList<PaymentAccount> getPossiblePaymentAccounts() {
|
||||
Set<PaymentAccount> paymentAccounts = user.getPaymentAccounts();
|
||||
checkNotNull(paymentAccounts, "paymentAccounts must not be null");
|
||||
return PaymentAccountUtil.getPossiblePaymentAccounts(offer, paymentAccounts);
|
||||
return PaymentAccountUtil.getPossiblePaymentAccounts(offer, paymentAccounts, accountAgeWitnessService);
|
||||
}
|
||||
|
||||
public PaymentAccount getLastSelectedPaymentAccount() {
|
||||
|
|
|
@ -35,6 +35,7 @@ import bisq.core.btc.wallet.BtcWalletService;
|
|||
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.ArrayList;
|
|||
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 class PendingTradesDataModel extends ActivatableDataModel {
|
|||
Preferences preferences,
|
||||
P2PService p2PService,
|
||||
WalletsSetup walletsSetup,
|
||||
AccountAgeWitnessService accountAgeWitnessService,
|
||||
Navigation navigation,
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
NotificationCenter notificationCenter) {
|
||||
|
@ -120,6 +126,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
this.preferences = preferences;
|
||||
this.p2PService = p2PService;
|
||||
this.walletsSetup = walletsSetup;
|
||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||
this.navigation = navigation;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.notificationCenter = notificationCenter;
|
||||
|
|
|
@ -301,7 +301,7 @@ public class BuyerStep2View extends TradeStepView {
|
|||
if (trade != null && model.getUser().getPaymentAccounts() != null) {
|
||||
Offer offer = trade.getOffer();
|
||||
List<PaymentAccount> 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();
|
||||
|
|
|
@ -99,46 +99,46 @@ public class OfferBookViewModelTest {
|
|||
Collection<PaymentAccount> 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 class OfferBookViewModelTest {
|
|||
|
||||
// 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
|
||||
|
|
Loading…
Add table
Reference in a new issue