From ed099981fa0ce8edd5af37281991162773d3edd9 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 9 Dec 2020 13:58:16 -0500 Subject: [PATCH 1/3] Make findWitness public Improve logs if trade limit was violated --- .../core/account/witness/AccountAgeWitnessService.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 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 7d655115bc6..40893c40652 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -265,7 +265,7 @@ Optional findWitness(PaymentAccountPayload paymentAccountPayl return getWitnessByHash(hash); } - private Optional findWitness(Offer offer) { + public Optional findWitness(Offer offer) { final Optional accountAgeWitnessHash = offer.getAccountAgeWitnessHashAsHex(); return accountAgeWitnessHash.isPresent() ? getWitnessByHashAsHex(accountAgeWitnessHash.get()) : @@ -613,7 +613,10 @@ private boolean verifyPeersTradeLimit(Offer offer, if (!result) { String msg = "The peers trade limit is less than the traded amount.\n" + "tradeAmount=" + tradeAmount.toFriendlyString() + - "\nPeers trade limit=" + Coin.valueOf(peersCurrentTradeLimit).toFriendlyString(); + "\nPeers trade limit=" + Coin.valueOf(peersCurrentTradeLimit).toFriendlyString() + + "\nOffer ID=" + offer.getShortId() + + "\nPaymentMethod=" + offer.getPaymentMethod().getId() + + "\nCurrencyCode=" + offer.getCurrencyCode(); log.warn(msg); errorMessageHandler.handleErrorMessage(msg); } From c962c1a183683ad84a6316b40dfbee5f810ce308 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 9 Dec 2020 14:02:57 -0500 Subject: [PATCH 2/3] Refactor: Rename presentation to displayString (no code change) --- .../account/witness/AccountAgeWitnessService.java | 12 ++++++------ .../core/account/witness/AccountAgeWitnessUtils.java | 2 +- .../java/bisq/desktop/components/PeerInfoIcon.java | 2 +- .../components/paymentmethods/PaymentMethodForm.java | 2 +- .../main/account/content/PaymentAccountsView.java | 4 +--- desktop/src/main/java/bisq/desktop/util/GUIUtil.java | 2 +- 6 files changed, 11 insertions(+), 13 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 40893c40652..b7678c7fa0c 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -102,12 +102,12 @@ public enum SignState { PEER_SIGNER(Res.get("offerbook.timeSinceSigning.info.signer")), BANNED(Res.get("offerbook.timeSinceSigning.info.banned")); - private String presentation; + private String displayString; private String hash = ""; private long daysUntilLimitLifted = 0; - SignState(String presentation) { - this.presentation = presentation; + SignState(String displayString) { + this.displayString = displayString; } public SignState addHash(String hash) { @@ -120,11 +120,11 @@ public SignState setDaysUntilLimitLifted(long days) { return this; } - public String getPresentation() { + public String getDisplayString() { if (!hash.isEmpty()) { // Only showing in DEBUG mode - return presentation + " " + hash; + return displayString + " " + hash; } - return String.format(presentation, daysUntilLimitLifted); + return String.format(displayString, daysUntilLimitLifted); } } diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java index c450e7e4aa0..6bc4b27218b 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessUtils.java @@ -123,7 +123,7 @@ private String getWitnessDebugLog(PaymentAccountPayload paymentAccountPayload, AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(accountAgeWitness.get()); - return signState.name() + " " + signState.getPresentation() + + return signState.name() + " " + signState.getDisplayString() + "\n" + accountAgeWitness.toString(); } diff --git a/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java b/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java index 0d91b849dfd..75f521c8e8e 100644 --- a/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java +++ b/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java @@ -279,7 +279,7 @@ private Tuple5 getPeersAccountAge(@Nullable if (hasChargebackRisk(trade, offer)) { String signAgeInfo = Res.get("peerInfo.age.chargeBackRisk"); - String accountSigningState = StringUtils.capitalize(signState.getPresentation()); + String accountSigningState = StringUtils.capitalize(signState.getDisplayString()); if (signState.equals(AccountAgeWitnessService.SignState.UNSIGNED)) signAgeInfo = null; diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java index 4dfebd26543..3aa4ba93633 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java @@ -218,7 +218,7 @@ protected void addLimitations(boolean isDisplayForm) { AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(myWitness); - accountSigningStateText = StringUtils.capitalize(signState.getPresentation()); + accountSigningStateText = StringUtils.capitalize(signState.getDisplayString()); long daysSinceSigning = TimeUnit.MILLISECONDS.toDays( accountAgeWitnessService.getWitnessSignAge(myWitness, new Date())); diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java index deb43641a5d..0ea778b2b0f 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/PaymentAccountsView.java @@ -9,14 +9,12 @@ import bisq.desktop.util.GUIUtil; import bisq.desktop.util.ImageUtil; -import bisq.core.account.sign.SignedWitnessService; import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.locale.Res; import bisq.core.payment.PaymentAccount; import bisq.core.payment.payload.PaymentMethod; import bisq.common.UserThread; -import bisq.common.app.DevEnv; import bisq.common.util.Utilities; import org.apache.commons.lang3.StringUtils; @@ -144,7 +142,7 @@ public void updateItem(final PaymentAccount item, boolean empty) { accountAgeWitnessService.getSignState(accountAgeWitnessService.getMyWitness( item.paymentAccountPayload)); - String info = StringUtils.capitalize(signState.getPresentation()); + String info = StringUtils.capitalize(signState.getDisplayString()); label.setIcon(GUIUtil.getIconForSignState(signState), info); } else { label.hideIcon(); diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index 93e1b9dc20e..6407cc2c76e 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -876,7 +876,7 @@ protected void updateItem(PaymentAccount item, boolean empty) { item.paymentAccountPayload); AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(myWitness); - String info = StringUtils.capitalize(signState.getPresentation()); + String info = StringUtils.capitalize(signState.getDisplayString()); MaterialDesignIcon icon; From 6378f3c602a36818b1d83fdba2a2e1a1bc01f5cc Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Wed, 9 Dec 2020 23:27:16 -0500 Subject: [PATCH 3/3] Add support for sorting by signed account age We separate in 4 groups and sort each by days. 1. signed accounts 2. not yet signed account, we use account age 3. not required signing, we use account age 4. no account age and no signing required (altcoins) --- .../offer/offerbook/OfferBookListItem.java | 130 +++++++++++++++++- .../main/offer/offerbook/OfferBookView.java | 67 ++------- 2 files changed, 143 insertions(+), 54 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookListItem.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookListItem.java index c65c255e993..4d389239935 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookListItem.java @@ -17,12 +17,140 @@ package bisq.desktop.main.offer.offerbook; +import bisq.desktop.util.GUIUtil; + +import bisq.core.account.sign.SignedWitnessService; +import bisq.core.account.witness.AccountAgeWitness; +import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.locale.CurrencyUtil; +import bisq.core.locale.Res; import bisq.core.offer.Offer; +import bisq.core.payment.payload.PaymentMethod; + +import de.jensd.fx.glyphs.GlyphIcons; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import java.util.Date; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import lombok.Getter; import lombok.Value; +import lombok.extern.slf4j.Slf4j; + +@Slf4j -@Value public class OfferBookListItem { + @Getter private final Offer offer; + + // We cache the data once created for performance reasons. AccountAgeWitnessService calls can + // be a bit expensive. + private WitnessAgeData witnessAgeData; + + public OfferBookListItem(Offer offer) { + this.offer = offer; + } + + public WitnessAgeData getWitnessAgeData(AccountAgeWitnessService accountAgeWitnessService, + SignedWitnessService signedWitnessService) { + if (witnessAgeData == null) { + long ageInMs; + long daysSinceSignedAsLong = -1; + long accountAgeDaysAsLong = -1; + long accountAgeDaysNotYetSignedAsLong = -1; + String displayString; + String info; + GlyphIcons icon; + + if (CurrencyUtil.isCryptoCurrency(offer.getCurrencyCode())) { + // Altcoins + displayString = Res.get("offerbook.timeSinceSigning.notSigned.noNeed"); + info = Res.get("shared.notSigned.noNeedAlts"); + icon = MaterialDesignIcon.INFORMATION_OUTLINE; + } else if (PaymentMethod.hasChargebackRisk(offer.getPaymentMethod(), offer.getCurrencyCode())) { + // Fiat and signed witness required + Optional optionalWitness = accountAgeWitnessService.findWitness(offer); + AccountAgeWitnessService.SignState signState = optionalWitness.map(accountAgeWitnessService::getSignState) + .orElse(AccountAgeWitnessService.SignState.UNSIGNED); + boolean isSignedAccountAgeWitness = optionalWitness.map(signedWitnessService::isSignedAccountAgeWitness) + .orElse(false); + if (isSignedAccountAgeWitness || !signState.equals(AccountAgeWitnessService.SignState.UNSIGNED)) { + // either signed & limits lifted, or waiting for limits to be lifted + // Or banned + daysSinceSignedAsLong = TimeUnit.MILLISECONDS.toDays(optionalWitness.map(witness -> + accountAgeWitnessService.getWitnessSignAge(witness, new Date())) + .orElse(0L)); + displayString = Res.get("offerbook.timeSinceSigning.daysSinceSigning", daysSinceSignedAsLong); + info = Res.get("offerbook.timeSinceSigning.info", signState.getDisplayString()); + } else { + // Unsigned case + ageInMs = optionalWitness.map(e -> accountAgeWitnessService.getAccountAge(e, new Date())) + .orElse(-1L); + accountAgeDaysNotYetSignedAsLong = ageInMs > -1 ? TimeUnit.MILLISECONDS.toDays(ageInMs) : 0; + displayString = Res.get("offerbook.timeSinceSigning.notSigned"); + info = Res.get("shared.notSigned", accountAgeDaysNotYetSignedAsLong); + } + + icon = GUIUtil.getIconForSignState(signState); + } else { + // Fiat, no signed witness required, we show account age + ageInMs = accountAgeWitnessService.getAccountAge(offer); + accountAgeDaysAsLong = ageInMs > -1 ? TimeUnit.MILLISECONDS.toDays(ageInMs) : 0; + displayString = Res.get("offerbook.timeSinceSigning.notSigned.ageDays", accountAgeDaysAsLong); + info = Res.get("shared.notSigned.noNeedDays", accountAgeDaysAsLong); + icon = MaterialDesignIcon.CHECKBOX_MARKED_OUTLINE; + } + + witnessAgeData = new WitnessAgeData(displayString, info, icon, daysSinceSignedAsLong, accountAgeDaysNotYetSignedAsLong, accountAgeDaysAsLong); + } + return witnessAgeData; + } + + @Value + public static class WitnessAgeData { + private final String displayString; + private final String info; + private final GlyphIcons icon; + private final Long daysSinceSignedAsLong; + private final long accountAgeDaysNotYetSignedAsLong; + private final Long accountAgeDaysAsLong; + // Used for sorting + private final Long type; + // Used for sorting + private final Long days; + + public WitnessAgeData(String displayString, + String info, + GlyphIcons icon, + long daysSinceSignedAsLong, + long accountAgeDaysNotYetSignedAsLong, + long accountAgeDaysAsLong) { + this.displayString = displayString; + this.info = info; + this.icon = icon; + this.daysSinceSignedAsLong = daysSinceSignedAsLong; + this.accountAgeDaysNotYetSignedAsLong = accountAgeDaysNotYetSignedAsLong; + this.accountAgeDaysAsLong = accountAgeDaysAsLong; + + if (daysSinceSignedAsLong > -1) { + // First we show signed accounts sorted by days + this.type = 3L; + this.days = daysSinceSignedAsLong; + } else if (accountAgeDaysNotYetSignedAsLong > -1) { + // Next group is not yet signed accounts sorted by account age + this.type = 2L; + this.days = accountAgeDaysNotYetSignedAsLong; + } else if (accountAgeDaysAsLong > -1) { + // Next group is not signing required accounts sorted by account age + this.type = 1L; + this.days = accountAgeDaysAsLong; + } else { + // No signing and age required (altcoins) + this.type = 0L; + this.days = 0L; + } + } + } } 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 3716b8ce7aa..32755d2b616 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 @@ -69,7 +69,6 @@ import javax.inject.Named; import de.jensd.fx.fontawesome.AwesomeIcon; -import de.jensd.fx.glyphs.GlyphIcons; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import javafx.scene.canvas.Canvas; @@ -107,9 +106,7 @@ import javafx.util.StringConverter; import java.util.Comparator; -import java.util.Date; import java.util.Optional; -import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; @@ -124,6 +121,7 @@ public class OfferBookView extends ActivatableViewAndModel currencyComboBox; private AutocompleteComboBox paymentMethodComboBox; @@ -151,7 +149,8 @@ public class OfferBookView extends ActivatableViewAndModel comparator = Comparator.comparing(e -> e.getWitnessAgeData(accountAgeWitnessService, signedWitnessService).getType(), Comparator.nullsFirst(Comparator.naturalOrder())); + signingStateColumn.setComparator(comparator. + thenComparing(e -> e.getWitnessAgeData(accountAgeWitnessService, signedWitnessService).getDays(), + Comparator.nullsFirst(Comparator.naturalOrder()))); + nrOfOffersLabel = new AutoTooltipLabel(""); nrOfOffersLabel.setId("num-offers"); GridPane.setHalignment(nrOfOffersLabel, HPos.LEFT); @@ -1108,55 +1113,11 @@ public void updateItem(final OfferBookListItem item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { - - GlyphIcons icon; - String info; - String timeSinceSigning; - - boolean needsSigning = PaymentMethod.hasChargebackRisk( - item.getOffer().getPaymentMethod(), item.getOffer().getCurrencyCode()); - - if (needsSigning) { - if (accountAgeWitnessService.hasSignedWitness(item.getOffer())) { - // either signed & limits lifted, or waiting for limits to be lifted - AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(item.getOffer()); - icon = GUIUtil.getIconForSignState(signState); - info = Res.get("offerbook.timeSinceSigning.info", - signState.getPresentation()); - long daysSinceSigning = TimeUnit.MILLISECONDS.toDays( - accountAgeWitnessService.getWitnessSignAge(item.getOffer(), new Date())); - timeSinceSigning = Res.get("offerbook.timeSinceSigning.daysSinceSigning", - daysSinceSigning); - } else { - // either banned, unsigned - AccountAgeWitnessService.SignState signState = accountAgeWitnessService.getSignState(item.getOffer()); - icon = GUIUtil.getIconForSignState(signState); - if (!signState.equals(AccountAgeWitnessService.SignState.UNSIGNED)) { - info = Res.get("offerbook.timeSinceSigning.info", signState.getPresentation()); - long daysSinceSigning = TimeUnit.MILLISECONDS.toDays( - accountAgeWitnessService.getWitnessSignAge(item.getOffer(), new Date())); - timeSinceSigning = Res.get("offerbook.timeSinceSigning.daysSinceSigning", - daysSinceSigning); - } else { - long accountAge = TimeUnit.MILLISECONDS.toDays(accountAgeWitnessService.getAccountAge(item.getOffer())); - info = Res.get("shared.notSigned", accountAge); - timeSinceSigning = Res.get("offerbook.timeSinceSigning.notSigned", accountAge); - } - } - } else { - if (CurrencyUtil.isFiatCurrency(item.getOffer().getCurrencyCode())) { - icon = MaterialDesignIcon.CHECKBOX_MARKED_OUTLINE; - long days = TimeUnit.MILLISECONDS.toDays(accountAgeWitnessService.getAccountAge(item.getOffer())); - info = Res.get("shared.notSigned.noNeedDays", days); - timeSinceSigning = Res.get("offerbook.timeSinceSigning.notSigned.ageDays", days); - } else { // altcoins - icon = MaterialDesignIcon.INFORMATION_OUTLINE; - info = Res.get("shared.notSigned.noNeedAlts"); - timeSinceSigning = Res.get("offerbook.timeSinceSigning.notSigned.noNeed"); - } - } - - InfoAutoTooltipLabel label = new InfoAutoTooltipLabel(timeSinceSigning, icon, ContentDisplay.RIGHT, info); + var witnessAgeData = item.getWitnessAgeData(accountAgeWitnessService, signedWitnessService); + InfoAutoTooltipLabel label = new InfoAutoTooltipLabel(witnessAgeData.getDisplayString(), + witnessAgeData.getIcon(), + ContentDisplay.RIGHT, + witnessAgeData.getInfo()); setGraphic(label); } else { setGraphic(null);