Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up deposit and transactions view loads #5120

Merged
56 changes: 37 additions & 19 deletions core/src/main/java/bisq/core/btc/wallet/WalletService.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@
import javax.inject.Inject;

import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.SetMultimap;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
Expand All @@ -84,12 +86,15 @@
import org.bouncycastle.crypto.params.KeyParameter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -116,6 +121,7 @@ public abstract class WalletService {
private final CopyOnWriteArraySet<BalanceListener> balanceListeners = new CopyOnWriteArraySet<>();
private final WalletChangeEventListener cacheInvalidationListener;
private final AtomicReference<Multiset<Address>> txOutputAddressCache = new AtomicReference<>();
private final AtomicReference<SetMultimap<Address, Transaction>> addressToMatchingTxSetCache = new AtomicReference<>();
@Getter
protected Wallet wallet;
@Getter
Expand All @@ -138,7 +144,10 @@ public abstract class WalletService {

params = walletsSetup.getParams();

cacheInvalidationListener = wallet -> txOutputAddressCache.set(null);
cacheInvalidationListener = wallet -> {
txOutputAddressCache.set(null);
addressToMatchingTxSetCache.set(null);
};
}


Expand Down Expand Up @@ -387,15 +396,28 @@ public void broadcastTx(Transaction tx, TxBroadcaster.Callback callback, int tim
public TransactionConfidence getConfidenceForAddress(Address address) {
List<TransactionConfidence> transactionConfidenceList = new ArrayList<>();
if (wallet != null) {
Set<Transaction> transactions = wallet.getTransactions(false);
if (transactions != null) {
transactionConfidenceList.addAll(transactions.stream().map(tx ->
getTransactionConfidence(tx, address)).collect(Collectors.toList()));
}
Set<Transaction> transactions = getAddressToMatchingTxSetMultiset().get(address);
transactionConfidenceList.addAll(transactions.stream().map(tx ->
getTransactionConfidence(tx, address)).collect(Collectors.toList()));
}
return getMostRecentConfidence(transactionConfidenceList);
}

private SetMultimap<Address, Transaction> getAddressToMatchingTxSetMultiset() {
return addressToMatchingTxSetCache.updateAndGet(set -> set != null ? set : computeAddressToMatchingTxSetMultimap());
}

private SetMultimap<Address, Transaction> computeAddressToMatchingTxSetMultimap() {
return wallet.getTransactions(false).stream()
.collect(ImmutableSetMultimap.flatteningToImmutableSetMultimap(
Function.identity(),
(Function<Transaction, Stream<Address>>) (
t -> getOutputsWithConnectedOutputs(t).stream()
.map(WalletService::getAddressFromOutput)
.filter(Objects::nonNull))))
.inverse();
}

@Nullable
public TransactionConfidence getConfidenceForTxId(String txId) {
if (wallet != null) {
Expand All @@ -408,18 +430,15 @@ public TransactionConfidence getConfidenceForTxId(String txId) {
return null;
}

protected TransactionConfidence getTransactionConfidence(Transaction tx, Address address) {
List<TransactionConfidence> transactionConfidenceList = getOutputsWithConnectedOutputs(tx)
.stream()
.filter(WalletService::isOutputScriptConvertibleToAddress)
.filter(output -> address != null && address.equals(getAddressFromOutput(output)))
.map(o -> tx.getConfidence())
.collect(Collectors.toList());
return getMostRecentConfidence(transactionConfidenceList);
private TransactionConfidence getTransactionConfidence(Transaction tx, Address address) {
stejbac marked this conversation as resolved.
Show resolved Hide resolved
boolean matchesAddress = getOutputsWithConnectedOutputs(tx).stream()
.anyMatch(output -> address != null && address.equals(getAddressFromOutput(output)));

return matchesAddress ? getMostRecentConfidence(List.of(tx.getConfidence())) : null;
stejbac marked this conversation as resolved.
Show resolved Hide resolved
}


protected List<TransactionOutput> getOutputsWithConnectedOutputs(Transaction tx) {
private List<TransactionOutput> getOutputsWithConnectedOutputs(Transaction tx) {
List<TransactionOutput> transactionOutputs = tx.getOutputs();
List<TransactionOutput> connectedOutputs = new ArrayList<>();

Expand Down Expand Up @@ -820,10 +839,9 @@ public void onReorganize(Wallet wallet) {
@Override
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
for (AddressConfidenceListener addressConfidenceListener : addressConfidenceListeners) {
List<TransactionConfidence> transactionConfidenceList = new ArrayList<>();
transactionConfidenceList.add(getTransactionConfidence(tx, addressConfidenceListener.getAddress()));

TransactionConfidence transactionConfidence = getMostRecentConfidence(transactionConfidenceList);
TransactionConfidence transactionConfidence = getMostRecentConfidence(Collections.singletonList(
getTransactionConfidence(tx, addressConfidenceListener.getAddress())
));
addressConfidenceListener.onTransactionConfidenceChanged(transactionConfidence);
}
txConfidenceListeners.stream()
Expand Down