From cb50faca95ddac3cbdb2a647a08ea627e7a0be2e Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 13 Oct 2017 21:36:15 -0500 Subject: [PATCH] Apply age based trade limit to offer --- .../payment/AccountAgeWitnessService.java | 68 ++++++++++++++++++- .../io/bisq/gui/components/PeerInfoIcon.java | 2 +- .../createoffer/CreateOfferDataModel.java | 8 +-- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/io/bisq/core/payment/AccountAgeWitnessService.java b/core/src/main/java/io/bisq/core/payment/AccountAgeWitnessService.java index 00087817b2..da6dc18199 100644 --- a/core/src/main/java/io/bisq/core/payment/AccountAgeWitnessService.java +++ b/core/src/main/java/io/bisq/core/payment/AccountAgeWitnessService.java @@ -20,8 +20,10 @@ package io.bisq.core.payment; import io.bisq.common.crypto.CryptoException; import io.bisq.common.crypto.KeyRing; import io.bisq.common.crypto.Sig; +import io.bisq.common.locale.CurrencyUtil; import io.bisq.common.proto.persistable.PersistedDataHost; import io.bisq.common.storage.Storage; +import io.bisq.common.util.MathUtils; import io.bisq.common.util.Utilities; import io.bisq.core.offer.Offer; import io.bisq.core.payment.payload.PaymentAccountPayload; @@ -113,7 +115,7 @@ public class AccountAgeWitnessService implements PersistedDataHost { } } - private Optional findWitnessByHash(String hash) { + public Optional findWitnessByHash(String hash) { return accountAgeWitnessMap.containsKey(hash) ? Optional.of(accountAgeWitnessMap.get(hash)) : Optional.empty(); } @@ -130,8 +132,11 @@ public class AccountAgeWitnessService implements PersistedDataHost { } } - public AccountAge getAccountAgeCategory(Offer offer) { - final long accountAge = getAccountAge(offer); + public long getAccountAge(AccountAgeWitness accountAgeWitness) { + return new Date().getTime() - accountAgeWitness.getDate(); + } + + public AccountAge getAccountAgeCategory(long accountAge) { if (accountAge < TimeUnit.DAYS.toMillis(30)) { return AccountAge.LESS_ONE_MONTH; } else if (accountAge < TimeUnit.DAYS.toMillis(60)) { @@ -154,12 +159,69 @@ public class AccountAgeWitnessService implements PersistedDataHost { return getWitnessHash(paymentAccountPayload, paymentAccountPayload.getSalt()); } + public String getWitnessHashAsHex(PaymentAccountPayload paymentAccountPayload) { + return Utilities.bytesAsHexString(getWitnessHash(paymentAccountPayload)); + } + private byte[] getWitnessHash(PaymentAccountPayload paymentAccountPayload, byte[] salt) { byte[] ageWitnessInputData = paymentAccountPayload.getAgeWitnessInputData(); final byte[] combined = ArrayUtils.addAll(ageWitnessInputData, salt); return Sha256Hash.hash(combined); } + public long getTradeLimit(PaymentAccount paymentAccount, String currencyCode) { + final long maxTradeLimit = paymentAccount.getPaymentMethod().getMaxTradeLimitAsCoin(currencyCode).value; + if (CurrencyUtil.isFiatCurrency(currencyCode)) { + double factor; + + Optional accountAgeWitnessOptional = findWitnessByHash(getWitnessHashAsHex(paymentAccount.getPaymentAccountPayload())); + AccountAge accountAgeCategory = accountAgeWitnessOptional.isPresent() ? + getAccountAgeCategory(getAccountAge((accountAgeWitnessOptional.get()))) : + AccountAgeWitnessService.AccountAge.LESS_ONE_MONTH; + + // TODO Fade in by date can be removed after feb 2018 + // We want to fade in the limit over 2 months to avoid that all users get limited to 25% of the limit when + // we deploy that feature. + final Date now = new Date(); + final Date dez = new GregorianCalendar(2017, GregorianCalendar.DECEMBER, 1).getTime(); + final Date jan = new GregorianCalendar(2017, GregorianCalendar.JANUARY, 1).getTime(); + final Date feb = new GregorianCalendar(2017, GregorianCalendar.FEBRUARY, 1).getTime(); + + switch (accountAgeCategory) { + case TWO_MONTHS_OR_MORE: + factor = 1; + break; + case ONE_TO_TWO_MONTHS: + if (now.before(dez)) { + factor = 1; + } else if (now.before(jan)) { + factor = 0.9; + } else if (now.before(feb)) { + factor = 0.75; + } else { + factor = 0.5; + } + break; + case LESS_ONE_MONTH: + default: + if (now.before(dez)) { + factor = 1; + } else if (now.before(jan)) { + factor = 0.8; + } else if (now.before(feb)) { + factor = 0.5; + } else { + factor = 0.25; + } + break; + } + + return MathUtils.roundDoubleToLong((double) maxTradeLimit * factor); + } else { + return maxTradeLimit; + } + } + boolean verifyAgeWitness(byte[] peersAgeWitnessInputData, AccountAgeWitness witness, byte[] peersSalt, diff --git a/gui/src/main/java/io/bisq/gui/components/PeerInfoIcon.java b/gui/src/main/java/io/bisq/gui/components/PeerInfoIcon.java index b736e42108..ceca83d590 100644 --- a/gui/src/main/java/io/bisq/gui/components/PeerInfoIcon.java +++ b/gui/src/main/java/io/bisq/gui/components/PeerInfoIcon.java @@ -57,7 +57,7 @@ public class PeerInfoIcon extends Group { // outer circle Color ringColor; - switch (accountAgeWitnessService.getAccountAgeCategory(offer)) { + switch (accountAgeWitnessService.getAccountAgeCategory(accountAgeWitnessService.getAccountAge(offer))) { case TWO_MONTHS_OR_MORE: ringColor = Color.rgb(0, 225, 0); // > 2 months green break; diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferDataModel.java index 939eaa0c8d..97d163ee16 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferDataModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferDataModel.java @@ -332,8 +332,8 @@ class CreateOfferDataModel extends ActivatableDataModel { checkNotNull(p2PService.getAddress(), "Address must not be null"); checkNotNull(getMakerFee(), "makerFee must not be null"); - - long maxTradeLimit = paymentAccount.getPaymentMethod().getMaxTradeLimitAsCoin(currencyCode).value; + + long maxTradeLimit = accountAgeWitnessService.getTradeLimit(paymentAccount, currencyCode); long maxTradePeriod = paymentAccount.getPaymentMethod().getMaxTradePeriod(); // reserved for future use cases @@ -346,8 +346,8 @@ class CreateOfferDataModel extends ActivatableDataModel { String hashOfChallenge = null; HashMap extraDataMap = new HashMap<>(); - byte[] hashOfPaymentAccount = accountAgeWitnessService.getWitnessHash(paymentAccount.getPaymentAccountPayload()); - extraDataMap.put(OfferPayload.ACCOUNT_AGE_WITNESS_HASH, Utilities.bytesAsHexString(hashOfPaymentAccount)); + final String hashOfPaymentAccountAsHex = accountAgeWitnessService.getWitnessHashAsHex(paymentAccount.getPaymentAccountPayload()); + extraDataMap.put(OfferPayload.ACCOUNT_AGE_WITNESS_HASH, hashOfPaymentAccountAsHex); Coin buyerSecurityDepositAsCoin = buyerSecurityDeposit.get(); checkArgument(buyerSecurityDepositAsCoin.compareTo(Restrictions.getMaxBuyerSecurityDeposit()) <= 0,