From d9f2ce425f46f2f9c983b377e3a89ca8f2ecfe30 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 5 Nov 2022 15:07:23 -0400 Subject: [PATCH] improve performance by pre-fetching txs for subaddress queries --- .../core/btc/wallet/XmrWalletService.java | 53 +++++++++++-------- .../trade/protocol/ArbitratorProtocol.java | 2 +- .../core/trade/protocol/TradeProtocol.java | 5 +- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java index 8a19701fbb..589dc5301b 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -37,9 +37,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; @@ -57,6 +54,7 @@ import monero.wallet.MoneroWallet; import monero.wallet.MoneroWalletRpc; import monero.wallet.model.MoneroCheckTx; import monero.wallet.model.MoneroDestination; +import monero.wallet.model.MoneroOutputQuery; import monero.wallet.model.MoneroOutputWallet; import monero.wallet.model.MoneroSubaddress; import monero.wallet.model.MoneroTransferQuery; @@ -784,8 +782,9 @@ public class XmrWalletService { return addressEntry.get(); } else { // We try to use available and not yet used entries + List incomingTxs = getIncomingTxs(null); // pre-fetch all incoming txs to avoid query per subaddress Optional emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream().filter(e -> XmrAddressEntry.Context.AVAILABLE == e.getContext()) - .filter(e -> isSubaddressUnused(e.getSubaddressIndex())).findAny(); + .filter(e -> isSubaddressUnused(e.getSubaddressIndex(), incomingTxs)).findAny(); if (emptyAvailableAddressEntry.isPresent()) { return xmrAddressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId); } else { @@ -890,7 +889,33 @@ public class XmrWalletService { } public boolean isSubaddressUnused(int subaddressIndex) { - return getNumTxOutputsForSubaddress(subaddressIndex) == 0; + return isSubaddressUnused(subaddressIndex, null); + } + + private boolean isSubaddressUnused(int subaddressIndex, List incomingTxs) { + return getNumTxOutputsForSubaddress(subaddressIndex, incomingTxs) == 0; + } + + public int getNumTxOutputsForSubaddress(int subaddressIndex) { + return getNumTxOutputsForSubaddress(subaddressIndex, null); + } + + private int getNumTxOutputsForSubaddress(int subaddressIndex, List incomingTxs) { + if (incomingTxs == null) incomingTxs = getIncomingTxs(subaddressIndex); + int numUnspentOutputs = 0; + for (MoneroTxWallet tx : incomingTxs) { + numUnspentOutputs += tx.isConfirmed() ? tx.getOutputsWallet(new MoneroOutputQuery().setSubaddressIndex(subaddressIndex)).size() : 1; // TODO: monero-project does not provide outputs for unconfirmed txs + } + return numUnspentOutputs; + } + + private List getIncomingTxs(Integer subaddressIndex) { + return wallet.getTxs(new MoneroTxQuery() + .setTransferQuery((new MoneroTransferQuery() + .setAccountIndex(0) + .setSubaddressIndex(subaddressIndex) + .setIsIncoming(true))) + .setIncludeOutputs(true)); } public Coin getBalanceForAddress(String address) { @@ -917,24 +942,6 @@ public class XmrWalletService { return Coin.valueOf(balance.longValueExact()); } - public int getNumTxOutputsForSubaddress(int subaddressIndex) { - - // get txs with transfers to the subaddress - List txs = wallet.getTxs(new MoneroTxQuery() - .setTransferQuery((new MoneroTransferQuery() - .setAccountIndex(0) - .setSubaddressIndex(subaddressIndex) - .setIsIncoming(true))) - .setIncludeOutputs(true)); - - // count num outputs - int numUnspentOutputs = 0; - for (MoneroTxWallet tx : txs) { - numUnspentOutputs += tx.isConfirmed() ? tx.getOutputs().size() : 1; // TODO: monero-project does not provide outputs for unconfirmed txs - } - return numUnspentOutputs; - } - public Coin getAvailableConfirmedBalance() { return wallet != null ? Coin.valueOf(wallet.getUnlockedBalance(0).longValueExact()) : Coin.ZERO; } diff --git a/core/src/main/java/bisq/core/trade/protocol/ArbitratorProtocol.java b/core/src/main/java/bisq/core/trade/protocol/ArbitratorProtocol.java index e6f70305e1..f908340ed9 100644 --- a/core/src/main/java/bisq/core/trade/protocol/ArbitratorProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/ArbitratorProtocol.java @@ -90,7 +90,7 @@ public class ArbitratorProtocol extends DisputeProtocol { ArbitratorProcessDepositRequest.class) .using(new TradeTaskRunner(trade, () -> { - if (trade.getState() == Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS) { + if (trade.getState().ordinal() >= Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS.ordinal()) { stopTimeout(); this.errorMessageHandler = null; } diff --git a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java index 0273859e94..cfe4af656e 100644 --- a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java @@ -424,7 +424,6 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D handleTaskRunnerSuccess(peer, message); }, (errorMessage) -> { - stopTimeout(); handleTaskRunnerFault(peer, message, errorMessage); }))) .executeTasks(true); @@ -589,14 +588,14 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D // Timeout /////////////////////////////////////////////////////////////////////////////////////////// - protected void startTimeout(long timeoutSec) { + protected synchronized void startTimeout(long timeoutSec) { stopTimeout(); timeoutTimer = UserThread.runAfter(() -> { handleError("Timeout reached. Protocol did not complete in " + timeoutSec + " sec. TradeID=" + trade.getId() + ", state=" + trade.stateProperty().get()); }, timeoutSec); } - protected void stopTimeout() { + protected synchronized void stopTimeout() { if (timeoutTimer != null) { timeoutTimer.stop(); timeoutTimer = null;