diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index f2c3d251c6d..7b65e81684f 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -72,6 +72,7 @@ import javax.inject.Inject; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Multiset; @@ -87,6 +88,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; @@ -122,6 +124,7 @@ public abstract class WalletService { private final WalletChangeEventListener cacheInvalidationListener; private final AtomicReference> txOutputAddressCache = new AtomicReference<>(); private final AtomicReference> addressToMatchingTxSetCache = new AtomicReference<>(); + private final AtomicReference> txByIdCache = new AtomicReference<>(); @Getter protected Wallet wallet; @Getter @@ -147,6 +150,7 @@ public abstract class WalletService { cacheInvalidationListener = wallet -> { txOutputAddressCache.set(null); addressToMatchingTxSetCache.set(null); + txByIdCache.set(null); }; } @@ -440,15 +444,15 @@ public void broadcastTx(Transaction tx, TxBroadcaster.Callback callback, int tim public TransactionConfidence getConfidenceForAddress(Address address) { List transactionConfidenceList = new ArrayList<>(); if (wallet != null) { - Set transactions = getAddressToMatchingTxSetMultiset().get(address); + Set transactions = getAddressToMatchingTxSetMultimap().get(address); transactionConfidenceList.addAll(transactions.stream().map(tx -> getTransactionConfidence(tx, address)).collect(Collectors.toList())); } return getMostRecentConfidence(transactionConfidenceList); } - private SetMultimap getAddressToMatchingTxSetMultiset() { - return addressToMatchingTxSetCache.updateAndGet(set -> set != null ? set : computeAddressToMatchingTxSetMultimap()); + private SetMultimap getAddressToMatchingTxSetMultimap() { + return addressToMatchingTxSetCache.updateAndGet(map -> map != null ? map : computeAddressToMatchingTxSetMultimap()); } private SetMultimap computeAddressToMatchingTxSetMultimap() { @@ -463,17 +467,25 @@ private SetMultimap computeAddressToMatchingTxSetMultimap( } @Nullable - public TransactionConfidence getConfidenceForTxId(String txId) { - if (wallet != null) { - Set transactions = wallet.getTransactions(false); - for (Transaction tx : transactions) { - if (tx.getTxId().toString().equals(txId)) - return tx.getConfidence(); + public TransactionConfidence getConfidenceForTxId(@Nullable String txId) { + if (wallet != null && txId != null) { + Transaction tx = getTxByIdMap().get(Sha256Hash.wrap(txId)); + if (tx != null) { + return tx.getConfidence(); } } return null; } + private Map getTxByIdMap() { + return txByIdCache.updateAndGet(map -> map != null ? map : computeTxByIdMap()); + } + + private Map computeTxByIdMap() { + return wallet.getTransactions(false).stream() + .collect(ImmutableMap.toImmutableMap(Transaction::getTxId, tx -> tx)); + } + @Nullable private TransactionConfidence getTransactionConfidence(Transaction tx, Address address) { List transactionConfidenceList = getOutputsWithConnectedOutputs(tx).stream() @@ -761,7 +773,7 @@ public Transaction getTransaction(Sha256Hash hash) { } @Nullable - public Transaction getTransaction(String txId) { + public Transaction getTransaction(@Nullable String txId) { if (txId == null) { return null; } diff --git a/core/src/main/java/bisq/core/trade/ClosedTradableManager.java b/core/src/main/java/bisq/core/trade/ClosedTradableManager.java index 284a75f862f..8d6fe591e63 100644 --- a/core/src/main/java/bisq/core/trade/ClosedTradableManager.java +++ b/core/src/main/java/bisq/core/trade/ClosedTradableManager.java @@ -29,7 +29,6 @@ import bisq.core.trade.model.MakerTrade; import bisq.core.trade.model.Tradable; import bisq.core.trade.model.TradableList; -import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Trade; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; @@ -47,6 +46,8 @@ import com.google.inject.Inject; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.Multiset; import javafx.collections.ObservableList; @@ -63,6 +64,8 @@ import lombok.extern.slf4j.Slf4j; +import javax.annotation.Nullable; + import static bisq.core.offer.OpenOffer.State.CANCELED; import static bisq.core.trade.ClosedTradableUtil.castToTrade; import static bisq.core.trade.ClosedTradableUtil.castToTradeModel; @@ -88,6 +91,8 @@ public class ClosedTradableManager implements PersistedDataHost { private final DumpDelayedPayoutTx dumpDelayedPayoutTx; private final TradableList closedTradables = new TradableList<>(); + @Nullable + private Multiset closedTradeNodeAddressCache; @Inject public ClosedTradableManager(KeyRing keyRing, @@ -109,6 +114,8 @@ public ClosedTradableManager(KeyRing keyRing, this.dumpDelayedPayoutTx = dumpDelayedPayoutTx; this.persistenceManager = persistenceManager; + closedTradables.addListener(c -> closedTradeNodeAddressCache = null); + this.persistenceManager.initialize(closedTradables, "ClosedTrades", PersistenceManager.Source.PRIVATE); } @@ -206,8 +213,7 @@ public boolean canTradeHaveSensitiveDataCleared(String tradeId) { Instant safeDate = getSafeDateForSensitiveDataClearing(); return closedTradables.stream() .filter(e -> e.getId().equals(tradeId)) - .filter(e -> e.getDate().toInstant().isBefore(safeDate)) - .count() > 0; + .anyMatch(e -> e.getDate().toInstant().isBefore(safeDate)); } public Instant getSafeDateForSensitiveDataClearing() { @@ -220,8 +226,16 @@ public Stream getTradesStreamWithFundsLockedIn() { .filter(Trade::isFundsLockedIn); } - public Stream getTradeModelStream() { - return Stream.concat(bsqSwapTradeManager.getConfirmedBsqSwapTrades(), getClosedTrades().stream()); + private Multiset getClosedTradeNodeAddresses() { + var addresses = closedTradeNodeAddressCache; + if (addresses == null) { + closedTradeNodeAddressCache = addresses = closedTradables.stream() + .filter(t -> t instanceof Trade) + .map(t -> ((Trade) t).getTradingPeerNodeAddress()) + .filter(Objects::nonNull) + .collect(ImmutableMultiset.toImmutableMultiset()); + } + return addresses; } public int getNumPastTrades(Tradable tradable) { @@ -229,11 +243,8 @@ public int getNumPastTrades(Tradable tradable) { return 0; } NodeAddress addressInTrade = castToTradeModel(tradable).getTradingPeerNodeAddress(); - return (int) getTradeModelStream() - .map(TradeModel::getTradingPeerNodeAddress) - .filter(Objects::nonNull) - .filter(address -> address.equals(addressInTrade)) - .count(); + return bsqSwapTradeManager.getConfirmedBsqSwapNodeAddresses().count(addressInTrade) + + getClosedTradeNodeAddresses().count(addressInTrade); } public boolean isCurrencyForTradeFeeBtc(Tradable tradable) { diff --git a/core/src/main/java/bisq/core/trade/bsq_swap/BsqSwapTradeManager.java b/core/src/main/java/bisq/core/trade/bsq_swap/BsqSwapTradeManager.java index 6ea6774a13b..03b7521a772 100644 --- a/core/src/main/java/bisq/core/trade/bsq_swap/BsqSwapTradeManager.java +++ b/core/src/main/java/bisq/core/trade/bsq_swap/BsqSwapTradeManager.java @@ -22,8 +22,11 @@ import bisq.core.provider.price.PriceFeedService; import bisq.core.trade.model.Tradable; import bisq.core.trade.model.TradableList; +import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bsq_swap.BsqSwapTrade; +import bisq.network.p2p.NodeAddress; + import bisq.common.crypto.KeyRing; import bisq.common.persistence.PersistenceManager; import bisq.common.proto.persistable.PersistedDataHost; @@ -34,6 +37,8 @@ import com.google.inject.Singleton; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.Multiset; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -42,20 +47,25 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import javax.annotation.Nullable; + @Slf4j @Singleton public class BsqSwapTradeManager implements PersistedDataHost { + private final KeyRing keyRing; + private final PriceFeedService priceFeedService; private final BsqWalletService bsqWalletService; private final PersistenceManager> persistenceManager; private final TradableList bsqSwapTrades = new TradableList<>(); - private final KeyRing keyRing; - private final PriceFeedService priceFeedService; + @Nullable + private Multiset confirmedBsqSwapNodeAddressCache; // Used for listening for notifications in the UI @Getter @@ -71,6 +81,9 @@ public BsqSwapTradeManager(KeyRing keyRing, this.bsqWalletService = bsqWalletService; this.persistenceManager = persistenceManager; + bsqWalletService.addWalletTransactionsChangeListener(() -> confirmedBsqSwapNodeAddressCache = null); + bsqSwapTrades.addListener(c -> confirmedBsqSwapNodeAddressCache = null); + this.persistenceManager.initialize(bsqSwapTrades, "BsqSwapTrades", PersistenceManager.Source.PRIVATE); } @@ -133,6 +146,18 @@ public Stream getConfirmedBsqSwapTrades() { return getObservableList().stream().filter(this::isConfirmed); } + public Multiset getConfirmedBsqSwapNodeAddresses() { + var addresses = confirmedBsqSwapNodeAddressCache; + if (addresses == null) { + confirmedBsqSwapNodeAddressCache = addresses = bsqSwapTrades.stream() + .filter(this::isConfirmed) + .map(TradeModel::getTradingPeerNodeAddress) + .filter(Objects::nonNull) + .collect(ImmutableMultiset.toImmutableMultiset()); + } + return addresses; + } + private boolean isUnconfirmed(BsqSwapTrade bsqSwapTrade) { return matchesConfidence(bsqSwapTrade, TransactionConfidence.ConfidenceType.PENDING); }