Skip to content

Commit

Permalink
Merge pull request #2801 from ManfredKarrer/restrict-new-accounts
Browse files Browse the repository at this point in the history
Add check for account age to apply restrictions
  • Loading branch information
ripcurlx committed May 3, 2019
2 parents 1d18029 + 364f666 commit 7523fc5
Show file tree
Hide file tree
Showing 32 changed files with 527 additions and 97 deletions.
1 change: 1 addition & 0 deletions common/src/main/java/bisq/common/app/Version.java
Expand Up @@ -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;
Expand Down
25 changes: 21 additions & 4 deletions core/src/main/java/bisq/core/app/BisqSetup.java
Expand Up @@ -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"));
}
}
});

Expand Down
22 changes: 19 additions & 3 deletions core/src/main/java/bisq/core/arbitration/DisputeManager.java
Expand Up @@ -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))
Expand Down Expand Up @@ -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<OpenOffer> 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);
Expand Down
16 changes: 15 additions & 1 deletion core/src/main/java/bisq/core/filter/FilterManager.java
Expand Up @@ -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;
}
Expand All @@ -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 &&
Expand Down
15 changes: 15 additions & 0 deletions core/src/main/java/bisq/core/offer/OfferBookService.java
Expand Up @@ -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;
Expand Down Expand Up @@ -63,6 +65,7 @@ public interface OfferBookChangedListener {
private final P2PService p2PService;
private final PriceFeedService priceFeedService;
private final List<OfferBookChangedListener> offerBookChangedListeners = new LinkedList<>();
private final FilterManager filterManager;
private final JsonFileManager jsonFileManager;


Expand All @@ -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() {
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down
59 changes: 59 additions & 0 deletions core/src/main/java/bisq/core/offer/OfferRestrictions.java
@@ -0,0 +1,59 @@
/*
* 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.offer;

import bisq.core.payment.payload.PaymentMethod;
import bisq.core.trade.Trade;

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 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());
}

public static boolean isAmountRisky(Coin amount) {
return amount.isGreaterThan(TOLERATED_SMALL_TRADE_AMOUNT);
}
}
82 changes: 82 additions & 0 deletions core/src/main/java/bisq/core/payment/AccountAgeRestrictions.java
@@ -0,0 +1,82 @@
/*
* 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.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;

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());
return accountCreationDate > SAFE_ACCOUNT_AGE_DATE;
}

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);
}
}
}
Expand Up @@ -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,
Expand Down

0 comments on commit 7523fc5

Please sign in to comment.