diff --git a/core/src/main/java/bisq/core/util/FormattingUtils.java b/core/src/main/java/bisq/core/util/FormattingUtils.java index d6658e7129..cf8e098b6f 100644 --- a/core/src/main/java/bisq/core/util/FormattingUtils.java +++ b/core/src/main/java/bisq/core/util/FormattingUtils.java @@ -7,6 +7,7 @@ import bisq.core.monetary.Altcoin; import bisq.core.monetary.Price; import bisq.common.util.MathUtils; +import bisq.common.util.Tuple3; import org.bitcoinj.core.Coin; import org.bitcoinj.core.Monetary; @@ -33,12 +34,15 @@ import org.jetbrains.annotations.NotNull; public class FormattingUtils { public static final String BTC_FORMATTER_KEY = "BTC"; - public final static String RANGE_SEPARATOR = " - "; + public static final String RANGE_SEPARATOR = " - "; private static final MonetaryFormat fiatPriceFormat = new MonetaryFormat().shift(0).minDecimals(4).repeatOptionalDecimals(0, 0); private static final MonetaryFormat altcoinFormat = new MonetaryFormat().shift(0).minDecimals(8).repeatOptionalDecimals(0, 0); private static final DecimalFormat decimalFormat = new DecimalFormat("#.#"); + private static final ThreadLocal> cachedUtcDateTimeFormatters = new ThreadLocal<>(); + private static final ThreadLocal> cachedLocalDateTimeFormatters = new ThreadLocal<>(); + public static String formatCoinWithCode(long value, MonetaryFormat coinFormat) { return formatCoinWithCode(Coin.valueOf(value), coinFormat); } @@ -183,12 +187,25 @@ public class FormattingUtils { public static String formatDateTime(Date date, boolean useLocaleAndLocalTimezone) { Locale locale = useLocaleAndLocalTimezone ? GlobalSettings.getLocale() : Locale.US; - DateFormat dateInstance = DateFormat.getDateInstance(DateFormat.DEFAULT, locale); - DateFormat timeInstance = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale); - if (!useLocaleAndLocalTimezone) { - dateInstance.setTimeZone(TimeZone.getTimeZone("UTC")); - timeInstance.setTimeZone(TimeZone.getTimeZone("UTC")); + + var formatterTuple = (useLocaleAndLocalTimezone ? + cachedLocalDateTimeFormatters : cachedUtcDateTimeFormatters).get(); + if (formatterTuple == null || !formatterTuple.first.equals(locale)) { + formatterTuple = new Tuple3<>(locale, + DateFormat.getDateInstance(DateFormat.DEFAULT, locale), + DateFormat.getTimeInstance(DateFormat.DEFAULT, locale)); + + if (useLocaleAndLocalTimezone) { + cachedLocalDateTimeFormatters.set(formatterTuple); + } else { + formatterTuple.second.setTimeZone(TimeZone.getTimeZone("UTC")); + formatterTuple.third.setTimeZone(TimeZone.getTimeZone("UTC")); + cachedUtcDateTimeFormatters.set(formatterTuple); + } } + DateFormat dateInstance = formatterTuple.second; + DateFormat timeInstance = formatterTuple.third; + return formatDateTime(date, dateInstance, timeInstance); } @@ -288,7 +305,8 @@ public class FormattingUtils { } @NotNull - public static String fillUpPlacesWithEmptyStrings(String formattedNumber, @SuppressWarnings("unused") int maxNumberOfDigits) { + public static String fillUpPlacesWithEmptyStrings(String formattedNumber, + @SuppressWarnings("unused") int maxNumberOfDigits) { //FIXME: temporary deactivate adding spaces in front of numbers as we don't use a monospace font right now. /*int numberOfPlacesToFill = maxNumberOfDigits - formattedNumber.length(); for (int i = 0; i < numberOfPlacesToFill; i++) { diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/RelatedTransactionFilterSlices.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/RelatedTransactionFilterSlices.java new file mode 100644 index 0000000000..08f168dc2e --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/RelatedTransactionFilterSlices.java @@ -0,0 +1,52 @@ +/* + * 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 . + */ + +package bisq.desktop.main.funds.transactions; + +import org.bitcoinj.core.Transaction; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static bisq.desktop.main.funds.transactions.TransactionAwareTradable.TX_FILTER_SIZE; + +public class RelatedTransactionFilterSlices { + private final List tradables; + private final BitSet[] filterSlices; + + public RelatedTransactionFilterSlices(Collection tradables) { + this.tradables = List.copyOf(tradables); + + filterSlices = new BitSet[TX_FILTER_SIZE]; + Arrays.setAll(filterSlices, i -> new BitSet(this.tradables.size())); + + IntStream.range(0, this.tradables.size()) + .forEach(j -> this.tradables.get(j).getRelatedTransactionFilter() + .forEach(i -> filterSlices[i].set(j))); + } + + public Stream getAllRelatedTradables(Transaction tx) { + int i = TransactionAwareTradable.bucketIndex(tx); + return filterSlices[i].stream() + .mapToObj(tradables::get) + .filter(t -> t.isRelatedToTransaction(tx)); + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareOpenOffer.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareOpenOffer.java index 4a4368b01a..b9f14f1e8a 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareOpenOffer.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareOpenOffer.java @@ -23,6 +23,8 @@ import bisq.core.trade.model.Tradable; import org.bitcoinj.core.Transaction; +import java.util.stream.IntStream; + class TransactionAwareOpenOffer implements TransactionAwareTradable { private final OpenOffer delegate; @@ -34,12 +36,18 @@ class TransactionAwareOpenOffer implements TransactionAwareTradable { Offer offer = delegate.getOffer(); String paymentTxId = offer.getOfferFeePaymentTxId(); - String txId = transaction.getTxId().toString(); - - return paymentTxId != null && paymentTxId.equals(txId); + return paymentTxId != null && paymentTxId.equals(transaction.getTxId().toString()); } public Tradable asTradable() { return delegate; } + + @Override + public IntStream getRelatedTransactionFilter() { + Offer offer = delegate.getOffer(); + String paymentTxId = offer.getOfferFeePaymentTxId(); + return IntStream.of(TransactionAwareTradable.bucketIndex(paymentTxId)) + .filter(i -> i >= 0); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTradable.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTradable.java index b00c283d96..99d05de3ac 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTradable.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTradable.java @@ -19,10 +19,35 @@ package bisq.desktop.main.funds.transactions; import bisq.core.trade.model.Tradable; +import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; +import java.util.stream.IntStream; + +import javax.annotation.Nullable; + interface TransactionAwareTradable { + int TX_FILTER_SIZE = 64; + int DELAYED_PAYOUT_TX_BUCKET_INDEX = TX_FILTER_SIZE - 1; + boolean isRelatedToTransaction(Transaction transaction); Tradable asTradable(); + + /** Returns a list of bucket indices of all transactions which might be related to this Tradable. */ + IntStream getRelatedTransactionFilter(); + + static int bucketIndex(Transaction tx) { + return tx.getLockTime() == 0 ? bucketIndex(tx.getTxId()) : DELAYED_PAYOUT_TX_BUCKET_INDEX; + } + + static int bucketIndex(Sha256Hash hash) { + int i = hash.getBytes()[31] & 255; + return i % TX_FILTER_SIZE != DELAYED_PAYOUT_TX_BUCKET_INDEX ? + i % TX_FILTER_SIZE : i / TX_FILTER_SIZE; + } + + static int bucketIndex(@Nullable String txId) { + return txId != null ? bucketIndex(Sha256Hash.wrap(txId)) : -1; + } } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTrade.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTrade.java index d940ad5df7..b0c21a6ff9 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTrade.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTrade.java @@ -29,18 +29,23 @@ import bisq.core.trade.model.bisq_v1.Trade; import bisq.core.trade.model.bsq_swap.BsqSwapTrade; import bisq.common.crypto.PubKeyRing; +import bisq.common.util.Tuple2; import org.bitcoinj.core.Address; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionOutput; +import com.google.common.collect.ImmutableSet; + import javafx.collections.ObservableList; -import java.util.Optional; +import java.util.Set; +import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; +import static bisq.desktop.main.funds.transactions.TransactionAwareTradable.bucketIndex; import static com.google.common.base.Preconditions.checkNotNull; @Slf4j @@ -51,6 +56,11 @@ class TransactionAwareTrade implements TransactionAwareTradable { private final BtcWalletService btcWalletService; private final PubKeyRing pubKeyRing; + // As Sha256Hash.toString() is expensive, cache the last result, which will usually be next one needed. + private static Tuple2 lastTxIdTuple; + // Similarly, cache the last computed set of tx receiver addresses, to speed up 'isRefundPayoutTx'. + private static Tuple2> lastReceiverAddressStringsTuple; + TransactionAwareTrade(TradeModel tradeModel, ArbitrationManager arbitrationManager, RefundManager refundManager, @@ -66,15 +76,19 @@ class TransactionAwareTrade implements TransactionAwareTradable { @Override public boolean isRelatedToTransaction(Transaction transaction) { Sha256Hash hash = transaction.getTxId(); - String txId = hash.toString(); + var txIdTuple = lastTxIdTuple; + if (txIdTuple == null || !txIdTuple.first.equals(hash)) { + lastTxIdTuple = txIdTuple = new Tuple2<>(hash, hash.toString()); + } + String txId = txIdTuple.second; boolean tradeRelated = false; if (tradeModel instanceof Trade) { Trade trade = (Trade) tradeModel; boolean isTakerOfferFeeTx = txId.equals(trade.getTakerFeeTxId()); boolean isOfferFeeTx = isOfferFeeTx(txId); - boolean isDepositTx = isDepositTx(hash); - boolean isPayoutTx = isPayoutTx(hash); + boolean isDepositTx = isDepositTx(txId); + boolean isPayoutTx = isPayoutTx(txId); boolean isDisputedPayoutTx = isDisputedPayoutTx(txId); boolean isDelayedPayoutTx = transaction.getLockTime() != 0 && isDelayedPayoutTx(txId); boolean isRefundPayoutTx = isRefundPayoutTx(trade, txId); @@ -91,36 +105,28 @@ class TransactionAwareTrade implements TransactionAwareTradable { return tradeRelated || isBsqSwapTrade; } - private boolean isPayoutTx(Sha256Hash txId) { + private boolean isPayoutTx(String txId) { if (isBsqSwapTrade()) return false; Trade trade = (Trade) tradeModel; - return Optional.ofNullable(trade.getPayoutTx()) - .map(Transaction::getTxId) - .map(hash -> hash.equals(txId)) - .orElse(false); + return txId.equals(trade.getPayoutTxId()); } - private boolean isDepositTx(Sha256Hash txId) { + private boolean isDepositTx(String txId) { if (isBsqSwapTrade()) return false; Trade trade = (Trade) tradeModel; - return Optional.ofNullable(trade.getDepositTx()) - .map(Transaction::getTxId) - .map(hash -> hash.equals(txId)) - .orElse(false); + return txId.equals(trade.getDepositTxId()); } private boolean isOfferFeeTx(String txId) { if (isBsqSwapTrade()) return false; - return Optional.ofNullable(tradeModel.getOffer()) - .map(Offer::getOfferFeePaymentTxId) - .map(paymentTxId -> paymentTxId.equals(txId)) - .orElse(false); + Offer offer = tradeModel.getOffer(); + return offer != null && txId.equals(offer.getOfferFeePaymentTxId()); } private boolean isDisputedPayoutTx(String txId) { @@ -130,7 +136,7 @@ class TransactionAwareTrade implements TransactionAwareTradable { String delegateId = tradeModel.getId(); ObservableList disputes = arbitrationManager.getDisputesAsObservableList(); - boolean isAnyDisputeRelatedToThis = arbitrationManager.getDisputedTradeIds().contains(tradeModel.getId()); + boolean isAnyDisputeRelatedToThis = arbitrationManager.getDisputedTradeIds().contains(delegateId); return isAnyDisputeRelatedToThis && disputes.stream() .anyMatch(dispute -> { @@ -155,7 +161,7 @@ class TransactionAwareTrade implements TransactionAwareTradable { if (transaction.getLockTime() == 0) return false; - if (transaction.getInputs() == null) + if (transaction.getInputs() == null || transaction.getInputs().size() != 1) return false; return transaction.getInputs().stream() @@ -168,7 +174,7 @@ class TransactionAwareTrade implements TransactionAwareTradable { if (parentTransaction == null) { return false; } - return isDepositTx(parentTransaction.getTxId()); + return isDepositTx(parentTransaction.getTxId().toString()); }); } @@ -177,33 +183,45 @@ class TransactionAwareTrade implements TransactionAwareTradable { return false; String tradeId = tradeModel.getId(); - ObservableList disputes = refundManager.getDisputesAsObservableList(); - boolean isAnyDisputeRelatedToThis = refundManager.getDisputedTradeIds().contains(tradeId); if (isAnyDisputeRelatedToThis) { - Transaction tx = btcWalletService.getTransaction(txId); - if (tx != null) { - for (TransactionOutput txo : tx.getOutputs()) { - if (btcWalletService.isTransactionOutputMine(txo)) { - try { - Address receiverAddress = txo.getScriptPubKey().getToAddress(btcWalletService.getParams()); - Contract contract = checkNotNull(trade.getContract()); - String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ? - contract.getBuyerPayoutAddressString() : - contract.getSellerPayoutAddressString(); - if (receiverAddress != null && myPayoutAddressString.equals(receiverAddress.toString())) { - return true; - } - } catch (RuntimeException ignore) { - } - } - } + try { + Contract contract = checkNotNull(trade.getContract()); + String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ? + contract.getBuyerPayoutAddressString() : + contract.getSellerPayoutAddressString(); + + return getReceiverAddressStrings(txId).contains(myPayoutAddressString); + } catch (RuntimeException ignore) { } } return false; } + private Set getReceiverAddressStrings(String txId) { + var tuple = lastReceiverAddressStringsTuple; + if (tuple == null || !tuple.first.equals(txId)) { + lastReceiverAddressStringsTuple = tuple = computeReceiverAddressStringsTuple(txId); + } + return tuple != null ? tuple.second : ImmutableSet.of(); + } + + private Tuple2> computeReceiverAddressStringsTuple(String txId) { + Transaction tx = btcWalletService.getTransaction(txId); + if (tx == null) { + // Clear cache if the tx isn't found, as theoretically it could be added to the wallet later. + return null; + } + Set addressStrings = tx.getOutputs().stream() + .filter(btcWalletService::isTransactionOutputMine) + .map(txo -> txo.getScriptPubKey().getToAddress(btcWalletService.getParams())) + .map(Address::toString) + .collect(ImmutableSet.toImmutableSet()); + + return new Tuple2<>(txId, addressStrings); + } + private boolean isBsqSwapTrade() { return tradeModel instanceof BsqSwapTrade; } @@ -219,4 +237,27 @@ class TransactionAwareTrade implements TransactionAwareTradable { public Tradable asTradable() { return tradeModel; } + + @Override + public IntStream getRelatedTransactionFilter() { + if (tradeModel instanceof Trade && !arbitrationManager.getDisputedTradeIds().contains(tradeModel.getId()) && + !refundManager.getDisputedTradeIds().contains(tradeModel.getId())) { + Trade trade = (Trade) tradeModel; + String takerFeeTxId = trade.getTakerFeeTxId(); + String offerFeeTxId = trade.getOffer() != null ? trade.getOffer().getOfferFeePaymentTxId() : null; + String depositTxId = trade.getDepositTxId(); + String payoutTxId = trade.getPayoutTxId(); + return IntStream.of(DELAYED_PAYOUT_TX_BUCKET_INDEX, bucketIndex(takerFeeTxId), bucketIndex(offerFeeTxId), + bucketIndex(depositTxId), bucketIndex(payoutTxId)) + .filter(i -> i >= 0); + } else if (tradeModel instanceof BsqSwapTrade) { + BsqSwapTrade trade = (BsqSwapTrade) tradeModel; + String swapTxId = trade.getTxId(); + return IntStream.of(bucketIndex(swapTxId)) + .filter(i -> i >= 0); + } else { + // We are involved in a dispute (rare) - don't do any initial tx filtering. + return IntStream.range(0, TX_FILTER_SIZE); + } + } } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java index 49a32bbcf7..6b67ee2d5d 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java @@ -17,12 +17,11 @@ package bisq.desktop.main.funds.transactions; -import bisq.desktop.util.filtering.FilterableListItem; import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.util.DisplayUtils; import bisq.desktop.util.GUIUtil; +import bisq.desktop.util.filtering.FilterableListItem; -import bisq.core.btc.listeners.TxConfidenceListener; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.WalletService; @@ -59,7 +58,6 @@ import javax.annotation.Nullable; @Slf4j class TransactionsListItem implements FilterableListItem { - private final BtcWalletService btcWalletService; private final CoinFormatter formatter; private String dateString; private final Date date; @@ -69,7 +67,6 @@ class TransactionsListItem implements FilterableListItem { private String details = ""; private String addressString = ""; private String direction = ""; - private TxConfidenceListener txConfidenceListener; private boolean received; private Coin amountAsCoin = Coin.ZERO; private String memo = ""; @@ -91,7 +88,6 @@ class TransactionsListItem implements FilterableListItem { // used at exportCSV TransactionsListItem() { date = null; - btcWalletService = null; txId = null; formatter = null; isDustAttackTx = false; @@ -105,7 +101,6 @@ class TransactionsListItem implements FilterableListItem { DaoFacade daoFacade, CoinFormatter formatter, long ignoreDustThreshold) { - this.btcWalletService = btcWalletService; this.formatter = formatter; this.memo = transaction.getMemo(); @@ -197,7 +192,6 @@ class TransactionsListItem implements FilterableListItem { if (optionalTradable.isPresent()) { tradable = optionalTradable.get(); - String tradeId = tradable.getShortId(); if (tradable instanceof OpenOffer) { details = Res.get("funds.tx.createOfferFee"); } else if (tradable instanceof Trade) { @@ -304,19 +298,6 @@ class TransactionsListItem implements FilterableListItem { GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator); confirmations = confidence.getDepthInBlocks(); }}); - - txConfidenceListener = new TxConfidenceListener(txId) { - @Override - public void onTransactionConfidenceChanged(TransactionConfidence confidence) { - GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator); - confirmations = confidence.getDepthInBlocks(); - } - }; - btcWalletService.addTxConfidenceListener(txConfidenceListener); - } - - public void cleanup() { - btcWalletService.removeTxConfidenceListener(txConfidenceListener); } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java index 8c341c91fb..39945f799c 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java @@ -95,6 +95,7 @@ import javafx.util.Callback; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -201,7 +202,7 @@ public class TransactionsView extends ActivatableView { dateColumn.setComparator(Comparator.comparing(TransactionsListItem::getDate)); tradeIdColumn.setComparator(Comparator.comparing(o -> o.getTradable() != null ? o.getTradable().getId() : "")); - detailsColumn.setComparator(Comparator.comparing(o -> o.getDetails())); + detailsColumn.setComparator(Comparator.comparing(TransactionsListItem::getDetails)); addressColumn.setComparator(Comparator.comparing(item -> item.getDirection() + item.getAddressString())); transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId)); amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmountAsCoin)); @@ -211,9 +212,7 @@ public class TransactionsView extends ActivatableView { dateColumn.setSortType(TableColumn.SortType.DESCENDING); tableView.getSortOrder().add(dateColumn); - walletChangeEventListener = wallet -> { - updateList(); - }; + walletChangeEventListener = wallet -> updateList(); keyEventEventHandler = event -> { // Not intended to be public to users as the feature is not well tested @@ -280,7 +279,6 @@ public class TransactionsView extends ActivatableView { protected void deactivate() { filterBox.deactivate(); sortedList.comparatorProperty().unbind(); - observableList.forEach(TransactionsListItem::cleanup); btcWalletService.removeChangeEventListener(walletChangeEventListener); if (scene != null) @@ -290,28 +288,30 @@ public class TransactionsView extends ActivatableView { } private void updateList() { + Set tradables = tradableRepository.getAll(); + var filterSlices = new RelatedTransactionFilterSlices(tradables.stream() + .map(tradable -> { + if (tradable instanceof OpenOffer) { + return new TransactionAwareOpenOffer((OpenOffer) tradable); + } else if (tradable instanceof TradeModel) { + return new TransactionAwareTrade( + (TradeModel) tradable, + arbitrationManager, + refundManager, + btcWalletService, + pubKeyRing + ); + } else { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toUnmodifiableList())); + List transactionsListItems = btcWalletService.getTransactions(false) .stream() .map(transaction -> { - Set tradables = tradableRepository.getAll(); - - TransactionAwareTradable maybeTradable = tradables.stream() - .map(tradable -> { - if (tradable instanceof OpenOffer) { - return new TransactionAwareOpenOffer((OpenOffer) tradable); - } else if (tradable instanceof TradeModel) { - return new TransactionAwareTrade( - (TradeModel) tradable, - arbitrationManager, - refundManager, - btcWalletService, - pubKeyRing - ); - } else { - return null; - } - }) - .filter(tradable -> tradable != null && tradable.isRelatedToTransaction(transaction)) + TransactionAwareTradable maybeTradable = filterSlices.getAllRelatedTradables(transaction) .findAny() .orElse(null); @@ -327,7 +327,6 @@ public class TransactionsView extends ActivatableView { }) .collect(Collectors.toList()); // are sorted by getRecentTransactions - transactionsListItems.forEach(TransactionsListItem::cleanup); observableList.setAll(transactionsListItems); } @@ -419,15 +418,13 @@ public class TransactionsView extends ActivatableView { TransactionsListItem> column) { return new TableCell<>() { - private HyperlinkWithIcon hyperlinkWithIcon; - @Override public void updateItem(final TransactionsListItem item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { if (item.isDustAttackTx()) { - hyperlinkWithIcon = new HyperlinkWithIcon(item.getDetails(), AwesomeIcon.WARNING_SIGN); + var hyperlinkWithIcon = new HyperlinkWithIcon(item.getDetails(), AwesomeIcon.WARNING_SIGN); hyperlinkWithIcon.setOnAction(event -> new Popup().warning(Res.get("funds.tx.dustAttackTx.popup")).show()); setGraphic(hyperlinkWithIcon); } else { diff --git a/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradeTest.java b/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradeTest.java index 711dac141c..9659e202fe 100644 --- a/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradeTest.java +++ b/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradeTest.java @@ -66,13 +66,13 @@ public class TransactionAwareTradeTest { @Test public void testIsRelatedToTransactionWhenPayoutTx() { - when(delegate.getPayoutTx().getTxId()).thenReturn(XID); + when(delegate.getPayoutTxId()).thenReturn(XID.toString()); assertTrue(trade.isRelatedToTransaction(transaction)); } @Test public void testIsRelatedToTransactionWhenDepositTx() { - when(delegate.getDepositTx().getTxId()).thenReturn(XID); + when(delegate.getDepositTxId()).thenReturn(XID.toString()); assertTrue(trade.isRelatedToTransaction(transaction)); }