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 c108f051b0..31780fc449 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -94,6 +94,7 @@ public class XmrWalletService { private TradeManager tradeManager; private MoneroWalletRpc wallet; private Map multisigWallets; + private final Map> txCache = new HashMap>(); @Inject XmrWalletService(CoreAccountService accountService, @@ -325,7 +326,7 @@ public class XmrWalletService { // submit tx to pool MoneroSubmitTxResult result = daemon.submitTxHex(txHex, true); // TODO (woodser): invert doNotRelay flag to relay for library consistency? if (!result.isGood()) throw new RuntimeException("Failed to submit tx to daemon: " + JsonUtils.serialize(result)); - tx = daemon.getTx(txHash); + tx = getTx(txHash); // verify reserved key images if (keyImages != null) { @@ -346,7 +347,6 @@ public class XmrWalletService { // verify mining fee BigInteger feeEstimate = getFeeEstimate(txHex); BigInteger feeThreshold = feeEstimate.multiply(BigInteger.valueOf(1l)).divide(BigInteger.valueOf(2l)); // must be at least 50% of estimated fee - tx = daemon.getTx(txHash); if (tx.getFee().compareTo(feeThreshold) < 0) { throw new RuntimeException("Mining fee is not enough, needed " + feeThreshold + " but was " + tx.getFee()); } @@ -372,6 +372,55 @@ public class XmrWalletService { return getDaemon().getFeeEstimate().multiply(BigInteger.valueOf(txHex.length())); } + public MoneroTx getTx(String txHash) { + synchronized (txCache) { + List txs = getTxs(Arrays.asList(txHash)); + return txs.isEmpty() ? null : txs.get(0); + } + } + + public List getTxs(List txHashes) { + synchronized (txCache) { + + // fetch txs + List txs = getDaemon().getTxs(txHashes, true); + + // store to cache + for (MoneroTx tx : txs) txCache.put(tx.getHash(), Optional.of(tx)); + + // schedule txs to be removed from cache + UserThread.runAfter(() -> { + synchronized (txCache) { + for (MoneroTx tx : txs) txCache.remove(tx.getHash()); + } + }, connectionsService.getDefaultRefreshPeriodMs()); + return txs; + } + } + + public MoneroTx getTxWithCache(String txHash) { + synchronized (txCache) { + List cachedTxs = getTxsWithCache(Arrays.asList(txHash)); + return cachedTxs.isEmpty() ? null : cachedTxs.get(0); + } + } + + public List getTxsWithCache(List txHashes) { + synchronized (txCache) { + + // get cached txs + List cachedTxs = new ArrayList(); + List uncachedTxHashes = new ArrayList(); + for (int i = 0; i < txHashes.size(); i++) { + if (txCache.containsKey(txHashes.get(i))) cachedTxs.add(txCache.get(txHashes.get(i)).orElse(null)); + else uncachedTxHashes.add(txHashes.get(i)); + } + + // return txs from cache if available, otherwise fetch + return uncachedTxHashes.isEmpty() ? cachedTxs : getTxs(txHashes); + } + } + public void shutDown() { closeAllWallets(); } diff --git a/core/src/main/java/bisq/core/trade/Trade.java b/core/src/main/java/bisq/core/trade/Trade.java index a57a59b829..b16415b450 100644 --- a/core/src/main/java/bisq/core/trade/Trade.java +++ b/core/src/main/java/bisq/core/trade/Trade.java @@ -446,6 +446,7 @@ public abstract class Trade implements Tradable, Model { transient Boolean takerDepositLocked; transient private MoneroTx makerDepositTx; transient private MoneroTx takerDepositTx; + private Long startTime; // cache /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, initialization @@ -852,7 +853,7 @@ public abstract class Trade implements Tradable, Model { MoneroWallet havenoWallet = processModel.getXmrWalletService().getWallet(); // fetch deposit txs from daemon - List txs = daemon.getTxs(Arrays.asList(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash()), true); + List txs = xmrWalletService.getTxs(Arrays.asList(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash())); // handle deposit txs seen if (txs.size() == 2) { @@ -889,7 +890,7 @@ public abstract class Trade implements Tradable, Model { if (unlockHeight != null && height < unlockHeight) return; // fetch txs from daemon - List txs = daemon.getTxs(Arrays.asList(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash()), true); + List txs = xmrWalletService.getTxs(Arrays.asList(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash())); // ignore if deposit txs not seen if (txs.size() != 2) return; @@ -924,7 +925,7 @@ public abstract class Trade implements Tradable, Model { public MoneroTx getTakerDepositTx() { String depositTxHash = getProcessModel().getTaker().getDepositTxHash(); try { - if (takerDepositTx == null) takerDepositTx = depositTxHash == null ? null : getXmrWalletService().getDaemon().getTx(depositTxHash); + if (takerDepositTx == null) takerDepositTx = depositTxHash == null ? null : getXmrWalletService().getTxWithCache(depositTxHash); return takerDepositTx; } catch (MoneroError e) { log.error("Wallet is missing taker deposit tx " + depositTxHash); @@ -936,7 +937,7 @@ public abstract class Trade implements Tradable, Model { public MoneroTx getMakerDepositTx() { String depositTxHash = getProcessModel().getMaker().getDepositTxHash(); try { - if (makerDepositTx == null) makerDepositTx = depositTxHash == null ? null : getXmrWalletService().getDaemon().getTx(depositTxHash); + if (makerDepositTx == null) makerDepositTx = depositTxHash == null ? null : getXmrWalletService().getTxWithCache(depositTxHash); return makerDepositTx; } catch (MoneroError e) { log.error("Wallet is missing maker deposit tx " + depositTxHash); @@ -1217,8 +1218,8 @@ public abstract class Trade implements Tradable, Model { } private long getStartTime() { + if (startTime != null) return startTime; long now = System.currentTimeMillis(); - long startTime; final MoneroTx takerDepositTx = getTakerDepositTx(); final MoneroTx makerDepositTx = getMakerDepositTx(); if (makerDepositTx != null && takerDepositTx != null && getTakeOfferDate() != null) { diff --git a/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java b/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java index 277fa6cce4..ba19ec3377 100644 --- a/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java +++ b/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java @@ -178,7 +178,7 @@ public class TxIdTextField extends AnchorPane { private void updateConfidence(String txId) { MoneroTx tx = null; try { - tx = xmrWalletService.getDaemon().getTx(txId); // TODO: cache results and don't re-fetch + tx = xmrWalletService.getTxWithCache(txId); tx.setNumConfirmations(tx.isConfirmed() ? xmrWalletService.getConnectionsService().getLastInfo().getHeight() - tx.getHeight() : 0l); // TODO: use tx.getNumConfirmations() when MoneroDaemonRpc supports it } catch (Exception e) { // do nothing