From 56c8cd15070cc01b116b3d8d68e8d0bfe6e05ae3 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Thu, 15 Oct 2020 17:26:19 +0200 Subject: [PATCH 1/6] Remove unused old activation date --- .../java/bisq/core/account/witness/AccountAgeWitnessService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index 1aeed551502..43a71e088dd 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -85,7 +85,6 @@ @Slf4j 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); private static final long SAFE_ACCOUNT_AGE_DATE = Utilities.getUTCDate(2019, GregorianCalendar.MARCH, 1).getTime(); public enum AccountAge { From b64c816047a6a30284b9d27cf91f620bc34b4910 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Thu, 15 Oct 2020 17:47:08 +0200 Subject: [PATCH 2/6] Refactor getTradeLimit --- .../witness/AccountAgeWitnessService.java | 110 ++++++++++-------- 1 file changed, 61 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index 43a71e088dd..6a6fcf9165a 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -365,58 +365,70 @@ private long getTradeLimit(Coin maxTradeLimit, AccountAge accountAgeCategory, OfferPayload.Direction direction, PaymentMethod paymentMethod) { - if (CurrencyUtil.isFiatCurrency(currencyCode)) { - double factor; - boolean isRisky = PaymentMethod.hasChargebackRisk(paymentMethod, currencyCode); - if (!isRisky || direction == OfferPayload.Direction.SELL) { - // Get age of witness rather than time since signing for non risky payment methods and for selling - accountAgeCategory = getAccountAgeCategory(getAccountAge(accountAgeWitness, new Date())); - } - long limit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value; - if (direction == OfferPayload.Direction.BUY && isRisky) { - // Used only for bying of BTC with risky payment methods - switch (accountAgeCategory) { - case TWO_MONTHS_OR_MORE: - factor = 1; - break; - case ONE_TO_TWO_MONTHS: - factor = 0.5; - break; - case LESS_ONE_MONTH: - case UNVERIFIED: - default: - factor = 0; - } - } else { - // Used by non risky payment methods and for selling BTC with risky methods - switch (accountAgeCategory) { - case TWO_MONTHS_OR_MORE: - factor = 1; - break; - case ONE_TO_TWO_MONTHS: - factor = 0.5; - break; - case LESS_ONE_MONTH: - case UNVERIFIED: - factor = 0.25; - break; - default: - factor = 0; - } - } - if (factor > 0) { - limit = MathUtils.roundDoubleToLong((double) maxTradeLimit.value * factor); - } + if (!CurrencyUtil.isFiatCurrency(currencyCode)) { + return maxTradeLimit.value; + } - log.debug("accountAgeCategory={}, limit={}, factor={}, accountAgeWitnessHash={}", - accountAgeCategory, - Coin.valueOf(limit).toFriendlyString(), - factor, - Utilities.bytesAsHexString(accountAgeWitness.getHash())); - return limit; + double factor; + boolean isRisky = PaymentMethod.hasChargebackRisk(paymentMethod, currencyCode); + if (!isRisky || direction == OfferPayload.Direction.SELL) { + // Get age of witness rather than time since signing for non risky payment methods and for selling + accountAgeCategory = getAccountAgeCategory(getAccountAge(accountAgeWitness, new Date())); + } + long limit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value; + if (direction == OfferPayload.Direction.BUY && isRisky) { + // Used only for bying of BTC with risky payment methods + factor = getFactorRisky(accountAgeCategory); } else { - return maxTradeLimit.value; + // Used by non risky payment methods and for selling BTC with risky methods + factor = getFactorNormal(accountAgeCategory); + } + if (factor > 0) { + limit = MathUtils.roundDoubleToLong((double) maxTradeLimit.value * factor); + } + + log.debug("accountAgeCategory={}, limit={}, factor={}, accountAgeWitnessHash={}", + accountAgeCategory, + Coin.valueOf(limit).toFriendlyString(), + factor, + Utilities.bytesAsHexString(accountAgeWitness.getHash())); + return limit; + } + + private double getFactorRisky(AccountAge accountAgeCategory) { + double factor; + switch (accountAgeCategory) { + case TWO_MONTHS_OR_MORE: + factor = 1; + break; + case ONE_TO_TWO_MONTHS: + factor = 0.5; + break; + case LESS_ONE_MONTH: + case UNVERIFIED: + default: + factor = 0; + } + return factor; + } + + private double getFactorNormal(AccountAge accountAgeCategory) { + double factor; + switch (accountAgeCategory) { + case TWO_MONTHS_OR_MORE: + factor = 1; + break; + case ONE_TO_TWO_MONTHS: + factor = 0.5; + break; + case LESS_ONE_MONTH: + case UNVERIFIED: + factor = 0.25; + break; + default: + factor = 0; } + return factor; } /////////////////////////////////////////////////////////////////////////////////////////// From 17745e3bee32585dc56997608592a2fc7c6543ca Mon Sep 17 00:00:00 2001 From: sqrrm Date: Sun, 25 Oct 2020 18:46:44 +0100 Subject: [PATCH 3/6] Clean up riskFactor methods --- .../witness/AccountAgeWitnessService.java | 58 ++++++++----------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index 6a6fcf9165a..c9eb861a622 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -358,7 +358,12 @@ private AccountAge getAccountAgeCategory(long accountAge) { } } - // Checks trade limit based on time since signing of AccountAgeWitness + // Get trade limit based on a time schedule + // Buying of BTC with a payment method that has chargeback risk will use a low trade limit schedule + // All selling and all other fiat payment methods use the normal trade limit schedule + // Non fiat always has max limit + // Account types that can get signed will use time since signing, other methods use time since account age creation + // when measuring account age private long getTradeLimit(Coin maxTradeLimit, String currencyCode, AccountAgeWitness accountAgeWitness, @@ -369,66 +374,51 @@ private long getTradeLimit(Coin maxTradeLimit, return maxTradeLimit.value; } - double factor; - boolean isRisky = PaymentMethod.hasChargebackRisk(paymentMethod, currencyCode); - if (!isRisky || direction == OfferPayload.Direction.SELL) { - // Get age of witness rather than time since signing for non risky payment methods and for selling - accountAgeCategory = getAccountAgeCategory(getAccountAge(accountAgeWitness, new Date())); - } long limit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value; - if (direction == OfferPayload.Direction.BUY && isRisky) { - // Used only for bying of BTC with risky payment methods - factor = getFactorRisky(accountAgeCategory); - } else { - // Used by non risky payment methods and for selling BTC with risky methods - factor = getFactorNormal(accountAgeCategory); - } + var factor = PaymentMethod.hasChargebackRisk(paymentMethod, currencyCode) ? + signedTypeFactor(accountAgeCategory, direction) : normalFactor(accountAgeCategory); if (factor > 0) { limit = MathUtils.roundDoubleToLong((double) maxTradeLimit.value * factor); } - log.debug("accountAgeCategory={}, limit={}, factor={}, accountAgeWitnessHash={}", - accountAgeCategory, + log.debug("limit={}, factor={}, accountAgeWitnessHash={}", Coin.valueOf(limit).toFriendlyString(), factor, Utilities.bytesAsHexString(accountAgeWitness.getHash())); return limit; } - private double getFactorRisky(AccountAge accountAgeCategory) { - double factor; + private double signedTypeFactor(AccountAge accountAgeCategory, + OfferPayload.Direction direction) { + return direction == OfferPayload.Direction.BUY ? signedBuyFactor(accountAgeCategory) : + normalFactor(accountAgeCategory); + } + + private double signedBuyFactor(AccountAge accountAgeCategory) { switch (accountAgeCategory) { case TWO_MONTHS_OR_MORE: - factor = 1; - break; + return 1; case ONE_TO_TWO_MONTHS: - factor = 0.5; - break; + return 0.5; case LESS_ONE_MONTH: case UNVERIFIED: default: - factor = 0; } - return factor; + return 0; } - private double getFactorNormal(AccountAge accountAgeCategory) { - double factor; + private double normalFactor(AccountAge accountAgeCategory) { switch (accountAgeCategory) { case TWO_MONTHS_OR_MORE: - factor = 1; - break; + return 1; case ONE_TO_TWO_MONTHS: - factor = 0.5; - break; + return 0.5; case LESS_ONE_MONTH: case UNVERIFIED: - factor = 0.25; - break; + return 0.25; default: - factor = 0; } - return factor; + return 0; } /////////////////////////////////////////////////////////////////////////////////////////// From 0f0b75c2ac7ae5d8a44db6f4f7f6a21b4eb101c4 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Sun, 25 Oct 2020 19:01:03 +0100 Subject: [PATCH 4/6] Change time dependent trade limits This is in accordance with https://github.com/bisq-network/proposals/issues/264 Any account type that doesn't need signing can now trade the full amount immediately, same goes for signed type accounts that are selling. Signed type accounts that are buying now has minimum limit (0.01 BTC) up until 30 days after getting signed, then the limit is increased to 0.5 times the max limit (0.125 BTC) and after 60 days they get the full limits (0.25 BTC). --- .../account/witness/AccountAgeWitnessService.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index c9eb861a622..ee7def52401 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -408,17 +408,7 @@ private double signedBuyFactor(AccountAge accountAgeCategory) { } private double normalFactor(AccountAge accountAgeCategory) { - switch (accountAgeCategory) { - case TWO_MONTHS_OR_MORE: - return 1; - case ONE_TO_TWO_MONTHS: - return 0.5; - case LESS_ONE_MONTH: - case UNVERIFIED: - return 0.25; - default: - } - return 0; + return 1; } /////////////////////////////////////////////////////////////////////////////////////////// From 7f734292f39c08d5972d27ba517ca3094440f81e Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 27 Oct 2020 00:22:00 +0100 Subject: [PATCH 5/6] Cleanup line breaks --- .../witness/AccountAgeWitnessService.java | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index ee7def52401..5ddd4d8bcd5 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -207,10 +207,11 @@ private void republishAllFiatAccounts() { user.getPaymentAccounts().stream() .filter(e -> !(e instanceof AssetAccount)) .forEach(e -> { - // We delay with a random interval of 20-60 sec to ensure to be better connected and don't stress the - // P2P network with publishing all at once at startup time. + // We delay with a random interval of 20-60 sec to ensure to be better connected and don't + // stress the P2P network with publishing all at once at startup time. final int delayInSec = 20 + new Random().nextInt(40); - UserThread.runAfter(() -> p2PService.addPersistableNetworkPayload(getMyWitness(e.getPaymentAccountPayload()), true), delayInSec); + UserThread.runAfter(() -> p2PService.addPersistableNetworkPayload(getMyWitness( + e.getPaymentAccountPayload()), true), delayInSec); }); } @@ -237,7 +238,8 @@ public byte[] getPeerAccountAgeWitnessHash(Trade trade) { } byte[] getAccountInputDataWithSalt(PaymentAccountPayload paymentAccountPayload) { - return Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(), paymentAccountPayload.getSalt()); + return Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(), + paymentAccountPayload.getSalt()); } @VisibleForTesting @@ -280,7 +282,8 @@ private Optional getWitnessByHash(byte[] hash) { if (!containsKey) log.debug("hash not found in accountAgeWitnessMap"); - return accountAgeWitnessMap.containsKey(hashAsByteArray) ? Optional.of(accountAgeWitnessMap.get(hashAsByteArray)) : Optional.empty(); + return accountAgeWitnessMap.containsKey(hashAsByteArray) ? + Optional.of(accountAgeWitnessMap.get(hashAsByteArray)) : Optional.empty(); } private Optional getWitnessByHashAsHex(String hashAsHex) { @@ -435,7 +438,8 @@ private boolean hasTradeLimitException(AccountAgeWitness accountAgeWitness) { /////////////////////////////////////////////////////////////////////////////////////////// public AccountAgeWitness getMyWitness(PaymentAccountPayload paymentAccountPayload) { - final Optional accountAgeWitnessOptional = findWitness(paymentAccountPayload, keyRing.getPubKeyRing()); + final Optional accountAgeWitnessOptional = + findWitness(paymentAccountPayload, keyRing.getPubKeyRing()); return accountAgeWitnessOptional.orElseGet(() -> getNewWitness(paymentAccountPayload, keyRing.getPubKeyRing())); } @@ -451,8 +455,7 @@ public long getMyAccountAge(PaymentAccountPayload paymentAccountPayload) { return getAccountAge(getMyWitness(paymentAccountPayload), new Date()); } - public long getMyTradeLimit(PaymentAccount paymentAccount, String currencyCode, OfferPayload.Direction - direction) { + public long getMyTradeLimit(PaymentAccount paymentAccount, String currencyCode, OfferPayload.Direction direction) { if (paymentAccount == null) return 0; @@ -483,7 +486,8 @@ public boolean verifyAccountAgeWitness(Trade trade, byte[] nonce, byte[] signature, ErrorMessageHandler errorMessageHandler) { - final Optional accountAgeWitnessOptional = findWitness(peersPaymentAccountPayload, peersPubKeyRing); + final Optional accountAgeWitnessOptional = + findWitness(peersPaymentAccountPayload, peersPubKeyRing); // If we don't find a stored witness data we create a new dummy object which makes is easier to reuse the // below validation methods. This peersWitness object is not used beside for validation. Some of the // validation calls are pointless in the case we create a new Witness ourselves but the verifyPeersTradeLimit @@ -504,8 +508,10 @@ public boolean verifyAccountAgeWitness(Trade trade, if (!verifyPeersCurrentDate(peersCurrentDate, errorMessageHandler)) return false; - final byte[] peersAccountInputDataWithSalt = Utilities.concatenateByteArrays(peersPaymentAccountPayload.getAgeWitnessInputData(), peersPaymentAccountPayload.getSalt()); - byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(peersAccountInputDataWithSalt, peersPubKeyRing.getSignaturePubKeyBytes())); + final byte[] peersAccountInputDataWithSalt = Utilities.concatenateByteArrays( + peersPaymentAccountPayload.getAgeWitnessInputData(), peersPaymentAccountPayload.getSalt()); + byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(peersAccountInputDataWithSalt, + peersPubKeyRing.getSignaturePubKeyBytes())); // Check if the hash in the witness data matches the hash derived from the data provided by the peer final byte[] peersWitnessHash = peersWitness.getHash(); @@ -582,7 +588,8 @@ private boolean verifyPeersTradeLimit(Offer offer, ErrorMessageHandler errorMessageHandler) { checkNotNull(offer); final String currencyCode = offer.getCurrencyCode(); - final Coin defaultMaxTradeLimit = PaymentMethod.getPaymentMethodById(offer.getOfferPayload().getPaymentMethodId()).getMaxTradeLimitAsCoin(currencyCode); + final Coin defaultMaxTradeLimit = PaymentMethod.getPaymentMethodById( + offer.getOfferPayload().getPaymentMethodId()).getMaxTradeLimitAsCoin(currencyCode); long peersCurrentTradeLimit = defaultMaxTradeLimit.value; if (!hasTradeLimitException(peersWitness)) { final long accountSignAge = getWitnessSignAge(peersWitness, peersCurrentDate); @@ -645,8 +652,8 @@ public String arbitratorSignOrphanWitness(AccountAgeWitness accountAgeWitness, .findAny() .orElse(null); checkNotNull(signedWitness); - return signedWitnessService.signAndPublishAccountAgeWitness(accountAgeWitness, key, signedWitness.getWitnessOwnerPubKey(), - time); + return signedWitnessService.signAndPublishAccountAgeWitness(accountAgeWitness, key, + signedWitness.getWitnessOwnerPubKey(), time); } public String arbitratorSignOrphanPubKey(ECKey key, @@ -667,7 +674,8 @@ public Optional traderSignAndPublishPeersAccountAgeWitness(Trade Coin tradeAmount = trade.getTradeAmount(); checkNotNull(trade.getProcessModel().getTradingPeer().getPubKeyRing(), "Peer must have a keyring"); PublicKey peersPubKey = trade.getProcessModel().getTradingPeer().getPubKeyRing().getSignaturePubKey(); - checkNotNull(peersWitness, "Not able to find peers witness, unable to sign for trade {}", trade.toString()); + checkNotNull(peersWitness, "Not able to find peers witness, unable to sign for trade {}", + trade.toString()); checkNotNull(tradeAmount, "Trade amount must not be null"); checkNotNull(peersPubKey, "Peers pub key must not be null"); @@ -708,7 +716,8 @@ private boolean isNotFiltered(Dispute dispute) { filterManager.isPaymentMethodBanned( PaymentMethod.getPaymentMethodById(dispute.getContract().getPaymentMethodId())) || filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getBuyerPaymentAccountPayload()) || - filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getSellerPaymentAccountPayload()) || + filterManager.arePeersPaymentAccountDataBanned( + dispute.getContract().getSellerPaymentAccountPayload()) || filterManager.isWitnessSignerPubKeyBanned( Utils.HEX.encode(dispute.getContract().getBuyerPubKeyRing().getSignaturePubKeyBytes())) || filterManager.isWitnessSignerPubKeyBanned( From 02e53304edd24453e58cc3a41882ce9b1c78c907 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 3 Nov 2020 17:35:29 +0100 Subject: [PATCH 6/6] Cleanup unused argument --- .../bisq/core/account/witness/AccountAgeWitnessService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index 5ddd4d8bcd5..3d6ac7a5de0 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -379,7 +379,7 @@ private long getTradeLimit(Coin maxTradeLimit, long limit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value; var factor = PaymentMethod.hasChargebackRisk(paymentMethod, currencyCode) ? - signedTypeFactor(accountAgeCategory, direction) : normalFactor(accountAgeCategory); + signedTypeFactor(accountAgeCategory, direction) : normalFactor(); if (factor > 0) { limit = MathUtils.roundDoubleToLong((double) maxTradeLimit.value * factor); } @@ -394,7 +394,7 @@ private long getTradeLimit(Coin maxTradeLimit, private double signedTypeFactor(AccountAge accountAgeCategory, OfferPayload.Direction direction) { return direction == OfferPayload.Direction.BUY ? signedBuyFactor(accountAgeCategory) : - normalFactor(accountAgeCategory); + normalFactor(); } private double signedBuyFactor(AccountAge accountAgeCategory) { @@ -410,7 +410,7 @@ private double signedBuyFactor(AccountAge accountAgeCategory) { return 0; } - private double normalFactor(AccountAge accountAgeCategory) { + private double normalFactor() { return 1; }